3 use vars qw( @ISA @EXPORT_OK );
4 use vars qw( $pid_dir $me $pid_file $sigint $sigterm $NOSIG $logfile );
10 use File::Slurp qw(slurp);
12 use FS::UID qw( forksuidsetup );
14 #this is a simple refactoring of the stuff from freeside-queued, just to
15 #avoid duplicate code. eventually this should use something from CPAN.
19 daemonize1 drop_root daemonize2 myexit logfile sigint sigterm
20 daemon_fork daemon_wait daemon_reconnect
22 %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ] );
24 $pid_dir = '/var/run';
29 our $MAX_KIDS = 10; # for daemon_fork
37 if ( $PID_NEWSTYLE ) {
38 $pid_file .= '/freeside';
39 mkdir $pid_file unless -d $pid_file;
40 chown $FS::UID::freeside_uid, -1, $pid_file;
43 $pid_file .= '.'.shift if scalar(@_);
46 chdir "/" or die "Can't chdir to /: $!";
47 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
48 defined(my $pid = fork) or die "Can't fork: $!";
50 print "$me started with pid $pid\n"; #logging to $log_file\n";
51 exit unless $pid_file;
52 my $pidfh = new IO::File ">$pid_file" or exit;
53 chown $FS::UID::freeside_uid, -1, $pid_file;
54 print $pidfh "$pid\n";
58 #sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
59 #$SIG{CHLD} = \&REAPER;
63 $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
64 $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
70 my $freeside_gid = scalar(getgrnam('freeside'))
71 or die "can't find freeside group\n";
74 #if freebsd can't setuid(), presumably it can't setgid() either. grr fleabsd
78 $> = $FS::UID::freeside_uid;
79 $< = $FS::UID::freeside_uid;
80 #freebsd is sofa king broken, won't setuid()
82 $> = $FS::UID::freeside_uid;
86 open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
87 setsid or die "Can't start a new session: $!";
88 open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
90 $SIG{__DIE__} = \&_die;
91 $SIG{__WARN__} = \&_logmsg;
93 warn "$me starting\n";
96 sub sigint { $sigint; }
97 sub sigterm { $sigterm; }
99 sub logfile { $logfile = shift; } #_logmsg('test'); }
102 chomp( my $pid = slurp($pid_file) );
103 unlink $pid_file if -e $pid_file && $$ == $pid;
108 die @_ if $^S; # $^S = 1 during an eval(), don't break exception handling
111 chomp( my $pid = slurp($pid_file) );
112 unlink $pid_file if -e $pid_file && $$ == $pid;
118 chomp( my $msg = shift );
119 # set the logfile sensibly
122 $logname =~ s/^freeside-//;
123 logfile("%%%FREESIDE_LOG%%%/$logname-log.$FS::UID::datasrc");
125 my $log = new IO::File ">>$logfile";
126 flock($log, LOCK_EX);
128 print $log "[". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
129 flock($log, LOCK_UN);
133 =item daemon_fork CODEREF[, ARGS ]
135 Executes CODEREF in a child process, with its own $FS::UID::dbh handle. If
136 the number of child processes is >= $FS::Daemon::MAX_KIDS then this will
137 block until some of the child processes are finished. ARGS will be passed
140 If the fork fails, this will throw an exception containing $!. Otherwise
141 it returns the PID of the child, like fork() does.
146 $FS::UID::dbh->{AutoInactiveDestroy} = 1;
147 # wait until there's a lane open
148 daemon_wait($MAX_KIDS - 1);
150 my ($code, @args) = @_;
152 my $user = $FS::CurrentUser::CurrentUser->username;
155 if (!defined($pid)) {
157 warn "WARNING: can't fork: $!\n";
160 } elsif ( $pid > 0 ) {
167 forksuidsetup( $user );
174 =item daemon_wait [ MAX ]
176 Waits until there are at most MAX daemon_fork() child processes running,
177 reaps the ones that are finished, and continues. MAX defaults to zero, i.e.
178 wait for everything to finish.
183 my $max = shift || 0;
184 while ($kids > $max) {
185 foreach my $pid (keys %kids) {
186 my $kid = waitpid($pid, WNOHANG);
196 =item daemon_reconnect
198 Checks whether the database connection is live, and reconnects if not.
202 sub daemon_reconnect {
203 my $dbh = $FS::UID::dbh;
204 unless ($dbh && $dbh->ping) {
205 warn "WARNING: connection to database lost, reconnecting...\n";
207 eval { $FS::UID::dbh = myconnect(); };
209 unless ( !$@ && $FS::UID::dbh && $FS::UID::dbh->ping ) {
210 warn "WARNING: still no connection to database, sleeping for retry...\n";
214 warn "WARNING: reconnected to database\n";