1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: Rasmus Lerdorf <rasmus@php.net> |
16 : | Ilia Alshanetsky <iliaa@php.net> |
17 : +----------------------------------------------------------------------+
18 : */
19 : /* $Id: exec.c 289624 2009-10-14 01:32:07Z iliaa $ */
20 :
21 : #include <stdio.h>
22 : #include "php.h"
23 : #include <ctype.h>
24 : #include "php_string.h"
25 : #include "safe_mode.h"
26 : #include "ext/standard/head.h"
27 : #include "ext/standard/file.h"
28 : #include "basic_functions.h"
29 : #include "exec.h"
30 : #include "php_globals.h"
31 : #include "SAPI.h"
32 :
33 : #if HAVE_SYS_WAIT_H
34 : #include <sys/wait.h>
35 : #endif
36 : #if HAVE_SIGNAL_H
37 : #include <signal.h>
38 : #endif
39 :
40 : #if HAVE_SYS_TYPES_H
41 : #include <sys/types.h>
42 : #endif
43 : #if HAVE_SYS_STAT_H
44 : #include <sys/stat.h>
45 : #endif
46 : #if HAVE_FCNTL_H
47 : #include <fcntl.h>
48 : #endif
49 :
50 : #if HAVE_NICE && HAVE_UNISTD_H
51 : #include <unistd.h>
52 : #endif
53 :
54 : /* {{{ php_exec
55 : * If type==0, only last line of output is returned (exec)
56 : * If type==1, all lines will be printed and last lined returned (system)
57 : * If type==2, all lines will be saved to given array (exec with &$array)
58 : * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
59 : *
60 : */
61 : int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC)
62 11 : {
63 : FILE *fp;
64 11 : char *buf, *tmp=NULL;
65 11 : int l = 0, pclose_return;
66 11 : char *cmd_p, *b, *c, *d=NULL;
67 : php_stream *stream;
68 11 : size_t buflen, bufl = 0;
69 : #if PHP_SIGCHILD
70 : void (*sig_handler)() = NULL;
71 : #endif
72 :
73 11 : if (PG(safe_mode)) {
74 0 : if ((c = strchr(cmd, ' '))) {
75 0 : *c = '\0';
76 0 : c++;
77 : }
78 0 : if (strstr(cmd, "..")) {
79 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
80 0 : goto err;
81 : }
82 0 : b = strrchr(cmd, PHP_DIR_SEPARATOR);
83 : #ifdef PHP_WIN32
84 : if (b && *b == '\\' && b == cmd) {
85 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid absolute path.");
86 : goto err;
87 : }
88 : #endif
89 0 : spprintf(&d, 0, "%s%s%s%s%s", PG(safe_mode_exec_dir), (b ? "" : "/"), (b ? b : cmd), (c ? " " : ""), (c ? c : ""));
90 0 : if (c) {
91 0 : *(c - 1) = ' ';
92 : }
93 0 : cmd_p = php_escape_shell_cmd(d);
94 0 : efree(d);
95 0 : d = cmd_p;
96 : } else {
97 11 : cmd_p = cmd;
98 : }
99 :
100 : #if PHP_SIGCHILD
101 : sig_handler = signal (SIGCHLD, SIG_DFL);
102 : #endif
103 :
104 : #ifdef PHP_WIN32
105 : fp = VCWD_POPEN(cmd_p, "rb");
106 : #else
107 11 : fp = VCWD_POPEN(cmd_p, "r");
108 : #endif
109 11 : if (!fp) {
110 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
111 0 : goto err;
112 : }
113 :
114 11 : stream = php_stream_fopen_from_pipe(fp, "rb");
115 :
116 11 : buf = (char *) emalloc(EXEC_INPUT_BUF);
117 11 : buflen = EXEC_INPUT_BUF;
118 :
119 11 : if (type != 3) {
120 9 : b = buf;
121 :
122 3296 : while (php_stream_get_line(stream, b, EXEC_INPUT_BUF, &bufl)) {
123 : /* no new line found, let's read some more */
124 3278 : if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
125 13 : if (buflen < (bufl + (b - buf) + EXEC_INPUT_BUF)) {
126 9 : bufl += b - buf;
127 9 : buflen = bufl + EXEC_INPUT_BUF;
128 9 : buf = erealloc(buf, buflen);
129 9 : b = buf + bufl;
130 : } else {
131 4 : b += bufl;
132 : }
133 13 : continue;
134 3265 : } else if (b != buf) {
135 4 : bufl += b - buf;
136 : }
137 :
138 3265 : if (type == 1) {
139 3250 : PHPWRITE(buf, bufl);
140 3250 : if (OG(ob_nesting_level) < 1) {
141 0 : sapi_flush(TSRMLS_C);
142 : }
143 15 : } else if (type == 2) {
144 : /* strip trailing whitespaces */
145 15 : l = bufl;
146 30 : while (l-- && isspace(((unsigned char *)buf)[l]));
147 15 : if (l != (int)(bufl - 1)) {
148 15 : bufl = l + 1;
149 15 : buf[bufl] = '\0';
150 : }
151 15 : add_next_index_stringl(array, buf, bufl, 1);
152 : }
153 3265 : b = buf;
154 : }
155 9 : if (bufl) {
156 : /* strip trailing whitespaces if we have not done so already */
157 8 : if ((type == 2 && bufl && !l) || type != 2) {
158 5 : l = bufl;
159 10 : while (l-- && isspace(((unsigned char *)buf)[l]));
160 5 : if (l != (int)(bufl - 1)) {
161 5 : bufl = l + 1;
162 5 : buf[bufl] = '\0';
163 : }
164 5 : if (type == 2) {
165 0 : add_next_index_stringl(array, buf, bufl, 1);
166 : }
167 : }
168 :
169 : /* Return last line from the shell command */
170 8 : if (PG(magic_quotes_runtime)) {
171 : int len;
172 :
173 0 : tmp = php_addslashes(buf, bufl, &len, 0 TSRMLS_CC);
174 0 : RETVAL_STRINGL(tmp, len, 0);
175 : } else {
176 8 : RETVAL_STRINGL(buf, bufl, 1);
177 : }
178 : } else { /* should return NULL, but for BC we return "" */
179 1 : RETVAL_EMPTY_STRING();
180 : }
181 : } else {
182 6593 : while((bufl = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
183 6589 : PHPWRITE(buf, bufl);
184 : }
185 : }
186 :
187 11 : pclose_return = php_stream_close(stream);
188 11 : efree(buf);
189 :
190 11 : done:
191 : #if PHP_SIGCHILD
192 : if (sig_handler) {
193 : signal(SIGCHLD, sig_handler);
194 : }
195 : #endif
196 11 : if (d) {
197 0 : efree(d);
198 : }
199 11 : return pclose_return;
200 0 : err:
201 0 : pclose_return = -1;
202 0 : goto done;
203 : }
204 : /* }}} */
205 :
206 : static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode)
207 11 : {
208 : char *cmd;
209 : int cmd_len;
210 11 : zval *ret_code=NULL, *ret_array=NULL;
211 : int ret;
212 :
213 11 : if (mode) {
214 7 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/", &cmd, &cmd_len, &ret_code) == FAILURE) {
215 0 : RETURN_FALSE;
216 : }
217 : } else {
218 4 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/z/", &cmd, &cmd_len, &ret_array, &ret_code) == FAILURE) {
219 0 : RETURN_FALSE;
220 : }
221 : }
222 11 : if (!cmd_len) {
223 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command");
224 0 : RETURN_FALSE;
225 : }
226 :
227 11 : if (!ret_array) {
228 8 : ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
229 : } else {
230 3 : if (Z_TYPE_P(ret_array) != IS_ARRAY) {
231 1 : zval_dtor(ret_array);
232 1 : array_init(ret_array);
233 : }
234 3 : ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
235 : }
236 11 : if (ret_code) {
237 2 : zval_dtor(ret_code);
238 2 : ZVAL_LONG(ret_code, ret);
239 : }
240 : }
241 :
242 : /* {{{ proto string exec(string command [, array &output [, int &return_value]])
243 : Execute an external program */
244 : PHP_FUNCTION(exec)
245 4 : {
246 4 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
247 4 : }
248 :
249 : /* }}} */
250 :
251 : /* {{{ proto int system(string command [, int &return_value])
252 : Execute an external program and display output */
253 : PHP_FUNCTION(system)
254 5 : {
255 5 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
256 5 : }
257 : /* }}} */
258 :
259 : /* {{{ proto void passthru(string command [, int &return_value])
260 : Execute an external program and display raw output */
261 : PHP_FUNCTION(passthru)
262 2 : {
263 2 : php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
264 2 : }
265 : /* }}} */
266 :
267 : /* {{{ php_escape_shell_cmd
268 : Escape all chars that could possibly be used to
269 : break out of a shell command
270 :
271 : This function emalloc's a string and returns the pointer.
272 : Remember to efree it when done with it.
273 :
274 : *NOT* safe for binary strings
275 : */
276 4 : char *php_escape_shell_cmd(char *str) {
277 : register int x, y, l;
278 : char *cmd;
279 4 : char *p = NULL;
280 :
281 : TSRMLS_FETCH();
282 :
283 4 : l = strlen(str);
284 4 : cmd = safe_emalloc(2, l, 1);
285 :
286 55 : for (x = 0, y = 0; x < l; x++) {
287 51 : int mb_len = php_mblen(str + x, (l - x));
288 :
289 : /* skip non-valid multibyte characters */
290 51 : if (mb_len < 0) {
291 0 : continue;
292 51 : } else if (mb_len > 1) {
293 5 : memcpy(cmd + y, str + x, mb_len);
294 5 : y += mb_len;
295 5 : x += mb_len - 1;
296 5 : continue;
297 : }
298 :
299 46 : switch (str[x]) {
300 : case '"':
301 : case '\'':
302 : #ifndef PHP_WIN32
303 2 : if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {
304 : /* noop */
305 2 : } else if (p && *p == str[x]) {
306 1 : p = NULL;
307 : } else {
308 0 : cmd[y++] = '\\';
309 : }
310 2 : cmd[y++] = str[x];
311 2 : break;
312 : #endif
313 : case '#': /* This is character-set independent */
314 : case '&':
315 : case ';':
316 : case '`':
317 : case '|':
318 : case '*':
319 : case '?':
320 : case '~':
321 : case '<':
322 : case '>':
323 : case '^':
324 : case '(':
325 : case ')':
326 : case '[':
327 : case ']':
328 : case '{':
329 : case '}':
330 : case '$':
331 : case '\\':
332 : case '\x0A': /* excluding these two */
333 : case '\xFF':
334 : #ifdef PHP_WIN32
335 : /* since Windows does not allow us to escape these chars, just remove them */
336 : case '%':
337 : cmd[y++] = ' ';
338 : break;
339 : #endif
340 6 : cmd[y++] = '\\';
341 : /* fall-through */
342 : default:
343 44 : cmd[y++] = str[x];
344 :
345 : }
346 : }
347 4 : cmd[y] = '\0';
348 4 : return cmd;
349 : }
350 : /* }}} */
351 :
352 : /* {{{ php_escape_shell_arg
353 : */
354 27 : char *php_escape_shell_arg(char *str) {
355 : int x, y, l;
356 : char *cmd;
357 : TSRMLS_FETCH();
358 :
359 27 : y = 0;
360 27 : l = strlen(str);
361 :
362 27 : cmd = safe_emalloc(4, l, 3); /* worst case */
363 :
364 : #ifdef PHP_WIN32
365 : cmd[y++] = '"';
366 : #else
367 27 : cmd[y++] = '\'';
368 : #endif
369 :
370 159 : for (x = 0; x < l; x++) {
371 132 : int mb_len = php_mblen(str + x, (l - x));
372 :
373 : /* skip non-valid multibyte characters */
374 132 : if (mb_len < 0) {
375 0 : continue;
376 132 : } else if (mb_len > 1) {
377 4 : memcpy(cmd + y, str + x, mb_len);
378 4 : y += mb_len;
379 4 : x += mb_len - 1;
380 4 : continue;
381 : }
382 :
383 128 : switch (str[x]) {
384 : #ifdef PHP_WIN32
385 : case '"':
386 : case '%':
387 : cmd[y++] = ' ';
388 : break;
389 : #else
390 : case '\'':
391 2 : cmd[y++] = '\'';
392 2 : cmd[y++] = '\\';
393 2 : cmd[y++] = '\'';
394 : #endif
395 : /* fall-through */
396 : default:
397 128 : cmd[y++] = str[x];
398 : }
399 : }
400 : #ifdef PHP_WIN32
401 : cmd[y++] = '"';
402 : #else
403 27 : cmd[y++] = '\'';
404 : #endif
405 27 : cmd[y] = '\0';
406 27 : return cmd;
407 : }
408 : /* }}} */
409 :
410 : /* {{{ proto string escapeshellcmd(string command)
411 : Escape shell metacharacters */
412 : PHP_FUNCTION(escapeshellcmd)
413 2 : {
414 : char *command;
415 : int command_len;
416 2 : char *cmd = NULL;
417 :
418 2 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
419 0 : return;
420 : }
421 :
422 2 : if (command_len) {
423 2 : cmd = php_escape_shell_cmd(command);
424 2 : RETVAL_STRING(cmd, 0);
425 : } else {
426 0 : RETVAL_EMPTY_STRING();
427 : }
428 : }
429 : /* }}} */
430 :
431 : /* {{{ proto string escapeshellarg(string arg)
432 : Quote and escape an argument for use in a shell command */
433 : PHP_FUNCTION(escapeshellarg)
434 32 : {
435 : char *argument;
436 : int argument_len;
437 32 : char *cmd = NULL;
438 :
439 32 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &argument, &argument_len) == FAILURE) {
440 5 : return;
441 : }
442 :
443 27 : if (argument) {
444 27 : cmd = php_escape_shell_arg(argument);
445 27 : RETVAL_STRING(cmd, 0);
446 : }
447 : }
448 : /* }}} */
449 :
450 : /* {{{ proto string shell_exec(string cmd)
451 : Execute command via shell and return complete output as string */
452 : PHP_FUNCTION(shell_exec)
453 108 : {
454 : FILE *in;
455 : size_t total_readbytes;
456 : zval **cmd;
457 : char *ret;
458 : php_stream *stream;
459 :
460 108 : if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &cmd)==FAILURE) {
461 0 : WRONG_PARAM_COUNT;
462 : }
463 :
464 108 : if (PG(safe_mode)) {
465 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute using backquotes in Safe Mode");
466 0 : RETURN_FALSE;
467 : }
468 :
469 108 : convert_to_string_ex(cmd);
470 : #ifdef PHP_WIN32
471 : if ((in=VCWD_POPEN(Z_STRVAL_PP(cmd), "rt"))==NULL) {
472 : #else
473 108 : if ((in=VCWD_POPEN(Z_STRVAL_PP(cmd), "r"))==NULL) {
474 : #endif
475 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to execute '%s'", Z_STRVAL_PP(cmd));
476 0 : RETURN_FALSE;
477 : }
478 :
479 108 : stream = php_stream_fopen_from_pipe(in, "rb");
480 108 : total_readbytes = php_stream_copy_to_mem(stream, &ret, PHP_STREAM_COPY_ALL, 0);
481 108 : php_stream_close(stream);
482 :
483 108 : if (total_readbytes > 0) {
484 92 : RETURN_STRINGL(ret, total_readbytes, 0);
485 : } else {
486 16 : RETURN_NULL();
487 : }
488 : }
489 : /* }}} */
490 :
491 : #ifdef HAVE_NICE
492 : /* {{{ proto bool proc_nice(int priority)
493 : Change the priority of the current process */
494 : PHP_FUNCTION(proc_nice)
495 25 : {
496 : long pri;
497 :
498 25 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pri) == FAILURE) {
499 12 : RETURN_FALSE;
500 : }
501 :
502 13 : errno = 0;
503 13 : nice(pri);
504 13 : if (errno) {
505 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a super user may attempt to increase the priority of a process");
506 1 : RETURN_FALSE;
507 : }
508 :
509 12 : RETURN_TRUE;
510 : }
511 : /* }}} */
512 : #endif
513 :
514 : /*
515 : * Local variables:
516 : * tab-width: 4
517 : * c-basic-offset: 4
518 : * End:
519 : * vim600: sw=4 ts=4 fdm=marker
520 : * vim<600: sw=4 ts=4
521 : */
|