From 0d6197bfb628e6001922e061e7e4bd7ee1842888 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Thu, 21 May 2026 15:48:09 +0200 Subject: [PATCH] feat: add Smart service control backend --- CMakeLists.txt | 19 ++++++ config.h.cmake | 5 +- control.cmake | 127 +++++++++++++++++++++++++++++++++++ mars-nwe-webui.service.cmake | 1 + smart.cmake | 21 +++++- smart.conf.cmake | 10 +++ static/start.html.cmake | 8 ++- 7 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 control.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 401755d..c6ef08e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,19 @@ # Generated files ############## +# systemd itself is detected by the top-level systemdservice.cmake. +# This webui submodule only consumes WITH_SYSTEMD and SYSTEMD_SERVICES_INSTALL_DIR. +if(NOT DEFINED MARS_NWE_SYSTEMD_SERVICE) + set(MARS_NWE_SYSTEMD_SERVICE "mars-nwe-serv.service" CACHE STRING "MARS_NWE systemd service name") +endif() + +if(NOT DEFINED SYSTEMCTL_EXECUTABLE) + find_program(SYSTEMCTL_EXECUTABLE systemctl) +endif() +if(NOT SYSTEMCTL_EXECUTABLE) + set(SYSTEMCTL_EXECUTABLE "/usr/bin/systemctl") +endif() + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @@ -25,6 +38,11 @@ configure_file( "${CMAKE_CURRENT_BINARY_DIR}/smart" IMMEDIATE @ONLY) +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/control.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/control" + IMMEDIATE @ONLY) + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/static/start.html.cmake" "${CMAKE_CURRENT_BINARY_DIR}/static/start.html" @@ -74,6 +92,7 @@ target_link_libraries(check_login install(FILES ${CMAKE_CURRENT_BINARY_DIR}/smart.conf DESTINATION ${MARS_NWE_INSTALL_FULL_CONFDIR}) install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/smart DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}) +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/control DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}) install(FILES smart.pamd DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d RENAME smart) install(PROGRAMS apply.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}) diff --git a/config.h.cmake b/config.h.cmake index 0f0a011..2701a2c 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -5,7 +5,10 @@ #define NWWEBUI_VERSION "@MARS_NWE_VERSION@" #define DEFAULT_SMART_CONF "@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf" -#define DEFAULT_SMART_PERL "@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart" +#define DEFAULT_SMART_PERL "@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart" +#define DEFAULT_CONTROL_PERL "@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/control" +#define DEFAULT_MARS_SERVICE "@MARS_NWE_SYSTEMD_SERVICE@" +#define DEFAULT_SYSTEMCTL_PATH "@SYSTEMCTL_EXECUTABLE@" #define LOG_PATH_DEFAULT "@MARS_NWE_LOG_DIR@/nwwebui.log" diff --git a/control.cmake b/control.cmake new file mode 100644 index 0000000..4a08152 --- /dev/null +++ b/control.cmake @@ -0,0 +1,127 @@ +#!/usr/bin/perl +# +# SMArT service control helper for MARS_NWE systemd unit. +# Installed next to the other SMArT perl helpers. +# + +use strict; +use warnings; + +our ($server_id, $mars_nwe_service, $smart_systemctl_path, $cc, %p); + +sub html_escape($) +{ + my ($s) = @_; + $s = '' unless defined $s; + $s =~ s/&/&/g; + $s =~ s//>/g; + $s =~ s/"/"/g; + return $s; +} + +sub handle_request() +{ + my $service = $mars_nwe_service || '@MARS_NWE_SYSTEMD_SERVICE@'; + my $systemctl = $smart_systemctl_path || '@SYSTEMCTL_EXECUTABLE@'; + my $query = defined($cc) ? $cc : ''; + my $action = ''; + + $query =~ s/^\?//; + + if( $query =~ /^(start|stop|restart|status)$/ ) + { + $action = $1; + } + elsif( $query =~ /(?:^|&)action=(start|stop|restart|status)(?:&|$)/ ) + { + $action = $1; + } + elsif( defined($p{action}) && $p{action} =~ /^(start|stop|restart|status)$/ ) + { + $action = $1; + } + + print < + + + + +MARS_NWE service control + + + + +
+

MARS_NWE service control

+EOF + + if( $action eq '' ) + { + print "

Invalid action.

\n"; + print "

Allowed actions: start, stop, restart, status

\n"; + print "

Back

\n"; + print "
\n"; + return; + } + + print '
Action: ' . html_escape($action) . '
' . "\n"; + print 'Service: ' . html_escape($service) . '
' . "\n"; + print 'systemctl: ' . html_escape($systemctl) . '
' . "\n"; + print "
\n";
+
+    my @cmd = ( $systemctl, $action, $service );
+    my $fh;
+    if( ! open( $fh, '-|', @cmd ) )
+    {
+        print 'Could not execute systemctl: ' . html_escape($!) . "\n";
+        print "

Failed.

\n"; + print "

Back

\n"; + print "\n"; + return; + } + + while( my $line = <$fh> ) + { + print html_escape($line); + } + + close($fh); + my $rc = $? >> 8; + + if( $rc == 0 ) + { + print "

Command completed successfully.

\n"; + } + else + { + print "

Command failed with exit code $rc.

\n"; + } + + print < +Status +Start +Stop +Restart + +

Back

+ + + +EOF +} + +1; diff --git a/mars-nwe-webui.service.cmake b/mars-nwe-webui.service.cmake index 2005c46..e86d211 100644 --- a/mars-nwe-webui.service.cmake +++ b/mars-nwe-webui.service.cmake @@ -19,6 +19,7 @@ RestartSec=2 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full +ReadWritePaths=@MARS_NWE_INSTALL_FULL_CONFDIR@ ProtectHome=true [Install] diff --git a/smart.cmake b/smart.cmake index b16fd43..8846ffe 100644 --- a/smart.cmake +++ b/smart.cmake @@ -38,11 +38,17 @@ $ENV{HOME} = '@MARS_NWE_INSTALL_FULL_CONFDIR@'; $smart_libexec_dir = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@'; $smart_libexec_dir =~ s#/*$##; +$smart_control_path = $smart_libexec_dir . '/control' unless defined $smart_control_path; +$mars_nwe_service = '@MARS_NWE_SYSTEMD_SERVICE@' unless defined $mars_nwe_service; +$smart_systemctl_path = '@SYSTEMCTL_EXECUTABLE@' unless defined $smart_systemctl_path; + $l = ; $l =~ s/[\n\r]//g; +$request_uri = ""; @c = split( ' ', $l ); if( scalar( @c ) > 2 ) { + $request_uri = $c[1]; while( keys( %h ) < 15 ) # Who would ever want to send more headers??? { $l = ; @@ -104,7 +110,20 @@ foreach $p ( @p ) } @c = split( '/', $c ); -if( $c[0] eq 'apply' ) +if( ( $c[0] eq 'service' && $c[1] eq 'control' ) || + ( $c[0] eq 'cgi-bin' && $c[1] eq 'control' ) ) +{ + # Service control must run before drop_root(). + # /service/control is the preferred path; /cgi-bin/control is kept as a legacy alias. + my $rv = do( $smart_control_path ); + if( ! defined $rv ) + { + error( 500 ); + } + handle_request(); + exit; +} +elsif( $c[0] eq 'apply' ) { do( $smart_libexec_dir . '/readconfig.pl' ); do( $smart_libexec_dir . '/apply.pl' ); diff --git a/smart.conf.cmake b/smart.conf.cmake index 4297614..10f7915 100644 --- a/smart.conf.cmake +++ b/smart.conf.cmake @@ -64,6 +64,16 @@ $smart_log_path = '@MARS_NWE_LOG_DIR@/smart.log'; # Path to the PAM-based login helper used for root authentication. $smart_check_login = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/check_login'; + +# Path to the SMArT service-control helper. +$smart_control_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/control'; + +# systemd unit controlled by the SMArT service-control page. +$mars_nwe_service = '@MARS_NWE_SYSTEMD_SERVICE@'; + +# systemctl executable used by the service-control helper. +$smart_systemctl_path = '@SYSTEMCTL_EXECUTABLE@'; + # Optional explicit path to the main SMArT Perl program. # This is normally not required, because nwwebui already has a built-in default. # Uncomment and adjust only if a non-standard location must be used. diff --git a/static/start.html.cmake b/static/start.html.cmake index cadb1e0..34f192d 100644 --- a/static/start.html.cmake +++ b/static/start.html.cmake @@ -100,8 +100,12 @@