You are not logged in.

Tuesday, December 15th 2009, 4:40pm

Tags

daemon, initd, Linux, linux standard base, lsb, standalone

Abstract

Dieser Wiki Artikel erläutert den Bau eines Linux Daemons, wie er im Betriebssystem eingebunden werden kann.

Article

1. 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.

Bash

1
/etc/init.d/veryimportant start


2. 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:

Bash

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


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

Bash

1
chmod 0755 /etc/init.d/veryimportant


3. 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

Bash

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:

PHP Quellcode

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

Lexikon 4.1.3, developed by www.viecode.com