mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 01:07:15 +03:00
b39f678b85
Summary: on macOS we cannot safely use `fork`. This commit replaces the use of `fork` in the startup logger subsystem. This was a little tricky to untangle; originally (prior to any of the `fork` removal efforts in this diff stack), the startup flow was to spawn a set of processes via fork: ``` edenfs (setuid) \-----edenfs (privhelper, as root) \------edenfs (daemonized) ``` The forked children take advantage of being able to implicitly pass state to the child processes from the parent. That data flow needs to become explicit when removing the fork which makes some things a little awkward. With fork removed: * `edenfs` unconditionally spawns `edenfs_privhelper` while it has root privs and before most of the process has been initialized. * That same `edenfs` process will then spawn a child `edenfs` process which starts from scratch, but that which needs to run as the real server instance * The original `edenfs` instance needs to linger for a while to remain connected to the controlling tty to pass back the startup state to the user, before terminating. This commit deletes the check that `edenfs` is started originally as root; previously the logic relied on the forked startup logger continuing past the `daemonizeIfRequested` call and simply deferring the check until after folly::init. With these changes we can't easily perform such a check without adding some extra gymnastics to pass the state around; the place where that is checked is in the spawned child of the original edenfs, which is not a privileged process and doesn't know the original euid. I don't believe this to be a great loss as we tuck `edenfs` away under the libexec dir. Reviewed By: chadaustin Differential Revision: D23696569 fbshipit-source-id: 55b95daf022601a4699274d696af419f0a11f6f2
100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2.
|
|
|
|
import contextlib
|
|
import os
|
|
import pathlib
|
|
import signal
|
|
import subprocess
|
|
import typing
|
|
|
|
from eden.thrift.legacy import create_thrift_client
|
|
|
|
from .find_executables import FindExe
|
|
|
|
|
|
class FakeEdenFS(typing.ContextManager[int]):
|
|
"""A running fake_edenfs process."""
|
|
|
|
@classmethod
|
|
def spawn(
|
|
cls,
|
|
eden_dir: pathlib.Path,
|
|
etc_eden_dir: pathlib.Path,
|
|
home_dir: pathlib.Path,
|
|
extra_arguments: typing.Optional[typing.Sequence[str]] = None,
|
|
) -> "FakeEdenFS":
|
|
command: typing.List[str] = [
|
|
FindExe.FAKE_EDENFS,
|
|
"--configPath",
|
|
str(home_dir / ".edenrc"),
|
|
"--edenDir",
|
|
str(eden_dir),
|
|
"--etcEdenDir",
|
|
str(etc_eden_dir),
|
|
"--edenfs",
|
|
]
|
|
if extra_arguments:
|
|
command.extend(extra_arguments)
|
|
subprocess.check_call(command)
|
|
return cls.from_existing_process(eden_dir=eden_dir)
|
|
|
|
@classmethod
|
|
def spawn_via_cli(
|
|
cls,
|
|
eden_dir: pathlib.Path,
|
|
etc_eden_dir: pathlib.Path,
|
|
home_dir: pathlib.Path,
|
|
extra_arguments: typing.Optional[typing.Sequence[str]] = None,
|
|
) -> "FakeEdenFS":
|
|
command: typing.List[str] = [
|
|
FindExe.EDEN_CLI,
|
|
"--config-dir",
|
|
str(eden_dir),
|
|
"--etc-eden-dir",
|
|
str(etc_eden_dir),
|
|
"--home-dir",
|
|
str(home_dir),
|
|
"start",
|
|
"--daemon-binary",
|
|
FindExe.FAKE_EDENFS,
|
|
]
|
|
if extra_arguments:
|
|
command.append("--")
|
|
command.extend(extra_arguments)
|
|
subprocess.check_call(command)
|
|
return cls.from_existing_process(eden_dir=eden_dir)
|
|
|
|
@staticmethod
|
|
def from_existing_process(eden_dir: pathlib.Path) -> "FakeEdenFS":
|
|
edenfs_pid = int((eden_dir / "lock").read_text())
|
|
return FakeEdenFS(process_id=edenfs_pid)
|
|
|
|
def __init__(self, process_id: int) -> None:
|
|
super().__init__()
|
|
self.process_id = process_id
|
|
|
|
def __enter__(self) -> int:
|
|
return self.process_id
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
with contextlib.suppress(ProcessLookupError):
|
|
os.kill(self.process_id, signal.SIGTERM)
|
|
return None
|
|
|
|
|
|
def get_fake_edenfs_argv(eden_dir: pathlib.Path) -> typing.List[str]:
|
|
with create_thrift_client(str(eden_dir)) as client:
|
|
argv = client.getDaemonInfo().commandLine
|
|
# StartupLogger may add `--startupLoggerFd 5` as a parameter.
|
|
# The 5 is a file descriptor number and has no guarantees as
|
|
# to which number is selected by the kernel.
|
|
# We perform various test assertions on these arguments.
|
|
# To make those easier, we rewrite the fd number to always be 5
|
|
if "--startupLoggerFd" in argv:
|
|
argv[argv.index("--startupLoggerFd") + 1] = "5"
|
|
return argv
|