2 # Functions for getting the status of services
4 BEGIN { push(@INC, ".."); };
7 %access = &get_module_acl();
10 $services_dir = "$module_config_directory/services";
11 $cron_cmd = "$module_config_directory/monitor.pl";
13 $oldstatus_file = "$module_config_directory/oldstatus";
14 $fails_file = "$module_config_directory/fails";
16 $templates_dir = "$module_config_directory/templates";
18 %monitor_os_support = ( 'traffic' => { 'os_support' => '*-linux freebsd' },
21 @monitor_statuses = ( 'up', 'down', 'un', 'webmin', 'timed', 'isdown' );
24 # Returns a list of all services this module knows how to get status on.
25 # If this is the first time the function is called a default set of services
30 if (!-d $services_dir) {
31 # setup initial services
32 mkdir($module_config_directory, 0700);
33 mkdir($services_dir, 0700);
34 system("cp services/* $services_dir");
36 map { $mod{$_}++ } &list_modules();
37 opendir(DIR, $services_dir);
38 while($f = readdir(DIR)) {
39 next if ($f !~ /^(.*)\.serv$/);
40 local $serv = &get_service($1);
41 next if (!$serv || !$serv->{'type'} || !$serv->{'id'});
42 if ($serv->{'depends'}) {
44 map { $d++ if (!$mod{$_}) } split(/\s+/, $serv->{'depends'});
45 push(@rv, $serv) if (!$d);
59 &read_file("$services_dir/$_[0].serv", \%serv);
60 $serv{'fails'} = 1 if (!defined($serv{'fails'}));
61 $serv{'_file'} = "$services_dir/$_[0].serv";
62 if (!defined($serv{'notify'})) {
63 $serv{'notify'} = 'email pager snmp';
65 $serv{'remote'} = "*" if (!$serv{'remote'} && !$serv{'groups'});
66 return $_[0] ne $serv{'id'} ? undef : \%serv;
72 mkdir($services_dir, 0755) if (!-d $services_dir);
73 &lock_file("$services_dir/$_[0]->{'id'}.serv");
74 &write_file("$services_dir/$_[0]->{'id'}.serv", $_[0]);
75 &unlock_file("$services_dir/$_[0]->{'id'}.serv");
78 # delete_service(serv)
81 &lock_file("$services_dir/$_[0]->{'id'}.serv");
82 unlink("$services_dir/$_[0]->{'id'}.serv");
83 &unlock_file("$services_dir/$_[0]->{'id'}.serv");
86 # expand_remotes(&service)
87 # Given a service with direct and group remote hosts, returns a list of the
88 # names of all actual hosts (* means local)
92 push(@remote, split(/\s+/, $_[0]->{'remote'}));
93 local @groupnames = split(/\s+/, $_[0]->{'groups'});
95 &foreign_require("servers");
96 local @groups = &servers::list_all_groups();
97 foreach my $g (@groupnames) {
98 local ($group) = grep { $_->{'name'} eq $g } @groups;
100 push(@remote, @{$group->{'members'}});
104 return &unique(@remote);
107 # service_status(&service, [from-cgi])
108 # Gets the status of a service, possibly on another server. If called in
109 # an array content, the status of all hosts for this monitor are returned.
112 local $t = $_[0]->{'type'};
114 foreach $r (&expand_remotes($_[0])) {
116 local $main::error_must_die = 1;
118 local $SIG{'ALRM'} = sub { die "status alarm\n" };
119 alarm(60); # wait at most 60 secs for a result
121 # Make a remote call to another webmin server
122 &remote_error_setup(\&remote_error);
123 $remote_error_msg = undef;
124 &remote_foreign_require($r, 'status', 'status-lib.pl')
125 if (!$done_remote_status{$r}++);
126 local $webmindown = $s->{'type'} eq 'alive' ? 0 : -2;
127 if ($remote_error_msg) {
128 $rv = { 'up' => $webmindown,
129 'desc' => "$text{'mon_webmin'} : $remote_error_msg" };
134 $s{'groups'} = undef;
135 ($rv) = &remote_foreign_call($r, 'status',
136 'service_status', \%s, $_[1]);
137 if ($remote_error_msg) {
138 $rv = { 'up' => $webmindown, 'desc' =>
139 "$text{'mon_webmin'} : $remote_error_msg" };
143 elsif ($t =~ /^(\S+)::(\S+)$/) {
144 # Call to another module
145 local ($mod, $mtype) = ($1, $2);
146 &foreign_require($mod, "status_monitor.pl");
147 $rv = &foreign_call($mod, "status_monitor_status",
148 $mtype, $_[0], $_[1]);
151 # Just include and use the local monitor library
152 do "${t}-monitor.pl" if (!$done_monitor{$t}++);
153 local $func = "get_${t}_status";
155 $_[0]->{'clone'} ? $_[0]->{'clone'} : $t,
160 if ($@ =~ /status alarm/) {
161 push(@rv, { 'up' => -3,
165 # A real error happened
169 $rv->{'remote'} = $r;
173 return wantarray ? @rv : $rv[0];
178 $remote_error_msg = join("", @_);
182 # Returns a list of all modules available on this system
185 return map { $_->{'dir'} } grep { &check_os_support($_) }
186 &get_all_module_infos();
190 # Returns a list of the module's monitor type handlers, and those
191 # defined in other modules.
196 while($f = readdir(DIR)) {
197 if ($f =~ /^(\S+)-monitor\.pl$/) {
199 local $oss = $monitor_os_support{$m};
200 next if ($oss && !&check_os_support($oss));
201 push(@rv, [ $m, $text{"type_$m"} ]);
206 foreach $m (&get_all_module_infos()) {
207 local $mdir = defined(&module_root_directory) ?
208 &module_root_directory($m->{'dir'}) :
209 "$root_directory/$m->{'dir'}";
210 if (-r "$mdir/status_monitor.pl" &&
211 &check_os_support($m)) {
212 &foreign_require($m->{'dir'}, "status_monitor.pl");
213 local @mms = &foreign_call($m->{'dir'}, "status_monitor_list");
214 push(@rv, map { [ $m->{'dir'}."::".$_->[0], $_->[1] ] } @mms);
220 # depends_check(&service, [module]+)
223 return if ($_[0]->{'id'}); # only check for new services
224 if ($_[0]->{'remote'}) {
225 # Check on the remote server
226 foreach $m (@_[1..$#_]) {
227 &remote_foreign_check($_[0]->{'remote'}, $m, 1) ||
228 &error(&text('depends_remote', "<tt>$m</tt>",
229 "<tt>$_[0]->{'remote'}</tt>"));
233 # Check on this server
234 foreach $m (@_[1..$#_]) {
235 local %minfo = &get_module_info($m);
236 %minfo || &error(&text('depends_mod', "<tt>$m</tt>"));
237 &check_os_support(\%minfo, undef, undef, 1) ||
238 &error(&text('depends_os', "<tt>$minfo{'desc'}</tt>"));
240 $_[0]->{'depends'} = join(" ", @_[1..$#_]);
244 # find_named_process(regexp)
245 sub find_named_process
247 foreach $p (&proc::list_processes()) {
248 $p->{'args'} =~ s/\s.*$//; $p->{'args'} =~ s/[\[\]]//g;
249 if ($p->{'args'} =~ /$_[0]/) {
256 # smtp_command(handle, command)
262 if ($r !~ /^[23]\d+/) {
263 &error(&text('sched_esmtpcmd', "<tt>$c</tt>", "<tt>$r</tt>"));
268 # Create a cron job based on the module's configuration
271 &lock_file($cron_cmd);
272 &foreign_require("cron");
274 foreach $j (&cron::list_cron_jobs()) {
275 $job = $j if ($j->{'user'} eq 'root' && $j->{'command'} eq $cron_cmd);
278 &lock_file(&cron::cron_file($job));
279 &cron::delete_cron_job($job);
280 &unlock_file(&cron::cron_file($job));
283 if ($config{'sched_mode'}) {
284 # Create the program that cron calls
285 &cron::create_wrapper($cron_cmd, $module_name, "monitor.pl");
287 # Setup the actual cron job
289 $njob = { 'user' => 'root', 'active' => 1,
290 'hours' => '*', 'days' => '*',
291 'months' => '*', 'weekdays' => '*',
292 'command' => $cron_cmd };
293 if ($config{'sched_period'} == 0) {
294 $njob->{'mins'} = &make_interval(60);
296 elsif ($config{'sched_period'} == 1) {
297 $njob->{'hours'} = &make_interval(24);
300 elsif ($config{'sched_period'} == 2) {
301 $njob->{'days'} = &make_interval(31, 1);
302 $njob->{'hours'} = $njob->{'mins'} = 0;
304 elsif ($config{'sched_period'} == 3) {
305 $njob->{'months'} = &make_interval(12, 1);
307 $njob->{'hours'} = $njob->{'mins'} = 0;
309 &lock_file(&cron::cron_file($njob));
310 &cron::create_cron_job($njob);
311 &unlock_file(&cron::cron_file($njob));
313 &unlock_file($cron_cmd);
316 # make_interval(length, offset2)
320 for($i=$config{'sched_offset'}+$_[1]; $i<$_[0]; $i+=$config{'sched_int'}) {
323 return join(",", @rv);
326 # expand_oldstatus(oldstatus, &serv)
327 # Converts an old-status string like *=1 foo.com=2 into a hash. If the string
328 # contains just one number, it is assumed to be for just the first remote host
331 local ($o, $serv) = @_;
332 local @remotes = split(/\s+/, $serv->{'remote'});
333 if ($o =~ /^\-?(\d+)$/) {
334 return { $remotes[0] => $o };
338 foreach my $hs (split(/\s+/, $o)) {
339 local ($h, $s) = split(/=/, $hs);
346 # nice_remotes(&monitor, [max])
349 local ($s, $max) = @_;
351 local @remotes = map { $_ eq "*" ? $text{'index_local'}
353 split(/\s+/, $s->{'remote'});
354 foreach my $g (split(/\s+/, $s->{'groups'})) {
355 push(@remotes, &text('index_group', $g));
357 return @remotes > $max ? join(", ", @remotes[0..$max]).", ..."
358 : join(", ", @remotes);
364 local $mems = scalar(@{$group->{'members'}});
365 return $group->{'name'}." (".
366 &text($mems == 0 ? 'mon_empty' :
367 $mems == 1 ? 'mon_onemem' : 'mon_members', $mems).")";
370 # list_notification_modes()
371 # Returns a list of available notifcation modes (like email, sms, etc..)
372 sub list_notification_modes
374 local @rv = ( "email" );
375 if ($config{'pager_cmd'} && $config{'sched_pager'}) {
378 if ($config{'snmp_server'}) {
380 eval "use Net::SNMP";
382 eval "use SNMP_Session";
384 push(@rv, "snmp") if ($gotmod);
386 if ($config{'sched_carrier'} && $config{'sched_sms'}) {
392 # list_sms_carriers()
393 # Returns a list of information about carriers to whom we can send SMS
394 sub list_sms_carriers
396 return ( { 'id' => 'tmobile',
397 'desc' => 'T-Mobile',
398 'domain' => 'tmomail.net' },
399 { 'id' => 'cingular',
401 'domain' => 'txt.att.net' },
402 { 'id' => 'oldcingular',
403 'desc' => 'Cingular',
404 'domain' => 'cingularme.com',
408 'domain' => 'vtext.com' },
410 'desc' => 'Sprint PCS',
411 'domain' => 'messaging.sprintpcs.com' },
414 'domain' => 'messaging.nextel.com' },
417 'domain' => 'message.alltel.com' },
419 'desc' => 'Boost Mobile',
420 'domain' => 'myboostmobile.com' },
422 'desc' => 'Virgin Mobile',
423 'domain' => 'vmobl.com' },
425 'desc' => 'Cincinnati Bell',
426 'domain' => 'gocbw.com' },
429 'domain' => 'sms.t-online.de' },
430 { 'id' => 'vodafone',
431 'desc' => 'Vodafone UK',
432 'domain' => 'vodafone.net' },
433 { 'id' => 'vodafonejapan',
434 'desc' => 'Vodafone Japan',
435 'domain' => 't.vodafone.ne.jp' },
436 { 'id' => 'bellcanada',
437 'desc' => 'Bell Canada',
438 'domain' => 'txt.bellmobility.ca' },
439 { 'id' => 'bellsouth',
440 'desc' => 'Bell South',
441 'domain' => 'sms.bellsouth.com' },
442 { 'id' => 'cellularone',
443 'desc' => 'Cellular One',
444 'domain' => 'mobile.celloneusa.com' },
447 'domain' => 'mmail.co.uk' },
449 'desc' => 'Rogers Canada',
450 'domain' => 'pcs.rogers.com' },
453 'domain' => 'skytel.com' },
458 # Returns a list of hash refs, one for each email template
461 opendir(DIR, $templates_dir) || return ( );
463 foreach my $f (readdir(DIR)) {
465 push(@rv, &get_template($f));
473 # Returns the hash ref for a specific template, by ID
478 &read_file("$templates_dir/$id", \%tmpl) || return undef;
480 $tmpl{'file'} = "$templates_dir/$id";
481 $tmpl{'email'} =~ s/\\n/\n/g;
482 $tmpl{'email'} =~ s/\\\\/\\/g;
486 # save_template(&template)
487 # Creates or saves an email template. Also does locking.
491 $tmpl->{'id'} ||= time().$$;
492 $tmpl->{'file'} = "$templates_dir/$tmpl->{'id'}";
493 local %write = %$tmpl;
494 $write{'email'} =~ s/\\/\\\\/g;
495 $write{'email'} =~ s/\n/\\n/g;
496 if (!-d $templates_dir) {
497 &make_dir($templates_dir, 0755);
499 &lock_file($tmpl->{'file'});
500 &write_file($tmpl->{'file'}, \%write);
501 &unlock_file($tmpl->{'file'});
504 # delete_template(&template)
505 # Removes an existing template. Also does locking.
509 &unlink_logged($tmpl->{'file'});