From cc2502737f4fb1807954776edad5c158f93a0076 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sun, 26 May 2024 01:12:39 +0300 Subject: [PATCH] Add support for additional units in systemd log viewer --- logviewer/config-debian-linux | 4 + logviewer/config-redhat-linux | 4 + logviewer/config.info | 1 + logviewer/index.cgi | 21 +++-- logviewer/logviewer-lib.pl | 140 +++++++++++++++++++++++++++++----- 5 files changed, 143 insertions(+), 27 deletions(-) create mode 100644 logviewer/config-debian-linux create mode 100644 logviewer/config-redhat-linux diff --git a/logviewer/config-debian-linux b/logviewer/config-debian-linux new file mode 100644 index 000000000..56e5bca54 --- /dev/null +++ b/logviewer/config-debian-linux @@ -0,0 +1,4 @@ +lines=100 +others=1 +reverse=1 +extras_units=apache2.service apparmor.service apport.service clamav-daemon.service clamav-freshclam.service console-setup.service cron.service dbus.service dovecot.service fail2ban.service fcgiwrap-*.service firewalld.service lm-sensors.service lookup-domain.service mariadb.service milter-greylist.service mysql.service named.service nginx.service opendkim.service php*-fpm.service polkit.service postfix.service postgrey.service proftpd.service quotaon.service saslauthd.service spamd.service ssh.service sshd.service unattended-upgrades.service usermin.service webmin.service diff --git a/logviewer/config-redhat-linux b/logviewer/config-redhat-linux new file mode 100644 index 000000000..9ca8e0041 --- /dev/null +++ b/logviewer/config-redhat-linux @@ -0,0 +1,4 @@ +lines=100 +others=1 +reverse=1 +extras_units=auditd.service chronyd.service crond.service dovecot.service dracut-shutdown.service fcgiwrap-*.service firewalld.service httpd.service kdump.service lm_sensors.service mariadb.service mysql.service named.service NetworkManager.service nginx.service php-fcgi-*.service php-fpm.service php*-php-fpm.service polkit.service postfix.service proftpd.service saslauthd.service sshd.service usermin.service webmin.service diff --git a/logviewer/config.info b/logviewer/config.info index 717d784e3..d07a8bb9e 100644 --- a/logviewer/config.info +++ b/logviewer/config.info @@ -1,5 +1,6 @@ lines=Default number of lines to display,0,6 refresh=Seconds between log view refreshes,3,Never others=Show logs from other modules?,1,1-Yes,0-No +extras_units=Extra units to show separate logs for,9,50,8,\t extras=Extra log files to show,9,50,4,\t reverse=Log display order,1,1-Newest lines at top,0-Newest lines at bottom diff --git a/logviewer/index.cgi b/logviewer/index.cgi index 39d195ec0..28855ded1 100755 --- a/logviewer/index.cgi +++ b/logviewer/index.cgi @@ -13,6 +13,7 @@ if (!&has_command('journalctl')) { } # Display syslog rules +my @col0; my @col1; my @col2; my @col3; @@ -20,10 +21,12 @@ if ($access{'syslog'}) { my @systemctl_cmds = &get_systemctl_cmds(); foreach $o (@systemctl_cmds) { local @cols; - push(@cols, &text('index_cmd', "".$o->{'cmd'}."")); - push(@cols, $o->{'desc'}); - push(@cols, &ui_link("view_log.cgi?idx=$o->{'id'}&view=1", $text{'index_view'}) ); - push(@col1, \@cols); + push(@cols, &text('index_cmd', "". + &cleanup_destination($o->{'cmd'})."")); + push(@cols, &cleanup_description($o->{'desc'})); + push(@cols, &ui_link("view_log.cgi?idx=$o->{'id'}&view=1", + $text{'index_view'}) ); + push(@col0, \@cols); } # System logs from other modules @@ -73,6 +76,7 @@ if ($access{'syslog'}) { "view_log.cgi?idx=syslog-ng-". $dest->{'index'}."&"."view=1", $text{'index_view'}) ); + @cols = sort { $a->[2] cmp $b->[2] } @cols; push(@col1, \@cols); } } @@ -95,9 +99,10 @@ if ($config{'others'} && $access{'others'}) { push(@cols, &text('index_cmd', "".&html_escape($o->{'cmd'})."")); } - push(@cols, &html_escape($o->{'desc'})); + push(@cols, $o->{'desc'} ? "⇿  ".&html_escape($o->{'desc'}) : ""); push(@cols, &ui_link("view_log.cgi?oidx=$o->{'mindex'}". "&omod=$o->{'mod'}&view=1", $text{'index_view'}) ); + @cols = sort { $a->[2] cmp $b->[2] } @cols; push(@col2, \@cols); } } @@ -114,18 +119,18 @@ foreach $e (&extra_log_files()) { push(@cols, &text('index_cmd', "".&html_escape($e->{'cmd'})."")); } - push(@cols, &html_escape($e->{'desc'})); + push(@cols, $e->{'desc'} ? "⇝  ".&html_escape($e->{'desc'}) : ""); push(@cols, &ui_link("view_log.cgi?extra=".&urlize($e->{'file'} || $e->{'cmd'})."&view=1", $text{'index_view'}) ); + @cols = sort { $a->[2] cmp $b->[2] } @cols; push(@col3, \@cols); } # Print sorted table with logs files and commands -my @acols = (@col1, @col2, @col3); +my @acols = (@col0, @col1, @col2, @col3); print &ui_columns_start( @acols ? [ $text{'index_to'}, $text{'index_rule'}, "" ] : [ ], 100); if (@acols) { - @acols = sort { $a->[2] cmp $b->[2] } @acols; foreach my $col (@acols) { print &ui_columns_row($col); } diff --git a/logviewer/logviewer-lib.pl b/logviewer/logviewer-lib.pl index 41635eeb6..00404f30e 100755 --- a/logviewer/logviewer-lib.pl +++ b/logviewer/logviewer-lib.pl @@ -32,28 +32,123 @@ return 0; sub get_systemctl_cmds { my $lines = $config{'lines'} || 1000; -return !&has_command('journalctl') ? () : ( - { 'cmd' => "journalctl --lines $lines -p alert..emerg", - 'desc' => $text{'journal_journalctl_alert_emerg'}, - 'id' => "journal-1", }, - { 'cmd' => "journalctl --lines $lines -p err..crit", - 'desc' => $text{'journal_journalctl_err_crit'}, - 'id' => "journal-2", }, - { 'cmd' => "journalctl --lines $lines -p notice..warning", - 'desc' => $text{'journal_journalctl_notice_warning'}, - 'id' => "journal-3", }, - { 'cmd' => "journalctl --lines $lines -p debug..info", - 'desc' => $text{'journal_journalctl_debug_info'}, - 'id' => "journal-4", }, - { 'cmd' => "journalctl --lines $lines -k ", - 'desc' => $text{'journal_journalctl_dmesg'}, - 'id' => "journal-5", }, - { 'cmd' => "journalctl --lines $lines -x ", - 'desc' => $text{'journal_expla_journalctl'}, - 'id' => "journal-6", }, +my $journalctl_cmd = &has_command('journalctl'); +return () if (!$journalctl_cmd); +my @rs = ( { 'cmd' => "journalctl --lines $lines", 'desc' => $text{'journal_journalctl'}, + 'id' => "journal-1", }, + { 'cmd' => "journalctl --lines $lines -x ", + 'desc' => $text{'journal_expla_journalctl'}, + 'id' => "journal-2", }, + { 'cmd' => "journalctl --lines $lines -p alert..emerg", + 'desc' => $text{'journal_journalctl_alert_emerg'}, + 'id' => "journal-3", }, + { 'cmd' => "journalctl --lines $lines -p err..crit", + 'desc' => $text{'journal_journalctl_err_crit'}, + 'id' => "journal-4", }, + { 'cmd' => "journalctl --lines $lines -p notice..warning", + 'desc' => $text{'journal_journalctl_notice_warning'}, + 'id' => "journal-5", }, + { 'cmd' => "journalctl --lines $lines -p debug..info", + 'desc' => $text{'journal_journalctl_debug_info'}, + 'id' => "journal-6", }, + { 'cmd' => "journalctl --lines $lines -k ", + 'desc' => $text{'journal_journalctl_dmesg'}, 'id' => "journal-7", } ); +return @rs if (!$config{'extras_units'}); +# Add more units from config if exists on the system +my (%ucache, %uread); +my $units_cache = "$module_config_directory/units.cache"; +&read_file($units_cache, \%ucache); +if (!%ucache) { + my $out = &backquote_command( + "systemctl list-units --full --all ". + "-t service --no-legend"); + foreach my $line (split(/\r?\n/, $out)) { + $line =~ s/^[^a-z0-9\-\_\.]+//i; + my ($unit, $desc) = (split(/\s+/, $line, 5))[0, 4]; + $uread{$unit} = $desc; + } + } +# Add extra units +foreach my $unit (split(/\t+/, $config{'extras_units'})) { + my %units = %uread ? %uread : %ucache; + my ($eunit) = grep { $_ eq $unit } keys %units; + next if (!$eunit && $unit !~ /\*/); + # Units by wildcard + if ($unit =~ /\*/) { + my $unit_re = $unit; + $unit_re =~ s/\*/.*/g; + foreach my $u (keys %units) { + if ($u =~ /^$unit_re$/) { + push(@rs, { 'cmd' => "journalctl --lines ". + "$lines -u $u", + 'desc' => "◦  ". + &fix_clashing_description( + $units{$u}, $u), + 'id' => "journal-$u", }); + $ucache{$u} = $units{$u}; + } + } + next; + } + # Unit by name + my $desc = $units{$eunit} || $unit; + $desc =~ s/\.$//; + push(@rs, { 'cmd' => "journalctl --lines $lines -u $unit", + 'desc' => "◦  ". + &fix_clashing_description($desc), + 'id' => "journal-$unit", }); + $ucache{$eunit} = $desc; + } +# Save cache +if (%uread) { + &lock_file($units_cache); + &write_file($units_cache, \%ucache); + &unlock_file($units_cache); + } +return @rs; +} + +# clear_systemctl_cache() +# Clear the cache of systemctl units +sub clear_systemctl_cache +{ +unlink("$module_config_directory/units.cache"); +} + +# cleanup_destination(cmd) +# Returns a destination of some command cleaned up for display +sub cleanup_destination +{ +my $cmd = shift; +$cmd =~ s/--lines\s+\d+\s*//; +$cmd =~ s/\.service$//; +return $cmd; +} + +# cleanup_description(desc) +# Returns a description cleaned up for display +sub cleanup_description +{ +my $desc = shift; +$desc =~ s/\s+\(Virtualmin\)//; +return $desc; +} + +# fix_clashing_description(description, service) +# Returns known clashing descriptions fixed +sub fix_clashing_description +{ +my ($desc, $serv) = @_; +# EL systems name for PHP FastCGI Process Manager is repeated +if ($serv =~ /php(\d+)-php-fpm/) { + my $php_version = $1; + $php_version = join(".", split(//, $php_version)); + $desc =~ s/PHP/PHP $php_version/; + } +return $desc; } # all_log_files(file) @@ -138,5 +233,12 @@ foreach my $f (@rv) { return @rv; } +# config_post_save +# Called after the module's configuration has been saved +sub config_post_save +{ +&clear_systemctl_cache(); +} + 1;