feat: add Smart service control backend

This commit is contained in:
Mario Fetka
2026-05-21 15:48:09 +02:00
parent 62508ff399
commit 0d6197bfb6
7 changed files with 187 additions and 4 deletions

View File

@@ -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})

View File

@@ -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"

127
control.cmake Normal file
View File

@@ -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/</&lt;/g;
$s =~ s/>/&gt;/g;
$s =~ s/"/&quot;/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 <<EOF;
HTTP/1.0 200 OK
Content-Type: text/html
$server_id
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MARS_NWE service control</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
<style>
body{margin:0;padding:22px;background:#f6f1ea;color:#342a25;font-family:Arial,Helvetica,sans-serif}
a{color:#9f1f1f;text-decoration:none;font-weight:bold}a:hover{text-decoration:underline}
.box{max-width:900px;margin:0 auto;background:#fffdfa;border:1px solid #e5d6c6;border-radius:16px;padding:22px;box-shadow:0 10px 30px rgba(60,30,10,.10)}
h1{margin-top:0;color:#9f1f1f}.meta{padding:12px 14px;background:#fbf6ef;border:1px solid #ecdcc8;border-radius:12px;margin-bottom:14px}
pre{background:#2b241f;color:#f8eadc;padding:16px;border-radius:12px;overflow:auto;white-space:pre-wrap}.ok{color:#2f6f35;font-weight:bold}.bad{color:#9f1f1f;font-weight:bold}
.actions a{display:inline-block;margin:5px 8px 5px 0;padding:9px 12px;border-radius:10px;background:#9f1f1f;color:#fff}.actions a.secondary{background:#6f5b4f}
</style>
</head>
<body>
<div class="box">
<h1>MARS_NWE service control</h1>
EOF
if( $action eq '' )
{
print "<p class=\"bad\">Invalid action.</p>\n";
print "<p>Allowed actions: start, stop, restart, status</p>\n";
print "<p><a href=\"/static/start.html\">Back</a></p>\n";
print "</div></body></html>\n";
return;
}
print '<div class="meta">Action: <b>' . html_escape($action) . '</b><br>' . "\n";
print 'Service: <b>' . html_escape($service) . '</b><br>' . "\n";
print 'systemctl: <b>' . html_escape($systemctl) . '</b></div>' . "\n";
print "<pre>\n";
my @cmd = ( $systemctl, $action, $service );
my $fh;
if( ! open( $fh, '-|', @cmd ) )
{
print 'Could not execute systemctl: ' . html_escape($!) . "\n";
print "</pre><p class=\"bad\">Failed.</p>\n";
print "<p><a href=\"/static/start.html\">Back</a></p>\n";
print "</div></body></html>\n";
return;
}
while( my $line = <$fh> )
{
print html_escape($line);
}
close($fh);
my $rc = $? >> 8;
if( $rc == 0 )
{
print "</pre><p class=\"ok\">Command completed successfully.</p>\n";
}
else
{
print "</pre><p class=\"bad\">Command failed with exit code $rc.</p>\n";
}
print <<EOF;
<div class="actions">
<a href="/cgi-bin/control?status" class="secondary">Status</a>
<a href="/cgi-bin/control?start">Start</a>
<a href="/cgi-bin/control?stop" class="secondary">Stop</a>
<a href="/cgi-bin/control?restart">Restart</a>
</div>
<p><a href="/static/start.html">Back</a></p>
</div>
</body>
</html>
EOF
}
1;

View File

@@ -19,6 +19,7 @@ RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ReadWritePaths=@MARS_NWE_INSTALL_FULL_CONFDIR@
ProtectHome=true
[Install]

View File

@@ -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 = <STDIN>;
$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 = <STDIN>;
@@ -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' );

View File

@@ -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.

View File

@@ -100,8 +100,12 @@
<DIV CLASS="actions">
<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
<TR>
<TD WIDTH="50%"><A CLASS="action" HREF="/cgi-bin/control?start">Start <TT>MARS_NWE</TT></A></TD>
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/cgi-bin/control?stop">Stop <TT>MARS_NWE</TT></A></TD>
<TD WIDTH="50%"><A CLASS="action" HREF="/service/control?start">Start <TT>MARS_NWE</TT></A></TD>
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?stop">Stop <TT>MARS_NWE</TT></A></TD>
</TR>
<TR>
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?restart">Restart <TT>MARS_NWE</TT></A></TD>
<TD WIDTH="50%"><A CLASS="action secondary" HREF="/service/control?status">Status <TT>MARS_NWE</TT></A></TD>
</TR>
</TABLE>
</DIV>