--- /dev/null
+#!/usr/bin/env perl
+#
+#$Id: serialmgrd,v 1.25 2009/06/27 18:24:33 staffcvs Exp $
+#
+
+use Data::Dumper;
+use Error qw(:try);
+use POSIX;
+use Sys::Syslog;
+
+my $stuff = [];
+my $hosts = [];
+my $hup = 0;
+
+my $consolidate_exe = "/usr/local/bin/consolidate";
+my $sympathy_exe = "/usr/local/bin/sympathy";
+
+my @keys = qw(host portcode baud task user password name options);
+
+my $labeling = {
+ shedu => { offsets => [ 0, 7, 3, 11, 15 ], },
+ phoenix => { offsets => [ 0, 3, 11, 15 ], },
+ leprechaun => { offsets => [ 0, 3 ], },
+ imp => { offsets => [ 0, 3 ], },
+ pixie => { offsets => [ 0, 3, 11 ], },
+ basilisk => { offsets => [ 0, 3, 11, 7, 15 ], },
+ wyvern => { offsets => [ 0, 3, 11 ], },
+ goblin => { offsets => [ 0, 3 ], },
+ naga => { offsets => [ 0, 5 ], },
+ dragon => { offsets => [ 0, 3, 11 ], },
+ woking => { offsets => [ 0, 3, 11 ], },
+};
+
+sub port_decode($$) {
+ my ( $host, $code ) = @_;
+
+ return "/dev/ttyS" . $code if ( $code =~ /^\d+$/ );
+ return "/dev/ttyUSB" . $1 if ( $code =~ /^u.(\d+)$/ );
+ return "/dev/ttyS" . ( $labeling->{$host}->{offsets}->[$1] + $2 )
+ if ( $code =~ /^(\d+)\.(\d+)$/ );
+
+ return $code;
+}
+
+sub quit() {
+ sleep(10);
+ exit(1);
+}
+
+sub read_file() {
+ my $lines = [];
+ my $c;
+ open FILE, "<" . "/etc/serialmgr/config";
+
+ while (<FILE>) {
+ next if ( $_ =~ /^#/ );
+
+ chomp;
+ split(':');
+ $c = 0;
+ push @$lines, { map { $keys[ $c++ ] => $_ } @_ };
+ }
+ close FILE;
+ return $lines;
+}
+
+sub hostname() {
+ my ( $sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
+ return $1 if ( $nodename =~ /([^.]+)\./ );
+ return $nodename;
+}
+
+sub check_users($) {
+ my $c = shift;
+
+ for my $l (@$c) {
+ my $shell;
+ if ( $l->{task} eq "sympathy" ) {
+ $shell = "/usr/local/bin/run_sympathy";
+ }
+ else {
+ $shell = "/usr/local/bin/run_conclient";
+ }
+ if ( not defined getpwnam( $l->{user} ) ) {
+ syslog( LOG_ERR, "creating an account for user " . $l->{user} );
+ mkdir "/export/home";
+ mkdir "/export/home/colo";
+ my @cmd = ();
+ push @cmd, "useradd";
+ push @cmd, "-d", "/export/home/colo/" . $l->{user};
+ if ( length( $l->{password} ) > 2 ) {
+ push @cmd, "-p", $l->{password};
+ }
+ push @cmd, "-s", $shell;
+ push @cmd, "-m";
+ push @cmd, $l->{user};
+
+ #print join( ' ', @cmd ), "\n";
+ system @cmd;
+ }
+ if ( not defined getgrnam( $l->{user} ) ) {
+ syslog( LOG_ERR, "need to create a group for " . $l->{user} );
+ quit();
+ }
+ my $current_shell = ( getpwnam( $l->{user} ) )[8];
+ my $uid = getpwnam( $l->{user} ) ;
+
+ if (( $uid > 0 ) and ( $current_shell ne $shell )) {
+ syslog( LOG_ERR,
+ "changing shell for user " . $l->{user} . " to " . $shell );
+ my @cmd = ();
+ push @cmd, "usermod";
+ push @cmd, "-s", $shell;
+ push @cmd, $l->{user};
+ system @cmd;
+
+ }
+
+ if ( length( $l->{password} ) > 2 ) {
+ my $pwd = ( getpwnam( $l->{user} ) )[1];
+ if ( $l->{password} ne $pwd ) {
+ syslog( LOG_ERR, "changing password for user " . $l->{user} );
+ my @cmd = ();
+ push @cmd, "usermod";
+ push @cmd, "-p", $l->{password};
+ push @cmd, $l->{user};
+ system @cmd;
+
+ }
+ }
+
+ if ( $l->{user} eq 'root' ) {
+ }
+ elsif ( ! -d "/export/home/colo/".$l->{user} ) {
+ syslog( LOG_ERR,
+ "need to create a home directory for " . $l->{user} );
+ quit();
+ }
+ elsif ( ( getpwnam( $l->{user} ) )[2] != ( stat _ )[4] ||
+ ( getpwnam( $l->{user} ) )[3] != ( stat _ )[5] ) {
+ syslog( LOG_ERR,
+ "changing home directory ownership for user " . $l->{user} );
+ my @cmd = ();
+ push @cmd, "chown", "-R";
+ push @cmd, $l->{user}.".".$l->{user};
+ push @cmd, "/export/home/colo/".$l->{user};
+ system @cmd;
+ }
+ }
+
+}
+
+sub resolve_users($) {
+ my $c = shift;
+ my $ret=[];
+ my $host = hostname();
+
+ for my $l (@$c) {
+ next if ( $l->{host} ne $host );
+
+ my $uid = $l->{user};
+ my $gid = $l->{user};
+
+ if ( $uid =~ /[^\d]/ ) {
+ $uid = getpwnam($uid);
+ if ( !defined($uid) ) {
+ syslog( LOG_ERR, "unknown user " . $l->{user} );
+ quit();
+ }
+ }
+
+ if ( $gid =~ /[^\d]/ ) {
+ $gid = getgrnam($gid);
+ if ( !defined($gid) ) {
+ syslog( LOG_ERR, "unknown group " . $l->{user} );
+ quit();
+ }
+ }
+ $l->{uid} = $uid;
+ $l->{gid} = $gid;
+ $l->{backoff} = 0;
+ $l->{badstarts} = 0;
+ $l->{running} = 0;
+
+ if ( $l->{uid} > 0 ) {
+ $l->{socket} = "/export/home/colo/" . $l->{user} . "/console-socket";
+ $l->{socket_dir} = "/export/home/colo/" . $l->{user};
+ $l->{log} = "/export/home/colo/" . $l->{user} . "/logs/console";
+ $l->{log_dir} = "/export/home/colo/" . $l->{user} . "/logs";
+ }
+ else {
+ if ( $l->{task} eq "sympathy" ) {
+ $l->{socket} = "/root/sympathy/" . $l->{name};
+ $l->{socket_dir} = "/root/sympathy";
+ $l->{log} = "/root/sympathy/" . $l->{name} . ".log";
+ $l->{log_dir} = "/root/sympathy";
+ }
+ else {
+ $l->{socket} = "/root/consoles/" . $l->{name};
+ $l->{socket_dir} = "/root/consoles";
+ $l->{log} = "/root/consoles/" . $l->{name} . ".log";
+ $l->{log_dir} = "/root/consoles";
+ }
+ }
+ push @$ret,$l;
+ }
+
+return $ret;
+}
+
+sub get_config() {
+ my $cf = read_file();
+ my $stuff = [];
+# my $host = hostname();
+
+ for $line (@$cf) {
+# if ( $line->{host} eq $host ) {
+ $line->{device} = port_decode( $line->{host}, $line->{portcode} );
+ push @$stuff, $line;
+# }
+ }
+
+ return $stuff;
+}
+
+sub start($) {
+ my $t = shift;
+ my $exe;
+ my @args;
+
+ $t->{started} = time();
+
+ my $pid = fork();
+ if ( !defined($pid) ) {
+ syslog( LOG_ERR, "fork failed" );
+ return undef;
+ }
+ elsif ( $pid != 0 ) {
+ $t->{pid} = $pid;
+ $t->{running} = 1;
+ syslog( LOG_ERR,
+ "Started "
+ . $t->{task} . " for "
+ . $t->{name} . " on "
+ . $t->{device} . " ("
+ . $t->{portcode}
+ . ") pid "
+ . $t->{pid} );
+ return $pid;
+ }
+
+ # use sympathy to clean up any old lock files whilst we're still root
+ system( $sympathy_exe, "-S", "-C", "-d", $t->{device} );
+
+ chown $t->{gid}, $t->{uid}, $t->{device};
+ $( = $) = $t->{gid} if ( $t->{gid} != 0 );
+ $< = $> = $t->{uid} if ( $t->{uid} != 0 );
+
+ mkdir $t->{socket_dir};
+ mkdir $t->{log_dir};
+
+ if ( $t->{task} eq "sympathy" ) {
+
+ push @args, "-S";
+ push @args, "-s";
+ push @args, "-F";
+ push @args, "-d";
+ push @args, $t->{device};
+ push @args, "-b";
+ push @args, $t->{baud};
+ push @args, "-K";
+ push @args, "-k";
+ push @args, $t->{socket};
+ push @args, "-L";
+ push @args, $t->{log};
+ push @args, "-R";
+ push @args, $t->{options} if $t->{options};
+
+ $exe = $sympathy_exe;
+ }
+ else {
+ push @args, "-d";
+ push @args, "-e";
+ push @args, "-s";
+ push @args, $t->{baud} . ",n,8";
+ push @args, "-f";
+ push @args, "none";
+ push @args, "-l";
+ push @args, "1048576";
+ push @args, $t->{device};
+ push @args, $t->{log};
+ push @args, $t->{socket};
+ push @args, $t->{options} if $t->{options};
+
+ $exe = $consolidate_exe;
+
+ }
+
+ syslog(LOG_ERR, $exe. " ". join( ' ', @args ));
+
+ exec( $exe, @args );
+
+ exit(1);
+
+}
+
+sub died($) {
+ my $t = shift;
+
+ my $ranfor = $t->{ended} - $t->{started};
+
+ syslog( LOG_ERR,
+ "Finished "
+ . $t->{task} . " for "
+ . $t->{name} . " on "
+ . $t->{device} . " ("
+ . $t->{portcode}
+ . ") pid "
+ . $t->{pid}
+ . " lasted "
+ . $ranfor
+ . "s" );
+
+ if ( $ranfor < 20 ) {
+ $t->{badstarts}++;
+ }
+
+ if ( $t->{badstarts} > 5 ) {
+ syslog( LOG_ERR,
+ "Looping "
+ . $t->{task} . " for "
+ . $t->{name} . " on "
+ . $t->{device} . " ("
+ . $t->{portcode}
+ . ") pid "
+ . $t->{pid}
+ . " suspended" );
+ $t->{backoff} = time() + 5 * 60;
+ $t->{badstarts} = 0;
+ }
+ $t->{pid} = undef;
+
+}
+
+sub hup() {
+ $hup++;
+}
+
+sub medea() {
+ for $t (@$stuff) {
+ kill POSIX::SIGTERM, $t->{pid} if ( defined( $t->{pid} ) );
+ }
+}
+
+sub terminate() {
+ syslog( LOG_ERR, "shutting down on signal" );
+ medea();
+ exit(0);
+}
+
+sub sigchld() {
+ for $t (@$stuff) {
+ if ( $t->{running} and ( $t->{pid} == waitpid( $t->{pid}, WNOHANG ) ) )
+ {
+ $t->{ended} = time();
+ $t->{running} = 0;
+ }
+ }
+}
+
+openlog( "serialmgrd", "pid,console", LOG_LOCAL0 );
+
+syslog( LOG_ERR, "starts" );
+
+$stuff = get_config();
+check_users($stuff);
+$stuff=resolve_users($stuff);
+
+$SIG{HUP} = \&hup;
+$SIG{INT} = $SIG{TERM} = \&terminate;
+$SIG{CHLD} = \&sigchld;
+
+while (1) {
+ my $now = time();
+
+ sigchld();
+
+ for $t (@$stuff) {
+ if ( defined $t->{pid} ) {
+ if ( ( $t->{running} ) and ( kill( 0, $t->{pid} ) == 0 ) ) {
+ $t->{running} = 0;
+ $t->{ended} = time();
+ }
+
+ if ( not $t->{running} ) {
+ died($t);
+ }
+ }
+
+ if ( ( not defined( $t->{pid} ) ) and ( $t->{backoff} < $now ) ) {
+ start($t);
+
+ }
+ }
+
+
+ if ( scalar(@$stuff) < 1 ) {
+ syslog( LOG_ERR, "Nothing to do - sleeping 3600s" );
+ sleep(3600);
+ }
+ if ($hup) {
+ $hup = 0;
+ syslog( LOG_ERR, "SIGHUP rereading config" );
+ medea();
+
+ $stuff = get_config();
+ check_users($stuff);
+ $stuff=resolve_users($stuff);
+ }
+
+ sleep(10);
+}