ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I disagree with Sergei in that I think a named pipe is much safer (and a nice privilege barrier) than having a PHP script create a file.
Let me illustrate. Save the below script in e.g. /usr/local/bin/interfacer. Create /var/lib/interfacer, owned by a user with sufficient rights, with read and traverse access to everybody, and write access for only the user: u=rwx,g=rx,o=rx. Then, run this script as that user.
Code:
#!/bin/bash
CONTROL=/var/lib/interfacer/control
PIDFILE=/var/run/interfacer.pid
# If the named pipe already exists, this script is already running.
if [ -p "$CONTROL" ]; then
echo "$CONTROL exists; already running." >&2
exit 1
fi
# Automatically remove the pipe and the pid file when done.
trap "rm -f '$CONTROL' '$PIDFILE'" EXIT
# If this is running as a service, we need to ignore HUP and USR1 signals.
trap "" HUP USR1
# Try creating the named pipe.
# If this fails, the trap will (try to) remove $CONTROL.
if ! mknod -m 0622 "$CONTROL" p ; then
echo "Cannot create $CONTROL." >&2
exit 1
fi
# Save the PID of this process in the pid file.
if ! echo "$$" > "$PIDFILE" ; then
echo "Cannot write pid to $PIDFILE." >&2
exit 1
fi
# At this point, we probably need to be quiet.
# You could redirect output and error to a log file here.
exec < /dev/null
exec > /dev/null
exec 2> /dev/null
# Infinite loop.
while [ 1 ] ; do
# Grab up to a megabyte from the pipe.
read -N 1048576 LINE < "$CONTROL"
/* Remove unused characters from LINE. */
LINE="${LINE//[^-.0-9A-Za-z_]/}"
/* Process commands from the named pipe. */
case "$LINE" in
interface)
/usr/local/wifi/bin/interface
;;
esac
done
exit 0
If you want to run that from an init script, or otherwise want it to run as a daemon, start it in a new session, in the background of a subshell:
to have the service script act. You can quite easily extend this for other scripts, too.
Note that this does use a bit more resources than the CGI alternative, because you have to have the Bash script (and therefore Bash shell) running all the time. It blocks in the read, so it does not consume any CPU time, but it does use memory (about a megabyte on x86, but most of that is code and thus shared; only about 250 kilobytes of unshareable data). If you have using a board with very little RAM, this is something you should be aware of.
Named pipe requires more attention than just creating a file. Try writing to named pipe while the the other side is not reading from it, for example.
Stalling/blocking in the case of no reader is easy to work around: just set an empty SIGALRM handler, and a short timeout alarm. The write operation will then fail when the alarm goes off.
Here's a working PHP example:
Code:
<?PHP
define("CONTROL_PIPE", "/var/lib/interfacer/control");
define("CONTROL_TIMEOUT", 3); /* An integer number of seconds */
/* Set up an empty SIGALRM handler.
* Note: User ticks are not needed, since nothing() does not need
* to be executed by the PHP interpreter.
*/
function nothing () { return; }
pcntl_signal(SIGALRM, 'nothing', FALSE);
/* Write a string to interfacer.
* Returns 0 if success, error code otherwise.
*
* Any pending timeout (alarm) will be re-armed,
* not counting the time spent in this function.
*/
function interfacer($command)
{
if (!is_string($command))
return 1; /* Command is not a string */
$length = @strlen($command);
if ($length < 1)
return 2; /* Empty command */
if (@filetype(CONTROL_PIPE) != 'fifo')
return 3; /* No control pipe */
/* Write $command to CONTROL_PIPE, with a
* CONTROL_TIMEOUT -second timeout.
*/
$pending = pcntl_alarm(CONTROL_TIMEOUT);
$result = @file_put_contents(CONTROL_PIPE, $command);
pcntl_alarm($pending);
if ($result < $length)
return 4; /* Cannot write to the pipe */
/* Success. */
return 0;
}
$result = interfacer("interface");
if ($result !== 0)
echo "Failed (reason $result)\n";
else
echo "Success!\n";
?>
(In a PHP page, each $result value should probably produce a different message.)
Quote:
Originally Posted by Sergei Steshenko
I suggested the simplest and most forgiving solution possible.
I disagree, at least with the "most forgiving" part.
The service script will have to either monitor the directory with the marker file (via e.g. inotifywait), or periodically check the existence of the file. inotifywait is usually not part of the default install. Periodically checking the file existence will consume power; depending on the period, the small board may not get into real deep sleep states.
Using files the PHP script has no indication of whether the service is active or not.
With the above PHP snippet and named pipes, the PHP can detect if the service is not running.
Allowing PHP scripts to create files is a security risk. (The risk is significantly smaller if the using a dedicated directory outside the web tree.)
The main source of the danger is not this PHP script (since you can make it very secure), but the fact that all other PHP scripts on your server can write there too. If there is a bug in one of them, an attacker can upload files, and even binaries to that directory, making certain attack vectors much easier.
Using a named pipe the filesystem access mode for the pipe works as a privilege separation barrier. The client (the PHP running as the same user as Apache in this case) only requires write access to the pipe. The server script I wrote earlier does not assume anything about the data from the pipe, and will discard extraneous data and possibly malign characters (escape sequences and whatnot).
Therefore, I claim named pipes are more forgiving than files, and overall a better choice.
I tried the script, the interfacer was running in background and i included the extra line in the php. I also has exec() call to the binary. However the wlan0 interface is still up and ifdown has not been executed.
... Periodically checking the file existence will consume power; depending on the period, the small board may not get into real deep sleep states.
...
?????????????
How does 'sleep(1)' consume power other than the fact that kernel once per second allows the task to check file existence ? And how can you prove that checking availability of data in a pip consumes less power ? Checking availability of data is also a periodic check - of circular buffer position.
Try to implement the simplest version I've suggested - it's very easy to debug. And security is not much of a concern - when you have it working, it'll be easy to add security measures.
...[*] Allowing PHP scripts to create files is a security risk. (The risk is significantly smaller if the using a dedicated directory outside the web tree.)
The main source of the danger is not this PHP script (since you can make it very secure), but the fact that all other PHP scripts on your server can write there too. If there is a bug in one of them, an attacker can upload files, and even binaries to that directory, making certain attack vectors much easier.
...
Somehow I'm not scared. Or no more scared than realizing that living contains risk of death.
To be more particular:
A partition with no exec mounting can be created, so whatever an attacker uploads won't be executable;
Either RAMFS or TMPFS have a provision of limiting the filesystem size, so the attacker won't be able to upload much. The needed size is very small - one zero length request file plus whatever directory records take;
The root process monitoring creation/existence of the request file can unconditionally delete all other files. In fact, assuming the request file is called 'request' the acknowledge action can be not just 'rm request', but rather 'rm -rf *' - the action, of course, should be taken in the directory containing the request file.
.
I very strongly suggest sticking to KISS and "don't panic" principles.
How does 'sleep(1)' consume power other than the fact that kernel once per second allows the task to check file existence ?
That is the reason. Small boards often use a tickless kernel, and use larger intervals (for filesystem caching et cetera) to avoid waking up the kernel too often. Depending on the board, the differences are surprisingly large.
Quote:
Originally Posted by Sergei Steshenko
And how can you prove that checking availability of data in a pip consumes less power ? Checking availability of data is also a periodic check - of circular buffer position.
There is no periodic check with a named pipe.
Named pipe will block a reader in open(), until there is a writer present. If you bother to strace the script, you'd see that it blocks in the open() syscall; this means that the kernel will not give it any CPU time before a writer opens the named pipe. This allows the kernel to go into any sleep state.
A partition with no exec mounting can be created, so whatever an attacker uploads won't be executable;
That will not prevent the attacker from uploading shell or PHP scripts. PHP ignores the noexec flag (scripts are just data files to it), and shell scripts can be sourced instead of executed directly. Uploaded binaries are rare, but scripts are often used for making the machine a drop box.
Quote:
Originally Posted by Sergei Steshenko
Either RAMFS or TMPFS have a provision of limiting the filesystem size, so the attacker won't be able to upload much. The needed size is very small - one zero length request file plus whatever directory records take
A good idea, especially if you use flag directories (since their creation is atomic) instead of files. However, this means a new filesystem, additional system-level configuration, and plenty of resources taken up in the kernel.
Quote:
Originally Posted by Sergei Steshenko
The root process monitoring creation/existence of the request file can unconditionally delete all other files.
Another good idea, except that any small typo or bug in the script may cause it to inadvertently remove the wrong tree.
Quote:
Originally Posted by Sergei Steshenko
I very strongly suggest sticking to KISS and "don't panic" principles.
I fully agree. I just believe named pipes are simpler, safer, and more appropriate for this.
I tried the script, the interfacer was running in background and i included the extra line in the php. I also has exec() call to the binary. However the wlan0 interface is still up and ifdown has not been executed.
Did you run the interfacer with sufficient privileges, i.e. as root in your case? I suspect /usr/local/wifi/bin/interface is called, but with insufficient privileges to take down the interface. I did test both the script and the PHP code, and it works for me.
And, if the OP is working on some kind of wireless router and its web interface, power consumption doesn't matter - it's not a battery operated device in the first place.
And, if the OP is working on some kind of wireless router and its web interface, power consumption doesn't matter - it's not a battery operated device in the first place.
Thermal issues do matter. Usually these devices do have a very low TDP, but often even worse cooling. Allowing the CPU all the opportunity to sleep will help.
Do you have any specific reason to avoid named pipes here? I showed both the reader side (56 lines in Bash) and safe writer side (46 lines of PHP). As far as I know they are safe and complete for this use case, and IMHO quite simple. I don't know how much simpler than that you can get, and stay safe!
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.