chiark / gitweb /
6a839adeaa3653ec34a644bef9c65d86db2ee565
[sympathy.git] / serialmgr / serialmgrd
1 #!/usr/bin/env perl
2 #
3 #$Id: serialmgrd,v 1.25 2009/06/27 18:24:33 staffcvs Exp $
4 #
5
6 use Data::Dumper;
7 use Error qw(:try);
8 use POSIX;
9 use Sys::Syslog;
10
11 my $stuff = [];
12 my $hosts = [];
13 my $hup   = 0;
14
15 my $consolidate_exe = "/usr/local/bin/consolidate";
16 my $sympathy_exe    = "/usr/local/bin/sympathy";
17
18 my @keys = qw(host portcode baud task user password name options);
19
20 my $labeling = {
21     shedu      => { offsets => [ 0, 7, 3, 11, 15 ], },
22     phoenix    => { offsets => [ 0, 3, 11, 15 ], },
23     leprechaun => { offsets => [ 0, 3 ], },
24     imp        => { offsets => [ 0, 3 ], },
25     pixie      => { offsets => [ 0, 3, 11 ], },
26     basilisk   => { offsets => [ 0, 3, 11, 7, 15 ], },
27     wyvern     => { offsets => [ 0, 3, 11 ], },
28     goblin     => { offsets => [ 0, 3 ], },
29     naga       => { offsets => [ 0, 5 ], },
30     dragon     => { offsets => [ 0, 3, 11 ], },
31     woking     => { offsets => [ 0, 3, 11 ], },
32 };
33
34 sub port_decode($$) {
35     my ( $host, $code ) = @_;
36
37     return "/dev/ttyS" . $code if ( $code =~ /^\d+$/ );
38     return "/dev/ttyUSB" . $1 if ( $code =~ /^u.(\d+)$/ );
39     return "/dev/ttyS" . ( $labeling->{$host}->{offsets}->[$1] + $2 )
40       if ( $code =~ /^(\d+)\.(\d+)$/ );
41
42     return $code;
43 }
44
45 sub quit() {
46     sleep(10);
47     exit(1);
48 }
49
50 sub read_file() {
51     my $lines = [];
52     my $c;
53     open FILE, "<" . "/etc/serialmgr/config";
54
55     while (<FILE>) {
56         next if ( $_ =~ /^#/ );
57
58         chomp;
59         split(':');
60         $c = 0;
61         push @$lines, { map { $keys[ $c++ ] => $_ } @_ };
62     }
63     close FILE;
64     return $lines;
65 }
66
67 sub hostname() {
68     my ( $sysname, $nodename, $release, $version, $machine ) = POSIX::uname();
69     return $1 if ( $nodename =~ /([^.]+)\./ );
70     return $nodename;
71 }
72
73 sub check_users($) {
74     my $c = shift;
75
76     for my $l (@$c) {
77         my $shell;
78         if ( $l->{task} eq "sympathy" ) {
79             $shell = "/usr/local/bin/run_sympathy";
80         }
81         else {
82             $shell = "/usr/local/bin/run_conclient";
83         }
84         if ( not defined getpwnam( $l->{user} ) ) {
85             syslog( LOG_ERR, "creating an account for user " . $l->{user} );
86             mkdir "/export/home";
87             mkdir "/export/home/colo";
88             my @cmd = ();
89             push @cmd, "useradd";
90             push @cmd, "-d", "/export/home/colo/" . $l->{user};
91             if ( length( $l->{password} ) > 2 ) {
92                 push @cmd, "-p", $l->{password};
93             }
94             push @cmd, "-s", $shell;
95             push @cmd, "-m";
96             push @cmd, $l->{user};
97
98             #print join( ' ', @cmd ), "\n";
99             system @cmd;
100         }
101         if ( not defined getgrnam( $l->{user} ) ) {
102             syslog( LOG_ERR, "need to create a group for " . $l->{user} );
103             quit();
104         }
105         my $current_shell = ( getpwnam( $l->{user} ) )[8];
106         my $uid =  getpwnam( $l->{user} ) ;
107
108         if (( $uid > 0 ) and ( $current_shell ne $shell )) {
109             syslog( LOG_ERR,
110                 "changing shell for user " . $l->{user} . " to " . $shell );
111             my @cmd = ();
112             push @cmd, "usermod";
113             push @cmd, "-s", $shell;
114             push @cmd, $l->{user};
115             system @cmd;
116
117         }
118
119         if ( length( $l->{password} ) > 2 ) {
120             my $pwd = ( getpwnam( $l->{user} ) )[1];
121             if ( $l->{password} ne $pwd ) {
122                 syslog( LOG_ERR, "changing password for user " . $l->{user} );
123                 my @cmd = ();
124                 push @cmd, "usermod";
125                 push @cmd, "-p", $l->{password};
126                 push @cmd, $l->{user};
127                 system @cmd;
128
129             }
130         }
131
132         if ( $l->{user} eq 'root' ) {
133         }
134         elsif ( ! -d "/export/home/colo/".$l->{user} ) {
135             syslog( LOG_ERR, 
136                  "need to create a home directory for " . $l->{user} );
137             quit();         
138         }
139         elsif ( ( getpwnam( $l->{user} ) )[2] != ( stat _ )[4] ||
140                   ( getpwnam( $l->{user} ) )[3] != ( stat _ )[5] ) {
141             syslog( LOG_ERR, 
142                 "changing home directory ownership for user " . $l->{user} );
143             my @cmd = (); 
144             push @cmd, "chown", "-R";
145             push @cmd, $l->{user}.".".$l->{user};
146             push @cmd, "/export/home/colo/".$l->{user};
147             system @cmd;
148         }
149     }
150
151 }
152
153 sub resolve_users($) {
154     my $c = shift;
155     my $ret=[];
156     my $host  = hostname();
157
158     for my $l (@$c) {
159         next if ( $l->{host} ne $host );
160
161         my $uid = $l->{user};
162         my $gid = $l->{user};
163
164         if ( $uid =~ /[^\d]/ ) {
165             $uid = getpwnam($uid);
166             if ( !defined($uid) ) {
167                 syslog( LOG_ERR, "unknown user " . $l->{user} );
168                 quit();
169             }
170         }
171
172         if ( $gid =~ /[^\d]/ ) {
173             $gid = getgrnam($gid);
174             if ( !defined($gid) ) {
175                 syslog( LOG_ERR, "unknown group " . $l->{user} );
176                 quit();
177             }
178         }
179         $l->{uid}       = $uid;
180         $l->{gid}       = $gid;
181         $l->{backoff}   = 0;
182         $l->{badstarts} = 0;
183         $l->{running}   = 0;
184
185         if ( $l->{uid} > 0 ) {
186             $l->{socket}     = "/export/home/colo/" . $l->{user} . "/console-socket";
187             $l->{socket_dir} = "/export/home/colo/" . $l->{user};
188             $l->{log}        = "/export/home/colo/" . $l->{user} . "/logs/console";
189             $l->{log_dir}    = "/export/home/colo/" . $l->{user} . "/logs";
190         }
191         else {
192             if ( $l->{task} eq "sympathy" ) {
193                 $l->{socket}     = "/root/sympathy/" . $l->{name};
194                 $l->{socket_dir} = "/root/sympathy";
195                 $l->{log}        = "/root/sympathy/" . $l->{name} . ".log";
196                 $l->{log_dir}    = "/root/sympathy";
197             }
198             else {
199                 $l->{socket}     = "/root/consoles/" . $l->{name};
200                 $l->{socket_dir} = "/root/consoles";
201                 $l->{log}        = "/root/consoles/" . $l->{name} . ".log";
202                 $l->{log_dir}    = "/root/consoles";
203             }
204         }
205         push @$ret,$l;
206     }
207
208 return $ret;
209 }
210
211 sub get_config() {
212     my $cf    = read_file();
213     my $stuff = [];
214 #    my $host  = hostname();
215
216     for $line (@$cf) {
217 #        if ( $line->{host} eq $host ) {
218             $line->{device} = port_decode( $line->{host}, $line->{portcode} );
219             push @$stuff, $line;
220 #        }
221     }
222
223     return $stuff;
224 }
225
226 sub start($) {
227     my $t = shift;
228     my $exe;
229     my @args;
230
231     $t->{started} = time();
232
233     my $pid = fork();
234     if ( !defined($pid) ) {
235         syslog( LOG_ERR, "fork failed" );
236         return undef;
237     }
238     elsif ( $pid != 0 ) {
239         $t->{pid}     = $pid;
240         $t->{running} = 1;
241         syslog( LOG_ERR,
242                 "Started  "
243               . $t->{task} . " for "
244               . $t->{name} . " on "
245               . $t->{device} . " ("
246               . $t->{portcode}
247               . ") pid "
248               . $t->{pid} );
249         return $pid;
250     }
251
252     # use sympathy to clean up any old lock files whilst we're still root
253     system( $sympathy_exe, "-S", "-C", "-d", $t->{device} );
254
255     chown $t->{gid}, $t->{uid}, $t->{device};
256     $( = $) = $t->{gid} if ( $t->{gid} != 0 );
257     $< = $> = $t->{uid} if ( $t->{uid} != 0 );
258
259     mkdir $t->{socket_dir};
260     mkdir $t->{log_dir};
261
262     if ( $t->{task} eq "sympathy" ) {
263
264         push @args, "-S";
265         push @args, "-s";
266         push @args, "-F";
267         push @args, "-d";
268         push @args, $t->{device};
269         push @args, "-b";
270         push @args, $t->{baud};
271         push @args, "-K";
272         push @args, "-k";
273         push @args, $t->{socket};
274         push @args, "-L";
275         push @args, $t->{log};
276         push @args, "-R";
277         push @args, $t->{options} if $t->{options};
278
279         $exe = $sympathy_exe;
280     }
281     else {
282         push @args, "-d";
283         push @args, "-e";
284         push @args, "-s";
285         push @args, $t->{baud} . ",n,8";
286         push @args, "-f";
287         push @args, "none";
288         push @args, "-l";
289         push @args, "1048576";
290         push @args, $t->{device};
291         push @args, $t->{log};
292         push @args, $t->{socket};
293         push @args, $t->{options} if $t->{options};
294
295         $exe = $consolidate_exe;
296
297     }
298
299     syslog(LOG_ERR, $exe. " ". join( ' ', @args ));
300
301     exec( $exe, @args );
302
303     exit(1);
304
305 }
306
307 sub died($) {
308     my $t = shift;
309
310     my $ranfor = $t->{ended} - $t->{started};
311
312     syslog( LOG_ERR,
313             "Finished "
314           . $t->{task} . " for "
315           . $t->{name} . " on "
316           . $t->{device} . " ("
317           . $t->{portcode}
318           . ") pid "
319           . $t->{pid}
320           . " lasted "
321           . $ranfor
322           . "s" );
323
324     if ( $ranfor < 20 ) {
325         $t->{badstarts}++;
326     }
327
328     if ( $t->{badstarts} > 5 ) {
329         syslog( LOG_ERR,
330                 "Looping  "
331               . $t->{task} . " for "
332               . $t->{name} . " on "
333               . $t->{device} . " ("
334               . $t->{portcode}
335               . ") pid "
336               . $t->{pid}
337               . " suspended" );
338         $t->{backoff}   = time() + 5 * 60;
339         $t->{badstarts} = 0;
340     }
341     $t->{pid} = undef;
342
343 }
344
345 sub hup() {
346     $hup++;
347 }
348
349 sub medea() {
350     for $t (@$stuff) {
351         kill POSIX::SIGTERM, $t->{pid} if ( defined( $t->{pid} ) );
352     }
353 }
354
355 sub terminate() {
356     syslog( LOG_ERR, "shutting down on signal" );
357     medea();
358     exit(0);
359 }
360
361 sub sigchld() {
362     for $t (@$stuff) {
363         if ( $t->{running} and ( $t->{pid} == waitpid( $t->{pid}, WNOHANG ) ) )
364         {
365             $t->{ended}   = time();
366             $t->{running} = 0;
367         }
368     }
369 }
370
371 openlog( "serialmgrd", "pid,console", LOG_LOCAL0 );
372
373 syslog( LOG_ERR, "starts" );
374
375 $stuff = get_config();
376 check_users($stuff);
377 $stuff=resolve_users($stuff);
378
379 $SIG{HUP}  = \&hup;
380 $SIG{INT}  = $SIG{TERM} = \&terminate;
381 $SIG{CHLD} = \&sigchld;
382
383 while (1) {
384     my $now = time();
385
386     sigchld();
387
388     for $t (@$stuff) {
389         if ( defined $t->{pid} ) {
390             if ( ( $t->{running} ) and ( kill( 0, $t->{pid} ) == 0 ) ) {
391                 $t->{running} = 0;
392                 $t->{ended}   = time();
393             }
394
395             if ( not $t->{running} ) {
396                 died($t);
397             }
398         }
399
400         if ( ( not defined( $t->{pid} ) ) and ( $t->{backoff} < $now ) ) {
401             start($t);
402
403         }
404     }
405
406
407     if ( scalar(@$stuff) < 1 ) {
408         syslog( LOG_ERR, "Nothing to do - sleeping 3600s" );
409         sleep(3600);
410     }
411     if ($hup) {
412         $hup = 0;
413         syslog( LOG_ERR, "SIGHUP rereading config" );
414         medea();
415
416         $stuff = get_config();
417         check_users($stuff);
418         $stuff=resolve_users($stuff);
419     }
420
421     sleep(10);
422 }