client/commands/kill.py (83 lines of code) (raw):
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import logging
import os
import shutil
import signal
from pathlib import Path
import psutil
from .. import (
configuration as configuration_module,
find_directories,
recently_used_configurations,
)
from . import commands, remote_logging, servers, server_connection, stop
LOG: logging.Logger = logging.getLogger(__name__)
PYRE_FIRE = """
',
,c:
', ;cc:
,:l; ';:lll:
'cllc' 'clllll:
;loooc' 'cllllllc'
;looool, :llllllll, :,
'looooool' ;lollllllo: ;ll;
;ooooooool, 'loooooloool; ,clll:
cdoddoooooo; ;oooooooooool: ;loooll:
cddddddddodo; cooooooooooooolloooooool;
;ddddddddddo; 'loooooooooooooooooooooooc'
cdddddddddc 'ldddddooooooooooooooooooo,
,coodolc; cddddddddddoooooooooooooo;
' ,oddddddddddddddddodooooo;
,::::::::::::, :ddddddddddddddddddddddl'
'lddddddddddxd: :ddddddddddddddddddddd:
;odddddddddddl, ;oxdddddddddddddddddl'
'ldddddddddddo: ,:ldxddxddddddddddl'
:ddddddddddddl' cdxxxxxxxdddddl'
,ldddddddddddo; ,oxxxxxxxxxdc
:ddddddddddddc;' 'cdxxxxxxo;
,ldddddddddddxo; ;dxxxo:
cdddddddddddddc 'lo:
;oddddddddddddo,
'cddddddddddddd:
;odddddddddddddl,
:ddddddddddddddddd:
,ldddddddddddddddddddl,
:odddddddddddddddddddddo:
'ldddddddddddddddddddddddddl'
;odddddddddddl, ,ldddddddddddo;
'cdddddddddddd: :ddddddddddddc'
;odddddddddddo, ,odddddddddddo;
cddddddddddddc cddddddddddddc
;oxddxddxddddo; ;odxxxxddddxxo,
;:::::::::::;' ';:::::::::::;
"""
def _kill_processes_by_name(name: str) -> None:
for process in psutil.process_iter(attrs=["name"]):
if process.name() != name:
continue
# Do not kill the `pyre kill` command itself.
pid_to_kill = process.pid
if pid_to_kill == os.getpgid(os.getpid()):
continue
try:
LOG.info(f"Killing process {name} with pid {pid_to_kill}.")
os.kill(pid_to_kill, signal.SIGKILL)
except (ProcessLookupError, PermissionError) as exception:
LOG.debug(
f"Failed to kill process {name} with pid {pid_to_kill} "
+ f"due to exception {exception}"
)
def _kill_binary_processes(configuration: configuration_module.Configuration) -> None:
LOG.warning("Force-killing all running pyre servers.")
LOG.warning(
"Use `pyre servers stop` if you want to gracefully stop all running servers."
)
binary = configuration.get_binary_respecting_override()
if binary is not None:
_kill_processes_by_name(binary)
def _kill_client_processes(configuration: configuration_module.Configuration) -> None:
_kill_processes_by_name(find_directories.CLIENT_NAME)
# TODO (T85602687): Run `buck kill` once buck is supported by the server
def _delete_server_files(configuration: configuration_module.Configuration) -> None:
socket_root = server_connection.get_default_socket_root()
LOG.info(f"Deleting socket files and lock files under {socket_root}")
for socket_path in servers.get_pyre_socket_files(socket_root):
stop.remove_socket_if_exists(socket_path)
log_directory = Path(configuration.log_directory) / "new_server"
LOG.info(f"Deleting server logs under {log_directory}")
try:
shutil.rmtree(str(log_directory), ignore_errors=True)
except OSError:
pass
# TODO(T92826668): Delete files under artifact root
def _delete_caches(configuration: configuration_module.Configuration) -> None:
dot_pyre_directory = configuration.dot_pyre_directory
resource_cache_directory = dot_pyre_directory / "resource_cache"
LOG.info(
f"Deleting local binary and typeshed cache under {resource_cache_directory}"
)
try:
shutil.rmtree(str(resource_cache_directory), ignore_errors=True)
recently_used_configurations.Cache(dot_pyre_directory).delete()
except OSError:
pass
# TODO (T85602687): Try to remove buck builder cache as well once buck is
# supported by the server
@remote_logging.log_usage(command_name="kill")
def run(
configuration: configuration_module.Configuration, with_fire: bool
) -> commands.ExitCode:
try:
_kill_binary_processes(configuration)
_kill_client_processes(configuration)
# TODO (T85602550): Store a rage log before this happens.
# TODO (T85614630): Delete client logs as well.
_delete_server_files(configuration)
_delete_caches(configuration)
if with_fire:
LOG.warning(
(
"Note that `--with-fire` adds emphasis to `pyre kill` but does"
+ f" not affect its behavior.\n{PYRE_FIRE}"
)
)
LOG.info("Done\n")
return commands.ExitCode.SUCCESS
except Exception as error:
raise commands.ClientException(
f"Exception occurred during `pyre kill`: {error}"
) from error