xref: /PHP-7.3/ext/pcntl/pcntl.c (revision cbb0efae)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Jason Greene <jason@inetgurus.net>                           |
16    +----------------------------------------------------------------------+
17  */
18 
19 #define PCNTL_DEBUG 0
20 
21 #if PCNTL_DEBUG
22 #define DEBUG_OUT printf("DEBUG: ");printf
23 #define IF_DEBUG(z) z
24 #else
25 #define IF_DEBUG(z)
26 #endif
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "php.h"
33 #include "php_ini.h"
34 #include "ext/standard/info.h"
35 #include "php_pcntl.h"
36 #include "php_signal.h"
37 #include "php_ticks.h"
38 
39 #if HAVE_GETPRIORITY || HAVE_SETPRIORITY || HAVE_WAIT3
40 #include <sys/wait.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #endif
44 
45 #include <errno.h>
46 
47 #ifndef NSIG
48 # ifdef SIGRTMAX
49 #  define NSIG (SIGRTMAX + 1)
50 # else
51 #  define NSIG 32
52 # endif
53 #endif
54 
55 ZEND_DECLARE_MODULE_GLOBALS(pcntl)
56 static PHP_GINIT_FUNCTION(pcntl);
57 
58 /* {{{ arginfo */
59 ZEND_BEGIN_ARG_INFO(arginfo_pcntl_void, 0)
60 ZEND_END_ARG_INFO()
61 
62 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_waitpid, 0, 0, 2)
63 	ZEND_ARG_INFO(0, pid)
64 	ZEND_ARG_INFO(1, status)
65 	ZEND_ARG_INFO(0, options)
66 	ZEND_ARG_INFO(1, rusage)
67 ZEND_END_ARG_INFO()
68 
69 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wait, 0, 0, 1)
70 	ZEND_ARG_INFO(1, status)
71 	ZEND_ARG_INFO(0, options)
72 	ZEND_ARG_INFO(1, rusage)
73 ZEND_END_ARG_INFO()
74 
75 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal, 0, 0, 2)
76 	ZEND_ARG_INFO(0, signo)
77 	ZEND_ARG_INFO(0, handler)
78 	ZEND_ARG_INFO(0, restart_syscalls)
79 ZEND_END_ARG_INFO()
80 
81 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal_get_handler, 0, 0, 1)
82 	ZEND_ARG_INFO(0, signo)
83 ZEND_END_ARG_INFO()
84 
85 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigprocmask, 0, 0, 2)
86 	ZEND_ARG_INFO(0, how)
87 	ZEND_ARG_INFO(0, set)
88 	ZEND_ARG_INFO(1, oldset)
89 ZEND_END_ARG_INFO()
90 
91 #ifdef HAVE_STRUCT_SIGINFO_T
92 # if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
93 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigwaitinfo, 0, 0, 1)
94 	ZEND_ARG_INFO(0, set)
95 	ZEND_ARG_INFO(1, info)
96 ZEND_END_ARG_INFO()
97 
98 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigtimedwait, 0, 0, 1)
99 	ZEND_ARG_INFO(0, set)
100 	ZEND_ARG_INFO(1, info)
101 	ZEND_ARG_INFO(0, seconds)
102 	ZEND_ARG_INFO(0, nanoseconds)
103 ZEND_END_ARG_INFO()
104 # endif
105 #endif
106 
107 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexited, 0, 0, 1)
108 	ZEND_ARG_INFO(0, status)
109 ZEND_END_ARG_INFO()
110 
111 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifstopped, 0, 0, 1)
112 	ZEND_ARG_INFO(0, status)
113 ZEND_END_ARG_INFO()
114 
115 #ifdef HAVE_WCONTINUED
116 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifcontinued, 0, 0, 1)
117 	ZEND_ARG_INFO(0, status)
118 ZEND_END_ARG_INFO()
119 #endif
120 
121 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifsignaled, 0, 0, 1)
122 	ZEND_ARG_INFO(0, status)
123 ZEND_END_ARG_INFO()
124 
125 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexitstatus, 0, 0, 1)
126 	ZEND_ARG_INFO(0, status)
127 ZEND_END_ARG_INFO()
128 
129 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wtermsig, 0, 0, 1)
130 	ZEND_ARG_INFO(0, status)
131 ZEND_END_ARG_INFO()
132 
133 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wstopsig, 0, 0, 1)
134 	ZEND_ARG_INFO(0, status)
135 ZEND_END_ARG_INFO()
136 
137 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_exec, 0, 0, 1)
138 	ZEND_ARG_INFO(0, path)
139 	ZEND_ARG_INFO(0, args)
140 	ZEND_ARG_INFO(0, envs)
141 ZEND_END_ARG_INFO()
142 
143 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_alarm, 0, 0, 1)
144 	ZEND_ARG_INFO(0, seconds)
145 ZEND_END_ARG_INFO()
146 
147 #ifdef HAVE_GETPRIORITY
148 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_getpriority, 0, 0, 0)
149 	ZEND_ARG_INFO(0, pid)
150 	ZEND_ARG_INFO(0, process_identifier)
151 ZEND_END_ARG_INFO()
152 #endif
153 
154 #ifdef HAVE_SETPRIORITY
155 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_setpriority, 0, 0, 1)
156 	ZEND_ARG_INFO(0, priority)
157 	ZEND_ARG_INFO(0, pid)
158 	ZEND_ARG_INFO(0, process_identifier)
159 ZEND_END_ARG_INFO()
160 #endif
161 
162 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_strerror, 0, 0, 1)
163         ZEND_ARG_INFO(0, errno)
164 ZEND_END_ARG_INFO()
165 
166 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_async_signals, 0, 0, 1)
167         ZEND_ARG_INFO(0, on)
168 ZEND_END_ARG_INFO()
169 /* }}} */
170 
171 static const zend_function_entry pcntl_functions[] = {
172 	PHP_FE(pcntl_fork,			arginfo_pcntl_void)
173 	PHP_FE(pcntl_waitpid,		arginfo_pcntl_waitpid)
174 	PHP_FE(pcntl_wait,			arginfo_pcntl_wait)
175 	PHP_FE(pcntl_signal,		arginfo_pcntl_signal)
176 	PHP_FE(pcntl_signal_get_handler,		arginfo_pcntl_signal_get_handler)
177 	PHP_FE(pcntl_signal_dispatch,	arginfo_pcntl_void)
178 	PHP_FE(pcntl_wifexited,		arginfo_pcntl_wifexited)
179 	PHP_FE(pcntl_wifstopped,	arginfo_pcntl_wifstopped)
180 	PHP_FE(pcntl_wifsignaled,	arginfo_pcntl_wifsignaled)
181 	PHP_FE(pcntl_wexitstatus,	arginfo_pcntl_wifexitstatus)
182 	PHP_FE(pcntl_wtermsig,		arginfo_pcntl_wtermsig)
183 	PHP_FE(pcntl_wstopsig,		arginfo_pcntl_wstopsig)
184 	PHP_FE(pcntl_exec,			arginfo_pcntl_exec)
185 	PHP_FE(pcntl_alarm,			arginfo_pcntl_alarm)
186 	PHP_FE(pcntl_get_last_error,	arginfo_pcntl_void)
187 	PHP_FALIAS(pcntl_errno, pcntl_get_last_error,	NULL)
188 	PHP_FE(pcntl_strerror,		arginfo_pcntl_strerror)
189 #ifdef HAVE_GETPRIORITY
190 	PHP_FE(pcntl_getpriority,	arginfo_pcntl_getpriority)
191 #endif
192 #ifdef HAVE_SETPRIORITY
193 	PHP_FE(pcntl_setpriority,	arginfo_pcntl_setpriority)
194 #endif
195 #ifdef HAVE_SIGPROCMASK
196 	PHP_FE(pcntl_sigprocmask,	arginfo_pcntl_sigprocmask)
197 #endif
198 #ifdef HAVE_STRUCT_SIGINFO_T
199 # if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
200 	PHP_FE(pcntl_sigwaitinfo,	arginfo_pcntl_sigwaitinfo)
201 	PHP_FE(pcntl_sigtimedwait,	arginfo_pcntl_sigtimedwait)
202 # endif
203 #endif
204 #ifdef HAVE_WCONTINUED
205 	PHP_FE(pcntl_wifcontinued,	arginfo_pcntl_wifcontinued)
206 #endif
207 	PHP_FE(pcntl_async_signals,	arginfo_pcntl_async_signals)
208 	PHP_FE_END
209 };
210 
211 zend_module_entry pcntl_module_entry = {
212 	STANDARD_MODULE_HEADER,
213 	"pcntl",
214 	pcntl_functions,
215 	PHP_MINIT(pcntl),
216 	PHP_MSHUTDOWN(pcntl),
217 	PHP_RINIT(pcntl),
218 	PHP_RSHUTDOWN(pcntl),
219 	PHP_MINFO(pcntl),
220 	PHP_PCNTL_VERSION,
221 	PHP_MODULE_GLOBALS(pcntl),
222 	PHP_GINIT(pcntl),
223 	NULL,
224 	NULL,
225 	STANDARD_MODULE_PROPERTIES_EX
226 };
227 
228 #ifdef COMPILE_DL_PCNTL
229 ZEND_GET_MODULE(pcntl)
230 #endif
231 
232 static void (*orig_interrupt_function)(zend_execute_data *execute_data);
233 
234 #ifdef HAVE_STRUCT_SIGINFO_T
235 static void pcntl_signal_handler(int, siginfo_t*, void*);
236 static void pcntl_siginfo_to_zval(int, siginfo_t*, zval*);
237 #else
238 static void pcntl_signal_handler(int);
239 #endif
240 static void pcntl_signal_dispatch();
241 static void pcntl_interrupt_function(zend_execute_data *execute_data);
242 
php_register_signal_constants(INIT_FUNC_ARGS)243 void php_register_signal_constants(INIT_FUNC_ARGS)
244 {
245 
246 	/* Wait Constants */
247 #ifdef WNOHANG
248 	REGISTER_LONG_CONSTANT("WNOHANG",  (zend_long) WNOHANG, CONST_CS | CONST_PERSISTENT);
249 #endif
250 #ifdef WUNTRACED
251 	REGISTER_LONG_CONSTANT("WUNTRACED",  (zend_long) WUNTRACED, CONST_CS | CONST_PERSISTENT);
252 #endif
253 #ifdef HAVE_WCONTINUED
254 	REGISTER_LONG_CONSTANT("WCONTINUED",  (zend_long) WCONTINUED, CONST_CS | CONST_PERSISTENT);
255 #endif
256 
257 	/* Signal Constants */
258 	REGISTER_LONG_CONSTANT("SIG_IGN",  (zend_long) SIG_IGN, CONST_CS | CONST_PERSISTENT);
259 	REGISTER_LONG_CONSTANT("SIG_DFL",  (zend_long) SIG_DFL, CONST_CS | CONST_PERSISTENT);
260 	REGISTER_LONG_CONSTANT("SIG_ERR",  (zend_long) SIG_ERR, CONST_CS | CONST_PERSISTENT);
261 	REGISTER_LONG_CONSTANT("SIGHUP",   (zend_long) SIGHUP,  CONST_CS | CONST_PERSISTENT);
262 	REGISTER_LONG_CONSTANT("SIGINT",   (zend_long) SIGINT,  CONST_CS | CONST_PERSISTENT);
263 	REGISTER_LONG_CONSTANT("SIGQUIT",  (zend_long) SIGQUIT, CONST_CS | CONST_PERSISTENT);
264 	REGISTER_LONG_CONSTANT("SIGILL",   (zend_long) SIGILL,  CONST_CS | CONST_PERSISTENT);
265 	REGISTER_LONG_CONSTANT("SIGTRAP",  (zend_long) SIGTRAP, CONST_CS | CONST_PERSISTENT);
266 	REGISTER_LONG_CONSTANT("SIGABRT",  (zend_long) SIGABRT, CONST_CS | CONST_PERSISTENT);
267 #ifdef SIGIOT
268 	REGISTER_LONG_CONSTANT("SIGIOT",   (zend_long) SIGIOT,  CONST_CS | CONST_PERSISTENT);
269 #endif
270 	REGISTER_LONG_CONSTANT("SIGBUS",   (zend_long) SIGBUS,  CONST_CS | CONST_PERSISTENT);
271 	REGISTER_LONG_CONSTANT("SIGFPE",   (zend_long) SIGFPE,  CONST_CS | CONST_PERSISTENT);
272 	REGISTER_LONG_CONSTANT("SIGKILL",  (zend_long) SIGKILL, CONST_CS | CONST_PERSISTENT);
273 	REGISTER_LONG_CONSTANT("SIGUSR1",  (zend_long) SIGUSR1, CONST_CS | CONST_PERSISTENT);
274 	REGISTER_LONG_CONSTANT("SIGSEGV",  (zend_long) SIGSEGV, CONST_CS | CONST_PERSISTENT);
275 	REGISTER_LONG_CONSTANT("SIGUSR2",  (zend_long) SIGUSR2, CONST_CS | CONST_PERSISTENT);
276 	REGISTER_LONG_CONSTANT("SIGPIPE",  (zend_long) SIGPIPE, CONST_CS | CONST_PERSISTENT);
277 	REGISTER_LONG_CONSTANT("SIGALRM",  (zend_long) SIGALRM, CONST_CS | CONST_PERSISTENT);
278 	REGISTER_LONG_CONSTANT("SIGTERM",  (zend_long) SIGTERM, CONST_CS | CONST_PERSISTENT);
279 #ifdef SIGSTKFLT
280 	REGISTER_LONG_CONSTANT("SIGSTKFLT",(zend_long) SIGSTKFLT, CONST_CS | CONST_PERSISTENT);
281 #endif
282 #ifdef SIGCLD
283 	REGISTER_LONG_CONSTANT("SIGCLD",   (zend_long) SIGCLD, CONST_CS | CONST_PERSISTENT);
284 #endif
285 #ifdef SIGCHLD
286 	REGISTER_LONG_CONSTANT("SIGCHLD",  (zend_long) SIGCHLD, CONST_CS | CONST_PERSISTENT);
287 #endif
288 	REGISTER_LONG_CONSTANT("SIGCONT",  (zend_long) SIGCONT, CONST_CS | CONST_PERSISTENT);
289 	REGISTER_LONG_CONSTANT("SIGSTOP",  (zend_long) SIGSTOP, CONST_CS | CONST_PERSISTENT);
290 	REGISTER_LONG_CONSTANT("SIGTSTP",  (zend_long) SIGTSTP, CONST_CS | CONST_PERSISTENT);
291 	REGISTER_LONG_CONSTANT("SIGTTIN",  (zend_long) SIGTTIN, CONST_CS | CONST_PERSISTENT);
292 	REGISTER_LONG_CONSTANT("SIGTTOU",  (zend_long) SIGTTOU, CONST_CS | CONST_PERSISTENT);
293 	REGISTER_LONG_CONSTANT("SIGURG",   (zend_long) SIGURG , CONST_CS | CONST_PERSISTENT);
294 	REGISTER_LONG_CONSTANT("SIGXCPU",  (zend_long) SIGXCPU, CONST_CS | CONST_PERSISTENT);
295 	REGISTER_LONG_CONSTANT("SIGXFSZ",  (zend_long) SIGXFSZ, CONST_CS | CONST_PERSISTENT);
296 	REGISTER_LONG_CONSTANT("SIGVTALRM",(zend_long) SIGVTALRM, CONST_CS | CONST_PERSISTENT);
297 	REGISTER_LONG_CONSTANT("SIGPROF",  (zend_long) SIGPROF, CONST_CS | CONST_PERSISTENT);
298 	REGISTER_LONG_CONSTANT("SIGWINCH", (zend_long) SIGWINCH, CONST_CS | CONST_PERSISTENT);
299 #ifdef SIGPOLL
300 	REGISTER_LONG_CONSTANT("SIGPOLL",  (zend_long) SIGPOLL, CONST_CS | CONST_PERSISTENT);
301 #endif
302 	REGISTER_LONG_CONSTANT("SIGIO",    (zend_long) SIGIO, CONST_CS | CONST_PERSISTENT);
303 #ifdef SIGPWR
304 	REGISTER_LONG_CONSTANT("SIGPWR",   (zend_long) SIGPWR, CONST_CS | CONST_PERSISTENT);
305 #endif
306 #ifdef SIGSYS
307 	REGISTER_LONG_CONSTANT("SIGSYS",   (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT);
308 	REGISTER_LONG_CONSTANT("SIGBABY",  (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT);
309 #endif
310 #ifdef SIGRTMIN
311 	REGISTER_LONG_CONSTANT("SIGRTMIN", (zend_long) SIGRTMIN, CONST_CS | CONST_PERSISTENT);
312 #endif
313 #ifdef SIGRTMAX
314 	REGISTER_LONG_CONSTANT("SIGRTMAX", (zend_long) SIGRTMAX, CONST_CS | CONST_PERSISTENT);
315 #endif
316 
317 #if HAVE_GETPRIORITY || HAVE_SETPRIORITY
318 	REGISTER_LONG_CONSTANT("PRIO_PGRP", PRIO_PGRP, CONST_CS | CONST_PERSISTENT);
319 	REGISTER_LONG_CONSTANT("PRIO_USER", PRIO_USER, CONST_CS | CONST_PERSISTENT);
320 	REGISTER_LONG_CONSTANT("PRIO_PROCESS", PRIO_PROCESS, CONST_CS | CONST_PERSISTENT);
321 #endif
322 
323 	/* {{{ "how" argument for sigprocmask */
324 #ifdef HAVE_SIGPROCMASK
325 	REGISTER_LONG_CONSTANT("SIG_BLOCK",   SIG_BLOCK, CONST_CS | CONST_PERSISTENT);
326 	REGISTER_LONG_CONSTANT("SIG_UNBLOCK", SIG_UNBLOCK, CONST_CS | CONST_PERSISTENT);
327 	REGISTER_LONG_CONSTANT("SIG_SETMASK", SIG_SETMASK, CONST_CS | CONST_PERSISTENT);
328 #endif
329 	/* }}} */
330 
331 	/* {{{ si_code */
332 #if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
333 	REGISTER_LONG_CONSTANT("SI_USER",    SI_USER,    CONST_CS | CONST_PERSISTENT);
334 #ifdef SI_NOINFO
335 	REGISTER_LONG_CONSTANT("SI_NOINFO",  SI_NOINFO,  CONST_CS | CONST_PERSISTENT);
336 #endif
337 #ifdef SI_KERNEL
338 	REGISTER_LONG_CONSTANT("SI_KERNEL",  SI_KERNEL,  CONST_CS | CONST_PERSISTENT);
339 #endif
340 	REGISTER_LONG_CONSTANT("SI_QUEUE",   SI_QUEUE,   CONST_CS | CONST_PERSISTENT);
341 	REGISTER_LONG_CONSTANT("SI_TIMER",   SI_TIMER,   CONST_CS | CONST_PERSISTENT);
342 	REGISTER_LONG_CONSTANT("SI_MESGQ",   SI_MESGQ,   CONST_CS | CONST_PERSISTENT);
343 	REGISTER_LONG_CONSTANT("SI_ASYNCIO", SI_ASYNCIO, CONST_CS | CONST_PERSISTENT);
344 #ifdef SI_SIGIO
345 	REGISTER_LONG_CONSTANT("SI_SIGIO",   SI_SIGIO,   CONST_CS | CONST_PERSISTENT);
346 #endif
347 #ifdef SI_TKILL
348 	REGISTER_LONG_CONSTANT("SI_TKILL",   SI_TKILL,   CONST_CS | CONST_PERSISTENT);
349 #endif
350 
351 	/* si_code for SIGCHILD */
352 #ifdef CLD_EXITED
353 	REGISTER_LONG_CONSTANT("CLD_EXITED",    CLD_EXITED,    CONST_CS | CONST_PERSISTENT);
354 #endif
355 #ifdef CLD_KILLED
356 	REGISTER_LONG_CONSTANT("CLD_KILLED",    CLD_KILLED,    CONST_CS | CONST_PERSISTENT);
357 #endif
358 #ifdef CLD_DUMPED
359 	REGISTER_LONG_CONSTANT("CLD_DUMPED",    CLD_DUMPED,    CONST_CS | CONST_PERSISTENT);
360 #endif
361 #ifdef CLD_TRAPPED
362 	REGISTER_LONG_CONSTANT("CLD_TRAPPED",   CLD_TRAPPED,   CONST_CS | CONST_PERSISTENT);
363 #endif
364 #ifdef CLD_STOPPED
365 	REGISTER_LONG_CONSTANT("CLD_STOPPED",   CLD_STOPPED,   CONST_CS | CONST_PERSISTENT);
366 #endif
367 #ifdef CLD_CONTINUED
368 	REGISTER_LONG_CONSTANT("CLD_CONTINUED", CLD_CONTINUED, CONST_CS | CONST_PERSISTENT);
369 #endif
370 
371 	/* si_code for SIGTRAP */
372 #ifdef TRAP_BRKPT
373 	REGISTER_LONG_CONSTANT("TRAP_BRKPT", TRAP_BRKPT, CONST_CS | CONST_PERSISTENT);
374 #endif
375 #ifdef TRAP_TRACE
376 	REGISTER_LONG_CONSTANT("TRAP_TRACE", TRAP_TRACE, CONST_CS | CONST_PERSISTENT);
377 #endif
378 
379 	/* si_code for SIGPOLL */
380 #ifdef POLL_IN
381 	REGISTER_LONG_CONSTANT("POLL_IN",  POLL_IN,  CONST_CS | CONST_PERSISTENT);
382 #endif
383 #ifdef POLL_OUT
384 	REGISTER_LONG_CONSTANT("POLL_OUT", POLL_OUT, CONST_CS | CONST_PERSISTENT);
385 #endif
386 #ifdef POLL_MSG
387 	REGISTER_LONG_CONSTANT("POLL_MSG", POLL_MSG, CONST_CS | CONST_PERSISTENT);
388 #endif
389 #ifdef POLL_ERR
390 	REGISTER_LONG_CONSTANT("POLL_ERR", POLL_ERR, CONST_CS | CONST_PERSISTENT);
391 #endif
392 #ifdef POLL_PRI
393 	REGISTER_LONG_CONSTANT("POLL_PRI", POLL_PRI, CONST_CS | CONST_PERSISTENT);
394 #endif
395 #ifdef POLL_HUP
396 	REGISTER_LONG_CONSTANT("POLL_HUP", POLL_HUP, CONST_CS | CONST_PERSISTENT);
397 #endif
398 
399 #ifdef ILL_ILLOPC
400 	REGISTER_LONG_CONSTANT("ILL_ILLOPC", ILL_ILLOPC, CONST_CS | CONST_PERSISTENT);
401 #endif
402 #ifdef ILL_ILLOPN
403 	REGISTER_LONG_CONSTANT("ILL_ILLOPN", ILL_ILLOPN, CONST_CS | CONST_PERSISTENT);
404 #endif
405 #ifdef ILL_ILLADR
406 	REGISTER_LONG_CONSTANT("ILL_ILLADR", ILL_ILLADR, CONST_CS | CONST_PERSISTENT);
407 #endif
408 #ifdef ILL_ILLTRP
409 	REGISTER_LONG_CONSTANT("ILL_ILLTRP", ILL_ILLTRP, CONST_CS | CONST_PERSISTENT);
410 #endif
411 #ifdef ILL_PRVOPC
412 	REGISTER_LONG_CONSTANT("ILL_PRVOPC", ILL_PRVOPC, CONST_CS | CONST_PERSISTENT);
413 #endif
414 #ifdef ILL_PRVREG
415 	REGISTER_LONG_CONSTANT("ILL_PRVREG", ILL_PRVREG, CONST_CS | CONST_PERSISTENT);
416 #endif
417 #ifdef ILL_COPROC
418 	REGISTER_LONG_CONSTANT("ILL_COPROC", ILL_COPROC, CONST_CS | CONST_PERSISTENT);
419 #endif
420 #ifdef ILL_BADSTK
421 	REGISTER_LONG_CONSTANT("ILL_BADSTK", ILL_BADSTK, CONST_CS | CONST_PERSISTENT);
422 #endif
423 
424 #ifdef FPE_INTDIV
425 	REGISTER_LONG_CONSTANT("FPE_INTDIV", FPE_INTDIV, CONST_CS | CONST_PERSISTENT);
426 #endif
427 #ifdef FPE_INTOVF
428 	REGISTER_LONG_CONSTANT("FPE_INTOVF", FPE_INTOVF, CONST_CS | CONST_PERSISTENT);
429 #endif
430 #ifdef FPE_FLTDIV
431 	REGISTER_LONG_CONSTANT("FPE_FLTDIV", FPE_FLTDIV, CONST_CS | CONST_PERSISTENT);
432 #endif
433 #ifdef FPE_FLTOVF
434 	REGISTER_LONG_CONSTANT("FPE_FLTOVF", FPE_FLTOVF, CONST_CS | CONST_PERSISTENT);
435 #endif
436 #ifdef FPE_FLTUND
437 	REGISTER_LONG_CONSTANT("FPE_FLTUND", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
438 #endif
439 #ifdef FPE_FLTRES
440 	REGISTER_LONG_CONSTANT("FPE_FLTRES", FPE_FLTRES, CONST_CS | CONST_PERSISTENT);
441 #endif
442 #ifdef FPE_FLTINV
443 	REGISTER_LONG_CONSTANT("FPE_FLTINV", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
444 #endif
445 #ifdef FPE_FLTSUB
446 	REGISTER_LONG_CONSTANT("FPE_FLTSUB", FPE_FLTSUB, CONST_CS | CONST_PERSISTENT);
447 #endif
448 
449 #ifdef SEGV_MAPERR
450 	REGISTER_LONG_CONSTANT("SEGV_MAPERR", SEGV_MAPERR, CONST_CS | CONST_PERSISTENT);
451 #endif
452 #ifdef SEGV_ACCERR
453 	REGISTER_LONG_CONSTANT("SEGV_ACCERR", SEGV_ACCERR, CONST_CS | CONST_PERSISTENT);
454 #endif
455 
456 #ifdef BUS_ADRALN
457 	REGISTER_LONG_CONSTANT("BUS_ADRALN", BUS_ADRALN, CONST_CS | CONST_PERSISTENT);
458 #endif
459 #ifdef BUS_ADRERR
460 	REGISTER_LONG_CONSTANT("BUS_ADRERR", BUS_ADRERR, CONST_CS | CONST_PERSISTENT);
461 #endif
462 #ifdef BUS_OBJERR
463 	REGISTER_LONG_CONSTANT("BUS_OBJERR", BUS_OBJERR, CONST_CS | CONST_PERSISTENT);
464 #endif
465 #endif /* HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT */
466 	/* }}} */
467 }
468 
php_pcntl_register_errno_constants(INIT_FUNC_ARGS)469 static void php_pcntl_register_errno_constants(INIT_FUNC_ARGS)
470 {
471 #ifdef EINTR
472 	REGISTER_PCNTL_ERRNO_CONSTANT(EINTR);
473 #endif
474 #ifdef ECHILD
475 	REGISTER_PCNTL_ERRNO_CONSTANT(ECHILD);
476 #endif
477 #ifdef EINVAL
478 	REGISTER_PCNTL_ERRNO_CONSTANT(EINVAL);
479 #endif
480 #ifdef EAGAIN
481 	REGISTER_PCNTL_ERRNO_CONSTANT(EAGAIN);
482 #endif
483 #ifdef ESRCH
484 	REGISTER_PCNTL_ERRNO_CONSTANT(ESRCH);
485 #endif
486 #ifdef EACCES
487 	REGISTER_PCNTL_ERRNO_CONSTANT(EACCES);
488 #endif
489 #ifdef EPERM
490 	REGISTER_PCNTL_ERRNO_CONSTANT(EPERM);
491 #endif
492 #ifdef ENOMEM
493 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOMEM);
494 #endif
495 #ifdef E2BIG
496 	REGISTER_PCNTL_ERRNO_CONSTANT(E2BIG);
497 #endif
498 #ifdef EFAULT
499 	REGISTER_PCNTL_ERRNO_CONSTANT(EFAULT);
500 #endif
501 #ifdef EIO
502 	REGISTER_PCNTL_ERRNO_CONSTANT(EIO);
503 #endif
504 #ifdef EISDIR
505 	REGISTER_PCNTL_ERRNO_CONSTANT(EISDIR);
506 #endif
507 #ifdef ELIBBAD
508 	REGISTER_PCNTL_ERRNO_CONSTANT(ELIBBAD);
509 #endif
510 #ifdef ELOOP
511 	REGISTER_PCNTL_ERRNO_CONSTANT(ELOOP);
512 #endif
513 #ifdef EMFILE
514 	REGISTER_PCNTL_ERRNO_CONSTANT(EMFILE);
515 #endif
516 #ifdef ENAMETOOLONG
517 	REGISTER_PCNTL_ERRNO_CONSTANT(ENAMETOOLONG);
518 #endif
519 #ifdef ENFILE
520 	REGISTER_PCNTL_ERRNO_CONSTANT(ENFILE);
521 #endif
522 #ifdef ENOENT
523 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOENT);
524 #endif
525 #ifdef ENOEXEC
526 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOEXEC);
527 #endif
528 #ifdef ENOTDIR
529 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOTDIR);
530 #endif
531 #ifdef ETXTBSY
532 	REGISTER_PCNTL_ERRNO_CONSTANT(ETXTBSY);
533 #endif
534 }
535 
PHP_GINIT_FUNCTION(pcntl)536 static PHP_GINIT_FUNCTION(pcntl)
537 {
538 	memset(pcntl_globals, 0, sizeof(*pcntl_globals));
539 }
540 
PHP_RINIT_FUNCTION(pcntl)541 PHP_RINIT_FUNCTION(pcntl)
542 {
543 	zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0);
544 	PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL;
545 	PCNTL_G(async_signals) = 0;
546 	return SUCCESS;
547 }
548 
PHP_MINIT_FUNCTION(pcntl)549 PHP_MINIT_FUNCTION(pcntl)
550 {
551 	php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
552 	php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
553 	php_add_tick_function(pcntl_signal_dispatch, NULL);
554 	orig_interrupt_function = zend_interrupt_function;
555 	zend_interrupt_function = pcntl_interrupt_function;
556 
557 	return SUCCESS;
558 }
559 
PHP_MSHUTDOWN_FUNCTION(pcntl)560 PHP_MSHUTDOWN_FUNCTION(pcntl)
561 {
562 	return SUCCESS;
563 }
564 
PHP_RSHUTDOWN_FUNCTION(pcntl)565 PHP_RSHUTDOWN_FUNCTION(pcntl)
566 {
567 	struct php_pcntl_pending_signal *sig;
568 
569 	/* FIXME: if a signal is delivered after this point, things will go pear shaped;
570 	 * need to remove signal handlers */
571 	zend_hash_destroy(&PCNTL_G(php_signal_table));
572 	while (PCNTL_G(head)) {
573 		sig = PCNTL_G(head);
574 		PCNTL_G(head) = sig->next;
575 		efree(sig);
576 	}
577 	while (PCNTL_G(spares)) {
578 		sig = PCNTL_G(spares);
579 		PCNTL_G(spares) = sig->next;
580 		efree(sig);
581 	}
582 	return SUCCESS;
583 }
584 
PHP_MINFO_FUNCTION(pcntl)585 PHP_MINFO_FUNCTION(pcntl)
586 {
587 	php_info_print_table_start();
588 	php_info_print_table_header(2, "pcntl support", "enabled");
589 	php_info_print_table_end();
590 }
591 
592 /* {{{ proto int pcntl_fork(void)
593    Forks the currently running process following the same behavior as the UNIX fork() system call*/
PHP_FUNCTION(pcntl_fork)594 PHP_FUNCTION(pcntl_fork)
595 {
596 	pid_t id;
597 
598 	id = fork();
599 	if (id == -1) {
600 		PCNTL_G(last_error) = errno;
601 		php_error_docref(NULL, E_WARNING, "Error %d", errno);
602 	}
603 
604 	RETURN_LONG((zend_long) id);
605 }
606 /* }}} */
607 
608 /* {{{ proto int pcntl_alarm(int seconds)
609    Set an alarm clock for delivery of a signal*/
PHP_FUNCTION(pcntl_alarm)610 PHP_FUNCTION(pcntl_alarm)
611 {
612 	zend_long seconds;
613 
614 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &seconds) == FAILURE)
615 		return;
616 
617 	RETURN_LONG ((zend_long) alarm(seconds));
618 }
619 /* }}} */
620 
621 #define PHP_RUSAGE_PARA(from, to, field) \
622 	add_assoc_long(to, #field, from.field)
623 #ifndef _OSD_POSIX
624 	#define PHP_RUSAGE_SPECIAL(from, to) \
625 		PHP_RUSAGE_PARA(from, to, ru_oublock); \
626 		PHP_RUSAGE_PARA(from, to, ru_inblock); \
627 		PHP_RUSAGE_PARA(from, to, ru_msgsnd); \
628 		PHP_RUSAGE_PARA(from, to, ru_msgrcv); \
629 		PHP_RUSAGE_PARA(from, to, ru_maxrss); \
630 		PHP_RUSAGE_PARA(from, to, ru_ixrss); \
631 		PHP_RUSAGE_PARA(from, to, ru_idrss); \
632 		PHP_RUSAGE_PARA(from, to, ru_minflt); \
633 		PHP_RUSAGE_PARA(from, to, ru_majflt); \
634 		PHP_RUSAGE_PARA(from, to, ru_nsignals); \
635 		PHP_RUSAGE_PARA(from, to, ru_nvcsw); \
636 		PHP_RUSAGE_PARA(from, to, ru_nivcsw); \
637 		PHP_RUSAGE_PARA(from, to, ru_nswap);
638 #else /*_OSD_POSIX*/
639 	#define PHP_RUSAGE_SPECIAL(from, to)
640 #endif
641 
642 #define PHP_RUSAGE_COMMON(from ,to) \
643 	PHP_RUSAGE_PARA(from, to, ru_utime.tv_usec); \
644 	PHP_RUSAGE_PARA(from, to, ru_utime.tv_sec); \
645 	PHP_RUSAGE_PARA(from, to, ru_stime.tv_usec); \
646 	PHP_RUSAGE_PARA(from, to, ru_stime.tv_sec);
647 
648 #define PHP_RUSAGE_TO_ARRAY(from, to) \
649 	if (to) { \
650 		PHP_RUSAGE_SPECIAL(from, to) \
651 		PHP_RUSAGE_COMMON(from, to); \
652 	}
653 
654 /* {{{ proto int pcntl_waitpid(int pid, int &status, int options, array &$rusage)
655    Waits on or returns the status of a forked child as defined by the waitpid() system call */
PHP_FUNCTION(pcntl_waitpid)656 PHP_FUNCTION(pcntl_waitpid)
657 {
658 	zend_long pid, options = 0;
659 	zval *z_status = NULL, *z_rusage = NULL;
660 	int status;
661 	pid_t child_id;
662 #ifdef HAVE_WAIT4
663 	struct rusage rusage;
664 #endif
665 
666 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/|lz/", &pid, &z_status, &options, &z_rusage) == FAILURE) {
667 		return;
668 	}
669 
670 	status = zval_get_long(z_status);
671 
672 #ifdef HAVE_WAIT4
673 	if (z_rusage) {
674 		if (Z_TYPE_P(z_rusage) != IS_ARRAY) {
675 			zval_ptr_dtor(z_rusage);
676 			array_init(z_rusage);
677 		} else {
678 			zend_hash_clean(Z_ARRVAL_P(z_rusage));
679 		}
680 
681 		memset(&rusage, 0, sizeof(struct rusage));
682 		child_id = wait4((pid_t) pid, &status, options, &rusage);
683 	} else {
684 		child_id = waitpid((pid_t) pid, &status, options);
685 	}
686 #else
687 	child_id = waitpid((pid_t) pid, &status, options);
688 #endif
689 
690 	if (child_id < 0) {
691 		PCNTL_G(last_error) = errno;
692 	}
693 
694 #ifdef HAVE_WAIT4
695 	if (child_id > 0) {
696 		PHP_RUSAGE_TO_ARRAY(rusage, z_rusage);
697 	}
698 #endif
699 
700 	zval_ptr_dtor(z_status);
701 	ZVAL_LONG(z_status, status);
702 
703 	RETURN_LONG((zend_long) child_id);
704 }
705 /* }}} */
706 
707 /* {{{ proto int pcntl_wait(int &status, int $options, array &$rusage)
708    Waits on or returns the status of a forked child as defined by the waitpid() system call */
PHP_FUNCTION(pcntl_wait)709 PHP_FUNCTION(pcntl_wait)
710 {
711 	zend_long options = 0;
712 	zval *z_status = NULL, *z_rusage = NULL;
713 	int status;
714 	pid_t child_id;
715 #ifdef HAVE_WAIT3
716 	struct rusage rusage;
717 #endif
718 
719 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/|lz/", &z_status, &options, &z_rusage) == FAILURE) {
720 		return;
721 	}
722 
723 	status = zval_get_long(z_status);
724 #ifdef HAVE_WAIT3
725 	if (z_rusage) {
726 		if (Z_TYPE_P(z_rusage) != IS_ARRAY) {
727 			zval_ptr_dtor(z_rusage);
728 			array_init(z_rusage);
729 		} else {
730 			zend_hash_clean(Z_ARRVAL_P(z_rusage));
731 		}
732 
733 		memset(&rusage, 0, sizeof(struct rusage));
734 		child_id = wait3(&status, options, &rusage);
735 	} else if (options) {
736 		child_id = wait3(&status, options, NULL);
737 	} else {
738 		child_id = wait(&status);
739 	}
740 #else
741 	child_id = wait(&status);
742 #endif
743 	if (child_id < 0) {
744 		PCNTL_G(last_error) = errno;
745 	}
746 
747 #ifdef HAVE_WAIT3
748 	if (child_id > 0) {
749 		PHP_RUSAGE_TO_ARRAY(rusage, z_rusage);
750 	}
751 #endif
752 
753 	zval_ptr_dtor(z_status);
754 	ZVAL_LONG(z_status, status);
755 
756 	RETURN_LONG((zend_long) child_id);
757 }
758 /* }}} */
759 
760 #undef PHP_RUSAGE_PARA
761 #undef PHP_RUSAGE_SPECIAL
762 #undef PHP_RUSAGE_COMMON
763 #undef PHP_RUSAGE_TO_ARRAY
764 
765 /* {{{ proto bool pcntl_wifexited(int status)
766    Returns true if the child status code represents a successful exit */
PHP_FUNCTION(pcntl_wifexited)767 PHP_FUNCTION(pcntl_wifexited)
768 {
769 #ifdef WIFEXITED
770 	zend_long status_word;
771 	int int_status_word;
772 
773 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
774 	       return;
775 	}
776 
777 	int_status_word = (int) status_word;
778 	if (WIFEXITED(int_status_word))
779 		RETURN_TRUE;
780 #endif
781 
782 	RETURN_FALSE;
783 }
784 /* }}} */
785 
786 /* {{{ proto bool pcntl_wifstopped(int status)
787    Returns true if the child status code represents a stopped process (WUNTRACED must have been used with waitpid) */
PHP_FUNCTION(pcntl_wifstopped)788 PHP_FUNCTION(pcntl_wifstopped)
789 {
790 #ifdef WIFSTOPPED
791 	zend_long status_word;
792 	int int_status_word;
793 
794 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
795 	       return;
796 	}
797 
798 	int_status_word = (int) status_word;
799 	if (WIFSTOPPED(int_status_word))
800 		RETURN_TRUE;
801 #endif
802 	RETURN_FALSE;
803 }
804 /* }}} */
805 
806 /* {{{ proto bool pcntl_wifsignaled(int status)
807    Returns true if the child status code represents a process that was terminated due to a signal */
PHP_FUNCTION(pcntl_wifsignaled)808 PHP_FUNCTION(pcntl_wifsignaled)
809 {
810 #ifdef WIFSIGNALED
811 	zend_long status_word;
812 	int int_status_word;
813 
814 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
815 	       return;
816 	}
817 
818 	int_status_word = (int) status_word;
819 	if (WIFSIGNALED(int_status_word))
820 		RETURN_TRUE;
821 #endif
822 	RETURN_FALSE;
823 }
824 /* }}} */
825 /* {{{ proto bool pcntl_wifcontinued(int status)
826    Returns true if the child status code represents a process that was resumed due to a SIGCONT signal */
PHP_FUNCTION(pcntl_wifcontinued)827 PHP_FUNCTION(pcntl_wifcontinued)
828 {
829 #ifdef HAVE_WCONTINUED
830 	zend_long status_word;
831 	int int_status_word;
832 
833 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
834 	       return;
835 	}
836 
837 	int_status_word = (int) status_word;
838 	if (WIFCONTINUED(int_status_word))
839 		RETURN_TRUE;
840 #endif
841 	RETURN_FALSE;
842 }
843 /* }}} */
844 
845 
846 /* {{{ proto int pcntl_wexitstatus(int status)
847    Returns the status code of a child's exit */
PHP_FUNCTION(pcntl_wexitstatus)848 PHP_FUNCTION(pcntl_wexitstatus)
849 {
850 #ifdef WEXITSTATUS
851 	zend_long status_word;
852 	int int_status_word;
853 
854 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
855 	       return;
856 	}
857 
858 	int_status_word = (int) status_word;
859 	RETURN_LONG(WEXITSTATUS(int_status_word));
860 #else
861 	RETURN_FALSE;
862 #endif
863 }
864 /* }}} */
865 
866 /* {{{ proto int pcntl_wtermsig(int status)
867    Returns the number of the signal that terminated the process who's status code is passed  */
PHP_FUNCTION(pcntl_wtermsig)868 PHP_FUNCTION(pcntl_wtermsig)
869 {
870 #ifdef WTERMSIG
871 	zend_long status_word;
872 	int int_status_word;
873 
874 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
875 	       return;
876 	}
877 
878 	int_status_word = (int) status_word;
879 	RETURN_LONG(WTERMSIG(int_status_word));
880 #else
881 	RETURN_FALSE;
882 #endif
883 }
884 /* }}} */
885 
886 /* {{{ proto int pcntl_wstopsig(int status)
887    Returns the number of the signal that caused the process to stop who's status code is passed */
PHP_FUNCTION(pcntl_wstopsig)888 PHP_FUNCTION(pcntl_wstopsig)
889 {
890 #ifdef WSTOPSIG
891 	zend_long status_word;
892 	int int_status_word;
893 
894 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
895 	       return;
896 	}
897 
898 	int_status_word = (int) status_word;
899 	RETURN_LONG(WSTOPSIG(int_status_word));
900 #else
901 	RETURN_FALSE;
902 #endif
903 }
904 /* }}} */
905 
906 /* {{{ proto bool pcntl_exec(string path [, array args [, array envs]])
907    Executes specified program in current process space as defined by exec(2) */
PHP_FUNCTION(pcntl_exec)908 PHP_FUNCTION(pcntl_exec)
909 {
910 	zval *args = NULL, *envs = NULL;
911 	zval *element;
912 	HashTable *args_hash, *envs_hash;
913 	int argc = 0, argi = 0;
914 	int envc = 0, envi = 0;
915 	char **argv = NULL, **envp = NULL;
916 	char **current_arg, **pair;
917 	int pair_length;
918 	zend_string *key;
919 	char *path;
920 	size_t path_len;
921 	zend_ulong key_num;
922 
923 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|aa", &path, &path_len, &args, &envs) == FAILURE) {
924 		return;
925 	}
926 
927 	if (ZEND_NUM_ARGS() > 1) {
928 		/* Build argument list */
929 		args_hash = Z_ARRVAL_P(args);
930 		argc = zend_hash_num_elements(args_hash);
931 
932 		argv = safe_emalloc((argc + 2), sizeof(char *), 0);
933 		*argv = path;
934 		current_arg = argv+1;
935 		ZEND_HASH_FOREACH_VAL(args_hash, element) {
936 			if (argi >= argc) break;
937 			convert_to_string_ex(element);
938 			*current_arg = Z_STRVAL_P(element);
939 			argi++;
940 			current_arg++;
941 		} ZEND_HASH_FOREACH_END();
942 		*(current_arg) = NULL;
943 	} else {
944 		argv = emalloc(2 * sizeof(char *));
945 		*argv = path;
946 		*(argv+1) = NULL;
947 	}
948 
949 	if ( ZEND_NUM_ARGS() == 3 ) {
950 		/* Build environment pair list */
951 		envs_hash = Z_ARRVAL_P(envs);
952 		envc = zend_hash_num_elements(envs_hash);
953 
954 		pair = envp = safe_emalloc((envc + 1), sizeof(char *), 0);
955 		ZEND_HASH_FOREACH_KEY_VAL(envs_hash, key_num, key, element) {
956 			if (envi >= envc) break;
957 			if (!key) {
958 				key = zend_long_to_str(key_num);
959 			} else {
960 				zend_string_addref(key);
961 			}
962 
963 			convert_to_string_ex(element);
964 
965 			/* Length of element + equal sign + length of key + null */
966 			pair_length = Z_STRLEN_P(element) + ZSTR_LEN(key) + 2;
967 			*pair = emalloc(pair_length);
968 			strlcpy(*pair, ZSTR_VAL(key), ZSTR_LEN(key) + 1);
969 			strlcat(*pair, "=", pair_length);
970 			strlcat(*pair, Z_STRVAL_P(element), pair_length);
971 
972 			/* Cleanup */
973 			zend_string_release_ex(key, 0);
974 			envi++;
975 			pair++;
976 		} ZEND_HASH_FOREACH_END();
977 		*(pair) = NULL;
978 
979 		if (execve(path, argv, envp) == -1) {
980 			PCNTL_G(last_error) = errno;
981 			php_error_docref(NULL, E_WARNING, "Error has occurred: (errno %d) %s", errno, strerror(errno));
982 		}
983 
984 		/* Cleanup */
985 		for (pair = envp; *pair != NULL; pair++) efree(*pair);
986 		efree(envp);
987 	} else {
988 
989 		if (execv(path, argv) == -1) {
990 			PCNTL_G(last_error) = errno;
991 			php_error_docref(NULL, E_WARNING, "Error has occurred: (errno %d) %s", errno, strerror(errno));
992 		}
993 	}
994 
995 	efree(argv);
996 
997 	RETURN_FALSE;
998 }
999 /* }}} */
1000 
1001 /* {{{ proto bool pcntl_signal(int signo, callback handle [, bool restart_syscalls])
1002    Assigns a system signal handler to a PHP function */
PHP_FUNCTION(pcntl_signal)1003 PHP_FUNCTION(pcntl_signal)
1004 {
1005 	zval *handle;
1006 	zend_long signo;
1007 	zend_bool restart_syscalls = 1;
1008 	char *error = NULL;
1009 
1010 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b", &signo, &handle, &restart_syscalls) == FAILURE) {
1011 		return;
1012 	}
1013 
1014 	if (signo < 1 || signo >= NSIG) {
1015 		php_error_docref(NULL, E_WARNING, "Invalid signal");
1016 		RETURN_FALSE;
1017 	}
1018 
1019 	if (!PCNTL_G(spares)) {
1020 		/* since calling malloc() from within a signal handler is not portable,
1021 		 * pre-allocate a few records for recording signals */
1022 		int i;
1023 		for (i = 0; i < NSIG; i++) {
1024 			struct php_pcntl_pending_signal *psig;
1025 
1026 			psig = emalloc(sizeof(*psig));
1027 			psig->next = PCNTL_G(spares);
1028 			PCNTL_G(spares) = psig;
1029 		}
1030 	}
1031 
1032 	/* Special long value case for SIG_DFL and SIG_IGN */
1033 	if (Z_TYPE_P(handle) == IS_LONG) {
1034 		if (Z_LVAL_P(handle) != (zend_long) SIG_DFL && Z_LVAL_P(handle) != (zend_long) SIG_IGN) {
1035 			php_error_docref(NULL, E_WARNING, "Invalid value for handle argument specified");
1036 			RETURN_FALSE;
1037 		}
1038 		if (php_signal(signo, (Sigfunc *) Z_LVAL_P(handle), (int) restart_syscalls) == (Sigfunc *)SIG_ERR) {
1039 			PCNTL_G(last_error) = errno;
1040 			php_error_docref(NULL, E_WARNING, "Error assigning signal");
1041 			RETURN_FALSE;
1042 		}
1043 		zend_hash_index_update(&PCNTL_G(php_signal_table), signo, handle);
1044 		RETURN_TRUE;
1045 	}
1046 
1047 	if (!zend_is_callable_ex(handle, NULL, 0, NULL, NULL, &error)) {
1048 		zend_string *func_name = zend_get_callable_name(handle);
1049 		PCNTL_G(last_error) = EINVAL;
1050 		php_error_docref(NULL, E_WARNING, "Specified handler \"%s\" is not callable (%s)", ZSTR_VAL(func_name), error);
1051 		zend_string_release_ex(func_name, 0);
1052 		efree(error);
1053 		RETURN_FALSE;
1054 	}
1055 	ZEND_ASSERT(!error);
1056 
1057 	/* Add the function name to our signal table */
1058 	handle = zend_hash_index_update(&PCNTL_G(php_signal_table), signo, handle);
1059 	Z_TRY_ADDREF_P(handle);
1060 
1061 	if (php_signal4(signo, pcntl_signal_handler, (int) restart_syscalls, 1) == (Sigfunc *)SIG_ERR) {
1062 		PCNTL_G(last_error) = errno;
1063 		php_error_docref(NULL, E_WARNING, "Error assigning signal");
1064 		RETURN_FALSE;
1065 	}
1066 	RETURN_TRUE;
1067 }
1068 /* }}} */
1069 
1070 /* {{{ proto bool pcntl_signal_get_handler(int signo)
1071    Gets signal handler */
PHP_FUNCTION(pcntl_signal_get_handler)1072 PHP_FUNCTION(pcntl_signal_get_handler)
1073 {
1074 	zval *prev_handle;
1075 	zend_long signo;
1076 
1077 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &signo) == FAILURE) {
1078 		return;
1079 	}
1080 
1081 	if (signo < 1 || signo > 32) {
1082 		php_error_docref(NULL, E_WARNING, "Invalid signal");
1083 		RETURN_FALSE;
1084 	}
1085 
1086 	if ((prev_handle = zend_hash_index_find(&PCNTL_G(php_signal_table), signo)) != NULL) {
1087 		RETURN_ZVAL(prev_handle, 1, 0);
1088 	} else {
1089 		RETURN_LONG((zend_long)SIG_DFL);
1090 	}
1091 }
1092 
1093 /* {{{ proto bool pcntl_signal_dispatch()
1094    Dispatch signals to signal handlers */
PHP_FUNCTION(pcntl_signal_dispatch)1095 PHP_FUNCTION(pcntl_signal_dispatch)
1096 {
1097 	pcntl_signal_dispatch();
1098 	RETURN_TRUE;
1099 }
1100 /* }}} */
1101 
1102 #ifdef HAVE_SIGPROCMASK
1103 /* {{{ proto bool pcntl_sigprocmask(int how, array set[, array &oldset])
1104    Examine and change blocked signals */
PHP_FUNCTION(pcntl_sigprocmask)1105 PHP_FUNCTION(pcntl_sigprocmask)
1106 {
1107 	zend_long          how, signo;
1108 	zval         *user_set, *user_oldset = NULL, *user_signo;
1109 	sigset_t      set, oldset;
1110 
1111 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "la|z/", &how, &user_set, &user_oldset) == FAILURE) {
1112 		return;
1113 	}
1114 
1115 	if (sigemptyset(&set) != 0 || sigemptyset(&oldset) != 0) {
1116 		PCNTL_G(last_error) = errno;
1117 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1118 		RETURN_FALSE;
1119 	}
1120 
1121 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(user_set), user_signo) {
1122 		signo = zval_get_long(user_signo);
1123 		if (sigaddset(&set, signo) != 0) {
1124 			PCNTL_G(last_error) = errno;
1125 			php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1126 			RETURN_FALSE;
1127 		}
1128 	} ZEND_HASH_FOREACH_END();
1129 
1130 	if (sigprocmask(how, &set, &oldset) != 0) {
1131 		PCNTL_G(last_error) = errno;
1132 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1133 		RETURN_FALSE;
1134 	}
1135 
1136 	if (user_oldset != NULL) {
1137 		if (Z_TYPE_P(user_oldset) != IS_ARRAY) {
1138 			zval_ptr_dtor(user_oldset);
1139 			array_init(user_oldset);
1140 		} else {
1141 			zend_hash_clean(Z_ARRVAL_P(user_oldset));
1142 		}
1143 		for (signo = 1; signo < NSIG; ++signo) {
1144 			if (sigismember(&oldset, signo) != 1) {
1145 				continue;
1146 			}
1147 			add_next_index_long(user_oldset, signo);
1148 		}
1149 	}
1150 
1151 	RETURN_TRUE;
1152 }
1153 /* }}} */
1154 #endif
1155 
1156 #ifdef HAVE_STRUCT_SIGINFO_T
1157 # if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS,int timedwait)1158 static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{{ */
1159 {
1160 	zval            *user_set, *user_signo, *user_siginfo = NULL;
1161 	zend_long             tv_sec = 0, tv_nsec = 0;
1162 	sigset_t         set;
1163 	int              signo;
1164 	siginfo_t        siginfo;
1165 	struct timespec  timeout;
1166 
1167 	if (timedwait) {
1168 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|z/ll", &user_set, &user_siginfo, &tv_sec, &tv_nsec) == FAILURE) {
1169 			return;
1170 		}
1171 	} else {
1172 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|z/", &user_set, &user_siginfo) == FAILURE) {
1173 			return;
1174 		}
1175 	}
1176 
1177 	if (sigemptyset(&set) != 0) {
1178 		PCNTL_G(last_error) = errno;
1179 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1180 		RETURN_FALSE;
1181 	}
1182 
1183 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(user_set), user_signo) {
1184 		signo = zval_get_long(user_signo);
1185 		if (sigaddset(&set, signo) != 0) {
1186 			PCNTL_G(last_error) = errno;
1187 			php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1188 			RETURN_FALSE;
1189 		}
1190 	} ZEND_HASH_FOREACH_END();
1191 
1192 	if (timedwait) {
1193 		timeout.tv_sec  = (time_t) tv_sec;
1194 		timeout.tv_nsec = tv_nsec;
1195 		signo = sigtimedwait(&set, &siginfo, &timeout);
1196 	} else {
1197 		signo = sigwaitinfo(&set, &siginfo);
1198 	}
1199 	if (signo == -1 && errno != EAGAIN) {
1200 		PCNTL_G(last_error) = errno;
1201 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1202 	}
1203 
1204 	/*
1205 	 * sigtimedwait and sigwaitinfo can return 0 on success on some
1206 	 * platforms, e.g. NetBSD
1207 	 */
1208 	if (!signo && siginfo.si_signo) {
1209 		signo = siginfo.si_signo;
1210 	}
1211 	pcntl_siginfo_to_zval(signo, &siginfo, user_siginfo);
1212 	RETURN_LONG(signo);
1213 }
1214 /* }}} */
1215 
1216 /* {{{ proto int pcnlt_sigwaitinfo(array set[, array &siginfo])
1217    Synchronously wait for queued signals */
PHP_FUNCTION(pcntl_sigwaitinfo)1218 PHP_FUNCTION(pcntl_sigwaitinfo)
1219 {
1220 	pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1221 }
1222 /* }}} */
1223 
1224 /* {{{ proto int pcntl_sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])
1225    Wait for queued signals */
PHP_FUNCTION(pcntl_sigtimedwait)1226 PHP_FUNCTION(pcntl_sigtimedwait)
1227 {
1228 	pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1229 }
1230 /* }}} */
1231 # endif
1232 
pcntl_siginfo_to_zval(int signo,siginfo_t * siginfo,zval * user_siginfo)1233 static void pcntl_siginfo_to_zval(int signo, siginfo_t *siginfo, zval *user_siginfo) /* {{{ */
1234 {
1235 	if (signo > 0 && user_siginfo) {
1236 		if (Z_TYPE_P(user_siginfo) != IS_ARRAY) {
1237 			zval_ptr_dtor(user_siginfo);
1238 			array_init(user_siginfo);
1239 		} else {
1240 			zend_hash_clean(Z_ARRVAL_P(user_siginfo));
1241 		}
1242 		add_assoc_long_ex(user_siginfo, "signo", sizeof("signo")-1, siginfo->si_signo);
1243 		add_assoc_long_ex(user_siginfo, "errno", sizeof("errno")-1, siginfo->si_errno);
1244 		add_assoc_long_ex(user_siginfo, "code",  sizeof("code")-1,  siginfo->si_code);
1245 		switch(signo) {
1246 #ifdef SIGCHLD
1247 			case SIGCHLD:
1248 				add_assoc_long_ex(user_siginfo,   "status", sizeof("status")-1, siginfo->si_status);
1249 # ifdef si_utime
1250 				add_assoc_double_ex(user_siginfo, "utime",  sizeof("utime")-1,  siginfo->si_utime);
1251 # endif
1252 # ifdef si_stime
1253 				add_assoc_double_ex(user_siginfo, "stime",  sizeof("stime")-1,  siginfo->si_stime);
1254 # endif
1255 				add_assoc_long_ex(user_siginfo,   "pid",    sizeof("pid")-1,    siginfo->si_pid);
1256 				add_assoc_long_ex(user_siginfo,   "uid",    sizeof("uid")-1,    siginfo->si_uid);
1257 				break;
1258 			case SIGUSR1:
1259 			case SIGUSR2:
1260 				add_assoc_long_ex(user_siginfo,   "pid",    sizeof("pid")-1,    siginfo->si_pid);
1261 				add_assoc_long_ex(user_siginfo,   "uid",    sizeof("uid")-1,    siginfo->si_uid);
1262 				break;
1263 #endif
1264 			case SIGILL:
1265 			case SIGFPE:
1266 			case SIGSEGV:
1267 			case SIGBUS:
1268 				add_assoc_double_ex(user_siginfo, "addr", sizeof("addr")-1, (zend_long)siginfo->si_addr);
1269 				break;
1270 #ifdef SIGPOLL
1271 			case SIGPOLL:
1272 				add_assoc_long_ex(user_siginfo, "band", sizeof("band")-1, siginfo->si_band);
1273 # ifdef si_fd
1274 				add_assoc_long_ex(user_siginfo, "fd",   sizeof("fd")-1,   siginfo->si_fd);
1275 # endif
1276 				break;
1277 #endif
1278 		}
1279 #if defined(SIGRTMIN) && defined(SIGRTMAX)
1280 		if (SIGRTMIN <= signo && signo <= SIGRTMAX) {
1281 			add_assoc_long_ex(user_siginfo, "pid", sizeof("pid")-1, siginfo->si_pid);
1282 			add_assoc_long_ex(user_siginfo, "uid", sizeof("uid")-1, siginfo->si_uid);
1283 		}
1284 #endif
1285 	}
1286 }
1287 /* }}} */
1288 #endif
1289 
1290 #ifdef HAVE_GETPRIORITY
1291 /* {{{ proto int pcntl_getpriority([int pid [, int process_identifier]])
1292    Get the priority of any process */
PHP_FUNCTION(pcntl_getpriority)1293 PHP_FUNCTION(pcntl_getpriority)
1294 {
1295 	zend_long who = PRIO_PROCESS;
1296 	zend_long pid = getpid();
1297 	int pri;
1298 
1299 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &pid, &who) == FAILURE) {
1300 		RETURN_FALSE;
1301 	}
1302 
1303 	/* needs to be cleared, since any returned value is valid */
1304 	errno = 0;
1305 
1306 	pri = getpriority(who, pid);
1307 
1308 	if (errno) {
1309 		PCNTL_G(last_error) = errno;
1310 		switch (errno) {
1311 			case ESRCH:
1312 				php_error_docref(NULL, E_WARNING, "Error %d: No process was located using the given parameters", errno);
1313 				break;
1314 			case EINVAL:
1315 				php_error_docref(NULL, E_WARNING, "Error %d: Invalid identifier flag", errno);
1316 				break;
1317 			default:
1318 				php_error_docref(NULL, E_WARNING, "Unknown error %d has occurred", errno);
1319 				break;
1320 		}
1321 		RETURN_FALSE;
1322 	}
1323 
1324 	RETURN_LONG(pri);
1325 }
1326 /* }}} */
1327 #endif
1328 
1329 #ifdef HAVE_SETPRIORITY
1330 /* {{{ proto bool pcntl_setpriority(int priority [, int pid [, int process_identifier]])
1331    Change the priority of any process */
PHP_FUNCTION(pcntl_setpriority)1332 PHP_FUNCTION(pcntl_setpriority)
1333 {
1334 	zend_long who = PRIO_PROCESS;
1335 	zend_long pid = getpid();
1336 	zend_long pri;
1337 
1338 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ll", &pri, &pid, &who) == FAILURE) {
1339 		RETURN_FALSE;
1340 	}
1341 
1342 	if (setpriority(who, pid, pri)) {
1343 		PCNTL_G(last_error) = errno;
1344 		switch (errno) {
1345 			case ESRCH:
1346 				php_error_docref(NULL, E_WARNING, "Error %d: No process was located using the given parameters", errno);
1347 				break;
1348 			case EINVAL:
1349 				php_error_docref(NULL, E_WARNING, "Error %d: Invalid identifier flag", errno);
1350 				break;
1351 			case EPERM:
1352 				php_error_docref(NULL, E_WARNING, "Error %d: A process was located, but neither its effective nor real user ID matched the effective user ID of the caller", errno);
1353 				break;
1354 			case EACCES:
1355 				php_error_docref(NULL, E_WARNING, "Error %d: Only a super user may attempt to increase the process priority", errno);
1356 				break;
1357 			default:
1358 				php_error_docref(NULL, E_WARNING, "Unknown error %d has occurred", errno);
1359 				break;
1360 		}
1361 		RETURN_FALSE;
1362 	}
1363 
1364 	RETURN_TRUE;
1365 }
1366 /* }}} */
1367 #endif
1368 
1369 /* {{{ proto int pcntl_get_last_error(void)
1370    Retrieve the error number set by the last pcntl function which failed. */
PHP_FUNCTION(pcntl_get_last_error)1371 PHP_FUNCTION(pcntl_get_last_error)
1372 {
1373         RETURN_LONG(PCNTL_G(last_error));
1374 }
1375 /* }}} */
1376 
1377 /* {{{ proto string pcntl_strerror(int errno)
1378    Retrieve the system error message associated with the given errno. */
PHP_FUNCTION(pcntl_strerror)1379 PHP_FUNCTION(pcntl_strerror)
1380 {
1381         zend_long error;
1382 
1383         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error) == FAILURE) {
1384                 RETURN_FALSE;
1385         }
1386 
1387         RETURN_STRING(strerror(error));
1388 }
1389 /* }}} */
1390 
1391 /* Our custom signal handler that calls the appropriate php_function */
1392 #ifdef HAVE_STRUCT_SIGINFO_T
pcntl_signal_handler(int signo,siginfo_t * siginfo,void * context)1393 static void pcntl_signal_handler(int signo, siginfo_t *siginfo, void *context)
1394 #else
1395 static void pcntl_signal_handler(int signo)
1396 #endif
1397 {
1398 	struct php_pcntl_pending_signal *psig;
1399 
1400 	psig = PCNTL_G(spares);
1401 	if (!psig) {
1402 		/* oops, too many signals for us to track, so we'll forget about this one */
1403 		return;
1404 	}
1405 	PCNTL_G(spares) = psig->next;
1406 
1407 	psig->signo = signo;
1408 	psig->next = NULL;
1409 
1410 #ifdef HAVE_STRUCT_SIGINFO_T
1411 	psig->siginfo = *siginfo;
1412 #endif
1413 
1414 	/* the head check is important, as the tick handler cannot atomically clear both
1415 	 * the head and tail */
1416 	if (PCNTL_G(head) && PCNTL_G(tail)) {
1417 		PCNTL_G(tail)->next = psig;
1418 	} else {
1419 		PCNTL_G(head) = psig;
1420 	}
1421 	PCNTL_G(tail) = psig;
1422 	PCNTL_G(pending_signals) = 1;
1423 	if (PCNTL_G(async_signals)) {
1424 		EG(vm_interrupt) = 1;
1425 	}
1426 }
1427 
pcntl_signal_dispatch()1428 void pcntl_signal_dispatch()
1429 {
1430 	zval params[2], *handle, retval;
1431 	struct php_pcntl_pending_signal *queue, *next;
1432 	sigset_t mask;
1433 	sigset_t old_mask;
1434 
1435 	if(!PCNTL_G(pending_signals)) {
1436 		return;
1437 	}
1438 
1439 	/* Mask all signals */
1440 	sigfillset(&mask);
1441 	sigprocmask(SIG_BLOCK, &mask, &old_mask);
1442 
1443 	/* Bail if the queue is empty or if we are already playing the queue */
1444 	if (!PCNTL_G(head) || PCNTL_G(processing_signal_queue)) {
1445 		sigprocmask(SIG_SETMASK, &old_mask, NULL);
1446 		return;
1447 	}
1448 
1449 	/* Prevent reentrant handler calls */
1450 	PCNTL_G(processing_signal_queue) = 1;
1451 
1452 	queue = PCNTL_G(head);
1453 	PCNTL_G(head) = NULL; /* simple stores are atomic */
1454 
1455 	/* Allocate */
1456 	while (queue) {
1457 		if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
1458 			if (Z_TYPE_P(handle) != IS_LONG) {
1459 				ZVAL_NULL(&retval);
1460 				ZVAL_LONG(&params[0], queue->signo);
1461 #ifdef HAVE_STRUCT_SIGINFO_T
1462 				array_init(&params[1]);
1463 				pcntl_siginfo_to_zval(queue->signo, &queue->siginfo, &params[1]);
1464 #else
1465 				ZVAL_NULL(&params[1]);
1466 #endif
1467 
1468 				/* Call php signal handler - Note that we do not report errors, and we ignore the return value */
1469 				/* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
1470 				call_user_function(EG(function_table), NULL, handle, &retval, 2, params);
1471 				zval_ptr_dtor(&retval);
1472 #ifdef HAVE_STRUCT_SIGINFO_T
1473 				zval_ptr_dtor(&params[1]);
1474 #endif
1475 			}
1476 		}
1477 
1478 		next = queue->next;
1479 		queue->next = PCNTL_G(spares);
1480 		PCNTL_G(spares) = queue;
1481 		queue = next;
1482 	}
1483 
1484 	PCNTL_G(pending_signals) = 0;
1485 
1486 	/* Re-enable queue */
1487 	PCNTL_G(processing_signal_queue) = 0;
1488 
1489 	/* return signal mask to previous state */
1490 	sigprocmask(SIG_SETMASK, &old_mask, NULL);
1491 }
1492 
1493 /* {{{ proto bool pcntl_async_signals([bool on[)
1494    Enable/disable asynchronous signal handling and return the old setting. */
PHP_FUNCTION(pcntl_async_signals)1495 PHP_FUNCTION(pcntl_async_signals)
1496 {
1497 	zend_bool on;
1498 
1499 	if (ZEND_NUM_ARGS() == 0) {
1500 		RETURN_BOOL(PCNTL_G(async_signals));
1501 	}
1502 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &on) == FAILURE) {
1503 		return;
1504 	}
1505 	RETVAL_BOOL(PCNTL_G(async_signals));
1506 	PCNTL_G(async_signals) = on;
1507 }
1508 /* }}} */
1509 
pcntl_interrupt_function(zend_execute_data * execute_data)1510 static void pcntl_interrupt_function(zend_execute_data *execute_data)
1511 {
1512 	pcntl_signal_dispatch();
1513 	if (orig_interrupt_function) {
1514 		orig_interrupt_function(execute_data);
1515 	}
1516 }
1517 
1518 /*
1519  * Local variables:
1520  * tab-width: 4
1521  * c-basic-offset: 4
1522  * indent-tabs-mode: t
1523  * End:
1524  */
1525