Исполняемый файл PHP будет работать в командной строке, но не в браузере

Таким образом, использование «php index.php» дает мне результат, который я хочу получить в командной строке. Но не даст вывод на веб-странице. Итак, прежде всего у меня есть этот файл Python, который в основном делает все, что я хочу:

import subprocess

subprocess.call("sudo nmap -sP 192.168.1.0/24 > /home/pi/whohome.txt", shell=True)

searchfile = open("/home/pi/whohome.txt", "r")
for line in searchfile:
    if "android-5ab6eb374b5fd6" in line: print "Jeremy is home (phone)"
    if "Jeremys-MBP" in line: print "Jeremy is home (computer)"
    if "LMCMs-iPhone" in line: print "Liam is home (phone)"
    if "Liam" in line: print "Liam is home (computer)"
    if "android-4a186cbbeb2c5229" in line: print "Lara is home (phone)"
    if "LaraD" in line: print "Lara is home (computer)"
    if "KristiansiPhone" in line: print "Martin is home (phone)"
    if "Martins-MBP" in line: print "Martin is home (computer)"

searchfile.close()

Во-вторых, у меня просто есть исполняемый файл sh, который поместит вывод этой команды python в другой текстовый файл:

python /home/pi/myRoomMates.py > /var/www/html/website.txt

Затем у меня есть php-файл на веб-сервере apache, работающем на Raspberry Pi, он гласит:

<?php
shell_exec('/home/pi/whoishome.sh');

echo file_get_contents ("/var/www/html/website.txt");

?>

Так что, если я не ошибаюсь, каждый раз, когда страница обновляется, она должна выполнять это, ждать завершения exec, а затем отображать содержимое txt файла? Я пробовал как shell_exec, так и просто exec, они оба делают то же самое.


person Jeremy de Rooy    schedule 23.05.2016    source источник
comment
я делаю ставку на разрешения.   -  person    schedule 23.05.2016
comment
Я удваиваю ставку @Dagon   -  person Darren    schedule 23.05.2016
comment
@ Даррен, мы не можем проиграть, у меня есть система   -  person    schedule 23.05.2016
comment
@Dagon Хорошо, я только что сделал chmod 777 для обоих каталогов, каталога /home/pi/ и каталога /var/www/html. Что, похоже, что-то сделало, поскольку теперь файл веб-сайта.txt меняется, но каждый раз, когда я обновляю веб-страницу, файл txt очищается. Тем не менее, если я запускаю php через командную строку, он дает правильный вывод в файле txt.   -  person Jeremy de Rooy    schedule 23.05.2016
comment
Я не знаю, включает ли ОС Raspberry Pi расширения безопасности SELinux, но если это так (и они включены), то возможно, что один или несколько из этих выходных файлов создаются в контексте безопасности, который делает их невидимыми для apache. (или в один из порожденных им процессов, таких как python или bash). Это может объяснить, почему website.txt оказывается пустым (или кажется пустым). Проверьте контекст безопасности файла с помощью ls -Z *whatever*.   -  person Kevin J. Chase    schedule 23.05.2016
comment
@KevinJ.Chase Просто ставит вопросительный знак рядом с файлом, но погуглил, как отключить SELinux, и выполнил эти команды, но не похоже, что SELinux находится где-либо в системе.   -  person Jeremy de Rooy    schedule 23.05.2016
comment
Действительно ли файл website.txt усекается до 0 байт при перезагрузке страницы? Это будет означать, что скрипт python работает, но выдает 0 байтов вывода. Есть ли какие-либо полезные данные в файле whohome.txt?   -  person Kevin J. Chase    schedule 23.05.2016
comment
@KevinJ.Chase доставляет оба с нулевыми байтами после перезагрузки страницы, но когда php запускается в командной строке, они оба содержат правильные выходные данные. Таким образом, команда python работает как таковая, просто производя вывод 0..   -  person Jeremy de Rooy    schedule 23.05.2016
comment
@KevinJ.Chase, извините, я очень новичок в программировании, а что нет, но заменит ли это раздел «файл поиска»? Можете ли вы показать мне, как будет выглядеть файл Python с этим subprocess.check_output. И где я могу сослаться на этот вывод, чтобы разместить его на веб-сервере?   -  person Jeremy de Rooy    schedule 23.05.2016
comment
Смотрите мой ответ для примера кода.   -  person Kevin J. Chase    schedule 23.05.2016
comment
Не забудьте восстановить нормальные права доступа ко всем файлам и каталогам, которые вы chmod изменили на 0777, и в целом повторно включить любую защиту, которую вы отключили при устранении неполадок.   -  person Kevin J. Chase    schedule 23.05.2016


Ответы (2)


Есть много прав, вы должны обеспечить:

  1. пользователь apache должен быть в группе sudoers
  2. пользователь apache должен писать в /home/pi/whohome.txt
  3. пользователь apache должен писать в /var/www/html/website.txt
  4. /home/pi/whoishome.shдолжен быть исполняемым для пользователя apache

Для пунктов с 1 по 3 обычно не рекомендуется давать пользователям Apache эти права.

Вы можете упростить задачу, если запустите свой скрипт Python как CGI:

import subprocess

ADDRESS = "192.168.1.0/24"

USERS = {
    "android-5ab6eb374b5fd6": ("Jeremy", "phone"),
    "Jeremys-MBP": ("Jeremy", "computer"),
    "LMCMs-iPhone": ("Liam", "phone"),
    "Liam": ("Liam", "computer"),
    "android-4a186cbbeb2c5229": ("Lara", "phone"),
    "LaraD": ("Lara", "computer"),
    "KristiansiPhone": ("Martin", "phone"),
    "Martins-MBP": ("Martin", "computer"),
}


nmap = subprocess.Popen(["sudo", "nmap", "-sP", ADDRESS], stdout=subprocess.PIPE)
for line in nmap.stdout:
    for user, name in USERS.items():
        if user in line:
            print "%s is home(%s)" % name
nmap.wait()

Должны быть выполнены только пункты 1 и 4.

person Daniel    schedule 23.05.2016

Я подозреваю, что ваша проблема связана с частью sudo командной строки nmap. Если вы замените subprocess.call на subprocess.check_call, я думаю, вы найдете эта команда вызывает ошибку CalledProcessError.

Предположительно, ваша учетная запись пользователя находится в файле /etc/sudoers, а веб-сервер — нет.

Поскольку первое, что делает оператор перенаправления вывода оболочки (>), — это усекает выходной файл, эта неудачная попытка запустить nmap приводит к нулевому байту whohome.txt. Затем остальная часть скрипта Python делает то же самое с website.txt, и в итоге на вашем веб-сайте нечего отображать.

Решения

sudo не требуется.

На моем рабочем столе Linux мне не нужно запускать nmap от имени пользователя root для выполнения локального ping-сканирования. Если это так в вашей системе, то вы сможете просто удалить часть sudo вашей команды nmap и покончить с этим.

Однако есть разница. nmap выполнит более тщательное тестирование каждой цели, когда root запустит проверку связи -pS. Со старой справочной страницы nmap (выделено мной):

-sP (пропустить сканирование портов).

[...]

Параметр -sP по умолчанию отправляет эхо-запрос ICMP, TCP SYN на порт 443, TCP ACK на порт 80 и запрос метки времени ICMP. При выполнении непривилегированным пользователем на порты 80 и 443 адресата отправляются только пакеты SYN (с помощью вызова соединения). Когда привилегированный пользователь пытается сканировать цели в локальной сети ethernet, используются запросы ARP, если не указано --send-ip. [...]

Включите sudo для вашего веб-сервера.

Если вам нужна эта дополнительная информация (и похоже, что вам это нужно), вам нужно запустить nmap (или скрипт Python, который его вызывает) с привилегиями суперпользователя. Я никогда не пытался заставить веб-сервер сделать это, но я предполагаю, что вам, по крайней мере, придется добавить пользователя вашего веб-сервера в /etc/sudoers. Что-то вроде:

apache    localhost=/usr/bin/nmap -sP

or:

httpd    ALL=/usr/local/bin/nmap

... и так далее, в зависимости от имени пользователя, где находится ваш nmap, насколько строго вы хотите ограничить аргументы до nmap и т. д.

Создайте исполняемый файл SUID для запуска nmap.

В качестве альтернативы (и я ненавижу себя за эту рекомендацию --- должен быть лучший способ) написать маленькую программу SUID (Set User ID), которая выполняет только nmap команда, которую вы хотите. Вот программа на C, которая сделает это:

#include <stdio.h>
#include <unistd.h>

int main(void);

int main(void) {
    int retval = 0;
    char* const error_string = "ERROR: Failed to execute \"/usr/bin/map\"";
    char* const nmap_args[] = {
      "/usr/bin/nmap",
      "-sP",
      "192.168.1.0/24",
      NULL
    };

    retval = execv("/usr/bin/nmap", nmap_args);
    /* execv returns _only_ if it fails, so if we've reached this
     * point, print an error and exit.
     */
    perror(error_string);
    return retval;
}

Сохраните приведенное выше как что-то вроде nmap_lan.c и скомпилируйте с помощью:

$ gcc -Wall -o nmap_lan nmap_lan.c

Затем переместите его туда, где хранятся сценарии вашего веб-сайта, и от имени пользователя root измените его владельца и разрешения:

# chown root:root nmap_lan  # Or whatever group name you use.
# chmod 4555 nmap_lan

Ведущий 4 устанавливает бит SUID. Цвет каталога ls, вероятно, покажет, что этот файл выделен. Разрешения должны выглядеть так:

# ls -l nmap_lan
-r-sr-xr-x. 1 root root 6682 May 23 03:04 nmap_lan

Любой пользователь, запускающий nmap_lan, будет временно повышен до владельца файла nmap_lan (в данном случае root) до выхода из программы. Это чрезвычайно щедро, поэтому я жестко запрограммировал все в этой программе... Чтобы изменить что-либо, что она делает --- даже диапазон IP-адресов для сканирования --- вам придется отредактировать nmap_lan.c файл, перекомпилировать и переустановить.

Я протестировал nmap_lan в своей командной строке, и он выдает вывод привилегированного пользователя nmap при запуске непривилегированным пользователем, который обычно получает только ограниченный вывод.

Комментарии к скрипту Python

В общем, Python намного лучше анализирует аргументы оболочки, чем сама оболочка (значение по умолчанию для shell равно False по какой-то причине), поэтому ваш сценарий Python должен выполнять как можно больше работы, включая разбор команды оболочки, перенаправление ввода и перенаправление вывода.

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

Я бы переписал эту команду call, чтобы использовать список явно разделенных аргументов. Вы можете обрабатывать перенаправление вывода, передав открытый файловый поток параметру stdout. Вы можете избавиться от последней части перенаправления оболочки, заставив Python открыть выходной файл и явно записать в него.

nmap_file='/home/pi/whohome.txt'
with open(nmap_file, 'wt', encoding='ascii') as fout:
    subprocess.call(
      ['/usr/bin/nmap', '-sP', '192.168.1.0/24'],  # Or just ['nmap_lan']
      stdout=fout,
      universal_newlines=True,
      )

output_file='/var/www/html/website.txt'
with open(nmap_file, 'rt', encoding='ascii') as fin:
    with open(output_file, 'wt', encoding='ascii') as fout:
        for line in fin:
            ...
            print('Output here', file=fout)  # Add `file=...` to each print.

Кроме того, если вам не нужен этот файл whohome.txt для чего-то другого, вы можете полностью удалить его, используя check_output, чтобы сохранить вывод команды nmap в виде строки, а затем разбить ее на отдельные строки. (Параметр universal_newlines также обрабатывает преобразование объекта bytes в str, по крайней мере, в Python 3.)

lines = subprocess.check_output(
  ['/usr/bin/nmap', '-sP', '192.168.1.0/24'],  # Or just ['nmap_lan']
  universal_newlines=True
  ).split('\n')

output_file='/var/www/html/website.txt'
with open(output_file, 'wt', encoding='ascii') as fout:
    for line in lines:
        ...
        print('Output here', file=fout)  # Add `file=...` to each print.

Обратите внимание, что я использовал блоки with, чтобы закрыть файл бесплатно.

(Наконец, эта последовательность команд if требует перезаписи в виде цикла for machine in machines_dict:, в котором строки, которые вы ищете, являются ключами в этом словаре, а вывод, который вы хотите распечатать, — значениями.)

person Kevin J. Chase    schedule 23.05.2016
comment
Абсолютная легенда, вы попали в точку, это была команда sudo. Не совсем уверен, почему у меня даже был sudo для nmap, поскольку он работает без него. Но удаление sudo и все просто сработало. Спасибо, Кевин! - person Jeremy de Rooy; 23.05.2016
comment
Единственное, необходимо запустить nmap от имени sudo, поскольку некоторые устройства не отображаются при сканировании nmap, если оно не запущено от имени sudo. - person Jeremy de Rooy; 23.05.2016
comment
@JeremydeRooy: я обновил свой ответ. Я не могу легко и надежно протестировать sudo под Apache на имеющейся у меня установке, поэтому я не могу сделать ничего, кроме как указать на /etc/sudoers (и, возможно, включить файлы в /etc/sudoers.d/) и предположить, что вашему пользователю Apache требуется разрешение на запуск либо nmap или ваш скрипт Python через sudo. (Альтернативой является написание крошечной программы Set-User-ID (SUID), которая запускает только nmap -sP ..., но вы хотели бы сделать это на скомпилированном языке, таком как C. Сценарии SUID очень трудно безопасный (а сценарии SUID shell практически невозможно защитить). - person Kevin J. Chase; 23.05.2016
comment
@JeremydeRooy: я добавил программу SUID в свой ответ в качестве третьего возможного решения. (Однако я ненавижу это. Должен быть способ сделать это с помощью Apache, или PHP, или sudo, или... чего-то еще.) - person Kevin J. Chase; 23.05.2016
comment
Черт возьми, это впечатляющий ответ - здорово! - person Darren; 24.05.2016