├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── Exception ├── Exception.php ├── ExceptionFactory.php ├── RuntimeException.php └── UnexpectedValueException.php ├── SystemD.php ├── Watchdog.php └── functions.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | First release. 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Михаил Красильников 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-systemd 2 | 3 | Object-oriented interface to systemd. 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/mekras/php-systemd/v/stable.png)](https://packagist.org/packages/mekras/php-systemd) 6 | [![License](https://poser.pugx.org/mekras/php-systemd/license.png)](https://packagist.org/packages/mekras/php-systemd) 7 | [![Build Status](https://travis-ci.org/mekras/php-systemd.svg?branch=develop)](https://travis-ci.org/mekras/php-systemd) 8 | [![Coverage Status](https://coveralls.io/repos/mekras/php-systemd/badge.svg?branch=master&service=github)](https://coveralls.io/github/mekras/php-systemd?branch=master) 9 | 10 | 11 | ## Watchdog 12 | 13 | systemd 14 | [watchdog](https://www.freedesktop.org/software/systemd/man/systemd.service.html#WatchdogSec=) 15 | restarts frozen service. 16 | 17 | ```php 18 | use Mekras\SystemD\SystemD; 19 | 20 | $systemd = new SystemD(); 21 | $keepAlive = $systemd->watchdog()->isEnabled(); 22 | 23 | //... 24 | 25 | while (true) { 26 | if ($keepAlive) { 27 | $systemd->watchdog()->ping(); 28 | } 29 | //... 30 | } 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mekras/php-systemd", 3 | "description": "Object-oriented PHP interface to systemd", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": ["systemd"], 7 | "authors": [ 8 | { 9 | "name": "Михаил Красильников", 10 | "email": "m.krasilnikov@yandex.ru" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.5" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "^4.8", 18 | "satooshi/php-coveralls": "^1.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Mekras\\SystemD\\": "src" 23 | }, 24 | "files": ["src/functions.php"] 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Mekras\\SystemD\\Tests\\": "tests" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD\Exception; 9 | 10 | /** 11 | * SystemD exceptions interface. 12 | * 13 | * @since 1.0 14 | */ 15 | interface Exception 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exception/ExceptionFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD\Exception; 9 | 10 | /** 11 | * Exception factory. 12 | * 13 | * @since 1.0 14 | */ 15 | class ExceptionFactory 16 | { 17 | /** 18 | * Create exception from error code 19 | * 20 | * @param int $errno 21 | * 22 | * @return Exception 23 | * 24 | * @throws \InvalidArgumentException if $errno >= 0 25 | * 26 | * @since 1.0 27 | */ 28 | public static function createFromCode($errno) 29 | { 30 | if ($errno >= 0) { 31 | throw new \InvalidArgumentException( 32 | sprintf('Argument 1 for %s should be less than zero', __METHOD__) 33 | ); 34 | } 35 | if (EINVAL === $errno) { 36 | return new UnexpectedValueException('Unexpected value'); 37 | } 38 | return new RuntimeException(sprintf('Unknown error: %d', $errno)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD\Exception; 9 | 10 | /** 11 | * Runtime exception 12 | * 13 | * @since 1.0 14 | */ 15 | class RuntimeException extends \RuntimeException implements Exception 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/Exception/UnexpectedValueException.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD\Exception; 9 | 10 | /** 11 | * Unexpected value exception 12 | * 13 | * @since 1.0 14 | */ 15 | class UnexpectedValueException extends RuntimeException 16 | { 17 | } 18 | -------------------------------------------------------------------------------- /src/SystemD.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD; 9 | 10 | /** 11 | * Systemd daemon interface. 12 | * 13 | * @api 14 | * @since 1.0 15 | */ 16 | class SystemD 17 | { 18 | /** 19 | * The Watchdog service instance. 20 | * 21 | * @var Watchdog|null 22 | */ 23 | private $watchdog = null; 24 | 25 | /** 26 | * Return Watchdog instance. 27 | * 28 | * @return Watchdog 29 | * 30 | * @link https://www.freedesktop.org/software/systemd/man/systemd.service.html#WatchdogSec= 31 | * @since 1.0 32 | */ 33 | public function watchdog() 34 | { 35 | if (null === $this->watchdog) { 36 | $this->watchdog = new Watchdog(); 37 | } 38 | 39 | return $this->watchdog; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Watchdog.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD; 9 | 10 | use Mekras\SystemD\Exception\Exception; 11 | use Mekras\SystemD\Exception\ExceptionFactory; 12 | 13 | /** 14 | * The Watchdog service. 15 | * 16 | * @api 17 | * @link https://www.freedesktop.org/software/systemd/man/systemd.service.html#WatchdogSec= 18 | * @since 1.0 19 | */ 20 | class Watchdog 21 | { 22 | /** 23 | * Return Watchdog timeout. 24 | * 25 | * @return int Timeout in seconds. 0 means that watchdog not enabled. 26 | * 27 | * @throws Exception 28 | * @throws \InvalidArgumentException 29 | * 30 | * @link isEnabled() 31 | * @since 1.0 32 | */ 33 | public function getTimeout() 34 | { 35 | $sec = 0; 36 | $errno = sd_watchdog_enabled(0, $sec); 37 | if ($errno < 0) { 38 | throw ExceptionFactory::createFromCode($errno); 39 | } 40 | 41 | return $sec; 42 | } 43 | 44 | /** 45 | * Return true if Watchdog is enabled. 46 | * 47 | * @return bool 48 | * 49 | * @throws Exception 50 | * @throws \InvalidArgumentException 51 | * 52 | * @link getTimeout() 53 | * @since 1.0 54 | */ 55 | public function isEnabled() 56 | { 57 | return $this->getTimeout() > 0; 58 | } 59 | 60 | /** 61 | * Send the "keep-alive ping". 62 | * 63 | * @return void 64 | * 65 | * @throws Exception 66 | * @throws \InvalidArgumentException 67 | * 68 | * @since 1.0 69 | */ 70 | public function ping() 71 | { 72 | $errno = sd_notify(0, 'WATCHDOG=1'); 73 | if ($errno < 0) { 74 | throw ExceptionFactory::createFromCode($errno); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/functions.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://opensource.org/licenses/MIT MIT 7 | */ 8 | namespace Mekras\SystemD; 9 | 10 | /* 11 | * Maybe someday it will be added to this https://github.com/systemd/php-systemd 12 | */ 13 | 14 | if (!defined('EINVAL')) { 15 | /** 16 | * Invalid argument (POSIX.1) 17 | * 18 | * @link http://man7.org/linux/man-pages/man3/errno.3.html 19 | */ 20 | define('EINVAL', 22); 21 | } 22 | 23 | /** 24 | * sd_notify PHP implementation 25 | * 26 | * @param bool $unset_environment 27 | * @param string $state 28 | * 29 | * @return int 30 | * 31 | * @link https://www.freedesktop.org/software/systemd/man/sd_notify.html 32 | */ 33 | function sd_notify($unset_environment, $state) 34 | { 35 | return sd_pid_notify_with_fds(0, $unset_environment, $state, []); 36 | } 37 | 38 | /** 39 | * sd_pid_notify_with_fds PHP implementation 40 | * 41 | * @param int $pid FIXME currently not usable! 42 | * @param bool $unset_environment 43 | * @param string $state 44 | * @param array $fds 45 | * 46 | * @return int 47 | * 48 | * @link https://github.com/systemd/systemd/blob/master/src/libsystemd/sd-daemon/sd-daemon.c 49 | */ 50 | function sd_pid_notify_with_fds($pid, $unset_environment, $state, array $fds) 51 | { 52 | $state = trim($state); 53 | 54 | if ('' === $state) { 55 | $r = -EINVAL; 56 | goto finish; 57 | } 58 | 59 | $e = getenv('NOTIFY_SOCKET'); 60 | if (!$e) { 61 | return 0; 62 | } 63 | 64 | /* Must be an abstract socket, or an absolute path */ 65 | if (strlen($e) < 2 || (strpos($e, '@') !== 0 && strpos($e, '/') !== 0)) { 66 | $r = -EINVAL; 67 | goto finish; 68 | } 69 | 70 | $fd = socket_create(AF_UNIX, SOCK_DGRAM /* |SOCK_CLOEXEC */, 0); 71 | if (!$fd) { 72 | $r = -1 * socket_last_error(); 73 | goto finish; 74 | } 75 | 76 | $msghdr = [ 77 | 'name' => [ 78 | 'path' => $e 79 | ], 80 | 'iov' => [ 81 | $state . "\n" 82 | ], 83 | 'control' => [] 84 | ]; 85 | if (strpos($msghdr['name']['path'], '@') === 0) { 86 | $msghdr['name'][0] = "\x00"; 87 | } 88 | 89 | $pid = (int) $pid; 90 | $have_pid = $pid && getmypid() !== $pid; 91 | 92 | if (count($fds) > 0 || $have_pid) { 93 | 94 | if (count($fds)) { 95 | $msghdr['control'][] = [ 96 | 'level' => SOL_SOCKET, 97 | 'type' => SCM_RIGHTS, 98 | 'data' => $fds 99 | ]; 100 | } 101 | 102 | if ($have_pid) { 103 | $msghdr['control'][] = [ 104 | 'level' => SOL_SOCKET, 105 | 'type' => SCM_CREDENTIALS, 106 | 'data' => [ 107 | 'pid' => $pid, 108 | 'uid' => getmyuid(), 109 | 'gid' => getmygid() 110 | ] 111 | ]; 112 | } 113 | } 114 | 115 | /* First try with fake ucred data, as requested */ 116 | if (@socket_sendmsg($fd, $msghdr, MSG_NOSIGNAL) !== false) { 117 | $r = 1; 118 | goto finish; 119 | } 120 | 121 | /* If that failed, try with our own ucred instead */ 122 | if ($have_pid) { 123 | 124 | $msghdr['control'] = []; 125 | 126 | if (@socket_sendmsg($fd, $msghdr, MSG_NOSIGNAL) !== false) { 127 | $r = 1; 128 | goto finish; 129 | } 130 | } 131 | 132 | $r = -1 * socket_last_error($fd); 133 | 134 | finish: 135 | 136 | if (isset($fd) && $fd) { 137 | socket_close($fd); 138 | } 139 | 140 | if ($unset_environment) { 141 | putenv('NOTIFY_SOCKET'); 142 | } 143 | 144 | return $r; 145 | } 146 | 147 | /** 148 | * sd_watchdog_enabled PHP implementation 149 | * 150 | * @param bool $unset_environment 151 | * @param int $usec 152 | * 153 | * @return int 154 | * 155 | * @link https://github.com/systemd/systemd/blob/master/src/libsystemd/sd-daemon/sd-daemon.c 156 | */ 157 | function sd_watchdog_enabled($unset_environment, &$usec) 158 | { 159 | $r = 0; 160 | $p = null; 161 | 162 | $s = getenv('WATCHDOG_USEC'); 163 | if (!$s) { 164 | goto finish; 165 | } 166 | 167 | if (!filter_var($s, FILTER_VALIDATE_INT)) { 168 | $r = -EINVAL; 169 | goto finish; 170 | } 171 | $u = (int) $s; 172 | 173 | if ($u <= 0) { 174 | $r = -EINVAL; 175 | goto finish; 176 | } 177 | 178 | $p = getenv('WATCHDOG_PID'); 179 | if ($p) { 180 | $pid = (int) $p; 181 | if ($pid < 1) { 182 | $r = -EINVAL; 183 | goto finish; 184 | } 185 | 186 | /* Is this for us? */ 187 | if (getmypid() !== $pid) { 188 | $r = 0; 189 | goto finish; 190 | } 191 | } 192 | 193 | $usec = $u; 194 | 195 | $r = 1; 196 | 197 | finish: 198 | 199 | if ($unset_environment && $s) { 200 | putenv('WATCHDOG_USEC'); 201 | } 202 | if ($unset_environment && $p) { 203 | putenv('WATCHDOG_PID'); 204 | } 205 | 206 | return $r; 207 | } 208 | --------------------------------------------------------------------------------