ljr/ljcom/bin/ljrpcd

150 lines
3.9 KiB
Perl
Executable File

#!/usr/bin/perl -w
# LiveJournal Remote Procedure Call Daemon (ljrpcd)
# Copyright 2001 Dormando (dormando@rydia.net)
# Going add a license (GPL?) later.
# This daemon forks off into the background, logs if told to
# waits for UDP messages on the given port, and executes
# commands sent to it.
#
# <LJDEP>
# lib: IO::Socket
# prog: bin/ljmaint.pl
# </LJDEP>
use strict;
use IO::Socket;
# Max message length and port to bind.
my $MAXLEN = 512;
my $PORTNO = 6100;
my $PIDFILE = '/var/run/ljrpcd.pid';
my $LOGFILE = '/var/log/ljrpcd.log';
my $TYPE = shift @ARGV;
unless ($TYPE eq "web" || $TYPE eq "db") {
print "Unknown/unspecified machine type, quitting.\n";
exit 1;
}
# Pid and pidfile.
my $pid;
my $is_parent = 1;
# Socket. Needs to be here for the HUP stuff.
my $sock;
# In case we're shot, unlink the pidfile.
$SIG{TERM} = sub {
unlink($PIDFILE);
exit 1;
};
if (-e $PIDFILE) {
open (PID, $PIDFILE);
my $tpid;
chomp ($tpid = <PID>);
close PID;
if ($tpid) {
# so Linux-specific, but Proc::ProcessTable
#iterates over /dev forever, which sucks on a NetApp
if (open (CMD, "/proc/$tpid/cmdline")) {
my $cmdline = <CMD>;
close CMD;
if ($cmdline =~ /ljrpcd/) {
print "Process exists already, quitting.\n";
exit 1;
}
}
}
}
# Print a banner.
print "LiveJournal RPC Daemon starting up into the background...\n";
# Perhaps I should give it a command to not do this in the future.
if ($pid = fork) {
# Parent, log pid and exit.
open(PID, ">$PIDFILE") or die "Couldn't open $PIDFILE for writing: $!\n";
print PID $pid;
close(PID);
print "Closing ($pid) wrote to $PIDFILE\n";
$is_parent = 1;
exit;
} else {
# This is the child, main loop-de-doo.
my($cmdmsg, $remaddr, $remhost);
# HUP signal handler.
$SIG{HUP} = \&restart_request;
open(LOG, ">>$LOGFILE") or die "Couldn't open log file for appending: $!\n";
flock(LOG, 2) or die "Couldn't flock log file for writing: $!\n";
select(LOG); # Why the hell not, eh?
$| = 1;
$sock = IO::Socket::INET->new(LocalPort => "$PORTNO", Proto => 'udp') or die "socket: $@";
$sock->sockopt(SO_BROADCAST, 1);
print "Bound, awaiting UDP commands on port $PORTNO\n";
# Main loop, simple parser.
while ($sock->recv($cmdmsg, $MAXLEN)) {
my ($port, $ipaddr) = sockaddr_in($sock->peername);
my $ip_addy = inet_ntoa($ipaddr);
$remhost = gethostbyaddr($ipaddr, AF_INET);
print "Client $remhost sent command: $cmdmsg\n";
# If the command is 'marco' return 'pollo'
if ($cmdmsg =~ /^marco(\s+($TYPE|all))?$/) {
print "Returning 'pollo' to client $remhost\n";
$sock->send("pollo") or print "Couldn't scream pollo at $remhost\n";
next;
} elsif ($cmdmsg =~ s/^cmd:\s//) {
# Handle the command.
if ($cmdmsg eq "restart") {
print "Restarting ljrpcd\n";
$sock->send("Restarting ljrpcd...\n");
restart_request();
}
my $return = handle_request($cmdmsg);
print "Returning $return to client $remhost\n";
$sock->send($return) or print "Couldn't return $return to $remhost\n";
next;
}
}
die "recv: $!\n";
} #if
# Sub to restart the daemon.
sub restart_request {
$sock->close;
unlink($PIDFILE);
exec($0, $TYPE);
}
# Handle a request... Do further parsing, pass it appropriately.
# Should return a discernable ok. Usually 'done' 'ok' or an
# informative reply no longer than 500 characters.
sub handle_request {
my $cmd = shift;
my $su = "";
if ($cmd =~ s/^(\w+?)://) {
my $user = $1;
my ($login,$pass,$uid,$gid) = getpwnam($user)
or return "$user not in passwd file.";
$su = "su $user -c";
}
unless ($cmd =~ /[\|\>\<]/) {
my $home = $ENV{'LJHOME'} || "/home/lj";
$cmd =~ s/[;]/\\$&/;
if ($su) {
return `$su \'$home/bin/ljmaint.pl $cmd\'`;
} else {
return `$home/bin/ljmaint.pl $cmd`;
}
}
return 'bad command';
}