1 <?php
2 3 4 5 6 7 8 9 10 11 12
13 namespace Yiinitializr\Clio;
14 15 16 17 18 19 20
21 class Daemon
22 {
23 24 25 26 27 28 29
30 public static function work(array $options, \Closure $callable)
31 {
32 if (!extension_loaded('pcntl')) {
33 throw new \Exception('pcntl extension required');
34 }
35
36 if (!extension_loaded('posix')) {
37 throw new \Exception('posix extension required');
38 }
39
40 if (!isset($options['pid'])) {
41 throw new \Exception('pid not specified');
42 }
43
44 $options = $options + array(
45 'stdin' => '/dev/null',
46 'stdout' => '/dev/null',
47 'stderr' => 'php://stdout',
48 );
49
50 if (($lock = @fopen($options['pid'], 'c+')) === false) {
51 throw new \Exception('unable to open pid file ' . $options['pid']);
52 }
53
54 if (!flock($lock, LOCK_EX | LOCK_NB)) {
55 throw new \Exception('could not acquire lock for ' . $options['pid']);
56 }
57
58 switch ($pid = pcntl_fork()) {
59 case -1:
60 throw new \Exception('unable to fork');
61 case 0:
62 break;
63 default:
64 fseek($lock, 0);
65 ftruncate($lock, 0);
66 fwrite($lock ,$pid);
67 fflush($lock);
68 return true;
69 }
70
71 if (posix_setsid() === -1) {
72 throw new \Exception('failed to setsid');
73 }
74
75 fclose(STDIN);
76 fclose(STDOUT);
77 fclose(STDERR);
78
79 if (!($stdin = fopen($options['stdin'], 'r'))) {
80 throw new \Exception('failed to open STDIN ' . $options['stdin']);
81 }
82
83 if (!($stdout = fopen($options['stdout'], 'w'))) {
84 throw new \Exception('failed to open STDOUT ' . $options['stdout']);
85 }
86
87 if (!($stderr = fopen($options['stderr'], 'w'))) {
88 throw new \Exception('failed to open STDERR ' . $options['stderr']);
89 }
90
91 pcntl_signal(SIGTSTP, SIG_IGN);
92 pcntl_signal(SIGTTOU, SIG_IGN);
93 pcntl_signal(SIGTTIN, SIG_IGN);
94 pcntl_signal(SIGHUP, SIG_IGN);
95
96 call_user_func($callable, $stdin, $stdout, $stderr);
97 }
98
99 100 101 102 103 104
105 public static function isRunning($file)
106 {
107 if (!extension_loaded('posix')) {
108 throw new \Exception('posix extension required');
109 }
110
111 if (!is_readable($file)) {
112 return false;
113 }
114
115 if (($lock = @fopen($file, 'c+')) === false) {
116 throw new \Exception('unable to open pid file ' . $file);
117 }
118
119 if (flock($lock, LOCK_EX | LOCK_NB)) {
120 return false;
121 } else {
122 flock($lock, LOCK_UN);
123 return true;
124 }
125 }
126
127 128 129 130 131 132 133 134
135 public static function kill($file, $delete = false)
136 {
137 if (!extension_loaded('posix')) {
138 throw new \Exception('posix extension required');
139 }
140
141 if (!is_readable($file)) {
142 throw new \Exception('unreadable pid file ' . $file);
143 }
144
145 if (($lock = @fopen($file, 'c+')) === false) {
146 throw new \Exception('unable to open pid file ' . $file);
147 }
148
149 if (flock($lock, LOCK_EX | LOCK_NB)) {
150 flock($lock, LOCK_UN);
151 throw new \Exception('process not running');
152 }
153
154 $pid = fgets($lock);
155
156 if (posix_kill($pid, SIGTERM)) {
157 if ($delete) unlink($file);
158 return true;
159 } else {
160 return false;
161 }
162 }
163 }
164