Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

jjohn (22)

jjohn
  (email not shown publicly)
http://taskboy.com/
AOL IM: taskboy3000 (Add Buddy, Send Message)

Perl hack/Linux buff/OSS junkie.

Journal of jjohn (22)

Monday September 22, 2003
06:00 AM

child processes and filehandles

[ #14830 ]

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.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.