
package JK::Lib::Daemon;

use strict;
use POSIX qw(setsid);
use JK::Lib::Misc;

my @EXPORT = qw/chid daemonize sighandler/;

my $childpid;

sub chid {
    my $user = shift;

    return unless $user;

    my ($uid,$gid) = (getpwnam($user))[2,3];

    if( $> == 0 || $< == 0){
        if( $uid && $gid ){
            debug( "switching to $uid/$gid" );
	    $! = 0;
	    $( = $) = $gid;
	    slowdie( "unable to chgid $user: $!" ) if $!;
	    $< = $> = $uid;
	    slowdie( "unable to chuid $user: $!" ) if $!;
        }else{
	    slowdie( "cannot chid to $user: uid for $user not found." );
        }
    }else{
        slowdie( "cannot chid when not running as root." ) unless( $uid == $> );
    }
}

sub daemonize {
    my $to = shift;
    my $name = shift;

    chdir('/');
    fork && exit;
    close STDIN;   open( STDIN,  '<',  "/dev/null" );
    close STDOUT;  open( STDOUT, '>>', "/dev/null" );
    close STDERR;  open( STDERR, '>>', "/dev/null" );
    setsid();

    $SIG{HUP} = $SIG{QUIT} = $SIG{INT} = $SIG{TERM} = sub { sighandler( $name, @_ ) };

    if( $name ){
        open(my $pid, ">/var/run/$name.pid") || die "cannot write to /var/run/$name.pid: $!\n";
        print $pid "$$\n";
        close $pid;
    }

    # run as 2 processes
    while( 1 ){
        if( $childpid = fork ){
            # parent
            wait;
            my $xcode = $?;
            $childpid = undef;
            verbose( "$name exited with code $xcode - restarting" );
            sleep $to;
        }else{
            # child

             $SIG{USR1} = sub {
                $::opt{D} = $::opt{D} ? undef : 1;
                verbose( "USR1 received - changing debug" );
             };

            return;
        }
    }
}

sub sighandler {
    my $name = shift;
    
    if( $childpid > 1 ){
        if( $name ){
            unlink( "/var/run/$name.pid" ) || verbose( "cannot unlink /var/run/$name.pid: $!" );
        }

        kill "TERM", $childpid;
        wait;
    }

    verbose( "caught signal SIG$_[0] - exiting" );
    exit;
}

sub import {
    my $pkg = shift;
    my $caller = caller;

    for my $f ( @EXPORT ){
        no strict;
        *{$caller . '::' . $f} = $pkg->can($f);
    }
}

1;
