Подтвердить что ты не робот

Exec всегда возвращает -1 (или 127)

Я использую php 5.2.9 на рабочем сервере, и кажется, что функция exec() ведет себя "нестандартно".

Если я запустил exec("ls", $output, $return_var), то $output будет содержать список файлов в текущей папке, как ожидалось, но $return_var будет установлен на -1 вместо 0, как и ожидалось. Я использую $return_var, чтобы определить, где была успешно завершена команда, и на каждом тестируемом сервере это работает как ожидалось:)

Кто-нибудь когда-нибудь сталкивался с такой ситуацией?


изменить:

<?php
$command = "asd";

$t1 = time();

$output = Array();
$result = -5;
$r = exec($command, $output, $result);
$t2 = time();

echo "<pre>";
var_export(Array(
    'command'=>$command,
    'result'=>$result,
    'output'=>implode("\n", $output),
    'r'=>$r,
    't2-t1'=>$t2-$t1,
));
echo "</pre>";

Какую бы команду я не ввел $command, $result всегда будет -1, даже для несуществующих команд... это очень странно

4b9b3361

Ответ 1

Предполагая, что система возвращает $result == -1 является Unix-подобным (я не знаю, как вести себя с Windows с тем же кодом)

Функция exec() PHP (5.2.9) не вызывает примитив C exec() (который возвращает -1, если он не может заменить/выполнить процесс, что здесь не так). Вместо этого он вызывает popen(), который создает канал, выполняет fork() и выполняет команду с вашей командой. Значение return_value -1 не является прямым результатом примитива C, а построено PHP внутренне, в зависимости от способа обработки вашей команды. Другими словами, команда "ls" могла быть выполнена хорошо, в то время как, например, PHP не смог правильно закрыть канал.

Глядя на код C, в ext/standard/exec.c, может быть две причины, по которым код возврата равен -1, вызванный ошибкой; второй происходит после вызова popen()

  fp = VCWD_POPEN(cmd_p, "r");

  if (!fp) {
       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
       goto err;
  }
  // ...
  err:

  pclose_return = -1;
  goto done;

Однако в этом случае вы не увидите результат, и журнал покажет ошибку.

Позже значение return_value устанавливается через строку

  pclose_return = php_stream_close(stream);

Глядя на _php_stream_free() (php_stream_close() - это макрос, замененный на _php_stream_free()), наиболее вероятным кандидатом, который может вернуть -1, является

  ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);

Что в свою очередь косвенно вызывает примитив pclose() C. Согласно руководству

Функция pclose() возвращает -1, если wait4 (2) возвращает ошибку или обнаруживается некоторая другая ошибка.

Кажется, что была обнаружена ошибка во время закрытия канала, что не мешает результирующим данным быть установленным. Чтобы строго определить причину, необходимо проверить настройки и журналы операционной системы, параметры конфигурации и компиляции PHP.

Я бы порекомендовал

  • применить исправления для вашей ОС и, возможно, обновить до более поздней версии (если применимо),
  • чтобы обновить PHP до 5.3.3 (последний на данный момент), поскольку код PHP exec() значительно изменился.

Помните, что в версии 5.3 были внесены изменения, связанные с модулем SUHOSIN PHP, которые по умолчанию повышают безопасность при запуске файлов PHP.

Ответ 2

Убедитесь, что вы не работаете в безопасном режиме и что exec не указан в disable_functions в php.ini.

Любая из этих ситуаций приведет к ошибке exec(), хотя я думаю, что уведомление будет поднято.

Ответ 3

Можем ли мы получить результат strace'ing процесса PHP? Вероятно, это будет содержать ответ, который мы ищем.

Также 5.2.14 является самой новой из серии 5.2. Есть ли у вас шанс попробовать? Если вы используете хостинг-провайдера, вы все равно можете запустить его локально, чтобы увидеть, изменилось ли поведение.

Ответ 4

Я попробовал это на двух разных Linux-ПК (PHP 5.03 и PHP 5.2.10) - оба работали отлично.

Пример PHP 5.2.10:

array (
  'command' => 'ls',
  'result' => 0,
  'output' => 'atmail
...
vhosts',
  'r' => 'vhosts',
  't2-t1' => 0,
)

Я бы дважды проверял любые директивы, связанные с безопасностью, в вашем файле php.ini, проверял разрешения файлов в каталоге, который вы пытаетесь выполнить, и посмотрите, есть ли у вас SELinux и/или AppArmor.

Вы также можете рассмотреть возможность использования altenrative, например opendir()/readdir().

IMHO.. PSM

Ответ 5

Похоже, проблема была исправлена ​​администратором сервера. Я понятия не имею, что он сделал, но теперь он работает. Дело в том, что администратор сервера довольно "строгий", и, возможно, он немного ограничился каким-то системным конфигом. Например, из оболочки SSH я не мог видеть, где были установлены бинарные файлы php. Я уверен, что оболочка SSH была chrooted, а также веб-сервер (либо это, либо они были совершенно разными серверами, но я не знаю, как это было возможно без использования какого-либо монтирования)...