@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Add a `bin/aphlict` wrapper to handle aphlict config / daemonization

Summary: Simple wrapper script to configure, launch, daemonize and restart the Aphlict server.

Test Plan: Ran "bin/aphlict", checked /notification/status/. Ran "bin/aphlict --foreground".

Reviewers: jungejason, vrana

Reviewed By: jungejason

CC: aran

Maniphest Tasks: T944

Differential Revision: https://secure.phabricator.com/D2784

+183
+1
bin/aphlict
··· 1 + ../support/aphlict/server/aphlict_launcher.php
+10
conf/default.conf.php
··· 199 199 // URI and port for the notification root server. 200 200 'notification.server-uri' => 'http://localhost:22281/', 201 201 202 + // The server must be started as root so it can bind to privileged ports, but 203 + // if you specify a user here it will drop permissions after binding. 204 + 'notification.user' => null, 205 + 206 + // Location where the server should log to. 207 + 'notification.log' => '/var/log/aphlict.log', 208 + 209 + // PID file to use. 210 + 'notification.pidfile' => '/var/run/aphlict.pid', 211 + 202 212 203 213 // -- Email ----------------------------------------------------------------- // 204 214
+172
support/aphlict/server/aphlict_launcher.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + /* 5 + * Copyright 2012 Facebook, Inc. 6 + * 7 + * Licensed under the Apache License, Version 2.0 (the "License"); 8 + * you may not use this file except in compliance with the License. 9 + * You may obtain a copy of the License at 10 + * 11 + * http://www.apache.org/licenses/LICENSE-2.0 12 + * 13 + * Unless required by applicable law or agreed to in writing, software 14 + * distributed under the License is distributed on an "AS IS" BASIS, 15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 + * See the License for the specific language governing permissions and 17 + * limitations under the License. 18 + */ 19 + 20 + // This is a launcher for the 'aphlict' Node.js notification server that 21 + // provides real-time notifications for Phabricator. It handles reading 22 + // configuration from the Phabricator config, daemonizing the server, 23 + // restarting the server if it crashes, and some basic sanity checks. 24 + 25 + 26 + $root = dirname(dirname(dirname(dirname(__FILE__)))); 27 + require_once $root.'/scripts/__init_script__.php'; 28 + 29 + 30 + // >>> Options and Arguments --------------------------------------------------- 31 + 32 + $args = new PhutilArgumentParser($argv); 33 + $args->setTagline('manage Aphlict notification server'); 34 + $args->setSynopsis(<<<EOHELP 35 + **aphlict** [__options__] 36 + Start (or restart) the Aphlict server. 37 + EOHELP 38 + ); 39 + $args->parseStandardArguments(); 40 + $args->parse(array( 41 + array( 42 + 'name' => 'foreground', 43 + 'help' => 'Run in the foreground instead of daemonizing.', 44 + ), 45 + )); 46 + 47 + if (posix_getuid() != 0) { 48 + throw new Exception( 49 + "You must run this script as root; the Aphlict server needs to bind to ". 50 + "privileged ports."); 51 + } 52 + 53 + list($err) = exec_manual('node -v'); 54 + if ($err) { 55 + throw new Exception( 56 + '`node` is not in $PATH. You must install Node.js to run the Aphlict '. 57 + 'server.'); 58 + } 59 + 60 + $server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri'); 61 + $server_uri = new PhutilURI($server_uri); 62 + 63 + $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); 64 + $client_uri = new PhutilURI($client_uri); 65 + 66 + $user = PhabricatorEnv::getEnvConfig('notification.user'); 67 + $log = PhabricatorEnv::getEnvConfig('notification.log'); 68 + 69 + $g_pidfile = PhabricatorEnv::getEnvConfig('notification.pidfile'); 70 + $g_future = null; 71 + 72 + $foreground = $args->getArg('foreground'); 73 + 74 + // Build the argument list for the server itself. 75 + $server_argv = array(); 76 + $server_argv[] = csprintf('--port=%s', $client_uri->getPort()); 77 + $server_argv[] = csprintf('--admin=%s', $server_uri->getPort()); 78 + 79 + if ($user) { 80 + $server_argv[] = csprintf('--user=%s', $user); 81 + } 82 + 83 + if ($log) { 84 + $server_argv[] = csprintf('--log=%s', $log); 85 + } 86 + 87 + 88 + // >>> Foreground / Background ------------------------------------------------- 89 + 90 + // If we start in the foreground, we use phutil_passthru() below to show any 91 + // output from the server to the console, but this means *this* process won't 92 + // receive signals until the child exits. If we write our pid to the pidfile 93 + // and then another process starts, it will try to SIGTERM us but we won't 94 + // receive the signal. Since the effect is the same and this is simpler, just 95 + // ignore the pidfile if launched in `--foreground` mode; this is a debugging 96 + // mode anyway. 97 + if ($foreground) { 98 + echo "Starting server in foreground, ignoring pidfile...\n"; 99 + $g_pidfile = null; 100 + } else { 101 + $pid = pcntl_fork(); 102 + if ($pid < 0) { 103 + throw new Exception("Failed to fork()!"); 104 + } else if ($pid) { 105 + exit(0); 106 + } 107 + } 108 + 109 + 110 + // >>> Signals / Cleanup ------------------------------------------------------- 111 + 112 + function cleanup($sig = '?') { 113 + global $g_pidfile; 114 + if ($g_pidfile) { 115 + Filesystem::remove($g_pidfile); 116 + $g_pidfile = null; 117 + } 118 + 119 + global $g_future; 120 + if ($g_future) { 121 + $g_future->resolveKill(); 122 + $g_future = null; 123 + } 124 + 125 + exit(1); 126 + } 127 + 128 + if (!$foreground) { 129 + declare(ticks = 1); 130 + pcntl_signal(SIGTERM, 'cleanup'); 131 + } 132 + 133 + register_shutdown_function('cleanup'); 134 + 135 + 136 + // >>> pidfile ----------------------------------------------------------------- 137 + 138 + if ($g_pidfile) { 139 + if (Filesystem::pathExists($g_pidfile)) { 140 + $old_pid = (int)Filesystem::readFile($g_pidfile); 141 + posix_kill($old_pid, SIGTERM); 142 + sleep(1); 143 + Filesystem::remove($g_pidfile); 144 + } 145 + Filesystem::writeFile($g_pidfile, getmypid()); 146 + } 147 + 148 + 149 + // >>> run --------------------------------------------------------------------- 150 + 151 + $command = csprintf( 152 + 'node %s %C', 153 + dirname(__FILE__).'/aphlict_server.js', 154 + implode(' ', $server_argv)); 155 + 156 + if ($foreground) { 157 + echo "Launching server:\n\n"; 158 + echo " $ ".$command."\n\n"; 159 + 160 + $err = phutil_passthru('%C', $command); 161 + echo ">>> Server exited!\n"; 162 + exit($err); 163 + } else { 164 + while (true) { 165 + $g_future = new ExecFuture('%C', $command); 166 + $g_future->resolve(); 167 + 168 + // If the server exited, wait a couple of seconds and restart it. 169 + unset($g_future); 170 + sleep(2); 171 + } 172 + }