PageRenderTime 80ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/PhpBrew/Command/FpmCommand/SetupCommand.php

http://github.com/c9s/phpbrew
PHP | 404 lines | 379 code | 16 blank | 9 comment | 31 complexity | 64fa0a2b9e67e6a69b2be8663efc29d7 MD5 | raw file
  1. <?php
  2. namespace PhpBrew\Command\FpmCommand;
  3. use CLIFramework\Command;
  4. use Exception;
  5. use PhpBrew\Config;
  6. class SetupCommand extends Command
  7. {
  8. public function brief()
  9. {
  10. return 'Generate and setup FPM startup config';
  11. }
  12. public function usage()
  13. {
  14. return 'phpbrew fpm setup [--systemctl|--initd|--launchctl] [--stdout] [<build name>]';
  15. }
  16. public function options($opts)
  17. {
  18. $opts->add(
  19. 'systemctl',
  20. "Generate systemd service entry. " .
  21. "This option only works for systemd-based Linux. " .
  22. "To use this option, be sure to compile PHP with --with-fpm-systemd option. " .
  23. "Start from 1.22, phpbrew automatically add --with-fpm-systemd when systemd is detected."
  24. );
  25. $opts->add(
  26. 'initd',
  27. 'Generate init.d script. ' .
  28. 'The generated init.d script depends on lsb-base >= 4.0. ' .
  29. 'If initctl is based on upstart, the init.d script will not be executed. ' .
  30. 'To check, please run /sbin/initctl --version in the command-line.'
  31. );
  32. $opts->add('launchctl', 'Generate plist for launchctl (OS X)');
  33. $opts->add('stdout', 'Print config to STDOUT instead of writing to the file.');
  34. }
  35. public function arguments($args)
  36. {
  37. $args->add('buildName')->optional();
  38. }
  39. public function execute($buildName = null)
  40. {
  41. if (!$buildName) {
  42. $buildName = Config::getCurrentPhpName();
  43. }
  44. if (!$buildName) {
  45. throw new Exception("PHPBREW_PHP is not set. You should provide the build name in the command.");
  46. }
  47. fprintf(
  48. STDERR,
  49. "*WARNING* php-fpm --pid option requires php >= 5.6. "
  50. . "You need to update your php-fpm.conf for the pid file location.\n"
  51. );
  52. $root = Config::getRoot();
  53. $fpmBin = "$root/php/$buildName/sbin/php-fpm";
  54. if (!file_exists($fpmBin)) {
  55. throw new Exception("$fpmBin doesn't exist.");
  56. }
  57. // TODO: require sudo permission
  58. if ($this->options->systemctl) {
  59. $content = $this->generateSystemctlService($buildName, $fpmBin);
  60. if ($this->options->stdout) {
  61. echo $content;
  62. return;
  63. }
  64. $file = '/lib/systemd/system/phpbrew-fpm.service';
  65. if (!is_writable(dirname($file))) {
  66. $this->logger->error("$file is not writable.");
  67. return;
  68. }
  69. $this->logger->info("Writing systemctl service entry: $file");
  70. file_put_contents($file, $content);
  71. $this->logger->info("To reload systemctl service:");
  72. $this->logger->info(" systemctl daemon-reload");
  73. $this->logger->info("Ensure that $buildName was built with --fpm-systemd option");
  74. } elseif ($this->options->initd) {
  75. $content = $this->generateInitD($buildName, $fpmBin);
  76. if ($this->options->stdout) {
  77. echo $content;
  78. return;
  79. }
  80. $file = '/etc/init.d/phpbrew-fpm';
  81. if (!is_writable(dirname($file))) {
  82. $this->logger->error("$file is not writable.");
  83. return;
  84. }
  85. $this->logger->info("Writing init.d script: $file");
  86. file_put_contents($file, $content);
  87. chmod($file, 0755); // make it executable
  88. $this->logger->info("To setup the startup item, remember to run update-rc.d to link the init script:");
  89. $this->logger->info(" sudo update-rc.d phpbrew-fpm defaults");
  90. } elseif ($this->options->launchctl) {
  91. $content = $this->generateLaunchctlService($buildName, $fpmBin);
  92. if ($this->options->stdout) {
  93. echo $content;
  94. return;
  95. }
  96. $file = '/Library/LaunchDaemons/org.phpbrew.fpm.plist';
  97. if (!is_writable(dirname($file))) {
  98. $this->logger->error("$file is not writable.");
  99. return;
  100. }
  101. $this->logger->info("Writing launchctl plist file: $file");
  102. file_put_contents($file, $content);
  103. $this->logger->info("To load the service:");
  104. $this->logger->info(" sudo launchctl load $file");
  105. } else {
  106. $this->logger->info(
  107. 'Please use one of the options [--systemctl, --initd, --launchctl] to setup system fpm service.'
  108. );
  109. }
  110. }
  111. protected function generateLaunchctlService($buildName, $fpmBin)
  112. {
  113. $root = Config::getRoot();
  114. $phpdir = "$root/php/$buildName";
  115. $config = <<<"EOS"
  116. <?xml version="1.0" encoding="UTF-8"?>
  117. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  118. <plist version="1.0">
  119. <dict>
  120. <key>KeepAlive</key>
  121. <true/>
  122. <key>Label</key>
  123. <string>org.phpbrew.fpm</string>
  124. <key>ProgramArguments</key>
  125. <array>
  126. <string>$fpmBin</string>
  127. <string>--nodaemonize</string>
  128. <string>--php-ini</string>
  129. <string>$phpdir/etc/php.ini</string>
  130. <string>--fpm-config</string>
  131. <string>$phpdir/etc/php-fpm.conf</string>
  132. </array>
  133. <key>RunAtLoad</key>
  134. <true/>
  135. <key>LaunchOnlyOnce</key>
  136. <true/>
  137. </dict>
  138. </plist>
  139. EOS;
  140. return $config;
  141. }
  142. protected function generateSystemctlService($buildName, $fpmBin)
  143. {
  144. $root = Config::getRoot();
  145. $phpdir = "$root/php/$buildName";
  146. $pidFile = $phpdir . '/var/run/php-fpm.pid';
  147. $config = <<<"EOS"
  148. [Unit]
  149. Description=The PHPBrew FastCGI Process Manager
  150. After=network.target
  151. [Service]
  152. Type=notify
  153. PIDFile=$pidFile
  154. ExecStart=$fpmBin --nodaemonize --fpm-config $phpdir/etc/php-fpm.conf --pid $pidFile
  155. ExecReload=/bin/kill -USR2 \$MAINPID
  156. [Install]
  157. WantedBy=multi-user.target
  158. EOS;
  159. return $config;
  160. }
  161. protected function generateInitD($buildName, $fpmBin)
  162. {
  163. $root = Config::getRoot();
  164. $phpdir = "$root/php/$buildName";
  165. $pidFile = $phpdir . '/var/run/php-fpm.pid';
  166. $config = <<<"EOS"
  167. #!/bin/sh
  168. ### BEGIN INIT INFO
  169. # Provides: phpbrew-fpm
  170. # Required-Start: \$remote_fs \$network
  171. # Required-Stop: \$remote_fs \$network
  172. # Default-Start: 2 3 4 5
  173. # Default-Stop: 0 1 6
  174. # Short-Description: starts phpbrew-fpm
  175. # Description: Starts The PHP FastCGI Process Manager Daemon
  176. ### END INIT INFO
  177. PATH=/sbin:/usr/sbin:/bin:/usr/bin
  178. DESC="PHPBrew FastCGI Process Manager"
  179. NAME=phpbrew-fpm
  180. PHP_VERSION=$buildName
  181. PHPBREW_ROOT=$root
  182. CONFFILE=$phpdir/etc/php-fpm.conf
  183. DAEMON=$fpmBin
  184. DAEMON_ARGS="--daemonize --fpm-config \$CONFFILE --pid $pidFile"
  185. PIDFILE=$pidFile
  186. TIMEOUT=30
  187. SCRIPTNAME=/etc/init.d/\$NAME
  188. # Exit if the package is not installed
  189. [ -x "\$DAEMON" ] || exit 0
  190. # Read configuration variable file if it is present
  191. [ -r /etc/default/\$NAME ] && . /etc/default/\$NAME
  192. # Load the VERBOSE setting and other rcS variables
  193. . /lib/init/vars.sh
  194. # Define LSB log_* functions.
  195. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
  196. . /lib/lsb/init-functions
  197. #
  198. # Function to check the correctness of the config file
  199. #
  200. do_check()
  201. {
  202. # FIXME
  203. # /usr/lib/php/phpbrew-fpm-checkconf || return 1
  204. return 0
  205. }
  206. #
  207. # Function that starts the daemon/service
  208. #
  209. do_start()
  210. {
  211. # Return
  212. # 0 if daemon has been started
  213. # 1 if daemon was already running
  214. # 2 if daemon could not be started
  215. start-stop-daemon --start --quiet --pidfile \$PIDFILE --exec \$DAEMON --test > /dev/null \
  216. || return 1
  217. start-stop-daemon --start --quiet --pidfile \$PIDFILE --exec \$DAEMON -- \
  218. \$DAEMON_ARGS 2>/dev/null \
  219. || return 2
  220. # Add code here, if necessary, that waits for the process to be ready
  221. # to handle requests from services started subsequently which depend
  222. # on this one. As a last resort, sleep for some time.
  223. }
  224. #
  225. # Function that stops the daemon/service
  226. #
  227. do_stop()
  228. {
  229. # Return
  230. # 0 if daemon has been stopped
  231. # 1 if daemon was already stopped
  232. # 2 if daemon could not be stopped
  233. # other if a failure occurred
  234. start-stop-daemon --stop --quiet --retry=QUIT/\$TIMEOUT/TERM/5/KILL/5 --pidfile \$PIDFILE --name \$NAME
  235. RETVAL="\$?"
  236. [ "\$RETVAL" = 2 ] && return 2
  237. # Wait for children to finish too if this is a daemon that forks
  238. # and if the daemon is only ever run from this initscript.
  239. # If the above conditions are not satisfied then add some other code
  240. # that waits for the process to drop all resources that could be
  241. # needed by services started subsequently. A last resort is to
  242. # sleep for some time.
  243. start-stop-daemon --stop --quiet --oknodo --retry=0/30/TERM/5/KILL/5 --exec \$DAEMON
  244. [ "\$?" = 2 ] && return 2
  245. # Many daemons don't delete their pidfiles when they exit.
  246. rm -f \$PIDFILE
  247. return "\$RETVAL"
  248. }
  249. #
  250. # Function that sends a SIGHUP to the daemon/service
  251. #
  252. do_reload() {
  253. #
  254. # If the daemon can reload its configuration without
  255. # restarting (for example, when it is sent a SIGHUP),
  256. # then implement that here.
  257. #
  258. start-stop-daemon --stop --signal USR2 --quiet --pidfile \$PIDFILE --name \$NAME
  259. return 0
  260. }
  261. do_tmpfiles() {
  262. local type path mode user group
  263. [ "\$1" != no ] && V=-v
  264. TMPFILES=/usr/lib/tmpfiles.d/phpbrew-fpm.conf
  265. if [ -r "\$TMPFILES" ]; then
  266. while read type path mode user group age argument; do
  267. if [ "\$type" = "d" ]; then
  268. mkdir \$V -p "\$path"
  269. chmod \$V "\$mode" "\$path"
  270. chown \$V "\$user:\$group" "\$path"
  271. fi
  272. done < "\$TMPFILES"
  273. fi
  274. }
  275. case "\$1" in
  276. start)
  277. if init_is_upstart; then
  278. exit 1
  279. fi
  280. [ "\$VERBOSE" != no ] && log_daemon_msg "Starting \$DESC" "\$NAME"
  281. do_tmpfiles \$VERBOSE
  282. do_check \$VERBOSE
  283. case "\$?" in
  284. 0)
  285. do_start
  286. case "\$?" in
  287. 0|1) [ "\$VERBOSE" != no ] && log_end_msg 0 ;;
  288. 2) [ "\$VERBOSE" != no ] && log_end_msg 1 ;;
  289. esac
  290. ;;
  291. 1) [ "\$VERBOSE" != no ] && log_end_msg 1 ;;
  292. esac
  293. ;;
  294. stop)
  295. if init_is_upstart; then
  296. exit 0
  297. fi
  298. [ "\$VERBOSE" != no ] && log_daemon_msg "Stopping \$DESC" "\$NAME"
  299. do_stop
  300. case "\$?" in
  301. 0|1) [ "\$VERBOSE" != no ] && log_end_msg 0 ;;
  302. 2) [ "\$VERBOSE" != no ] && log_end_msg 1 ;;
  303. esac
  304. ;;
  305. status)
  306. status_of_proc "\$DAEMON" "\$NAME" && exit 0 || exit \$?
  307. ;;
  308. check)
  309. do_check yes
  310. ;;
  311. reload|force-reload)
  312. if init_is_upstart; then
  313. exit 1
  314. fi
  315. log_daemon_msg "Reloading \$DESC" "\$NAME"
  316. do_reload
  317. log_end_msg \$?
  318. ;;
  319. reopen-logs)
  320. log_daemon_msg "Reopening \$DESC logs" \$NAME
  321. if start-stop-daemon --stop --signal USR1 --oknodo --quiet \
  322. --pidfile \$PIDFILE --exec \$DAEMON
  323. then
  324. log_end_msg 0
  325. else
  326. log_end_msg 1
  327. fi
  328. ;;
  329. restart)
  330. if init_is_upstart; then
  331. exit 1
  332. fi
  333. log_daemon_msg "Restarting \$DESC" "\$NAME"
  334. do_stop
  335. case "\$?" in
  336. 0|1)
  337. do_start
  338. case "\$?" in
  339. 0) log_end_msg 0 ;;
  340. 1) log_end_msg 1 ;; # Old process is still running
  341. *) log_end_msg 1 ;; # Failed to start
  342. esac
  343. ;;
  344. *)
  345. # Failed to stop
  346. log_end_msg 1
  347. ;;
  348. esac
  349. ;;
  350. *)
  351. echo "Usage: \$SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2
  352. exit 1
  353. ;;
  354. esac
  355. :
  356. EOS;
  357. return $config;
  358. }
  359. }