xref: /PHP-7.2/ext/pcntl/pcntl.c (revision b5cb3ac8)
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 /* $Id$ */
20 
21 #define PCNTL_DEBUG 0
22 
23 #if PCNTL_DEBUG
24 #define DEBUG_OUT printf("DEBUG: ");printf
25 #define IF_DEBUG(z) z
26 #else
27 #define IF_DEBUG(z)
28 #endif
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include "php.h"
35 #include "php_ini.h"
36 #include "ext/standard/info.h"
37 #include "php_pcntl.h"
38 #include "php_signal.h"
39 #include "php_ticks.h"
40 
41 #if HAVE_GETPRIORITY || HAVE_SETPRIORITY || HAVE_WAIT3
42 #include <sys/wait.h>
43 #include <sys/time.h>
44 #include <sys/resource.h>
45 #endif
46 
47 #include <errno.h>
48 
49 #ifndef NSIG
50 # ifdef SIGRTMAX
51 #  define NSIG (SIGRTMAX + 1)
52 # else
53 #  define NSIG 32
54 # endif
55 #endif
56 
57 ZEND_DECLARE_MODULE_GLOBALS(pcntl)
58 static PHP_GINIT_FUNCTION(pcntl);
59 
60 /* {{{ arginfo */
61 ZEND_BEGIN_ARG_INFO(arginfo_pcntl_void, 0)
62 ZEND_END_ARG_INFO()
63 
64 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_waitpid, 0, 0, 2)
65 	ZEND_ARG_INFO(0, pid)
66 	ZEND_ARG_INFO(1, status)
67 	ZEND_ARG_INFO(0, options)
68 	ZEND_ARG_INFO(1, rusage)
69 ZEND_END_ARG_INFO()
70 
71 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wait, 0, 0, 1)
72 	ZEND_ARG_INFO(1, status)
73 	ZEND_ARG_INFO(0, options)
74 	ZEND_ARG_INFO(1, rusage)
75 ZEND_END_ARG_INFO()
76 
77 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal, 0, 0, 2)
78 	ZEND_ARG_INFO(0, signo)
79 	ZEND_ARG_INFO(0, handler)
80 	ZEND_ARG_INFO(0, restart_syscalls)
81 ZEND_END_ARG_INFO()
82 
83 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal_get_handler, 0, 0, 1)
84 	ZEND_ARG_INFO(0, signo)
85 ZEND_END_ARG_INFO()
86 
87 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigprocmask, 0, 0, 2)
88 	ZEND_ARG_INFO(0, how)
89 	ZEND_ARG_INFO(0, set)
90 	ZEND_ARG_INFO(1, oldset)
91 ZEND_END_ARG_INFO()
92 
93 #ifdef HAVE_STRUCT_SIGINFO_T
94 # if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
95 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigwaitinfo, 0, 0, 1)
96 	ZEND_ARG_INFO(0, set)
97 	ZEND_ARG_INFO(1, info)
98 ZEND_END_ARG_INFO()
99 
100 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigtimedwait, 0, 0, 1)
101 	ZEND_ARG_INFO(0, set)
102 	ZEND_ARG_INFO(1, info)
103 	ZEND_ARG_INFO(0, seconds)
104 	ZEND_ARG_INFO(0, nanoseconds)
105 ZEND_END_ARG_INFO()
106 # endif
107 #endif
108 
109 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexited, 0, 0, 1)
110 	ZEND_ARG_INFO(0, status)
111 ZEND_END_ARG_INFO()
112 
113 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifstopped, 0, 0, 1)
114 	ZEND_ARG_INFO(0, status)
115 ZEND_END_ARG_INFO()
116 
117 #ifdef HAVE_WCONTINUED
118 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifcontinued, 0, 0, 1)
119 	ZEND_ARG_INFO(0, status)
120 ZEND_END_ARG_INFO()
121 #endif
122 
123 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifsignaled, 0, 0, 1)
124 	ZEND_ARG_INFO(0, status)
125 ZEND_END_ARG_INFO()
126 
127 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexitstatus, 0, 0, 1)
128 	ZEND_ARG_INFO(0, status)
129 ZEND_END_ARG_INFO()
130 
131 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wtermsig, 0, 0, 1)
132 	ZEND_ARG_INFO(0, status)
133 ZEND_END_ARG_INFO()
134 
135 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wstopsig, 0, 0, 1)
136 	ZEND_ARG_INFO(0, status)
137 ZEND_END_ARG_INFO()
138 
139 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_exec, 0, 0, 1)
140 	ZEND_ARG_INFO(0, path)
141 	ZEND_ARG_INFO(0, args)
142 	ZEND_ARG_INFO(0, envs)
143 ZEND_END_ARG_INFO()
144 
145 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_alarm, 0, 0, 1)
146 	ZEND_ARG_INFO(0, seconds)
147 ZEND_END_ARG_INFO()
148 
149 #ifdef HAVE_GETPRIORITY
150 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_getpriority, 0, 0, 0)
151 	ZEND_ARG_INFO(0, pid)
152 	ZEND_ARG_INFO(0, process_identifier)
153 ZEND_END_ARG_INFO()
154 #endif
155 
156 #ifdef HAVE_SETPRIORITY
157 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_setpriority, 0, 0, 1)
158 	ZEND_ARG_INFO(0, priority)
159 	ZEND_ARG_INFO(0, pid)
160 	ZEND_ARG_INFO(0, process_identifier)
161 ZEND_END_ARG_INFO()
162 #endif
163 
164 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_strerror, 0, 0, 1)
165         ZEND_ARG_INFO(0, errno)
166 ZEND_END_ARG_INFO()
167 
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_async_signals, 0, 0, 1)
169         ZEND_ARG_INFO(0, on)
170 ZEND_END_ARG_INFO()
171 /* }}} */
172 
173 const zend_function_entry pcntl_functions[] = {
174 	PHP_FE(pcntl_fork,			arginfo_pcntl_void)
175 	PHP_FE(pcntl_waitpid,		arginfo_pcntl_waitpid)
176 	PHP_FE(pcntl_wait,			arginfo_pcntl_wait)
177 	PHP_FE(pcntl_signal,		arginfo_pcntl_signal)
178 	PHP_FE(pcntl_signal_get_handler,		arginfo_pcntl_signal_get_handler)
179 	PHP_FE(pcntl_signal_dispatch,	arginfo_pcntl_void)
180 	PHP_FE(pcntl_wifexited,		arginfo_pcntl_wifexited)
181 	PHP_FE(pcntl_wifstopped,	arginfo_pcntl_wifstopped)
182 	PHP_FE(pcntl_wifsignaled,	arginfo_pcntl_wifsignaled)
183 	PHP_FE(pcntl_wexitstatus,	arginfo_pcntl_wifexitstatus)
184 	PHP_FE(pcntl_wtermsig,		arginfo_pcntl_wtermsig)
185 	PHP_FE(pcntl_wstopsig,		arginfo_pcntl_wstopsig)
186 	PHP_FE(pcntl_exec,			arginfo_pcntl_exec)
187 	PHP_FE(pcntl_alarm,			arginfo_pcntl_alarm)
188 	PHP_FE(pcntl_get_last_error,	arginfo_pcntl_void)
189 	PHP_FALIAS(pcntl_errno, pcntl_get_last_error,	NULL)
190 	PHP_FE(pcntl_strerror,		arginfo_pcntl_strerror)
191 #ifdef HAVE_GETPRIORITY
192 	PHP_FE(pcntl_getpriority,	arginfo_pcntl_getpriority)
193 #endif
194 #ifdef HAVE_SETPRIORITY
195 	PHP_FE(pcntl_setpriority,	arginfo_pcntl_setpriority)
196 #endif
197 #ifdef HAVE_SIGPROCMASK
198 	PHP_FE(pcntl_sigprocmask,	arginfo_pcntl_sigprocmask)
199 #endif
200 #ifdef HAVE_STRUCT_SIGINFO_T
201 # if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
202 	PHP_FE(pcntl_sigwaitinfo,	arginfo_pcntl_sigwaitinfo)
203 	PHP_FE(pcntl_sigtimedwait,	arginfo_pcntl_sigtimedwait)
204 # endif
205 #endif
206 #ifdef HAVE_WCONTINUED
207 	PHP_FE(pcntl_wifcontinued,	arginfo_pcntl_wifcontinued)
208 #endif
209 	PHP_FE(pcntl_async_signals,	arginfo_pcntl_async_signals)
210 	PHP_FE_END
211 };
212 
213 zend_module_entry pcntl_module_entry = {
214 	STANDARD_MODULE_HEADER,
215 	"pcntl",
216 	pcntl_functions,
217 	PHP_MINIT(pcntl),
218 	PHP_MSHUTDOWN(pcntl),
219 	PHP_RINIT(pcntl),
220 	PHP_RSHUTDOWN(pcntl),
221 	PHP_MINFO(pcntl),
222 	PHP_PCNTL_VERSION,
223 	PHP_MODULE_GLOBALS(pcntl),
224 	PHP_GINIT(pcntl),
225 	NULL,
226 	NULL,
227 	STANDARD_MODULE_PROPERTIES_EX
228 };
229 
230 #ifdef COMPILE_DL_PCNTL
231 ZEND_GET_MODULE(pcntl)
232 #endif
233 
234 static void (*orig_interrupt_function)(zend_execute_data *execute_data);
235 
236 #ifdef HAVE_STRUCT_SIGINFO_T
237 static void pcntl_signal_handler(int, siginfo_t*, void*);
238 static void pcntl_siginfo_to_zval(int, siginfo_t*, zval*);
239 #else
240 static void pcntl_signal_handler(int);
241 #endif
242 static void pcntl_signal_dispatch();
243 static void pcntl_interrupt_function(zend_execute_data *execute_data);
244 
php_register_signal_constants(INIT_FUNC_ARGS)245 void php_register_signal_constants(INIT_FUNC_ARGS)
246 {
247 
248 	/* Wait Constants */
249 #ifdef WNOHANG
250 	REGISTER_LONG_CONSTANT("WNOHANG",  (zend_long) WNOHANG, CONST_CS | CONST_PERSISTENT);
251 #endif
252 #ifdef WUNTRACED
253 	REGISTER_LONG_CONSTANT("WUNTRACED",  (zend_long) WUNTRACED, CONST_CS | CONST_PERSISTENT);
254 #endif
255 #ifdef HAVE_WCONTINUED
256 	REGISTER_LONG_CONSTANT("WCONTINUED",  (zend_long) WCONTINUED, CONST_CS | CONST_PERSISTENT);
257 #endif
258 
259 	/* Signal Constants */
260 	REGISTER_LONG_CONSTANT("SIG_IGN",  (zend_long) SIG_IGN, CONST_CS | CONST_PERSISTENT);
261 	REGISTER_LONG_CONSTANT("SIG_DFL",  (zend_long) SIG_DFL, CONST_CS | CONST_PERSISTENT);
262 	REGISTER_LONG_CONSTANT("SIG_ERR",  (zend_long) SIG_ERR, CONST_CS | CONST_PERSISTENT);
263 	REGISTER_LONG_CONSTANT("SIGHUP",   (zend_long) SIGHUP,  CONST_CS | CONST_PERSISTENT);
264 	REGISTER_LONG_CONSTANT("SIGINT",   (zend_long) SIGINT,  CONST_CS | CONST_PERSISTENT);
265 	REGISTER_LONG_CONSTANT("SIGQUIT",  (zend_long) SIGQUIT, CONST_CS | CONST_PERSISTENT);
266 	REGISTER_LONG_CONSTANT("SIGILL",   (zend_long) SIGILL,  CONST_CS | CONST_PERSISTENT);
267 	REGISTER_LONG_CONSTANT("SIGTRAP",  (zend_long) SIGTRAP, CONST_CS | CONST_PERSISTENT);
268 	REGISTER_LONG_CONSTANT("SIGABRT",  (zend_long) SIGABRT, CONST_CS | CONST_PERSISTENT);
269 #ifdef SIGIOT
270 	REGISTER_LONG_CONSTANT("SIGIOT",   (zend_long) SIGIOT,  CONST_CS | CONST_PERSISTENT);
271 #endif
272 	REGISTER_LONG_CONSTANT("SIGBUS",   (zend_long) SIGBUS,  CONST_CS | CONST_PERSISTENT);
273 	REGISTER_LONG_CONSTANT("SIGFPE",   (zend_long) SIGFPE,  CONST_CS | CONST_PERSISTENT);
274 	REGISTER_LONG_CONSTANT("SIGKILL",  (zend_long) SIGKILL, CONST_CS | CONST_PERSISTENT);
275 	REGISTER_LONG_CONSTANT("SIGUSR1",  (zend_long) SIGUSR1, CONST_CS | CONST_PERSISTENT);
276 	REGISTER_LONG_CONSTANT("SIGSEGV",  (zend_long) SIGSEGV, CONST_CS | CONST_PERSISTENT);
277 	REGISTER_LONG_CONSTANT("SIGUSR2",  (zend_long) SIGUSR2, CONST_CS | CONST_PERSISTENT);
278 	REGISTER_LONG_CONSTANT("SIGPIPE",  (zend_long) SIGPIPE, CONST_CS | CONST_PERSISTENT);
279 	REGISTER_LONG_CONSTANT("SIGALRM",  (zend_long) SIGALRM, CONST_CS | CONST_PERSISTENT);
280 	REGISTER_LONG_CONSTANT("SIGTERM",  (zend_long) SIGTERM, CONST_CS | CONST_PERSISTENT);
281 #ifdef SIGSTKFLT
282 	REGISTER_LONG_CONSTANT("SIGSTKFLT",(zend_long) SIGSTKFLT, CONST_CS | CONST_PERSISTENT);
283 #endif
284 #ifdef SIGCLD
285 	REGISTER_LONG_CONSTANT("SIGCLD",   (zend_long) SIGCLD, CONST_CS | CONST_PERSISTENT);
286 #endif
287 #ifdef SIGCHLD
288 	REGISTER_LONG_CONSTANT("SIGCHLD",  (zend_long) SIGCHLD, CONST_CS | CONST_PERSISTENT);
289 #endif
290 	REGISTER_LONG_CONSTANT("SIGCONT",  (zend_long) SIGCONT, CONST_CS | CONST_PERSISTENT);
291 	REGISTER_LONG_CONSTANT("SIGSTOP",  (zend_long) SIGSTOP, CONST_CS | CONST_PERSISTENT);
292 	REGISTER_LONG_CONSTANT("SIGTSTP",  (zend_long) SIGTSTP, CONST_CS | CONST_PERSISTENT);
293 	REGISTER_LONG_CONSTANT("SIGTTIN",  (zend_long) SIGTTIN, CONST_CS | CONST_PERSISTENT);
294 	REGISTER_LONG_CONSTANT("SIGTTOU",  (zend_long) SIGTTOU, CONST_CS | CONST_PERSISTENT);
295 	REGISTER_LONG_CONSTANT("SIGURG",   (zend_long) SIGURG , CONST_CS | CONST_PERSISTENT);
296 	REGISTER_LONG_CONSTANT("SIGXCPU",  (zend_long) SIGXCPU, CONST_CS | CONST_PERSISTENT);
297 	REGISTER_LONG_CONSTANT("SIGXFSZ",  (zend_long) SIGXFSZ, CONST_CS | CONST_PERSISTENT);
298 	REGISTER_LONG_CONSTANT("SIGVTALRM",(zend_long) SIGVTALRM, CONST_CS | CONST_PERSISTENT);
299 	REGISTER_LONG_CONSTANT("SIGPROF",  (zend_long) SIGPROF, CONST_CS | CONST_PERSISTENT);
300 	REGISTER_LONG_CONSTANT("SIGWINCH", (zend_long) SIGWINCH, CONST_CS | CONST_PERSISTENT);
301 #ifdef SIGPOLL
302 	REGISTER_LONG_CONSTANT("SIGPOLL",  (zend_long) SIGPOLL, CONST_CS | CONST_PERSISTENT);
303 #endif
304 	REGISTER_LONG_CONSTANT("SIGIO",    (zend_long) SIGIO, CONST_CS | CONST_PERSISTENT);
305 #ifdef SIGPWR
306 	REGISTER_LONG_CONSTANT("SIGPWR",   (zend_long) SIGPWR, CONST_CS | CONST_PERSISTENT);
307 #endif
308 #ifdef SIGSYS
309 	REGISTER_LONG_CONSTANT("SIGSYS",   (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT);
310 	REGISTER_LONG_CONSTANT("SIGBABY",  (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT);
311 #endif
312 #ifdef SIGRTMIN
313 	REGISTER_LONG_CONSTANT("SIGRTMIN", (zend_long) SIGRTMIN, CONST_CS | CONST_PERSISTENT);
314 #endif
315 #ifdef SIGRTMAX
316 	REGISTER_LONG_CONSTANT("SIGRTMAX", (zend_long) SIGRTMAX, CONST_CS | CONST_PERSISTENT);
317 #endif
318 
319 #if HAVE_GETPRIORITY || HAVE_SETPRIORITY
320 	REGISTER_LONG_CONSTANT("PRIO_PGRP", PRIO_PGRP, CONST_CS | CONST_PERSISTENT);
321 	REGISTER_LONG_CONSTANT("PRIO_USER", PRIO_USER, CONST_CS | CONST_PERSISTENT);
322 	REGISTER_LONG_CONSTANT("PRIO_PROCESS", PRIO_PROCESS, CONST_CS | CONST_PERSISTENT);
323 #endif
324 
325 	/* {{{ "how" argument for sigprocmask */
326 #ifdef HAVE_SIGPROCMASK
327 	REGISTER_LONG_CONSTANT("SIG_BLOCK",   SIG_BLOCK, CONST_CS | CONST_PERSISTENT);
328 	REGISTER_LONG_CONSTANT("SIG_UNBLOCK", SIG_UNBLOCK, CONST_CS | CONST_PERSISTENT);
329 	REGISTER_LONG_CONSTANT("SIG_SETMASK", SIG_SETMASK, CONST_CS | CONST_PERSISTENT);
330 #endif
331 	/* }}} */
332 
333 	/* {{{ si_code */
334 #if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
335 	REGISTER_LONG_CONSTANT("SI_USER",    SI_USER,    CONST_CS | CONST_PERSISTENT);
336 #ifdef SI_NOINFO
337 	REGISTER_LONG_CONSTANT("SI_NOINFO",  SI_NOINFO,  CONST_CS | CONST_PERSISTENT);
338 #endif
339 #ifdef SI_KERNEL
340 	REGISTER_LONG_CONSTANT("SI_KERNEL",  SI_KERNEL,  CONST_CS | CONST_PERSISTENT);
341 #endif
342 	REGISTER_LONG_CONSTANT("SI_QUEUE",   SI_QUEUE,   CONST_CS | CONST_PERSISTENT);
343 	REGISTER_LONG_CONSTANT("SI_TIMER",   SI_TIMER,   CONST_CS | CONST_PERSISTENT);
344 	REGISTER_LONG_CONSTANT("SI_MESGQ",   SI_MESGQ,   CONST_CS | CONST_PERSISTENT);
345 	REGISTER_LONG_CONSTANT("SI_ASYNCIO", SI_ASYNCIO, CONST_CS | CONST_PERSISTENT);
346 #ifdef SI_SIGIO
347 	REGISTER_LONG_CONSTANT("SI_SIGIO",   SI_SIGIO,   CONST_CS | CONST_PERSISTENT);
348 #endif
349 #ifdef SI_TKILL
350 	REGISTER_LONG_CONSTANT("SI_TKILL",   SI_TKILL,   CONST_CS | CONST_PERSISTENT);
351 #endif
352 
353 	/* si_code for SIGCHILD */
354 #ifdef CLD_EXITED
355 	REGISTER_LONG_CONSTANT("CLD_EXITED",    CLD_EXITED,    CONST_CS | CONST_PERSISTENT);
356 #endif
357 #ifdef CLD_KILLED
358 	REGISTER_LONG_CONSTANT("CLD_KILLED",    CLD_KILLED,    CONST_CS | CONST_PERSISTENT);
359 #endif
360 #ifdef CLD_DUMPED
361 	REGISTER_LONG_CONSTANT("CLD_DUMPED",    CLD_DUMPED,    CONST_CS | CONST_PERSISTENT);
362 #endif
363 #ifdef CLD_TRAPPED
364 	REGISTER_LONG_CONSTANT("CLD_TRAPPED",   CLD_TRAPPED,   CONST_CS | CONST_PERSISTENT);
365 #endif
366 #ifdef CLD_STOPPED
367 	REGISTER_LONG_CONSTANT("CLD_STOPPED",   CLD_STOPPED,   CONST_CS | CONST_PERSISTENT);
368 #endif
369 #ifdef CLD_CONTINUED
370 	REGISTER_LONG_CONSTANT("CLD_CONTINUED", CLD_CONTINUED, CONST_CS | CONST_PERSISTENT);
371 #endif
372 
373 	/* si_code for SIGTRAP */
374 #ifdef TRAP_BRKPT
375 	REGISTER_LONG_CONSTANT("TRAP_BRKPT", TRAP_BRKPT, CONST_CS | CONST_PERSISTENT);
376 #endif
377 #ifdef TRAP_TRACE
378 	REGISTER_LONG_CONSTANT("TRAP_TRACE", TRAP_TRACE, CONST_CS | CONST_PERSISTENT);
379 #endif
380 
381 	/* si_code for SIGPOLL */
382 #ifdef POLL_IN
383 	REGISTER_LONG_CONSTANT("POLL_IN",  POLL_IN,  CONST_CS | CONST_PERSISTENT);
384 #endif
385 #ifdef POLL_OUT
386 	REGISTER_LONG_CONSTANT("POLL_OUT", POLL_OUT, CONST_CS | CONST_PERSISTENT);
387 #endif
388 #ifdef POLL_MSG
389 	REGISTER_LONG_CONSTANT("POLL_MSG", POLL_MSG, CONST_CS | CONST_PERSISTENT);
390 #endif
391 #ifdef POLL_ERR
392 	REGISTER_LONG_CONSTANT("POLL_ERR", POLL_ERR, CONST_CS | CONST_PERSISTENT);
393 #endif
394 #ifdef POLL_PRI
395 	REGISTER_LONG_CONSTANT("POLL_PRI", POLL_PRI, CONST_CS | CONST_PERSISTENT);
396 #endif
397 #ifdef POLL_HUP
398 	REGISTER_LONG_CONSTANT("POLL_HUP", POLL_HUP, CONST_CS | CONST_PERSISTENT);
399 #endif
400 
401 #ifdef ILL_ILLOPC
402 	REGISTER_LONG_CONSTANT("ILL_ILLOPC", ILL_ILLOPC, CONST_CS | CONST_PERSISTENT);
403 #endif
404 #ifdef ILL_ILLOPN
405 	REGISTER_LONG_CONSTANT("ILL_ILLOPN", ILL_ILLOPN, CONST_CS | CONST_PERSISTENT);
406 #endif
407 #ifdef ILL_ILLADR
408 	REGISTER_LONG_CONSTANT("ILL_ILLADR", ILL_ILLADR, CONST_CS | CONST_PERSISTENT);
409 #endif
410 #ifdef ILL_ILLTRP
411 	REGISTER_LONG_CONSTANT("ILL_ILLTRP", ILL_ILLTRP, CONST_CS | CONST_PERSISTENT);
412 #endif
413 #ifdef ILL_PRVOPC
414 	REGISTER_LONG_CONSTANT("ILL_PRVOPC", ILL_PRVOPC, CONST_CS | CONST_PERSISTENT);
415 #endif
416 #ifdef ILL_PRVREG
417 	REGISTER_LONG_CONSTANT("ILL_PRVREG", ILL_PRVREG, CONST_CS | CONST_PERSISTENT);
418 #endif
419 #ifdef ILL_COPROC
420 	REGISTER_LONG_CONSTANT("ILL_COPROC", ILL_COPROC, CONST_CS | CONST_PERSISTENT);
421 #endif
422 #ifdef ILL_BADSTK
423 	REGISTER_LONG_CONSTANT("ILL_BADSTK", ILL_BADSTK, CONST_CS | CONST_PERSISTENT);
424 #endif
425 
426 #ifdef FPE_INTDIV
427 	REGISTER_LONG_CONSTANT("FPE_INTDIV", FPE_INTDIV, CONST_CS | CONST_PERSISTENT);
428 #endif
429 #ifdef FPE_INTOVF
430 	REGISTER_LONG_CONSTANT("FPE_INTOVF", FPE_INTOVF, CONST_CS | CONST_PERSISTENT);
431 #endif
432 #ifdef FPE_FLTDIV
433 	REGISTER_LONG_CONSTANT("FPE_FLTDIV", FPE_FLTDIV, CONST_CS | CONST_PERSISTENT);
434 #endif
435 #ifdef FPE_FLTOVF
436 	REGISTER_LONG_CONSTANT("FPE_FLTOVF", FPE_FLTOVF, CONST_CS | CONST_PERSISTENT);
437 #endif
438 #ifdef FPE_FLTUND
439 	REGISTER_LONG_CONSTANT("FPE_FLTUND", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
440 #endif
441 #ifdef FPE_FLTRES
442 	REGISTER_LONG_CONSTANT("FPE_FLTRES", FPE_FLTRES, CONST_CS | CONST_PERSISTENT);
443 #endif
444 #ifdef FPE_FLTINV
445 	REGISTER_LONG_CONSTANT("FPE_FLTINV", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
446 #endif
447 #ifdef FPE_FLTSUB
448 	REGISTER_LONG_CONSTANT("FPE_FLTSUB", FPE_FLTSUB, CONST_CS | CONST_PERSISTENT);
449 #endif
450 
451 #ifdef SEGV_MAPERR
452 	REGISTER_LONG_CONSTANT("SEGV_MAPERR", SEGV_MAPERR, CONST_CS | CONST_PERSISTENT);
453 #endif
454 #ifdef SEGV_ACCERR
455 	REGISTER_LONG_CONSTANT("SEGV_ACCERR", SEGV_ACCERR, CONST_CS | CONST_PERSISTENT);
456 #endif
457 
458 #ifdef BUS_ADRALN
459 	REGISTER_LONG_CONSTANT("BUS_ADRALN", BUS_ADRALN, CONST_CS | CONST_PERSISTENT);
460 #endif
461 #ifdef BUS_ADRERR
462 	REGISTER_LONG_CONSTANT("BUS_ADRERR", BUS_ADRERR, CONST_CS | CONST_PERSISTENT);
463 #endif
464 #ifdef BUS_OBJERR
465 	REGISTER_LONG_CONSTANT("BUS_OBJERR", BUS_OBJERR, CONST_CS | CONST_PERSISTENT);
466 #endif
467 #endif /* HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT */
468 	/* }}} */
469 }
470 
php_pcntl_register_errno_constants(INIT_FUNC_ARGS)471 static void php_pcntl_register_errno_constants(INIT_FUNC_ARGS)
472 {
473 #ifdef EINTR
474 	REGISTER_PCNTL_ERRNO_CONSTANT(EINTR);
475 #endif
476 #ifdef ECHILD
477 	REGISTER_PCNTL_ERRNO_CONSTANT(ECHILD);
478 #endif
479 #ifdef EINVAL
480 	REGISTER_PCNTL_ERRNO_CONSTANT(EINVAL);
481 #endif
482 #ifdef EAGAIN
483 	REGISTER_PCNTL_ERRNO_CONSTANT(EAGAIN);
484 #endif
485 #ifdef ESRCH
486 	REGISTER_PCNTL_ERRNO_CONSTANT(ESRCH);
487 #endif
488 #ifdef EACCES
489 	REGISTER_PCNTL_ERRNO_CONSTANT(EACCES);
490 #endif
491 #ifdef EPERM
492 	REGISTER_PCNTL_ERRNO_CONSTANT(EPERM);
493 #endif
494 #ifdef ENOMEM
495 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOMEM);
496 #endif
497 #ifdef E2BIG
498 	REGISTER_PCNTL_ERRNO_CONSTANT(E2BIG);
499 #endif
500 #ifdef EFAULT
501 	REGISTER_PCNTL_ERRNO_CONSTANT(EFAULT);
502 #endif
503 #ifdef EIO
504 	REGISTER_PCNTL_ERRNO_CONSTANT(EIO);
505 #endif
506 #ifdef EISDIR
507 	REGISTER_PCNTL_ERRNO_CONSTANT(EISDIR);
508 #endif
509 #ifdef ELIBBAD
510 	REGISTER_PCNTL_ERRNO_CONSTANT(ELIBBAD);
511 #endif
512 #ifdef ELOOP
513 	REGISTER_PCNTL_ERRNO_CONSTANT(ELOOP);
514 #endif
515 #ifdef EMFILE
516 	REGISTER_PCNTL_ERRNO_CONSTANT(EMFILE);
517 #endif
518 #ifdef ENAMETOOLONG
519 	REGISTER_PCNTL_ERRNO_CONSTANT(ENAMETOOLONG);
520 #endif
521 #ifdef ENFILE
522 	REGISTER_PCNTL_ERRNO_CONSTANT(ENFILE);
523 #endif
524 #ifdef ENOENT
525 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOENT);
526 #endif
527 #ifdef ENOEXEC
528 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOEXEC);
529 #endif
530 #ifdef ENOTDIR
531 	REGISTER_PCNTL_ERRNO_CONSTANT(ENOTDIR);
532 #endif
533 #ifdef ETXTBSY
534 	REGISTER_PCNTL_ERRNO_CONSTANT(ETXTBSY);
535 #endif
536 }
537 
PHP_GINIT_FUNCTION(pcntl)538 static PHP_GINIT_FUNCTION(pcntl)
539 {
540 	memset(pcntl_globals, 0, sizeof(*pcntl_globals));
541 }
542 
PHP_RINIT_FUNCTION(pcntl)543 PHP_RINIT_FUNCTION(pcntl)
544 {
545 	zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0);
546 	PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL;
547 	PCNTL_G(async_signals) = 0;
548 	return SUCCESS;
549 }
550 
PHP_MINIT_FUNCTION(pcntl)551 PHP_MINIT_FUNCTION(pcntl)
552 {
553 	php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
554 	php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
555 	php_add_tick_function(pcntl_signal_dispatch, NULL);
556 	orig_interrupt_function = zend_interrupt_function;
557 	zend_interrupt_function = pcntl_interrupt_function;
558 
559 	return SUCCESS;
560 }
561 
PHP_MSHUTDOWN_FUNCTION(pcntl)562 PHP_MSHUTDOWN_FUNCTION(pcntl)
563 {
564 	return SUCCESS;
565 }
566 
PHP_RSHUTDOWN_FUNCTION(pcntl)567 PHP_RSHUTDOWN_FUNCTION(pcntl)
568 {
569 	struct php_pcntl_pending_signal *sig;
570 
571 	/* FIXME: if a signal is delivered after this point, things will go pear shaped;
572 	 * need to remove signal handlers */
573 	zend_hash_destroy(&PCNTL_G(php_signal_table));
574 	while (PCNTL_G(head)) {
575 		sig = PCNTL_G(head);
576 		PCNTL_G(head) = sig->next;
577 		efree(sig);
578 	}
579 	while (PCNTL_G(spares)) {
580 		sig = PCNTL_G(spares);
581 		PCNTL_G(spares) = sig->next;
582 		efree(sig);
583 	}
584 	return SUCCESS;
585 }
586 
PHP_MINFO_FUNCTION(pcntl)587 PHP_MINFO_FUNCTION(pcntl)
588 {
589 	php_info_print_table_start();
590 	php_info_print_table_header(2, "pcntl support", "enabled");
591 	php_info_print_table_end();
592 }
593 
594 /* {{{ proto int pcntl_fork(void)
595    Forks the currently running process following the same behavior as the UNIX fork() system call*/
PHP_FUNCTION(pcntl_fork)596 PHP_FUNCTION(pcntl_fork)
597 {
598 	pid_t id;
599 
600 	id = fork();
601 	if (id == -1) {
602 		PCNTL_G(last_error) = errno;
603 		php_error_docref(NULL, E_WARNING, "Error %d", errno);
604 	}
605 
606 	RETURN_LONG((zend_long) id);
607 }
608 /* }}} */
609 
610 /* {{{ proto int pcntl_alarm(int seconds)
611    Set an alarm clock for delivery of a signal*/
PHP_FUNCTION(pcntl_alarm)612 PHP_FUNCTION(pcntl_alarm)
613 {
614 	zend_long seconds;
615 
616 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &seconds) == FAILURE)
617 		return;
618 
619 	RETURN_LONG ((zend_long) alarm(seconds));
620 }
621 /* }}} */
622 
623 #define PHP_RUSAGE_PARA(from, to, field) \
624 	add_assoc_long(to, #field, from.field)
625 #if !defined(_OSD_POSIX) && !defined(__BEOS__) /* BS2000 has only a few fields in the rusage struct */
626 	#define PHP_RUSAGE_SPECIAL(from, to) \
627 		PHP_RUSAGE_PARA(from, to, ru_oublock); \
628 		PHP_RUSAGE_PARA(from, to, ru_inblock); \
629 		PHP_RUSAGE_PARA(from, to, ru_msgsnd); \
630 		PHP_RUSAGE_PARA(from, to, ru_msgrcv); \
631 		PHP_RUSAGE_PARA(from, to, ru_maxrss); \
632 		PHP_RUSAGE_PARA(from, to, ru_ixrss); \
633 		PHP_RUSAGE_PARA(from, to, ru_idrss); \
634 		PHP_RUSAGE_PARA(from, to, ru_minflt); \
635 		PHP_RUSAGE_PARA(from, to, ru_majflt); \
636 		PHP_RUSAGE_PARA(from, to, ru_nsignals); \
637 		PHP_RUSAGE_PARA(from, to, ru_nvcsw); \
638 		PHP_RUSAGE_PARA(from, to, ru_nivcsw); \
639 		PHP_RUSAGE_PARA(from, to, ru_nswap);
640 #else /*_OSD_POSIX*/
641 	#define PHP_RUSAGE_SPECIAL(from, to)
642 #endif
643 
644 #define PHP_RUSAGE_COMMON(from ,to) \
645 	PHP_RUSAGE_PARA(from, to, ru_utime.tv_usec); \
646 	PHP_RUSAGE_PARA(from, to, ru_utime.tv_sec); \
647 	PHP_RUSAGE_PARA(from, to, ru_stime.tv_usec); \
648 	PHP_RUSAGE_PARA(from, to, ru_stime.tv_sec);
649 
650 #define PHP_RUSAGE_TO_ARRAY(from, to) \
651 	if (to) { \
652 		PHP_RUSAGE_SPECIAL(from, to) \
653 		PHP_RUSAGE_COMMON(from, to); \
654 	}
655 
656 /* {{{ proto int pcntl_waitpid(int pid, int &status, int options, array &$rusage)
657    Waits on or returns the status of a forked child as defined by the waitpid() system call */
PHP_FUNCTION(pcntl_waitpid)658 PHP_FUNCTION(pcntl_waitpid)
659 {
660 	zend_long pid, options = 0;
661 	zval *z_status = NULL, *z_rusage = NULL;
662 	int status;
663 	pid_t child_id;
664 #ifdef HAVE_WAIT4
665 	struct rusage rusage;
666 #endif
667 
668 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/|lz/", &pid, &z_status, &options, &z_rusage) == FAILURE) {
669 		return;
670 	}
671 
672 	status = zval_get_long(z_status);
673 
674 #ifdef HAVE_WAIT4
675 	if (z_rusage) {
676 		if (Z_TYPE_P(z_rusage) != IS_ARRAY) {
677 			zval_dtor(z_rusage);
678 			array_init(z_rusage);
679 		} else {
680 			zend_hash_clean(Z_ARRVAL_P(z_rusage));
681 		}
682 
683 		memset(&rusage, 0, sizeof(struct rusage));
684 		child_id = wait4((pid_t) pid, &status, options, &rusage);
685 	} else {
686 		child_id = waitpid((pid_t) pid, &status, options);
687 	}
688 #else
689 	child_id = waitpid((pid_t) pid, &status, options);
690 #endif
691 
692 	if (child_id < 0) {
693 		PCNTL_G(last_error) = errno;
694 	}
695 
696 #ifdef HAVE_WAIT4
697 	if (child_id > 0) {
698 		PHP_RUSAGE_TO_ARRAY(rusage, z_rusage);
699 	}
700 #endif
701 
702 	zval_dtor(z_status);
703 	ZVAL_LONG(z_status, status);
704 
705 	RETURN_LONG((zend_long) child_id);
706 }
707 /* }}} */
708 
709 /* {{{ proto int pcntl_wait(int &status, int $options, array &$rusage)
710    Waits on or returns the status of a forked child as defined by the waitpid() system call */
PHP_FUNCTION(pcntl_wait)711 PHP_FUNCTION(pcntl_wait)
712 {
713 	zend_long options = 0;
714 	zval *z_status = NULL, *z_rusage = NULL;
715 	int status;
716 	pid_t child_id;
717 #ifdef HAVE_WAIT3
718 	struct rusage rusage;
719 #endif
720 
721 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/|lz/", &z_status, &options, &z_rusage) == FAILURE) {
722 		return;
723 	}
724 
725 	status = zval_get_long(z_status);
726 #ifdef HAVE_WAIT3
727 	if (z_rusage) {
728 		if (Z_TYPE_P(z_rusage) != IS_ARRAY) {
729 			zval_dtor(z_rusage);
730 			array_init(z_rusage);
731 		} else {
732 			zend_hash_clean(Z_ARRVAL_P(z_rusage));
733 		}
734 
735 		memset(&rusage, 0, sizeof(struct rusage));
736 		child_id = wait3(&status, options, &rusage);
737 	} else if (options) {
738 		child_id = wait3(&status, options, NULL);
739 	} else {
740 		child_id = wait(&status);
741 	}
742 #else
743 	child_id = wait(&status);
744 #endif
745 	if (child_id < 0) {
746 		PCNTL_G(last_error) = errno;
747 	}
748 
749 #ifdef HAVE_WAIT3
750 	if (child_id > 0) {
751 		PHP_RUSAGE_TO_ARRAY(rusage, z_rusage);
752 	}
753 #endif
754 
755 	zval_dtor(z_status);
756 	ZVAL_LONG(z_status, status);
757 
758 	RETURN_LONG((zend_long) child_id);
759 }
760 /* }}} */
761 
762 #undef PHP_RUSAGE_PARA
763 #undef PHP_RUSAGE_SPECIAL
764 #undef PHP_RUSAGE_COMMON
765 #undef PHP_RUSAGE_TO_ARRAY
766 
767 /* {{{ proto bool pcntl_wifexited(int status)
768    Returns true if the child status code represents a successful exit */
PHP_FUNCTION(pcntl_wifexited)769 PHP_FUNCTION(pcntl_wifexited)
770 {
771 #ifdef WIFEXITED
772 	zend_long status_word;
773 	int int_status_word;
774 
775 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
776 	       return;
777 	}
778 
779 	int_status_word = (int) status_word;
780 	if (WIFEXITED(int_status_word))
781 		RETURN_TRUE;
782 #endif
783 
784 	RETURN_FALSE;
785 }
786 /* }}} */
787 
788 /* {{{ proto bool pcntl_wifstopped(int status)
789    Returns true if the child status code represents a stopped process (WUNTRACED must have been used with waitpid) */
PHP_FUNCTION(pcntl_wifstopped)790 PHP_FUNCTION(pcntl_wifstopped)
791 {
792 #ifdef WIFSTOPPED
793 	zend_long status_word;
794 	int int_status_word;
795 
796 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
797 	       return;
798 	}
799 
800 	int_status_word = (int) status_word;
801 	if (WIFSTOPPED(int_status_word))
802 		RETURN_TRUE;
803 #endif
804 	RETURN_FALSE;
805 }
806 /* }}} */
807 
808 /* {{{ proto bool pcntl_wifsignaled(int status)
809    Returns true if the child status code represents a process that was terminated due to a signal */
PHP_FUNCTION(pcntl_wifsignaled)810 PHP_FUNCTION(pcntl_wifsignaled)
811 {
812 #ifdef WIFSIGNALED
813 	zend_long status_word;
814 	int int_status_word;
815 
816 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
817 	       return;
818 	}
819 
820 	int_status_word = (int) status_word;
821 	if (WIFSIGNALED(int_status_word))
822 		RETURN_TRUE;
823 #endif
824 	RETURN_FALSE;
825 }
826 /* }}} */
827 /* {{{ proto bool pcntl_wifcontinued(int status)
828    Returns true if the child status code represents a process that was resumed due to a SIGCONT signal */
PHP_FUNCTION(pcntl_wifcontinued)829 PHP_FUNCTION(pcntl_wifcontinued)
830 {
831 #ifdef HAVE_WCONTINUED
832 	zend_long status_word;
833 	int int_status_word;
834 
835 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
836 	       return;
837 	}
838 
839 	int_status_word = (int) status_word;
840 	if (WIFCONTINUED(int_status_word))
841 		RETURN_TRUE;
842 #endif
843 	RETURN_FALSE;
844 }
845 /* }}} */
846 
847 
848 /* {{{ proto int pcntl_wexitstatus(int status)
849    Returns the status code of a child's exit */
PHP_FUNCTION(pcntl_wexitstatus)850 PHP_FUNCTION(pcntl_wexitstatus)
851 {
852 #ifdef WEXITSTATUS
853 	zend_long status_word;
854 	int int_status_word;
855 
856 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
857 	       return;
858 	}
859 
860 	int_status_word = (int) status_word;
861 	RETURN_LONG(WEXITSTATUS(int_status_word));
862 #else
863 	RETURN_FALSE;
864 #endif
865 }
866 /* }}} */
867 
868 /* {{{ proto int pcntl_wtermsig(int status)
869    Returns the number of the signal that terminated the process who's status code is passed  */
PHP_FUNCTION(pcntl_wtermsig)870 PHP_FUNCTION(pcntl_wtermsig)
871 {
872 #ifdef WTERMSIG
873 	zend_long status_word;
874 	int int_status_word;
875 
876 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
877 	       return;
878 	}
879 
880 	int_status_word = (int) status_word;
881 	RETURN_LONG(WTERMSIG(int_status_word));
882 #else
883 	RETURN_FALSE;
884 #endif
885 }
886 /* }}} */
887 
888 /* {{{ proto int pcntl_wstopsig(int status)
889    Returns the number of the signal that caused the process to stop who's status code is passed */
PHP_FUNCTION(pcntl_wstopsig)890 PHP_FUNCTION(pcntl_wstopsig)
891 {
892 #ifdef WSTOPSIG
893 	zend_long status_word;
894 	int int_status_word;
895 
896 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) {
897 	       return;
898 	}
899 
900 	int_status_word = (int) status_word;
901 	RETURN_LONG(WSTOPSIG(int_status_word));
902 #else
903 	RETURN_FALSE;
904 #endif
905 }
906 /* }}} */
907 
908 /* {{{ proto bool pcntl_exec(string path [, array args [, array envs]])
909    Executes specified program in current process space as defined by exec(2) */
PHP_FUNCTION(pcntl_exec)910 PHP_FUNCTION(pcntl_exec)
911 {
912 	zval *args = NULL, *envs = NULL;
913 	zval *element;
914 	HashTable *args_hash, *envs_hash;
915 	int argc = 0, argi = 0;
916 	int envc = 0, envi = 0;
917 	char **argv = NULL, **envp = NULL;
918 	char **current_arg, **pair;
919 	int pair_length;
920 	zend_string *key;
921 	char *path;
922 	size_t path_len;
923 	zend_ulong key_num;
924 
925 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|aa", &path, &path_len, &args, &envs) == FAILURE) {
926 		return;
927 	}
928 
929 	if (ZEND_NUM_ARGS() > 1) {
930 		/* Build argument list */
931 		args_hash = Z_ARRVAL_P(args);
932 		argc = zend_hash_num_elements(args_hash);
933 
934 		argv = safe_emalloc((argc + 2), sizeof(char *), 0);
935 		*argv = path;
936 		current_arg = argv+1;
937 		ZEND_HASH_FOREACH_VAL(args_hash, element) {
938 			if (argi >= argc) break;
939 			convert_to_string_ex(element);
940 			*current_arg = Z_STRVAL_P(element);
941 			argi++;
942 			current_arg++;
943 		} ZEND_HASH_FOREACH_END();
944 		*(current_arg) = NULL;
945 	} else {
946 		argv = emalloc(2 * sizeof(char *));
947 		*argv = path;
948 		*(argv+1) = NULL;
949 	}
950 
951 	if ( ZEND_NUM_ARGS() == 3 ) {
952 		/* Build environment pair list */
953 		envs_hash = Z_ARRVAL_P(envs);
954 		envc = zend_hash_num_elements(envs_hash);
955 
956 		pair = envp = safe_emalloc((envc + 1), sizeof(char *), 0);
957 		ZEND_HASH_FOREACH_KEY_VAL(envs_hash, key_num, key, element) {
958 			if (envi >= envc) break;
959 			if (!key) {
960 				key = zend_long_to_str(key_num);
961 			} else {
962 				zend_string_addref(key);
963 			}
964 
965 			convert_to_string_ex(element);
966 
967 			/* Length of element + equal sign + length of key + null */
968 			pair_length = Z_STRLEN_P(element) + ZSTR_LEN(key) + 2;
969 			*pair = emalloc(pair_length);
970 			strlcpy(*pair, ZSTR_VAL(key), ZSTR_LEN(key) + 1);
971 			strlcat(*pair, "=", pair_length);
972 			strlcat(*pair, Z_STRVAL_P(element), pair_length);
973 
974 			/* Cleanup */
975 			zend_string_release(key);
976 			envi++;
977 			pair++;
978 		} ZEND_HASH_FOREACH_END();
979 		*(pair) = NULL;
980 
981 		if (execve(path, argv, envp) == -1) {
982 			PCNTL_G(last_error) = errno;
983 			php_error_docref(NULL, E_WARNING, "Error has occurred: (errno %d) %s", errno, strerror(errno));
984 		}
985 
986 		/* Cleanup */
987 		for (pair = envp; *pair != NULL; pair++) efree(*pair);
988 		efree(envp);
989 	} else {
990 
991 		if (execv(path, argv) == -1) {
992 			PCNTL_G(last_error) = errno;
993 			php_error_docref(NULL, E_WARNING, "Error has occurred: (errno %d) %s", errno, strerror(errno));
994 		}
995 	}
996 
997 	efree(argv);
998 
999 	RETURN_FALSE;
1000 }
1001 /* }}} */
1002 
1003 /* {{{ proto bool pcntl_signal(int signo, callback handle [, bool restart_syscalls])
1004    Assigns a system signal handler to a PHP function */
PHP_FUNCTION(pcntl_signal)1005 PHP_FUNCTION(pcntl_signal)
1006 {
1007 	zval *handle;
1008 	zend_long signo;
1009 	zend_bool restart_syscalls = 1;
1010 
1011 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b", &signo, &handle, &restart_syscalls) == FAILURE) {
1012 		return;
1013 	}
1014 
1015 	if (signo < 1 || signo >= NSIG) {
1016 		php_error_docref(NULL, E_WARNING, "Invalid signal");
1017 		RETURN_FALSE;
1018 	}
1019 
1020 	if (!PCNTL_G(spares)) {
1021 		/* since calling malloc() from within a signal handler is not portable,
1022 		 * pre-allocate a few records for recording signals */
1023 		int i;
1024 		for (i = 0; i < NSIG; i++) {
1025 			struct php_pcntl_pending_signal *psig;
1026 
1027 			psig = emalloc(sizeof(*psig));
1028 			psig->next = PCNTL_G(spares);
1029 			PCNTL_G(spares) = psig;
1030 		}
1031 	}
1032 
1033 	/* Special long value case for SIG_DFL and SIG_IGN */
1034 	if (Z_TYPE_P(handle) == IS_LONG) {
1035 		if (Z_LVAL_P(handle) != (zend_long) SIG_DFL && Z_LVAL_P(handle) != (zend_long) SIG_IGN) {
1036 			php_error_docref(NULL, E_WARNING, "Invalid value for handle argument specified");
1037 			RETURN_FALSE;
1038 		}
1039 		if (php_signal(signo, (Sigfunc *) Z_LVAL_P(handle), (int) restart_syscalls) == (Sigfunc *)SIG_ERR) {
1040 			PCNTL_G(last_error) = errno;
1041 			php_error_docref(NULL, E_WARNING, "Error assigning signal");
1042 			RETURN_FALSE;
1043 		}
1044 		zend_hash_index_update(&PCNTL_G(php_signal_table), signo, handle);
1045 		RETURN_TRUE;
1046 	}
1047 
1048 	if (!zend_is_callable(handle, 0, NULL)) {
1049 		zend_string *func_name = zend_get_callable_name(handle);
1050 		PCNTL_G(last_error) = EINVAL;
1051 		php_error_docref(NULL, E_WARNING, "%s is not a callable function name error", ZSTR_VAL(func_name));
1052 		zend_string_release(func_name);
1053 		RETURN_FALSE;
1054 	}
1055 
1056 	/* Add the function name to our signal table */
1057 	if (zend_hash_index_update(&PCNTL_G(php_signal_table), signo, handle)) {
1058 		if (Z_REFCOUNTED_P(handle)) Z_ADDREF_P(handle);
1059 	}
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_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_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 				zval_ptr_dtor(&params[0]);
1473 				zval_ptr_dtor(&params[1]);
1474 			}
1475 		}
1476 
1477 		next = queue->next;
1478 		queue->next = PCNTL_G(spares);
1479 		PCNTL_G(spares) = queue;
1480 		queue = next;
1481 	}
1482 
1483 	PCNTL_G(pending_signals) = 0;
1484 
1485 	/* Re-enable queue */
1486 	PCNTL_G(processing_signal_queue) = 0;
1487 
1488 	/* return signal mask to previous state */
1489 	sigprocmask(SIG_SETMASK, &old_mask, NULL);
1490 }
1491 
1492 /* {{{ proto bool pcntl_async_signals([bool on[)
1493    Enable/disable asynchronous signal handling and return the old setting. */
PHP_FUNCTION(pcntl_async_signals)1494 PHP_FUNCTION(pcntl_async_signals)
1495 {
1496 	zend_bool on;
1497 
1498 	if (ZEND_NUM_ARGS() == 0) {
1499 		RETURN_BOOL(PCNTL_G(async_signals));
1500 	}
1501 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &on) == FAILURE) {
1502 		return;
1503 	}
1504 	RETVAL_BOOL(PCNTL_G(async_signals));
1505 	PCNTL_G(async_signals) = on;
1506 }
1507 /* }}} */
1508 
pcntl_interrupt_function(zend_execute_data * execute_data)1509 static void pcntl_interrupt_function(zend_execute_data *execute_data)
1510 {
1511 	pcntl_signal_dispatch();
1512 	if (orig_interrupt_function) {
1513 		orig_interrupt_function(execute_data);
1514 	}
1515 }
1516 
1517 /*
1518  * Local variables:
1519  * tab-width: 4
1520  * c-basic-offset: 4
1521  * indent-tabs-mode: t
1522  * End:
1523  */
1524