diff --git a/server/setup b/server/setup index 50606e6..0a0ff04 100755 --- a/server/setup +++ b/server/setup @@ -1,12 +1,18 @@ #!/usr/bin/env python3 import argparse -import shlex +import os from pathlib import Path -from subprocess import check_call, run +import platform +import shlex +from subprocess import check_call, run, DEVNULL from grasp_server import setup_parser -SYSTEMD_CONFIG = """ + +SYSTEM = platform.system() + + +SYSTEMD_TEMPLATE = """ [Unit] Description=Grasp extension server conterpart @@ -17,7 +23,29 @@ WantedBy=default.target ExecStart={server} {extra_args} Type=simple Restart=always -""" +""".lstrip() + + +LAUNCHD_TEMPLATE = ''' + + + + + Label + {service_name} + + ProgramArguments + +{arguments} + + + RunAtLoad + + KeepAlive + + + +'''.lstrip() def systemd(*args, method=check_call): @@ -25,31 +53,36 @@ def systemd(*args, method=check_call): 'systemctl', '--user', *args, ]) -def setup(args): + +def get_command(args) -> list[str]: + return [ + '--port', args.port, + '--path', args.path, + '--template', args.template, + *([] if args.config is None else ['--config' , args.config]), + ] + + +def setup_systemd(args) -> None: + args.template = args.template.replace('%', '%%').replace('\n', r'\n') # escape for systemd... + args.unit_name = args.unit_name.removesuffix('.service') # legacy unit_name default had .service + base_path = '~/.config/systemd/user' if not Path(base_path).expanduser().is_dir(): Path(base_path).expanduser().mkdir(parents=True, exist_ok=True) unit_name = args.unit_name - out = Path(f'{base_path}/{unit_name}').expanduser() - print(f"Writing systemd config to {out}:") + out = Path(f'{base_path}/{unit_name}.service').expanduser() + print(f"Writing unit file to {out}") server_bin = Path(__file__).parent.joinpath('grasp_server.py').absolute() + command = get_command(args) + extra_args = ' '.join(map(shlex.quote, command)) - template = args.template.replace('%', '%%').replace('\n', r'\n') # escape for systemd... - args = [ - '--port', args.port, - '--path', args.path, - '--template', template, - *([] if args.config is None else ['--config' , args.config]), - ] - extra_args = ' '.join(map(shlex.quote, args)) - - with out.open('w') as fo: - fo.write(SYSTEMD_CONFIG.format( + out.write_text(SYSTEMD_TEMPLATE.format( server=server_bin, extra_args=extra_args, - )) + )) try: systemd('stop' , unit_name, method=run) # ignore errors here if it wasn't running in the first place systemd('daemon-reload') @@ -60,12 +93,48 @@ def setup(args): print(f"Something has gone wrong... you might want to use 'journalctl --user -u {unit_name}' to debug") raise e -def main(): + +def setup_launchd(args) -> None: + # launchd keep everything as xml and treats whitespace literally + args.template = args.template.replace(r'\n', '\n') + + base_path = Path('~/Library/LaunchAgents') + + unit_name = args.unit_name + out = Path(f'{base_path}/{unit_name}.plist').expanduser() + print(f"Writing unit file to {out}") + + server_bin = Path(__file__).parent.joinpath('grasp_server.py').absolute() + command = get_command(args) + + + command = [str(server_bin)] + command + arguments = '\n'.join(f'{a}' for a in command) + + out.write_text(LAUNCHD_TEMPLATE.format( + service_name=unit_name, + arguments=arguments, + )) + + DOMAIN = f'gui/{os.getuid()}' + # defensive since might not have been running + run(['launchctl', 'bootout', DOMAIN + '/' + unit_name], stdout=DEVNULL, stderr=DEVNULL) + check_call(['launchctl', 'bootstrap', DOMAIN, out]) + + +def main() -> None: + default_unit_name = 'grasp' if SYSTEM == 'Linux' else 'com.github.karlicoss.grasp' + p = argparse.ArgumentParser('grasp server setup', formatter_class=lambda prog: argparse.ArgumentDefaultsHelpFormatter(prog, width=100)) # type: ignore - p.add_argument('--unit-name', type=str, default='grasp.service', help='Systemd unit name') + p.add_argument('--unit-name', type=str, default=default_unit_name, help='systemd/launchd unit name') setup_parser(p) args = p.parse_args() - setup(args) + if SYSTEM == 'Linux': + setup_systemd(args) + else: # assume macos + setup_launchd(args) + + if __name__ == '__main__': main()