brasey has asked for the wisdom of the Perl Monks concerning the following question:

Here's what my goal is:

I have an application that controls a printer. It can be given arguments to affect its behavior. It prints status messages to STDOUT. It is not written in Perl.

I would like my daemon script to spawn children, each one of which is an incarnation of this print app, run with its own arguments. I would like the output of each child to print to its own file in realtime (i.e. not buffered) so I can tail -f to see what a given printer is up to at any given time. The daemon should also listen for signals and terminate the children nicely.

Here's what I've got so far:

For testing purposes, replace the print app with this:

#!/usr/bin/perl -w use strict; while (1) { print localtime() . "\n"; sleep 1; }

Here is how far I've gotten with the daemon:

#!/usr/bin/perl -w use strict; use POSIX; use IO::File; my $time_to_die = 0; my $pid = fork; exit if $pid; die "Couldn't fork: $!" unless defined $pid; print "daemon pid = $$\n"; POSIX::setsid() or die "Can't start a new session: $!"; for my $n (1 .. 5) { my $pid; my $logfile = $n . '.log'; if ($pid = fork) { print "child pid = $pid\n"; } else { my $fh = IO::File->new; $fh->open("> $logfile") or warn "Couldn't open $logfile: $!\n" +; open (STDOUT, ">&" . fileno($fh)) or warn "Couldn't redirect S +TDOUT to $logfile: $!\n"; STDOUT->autoflush(1); $SIG{INT} = "IGNORE"; my $command = ("/home/brasey/daemon/timer"); exec $command; } } until ($time_to_die) { $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&sig_handler; sleep; } exit 0; sub sig_handler { print "time to die\n"; $time_to_die = 1; kill ('HUP', -$$); }

(Also linked here)

Here's the skinny:

Basically, it all works, but output is buffered. I've tried these methods:

$| = 1; $^F = 10000; STDOUT->autoflush(1);

Where have I gone wrong?

Bob

Replies are listed 'Best First'.
Re: Writing a perl daemon script - having trouble with output
by calin (Deacon) on Jul 07, 2004 at 18:39 UTC

    I think the blackbox printer application does its own buffering. AFAIK, buffering is userspace stuff (done in libc or in other high level file handling library) and there's no way you can control it from the parent process. $|=1 sets Perl's idea of autoflush. After exec, Perl is gone. The exec'ed process inherits the bare POSIX STDOUT filehandle, not the stdio.h FILE struct, or the new perlio buffering filehandle object if you're using newer Perls etc.

    Update: expanded a bit

Re: Writing a perl daemon script - having trouble with output
by Anonymous Monk on Jul 07, 2004 at 22:34 UTC
    The previous poster was correct that the exec'ed application will do its own buffering, since its output has been redirected to a file (output to the terminal is just line-buffered, usually). If you have the source for that app, you could just flush after each line you print.

    Another approach would be to start the app using Expect.pm, which tricks apps you spawn into thinking they're talking to a terminal.

Re: Writing a perl daemon script - having trouble with output
by brasey (Initiate) on Jul 08, 2004 at 01:46 UTC
    Of course, you were both right.

    I added

    $| = 1;

    to the little timer app, and the output was unbuffered. Thanks for the advice!