Note: I may have stolen this idea from Gnat and Tom’s Perl Cookbook or some other high profile source. However, this tidbit is useful to publicize. The code below is my own.
Here’s a random Perl tip that you may find helpful. In certain flavors of UNIX, including Linux and Solaris, the ownership of open filehandles is inherited by child processes. Although this doesn’t seem all that useful at first, consider the case in which you have a forking server that writes to a log file. You’d like all the child processes to write to that log file too, but the children run as a different user. One solution is to open a filehandle in the parent as a globally visible variable (both lexically scoped scalars and package-global filehandles work). After the parent forks and changes real and effective UIDs, the child process has that same open filehandle. Take a look at the code sample below. There is no tricky Perl here (except for setting the autoflush to prevent a weird extra copy of sample string from appearing in the outfile when the process forks [I don’t know why I had to do this, but it seems to be an stdio buffering issue]).
!/usr/bin/env perl
#
unless ($> == 0) { print “need to be root to run this (child process changes UID)\n”; exit; }
$< = $> = 0; unlink ‘testing’;
print “Current real ID: $>\n”, “Opening filehandle\n”; open my $in, “>testing” or die “can’t open ‘testing’ $!\n”; select((select($in), $|++)[0]);
print $in localtime() . “: $< says ‘hello’ from $$\n”;
my ($nobody_uid) = (getpwnam(‘nobody’))[2]; print “ID of ‘nobody’: $nobody_uid\n”;
print “Forking and changing real and effective ID to $nobody_uid\n”;
$SIG{CHLD} = sub { wait }; my $pid = fork;
if ($pid) { sleep(1); print $in localtime() . “: $< says ‘hello’ from $$\n”; close $in; exit; }
sleep(3); $< = $> = $nobody_uid;
print $in localtime() . “: $< says ‘hello’ from $$\n”; ; close $in;
try appending to file as this user
open my $try, “>>testing” or die “can’t open ‘testing’: $!\n”; print $try “I can write to this file and I’m $<\n”; close $try;
If you run this code on a compatible system, your ‘testing’ outfile should
have two lines in it from different processes and different UIDs. Of course,
you need to be aware of the lurking dangers of multiple processes trying to
write to the same file at the same time. This resource contention issue may
be solved with a variety of IPC synchronization techniques, such as semaphores
and lock files. I like to use lock files, although I find flock a bit
cumbersome. Unfortunately, semaphores are even more troublesome, as
a quick look at the perlipc manpage reveals. Conceptually simple,
semaphors require some weird pack
and unpack
formats and IPC::SysV
constants to work.
Eek.
Now, you have one less excuse to run your perl daemons as root.