Flushing PHP cache via SQL query or from a sparrow cannon

  • Tutorial

Good day. Faced such a problem here - through SQL query to flush PHP-cache. Simply put, delete multiple files in a specific directory. At the entrance there is:
  • DBMS - PostgreSQL 9.3
  • OS - Linux (Fedora, but not the point)

As a result, we got a solution in the python + C + bash bundle (a bit of everything). Not a bit of a Unix-way, but it might come in handy for someone.

We wrap the PHP cache removal command in a bash script, thinking about its further expansion with new functionality. Generally speaking, it would be nice to pass the desired command as a parameter. Here is the script:
#!/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

Here we declare an array that will contain a list of functions (i.e., commands) and, before calling the transferred command (i.e., functions), we check if it is in the list. Otherwise, a bad user can pass something like rm -rf ... as a parameter, which will succeed in eval . Set the owner for this root script (although apache is enough, but we think about extensibility, not forgetting caution) and make the script executable:
chown root:root sysutils
chmod ugo+x sysutils

You can execute this script from a DBMS through C (a long way, since you need to bother with creating an extension to PostgreSQL) or through an unmanaged scripting language, which is plpython. But first, let's think about it - the script from the DBMS will be executed with the rights of the postgres user, and only apache (at least) can delete the cache. But it doesn’t matter, there is such a thing as a SUID flag. But the problem is that in Linux you cannot set the SUID flag for scripts (more details here ). Rather, it is possible, but an effective user ID will still be the same as the real one. Let's try to get around this limitation by writing a small C program that will call our script. Here is her code:
#include 
#include 
#include 
#include 
int main(int argc, char *argv[])
{
        setuid(0);
        char command[255];
        if (argc == 2)
        {
                snprintf(command, 255, "/usr/local/bin/sysutils %s", argv[1]);
                system(command);
        }
        else
        {
                printf("USAGE: sysutils-core \n");
        }
        return 0;
}

First, we set the effective user ID, then call the script by passing it the parameter - the required command. Compile the program and set the SUID flag:
gcc -o sysutils-core sysutils-core.c
chmod u+s sysutils-core

We check:
su postgres
./sysutils-core clear_cache

Now let's move on to the DBMS part.

Install the plpython3 extension (pre-installed on the system) by executing the SQL command in the appropriate database:
CREATE EXTENSION plpython3u;

Or using the console:
createlang plpython3u -h localhost -U postgres testdb

The function in the DBMS through which the cache will be flushed looks like:
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;

This is just a sysutils-core call with the clear_cache parameter. We check:
SELECT clear_cache ( );

If necessary, a function call can be not only under postgres (it is this user who can create functions in unmanaged languages), in this case, when creating functions, you must specify the option - SECURITY DEFINER (an analogue of SUID in the DBMS).

That's all. If you wish, you can add passing arguments, new commands, and generally manage the linux server through the DBMS. Let it be homework.

UPD : A safer version of a C program (via execl ):
Hidden text
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char *argv[])
{
	setuid(0);
	if (argc == 2)
	{
		int status = 0;
		int pid = fork();
		switch (pid)
		{
		case -1:
			printf("Fork sysutils process failed");
			exit(1);
		case 0:
			execl("/bin/bash", "bash", "/usr/local/bin/sysutils", argv[1], NULL);
		default:
			wait(&status);
		}
	}
	else
	{
		printf("USAGE: sysutils-core \n");
	}
	return 0;
}


Also popular now: