150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			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';
 | 
						|
}
 |