Skip to content
This repository was archived by the owner on Aug 9, 2021. It is now read-only.

Commit 7f771ba

Browse files
committed
refactor(mqtt): implement php only daemon
1 parent 1b2bc4f commit 7f771ba

File tree

3 files changed

+362
-99
lines changed

3 files changed

+362
-99
lines changed

scripts/loop-run.sh

Lines changed: 0 additions & 28 deletions
This file was deleted.

scripts/service.php

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#!/usr/bin/php
2+
<?php
3+
/**
4+
* LICENSE
5+
*
6+
* Copyright © 2016-2017 Teclib'
7+
* Copyright © 2010-2016 by the FusionInventory Development Team.
8+
*
9+
* This file is part of Flyve MDM Plugin for GLPI.
10+
*
11+
* Flyve MDM Plugin for GLPI is a subproject of Flyve MDM. Flyve MDM is a mobile
12+
* device management software.
13+
*
14+
* Flyve MDM Plugin for GLPI is free software: you can redistribute it and/or
15+
* modify it under the terms of the GNU Affero General Public License as published
16+
* by the Free Software Foundation, either version 3 of the License, or
17+
* (at your option) any later version.
18+
* Flyve MDM Plugin for GLPI is distributed in the hope that it will be useful,
19+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+
* GNU Affero General Public License for more details.
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with Flyve MDM Plugin for GLPI. If not, see http://www.gnu.org/licenses/.
24+
* ------------------------------------------------------------------------------
25+
* @author Thierry Bugier Pineau
26+
* @author Volker Theile <[email protected]>
27+
* @copyright Copyright © 2017 Teclib
28+
* @license AGPLv3+ http://www.gnu.org/licenses/agpl.txt
29+
* @link https://github.com/flyve-mdm/flyve-mdm-glpi
30+
* @link https://flyve-mdm.com/
31+
* ------------------------------------------------------------------------------
32+
*/
33+
34+
/*
35+
* based on RPC daemon of Openmediavault
36+
* https://raw.githubusercontent.com/openmediavault/openmediavault/master/deb/openmediavault/usr/sbin/omv-engined
37+
*/
38+
39+
$sigTerm = false;
40+
$sigChld = false;
41+
$socket = null;
42+
$maxConnections = 10;
43+
$timeout = 1;
44+
$debug = false;
45+
$daemon = true;
46+
$xdebug = false;
47+
$pidFile = null;
48+
$children = [];
49+
50+
$stdIn = null;
51+
$stdOut = null;
52+
$stdErr = null;
53+
54+
set_error_handler("errorHandler");
55+
register_shutdown_function("shutdownHandler");
56+
57+
function main() {
58+
global $argc, $argv, $debug, $daemon, $xdebug, $pidFile;
59+
global $CFG_GLPI, $PLUGIN_HOOKS;
60+
61+
$cmdName = basename($argv[0]);
62+
$pidFile = "/var/run/$cmdName.pid";
63+
64+
// Check the command line arguments. Exit and display usage if
65+
// nessecary.
66+
$cmdArgs = [
67+
'd::' => 'debug::',
68+
'f::' => 'foreground::',
69+
'h::' => 'help::',
70+
'x::' => 'xdebug::'
71+
];
72+
$options = getopt(implode('', array_keys($cmdArgs)), $cmdArgs);
73+
foreach ($options as $optionk => $optionv) {
74+
switch ($optionk) {
75+
case 'd':
76+
case 'debug':
77+
$argc -= 1;
78+
$debug = true;
79+
break;
80+
case 'f':
81+
case 'foreground':
82+
$argc -= 1;
83+
$daemon = false;
84+
break;
85+
case 'h':
86+
case 'help':
87+
usage();
88+
exit(0);
89+
break;
90+
case 'x':
91+
case 'xdebug':
92+
$argc -= 1;
93+
$xdebug = true;
94+
break;
95+
}
96+
}
97+
if ($argc > 1) {
98+
print gettext('ERROR: Invalid number of arguments\n');
99+
usage();
100+
exit(1);
101+
}
102+
103+
ini_set('max_execution_time', '0');
104+
ini_set('max_input_time', '0');
105+
set_time_limit(0);
106+
107+
// Open syslog, include the process ID and also send the log to
108+
// standard error.
109+
openlog($cmdName, LOG_PID | LOG_PERROR, LOG_USER);
110+
111+
// Change process name.
112+
cli_set_process_title($cmdName);
113+
114+
daemonize();
115+
116+
pcntl_signal(SIGINT, 'signalHandler');
117+
pcntl_signal(SIGTERM, 'signalHandler');
118+
pcntl_signal(SIGCHLD, 'signalHandler');
119+
120+
include (__DIR__ . '/../../../inc/includes.php');
121+
122+
$mqttClient = PluginFlyvemdmMqttclient::getInstance();
123+
$mqttClient->setHandler(PluginFlyvemdmMqtthandler::getInstance());
124+
$mqttClient->subscribe();
125+
}
126+
127+
/**
128+
* Display command usage.
129+
*/
130+
function usage() {
131+
global $argv, $cmdName;
132+
133+
$text = <<<EOF
134+
The MQTT subscriber daemon. MQTT messages will be received via a socket.
135+
Usage:
136+
%s [options]
137+
138+
OPTIONS:
139+
-d --debug Enable debug mode
140+
-f --foreground Run in foreground
141+
-h --help Print a help text
142+
-x --xdebug Enable XDebug compatibility mode
143+
144+
EOF;
145+
printf($text, $cmdName);
146+
}
147+
148+
/**
149+
* Signal handler function.
150+
*
151+
* @param signal The signal.
152+
*/
153+
function signalHandler($signal) {
154+
global $sigTerm, $sigChld;
155+
156+
switch($signal) {
157+
case SIGINT:
158+
debug("SIGINT received ...\n");
159+
$sigTerm = true;
160+
break;
161+
case SIGTERM:
162+
debug("SIGTERM received ...\n");
163+
$sigTerm = true;
164+
break;
165+
case SIGCHLD:
166+
debug("SIGCHLD received ...\n");
167+
$sigChld = true;
168+
break;
169+
default:
170+
// Nothing to do here.
171+
break;
172+
}
173+
}
174+
175+
/**
176+
* Process SIGCHLD signals.
177+
*/
178+
function handleSigChld() {
179+
global $sigChld, $children;
180+
181+
$status = null;
182+
while (($pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {
183+
foreach ($children as $childk => $childv) {
184+
if ($childv !== $pid) {
185+
continue;
186+
}
187+
unset($children[$childk]);
188+
if (pcntl_wifexited($status)) {
189+
debug("Child (pid=%d) terminated with exit code %d\n",
190+
$pid, pcntl_wexitstatus($status));
191+
} else {
192+
debug("Child (pid=%d) terminated with signal %d\n",
193+
$pid, pcntl_wtermsig($status));
194+
}
195+
break;
196+
}
197+
}
198+
$sigChld = false;
199+
}
200+
201+
/**
202+
* Kill all child processes.
203+
*/
204+
function killChld() {
205+
global $children;
206+
207+
foreach($children as $childk => $childv) {
208+
if(posix_kill($childv, SIGTERM)) {
209+
debug("Send SIGTERM to child (pid=%d)\n", $childv);
210+
}
211+
}
212+
while(!empty($children)) {
213+
debug("Waiting for children to terminate ...\n");
214+
handleSigChld();
215+
usleep(1000);
216+
}
217+
}
218+
219+
/**
220+
* Daemonize the application.
221+
* @see http://www.freedesktop.org/software/systemd/man/daemon.html
222+
* @see http://stackoverflow.com/a/17955149
223+
* @see https://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon
224+
*/
225+
function daemonize() {
226+
global $debug, $daemon, $pidFile, $stdIn, $stdOut, $stdErr;
227+
228+
if($daemon === false) {
229+
return;
230+
}
231+
232+
// Check if PID file already exists and whether a daemon is already
233+
// running.
234+
if(file_exists($pidFile)) {
235+
$pid = file_get_contents($pidFile);
236+
if(posix_kill($pid, 0) === true) {
237+
error("Daemon already running (pid=%d)\n", $pid);
238+
exit(1);
239+
}
240+
unlink($pidFile);
241+
}
242+
243+
$pid = pcntl_fork();
244+
if($pid == -1) {
245+
error("Failed to fork process\n");
246+
exit(1);
247+
} else if($pid) { // Parent process
248+
exit(0);
249+
}
250+
251+
// Make the current process a session leader.
252+
if(posix_setsid() < 0) {
253+
error("Could not detach from terminal\n");
254+
exit(1);
255+
}
256+
257+
// Ignore signals.
258+
pcntl_signal(SIGHUP, SIG_IGN);
259+
260+
// If starting a process on the command line, the shell will become the
261+
// session leader of that command. To create a new process group with the
262+
// daemon as session leader it is necessary to fork a new process again.
263+
$pid = pcntl_fork();
264+
if($pid == -1) {
265+
error("Failed to fork process\n");
266+
exit(1);
267+
} else if($pid) { // Parent process
268+
debug("Daemon process started (pid=%d)\n", $pid);
269+
// Exit parent process.
270+
exit(0);
271+
}
272+
273+
// Change the current working directory.
274+
if(chdir(__DIR__) === false) {
275+
error("Failed to change current directory\n");
276+
exit(1);
277+
}
278+
279+
// Create PID file.
280+
file_put_contents($pidFile, posix_getpid());
281+
282+
if($debug === false) {
283+
// Close all of the standard file descriptors.
284+
if(is_resource(STDIN)) fclose(STDIN);
285+
if(is_resource(STDOUT)) fclose(STDOUT);
286+
if(is_resource(STDERR)) fclose(STDERR);
287+
// Create new standard file descriptors.
288+
$stdIn = fopen("/dev/null", "r");
289+
$stdOut = fopen("/dev/null", "w");
290+
$stdErr = fopen("/dev/null", "w");
291+
}
292+
}
293+
294+
/**
295+
* Error function. Output message to system log and console in debug mode.
296+
* @param msg The error message.
297+
*/
298+
function error() {
299+
global $debug;
300+
301+
$args = func_get_args();
302+
$msg = array_shift($args);
303+
// Log the message in syslog.
304+
syslog(LOG_ALERT, vsprintf($msg, $args));
305+
// Print the message to STDOUT if debug mode is enabled.
306+
if (true === $debug) {
307+
// Append a new line if necessary.
308+
if ("\n" !== substr($msg, -1))
309+
$msg .= "\n";
310+
// Print the message to STDOUT.
311+
vprintf($msg, $args);
312+
}
313+
}
314+
315+
/**
316+
* Debug function. Output message to syslog or console in debug mode.
317+
* @param msg The debug message.
318+
*/
319+
function debug() {
320+
global $debug, $daemon;
321+
322+
$args = func_get_args();
323+
$msg = array_shift($args);
324+
if (true === $debug) {
325+
if (false === $daemon) {
326+
vprintf($msg, $args);
327+
} else {
328+
syslog(LOG_DEBUG, vsprintf($msg, $args));
329+
}
330+
}
331+
}
332+
333+
/**
334+
* The error handler.
335+
*/
336+
function errorHandler($errno, $errstr, $errfile, $errline) {
337+
switch ($errno) {
338+
case E_RECOVERABLE_ERROR:
339+
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
340+
break;
341+
default:
342+
// Nothing to do here.
343+
break;
344+
}
345+
// Don't execute the PHP internal error handler.
346+
return true;
347+
}
348+
349+
/**
350+
* The function for execution on shutdown.
351+
*/
352+
function shutdownHandler() {
353+
// Check if there was a fatal error.
354+
$error = error_get_last();
355+
if (!is_null($error) && (E_ERROR == $error['type'])) {
356+
// Log fatal errors to syslog.
357+
error("PHP Fatal error: %s in %s on line %d", $error['message'],
358+
$error['file'], $error['line']);
359+
}
360+
}
361+
362+
main();

0 commit comments

Comments
 (0)