Beekeeper

 view release on metacpan or  search on metacpan

lib/Beekeeper/WorkerPool/Daemon.pm  view on Meta::CPAN

    unless ($self->{options}->{foreground}) {

        # Fork and exit parent
        _fork() && return;

        # Detach ourselves from the terminal
        POSIX::setsid() or die("Cannot detach from controlling terminal");

        # Prevent possibility of acquiring a controling terminal
        $SIG{'HUP'} = 'IGNORE';
        _fork() && CORE::exit(0);

        # Change working directory
        chdir "/";

        # Clear file creation mask
        umask 0;

        # Close open file descriptors
        my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
        $openmax = 64 if (!defined($openmax) || $openmax < 0);
        foreach my $i (0..$openmax) { POSIX::close($i); }

        $self->redirect_output;

        $self->{daemonized} = 1;
    }

    $self->write_pid_file;

    $self->change_effective_user;

    $self->main;

    CORE::exit(0);
}

sub _fork {
    FORK: {
        if (defined(my $pid = fork())) {
            return $pid;
        }
        elsif ($! =~ /No more process/) {
            sleep(5);
            redo FORK;
        }
        else {
            die("Can't fork: $!");
        }
    }
}


sub redirect_output {
    my $self = shift;

    my $logfile = $self->{config}->{log_file};

    unless ($logfile) {
        my $dir  = $LOG_FILE_DIR;
        my $user = getpwuid($<);
        my $file = $self->daemon_name . '.log';
        $logfile = (-d "$dir/$user") ? "$dir/$user/$file" : "$dir/$file";
    }

    die unless ($logfile =~ m/\.log$/);

    open(LOG, '>>', $logfile) or die("Can't open log file '$logfile': $!");

    open(STDERR, '>&', \*LOG)     or (print "Can't redirect STDERR to log file: $!" && CORE::exit(1));
    open(STDOUT, '>&', \*LOG)     or die("Can't redirect STDOUT to log file: $!");
    open(STDIN, '<', '/dev/null') or die("Can't reopen STDIN to /dev/null: $!");

    # Autoflush after each write
    $| = 1;
}


sub change_effective_user {
    my $self = shift;

    # Note that privileges are not permanently dropped and can be restored.
    # If you need to drop privileges permanently, override this method and
    # use the module Unix::SetUser which allows to do that (or think about
    # using 'su' to start your daemon as a non root user)

    # Only root can swith user
    return unless ($> == 0);

    my $as_user  = $self->{options}->{user}  || "nobody";
    my $as_group = $self->{options}->{group} || "nogroup";

    my $uid = getpwnam($as_user);
    my $gid = getgrnam($as_group);

    unless (defined $uid) {
        die("Cannot switch to a non existent user '$as_user'");
    }
    unless (defined $gid) {
        die("Cannot switch to a non existent group '$as_group'");
    }
    unless ($uid > 0) {
        die("Cannot run daemon as root");
    }

    # Change the effective gid
    $) = $gid  or die("Cannot switch to group '$as_group': $!");

    # Change the effective uid
    $> = $uid  or die("Cannot switch to user '$as_user': $!");
}

sub restore_effective_user {
    my $self = shift;

    # Only root can swith user
    return unless ($< == 0);
 
    # Restore the effective uid to the real uid
    $> = $<;

    # Restore the effective gid to the real gid
    $) = $(;
}


#------------------------------------------------------------------------------

# PIDFILE HANDLING

sub pid_file {
    my $self = shift;

    my $pidfile = $self->{config}->{pidfile};

    unless ($pidfile) {
        my $dir  = $PID_FILE_DIR;
        my $user = getpwuid($<);
        my $file = $self->daemon_name . '.pid';
        $pidfile = (-d "$dir/$user") ? "$dir/$user/$file" : "$dir/$file";
    }

    return $pidfile;
}

sub write_pid_file {
    my $self = shift;
    my $pidfile = $self->pid_file;

    die unless ($pidfile =~ m/\.pid$/);

    # Open the pidfile in exclusive mode, to avoid race conditions
    sysopen(my $fh, $pidfile, O_RDWR|O_CREAT)  or die("Cannot open pid file '$pidfile': $!");
    flock($fh, LOCK_EX | LOCK_NB)              or die("Pid file '$pidfile' is already locked");

    # Read the content of the pidfile
    my $pid = <$fh>;

    if ($pid && $pid =~ m/^(\d+)/ && $pid != $$) {
        # File already exists and contains a process id. Check then if that 
        # process id actually belong to a running instance of this daemon
        if ($self->verify_daemon_process($pid)) {
            close($fh);
            die("Cannot write pid file: alredy running");
        }
    }

    # Write our process id to the file
    sysseek($fh, 0, 0)                     or die("Cannot seek in pid file '$pidfile': $!");
    truncate($fh, 0)                       or die("Cannot truncate pid file '$pidfile': $!");
    syswrite($fh, "$$\n", length("$$\n"))  or die("Cannot write to pid file '$pidfile': $!");
    close($fh);
}

sub read_pid_file {
    my $self = shift;
    my $pidfile = $self->pid_file;

    unless (-e $pidfile) {
        # Pidfile does not exists
        return;
    }

    # Read the content of the pidfile
    open(my $fh, '<', $pidfile) or die("Cannot open pid file '$pidfile': $!");
    my ($pid) = <$fh> =~ /^(\d+)/;
    close($fh);

    return $pid;
}

sub delete_pid_file {
    my $self = shift;

    my $pid = $self->read_pid_file;

    unless ($pid) {
        # Do not delete file, it does not exist or does not contain a process id



( run in 0.913 second using v1.01-cache-2.11-cpan-39bf76dae61 )