Доброго времени суток. Столкнулся с такой вот задачей — через SQL запрос сбросить PHP-кеш. Проще говоря, удалить несколько файлов в определённом каталоге. На входе имеется:
- СУБД – PostgreSQL 9.3
- ОС – Linux (Fedora, но не суть важно)
В итоге, получилось решение в связке python + C + bash (всего понемногу). Немного не Unix-way, но может кому-то пригодится.
Обернём команду удаления PHP-кеша в bash-скрипт, подумав про его дальнейшее расширение новым функционалом. Вообще говоря, было бы неплохо передавать нужную команду в виде параметра. Вот этот скрипт:
#!/bin/bash
function __clear_cache() {
rm /var/www/html/cache/*.php
}
FUNCS=()
FUNCS+=("__clear_cache")
function function_exists() {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
if function_exists "$1" "${FUNCS[@]}"; then
eval "$1"
else
echo "Function $1 does not exists"
exit 1
fi
Тут мы объявляем массив, который будет содержать список функций (т.е команд) и, перед вызовом переданной команды (т.е функции), проверяем, есть ли она в списке. Иначе нехороший пользователь может передать что-то типа rm -rf ... в качестве параметра, что успешно выполнится в eval. Установим владельца для этого скрипта root (хотя и apache хватит, но думаем про расширяемость, не забывая про осторожность) и сделаем скрипт выполняемым:
chown root:root sysutils
chmod ugo+x sysutils
Выполнять этот скрипт из СУБД можно через C (долгий путь, так как необходимо заморачиваться с созданием расширения к PostgreSQL) или через неуправляемый скриптовый язык, коим является plpython. Но для начала задумаемся — скрипт из СУБД будет выполняется с правами пользователя postgres, а удалить кеш может только apache (как минимум). Но не беда, есть же такая штука, как SUID флаг. Только вот проблема в том, что в Linux нельзя установить SUID флаг для скриптов (подробнее — тут). Вернее, можно, но эффективный user ID всё равно будет таким же как и реальный. Попробуем обойти это ограничение, написав небольшую программу на C, в которой будет вызов нашего скрипта. Вот её код:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
setuid(0);
char command[255];
if (argc == 2)
{
sprintf(command, "/usr/local/bin/sysutils %s", argv[1]);
system(command);
}
else
{
printf("USAGE: sysutils-core <command>n");
}
return 0;
}
Сперва мы устанавливаем эффективный user ID, затем вызываем скрипт, передав ему параметр – требуемую команду. Скомпилируем программу и установим SUID-флаг:
gcc -o sysutils-core sysutils-core.c
chmod u+s sysutils-core
Проверяем:
su postgres
./sysutils-core clear_cache
Теперь перейдём к СУБД-части.
Установим расширение plpython3 (предварительно установленное в систему), выполнив SQL-команду в соответствующей БД:
CREATE EXTENSION plpython3u;
Или используя консоль:
createlang plpython3u -h localhost -U postgres testdb
Функция в СУБД, через которую будет сбрасываться кеш имеет вид:
CREATE OR REPLACE FUNCTION clear_cache ( )
RETURNS void AS
$BODY$
import os
os.system("/usr/local/bin/sysutils-core clear_cache")
$BODY$
LANGUAGE plpython3u VOLATILE;
Тут просто вызов sysutils-core с параметром clear_cache. Проверяем:
SELECT clear_cache ( );
При необходимости, вызов функции может быть не только под postgres (именно этот пользователь может создавать функции на неуправляемых языках), в таком случае функции при создании необходимо указать опцию — SECURITY DEFINER (аналог SUID в СУБД).
Вот и всё. При желании, можно добавить передачу аргументов, новые команды да и вообще управлять linux-сервером через СУБД. Пусть это будет домашним заданием.
Автор: blackmaster