PHP Linux Daemon erstellen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Dieser Wiki Artikel erläutert den Bau eines Linux Daemons, wie er im Betriebssystem eingebunden werden kann.
    == Beispiel ==
    Unser Daemon hört im Beispiel auf den Namen "veryimportant", was übersetzt aus dem Englischen für "sehr wichtig" steht.
    Nach gültigen Konventionen wird der Daemon gestartet über ein Init Script.

    Quellcode

    1. /etc/init.d/veryimportant start


    == Linux Standard Base (LSB) ==
    Die Linux Standard Base definiert eine Binärschnittstelle mit dem Ziel, die Kompatibilität zwischen den verschiedenen Linux-Distributionen zu verbessern.
    Das Script unter /etc/init.d/veryimportant sieht wie folgt aus:

    Quellcode

    1. #!/bin/sh
    2. ### BEGIN INIT INFO
    3. # Provides: php-queue
    4. # Required-Start: $remote_fs mysql
    5. # Required-Stop: $remote_fs mysql
    6. # Should-Start: $network $named $time
    7. # Should-Stop: $network $named $time
    8. # Default-Start: 2 3 4 5
    9. # Default-Stop: 0 1 6
    10. # Short-Description: Starts and stops the veryimportant php daemon.
    11. # Description: Controls the veryimportant PHP script for me, so that
    12. # processing is active in all run-levels in which network
    13. # services are active
    14. ### END INIT INFO
    15. #
    16. # Author: Max Mustermann <max@mustermann.de>
    17. #
    18. # This init script conforms to LSB 3.2.0.
    19. #
    20. PATH=/bin:/usr/bin:/sbin:/usr/sbin
    21. DAEMON=/home/veryimportant/daemon.php
    22. PIDFILE=/var/run/veryimportant.pid
    23. test -x $DAEMON || exit 0
    24. . /lib/lsb/init-functions
    25. case "$1" in
    26. start)
    27. log_daemon_msg "Starting veryimportant processing" "veryimportant.php"
    28. start_daemon -p $PIDFILE $DAEMON
    29. log_end_msg $?
    30. ;;
    31. stop)
    32. log_daemon_msg "Stopping veryimportant processing" "veryimportant.php"
    33. killproc -p $PIDFILE $DAEMON
    34. log_end_msg $?
    35. ;;
    36. force-reload|restart)
    37. $0 stop
    38. $0 start
    39. ;;
    40. *)
    41. echo "Usage: $0 {start|stop|restart|force-reload}" >&2
    42. exit 2
    43. ;;
    44. esac
    Alles anzeigen


    Wir machen das Script nur für root ausführbar, aber für alle lesbar.

    Quellcode

    1. chmod 0755 /etc/init.d/veryimportant


    == Das PHP Script ==
    Das Script des Hauptprogramms legen wir unter /home/veryimportant/daemon.php ab.
    Die Kommandozeilenparameter "d" und "u" sind aktiviert. Mit "d" schalten wir in den Debug Modus und mit "u" nehmen wir unterschiedliche Benutzer an. Das Script soll nicht unter root, sondern einem anderen Benutzer laufen.
    Bei uns "veryimportant".

    Wir legen den Benutzer an mit

    Quellcode

    1. adduser veryimportant


    Das Script enthält einen Logger, der entsprechende Ausgaben an einen Output Stream leitet. Das kann einer Logdatei oder der Standard-Output sein.
    Die Log Datei befindet sich unter /var/log/veryimportant.log.
    Damit der Daemon nicht mehrfach gestartet werden kann verwenden wir eine PID Datei unter /var/run/veryimportant.pid.

    Nun zum Quellcode:

    Quellcode

    1. #!/usr/bin/php
    2. <?php
    3. $options = getopt('du:');
    4. if ($options === FALSE) {
    5. die("Failed to parse command line options\n");
    6. }
    7. $debug = isset($options['d']);
    8. $user = isset($options['u']) ? $options['u'] : "veryimportantdaemon";
    9. $pidfile = "/var/run/veryimportant.pid";
    10. if (getmyuid() != 0) {
    11. die("This script must be started as root (it will revoke its privileges)\n");
    12. }
    13. if (!($passwd = posix_getpwnam($user))) {
    14. die("Failed to get passwd entry for user \"$user\"\n");
    15. }
    16. # Set the default timezone to the system's timezone.
    17. if (function_exists("date_default_timezone_set") and
    18. function_exists("date_default_timezone_get"))
    19. @date_default_timezone_set(@date_default_timezone_get());
    20. if (file_exists($pidfile)) {
    21. $pid = rtrim(file_get_contents($pidfile));
    22. if (posix_kill($pid, 0)) {
    23. die("Failed to create pidfile $pidfile (PID $pid is still running)\n");
    24. }
    25. trigger_error("Removing stale pidfile $pidfile", E_USER_NOTICE);
    26. unlink($pidfile);
    27. }
    28. # if debug ist not set open a child process
    29. if (!$debug) {
    30. $pid = pcntl_fork();
    31. if ($pid == -1) {
    32. die("Could not fork");
    33. } else if ($pid) {
    34. // We are the parent, we may exit now.
    35. exit(0);
    36. }
    37. }
    38. $pid = pcntl_fork();
    39. if ($pid == -1) {
    40. die("Could not fork again");
    41. } else if ($pid) {
    42. pcntl_signal(SIGINT, SIG_IGN);
    43. pcntl_signal(SIGTERM, SIG_IGN);
    44. // We are the parent. Write pidfile and wait until the child exists.
    45. if (!file_put_contents($pidfile, "$pid\n")) {
    46. posix_kill($pid, 15);
    47. die("Failed to create pidfile $pidfile");
    48. }
    49. $child = pcntl_waitpid($pid, $status);
    50. unlink($pidfile);
    51. exit;
    52. }
    53. $uid = $passwd['uid'];
    54. $gid = $passwd['gid'];
    55. if (!posix_setgid($gid) or !posix_setuid($uid)) {
    56. die("Failed revoke privileges to run as user \"$user\"\n");
    57. }
    58. function sig_handler($signo)
    59. {
    60. switch ($signo) {
    61. case SIGINT:
    62. case SIGTERM:
    63. // handle shutdown tasks
    64. Logger::info("Got signal $signo, exiting");
    65. exit(128 + $signo);
    66. }
    67. }
    68. function err_handler($errno, $errstr, $errfile, $errline, $errcontext)
    69. {
    70. switch ($errno) {
    71. case E_USER_ERROR:
    72. // print and exit
    73. Logger::error("Fatal error: $errstr in $errfile on line $errline (exiting)");
    74. case E_USER_WARNING:
    75. Logger::warn("Warning: $errstr in $errfile on line $errline");
    76. break;
    77. case E_USER_NOTICE:
    78. Logger::info("Notice: $errstr in $errfile on line $errline");
    79. break;
    80. default:
    81. Logger::warn("Unknown error type $errno: $errstr in $errfile on line $errline");
    82. break;
    83. }
    84. return TRUE;
    85. }
    86. /**
    87. * basic logger
    88. *
    89. * Example usage:
    90. * @code
    91. * Logger::init(fopen("/var/log/veryimportant.log", "a"));
    92. *
    93. * Logger::info("Small info line");
    94. * Logger::warn("A little warning");
    95. * Logger::debug("Some sweet debug information");
    96. * Logger::error("Houston, we've got a (serious) problem");
    97. * @endcode
    98. */
    99. class Logger {
    100. private static $instance = NULL;
    101. private $handle = null;
    102. protected function __construct($handle) {
    103. $this->handle = $handle;
    104. register_shutdown_function(array(&$this, "__destruct"));
    105. }
    106. public function __destruct() {
    107. if($this->handle) {
    108. fclose($this->handle);
    109. }
    110. return true;
    111. }
    112. public function writeLog($level, $message) {
    113. if($this->handle) {
    114. fwrite($this->handle, sprintf("%s - %s - %s\n", date('r'), $level, $message));
    115. }
    116. }
    117. private final function __clone() {}
    118. private static function getInstance() {
    119. if (self::$instance === NULL) {
    120. throw new Exception('call init before');
    121. }
    122. return self::$instance;
    123. }
    124. public static function init($output) {
    125. self::$instance = new self($output);
    126. }
    127. public static function info($message) {
    128. self::getInstance()->writeLog('INFO', $message);
    129. }
    130. public static function warn($message) {
    131. self::getInstance()->writeLog('WARN', $message);
    132. }
    133. public static function debug($message) {
    134. self::getInstance()->writeLog('DEBUG', $message);
    135. }
    136. public static function error($message) {
    137. self::getInstance()->writeLog('ERROR', $message);
    138. exit;
    139. }
    140. }
    141. set_error_handler("err_handler");
    142. pcntl_signal(SIGINT, "sig_handler");
    143. pcntl_signal(SIGTERM, "sig_handler");
    144. # Enable the signal handle callback mechanism.
    145. declare(ticks = 1);
    146. # Open the output stream which should receive all log messages.
    147. # if debug is set just print the messages - if not, then log to file
    148. Logger::init($debug ? STDOUT : fopen("/var/log/veryimportant.log", "a"));
    149. # program code
    150. # run controller from here
    151. # e.g. Controller::dispatch()
    152. while(true) {
    153. Logger::info("i am running");
    154. sleep(1);
    155. }
    156. ?>
    Alles anzeigen

    11.771 mal gelesen