2 # Functions for the syslog module
4 BEGIN { push(@INC, ".."); };
7 %access = &get_module_acl();
10 # Parses the syslog configuration file into an array ref of hash refs, one
11 # for each log file or destination
15 $cfile ||= $config{'syslog_conf'};
17 local ($line, $cont, @rv);
18 local $tag = { 'tag' => '*',
22 &open_readfile(CONF, $cfile);
23 local @lines = <CONF>;
25 foreach my $line (@lines) {
29 # continuation .. get the next lines
31 while($cont = <CONF>) {
36 last if ($line !~ s/\\$//);
39 if ($line =~ /^\$IncludeConfig\s+(\S+)/) {
40 # rsyslog include statement .. follow the money
41 foreach my $icfile (glob($1)) {
42 my $ic = &get_config($icfile);
44 foreach my $c (@$ic) {
45 $c->{'index'} += scalar(@rv);
51 elsif ($line =~ /^\$(\S+)\s*(\S*)/) {
52 # rsyslog special directive - ignored for now
54 elsif ($line =~ /^if\s+/) {
55 # rsyslog if statement .. ignored too
57 elsif ($line =~ /^(#*)\s*([^#\s]+\.\S+)\s+(\S+)$/ ||
58 $line =~ /^(#*)\s*([^#\s]+\.\S+)\s+(\|.*)$/) {
59 # Regular log destination
61 local $log = { 'active' => !$1,
62 'sel' => [ split(/;/, $2) ],
66 if ($act =~ /^\-(\/\S+)$/) {
70 elsif ($act =~ /^\|(.*)$/) {
73 elsif ($act =~ /^(\/\S+)$/) {
77 elsif ($act =~ /^\@\@(\S+)$/) {
78 $log->{'socket'} = $1;
80 elsif ($act =~ /^\@(\S+)$/) {
87 $log->{'users'} = [ split(/,/, $act) ];
89 $log->{'index'} = scalar(@rv);
90 $log->{'section'} = $tag;
91 $tag->{'eline'} = $lnum;
92 if ($log->{'file'} =~ s/^(\/\S+);(\S+)$/$1/ ||
93 $log->{'pipe'} =~ s/^(\/\S+);(\S+)$/$1/) {
95 $log->{'format'} = $2;
99 elsif ($line =~ /^(#?)!(\S+)$/) {
100 # Start of tagged section, as seen on BSD
101 push(@rv, { 'tag' => $2,
102 'index' => scalar(@rv),
116 local $lref = &read_file_lines($config{'syslog_conf'});
117 if ($config{'tags'}) {
118 splice(@$lref, $_[0]->{'section'}->{'eline'}+1, 0, &log_line($_[0]));
121 push(@$lref, &log_line($_[0]));
126 # update_log(&old, &log)
129 local $lref = &read_file_lines($_[0]->{'cfile'} || $config{'syslog_conf'});
130 if ($config{'tags'} && $_[0]->{'section'} ne $_[1]->{'section'}) {
131 if ($_[0]->{'section'}->{'line'} < $_[1]->{'section'}->{'line'}) {
132 splice(@$lref, $_[1]->{'section'}->{'eline'}+1, 0,
134 splice(@$lref, $_[0]->{'line'},
135 $_[0]->{'eline'} - $_[0]->{'line'} + 1);
138 splice(@$lref, $_[0]->{'line'},
139 $_[0]->{'eline'} - $_[0]->{'line'} + 1);
140 splice(@$lref, $_[1]->{'section'}->{'eline'}+1, 0,
145 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
154 local $lref = &read_file_lines($_[0]->{'cfile'} || $config{'syslog_conf'});
155 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1);
162 if ($_[0]->{'file'}) {
163 $d = ($_[0]->{'sync'} || !$config{'sync'} ? "" : "-").$_[0]->{'file'};
165 elsif ($_[0]->{'pipe'}) {
166 $d = '|'.$_[0]->{'pipe'};
168 elsif ($_[0]->{'host'}) {
169 $d = '@'.$_[0]->{'host'};
171 elsif ($_[0]->{'users'}) {
172 $d = join(",", @{$_[0]->{'users'}});
174 elsif ($_[0]->{'socket'}) {
175 $d = '@@'.$_[0]->{'socket'};
180 if ($_[0]->{'format'}) {
182 $d .= ";".$_[0]->{'format'};
184 return ($_[0]->{'active'} ? "" : "#").join(";", @{$_[0]->{'sel'}})."\t".$d;
188 # Returns a list of all priorities
191 return ( 'debug', 'info', 'notice', 'warning',
192 'err', 'crit', 'alert', 'emerg' );
196 # Returns 1 if some log can be viewed/edited, 0 if not
199 return 1 if (!$access{'logs'});
200 local @files = split(/\s+/, $access{'logs'});
203 $lf = $_[0]->{'file'} || $_[0]->{'pipe'} || $_[0]->{'host'} ||
204 $_[0]->{'socket'} || $_[0]->{'cmd'} ||
205 ($_[0]->{'all'} ? "*" : "users");
210 foreach $f (@files) {
211 return 1 if ($f eq $lf || &is_under_directory($f, $lf));
218 local $oldslash = $/;
220 &open_readfile(CONF, $config{'syslog_conf'});
221 local $conf1 = <CONF>;
223 &open_execute_command(CONF, "$config{'m4_path'} $config{'syslog_conf'}", 1, 1);
224 local $conf2 = <CONF>;
227 return $conf1 ne $conf2;
230 # get_syslog_pid(pid)
231 # Returns the syslog PID file
235 if ($config{'pid_file'}) {
236 if (&open_readfile(PID, $config{'pid_file'}) &&
237 <PID> =~ /^(\d+)$/ && kill(0, $1)) {
242 ($pid) = &find_byname("syslogd");
248 # Stop and re-start the syslog server. Returns an error message on failure.
251 if ($config{'restart_cmd'}) {
252 &system_logged("$config{'restart_cmd'} >/dev/null 2>/dev/null </dev/null");
255 local $pid = &get_syslog_pid();
256 $pid && &kill_logged('TERM', $pid) ||
257 return &text('restart_ekill', $pid, $!);
259 if ($config{'start_cmd'}) {
260 &system_logged("$config{'start_cmd'} >/dev/null 2>/dev/null </dev/null");
263 &system_logged("cd / ; $config{'syslogd'} >/dev/null 2>/dev/null </dev/null &");
270 # Tell the syslog server to re-open it's log files
273 if ($config{'signal_cmd'}) {
274 &system_logged("$config{'signal_cmd'} >/dev/null 2>/dev/null </dev/null");
278 local $pid = &get_syslog_pid();
280 &kill_logged('HUP', $pid);
285 # all_log_files(file)
286 # Given a filename, returns all rotated versions, ordered by oldest first
289 $_[0] =~ /^(.*)\/([^\/]+)$/;
293 opendir(DIR, &translate_filename($dir));
294 foreach $f (readdir(DIR)) {
295 local $trans = &translate_filename("$dir/$f");
296 if ($f =~ /^\Q$base\E/ && -f $trans && $f !~ /\.offset$/) {
297 push(@rv, "$dir/$f");
298 $mtime{"$dir/$f"} = [ stat($trans) ];
302 return sort { $mtime{$a}->[9] <=> $mtime{$b}->[9] } @rv;
305 # get_other_module_logs([module])
306 # Returns a list of logs supplied by other modules
307 sub get_other_module_logs
312 foreach my $minfo (&get_all_module_infos()) {
313 next if ($mod && $minfo->{'dir'} ne $mod);
314 next if (!$minfo->{'syslog'});
315 next if (!&foreign_installed($minfo->{'dir'}));
316 local $mdir = &module_root_directory($minfo->{'dir'});
317 next if (!-r "$mdir/syslog_logs.pl");
318 &foreign_require($minfo->{'dir'}, "syslog_logs.pl");
320 foreach my $l (&foreign_call($minfo->{'dir'}, "syslog_getlogs")) {
321 local $fc = $l->{'file'} || $l->{'cmd'};
322 next if ($done{$fc}++);
323 $l->{'minfo'} = $minfo;
324 $l->{'mod'} = $minfo->{'dir'};
325 $l->{'mindex'} = $j++;
329 @rv = sort { $a->{'minfo'}->{'desc'} cmp $b->{'minfo'}->{'desc'} } @rv;
331 foreach my $l (@rv) {
332 $l->{'index'} = $i++;
337 # catter_command(file)
338 # Given a file that may be compressed, returns the command to output it in
339 # plain text, or undef if impossible
343 local $q = quotemeta($l);
344 if ($l =~ /\.gz$/i) {
345 return &has_command("gunzip") ? "gunzip -c $q" : undef;
347 elsif ($l =~ /\.Z$/i) {
348 return &has_command("uncompress") ? "uncompress -c $q" : undef;
350 elsif ($l =~ /\.bz2$/i) {
351 return &has_command("bunzip2") ? "bunzip2 -c $q" : undef;
359 # Returns a list of extra log files available to the current Webmin user. No filtering
360 # based on allowed directory is done though!
364 foreach my $fd (split(/\t+/, $config{'extras'}), split(/\t+/, $access{'extras'})) {
365 if ($fd =~ /^(\S+)\s+(\S.*)$/) {
366 push(@rv, { 'file' => $1, 'desc' => $2 });
369 push(@rv, { 'file' => $fd });