Often, admin and web-based programming tasks require synchronization between different components of the system, for example, a webmord accepts a command to perform some action, it is desirable to perform this action as early as possible, but the web interface itself cannot firewall or routing table simply because root authority is required). I usually solved it in an ugly and inefficient way - the web interface wrote a command to some special file, and another shell script (working as root) looped through the file once every few seconds, and if there are commands, I processed them.
In this post I will describe a simple way that:
- no programming required - only unix-way system building from small bricks
- it doesn’t wipe a lot of resources (you don’t need to waste a file, and the program itself weighs much less shell)
- triggered instantly
The solution is peeped in qmail's source. The standard read () operation is blocked if there is no read data (except for the case when you specifically set non-blocking options for the socket). read () + fifo files solve the problem. The handler must read the data from the fifo file. While it is empty - it will simply go to sleep and will not create a load. And as soon as the data appears, read () is completed, and processing code can be executed.
')
To do this, "on the knee" was written a simple program from the
trigger . When launched, it listens to the file specified by it, and when data appears in it, it immediately calls the command specified in the parameters (from the user from whom the trigger is running, and not from the user who “pulled the trigger”)
xenon@dot:~/trigger-1.0$ ./trigger /tmp/myfifo ./prog.sh
fifo: /tmp/myfifo, program: ./prog.sh
file /tmp/myfifo doesn't exists, create it
created fifo /tmp/myfifo
xenon@dot:~/trigger-1.0$ chmod a+w /tmp/myfifo
xenon@dot:~/trigger-1.0$ pidof trigger
8552
The program started (by the daemon), created the FIFO file myfifo and when the data appears in it, it will launch the program prog.sh. With the second command, we allowed everyone to write in the FIFO, but, of course, you can configure it more carefully (for example, allow recording for group members and assign the group www-data).
The prog.sh script itself is very simple in our case - it simply writes the start time to the log:
xenon@dot:~/trigger-1.0$ cat prog.sh
#!/bin/sh
echo `date` started, ui: `whoami` >> program.log
Naturally, in your case, you need to replace it with your script that will do the work.
Now check:
www-data@dot:/tmp$ id
uid=33(www-data) gid=33(www-data) =33(www-data)
www-data@dot:/tmp$ echo > /tmp/myfifo
www-data@dot:/tmp$ echo > /tmp/myfifo
www-data@dot:/tmp$ echo > /tmp/myfifo
www-data@dot:/tmp$ exit
Look what happened:
xenon@dot:~/trigger-1.0$ cat program.log
. . 1 20:47:47 NOVST 2011 started, ui: xenon
. . 1 20:47:48 NOVST 2011 started, ui: xenon
. . 1 20:47:49 NOVST 2011 started, ui: xenon
As you can see, the script started at the same second as the command was sent. Voila!
The trigger itself weighs only 12Kb, and the blocked read () doesn’t particularly load the system and works instantly.
Now you can easily stick the web interface to system operations (reboot the machine, restart the service, add rules to iptables, etc.). Naturally, we believe that the untrusted code from the web server group does not work, and access to the web interface will be password protected via https and only from the local network. You have a trigger, so now it's easier to shoot your foot. :-)