Compare commits
16 Commits
ce43a20174
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c188553107 | ||
|
|
a39b33e6e0 | ||
|
|
5a779f1299 | ||
|
|
4ad200c9bb | ||
|
|
1394b3ba6c | ||
|
|
49ff80ecbc | ||
|
|
7c69a35a25 | ||
|
|
0d6197bfb6 | ||
|
|
62508ff399 | ||
|
|
1865a2fc79 | ||
|
|
206dbfa073 | ||
|
|
e5c4fb8045 | ||
|
|
2696ff557c | ||
|
|
4bd8223415 | ||
|
|
a36eb32d33 | ||
|
|
cfa1579f8e |
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
error.log
|
||||||
141
CMakeLists.txt
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#################################
|
||||||
|
# Project
|
||||||
|
##############
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Dependencies
|
||||||
|
##############
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# 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"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/smart.conf.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/smart.conf"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/smart.cmake"
|
||||||
|
"${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"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/mars-nwe-webui.service.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/mars-nwe-webui.service"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Compiler Switches
|
||||||
|
##############
|
||||||
|
|
||||||
|
INCLUDE_DIRECTORIES(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
${CMAKE_BINARY_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Source Files
|
||||||
|
##############
|
||||||
|
|
||||||
|
add_executable(nwwebui nwwebui.c)
|
||||||
|
add_executable(check_login check_login.c)
|
||||||
|
add_executable(smart_userlist smart_userlist.c)
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Linking
|
||||||
|
##############
|
||||||
|
|
||||||
|
target_link_libraries(nwwebui
|
||||||
|
OpenSSL::SSL
|
||||||
|
OpenSSL::Crypto
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(check_login
|
||||||
|
${PAM_LIBRARY}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(smart_userlist
|
||||||
|
${PAM_LIBRARY}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
#################################
|
||||||
|
# Install Files
|
||||||
|
##############
|
||||||
|
|
||||||
|
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})
|
||||||
|
install(PROGRAMS readconfig.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||||
|
install(PROGRAMS settings.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||||
|
install(PROGRAMS static.pl DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/static/start.html DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/menu.html DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/smart_icon.jpg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/smart.jpg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/favicon.ico DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/favicon-32x32.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/favicon-16x16.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/apple-touch-icon.png DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-start.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-service.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-general.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-dirs.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-configh.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-security.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-susers.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-volumes.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-devices.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-logging.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-smart.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-stations.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-users.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-groups.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
install(FILES static/icon-queues.svg DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR}/static)
|
||||||
|
|
||||||
|
if(WITH_SYSTEMD)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mars-nwe-webui.service DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS check_login DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||||
|
install(TARGETS smart_userlist DESTINATION ${MARS_NWE_INSTALL_FULL_LIBEXECDIR})
|
||||||
|
|
||||||
|
install(TARGETS nwwebui DESTINATION ${CMAKE_INSTALL_SBINDIR})
|
||||||
|
|
||||||
489
README.md
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
# SMArT
|
||||||
|
|
||||||
|
SMArT is the web-based configuration interface for **MARS_NWE**, a Novell NetWare 3.x emulator for Linux and FreeBSD.
|
||||||
|
|
||||||
|
In the current setup, this repository is integrated into the main `mars_nwe` project as a **Git submodule**. It is built, installed, and released as part of the normal **MARS_NWE** release process rather than as a separate end-user component.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
The main menu is the normal entry point after login. It shows the available
|
||||||
|
configuration sections on the left and opens the selected explanation or editor
|
||||||
|
view on the right.
|
||||||
|
|
||||||
|
All screenshots in this section use anonymized example values for users,
|
||||||
|
hostnames, printer names and local mountpoints.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Additional UI examples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td width="50%"><a href="doc/screenshots/login.png"><img src="doc/screenshots/thumbs/login.png" alt="Login screen"></a><br><strong>Login</strong><br>PAM-based login with SMArT session cookies.</td>
|
||||||
|
<td width="50%"><a href="doc/screenshots/service-runtime.png"><img src="doc/screenshots/thumbs/service-runtime.png" alt="Runtime information"></a><br><strong>Runtime information</strong><br>Configured paths, service name and project link.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%"><a href="doc/screenshots/volumes-import.png"><img src="doc/screenshots/thumbs/volumes-import.png" alt="Volume import"></a><br><strong>Volume import</strong><br>Detected host mountpoints can be imported as MARS_NWE volumes.</td>
|
||||||
|
<td width="50%"><a href="doc/screenshots/user-editor.png"><img src="doc/screenshots/thumbs/user-editor.png" alt="User editor"></a><br><strong>User editor</strong><br>Optional Unix user mapping and bindery group membership.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%"><a href="doc/screenshots/print-queue-cups.png"><img src="doc/screenshots/thumbs/print-queue-cups.png" alt="Print queue CUPS integration"></a><br><strong>Print queues</strong><br>CUPS printer selection can prefill the Unix print command.</td>
|
||||||
|
<td width="50%"><a href="doc/screenshots/smart-settings.png"><img src="doc/screenshots/thumbs/smart-settings.png" alt="SMArT settings"></a><br><strong>SMArT settings</strong><br>Bindery server settings and server-name synchronization.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%"><a href="doc/screenshots/general-settings.png"><img src="doc/screenshots/thumbs/general-settings.png" alt="General settings"></a><br><strong>General settings</strong><br>Core MARS_NWE options with synchronized bindery naming.</td>
|
||||||
|
<td width="50%"><a href="doc/screenshots/advanced-path-settings.png"><img src="doc/screenshots/thumbs/advanced-path-settings.png" alt="Advanced path settings"></a><br><strong>Advanced sections</strong><br>Only the matching advanced section is displayed for the selected menu item.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%"><a href="doc/screenshots/validation-error.png"><img src="doc/screenshots/thumbs/validation-error.png" alt="Validation error"></a><br><strong>Validation errors</strong><br>Invalid input is rejected with a focused error page and a back action.</td>
|
||||||
|
<td width="50%"><a href="doc/screenshots/service-control.png"><img src="doc/screenshots/thumbs/service-control.png" alt="Service control output"></a><br><strong>Service control</strong><br>Start, stop, restart and status output for the configured MARS_NWE service.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
A compact maintainer overview of the screenshot set is available as
|
||||||
|
[`doc/screenshots/screenshot-contact-sheet.png`](doc/screenshots/screenshot-contact-sheet.png).
|
||||||
|
|
||||||
|
## Project status and integration
|
||||||
|
|
||||||
|
This repository is intended to be embedded into the main `mars_nwe` Git repository:
|
||||||
|
|
||||||
|
- Main project: `mars_nwe`
|
||||||
|
- Submodule role: provides the SMArT web UI and helper tools
|
||||||
|
- Release model: shipped as part of the integrated **MARS_NWE** release
|
||||||
|
|
||||||
|
The build and install rules install the web UI binary, Perl helpers, configuration template, static assets, systemd unit, and PAM file as part of the integrated installation target.
|
||||||
|
|
||||||
|
## Architecture overview
|
||||||
|
|
||||||
|
SMArT consists of three main pieces:
|
||||||
|
|
||||||
|
1. **`nwwebui`** – a dedicated HTTP/HTTPS frontend service
|
||||||
|
2. **Perl helper scripts** – request routing, configuration pages, bindery operations, validation, and service control
|
||||||
|
3. **Small native helpers** – PAM login checking and optional user enumeration helpers
|
||||||
|
|
||||||
|
The `nwwebui` service can expose the UI over:
|
||||||
|
|
||||||
|
- **HTTP on port 9080**
|
||||||
|
- **HTTPS on port 9443**
|
||||||
|
|
||||||
|
Both listeners can run in parallel. HTTPS is recommended for real deployments because the login form transmits credentials. Plain HTTP is still useful for local testing or trusted internal environments.
|
||||||
|
|
||||||
|
## Major features
|
||||||
|
|
||||||
|
### HTML login, sessions, and logout
|
||||||
|
|
||||||
|
SMArT provides a form-based login page, session cookies, and a logout action. Static assets such as the SMArT logo are served before authentication so the login page can render correctly.
|
||||||
|
|
||||||
|
Runtime session files are stored under the WebUI runtime directory, typically:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/run/mars-nwe-webui
|
||||||
|
```
|
||||||
|
|
||||||
|
### PAM authentication with administrator group restriction
|
||||||
|
|
||||||
|
Authentication is performed through the PAM service `smart` using the `check_login` helper. In addition to a successful PAM login, the user must be a member of the configured Unix administrator group.
|
||||||
|
|
||||||
|
The administrator group is configured at build time through the main `mars_nwe` CMake project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DMARS_NWE_SMART_ADMIN_GROUP=root ...
|
||||||
|
```
|
||||||
|
|
||||||
|
The default is `root` to preserve the traditional behavior on existing systems. On normal Unix systems the `root` user has primary group `root`, so root can still log in. For delegated administration, build with a dedicated group instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DMARS_NWE_SMART_ADMIN_GROUP=nwadmin ...
|
||||||
|
groupadd nwadmin
|
||||||
|
usermod -aG nwadmin mario
|
||||||
|
```
|
||||||
|
|
||||||
|
After installation the effective setting appears in `smart.conf` as:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$smart_admin_group = 'root';
|
||||||
|
```
|
||||||
|
|
||||||
|
or, for a delegated build:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$smart_admin_group = 'nwadmin';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service control page
|
||||||
|
|
||||||
|
The start page includes controls for the configured MARS_NWE service:
|
||||||
|
|
||||||
|
- start
|
||||||
|
- stop
|
||||||
|
- restart
|
||||||
|
- status
|
||||||
|
|
||||||
|
The service name is supplied by the build configuration and can be overridden in `smart.conf`:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$mars_nwe_service = 'mars-nwe-serv.service';
|
||||||
|
$smart_systemctl_path = '/usr/bin/systemctl';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Runtime information page
|
||||||
|
|
||||||
|
The start page shows the important runtime paths generated by CMake, including:
|
||||||
|
|
||||||
|
- main MARS_NWE configuration file
|
||||||
|
- SMArT configuration file
|
||||||
|
- WebUI helper/script directory
|
||||||
|
- MARS_NWE service unit name
|
||||||
|
- `systemctl` executable
|
||||||
|
|
||||||
|
This helps diagnose packaging or installation path issues without searching through generated files.
|
||||||
|
|
||||||
|
### Configuration editors and advanced sections
|
||||||
|
|
||||||
|
The main menu contains the commonly used configuration areas and advanced sections. Advanced pages are shown only when the selected section is opened, which keeps the menu usable while still exposing low-level MARS_NWE options.
|
||||||
|
|
||||||
|
Current sections include, among others:
|
||||||
|
|
||||||
|
- setup first
|
||||||
|
- MARS_NWE service
|
||||||
|
- general settings
|
||||||
|
- directories
|
||||||
|
- precompiled/path settings
|
||||||
|
- security
|
||||||
|
- user configuration
|
||||||
|
- volumes
|
||||||
|
- devices
|
||||||
|
- logging
|
||||||
|
- stations/access control
|
||||||
|
- users
|
||||||
|
- groups
|
||||||
|
- print queues
|
||||||
|
|
||||||
|
### Import helpers
|
||||||
|
|
||||||
|
SMArT can prefill or discover host-side data for common configuration tasks:
|
||||||
|
|
||||||
|
- local mount points for volume creation
|
||||||
|
- Unix users for MARS_NWE user mapping
|
||||||
|
- CUPS printers for print queue command generation
|
||||||
|
- IPX interfaces for device configuration
|
||||||
|
|
||||||
|
These helpers are meant to reduce manual typing while still leaving the final configuration under administrator control.
|
||||||
|
|
||||||
|
### Validation and error pages
|
||||||
|
|
||||||
|
The apply path validates common input before writing configuration or changing bindery data. Invalid values are shown on a dedicated validation page instead of failing silently or returning an empty HTTP response.
|
||||||
|
|
||||||
|
Validation currently covers areas such as:
|
||||||
|
|
||||||
|
- volume names and Unix paths
|
||||||
|
- device/network parameters
|
||||||
|
- print queue names, print commands, and spool directories
|
||||||
|
- user names and group names
|
||||||
|
- invalid bindery characters
|
||||||
|
|
||||||
|
### Bindery command handling
|
||||||
|
|
||||||
|
Bindery operations are executed through checked helper functions instead of silent `system()` calls. The WebUI logs command start, command success, command failure, and relevant output.
|
||||||
|
|
||||||
|
Commands such as `nwbocreate`, `nwbprm`, and `nwborm` are handled through `run_bindery_cmd()`. Pipe-style `nwbpset` operations are handled through `run_bindery_pipe()` using a temporary input file and checked return code.
|
||||||
|
|
||||||
|
This improves diagnostics for user, group, and print queue operations. The browser receives a structured error page when a bindery command fails.
|
||||||
|
|
||||||
|
### Bindery success pages
|
||||||
|
|
||||||
|
After successful user, group, or print queue changes, SMArT can show a result page with the number of successful bindery commands. This makes bindery changes visible to the administrator and avoids silent redirects after complex operations.
|
||||||
|
|
||||||
|
### Optional Unix user mapping updates
|
||||||
|
|
||||||
|
Existing MARS_NWE users no longer have their `UNIX_USER` mapping removed unless the administrator explicitly requests a mapping change. This prevents accidental loss of Unix user assignments when only editing full name, password, or group membership.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
SMArT has two relevant log streams:
|
||||||
|
|
||||||
|
- the `nwwebui` service log
|
||||||
|
- the Perl frontend log, normally `smart.log`
|
||||||
|
|
||||||
|
Typical paths:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/var/log/mars_nwe/nwwebui.log
|
||||||
|
/var/log/mars_nwe/smart.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Perl frontend log level
|
||||||
|
|
||||||
|
The Perl frontend log level is configured in `smart.conf`:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
# SMArT Perl logging verbosity.
|
||||||
|
# Values: error, warning, info, debug, trace
|
||||||
|
# Default: info
|
||||||
|
$smart_debug_level = 'info';
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported values, from quiet to verbose:
|
||||||
|
|
||||||
|
- `error` – only real errors that abort or fail an operation
|
||||||
|
- `warning` – errors and warnings about unusual but non-fatal situations
|
||||||
|
- `info` – normal operational messages, command start/finish, default
|
||||||
|
- `debug` – additional diagnostic information for troubleshooting
|
||||||
|
- `trace` – very verbose step-by-step traces, including bindery pipe payloads
|
||||||
|
|
||||||
|
Use `trace` only while debugging a concrete problem. It may include submitted bindery payload data and can produce a lot of log output. After debugging, switch back to `info`.
|
||||||
|
|
||||||
|
### `nwwebui` service log level
|
||||||
|
|
||||||
|
The `nwwebui` service has its own numeric log level:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$nw_log_level = 'info';
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical meanings:
|
||||||
|
|
||||||
|
- `0` = errors only
|
||||||
|
- `1` = informational messages
|
||||||
|
- `2` = debug output
|
||||||
|
|
||||||
|
## Installed components
|
||||||
|
|
||||||
|
### Binaries
|
||||||
|
|
||||||
|
- `nwwebui` – dedicated web service frontend
|
||||||
|
- `check_login` – PAM authentication and administrator-group helper
|
||||||
|
- optional host discovery helpers, depending on build options
|
||||||
|
|
||||||
|
### Native helper tools
|
||||||
|
|
||||||
|
- `check_login` – validates PAM credentials and verifies membership in the configured SMArT administrator group
|
||||||
|
- `smart_userlist` – lists local Unix users for the optional bindery-to-Unix user mapping selector
|
||||||
|
|
||||||
|
### Perl helpers
|
||||||
|
|
||||||
|
- `smart`
|
||||||
|
- `apply.pl`
|
||||||
|
- `readconfig.pl`
|
||||||
|
- `settings.pl`
|
||||||
|
- `static.pl`
|
||||||
|
- `control`
|
||||||
|
|
||||||
|
### Configuration and assets
|
||||||
|
|
||||||
|
- `smart.conf`
|
||||||
|
- static HTML/image assets for the WebUI
|
||||||
|
- optional `mars-nwe-webui.service` systemd unit
|
||||||
|
- PAM file installed as `/etc/pam.d/smart`
|
||||||
|
- local Unix user-list helper used by the user editor
|
||||||
|
|
||||||
|
## Typical runtime paths
|
||||||
|
|
||||||
|
The templates use CMake placeholders. In a typical Linux installation, the effective paths are similar to:
|
||||||
|
|
||||||
|
- Main MARS_NWE config directory: `/etc/mars_nwe`
|
||||||
|
- SMArT config file: `/etc/mars_nwe/smart.conf`
|
||||||
|
- Main MARS_NWE server config: `/etc/mars_nwe/nwserv.conf`
|
||||||
|
- Helper binaries and scripts, including `smart`, `check_login`, `smart_userlist` and Perl helpers: `/usr/libexec/mars_nwe`
|
||||||
|
- Static SMArT assets: `/usr/libexec/mars_nwe/static`
|
||||||
|
- Log directory: `/var/log/mars_nwe`
|
||||||
|
- Runtime/session directory: `/run/mars-nwe-webui`
|
||||||
|
- PID directory: `/run/mars_nwe`
|
||||||
|
- TLS certificate: `/etc/mars_nwe/server.crt`
|
||||||
|
- TLS private key: `/etc/mars_nwe/server.key`
|
||||||
|
- PAM file: `/etc/pam.d/smart`
|
||||||
|
|
||||||
|
Packaging may adjust these paths depending on the target distribution.
|
||||||
|
|
||||||
|
## `smart.conf` example
|
||||||
|
|
||||||
|
A documented example with standard installation paths:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
# Main MARS_NWE configuration
|
||||||
|
$mars_config = '/etc/mars_nwe/nwserv.conf';
|
||||||
|
$nonroot_user = 'nobody';
|
||||||
|
$smart_compact_nwservconf = 0;
|
||||||
|
|
||||||
|
# SMArT internal file layout
|
||||||
|
$smart_conf_path = '/etc/mars_nwe/smart.conf';
|
||||||
|
$smart_nwclient_path = '/etc/mars_nwe/.nwclient';
|
||||||
|
$smart_static_dir = '/usr/libexec/mars_nwe/static';
|
||||||
|
$smart_log_path = '/var/log/mars_nwe/smart.log';
|
||||||
|
$smart_check_login = '/usr/libexec/mars_nwe/check_login';
|
||||||
|
$smart_admin_group = 'root';
|
||||||
|
|
||||||
|
# Perl frontend logging
|
||||||
|
$smart_debug_level = 'info';
|
||||||
|
|
||||||
|
# Service control
|
||||||
|
$mars_nwe_service = 'mars-nwe-serv.service';
|
||||||
|
$smart_systemctl_path = '/usr/bin/systemctl';
|
||||||
|
|
||||||
|
# CUPS helper integration
|
||||||
|
$smart_cups_enable = '1';
|
||||||
|
$smart_cups_lpstat_path = '/usr/bin/lpstat';
|
||||||
|
$smart_cups_print_command_template = '/usr/bin/lp -d %p -';
|
||||||
|
|
||||||
|
# nwwebui listener settings
|
||||||
|
$nw_bind_ip = '0.0.0.0';
|
||||||
|
$nw_log_level = 'info';
|
||||||
|
$nw_daemonize = 0;
|
||||||
|
$nw_pid_file = '/run/mars_nwe/nwwebui.pid';
|
||||||
|
$nw_log_file = '/var/log/mars_nwe/nwwebui.log';
|
||||||
|
|
||||||
|
$nw_ssl_enable = 0;
|
||||||
|
$nw_http_port = 9080;
|
||||||
|
$nw_https_port = 9443;
|
||||||
|
|
||||||
|
$nw_cert_file = '/etc/mars_nwe/server.crt';
|
||||||
|
$nw_key_file = '/etc/mars_nwe/server.key';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print queue test action
|
||||||
|
|
||||||
|
The print queue page can submit a small test job to an existing MARS_NWE queue
|
||||||
|
through the ncpfs `nprint` tool. This verifies the queue through the NetWare
|
||||||
|
print-queue path instead of only checking the local Unix/CUPS command.
|
||||||
|
|
||||||
|
The helper path can be adjusted in `smart.conf`:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$smart_nprint_path = '/usr/bin/nprint';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recent SMArT WebUI features
|
||||||
|
|
||||||
|
The current WebUI includes several usability and safety improvements:
|
||||||
|
|
||||||
|
- HTML login, session cookies and logout handling
|
||||||
|
- static asset serving before login, so the login page can load the SMArT logo
|
||||||
|
- service-control pages for start, stop, restart and status actions
|
||||||
|
- runtime information on the start page, including generated paths and service name
|
||||||
|
- import helpers for CUPS printers, IPX interfaces, Unix users and local mountpoints
|
||||||
|
- `smart_userlist` helper for reliable Unix-user enumeration without parsing page output
|
||||||
|
- input validation pages for volumes, devices, print queues, users and groups
|
||||||
|
- stricter bindery object-name validation
|
||||||
|
- optional Unix user mapping for bindery users
|
||||||
|
- delete confirmation pages
|
||||||
|
- bindery command and bindery pipe logging
|
||||||
|
- success and error pages for bindery operations
|
||||||
|
- a `UNIX_USER` guard so existing users are not remapped unless requested
|
||||||
|
- configurable Perl log verbosity through `$smart_debug_level`
|
||||||
|
|
||||||
|
## Build and installation notes
|
||||||
|
|
||||||
|
This repository is built as part of the main `mars_nwe` build. The build system:
|
||||||
|
|
||||||
|
- generates `smart.conf` from the template
|
||||||
|
- generates the main `smart` Perl launcher script
|
||||||
|
- builds `nwwebui`
|
||||||
|
- builds `check_login`
|
||||||
|
- builds `smart_userlist`
|
||||||
|
- installs the PAM file and static UI assets
|
||||||
|
- installs helper scripts and optional systemd units
|
||||||
|
|
||||||
|
Useful build-time settings include:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DMARS_NWE_SMART_ADMIN_GROUP=root ...
|
||||||
|
cmake -DMARS_NWE_SMART_ADMIN_GROUP=nwadmin ...
|
||||||
|
cmake -DMARS_NWE_SYSTEMD_SERVICE=mars-nwe-serv.service ...
|
||||||
|
```
|
||||||
|
|
||||||
|
The administrator group defaults to `root` for compatibility. Use a dedicated group such as `nwadmin` when non-root administrators should be allowed to access the WebUI.
|
||||||
|
|
||||||
|
## Starting the service
|
||||||
|
|
||||||
|
### Starting with systemd
|
||||||
|
|
||||||
|
A typical installed system uses the `mars-nwe-webui.service` unit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl enable --now mars-nwe-webui.service
|
||||||
|
systemctl start mars-nwe-webui.service
|
||||||
|
systemctl stop mars-nwe-webui.service
|
||||||
|
systemctl restart mars-nwe-webui.service
|
||||||
|
systemctl status mars-nwe-webui.service
|
||||||
|
```
|
||||||
|
|
||||||
|
The service starts `nwwebui` with the equivalent of:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/usr/sbin/nwwebui -c /etc/mars_nwe/smart.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Starting from the command line
|
||||||
|
|
||||||
|
```text
|
||||||
|
Usage: nwwebui [-h] [-d] [-s] [-c <smart.conf>] [-p <pidfile>] [-l <logfile>]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Show this help text and exit
|
||||||
|
-d, --daemon Run in daemon mode
|
||||||
|
-s, --stop Stop the running nwwebui instance
|
||||||
|
-c, --config <file> Use an alternate smart.conf path
|
||||||
|
-p, --pidfile <file> Override PID file path
|
||||||
|
-l, --logfile <file> Override log file path
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/usr/sbin/nwwebui -c /etc/mars_nwe/smart.conf
|
||||||
|
/usr/sbin/nwwebui -d -c /etc/mars_nwe/smart.conf
|
||||||
|
/usr/sbin/nwwebui -s -c /etc/mars_nwe/smart.conf
|
||||||
|
/usr/sbin/nwwebui -c /etc/mars_nwe/smart.conf -l /var/log/mars_nwe/custom-nwwebui.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical access URLs:
|
||||||
|
|
||||||
|
- `http://<host>:9080/`
|
||||||
|
- `https://<host>:9443/`
|
||||||
|
|
||||||
|
For production use, HTTPS should be preferred.
|
||||||
|
|
||||||
|
## Native helper logging
|
||||||
|
|
||||||
|
The native helper programs `check_login` and `smart_userlist` read their log
|
||||||
|
destination and verbosity from `smart.conf` when called by the WebUI.
|
||||||
|
|
||||||
|
They use the same Perl frontend settings:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$smart_log_path = '/var/log/mars_nwe/smart.log';
|
||||||
|
$smart_debug_level = 'info';
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated `config.h` also provides fallback defaults for these values, so
|
||||||
|
the helpers can still write useful diagnostics when they are executed manually
|
||||||
|
or before `smart.conf` could be loaded.
|
||||||
|
|
||||||
|
`check_login` logs authentication and authorization results, but never logs the
|
||||||
|
submitted password. `smart_userlist` keeps its tab-separated user-list output
|
||||||
|
on stdout unchanged and writes diagnostics only to the configured log file.
|
||||||
|
|
||||||
|
## Unix user discovery helper
|
||||||
|
|
||||||
|
The WebUI user editor can assign a MARS_NWE bindery user to a local Unix user.
|
||||||
|
For this selector, SMArT uses the native `smart_userlist` helper.
|
||||||
|
|
||||||
|
`smart_userlist` enumerates local users through the system user database instead
|
||||||
|
of relying on fragile parsing in the web page itself. This keeps the optional
|
||||||
|
Unix-user mapping UI usable even when the available users come from NSS-backed
|
||||||
|
sources such as local files, LDAP, SSSD or similar site-specific setups.
|
||||||
|
|
||||||
|
The helper is installed together with the other SMArT native tools and is
|
||||||
|
normally referenced from `smart.conf` as:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
$smart_userlist_path = '/usr/libexec/mars_nwe/smart_userlist';
|
||||||
|
```
|
||||||
|
|
||||||
|
If the path is not set explicitly, SMArT falls back to the standard libexec
|
||||||
|
location generated by the build system.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
SMArT is now an integrated part of the `mars_nwe` release. It includes a dedicated `nwwebui` service, form-based sessions, PAM authentication with configurable administrator group restriction, service control, validation pages, import helpers, improved bindery command handling, and configurable logging.
|
||||||
|
|
||||||
BIN
check_login
409
check_login.c
@@ -1,30 +1,266 @@
|
|||||||
/*
|
/*
|
||||||
SMArT
|
SMArT
|
||||||
|
|
||||||
Check username/password combination using PAM
|
Check username/password combination using PAM and require membership in
|
||||||
|
the configured SMArT administrator Unix group.
|
||||||
|
|
||||||
Copyright 2001 Wilmer van der Gaast
|
Usage:
|
||||||
|
check_login <user> <password> <admin-group> [smart.conf]
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
Passwords are never written to the log.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <security/pam_appl.h>
|
#include <security/pam_appl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);
|
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);
|
||||||
|
static int user_in_group(const char *username, const char *groupname);
|
||||||
|
|
||||||
|
|
||||||
|
#define SMART_LOG_ERROR 0
|
||||||
|
#define SMART_LOG_WARNING 1
|
||||||
|
#define SMART_LOG_INFO 2
|
||||||
|
#define SMART_LOG_DEBUG 3
|
||||||
|
#define SMART_LOG_TRACE 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char log_path[512];
|
||||||
|
char debug_level[64];
|
||||||
|
char admin_group[256];
|
||||||
|
int level;
|
||||||
|
} smart_helper_config_t;
|
||||||
|
|
||||||
|
static void trim(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != s) {
|
||||||
|
memmove(s, p, strlen(p) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(s);
|
||||||
|
while (len > 0 && isspace((unsigned char)s[len - 1])) {
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strip_quotes(char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
if (len >= 2) {
|
||||||
|
if ((s[0] == '\'' && s[len - 1] == '\'') ||
|
||||||
|
(s[0] == '"' && s[len - 1] == '"')) {
|
||||||
|
memmove(s, s + 1, len - 2);
|
||||||
|
s[len - 2] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *val, size_t vsz)
|
||||||
|
{
|
||||||
|
const char *p = line;
|
||||||
|
size_t ki = 0;
|
||||||
|
size_t vi = 0;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p != '$') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
|
||||||
|
if (ki + 1 < ksz) {
|
||||||
|
key[ki++] = *p;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
key[ki] = '\0';
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p != '=') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
|
||||||
|
if (vi + 1 < vsz) {
|
||||||
|
val[vi++] = *p;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
val[vi] = '\0';
|
||||||
|
|
||||||
|
trim(key);
|
||||||
|
trim(val);
|
||||||
|
strip_quotes(val);
|
||||||
|
|
||||||
|
return key[0] != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_log_level(const char *value)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s", value);
|
||||||
|
trim(buf);
|
||||||
|
|
||||||
|
for (i = 0; buf[i]; i++) {
|
||||||
|
buf[i] = (char)tolower((unsigned char)buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(buf, "error") == 0 || strcmp(buf, "err") == 0 || strcmp(buf, "0") == 0) {
|
||||||
|
return SMART_LOG_ERROR;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "warning") == 0 || strcmp(buf, "warn") == 0 || strcmp(buf, "1") == 0) {
|
||||||
|
return SMART_LOG_WARNING;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "info") == 0 || strcmp(buf, "2") == 0) {
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "debug") == 0 || strcmp(buf, "3") == 0) {
|
||||||
|
return SMART_LOG_DEBUG;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "trace") == 0 || strcmp(buf, "4") == 0) {
|
||||||
|
return SMART_LOG_TRACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *level_name(int level)
|
||||||
|
{
|
||||||
|
if (level <= SMART_LOG_ERROR) {
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
if (level == SMART_LOG_WARNING) {
|
||||||
|
return "WARNING";
|
||||||
|
}
|
||||||
|
if (level == SMART_LOG_DEBUG) {
|
||||||
|
return "DEBUG";
|
||||||
|
}
|
||||||
|
if (level >= SMART_LOG_TRACE) {
|
||||||
|
return "TRACE";
|
||||||
|
}
|
||||||
|
return "INFO";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smart_cfg_init(smart_helper_config_t *cfg)
|
||||||
|
{
|
||||||
|
memset(cfg, 0, sizeof(*cfg));
|
||||||
|
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", DEFAULT_SMART_LOG_PATH);
|
||||||
|
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", DEFAULT_SMART_LOG_LEVEL);
|
||||||
|
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", "root");
|
||||||
|
cfg->level = parse_log_level(cfg->debug_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smart_cfg_load(smart_helper_config_t *cfg, const char *path)
|
||||||
|
{
|
||||||
|
FILE *fh;
|
||||||
|
char line[2048];
|
||||||
|
|
||||||
|
if (path == NULL || path[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = fopen(path, "r");
|
||||||
|
if (fh == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fh) != NULL) {
|
||||||
|
char key[256];
|
||||||
|
char val[1024];
|
||||||
|
|
||||||
|
if (!parse_perl_assignment(line, key, sizeof(key), val, sizeof(val))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, "smart_log_path") == 0) {
|
||||||
|
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", val);
|
||||||
|
} else if (strcmp(key, "smart_debug_level") == 0 ||
|
||||||
|
strcmp(key, "smart_log_level") == 0) {
|
||||||
|
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", val);
|
||||||
|
cfg->level = parse_log_level(val);
|
||||||
|
} else if (strcmp(key, "smart_admin_group") == 0) {
|
||||||
|
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void helper_log(smart_helper_config_t *cfg, const char *component, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
FILE *fh = stderr;
|
||||||
|
int close_fh = 0;
|
||||||
|
time_t now;
|
||||||
|
struct tm tm_now;
|
||||||
|
char tbuf[64];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (cfg != NULL && level > cfg->level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg != NULL && cfg->log_path[0] != '\0') {
|
||||||
|
fh = fopen(cfg->log_path, "a");
|
||||||
|
if (fh != NULL) {
|
||||||
|
close_fh = 1;
|
||||||
|
} else {
|
||||||
|
fh = stderr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
localtime_r(&now, &tm_now);
|
||||||
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_now);
|
||||||
|
|
||||||
|
fprintf(fh, "[%s] [%s] [SMArT helper] [%s] ", tbuf, level_name(level), component);
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(fh, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
fputc('\n', fh);
|
||||||
|
fflush(fh);
|
||||||
|
|
||||||
|
if (close_fh) {
|
||||||
|
fclose(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct pam_conv conv = {
|
static struct pam_conv conv = {
|
||||||
my_conv,
|
my_conv,
|
||||||
@@ -36,21 +272,137 @@ char *pass;
|
|||||||
|
|
||||||
int main( int argc, char **argv )
|
int main( int argc, char **argv )
|
||||||
{
|
{
|
||||||
pam_handle_t *pamh;
|
pam_handle_t *pamh = NULL;
|
||||||
int retval, st = 1;
|
int retval, st = 1;
|
||||||
|
const char *admin_group;
|
||||||
|
const char *smart_conf = DEFAULT_SMART_CONF;
|
||||||
|
smart_helper_config_t cfg;
|
||||||
|
|
||||||
|
smart_cfg_init(&cfg);
|
||||||
|
|
||||||
|
if( argc < 4 )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "Usage: %s <user> <password> <admin-group> [smart.conf]\n", argv[0] );
|
||||||
|
return( 3 );
|
||||||
|
}
|
||||||
|
|
||||||
user = argv[1];
|
user = argv[1];
|
||||||
pass = argv[2];
|
pass = argv[2];
|
||||||
|
admin_group = argv[3];
|
||||||
|
|
||||||
|
if (argc >= 5 && argv[4] != NULL && argv[4][0] != '\0') {
|
||||||
|
smart_conf = argv[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
smart_cfg_load(&cfg, smart_conf);
|
||||||
|
|
||||||
|
if (admin_group == NULL || admin_group[0] == '\0' || strcmp(admin_group, "-") == 0) {
|
||||||
|
admin_group = cfg.admin_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( user == NULL || user[0] == '\0' ||
|
||||||
|
pass == NULL ||
|
||||||
|
admin_group == NULL || admin_group[0] == '\0' )
|
||||||
|
{
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_ERROR, "invalid helper arguments");
|
||||||
|
return( 3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_INFO, "authentication requested user='%s' admin_group='%s'", user, admin_group);
|
||||||
|
|
||||||
retval = pam_start( "smart", user, &conv, &pamh );
|
retval = pam_start( "smart", user, &conv, &pamh );
|
||||||
if ( retval == PAM_SUCCESS )
|
if( retval == PAM_SUCCESS )
|
||||||
retval = pam_authenticate( pamh, PAM_SILENT );
|
retval = pam_authenticate( pamh, PAM_SILENT );
|
||||||
if ( retval == PAM_SUCCESS )
|
if( retval == PAM_SUCCESS )
|
||||||
st = retval = pam_acct_mgmt( pamh, PAM_SILENT );
|
st = retval = pam_acct_mgmt( pamh, PAM_SILENT );
|
||||||
if ( pam_end( pamh, retval ) != PAM_SUCCESS )
|
|
||||||
return( 1 );
|
|
||||||
|
|
||||||
return( st != PAM_SUCCESS );
|
if( pamh != NULL && pam_end( pamh, retval ) != PAM_SUCCESS ) {
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_ERROR, "pam_end failed user='%s'", user);
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( st != PAM_SUCCESS ) {
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_WARNING, "pam authentication failed user='%s' pam_status=%d", user, st);
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_DEBUG, "pam authentication ok user='%s'", user);
|
||||||
|
|
||||||
|
if( ! user_in_group( user, admin_group ) ) {
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_WARNING, "group authorization failed user='%s' required_group='%s'", user, admin_group);
|
||||||
|
return( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_log(&cfg, "check_login", SMART_LOG_INFO, "login accepted user='%s' required_group='%s'", user, admin_group);
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int user_in_group(const char *username, const char *groupname)
|
||||||
|
{
|
||||||
|
struct passwd *pw;
|
||||||
|
struct group *gr;
|
||||||
|
int ngroups = 0;
|
||||||
|
gid_t *groups;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if( username == NULL || username[0] == '\0' ||
|
||||||
|
groupname == NULL || groupname[0] == '\0' )
|
||||||
|
{
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
pw = getpwnam( username );
|
||||||
|
gr = getgrnam( groupname );
|
||||||
|
|
||||||
|
if( pw == NULL || gr == NULL )
|
||||||
|
{
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pw->pw_gid == gr->gr_gid )
|
||||||
|
{
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__GLIBC__)
|
||||||
|
getgrouplist( username, pw->pw_gid, NULL, &ngroups );
|
||||||
|
|
||||||
|
if( ngroups > 0 )
|
||||||
|
{
|
||||||
|
groups = calloc( (size_t) ngroups, sizeof( gid_t ) );
|
||||||
|
|
||||||
|
if( groups != NULL )
|
||||||
|
{
|
||||||
|
if( getgrouplist( username, pw->pw_gid, groups, &ngroups ) >= 0 )
|
||||||
|
{
|
||||||
|
for( i = 0; i < ngroups; i++ )
|
||||||
|
{
|
||||||
|
if( groups[i] == gr->gr_gid )
|
||||||
|
{
|
||||||
|
free( groups );
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free( groups );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if( gr->gr_mem != NULL )
|
||||||
|
{
|
||||||
|
for( i = 0; gr->gr_mem[i] != NULL; i++ )
|
||||||
|
{
|
||||||
|
if( strcmp( gr->gr_mem[i], username ) == 0 )
|
||||||
|
{
|
||||||
|
return( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
|
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
|
||||||
@@ -58,12 +410,21 @@ int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **r
|
|||||||
struct pam_response *reply;
|
struct pam_response *reply;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
reply = (struct pam_response *) calloc( num_msg, sizeof( struct pam_response ) );
|
(void) msg;
|
||||||
|
(void) appdata_ptr;
|
||||||
|
|
||||||
|
reply = (struct pam_response *) calloc( (size_t) num_msg, sizeof( struct pam_response ) );
|
||||||
|
if( reply == NULL )
|
||||||
|
{
|
||||||
|
return( PAM_BUF_ERR );
|
||||||
|
}
|
||||||
|
|
||||||
for( i = 0; i < num_msg; i ++ )
|
for( i = 0; i < num_msg; i ++ )
|
||||||
{
|
{
|
||||||
reply[i].resp = (char *) strdup( pass ); /* Just give the password... It's all we know */
|
reply[i].resp = (char *) strdup( pass );
|
||||||
reply[i].resp_retcode = 0;
|
reply[i].resp_retcode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*resp = reply;
|
*resp = reply;
|
||||||
|
|
||||||
return( PAM_SUCCESS );
|
return( PAM_SUCCESS );
|
||||||
|
|||||||
39
config.h.cmake
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef NWWEBUI_CONFIG_H
|
||||||
|
#define NWWEBUI_CONFIG_H
|
||||||
|
|
||||||
|
#define NWWEBUI_NAME "nwwebui"
|
||||||
|
#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_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"
|
||||||
|
|
||||||
|
#define DEFAULT_SMART_LOG_PATH "@MARS_NWE_LOG_DIR@/smart.log"
|
||||||
|
#define DEFAULT_SMART_LOG_LEVEL "info"
|
||||||
|
|
||||||
|
#define LOG_LEVEL_ERROR 0
|
||||||
|
#define LOG_LEVEL_WARNING 1
|
||||||
|
#define LOG_LEVEL_INFO 2
|
||||||
|
#define LOG_LEVEL_DEBUG 3
|
||||||
|
#define LOG_LEVEL_TRACE 4
|
||||||
|
#define LOG_LEVEL_DEFAULT LOG_LEVEL_INFO
|
||||||
|
|
||||||
|
#define DEFAULT_BIND_IP "0.0.0.0"
|
||||||
|
#define DEFAULT_SSL_ENABLE 1
|
||||||
|
#define DEFAULT_HTTP_PORT 9080
|
||||||
|
#define DEFAULT_HTTPS_PORT 9443
|
||||||
|
|
||||||
|
#define DEFAULT_CERT_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt"
|
||||||
|
#define DEFAULT_KEY_FILE "@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key"
|
||||||
|
|
||||||
|
#define DEFAULT_PID_FILE "@MARS_NWE_PID_DIR@/nwwebui.pid"
|
||||||
|
#define DEFAULT_DAEMONIZE 0
|
||||||
|
|
||||||
|
#define NW_BACKLOG 64
|
||||||
|
#define NW_BUF_SZ 16384
|
||||||
|
|
||||||
|
#endif
|
||||||
297
control.cmake
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
#!/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;
|
||||||
|
$s =~ s/"/"/g;
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_systemctl_command
|
||||||
|
{
|
||||||
|
my ($systemctl, @args) = @_;
|
||||||
|
my $output = '';
|
||||||
|
my ($reader, $writer);
|
||||||
|
|
||||||
|
if( ! pipe( $reader, $writer ) )
|
||||||
|
{
|
||||||
|
return (1, 'Could not create pipe: ' . $! . "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
my $pid = fork();
|
||||||
|
if( ! defined( $pid ) )
|
||||||
|
{
|
||||||
|
close( $reader );
|
||||||
|
close( $writer );
|
||||||
|
return (1, 'Could not fork: ' . $! . "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $pid == 0 )
|
||||||
|
{
|
||||||
|
close( $reader );
|
||||||
|
|
||||||
|
# Capture stdout for the web UI. Leave stderr untouched so systemctl
|
||||||
|
# warnings/errors still go to smart.log, matching the old behaviour.
|
||||||
|
open( STDOUT, '>&', $writer ) or exit 127;
|
||||||
|
close( $writer );
|
||||||
|
|
||||||
|
exec( $systemctl, @args ) or do {
|
||||||
|
print 'Could not execute ' . $systemctl . ': ' . $! . "\n";
|
||||||
|
exit 127;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
close( $writer );
|
||||||
|
while( my $line = <$reader> )
|
||||||
|
{
|
||||||
|
$output .= $line;
|
||||||
|
}
|
||||||
|
close( $reader );
|
||||||
|
|
||||||
|
waitpid( $pid, 0 );
|
||||||
|
my $rc = $? >> 8;
|
||||||
|
|
||||||
|
return ( $rc, $output );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub wait_for_service_state
|
||||||
|
{
|
||||||
|
my ($systemctl, $service, $wanted, $timeout) = @_;
|
||||||
|
my $elapsed = 0;
|
||||||
|
my $last_state = '';
|
||||||
|
my $ok = 0;
|
||||||
|
|
||||||
|
$timeout = 45 unless defined($timeout) && $timeout > 0;
|
||||||
|
|
||||||
|
print '# waiting for ' . html_escape($service) . ' to become ' . html_escape($wanted) . "\n";
|
||||||
|
|
||||||
|
while( $elapsed <= $timeout )
|
||||||
|
{
|
||||||
|
my ($state_rc, $state_output) = run_systemctl_command( $systemctl, 'is-active', $service );
|
||||||
|
my $state = $state_output;
|
||||||
|
|
||||||
|
$state =~ s/[\r\n]+$//;
|
||||||
|
$state =~ s/^\s+//;
|
||||||
|
$state =~ s/\s+$//;
|
||||||
|
$state = 'unknown' if $state eq '';
|
||||||
|
|
||||||
|
if( $state ne $last_state )
|
||||||
|
{
|
||||||
|
print sprintf( "[%2ds] state: %s\n", $elapsed, html_escape($state) );
|
||||||
|
$last_state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $wanted eq 'inactive' )
|
||||||
|
{
|
||||||
|
if( $state eq 'inactive' || $state eq 'failed' )
|
||||||
|
{
|
||||||
|
$ok = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif( $wanted eq 'active' )
|
||||||
|
{
|
||||||
|
if( $state eq 'active' )
|
||||||
|
{
|
||||||
|
$ok = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $state eq 'failed' )
|
||||||
|
{
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep( 1 );
|
||||||
|
$elapsed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $ok )
|
||||||
|
{
|
||||||
|
print "wait result: reached $wanted\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "wait result: timeout after $timeout seconds\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub append_command_output
|
||||||
|
{
|
||||||
|
my ($title, $rc, $output) = @_;
|
||||||
|
|
||||||
|
print '# ' . html_escape($title) . "\n";
|
||||||
|
if( defined($output) && $output ne '' )
|
||||||
|
{
|
||||||
|
print html_escape($output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print "(no output)\n";
|
||||||
|
}
|
||||||
|
print "exit code: $rc\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_request
|
||||||
|
{
|
||||||
|
local $| = 1;
|
||||||
|
|
||||||
|
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 <<HTML_HEAD;
|
||||||
|
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}
|
||||||
|
.waitbox{display:flex;align-items:center;gap:14px;margin:14px 0;padding:14px 16px;border:1px solid #ecdcc8;border-radius:14px;background:#fbf6ef;color:#6f5b4f}
|
||||||
|
.spinner{width:30px;height:30px;border:4px solid #e5d6c6;border-top-color:#9f1f1f;border-radius:50%;animation:spin .9s linear infinite;flex:0 0 auto}
|
||||||
|
\@keyframes spin{to{transform:rotate(360deg)}}
|
||||||
|
.waittitle{font-weight:bold;color:#9f1f1f}.waitsub{font-size:13px;margin-top:3px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="box">
|
||||||
|
<h1>MARS_NWE service control</h1>
|
||||||
|
HTML_HEAD
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
if( $action ne 'status' )
|
||||||
|
{
|
||||||
|
my $wait_text = 'Waiting for service state change';
|
||||||
|
if( $action eq 'stop' )
|
||||||
|
{
|
||||||
|
$wait_text = 'Stopping service, waiting until it is inactive';
|
||||||
|
}
|
||||||
|
elsif( $action eq 'start' )
|
||||||
|
{
|
||||||
|
$wait_text = 'Starting service, waiting until it is active';
|
||||||
|
}
|
||||||
|
elsif( $action eq 'restart' )
|
||||||
|
{
|
||||||
|
$wait_text = 'Restarting service, waiting until it is active';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '<div id="waitbox" class="waitbox">' . "\n";
|
||||||
|
print '<div class="spinner" aria-hidden="true"></div>' . "\n";
|
||||||
|
print '<div><div class="waittitle">' . html_escape($wait_text) . '</div>' . "\n";
|
||||||
|
print '<div class="waitsub">This can take up to 45 seconds. Please wait until the final status appears below.</div></div>' . "\n";
|
||||||
|
print '</div>' . "\n";
|
||||||
|
print " " x 4096; # help browsers render the waiting box before the command finishes
|
||||||
|
}
|
||||||
|
|
||||||
|
print "<pre>\n";
|
||||||
|
|
||||||
|
my $rc = 0;
|
||||||
|
my $output = '';
|
||||||
|
|
||||||
|
if( $action eq 'status' )
|
||||||
|
{
|
||||||
|
( $rc, $output ) = run_systemctl_command( $systemctl, '--no-pager', '--full', 'status', $service );
|
||||||
|
append_command_output( "systemctl --no-pager --full status $service", $rc, $output );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Avoid repeated "unit file changed on disk" warnings after install/update.
|
||||||
|
my ( $reload_rc, $reload_output ) = run_systemctl_command( $systemctl, 'daemon-reload' );
|
||||||
|
append_command_output( 'systemctl daemon-reload', $reload_rc, $reload_output );
|
||||||
|
print "\n";
|
||||||
|
|
||||||
|
( $rc, $output ) = run_systemctl_command( $systemctl, $action, $service );
|
||||||
|
append_command_output( "systemctl $action $service", $rc, $output );
|
||||||
|
print "\n";
|
||||||
|
|
||||||
|
if( $rc == 0 )
|
||||||
|
{
|
||||||
|
my $wanted_state = ( $action eq 'stop' ) ? 'inactive' : 'active';
|
||||||
|
my $wait_rc = wait_for_service_state( $systemctl, $service, $wanted_state, 45 );
|
||||||
|
print "\n";
|
||||||
|
$rc = $wait_rc if $wait_rc != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my ( $status_rc, $status_output ) = run_systemctl_command( $systemctl, '--no-pager', '--full', 'status', $service );
|
||||||
|
append_command_output( "systemctl --no-pager --full status $service", $status_rc, $status_output );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $rc == 0 )
|
||||||
|
{
|
||||||
|
print "</pre><script>var w=document.getElementById('waitbox');if(w){w.style.display='none';}</script><p class=\"ok\">Command completed successfully.</p>\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print "</pre><script>var w=document.getElementById('waitbox');if(w){w.style.display='none';}</script><p class=\"bad\">Command failed with exit code $rc.</p>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<HTML_FOOT;
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/service/control?status" class="secondary">Status</a>
|
||||||
|
<a href="/service/control?start">Start</a>
|
||||||
|
<a href="/service/control?stop" class="secondary">Stop</a>
|
||||||
|
<a href="/service/control?restart">Restart</a>
|
||||||
|
</div>
|
||||||
|
<p><a href="/static/start.html">Back</a></p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML_FOOT
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
BIN
doc/screenshots/advanced-path-settings.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
doc/screenshots/general-settings.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
doc/screenshots/login.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/screenshots/main-menu.png
Normal file
|
After Width: | Height: | Size: 398 KiB |
BIN
doc/screenshots/print-queue-cups.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
doc/screenshots/screenshot-contact-sheet.png
Normal file
|
After Width: | Height: | Size: 467 KiB |
BIN
doc/screenshots/service-control.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
doc/screenshots/service-runtime.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
doc/screenshots/smart-settings.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
doc/screenshots/thumbs/advanced-path-settings.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
doc/screenshots/thumbs/general-settings.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/screenshots/thumbs/login.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
doc/screenshots/thumbs/main-menu.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
doc/screenshots/thumbs/print-queue-cups.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/screenshots/thumbs/service-control.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
doc/screenshots/thumbs/service-runtime.png
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
doc/screenshots/thumbs/smart-settings.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
doc/screenshots/thumbs/user-editor.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
doc/screenshots/thumbs/validation-error.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
doc/screenshots/thumbs/volumes-import.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
doc/screenshots/user-editor.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/screenshots/validation-error.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
doc/screenshots/volumes-import.png
Normal file
|
After Width: | Height: | Size: 1005 KiB |
28
mars-nwe-webui.service.cmake
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=MARS NWE Web UI
|
||||||
|
After=network.target mars-nwe-serv.service
|
||||||
|
Wants=network.target
|
||||||
|
Documentation=man:systemd.service(5)
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
RuntimeDirectory=mars-nwe-webui
|
||||||
|
RuntimeDirectoryMode=0700
|
||||||
|
WorkingDirectory=/
|
||||||
|
|
||||||
|
ExecStartPre=/bin/mkdir -p @MARS_NWE_LOG_DIR@
|
||||||
|
ExecStartPre=/bin/mkdir -p @MARS_NWE_PID_DIR@
|
||||||
|
ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/nwwebui -c @MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=full
|
||||||
|
ReadWritePaths=@MARS_NWE_INSTALL_FULL_CONFDIR@
|
||||||
|
ProtectHome=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
BIN
opt/Lable.png
Normal file
|
After Width: | Height: | Size: 573 KiB |
BIN
opt/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
opt/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 393 KiB |
BIN
opt/logo-small.png
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
opt/logo.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
406
readconfig.pl
@@ -1,28 +1,12 @@
|
|||||||
#
|
|
||||||
# SMArT
|
# SMArT
|
||||||
#
|
#
|
||||||
# Configuration file code
|
# Configuration file code
|
||||||
#
|
#
|
||||||
# Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx)
|
# Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx)
|
||||||
#
|
#
|
||||||
#
|
# Updated for marker-aware config writing.
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
my( @info, @conf, $l );
|
my( @info, @conf, @rawconf, $l );
|
||||||
|
|
||||||
$info[1] = 'Volume';
|
$info[1] = 'Volume';
|
||||||
$info[2] = 'Server name';
|
$info[2] = 'Server name';
|
||||||
@@ -53,6 +37,7 @@ $info[47] = 'Trustee files';
|
|||||||
$info[50] = 'Conversion tables';
|
$info[50] = 'Conversion tables';
|
||||||
$info[60] = 'MAX_CONNECTIONS';
|
$info[60] = 'MAX_CONNECTIONS';
|
||||||
$info[61] = 'MAX_NW_VOLS';
|
$info[61] = 'MAX_NW_VOLS';
|
||||||
|
$info[62] = 'Reserved';
|
||||||
$info[63] = 'MAX_DIR_BASE_ENTRIES';
|
$info[63] = 'MAX_DIR_BASE_ENTRIES';
|
||||||
$info[68] = 'USE_MMAP';
|
$info[68] = 'USE_MMAP';
|
||||||
$info[69] = 'HANDLE_ALL_SAP_TYPS';
|
$info[69] = 'HANDLE_ALL_SAP_TYPS';
|
||||||
@@ -79,19 +64,25 @@ $info[400] = 'nwserv.stations file';
|
|||||||
$info[401] = 'Reply to nearest server requests';
|
$info[401] = 'Reply to nearest server requests';
|
||||||
$info[402] = 'Reply to connect requests';
|
$info[402] = 'Reply to connect requests';
|
||||||
|
|
||||||
open( CONF, '<' . $mars_config );
|
open( CONF, '<' . $mars_config ) or die "Could not open $mars_config: $!";
|
||||||
@conf = ();
|
@conf = ();
|
||||||
|
@rawconf = ();
|
||||||
|
|
||||||
while( $l = <CONF> )
|
while( $l = <CONF> )
|
||||||
{
|
{
|
||||||
$l =~ s/[\r\n]//g;
|
push( @rawconf, $l );
|
||||||
$l =~ s/[\t ]+/ /g;
|
|
||||||
$l =~ s/#.*//;
|
my $x = $l;
|
||||||
$l =~ s/^[\t ]*//;
|
$x =~ s/[\r\n]//g;
|
||||||
$l =~ s/[\t ]*$//;
|
$x =~ s/[\t ]+/ /g;
|
||||||
if( $l ne '' )
|
$x =~ s/#.*//;
|
||||||
{
|
$x =~ s/^[\t ]*//;
|
||||||
unshift( @conf, $l );
|
$x =~ s/[\t ]*$//;
|
||||||
}
|
|
||||||
|
if( $x ne '' )
|
||||||
|
{
|
||||||
|
push( @conf, $x );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
close( CONF );
|
close( CONF );
|
||||||
|
|
||||||
@@ -99,53 +90,358 @@ sortconfig();
|
|||||||
|
|
||||||
sub sortconfig()
|
sub sortconfig()
|
||||||
{
|
{
|
||||||
@conf = sort( { $a cmp $b } @conf );
|
@conf = sort( { $a cmp $b } @conf );
|
||||||
@conf = sort( { ( split( ' ', $a ) )[0] <=> ( split( ' ', $b ) )[0] } @conf );
|
@conf = sort( { ( split( ' ', $a ) )[0] <=> ( split( ' ', $b ) )[0] } @conf );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getconfigline( $ )
|
sub getconfigline( $ )
|
||||||
{
|
{
|
||||||
my( @c, $c );
|
my( @c, $c );
|
||||||
|
|
||||||
@c = getconfig( @_ );
|
@c = getconfig( @_ );
|
||||||
$c = $c[0];
|
$c = $c[0];
|
||||||
$c =~ s/^[0-9]* //;
|
$c =~ s/^[0-9]* //;
|
||||||
return( $c );
|
return( $c );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getconfig( $ )
|
sub getconfig( $ )
|
||||||
{
|
{
|
||||||
my( @c );
|
my( @c );
|
||||||
|
|
||||||
@c = grep( /^$_[0] /i, @conf );
|
@c = grep( /^$_[0] /i, @conf );
|
||||||
return( @c );
|
return( @c );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addconfigline( $ )
|
sub addconfigline( $ )
|
||||||
{
|
{
|
||||||
unshift( @conf, $_[0] );
|
unshift( @conf, $_[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub delconfigline( $ )
|
sub delconfigline( $ )
|
||||||
{
|
{
|
||||||
@conf = grep( !/^$_[0] /i, grep( !/^$_[0]$/i, @conf ) );
|
@conf = grep( !/^$_[0] /i, grep( !/^$_[0]$/i, @conf ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub queue_config_name_from_line( $ )
|
||||||
|
{
|
||||||
|
my $line = $_[0];
|
||||||
|
|
||||||
|
$line = '' unless defined( $line );
|
||||||
|
$line =~ s/[\r\n]//g;
|
||||||
|
$line =~ s/#.*//;
|
||||||
|
$line =~ s/^\s*21\s+//i;
|
||||||
|
$line =~ s/^\s+//;
|
||||||
|
$line =~ s/\s+$//;
|
||||||
|
|
||||||
|
return ( split( /\s+/, $line, 2 ) )[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub delconfigqueue( $ )
|
||||||
|
{
|
||||||
|
my $name = $_[0];
|
||||||
|
my $conf_before = scalar( @conf );
|
||||||
|
my $raw_before = scalar( @rawconf );
|
||||||
|
|
||||||
|
$name = '' unless defined( $name );
|
||||||
|
$name =~ s/^\s+//;
|
||||||
|
$name =~ s/\s+$//;
|
||||||
|
$name = uc( $name );
|
||||||
|
|
||||||
|
return( 0, 0 ) if $name eq '';
|
||||||
|
|
||||||
|
@conf = grep {
|
||||||
|
my $qname = queue_config_name_from_line( $_ );
|
||||||
|
!( $_ =~ /^\s*21(?:\s|$)/i && uc( $qname ) eq $name )
|
||||||
|
} @conf;
|
||||||
|
|
||||||
|
@rawconf = grep {
|
||||||
|
my $raw = $_;
|
||||||
|
my $qname = queue_config_name_from_line( $raw );
|
||||||
|
!( $raw =~ /^\s*21(?:\s|$)/i && uc( $qname ) eq $name )
|
||||||
|
} @rawconf;
|
||||||
|
|
||||||
|
return( $conf_before - scalar( @conf ), $raw_before - scalar( @rawconf ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub normalize_line( $ )
|
||||||
|
{
|
||||||
|
my $x = $_[0];
|
||||||
|
|
||||||
|
$x =~ s/[\r\n]//g;
|
||||||
|
$x =~ s/[\t ]+/ /g;
|
||||||
|
$x =~ s/#.*//;
|
||||||
|
$x =~ s/^[\t ]*//;
|
||||||
|
$x =~ s/[\t ]*$//;
|
||||||
|
|
||||||
|
return( $x );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub section_of_line( $ )
|
||||||
|
{
|
||||||
|
my $x = normalize_line( $_[0] );
|
||||||
|
|
||||||
|
if( $x =~ /^([0-9]+)\b/ )
|
||||||
|
{
|
||||||
|
return( $1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( '' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub grouped_section_key( $ )
|
||||||
|
{
|
||||||
|
my $sec = $_[0];
|
||||||
|
|
||||||
|
if( $sec >= 100 && $sec <= 106 ) { return '100-106'; }
|
||||||
|
if( $sec >= 200 && $sec <= 202 ) { return '200-202'; }
|
||||||
|
if( $sec >= 210 && $sec <= 211 ) { return '210-211'; }
|
||||||
|
if( $sec >= 300 && $sec <= 302 ) { return '300-302'; }
|
||||||
|
|
||||||
|
return $sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub build_marker_map()
|
||||||
|
{
|
||||||
|
my( %map, $line, $sec, $key );
|
||||||
|
|
||||||
|
foreach $line ( @conf )
|
||||||
|
{
|
||||||
|
$sec = section_of_line( $line );
|
||||||
|
next if $sec eq '';
|
||||||
|
|
||||||
|
$key = grouped_section_key( $sec );
|
||||||
|
push( @{ $map{$key} }, $line );
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep SYS as first entry in section 1
|
||||||
|
if( defined( $map{'1'} ) )
|
||||||
|
{
|
||||||
|
my @sys = grep( /^1 SYS /, @{ $map{'1'} } );
|
||||||
|
my @rest = grep( !/^1 SYS /, @{ $map{'1'} } );
|
||||||
|
$map{'1'} = [ @sys, @rest ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return %map;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub writeconfig_compact()
|
||||||
|
{
|
||||||
|
my( $i, $l );
|
||||||
|
|
||||||
|
sortconfig();
|
||||||
|
|
||||||
|
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||||
|
|
||||||
|
$l = ( getconfig( '1 SYS' ) )[0];
|
||||||
|
delconfigline( '1 SYS' );
|
||||||
|
addconfigline( $l );
|
||||||
|
|
||||||
|
foreach $i ( @conf )
|
||||||
|
{
|
||||||
|
$l = $i;
|
||||||
|
$l =~ s/ .*//;
|
||||||
|
printf( CONF "%-50s # %s\n", $i, $info[$l] );
|
||||||
|
}
|
||||||
|
|
||||||
|
close( CONF );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub writeconfig_markers()
|
||||||
|
{
|
||||||
|
my( %secmap, %emitted, %managed );
|
||||||
|
my( $line, $active_key, $inside_active, $sec, $key );
|
||||||
|
|
||||||
|
%secmap = build_marker_map();
|
||||||
|
$inside_active = '';
|
||||||
|
|
||||||
|
foreach $key ( keys( %secmap ) )
|
||||||
|
{
|
||||||
|
$managed{$key} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||||
|
|
||||||
|
foreach $line ( @rawconf )
|
||||||
|
{
|
||||||
|
if( $line =~ /^\s*#\s*>>>\s*SMARTHOOK\s+SECTION\s+([0-9]+(?:-[0-9]+)?)\s+ACTIVE\s+BEGIN/i )
|
||||||
|
{
|
||||||
|
$active_key = $1;
|
||||||
|
$inside_active = $active_key;
|
||||||
|
|
||||||
|
print CONF $line;
|
||||||
|
|
||||||
|
if( defined( $secmap{$active_key} ) )
|
||||||
|
{
|
||||||
|
foreach my $entry ( @{ $secmap{$active_key} } )
|
||||||
|
{
|
||||||
|
print CONF $entry . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$emitted{$active_key} = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $line =~ /^\s*#\s*<<<\s*SMARTHOOK\s+SECTION\s+([0-9]+(?:-[0-9]+)?)\s+ACTIVE\s+END/i )
|
||||||
|
{
|
||||||
|
$inside_active = '';
|
||||||
|
print CONF $line;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $inside_active ne '' )
|
||||||
|
{
|
||||||
|
# Skip old content inside ACTIVE blocks completely.
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sec = section_of_line( $line );
|
||||||
|
if( $sec ne '' )
|
||||||
|
{
|
||||||
|
$key = grouped_section_key( $sec );
|
||||||
|
|
||||||
|
if( $managed{$key} )
|
||||||
|
{
|
||||||
|
# This section was changed in @conf. Do not write the stale
|
||||||
|
# raw line again from @rawconf. If the section has no ACTIVE
|
||||||
|
# marker, it is emitted once after the raw file has been copied.
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print CONF $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $key ( sort {
|
||||||
|
my $aa = $a; my $bb = $b;
|
||||||
|
$aa =~ s/-.*//; $bb =~ s/-.*//;
|
||||||
|
$aa <=> $bb
|
||||||
|
} keys( %secmap ) )
|
||||||
|
{
|
||||||
|
next if $emitted{$key};
|
||||||
|
|
||||||
|
print CONF "\n";
|
||||||
|
foreach my $entry ( @{ $secmap{$key} } )
|
||||||
|
{
|
||||||
|
print CONF $entry . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$emitted{$key} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close( CONF );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub writeconfig_preserve_layout()
|
||||||
|
{
|
||||||
|
my( %secmap, %written, @sections, $sec, $line, $sysline );
|
||||||
|
my( $heading_sec );
|
||||||
|
|
||||||
|
sortconfig();
|
||||||
|
|
||||||
|
$sysline = ( grep( /^1 SYS /, @conf ) )[0];
|
||||||
|
if( defined( $sysline ) )
|
||||||
|
{
|
||||||
|
@conf = grep( $_ ne $sysline, @conf );
|
||||||
|
unshift( @conf, $sysline );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach $line ( @conf )
|
||||||
|
{
|
||||||
|
$sec = section_of_line( $line );
|
||||||
|
if( $sec ne '' )
|
||||||
|
{
|
||||||
|
push( @{ $secmap{$sec} }, $line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open( CONF, '>' . $mars_config ) or die "Could not write $mars_config: $!";
|
||||||
|
|
||||||
|
foreach $line ( @rawconf )
|
||||||
|
{
|
||||||
|
if( $line =~ /^\s*#.*Section\s+([0-9]+)\b/i )
|
||||||
|
{
|
||||||
|
$heading_sec = $1;
|
||||||
|
|
||||||
|
foreach $sec ( sort { $a <=> $b } keys( %secmap ) )
|
||||||
|
{
|
||||||
|
next if $written{$sec};
|
||||||
|
next if $sec > $heading_sec;
|
||||||
|
|
||||||
|
if( defined( $secmap{$sec} ) )
|
||||||
|
{
|
||||||
|
foreach my $entry ( @{ $secmap{$sec} } )
|
||||||
|
{
|
||||||
|
print CONF $entry . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$written{$sec} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sec = section_of_line( $line );
|
||||||
|
|
||||||
|
if( $sec eq '' )
|
||||||
|
{
|
||||||
|
print CONF $line;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! $written{$sec} )
|
||||||
|
{
|
||||||
|
if( defined( $secmap{$sec} ) )
|
||||||
|
{
|
||||||
|
foreach my $entry ( @{ $secmap{$sec} } )
|
||||||
|
{
|
||||||
|
print CONF $entry . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$written{$sec} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@sections = sort { $a <=> $b } keys( %secmap );
|
||||||
|
foreach $sec ( @sections )
|
||||||
|
{
|
||||||
|
next if $written{$sec};
|
||||||
|
|
||||||
|
print CONF "\n";
|
||||||
|
foreach my $entry ( @{ $secmap{$sec} } )
|
||||||
|
{
|
||||||
|
print CONF $entry . "\n";
|
||||||
|
}
|
||||||
|
$written{$sec} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close( CONF );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub config_has_smart_markers()
|
||||||
|
{
|
||||||
|
foreach my $line ( @rawconf )
|
||||||
|
{
|
||||||
|
if( $line =~ /SMARTHOOK\s+SECTION/i )
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub writeconfig()
|
sub writeconfig()
|
||||||
{
|
{
|
||||||
my( $i, $l );
|
if( defined( $smart_compact_nwservconf ) && $smart_compact_nwservconf )
|
||||||
|
{
|
||||||
sortconfig();
|
writeconfig_compact();
|
||||||
|
}
|
||||||
open( CONF, '>' . $mars_config );
|
elsif( config_has_smart_markers() )
|
||||||
$l = ( getconfig( '1 SYS' ) )[0];
|
{
|
||||||
delconfigline( '1 SYS' );
|
writeconfig_markers();
|
||||||
addconfigline( $l );
|
}
|
||||||
foreach $i ( @conf )
|
else
|
||||||
{
|
{
|
||||||
$l = $i;
|
writeconfig_preserve_layout();
|
||||||
$l =~ s/ .*//;
|
}
|
||||||
printf( CONF "%-50s # %s\n", $i, $info[$l] );
|
|
||||||
}
|
|
||||||
close( CONF );
|
|
||||||
}
|
}
|
||||||
|
|||||||
2569
settings.pl
310
smart
@@ -1,310 +0,0 @@
|
|||||||
#!/usr/bin/perl
|
|
||||||
#
|
|
||||||
# SMArT
|
|
||||||
#
|
|
||||||
# Main program file
|
|
||||||
#
|
|
||||||
# Copyright 2001 Wilmer van der Gaast
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
$redirected = 0;
|
|
||||||
|
|
||||||
$server_id = 'Server: SMArT/Perl/0.99';
|
|
||||||
$base = $0;
|
|
||||||
$base =~ s/\/[^\/]*$//g;
|
|
||||||
$base .= '/';
|
|
||||||
|
|
||||||
do( $base . 'smart.conf' );
|
|
||||||
|
|
||||||
close( STDERR );
|
|
||||||
open( STDERR, '>>' . $base . 'error.log' );
|
|
||||||
$ENV{HOME} = $base;
|
|
||||||
|
|
||||||
$l = <STDIN>;
|
|
||||||
$l =~ s/[\n\r]//g;
|
|
||||||
@c = split( ' ', $l );
|
|
||||||
if( scalar( @c ) > 2 )
|
|
||||||
{
|
|
||||||
while( keys( %h ) < 15 ) # Who would ever want to send more headers???
|
|
||||||
{
|
|
||||||
$l = <STDIN>;
|
|
||||||
$l =~ s/[\n\r]//g;
|
|
||||||
if( $l eq '' )
|
|
||||||
{ last; }
|
|
||||||
$n = $l;
|
|
||||||
$n =~ s/:[^:]*$//g;
|
|
||||||
$v = $l;
|
|
||||||
$v =~ s/^[^:]*://g;
|
|
||||||
$h{$n} = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$c[0] = uc( $c[0] );
|
|
||||||
|
|
||||||
if( $h{Authorization} eq '' )
|
|
||||||
{ error( 401 ); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@s = split( ' ', $h{Authorization} );
|
|
||||||
if( $s[0] ne 'Basic' or length( $h{Authorization} ) > 80 ) # We can't be too careful, can we...
|
|
||||||
{ error( 401 ); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$s[1] =~ tr#A-Za-z0-9+/##cd;
|
|
||||||
$s[1] =~ tr#A-Za-z0-9+/# -_#;
|
|
||||||
$s[1] = pack( 'c', 32 + 0.75 * length( $s[1] ) ) . $s[1];
|
|
||||||
$s[1] = unpack( 'u', $s[1] );
|
|
||||||
$s[1] =~ s/[\r\n]//g;
|
|
||||||
@l = split( ':', $s[1] );
|
|
||||||
if( $l[0] ne 'root' )
|
|
||||||
{ error( 401 ); }
|
|
||||||
else
|
|
||||||
{ if( $x = system( $base . 'check_login', @l ) )
|
|
||||||
{ error( 401 ); } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( $c[0] ne 'GET' )
|
|
||||||
{
|
|
||||||
error( 501 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@p = split( '\?', $c[1] );
|
|
||||||
$cc = $c[1];
|
|
||||||
$cc =~ s/[^\?]*\?//;
|
|
||||||
$c = substr( shift( @p ), 1 );
|
|
||||||
@p = split( '&', $p[0] );
|
|
||||||
foreach $p ( @p )
|
|
||||||
{
|
|
||||||
$n = $p;
|
|
||||||
$n =~ s/=.*//;
|
|
||||||
$v = $p;
|
|
||||||
$v =~ s/.*=//;
|
|
||||||
$v =~ s/\+/ /g;
|
|
||||||
$v =~ s/%([0-9A-F][0-9A-F])/pack('c',hex($1))/gie;
|
|
||||||
$p{$n} = $v;
|
|
||||||
}
|
|
||||||
@c = split( '/', $c );
|
|
||||||
|
|
||||||
if( $c[0] eq 'apply' )
|
|
||||||
{
|
|
||||||
do( $base . 'readconfig.pl' );
|
|
||||||
do( $base . 'apply.pl' );
|
|
||||||
handle_request();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
elsif( $c[0] eq 'settings' )
|
|
||||||
{
|
|
||||||
do( $base . 'readconfig.pl' );
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_root();
|
|
||||||
|
|
||||||
if( $c[0] eq '' )
|
|
||||||
{
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 200 OK
|
|
||||||
Content-Type: text/html
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
<HTML>
|
|
||||||
<HEAD>
|
|
||||||
<TITLE>SMArT</TITLE>
|
|
||||||
</HEAD>
|
|
||||||
<FRAMESET COLS="50%,50%">
|
|
||||||
<FRAME NAME="MENU" SRC="/static/menu.html">
|
|
||||||
<FRAME NAME="OPTS" SRC="/static/start.html">
|
|
||||||
</FRAMESET>
|
|
||||||
</HTML>
|
|
||||||
EOF
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
elsif( $c[0] eq 'static' )
|
|
||||||
{
|
|
||||||
do( $base . 'static.pl' );
|
|
||||||
}
|
|
||||||
elsif( $c[0] eq 'settings' )
|
|
||||||
{
|
|
||||||
do( $base . 'settings.pl' );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error( 500 );
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_request();
|
|
||||||
exit;
|
|
||||||
|
|
||||||
##########################################
|
|
||||||
##### END OF MAIN PROCEDURES FOLLOW #####
|
|
||||||
##########################################
|
|
||||||
|
|
||||||
sub error( $ )
|
|
||||||
{
|
|
||||||
if( $_[0] eq '401' )
|
|
||||||
{
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 401 Access denied
|
|
||||||
Content-Type: text/plain
|
|
||||||
WWW-Authenticate: Basic realm="SMArT"
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
You're not allowed to access this URL.
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
elsif( $_[0] eq '404' )
|
|
||||||
{
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 404 File not found
|
|
||||||
Content-Type: text/plain
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
The file you requested does not exist.
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
elsif( $_[0] eq '501' )
|
|
||||||
{
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 501 Unknown command
|
|
||||||
Content-Type: text/plain
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
Unknown command: $c[0]
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 500 Internal server error
|
|
||||||
Content-Type: text/plain
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
Something went wrong...
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub unix_userlist()
|
|
||||||
{
|
|
||||||
my( @c, @d, %e );
|
|
||||||
|
|
||||||
while( @d = getpwent )
|
|
||||||
{
|
|
||||||
unshift( @c, { name => $d[0], uid => $d[2] } );
|
|
||||||
}
|
|
||||||
return( sort( { $a->{"name"} cmp $b->{"name"} } @c ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub unix_grouplist()
|
|
||||||
{
|
|
||||||
my( @c, @d, %e );
|
|
||||||
|
|
||||||
while( @d = getgrent )
|
|
||||||
{
|
|
||||||
unshift( @c, { name => $d[0], gid => $d[2] } );
|
|
||||||
}
|
|
||||||
return( sort( { $a->{"name"} cmp $b->{"name"} } @c ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub redirect( $ )
|
|
||||||
{
|
|
||||||
if( $redirected != 0 )
|
|
||||||
{ return( 0 ); }
|
|
||||||
$redirected = 1;
|
|
||||||
print <<EOF;
|
|
||||||
HTTP/1.0 302 Page relocated
|
|
||||||
Content-Type: text/plain
|
|
||||||
Location: $_[0]
|
|
||||||
$server_id
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_server
|
|
||||||
{
|
|
||||||
open( SFILE, '<' . $base . '.nwclient' );
|
|
||||||
chomp( $line = <SFILE> );
|
|
||||||
close( SFILE );
|
|
||||||
|
|
||||||
$line =~ s/\/.*//;
|
|
||||||
return( $line );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_bindery_password
|
|
||||||
{
|
|
||||||
open( SFILE, '<' . $base . '.nwclient' );
|
|
||||||
chomp( $line = <SFILE> );
|
|
||||||
close( SFILE );
|
|
||||||
|
|
||||||
$line =~ s/.* //;
|
|
||||||
return( $line );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub read_property_string
|
|
||||||
{
|
|
||||||
my @x = split( "\n", `nwbpvalues -c -o $_[0] -t $_[1] -p $_[2] -S $server` );
|
|
||||||
my( $i, $s );
|
|
||||||
|
|
||||||
$i = 5;
|
|
||||||
while( hex( $x[$i] ) > 0 )
|
|
||||||
{
|
|
||||||
$s .= pack( 'c', hex( $x[$i] ) );
|
|
||||||
$i ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( $s );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub read_property_list
|
|
||||||
{
|
|
||||||
my @x = split( "\n", `nwbpvalues -c -o $_[0] -t $_[1] -p $_[2] -S $server` );
|
|
||||||
my( $i, @l );
|
|
||||||
|
|
||||||
$i = 6;
|
|
||||||
while( $x[$i] ne '' )
|
|
||||||
{
|
|
||||||
unshift( @l, $x[$i] );
|
|
||||||
$i += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( @l );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub write_property_string
|
|
||||||
{
|
|
||||||
open( FILE, '|' . 'nwbpset -S ' . $server );
|
|
||||||
print FILE <<EOF;
|
|
||||||
000$_[1]
|
|
||||||
$_[0]
|
|
||||||
$_[2]
|
|
||||||
$_[3]
|
|
||||||
$_[4]
|
|
||||||
EOF
|
|
||||||
for $i ( 0 .. length( $_[5] ) - 1 )
|
|
||||||
{
|
|
||||||
print( FILE unpack( 'H2', substr( $_[5], $i, 1 ) ) . "\n" );
|
|
||||||
}
|
|
||||||
close( FILE );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub drop_root
|
|
||||||
{
|
|
||||||
$< = $> = getpwnam( $nonroot_user );
|
|
||||||
}
|
|
||||||
740
smart.cmake
Normal file
@@ -0,0 +1,740 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# SMArT
|
||||||
|
#
|
||||||
|
# Main program file
|
||||||
|
#
|
||||||
|
# Copyright 2001 Wilmer van der Gaast
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
$redirected = 0;
|
||||||
|
|
||||||
|
$server_id = 'Server: SMArT/Perl/@MARS_NWE_VERSION@';
|
||||||
|
|
||||||
|
do( '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf' )
|
||||||
|
or die "Could not load @MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf: $@ $!";
|
||||||
|
|
||||||
|
close( STDERR );
|
||||||
|
|
||||||
|
# Prefix all raw STDERR from helper tools with timestamp/component before it
|
||||||
|
# reaches smart.log. This also catches output from nwbols/nwbpset/nwpasswd
|
||||||
|
# and systemctl warnings.
|
||||||
|
my $smart_stderr_filter = "perl -MPOSIX=strftime -ne 'chomp; " .
|
||||||
|
"my \\$v=\\$ENV{SMART_VERSION}||q{0.99.pl28}; " .
|
||||||
|
"my \\$f=\\$ENV{SMART_LOG_FILE}||q{stderr}; " .
|
||||||
|
"print strftime(q{[%Y-%m-%d %H:%M:%S]}, localtime), qq{ [ERROR] [SMArT \\$v] [\\$f] \\$_\\n};' >> " .
|
||||||
|
quotemeta( $smart_log_path );
|
||||||
|
|
||||||
|
$ENV{SMART_VERSION} = defined( $smart_version ) && $smart_version ne '' ? $smart_version : '0.99.pl28';
|
||||||
|
$ENV{SMART_LOG_FILE} = 'stderr';
|
||||||
|
|
||||||
|
open( STDERR, '|-', $smart_stderr_filter )
|
||||||
|
or open( STDERR, '>>' . $smart_log_path )
|
||||||
|
or die "Could not open $smart_log_path: $!";
|
||||||
|
|
||||||
|
$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;
|
||||||
|
$smart_admin_group = '@MARS_NWE_SMART_ADMIN_GROUP@' unless defined $smart_admin_group;
|
||||||
|
$smart_admin_group = 'root' if ! defined( $smart_admin_group ) || $smart_admin_group eq '' || $smart_admin_group =~ /^\@MARS_NWE_SMART_ADMIN_GROUP\@$/;
|
||||||
|
|
||||||
|
$l = <STDIN>;
|
||||||
|
$l =~ s/[\n\r]//g;
|
||||||
|
$request_uri = "";
|
||||||
|
$post_body = "";
|
||||||
|
%hl = ();
|
||||||
|
|
||||||
|
@c = split( ' ', $l );
|
||||||
|
if( scalar( @c ) > 2 )
|
||||||
|
{
|
||||||
|
$request_uri = $c[1];
|
||||||
|
while( keys( %h ) < 50 )
|
||||||
|
{
|
||||||
|
$l = <STDIN>;
|
||||||
|
$l =~ s/[\n\r]//g;
|
||||||
|
if( $l eq '' )
|
||||||
|
{ last; }
|
||||||
|
$n = $l;
|
||||||
|
$n =~ s/:[^:]*$//g;
|
||||||
|
$v = $l;
|
||||||
|
$v =~ s/^[^:]*://g;
|
||||||
|
$v =~ s/^\s+//;
|
||||||
|
$v =~ s/\s+$//;
|
||||||
|
$h{$n} = $v;
|
||||||
|
$hl{lc( $n )} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$c[0] = uc( $c[0] );
|
||||||
|
$request_method = $c[0];
|
||||||
|
|
||||||
|
if( $request_method eq 'POST' )
|
||||||
|
{
|
||||||
|
my $content_length = 0;
|
||||||
|
|
||||||
|
if( defined( $hl{'content-length'} ) && $hl{'content-length'} =~ /^[0-9]+$/ )
|
||||||
|
{
|
||||||
|
$content_length = int( $hl{'content-length'} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $content_length > 0 && $content_length < 8192 )
|
||||||
|
{
|
||||||
|
read( STDIN, $post_body, $content_length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $request_method ne 'GET' && $request_method ne 'POST' )
|
||||||
|
{
|
||||||
|
error( 501 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@p = split( '\?', $c[1] );
|
||||||
|
$cc = $c[1];
|
||||||
|
$cc =~ s/[^\?]*\?//;
|
||||||
|
$c = substr( shift( @p ), 1 );
|
||||||
|
|
||||||
|
parse_params( $p[0] );
|
||||||
|
parse_params( $post_body ) if $request_method eq 'POST';
|
||||||
|
|
||||||
|
@c = split( '/', $c );
|
||||||
|
|
||||||
|
if( $c[0] eq 'login' )
|
||||||
|
{
|
||||||
|
handle_login_route();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $c[0] eq 'logout' )
|
||||||
|
{
|
||||||
|
handle_logout_route();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static assets must be available before login, otherwise the login page
|
||||||
|
# cannot load the SMArT logo and icons.
|
||||||
|
if( $c[0] eq 'static' )
|
||||||
|
{
|
||||||
|
do( $smart_libexec_dir . '/static.pl' );
|
||||||
|
handle_request();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! valid_session() )
|
||||||
|
{
|
||||||
|
redirect( '/login' );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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' );
|
||||||
|
handle_request();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
elsif( $c[0] eq 'settings' )
|
||||||
|
{
|
||||||
|
do( $smart_libexec_dir . '/readconfig.pl' );
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_root();
|
||||||
|
|
||||||
|
if( $c[0] eq '' )
|
||||||
|
{
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>SMArT</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<FRAMESET COLS="50%,50%">
|
||||||
|
<FRAME NAME="MENU" SRC="/static/menu.html">
|
||||||
|
<FRAME NAME="OPTS" SRC="/static/start.html">
|
||||||
|
</FRAMESET>
|
||||||
|
</HTML>
|
||||||
|
EOF
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
elsif( $c[0] eq 'static' )
|
||||||
|
{
|
||||||
|
do( $smart_libexec_dir . '/static.pl' );
|
||||||
|
}
|
||||||
|
elsif( $c[0] eq 'settings' )
|
||||||
|
{
|
||||||
|
do( $smart_libexec_dir . '/settings.pl' );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error( 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_request();
|
||||||
|
exit;
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
##### END OF MAIN PROCEDURES FOLLOW #####
|
||||||
|
##########################################
|
||||||
|
|
||||||
|
sub smart_log_line( $$$ )
|
||||||
|
{
|
||||||
|
my( $level, $file, $msg ) = @_;
|
||||||
|
|
||||||
|
$level = 'INFO' unless defined( $level ) && $level ne '';
|
||||||
|
$file = 'smart' unless defined( $file ) && $file ne '';
|
||||||
|
$msg = '' unless defined( $msg );
|
||||||
|
|
||||||
|
my( $sec, $min, $hour, $mday, $mon, $year ) = localtime( time() );
|
||||||
|
my $ts = sprintf( "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
$year + 1900, $mon + 1, $mday, $hour, $min, $sec );
|
||||||
|
|
||||||
|
my $version = defined( $smart_version ) && $smart_version ne '' ? $smart_version : '0.99.pl28';
|
||||||
|
|
||||||
|
if( open( my $fh, '>>', $smart_log_path ) )
|
||||||
|
{
|
||||||
|
print( $fh '[' . $ts . '] [' . $level . '] [SMArT ' . $version . '] [' . $file . '] ' . $msg . "\n" );
|
||||||
|
close( $fh );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub smart_auth_log( $ )
|
||||||
|
{
|
||||||
|
my $msg = $_[0];
|
||||||
|
$msg = '' unless defined $msg;
|
||||||
|
|
||||||
|
my( $sec, $min, $hour, $mday, $mon, $year ) = localtime( time() );
|
||||||
|
my $ts = sprintf( "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
$year + 1900, $mon + 1, $mday, $hour, $min, $sec );
|
||||||
|
|
||||||
|
my $version = defined( $smart_version ) && $smart_version ne '' ? $smart_version : '0.99.pl28';
|
||||||
|
|
||||||
|
if( open( my $fh, '>>', $smart_log_path ) )
|
||||||
|
{
|
||||||
|
print( $fh '[' . $ts . '] [INFO] [SMArT ' . $version . '] [smart] ' . $msg . "\n" );
|
||||||
|
close( $fh );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub parse_params( $ )
|
||||||
|
{
|
||||||
|
my $qs = $_[0];
|
||||||
|
|
||||||
|
return if ! defined( $qs ) || $qs eq '';
|
||||||
|
|
||||||
|
my @items = split( '&', $qs );
|
||||||
|
|
||||||
|
foreach my $item ( @items )
|
||||||
|
{
|
||||||
|
my $n = $item;
|
||||||
|
my $v = $item;
|
||||||
|
|
||||||
|
$n =~ s/=.*//;
|
||||||
|
$v =~ s/^[^=]*=?//;
|
||||||
|
|
||||||
|
$n =~ s/\+/ /g;
|
||||||
|
$v =~ s/\+/ /g;
|
||||||
|
|
||||||
|
$n =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('c',hex($1))/gie;
|
||||||
|
$v =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('c',hex($1))/gie;
|
||||||
|
|
||||||
|
$p{$n} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub smart_html_escape( $ )
|
||||||
|
{
|
||||||
|
my $s = $_[0];
|
||||||
|
$s = '' unless defined $s;
|
||||||
|
$s =~ s/&/&/g;
|
||||||
|
$s =~ s/</</g;
|
||||||
|
$s =~ s/>/>/g;
|
||||||
|
$s =~ s/"/"/g;
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub session_timeout()
|
||||||
|
{
|
||||||
|
return $smart_session_timeout if defined( $smart_session_timeout ) && $smart_session_timeout =~ /^[0-9]+$/ && $smart_session_timeout > 0;
|
||||||
|
return 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub session_dir()
|
||||||
|
{
|
||||||
|
my $dir = defined( $smart_session_dir ) && $smart_session_dir ne '' ? $smart_session_dir : '/run/mars-nwe-webui';
|
||||||
|
|
||||||
|
if( ! -d $dir )
|
||||||
|
{
|
||||||
|
if( ! mkdir( $dir, 0700 ) )
|
||||||
|
{
|
||||||
|
smart_auth_log( 'could not create session dir ' . $dir . ': ' . $! );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( -d $dir )
|
||||||
|
{
|
||||||
|
chmod( 0700, $dir );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smart_auth_log( 'session dir is not available: ' . $dir );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub session_token()
|
||||||
|
{
|
||||||
|
my $token = '';
|
||||||
|
|
||||||
|
if( open( my $fh, '<', '/dev/urandom' ) )
|
||||||
|
{
|
||||||
|
my $buf = '';
|
||||||
|
read( $fh, $buf, 24 );
|
||||||
|
close( $fh );
|
||||||
|
$token = unpack( 'H*', $buf );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $token eq '' )
|
||||||
|
{
|
||||||
|
$token = sprintf( "%08x%08x%08x%08x", time(), $$, int( rand( 0xffffffff ) ), int( rand( 0xffffffff ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$token =~ s/[^A-Fa-f0-9]//g;
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub session_file( $ )
|
||||||
|
{
|
||||||
|
my $token = $_[0];
|
||||||
|
$token = '' unless defined $token;
|
||||||
|
$token =~ s/[^A-Fa-f0-9]//g;
|
||||||
|
|
||||||
|
return '' if $token eq '';
|
||||||
|
|
||||||
|
return session_dir() . '/' . $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cookie_session_id()
|
||||||
|
{
|
||||||
|
my $cookie = defined( $hl{'cookie'} ) ? $hl{'cookie'} : '';
|
||||||
|
|
||||||
|
foreach my $part ( split( /;/, $cookie ) )
|
||||||
|
{
|
||||||
|
$part =~ s/^\s+//;
|
||||||
|
$part =~ s/\s+$//;
|
||||||
|
|
||||||
|
if( $part =~ /^SMArT_SID=([A-Fa-f0-9]+)$/ )
|
||||||
|
{
|
||||||
|
return $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub valid_session()
|
||||||
|
{
|
||||||
|
my $token = cookie_session_id();
|
||||||
|
my $file = session_file( $token );
|
||||||
|
|
||||||
|
return 0 if $token eq '';
|
||||||
|
return 0 if $file eq '';
|
||||||
|
|
||||||
|
if( ! -f $file )
|
||||||
|
{
|
||||||
|
smart_auth_log( 'session cookie exists but file is missing: ' . $file );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @st = stat( $file );
|
||||||
|
if( scalar( @st ) == 0 )
|
||||||
|
{
|
||||||
|
smart_auth_log( 'could not stat session file: ' . $file );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( time() - $st[9] > session_timeout() )
|
||||||
|
{
|
||||||
|
unlink( $file );
|
||||||
|
smart_auth_log( 'session expired: ' . $file );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
utime( time(), time(), $file );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub create_session( $ )
|
||||||
|
{
|
||||||
|
my $user = $_[0];
|
||||||
|
my $token = session_token();
|
||||||
|
my $file = session_file( $token );
|
||||||
|
|
||||||
|
if( $file eq '' )
|
||||||
|
{
|
||||||
|
smart_auth_log( 'could not build session file path' );
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if( open( my $fh, '>', $file ) )
|
||||||
|
{
|
||||||
|
print( $fh $user . "\n" . time() . "\n" );
|
||||||
|
close( $fh );
|
||||||
|
chmod( 0600, $file );
|
||||||
|
smart_auth_log( 'created session for ' . $user . ' at ' . $file );
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
smart_auth_log( 'could not create session file ' . $file . ': ' . $! );
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub destroy_session()
|
||||||
|
{
|
||||||
|
my $token = cookie_session_id();
|
||||||
|
my $file = session_file( $token );
|
||||||
|
|
||||||
|
unlink( $file ) if $file ne '' && -f $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_login_password( $$ )
|
||||||
|
{
|
||||||
|
my( $user, $pass ) = @_;
|
||||||
|
|
||||||
|
return 0 if ! defined( $user ) || ! defined( $pass );
|
||||||
|
return 0 if $user eq '' || $pass eq '';
|
||||||
|
|
||||||
|
if( ! defined( $smart_check_login ) || $smart_check_login eq '' || ! -x $smart_check_login )
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $admin_group = defined( $smart_admin_group ) && $smart_admin_group ne '' ? $smart_admin_group : 'root';
|
||||||
|
|
||||||
|
my $conf_path = defined( $smart_conf_path ) && $smart_conf_path ne '' ? $smart_conf_path : '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf';
|
||||||
|
my $rc = system( $smart_check_login, $user, $pass, $admin_group, $conf_path );
|
||||||
|
|
||||||
|
if( $rc == 0 )
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $exit = $rc >> 8;
|
||||||
|
|
||||||
|
return -2 if $exit == 2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_login_page( $ )
|
||||||
|
{
|
||||||
|
my $msg = smart_html_escape( $_[0] );
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 200 OK
|
||||||
|
Content-Type: text/html
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>SMArT Login</title>
|
||||||
|
<style>
|
||||||
|
:root{--bg:#f4f1ea;--panel:#faf8f4;--line:#dfd2bf;--text:#3d342c;--muted:#6f6257;--accent:#ad1d1c;--gold:#b9813d}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html,body{margin:0;padding:0;min-height:100%;background:var(--bg);color:var(--text);font:15px/1.5 Arial,Helvetica,sans-serif}
|
||||||
|
body{display:flex;align-items:center;justify-content:center;padding:24px}
|
||||||
|
.login{width:min(440px,100%);background:var(--panel);border:1px solid var(--line);border-radius:20px;box-shadow:0 18px 45px rgba(64,36,12,.12);overflow:hidden}
|
||||||
|
.hero{padding:26px 28px;background:linear-gradient(135deg,#a80f18,#c44731 60%,#d79a54);color:white}
|
||||||
|
.hero{display:flex;align-items:center;gap:18px}.hero img{width:120px;max-width:34%;height:auto;display:block;background:#fff;border-radius:16px;padding:8px 10px;box-shadow:0 8px 20px rgba(0,0,0,.12)}.hero h1{margin:0;font-size:28px}
|
||||||
|
.hero p{margin:6px 0 0;opacity:.95}
|
||||||
|
form{padding:24px 28px 28px}
|
||||||
|
label{display:block;font-weight:bold;margin:0 0 7px}
|
||||||
|
input{width:100%;border:1px solid #cdbb9f;border-radius:12px;padding:10px 12px;background:#fffdf9;color:var(--text);font-size:15px;margin:0 0 16px}
|
||||||
|
button{width:100%;border:1px solid #a33d2f;border-radius:12px;padding:11px 14px;background:#b84434;color:#fff;font-weight:bold;font-size:15px;cursor:pointer}
|
||||||
|
.msg{margin:0 0 16px;padding:10px 12px;border-radius:12px;background:#fff3e0;border:1px solid #ead0a4;color:#7a3d18}
|
||||||
|
.note{margin-top:14px;color:var(--muted);font-size:13px;text-align:center;line-height:1.45}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login">
|
||||||
|
<div class="hero">
|
||||||
|
<img src="/static/smart.jpg" alt="SMArT logo">
|
||||||
|
<div>
|
||||||
|
<h1>SMArT Login</h1>
|
||||||
|
<p>MARS_NWE web administration</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="/login">
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if( $msg ne '' )
|
||||||
|
{
|
||||||
|
print '<div class="msg">' . $msg . "</div>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
<label for="user">User</label>
|
||||||
|
<input id="user" name="user" value="root" autocomplete="username">
|
||||||
|
<label for="pass">Password</label>
|
||||||
|
<input id="pass" name="pass" type="password" autocomplete="current-password" autofocus>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
<div class="note">© Copyright 2026 Mario Fetka</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_login_route()
|
||||||
|
{
|
||||||
|
if( $request_method ne 'POST' )
|
||||||
|
{
|
||||||
|
print_login_page( '' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $rv = check_login_password( $p{user}, $p{pass} );
|
||||||
|
|
||||||
|
if( $rv == -1 )
|
||||||
|
{
|
||||||
|
print_login_page( 'Login helper check_login is missing or not executable.' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $rv == -2 )
|
||||||
|
{
|
||||||
|
my $admin_group = defined( $smart_admin_group ) && $smart_admin_group ne '' ? $smart_admin_group : 'root';
|
||||||
|
print_login_page( 'Login denied. User is not a member of required admin group: ' . $admin_group );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $rv != 1 )
|
||||||
|
{
|
||||||
|
print_login_page( 'Login failed.' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $token = create_session( $p{user} );
|
||||||
|
|
||||||
|
if( $token eq '' )
|
||||||
|
{
|
||||||
|
print_login_page( 'Could not create login session.' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 302 Found
|
||||||
|
Location: /
|
||||||
|
Set-Cookie: SMArT_SID=$token; Path=/; HttpOnly; Max-Age=3600
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handle_logout_route()
|
||||||
|
{
|
||||||
|
destroy_session();
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 302 Found
|
||||||
|
Location: /login
|
||||||
|
Set-Cookie: SMArT_SID=deleted; Path=/; HttpOnly; Max-Age=0
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub error( $ )
|
||||||
|
{
|
||||||
|
if( $_[0] eq '401' )
|
||||||
|
{
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 401 Access denied
|
||||||
|
Content-Type: text/plain
|
||||||
|
WWW-Authenticate: Basic realm="SMArT"
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
You're not allowed to access this URL.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
elsif( $_[0] eq '404' )
|
||||||
|
{
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 404 File not found
|
||||||
|
Content-Type: text/plain
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
The file you requested does not exist.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
elsif( $_[0] eq '501' )
|
||||||
|
{
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 501 Unknown command
|
||||||
|
Content-Type: text/plain
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
Unknown command: $c[0]
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 500 Internal server error
|
||||||
|
Content-Type: text/plain
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
Something went wrong...
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unix_userlist()
|
||||||
|
{
|
||||||
|
my( @c, @d, %e );
|
||||||
|
|
||||||
|
while( @d = getpwent )
|
||||||
|
{
|
||||||
|
unshift( @c, { name => $d[0], uid => $d[2] } );
|
||||||
|
}
|
||||||
|
return( sort( { $a->{"name"} cmp $b->{"name"} } @c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unix_grouplist()
|
||||||
|
{
|
||||||
|
my( @c, @d, %e );
|
||||||
|
|
||||||
|
while( @d = getgrent )
|
||||||
|
{
|
||||||
|
unshift( @c, { name => $d[0], gid => $d[2] } );
|
||||||
|
}
|
||||||
|
return( sort( { $a->{"name"} cmp $b->{"name"} } @c ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub redirect( $ )
|
||||||
|
{
|
||||||
|
if( $redirected != 0 )
|
||||||
|
{ return( 0 ); }
|
||||||
|
$redirected = 1;
|
||||||
|
print <<EOF;
|
||||||
|
HTTP/1.0 302 Page relocated
|
||||||
|
Content-Type: text/plain
|
||||||
|
Location: $_[0]
|
||||||
|
$server_id
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_server
|
||||||
|
{
|
||||||
|
open( SFILE, '<' . $smart_nwclient_path );
|
||||||
|
chomp( $line = <SFILE> );
|
||||||
|
close( SFILE );
|
||||||
|
|
||||||
|
$line =~ s/\/.*//;
|
||||||
|
return( $line );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_bindery_password
|
||||||
|
{
|
||||||
|
open( SFILE, '<' . $smart_nwclient_path );
|
||||||
|
chomp( $line = <SFILE> );
|
||||||
|
close( SFILE );
|
||||||
|
|
||||||
|
$line =~ s/.* //;
|
||||||
|
return( $line );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_property_string
|
||||||
|
{
|
||||||
|
my @x = split( "\n", `nwbpvalues -c -o $_[0] -t $_[1] -p $_[2] -S $server` );
|
||||||
|
my( $i, $s );
|
||||||
|
|
||||||
|
$i = 5;
|
||||||
|
while( hex( $x[$i] ) > 0 )
|
||||||
|
{
|
||||||
|
$s .= pack( 'c', hex( $x[$i] ) );
|
||||||
|
$i ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( $s );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_property_list
|
||||||
|
{
|
||||||
|
my @x = split( "\n", `nwbpvalues -c -o $_[0] -t $_[1] -p $_[2] -S $server` );
|
||||||
|
my( $i, @l );
|
||||||
|
|
||||||
|
$i = 6;
|
||||||
|
while( $x[$i] ne '' )
|
||||||
|
{
|
||||||
|
unshift( @l, $x[$i] );
|
||||||
|
$i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( @l );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub write_property_string
|
||||||
|
{
|
||||||
|
open( FILE, '|' . 'nwbpset -S ' . $server );
|
||||||
|
print FILE <<EOF;
|
||||||
|
000$_[1]
|
||||||
|
$_[0]
|
||||||
|
$_[2]
|
||||||
|
$_[3]
|
||||||
|
$_[4]
|
||||||
|
EOF
|
||||||
|
for $i ( 0 .. length( $_[5] ) - 1 )
|
||||||
|
{
|
||||||
|
print( FILE unpack( 'H2', substr( $_[5], $i, 1 ) ) . "\n" );
|
||||||
|
}
|
||||||
|
close( FILE );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub drop_root
|
||||||
|
{
|
||||||
|
$< = $> = getpwnam( $nonroot_user );
|
||||||
|
}
|
||||||
11
smart.conf
@@ -1,11 +0,0 @@
|
|||||||
$COLOR_BACK = "#F0F0FF";
|
|
||||||
$COLOR_HEAD_BACK = "#C0C0FF";
|
|
||||||
$COLOR_HEAD_FORE = "#000000";
|
|
||||||
$COLOR_SUBH_BACK = "#D0D0FF";
|
|
||||||
$COLOR_SUBH_FORE = "#000000";
|
|
||||||
$COLOR_TEXT_BACK = "#E0E0FF";
|
|
||||||
$COLOR_TEXT_FORE = "#000000";
|
|
||||||
|
|
||||||
$mars_config = '/usr/local/etc/nwserv.conf';
|
|
||||||
|
|
||||||
$nonroot_user = 'nobody';
|
|
||||||
213
smart.conf.cmake
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
# SMArT / nwwebui configuration file
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# UI colors
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Background color used for the main page body.
|
||||||
|
$COLOR_BACK = "#F0F0FF";
|
||||||
|
|
||||||
|
# Background color used for section headers.
|
||||||
|
$COLOR_HEAD_BACK = "#C0C0FF";
|
||||||
|
|
||||||
|
# Text color used for section headers.
|
||||||
|
$COLOR_HEAD_FORE = "#000000";
|
||||||
|
|
||||||
|
# Background color used for subsection headers.
|
||||||
|
$COLOR_SUBH_BACK = "#D0D0FF";
|
||||||
|
|
||||||
|
# Text color used for subsection headers.
|
||||||
|
$COLOR_SUBH_FORE = "#000000";
|
||||||
|
|
||||||
|
# Background color used for normal content rows.
|
||||||
|
$COLOR_TEXT_BACK = "#E0E0FF";
|
||||||
|
|
||||||
|
# Text color used for normal content rows.
|
||||||
|
$COLOR_TEXT_FORE = "#000000";
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Main MARS NWE configuration
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Path to the main mars_nwe server configuration file.
|
||||||
|
# This file is read and modified by the SMArT configuration pages.
|
||||||
|
$mars_config = '@MARS_NWE_INSTALL_FULL_CONFDIR@/nwserv.conf';
|
||||||
|
|
||||||
|
# User name used when SMArT drops privileges for non-root operations.
|
||||||
|
# Keep this set to an unprivileged local account.
|
||||||
|
$nonroot_user = 'nobody';
|
||||||
|
|
||||||
|
# Write the mars_nwe configuration file in compact form.
|
||||||
|
# 0 = preserve comments, blank lines and the original section layout
|
||||||
|
# 1 = write a compact configuration file without the original long comments
|
||||||
|
$smart_compact_nwservconf = 0;
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# SMArT internal file layout
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Absolute path to the SMArT configuration file itself.
|
||||||
|
# Used when SMArT needs to append updated settings.
|
||||||
|
$smart_conf_path = '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf';
|
||||||
|
|
||||||
|
# File used to store bindery login information for SMArT helper tools.
|
||||||
|
# This file should remain readable only by the service user or root.
|
||||||
|
$smart_nwclient_path = '@MARS_NWE_INSTALL_FULL_CONFDIR@/.nwclient';
|
||||||
|
|
||||||
|
# Directory containing static HTML and image files served by SMArT.
|
||||||
|
$smart_static_dir = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/static';
|
||||||
|
|
||||||
|
# Log file used by the Perl SMArT frontend.
|
||||||
|
# Keep this separate from the nwwebui log file.
|
||||||
|
$smart_log_path = '@MARS_NWE_LOG_DIR@/smart.log';
|
||||||
|
|
||||||
|
# Path to the PAM-based login helper used for SMArT authentication.
|
||||||
|
$smart_check_login = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/check_login';
|
||||||
|
|
||||||
|
# Path to the native Unix-user enumeration helper used by the user editor.
|
||||||
|
$smart_userlist_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart_userlist';
|
||||||
|
|
||||||
|
# Unix group allowed to log in to the SMArT/nwwebui admin interface.
|
||||||
|
#
|
||||||
|
# Authentication is still done through PAM service "smart", but a user must
|
||||||
|
# also be a member of this Unix group.
|
||||||
|
#
|
||||||
|
# The build-time default is "root" to preserve the traditional behavior on
|
||||||
|
# existing installations: the root user is allowed because its primary Unix
|
||||||
|
# group is normally also "root". Do not add normal users to the "root" group.
|
||||||
|
#
|
||||||
|
# For delegated administration, use a dedicated group instead, for example:
|
||||||
|
#
|
||||||
|
# cmake -DMARS_NWE_SMART_ADMIN_GROUP=nwadmin ...
|
||||||
|
# groupadd nwadmin
|
||||||
|
# usermod -aG nwadmin mario
|
||||||
|
#
|
||||||
|
# Changes to local group membership normally require the user to start a new
|
||||||
|
# login session before NSS/PAM reports the new membership.
|
||||||
|
$smart_admin_group = '@MARS_NWE_SMART_ADMIN_GROUP@';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# $smart_perl_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart';
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Host printer / CUPS utility integration
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Enable CUPS host-printer discovery in the SMArT queue UI.
|
||||||
|
# This value is generated from cmake/modules/cupsutils.cmake.
|
||||||
|
# 1 = enabled, 0 = disabled
|
||||||
|
$smart_cups_enable = '@SMART_CUPS_ENABLE@';
|
||||||
|
|
||||||
|
# lpstat executable used to discover local CUPS printers for the queue UI.
|
||||||
|
$smart_cups_lpstat_path = '@CUPS_LPSTAT_EXECUTABLE@';
|
||||||
|
|
||||||
|
# Command template used when SMArT creates a print queue from a CUPS printer.
|
||||||
|
# %p is replaced with the sanitized CUPS printer name.
|
||||||
|
# The trailing '-' makes lp read the print job from stdin.
|
||||||
|
$smart_cups_print_command_template = '@CUPS_LP_EXECUTABLE@ -d %p -';
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# nwwebui listener settings
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# IP address used for the HTTP and HTTPS listeners.
|
||||||
|
# Use 0.0.0.0 to listen on all IPv4 interfaces.
|
||||||
|
# Use 127.0.0.1 for local-only testing.
|
||||||
|
$nw_bind_ip = '0.0.0.0';
|
||||||
|
|
||||||
|
# Log level used by the native nwwebui frontend service.
|
||||||
|
#
|
||||||
|
# Supported values, from quiet to verbose:
|
||||||
|
#
|
||||||
|
# error - only real errors
|
||||||
|
# warning - errors and warnings
|
||||||
|
# info - normal operational messages, default
|
||||||
|
# debug - additional diagnostic information
|
||||||
|
# trace - very verbose request/connection tracing
|
||||||
|
#
|
||||||
|
# Older numeric values are still accepted for compatibility, but named values
|
||||||
|
# are preferred for new configurations.
|
||||||
|
$nw_log_level = 'info';
|
||||||
|
|
||||||
|
# Run nwwebui in daemon mode by default.
|
||||||
|
# 0 = stay in foreground
|
||||||
|
# 1 = detach into background
|
||||||
|
$nw_daemonize = 0;
|
||||||
|
|
||||||
|
# PID file written by nwwebui.
|
||||||
|
$nw_pid_file = '@MARS_NWE_PID_DIR@/nwwebui.pid';
|
||||||
|
|
||||||
|
# Log file written by nwwebui.
|
||||||
|
$nw_log_file = '@MARS_NWE_LOG_DIR@/nwwebui.log';
|
||||||
|
|
||||||
|
# Enable or disable TLS/SSL support.
|
||||||
|
# 1 = enable HTTPS listener
|
||||||
|
# 0 = disable HTTPS listener
|
||||||
|
#
|
||||||
|
# When disabled, nwwebui can still serve plain HTTP if nw_http_port > 0.
|
||||||
|
$nw_ssl_enable = 0;
|
||||||
|
|
||||||
|
# Plain HTTP listener port.
|
||||||
|
# Set to 0 to disable plain HTTP completely.
|
||||||
|
# This is useful for local or isolated-network testing.
|
||||||
|
$nw_http_port = 9080;
|
||||||
|
|
||||||
|
# HTTPS listener port.
|
||||||
|
# Used only when $nw_ssl_enable is set to 1.
|
||||||
|
# Set to 0 to disable HTTPS listening.
|
||||||
|
$nw_https_port = 9443;
|
||||||
|
|
||||||
|
# TLS certificate file in PEM format.
|
||||||
|
# Required only when HTTPS is enabled.
|
||||||
|
$nw_cert_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.crt';
|
||||||
|
|
||||||
|
# TLS private key file in PEM format.
|
||||||
|
# Required only when HTTPS is enabled.
|
||||||
|
$nw_key_file = '@MARS_NWE_INSTALL_FULL_CONFDIR@/server.key';
|
||||||
|
|
||||||
|
# Directory for HTML login cookie sessions. Created by systemd RuntimeDirectory.
|
||||||
|
$smart_session_dir = '/run/mars-nwe-webui';
|
||||||
|
$smart_session_timeout = 3600;
|
||||||
|
|
||||||
|
|
||||||
|
# SMArT Perl logging verbosity.
|
||||||
|
#
|
||||||
|
# This controls log messages written by the Perl CGI-style helper scripts
|
||||||
|
# such as apply.pl. The messages are written to the SMArT log file configured
|
||||||
|
# for the WebUI, normally:
|
||||||
|
#
|
||||||
|
# /var/log/mars_nwe/smart.log
|
||||||
|
#
|
||||||
|
# Supported values, from quiet to verbose:
|
||||||
|
#
|
||||||
|
# error - only real errors that abort or fail an operation
|
||||||
|
# warning - errors and warnings about unusual but non-fatal situations
|
||||||
|
# info - normal operational messages, command start/finish, default
|
||||||
|
# debug - additional diagnostic information for troubleshooting
|
||||||
|
# trace - very verbose step-by-step traces, including bindery pipe payloads
|
||||||
|
#
|
||||||
|
# Recommended setting for normal operation:
|
||||||
|
#
|
||||||
|
# $smart_debug_level = 'info';
|
||||||
|
#
|
||||||
|
# Use 'trace' only while debugging a concrete problem. Trace logging may
|
||||||
|
# include submitted bindery payload data and can produce a lot of log output.
|
||||||
|
# After debugging, switch back to 'info'.
|
||||||
|
$smart_debug_level = 'info';
|
||||||
|
|
||||||
|
|
||||||
|
# ncpfs nprint executable used by the queue test action.
|
||||||
|
$smart_nprint_path = '/usr/bin/nprint';
|
||||||
4
smart.pamd
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
auth required pam_unix.so
|
||||||
|
account required pam_unix.so
|
||||||
|
password required pam_unix.so
|
||||||
|
session required pam_unix.so
|
||||||
438
smart_userlist.c
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
SMArT
|
||||||
|
|
||||||
|
List local/NSS users for the WebUI.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
smart_userlist [--config /etc/mars_nwe/smart.conf] [--all]
|
||||||
|
[--min-uid UID] [--pam-check] [--pam-service SERVICE]
|
||||||
|
|
||||||
|
Output format on stdout stays unchanged:
|
||||||
|
username<TAB>uid<TAB>gid<TAB>gecos<TAB>home<TAB>shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define SMART_LOG_ERROR 0
|
||||||
|
#define SMART_LOG_WARNING 1
|
||||||
|
#define SMART_LOG_INFO 2
|
||||||
|
#define SMART_LOG_DEBUG 3
|
||||||
|
#define SMART_LOG_TRACE 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char log_path[512];
|
||||||
|
char debug_level[64];
|
||||||
|
char admin_group[256];
|
||||||
|
int level;
|
||||||
|
} smart_helper_config_t;
|
||||||
|
|
||||||
|
static void trim(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != s) {
|
||||||
|
memmove(s, p, strlen(p) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(s);
|
||||||
|
while (len > 0 && isspace((unsigned char)s[len - 1])) {
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strip_quotes(char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
if (len >= 2) {
|
||||||
|
if ((s[0] == '\'' && s[len - 1] == '\'') ||
|
||||||
|
(s[0] == '"' && s[len - 1] == '"')) {
|
||||||
|
memmove(s, s + 1, len - 2);
|
||||||
|
s[len - 2] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *val, size_t vsz)
|
||||||
|
{
|
||||||
|
const char *p = line;
|
||||||
|
size_t ki = 0;
|
||||||
|
size_t vi = 0;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p != '$') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
|
||||||
|
if (ki + 1 < ksz) {
|
||||||
|
key[ki++] = *p;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
key[ki] = '\0';
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p != '=') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (*p && isspace((unsigned char)*p)) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
|
||||||
|
if (vi + 1 < vsz) {
|
||||||
|
val[vi++] = *p;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
val[vi] = '\0';
|
||||||
|
|
||||||
|
trim(key);
|
||||||
|
trim(val);
|
||||||
|
strip_quotes(val);
|
||||||
|
|
||||||
|
return key[0] != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_log_level(const char *value)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s", value);
|
||||||
|
trim(buf);
|
||||||
|
|
||||||
|
for (i = 0; buf[i]; i++) {
|
||||||
|
buf[i] = (char)tolower((unsigned char)buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(buf, "error") == 0 || strcmp(buf, "err") == 0 || strcmp(buf, "0") == 0) {
|
||||||
|
return SMART_LOG_ERROR;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "warning") == 0 || strcmp(buf, "warn") == 0 || strcmp(buf, "1") == 0) {
|
||||||
|
return SMART_LOG_WARNING;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "info") == 0 || strcmp(buf, "2") == 0) {
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "debug") == 0 || strcmp(buf, "3") == 0) {
|
||||||
|
return SMART_LOG_DEBUG;
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "trace") == 0 || strcmp(buf, "4") == 0) {
|
||||||
|
return SMART_LOG_TRACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SMART_LOG_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *level_name(int level)
|
||||||
|
{
|
||||||
|
if (level <= SMART_LOG_ERROR) {
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
if (level == SMART_LOG_WARNING) {
|
||||||
|
return "WARNING";
|
||||||
|
}
|
||||||
|
if (level == SMART_LOG_DEBUG) {
|
||||||
|
return "DEBUG";
|
||||||
|
}
|
||||||
|
if (level >= SMART_LOG_TRACE) {
|
||||||
|
return "TRACE";
|
||||||
|
}
|
||||||
|
return "INFO";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smart_cfg_init(smart_helper_config_t *cfg)
|
||||||
|
{
|
||||||
|
memset(cfg, 0, sizeof(*cfg));
|
||||||
|
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", DEFAULT_SMART_LOG_PATH);
|
||||||
|
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", DEFAULT_SMART_LOG_LEVEL);
|
||||||
|
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", "root");
|
||||||
|
cfg->level = parse_log_level(cfg->debug_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smart_cfg_load(smart_helper_config_t *cfg, const char *path)
|
||||||
|
{
|
||||||
|
FILE *fh;
|
||||||
|
char line[2048];
|
||||||
|
|
||||||
|
if (path == NULL || path[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh = fopen(path, "r");
|
||||||
|
if (fh == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fh) != NULL) {
|
||||||
|
char key[256];
|
||||||
|
char val[1024];
|
||||||
|
|
||||||
|
if (!parse_perl_assignment(line, key, sizeof(key), val, sizeof(val))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, "smart_log_path") == 0) {
|
||||||
|
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", val);
|
||||||
|
} else if (strcmp(key, "smart_debug_level") == 0 ||
|
||||||
|
strcmp(key, "smart_log_level") == 0) {
|
||||||
|
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", val);
|
||||||
|
cfg->level = parse_log_level(val);
|
||||||
|
} else if (strcmp(key, "smart_admin_group") == 0) {
|
||||||
|
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void helper_log(smart_helper_config_t *cfg, const char *component, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
FILE *fh = stderr;
|
||||||
|
int close_fh = 0;
|
||||||
|
time_t now;
|
||||||
|
struct tm tm_now;
|
||||||
|
char tbuf[64];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (cfg != NULL && level > cfg->level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg != NULL && cfg->log_path[0] != '\0') {
|
||||||
|
fh = fopen(cfg->log_path, "a");
|
||||||
|
if (fh != NULL) {
|
||||||
|
close_fh = 1;
|
||||||
|
} else {
|
||||||
|
fh = stderr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
localtime_r(&now, &tm_now);
|
||||||
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_now);
|
||||||
|
|
||||||
|
fprintf(fh, "[%s] [%s] [SMArT helper] [%s] ", tbuf, level_name(level), component);
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(fh, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
fputc('\n', fh);
|
||||||
|
fflush(fh);
|
||||||
|
|
||||||
|
if (close_fh) {
|
||||||
|
fclose(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int empty_conv(int num_msg, const struct pam_message **msg,
|
||||||
|
struct pam_response **resp, void *appdata_ptr)
|
||||||
|
{
|
||||||
|
struct pam_response *reply;
|
||||||
|
|
||||||
|
(void) msg;
|
||||||
|
(void) appdata_ptr;
|
||||||
|
|
||||||
|
if (num_msg <= 0) {
|
||||||
|
*resp = NULL;
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = calloc((size_t) num_msg, sizeof(struct pam_response));
|
||||||
|
if (reply == NULL) {
|
||||||
|
return PAM_BUF_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*resp = reply;
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pam_account_ok(const char *service, const char *user)
|
||||||
|
{
|
||||||
|
struct pam_conv conv = { empty_conv, NULL };
|
||||||
|
pam_handle_t *pamh = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pam_start(service, user, &conv, &pamh);
|
||||||
|
if (rc == PAM_SUCCESS) {
|
||||||
|
rc = pam_acct_mgmt(pamh, PAM_SILENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pamh != NULL) {
|
||||||
|
pam_end(pamh, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc == PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_sanitized(const char *s)
|
||||||
|
{
|
||||||
|
const unsigned char *p = (const unsigned char *) (s != NULL ? s : "");
|
||||||
|
|
||||||
|
while (*p != '\0') {
|
||||||
|
if (*p == '\t' || *p == '\n' || *p == '\r') {
|
||||||
|
putchar(' ');
|
||||||
|
} else {
|
||||||
|
putchar((int) *p);
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_safe_name(const char *s)
|
||||||
|
{
|
||||||
|
const unsigned char *p = (const unsigned char *) s;
|
||||||
|
|
||||||
|
if (s == NULL || *s == '\0') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p != '\0') {
|
||||||
|
if (!( (*p >= 'A' && *p <= 'Z') ||
|
||||||
|
(*p >= 'a' && *p <= 'z') ||
|
||||||
|
(*p >= '0' && *p <= '9') ||
|
||||||
|
*p == '_' || *p == '-' || *p == '.' )) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct passwd *pw;
|
||||||
|
uid_t min_uid = 1000;
|
||||||
|
int include_system = 0;
|
||||||
|
int pam_check = 0;
|
||||||
|
const char *pam_service = "smart";
|
||||||
|
const char *smart_conf = DEFAULT_SMART_CONF;
|
||||||
|
int i;
|
||||||
|
unsigned long emitted = 0;
|
||||||
|
unsigned long skipped = 0;
|
||||||
|
smart_helper_config_t cfg;
|
||||||
|
|
||||||
|
smart_cfg_init(&cfg);
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--all") == 0) {
|
||||||
|
include_system = 1;
|
||||||
|
min_uid = 0;
|
||||||
|
} else if (strcmp(argv[i], "--min-uid") == 0 && i + 1 < argc) {
|
||||||
|
char *end = NULL;
|
||||||
|
unsigned long v = strtoul(argv[++i], &end, 10);
|
||||||
|
if (end == NULL || *end != '\0') {
|
||||||
|
smart_cfg_load(&cfg, smart_conf);
|
||||||
|
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "invalid --min-uid value");
|
||||||
|
fprintf(stderr, "Invalid --min-uid value\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
min_uid = (uid_t) v;
|
||||||
|
} else if (strcmp(argv[i], "--pam-check") == 0) {
|
||||||
|
pam_check = 1;
|
||||||
|
} else if (strcmp(argv[i], "--pam-service") == 0 && i + 1 < argc) {
|
||||||
|
pam_service = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
|
||||||
|
smart_conf = argv[++i];
|
||||||
|
} else {
|
||||||
|
smart_cfg_load(&cfg, smart_conf);
|
||||||
|
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "invalid command line");
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s [--config FILE] [--all] [--min-uid UID] [--pam-check] [--pam-service SERVICE]\n",
|
||||||
|
argv[0]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smart_cfg_load(&cfg, smart_conf);
|
||||||
|
|
||||||
|
helper_log(&cfg, "smart_userlist", SMART_LOG_DEBUG,
|
||||||
|
"user enumeration started include_system=%d min_uid=%lu pam_check=%d pam_service='%s'",
|
||||||
|
include_system, (unsigned long) min_uid, pam_check, pam_service);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
setpwent();
|
||||||
|
|
||||||
|
while ((pw = getpwent()) != NULL) {
|
||||||
|
if (!is_safe_name(pw->pw_name)) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!include_system && pw->pw_uid < min_uid) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!include_system &&
|
||||||
|
(strcmp(pw->pw_name, "root") == 0 || strcmp(pw->pw_name, "nobody") == 0)) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pam_check && !pam_account_ok(pam_service, pw->pw_name)) {
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_sanitized(pw->pw_name);
|
||||||
|
printf("\t%lu\t%lu\t", (unsigned long) pw->pw_uid, (unsigned long) pw->pw_gid);
|
||||||
|
print_sanitized(pw->pw_gecos);
|
||||||
|
putchar('\t');
|
||||||
|
print_sanitized(pw->pw_dir);
|
||||||
|
putchar('\t');
|
||||||
|
print_sanitized(pw->pw_shell);
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
emitted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
if (errno != 0) {
|
||||||
|
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "getpwent failed: %s", strerror(errno));
|
||||||
|
perror("getpwent");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
helper_log(&cfg, "smart_userlist", SMART_LOG_DEBUG,
|
||||||
|
"user enumeration finished emitted=%lu skipped=%lu",
|
||||||
|
emitted, skipped);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
55
static.pl
@@ -5,33 +5,20 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx)
|
# Copyright 2001 Wilmer van der Gaast (lintux@lintux.cx)
|
||||||
#
|
#
|
||||||
#
|
# Updated with favicon and modern asset MIME type support.
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
sub handle_request()
|
sub handle_request()
|
||||||
{
|
{
|
||||||
$f = $c[1];
|
$f = $c[1];
|
||||||
$f =~ s/[^-_\.A-Za-z0-9]//g;
|
$f =~ s/[^-_\.A-Za-z0-9]//g;
|
||||||
if( ! open( FILE, $base . 'static/' . $f ) )
|
if( ! open( FILE, $smart_static_dir . '/' . $f ) )
|
||||||
{
|
{
|
||||||
error( 404 );
|
error( 404 );
|
||||||
}
|
}
|
||||||
$e = $f;
|
$e = $f;
|
||||||
$e =~ s/^.*\.//;
|
$e =~ s/^.*\.//;
|
||||||
|
$e = lc( $e );
|
||||||
if( $e eq 'html' )
|
if( $e eq 'html' )
|
||||||
{
|
{
|
||||||
$t = 'text/html';
|
$t = 'text/html';
|
||||||
@@ -40,22 +27,48 @@ sub handle_request()
|
|||||||
{
|
{
|
||||||
$t = 'image/gif';
|
$t = 'image/gif';
|
||||||
}
|
}
|
||||||
elsif( $e eq 'jpg' )
|
elsif( $e eq 'jpg' || $e eq 'jpeg' )
|
||||||
{
|
{
|
||||||
$t = 'image/jpeg';
|
$t = 'image/jpeg';
|
||||||
}
|
}
|
||||||
|
elsif( $e eq 'png' )
|
||||||
|
{
|
||||||
|
$t = 'image/png';
|
||||||
|
}
|
||||||
|
elsif( $e eq 'ico' )
|
||||||
|
{
|
||||||
|
$t = 'image/x-icon';
|
||||||
|
}
|
||||||
|
elsif( $e eq 'svg' )
|
||||||
|
{
|
||||||
|
$t = 'image/svg+xml';
|
||||||
|
}
|
||||||
|
elsif( $e eq 'webp' )
|
||||||
|
{
|
||||||
|
$t = 'image/webp';
|
||||||
|
}
|
||||||
|
elsif( $e eq 'css' )
|
||||||
|
{
|
||||||
|
$t = 'text/css';
|
||||||
|
}
|
||||||
|
elsif( $e eq 'js' )
|
||||||
|
{
|
||||||
|
$t = 'application/javascript';
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$t = 'application/octet-stream';
|
$t = 'application/octet-stream';
|
||||||
}
|
}
|
||||||
print <<EOF;
|
print <<EOF2;
|
||||||
HTTP/1.0 200 OK
|
HTTP/1.0 200 OK
|
||||||
Content-Type: $t
|
Content-Type: $t
|
||||||
$server_id
|
$server_id
|
||||||
|
|
||||||
EOF
|
EOF2
|
||||||
while( <FILE> )
|
binmode( FILE );
|
||||||
|
while( read( FILE, $buf, 8192 ) )
|
||||||
{
|
{
|
||||||
print;
|
print $buf;
|
||||||
}
|
}
|
||||||
|
close( FILE );
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
static/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
static/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 973 B |
BIN
static/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
static/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
17
static/icon-configh.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M42 16h16l3 13 12-7 8 14-10 8 10 8-8 14-12-7-3 13H42l-3-13-12 7-8-14 10-8-10-8 8-14 12 7z"/>
|
||||||
|
<circle cx="50" cy="50" r="12"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 704 B |
19
static/icon-devices.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M32 18v23M68 18v23"/>
|
||||||
|
<path d="M26 41h48v13a24 24 0 0 1-48 0z"/>
|
||||||
|
<path d="M50 78v10M36 88h28"/>
|
||||||
|
<path d="M38 54h24"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 696 B |
18
static/icon-dirs.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M18 34h23l6 8h35v32a6 6 0 0 1-6 6H24a6 6 0 0 1-6-6z"/>
|
||||||
|
<path d="M18 34v-7a5 5 0 0 1 5-5h19l7 8h28a5 5 0 0 1 5 5v7"/>
|
||||||
|
<path d="M27 57h46"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 717 B |
18
static/icon-general.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M50 20v10M50 70v10M20 50h10M70 50h10M29 29l7 7M64 64l7 7M71 29l-7 7M36 64l-7 7"/>
|
||||||
|
<circle cx="50" cy="50" r="14"/>
|
||||||
|
<circle cx="50" cy="50" r="5" fill="currentColor" stroke="none"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 759 B |
18
static/icon-groups.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M24 29h27l8 8h17v34a6 6 0 0 1-6 6H24z"/>
|
||||||
|
<path d="M31 50h38M31 61h28"/>
|
||||||
|
<circle cx="72" cy="27" r="9"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 682 B |
19
static/icon-logging.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M28 16h34l10 10v58H28z"/>
|
||||||
|
<path d="M62 16v14h14"/>
|
||||||
|
<path d="M37 43h26M37 55h26M37 67h18"/>
|
||||||
|
<path d="M70 58l9 9-17 17-10 2 2-10z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 709 B |
20
static/icon-queues.svg
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M30 20h40v20H30z"/>
|
||||||
|
<path d="M22 40h56a8 8 0 0 1 8 8v22H14V48a8 8 0 0 1 8-8z"/>
|
||||||
|
<path d="M30 62h40v22H30z"/>
|
||||||
|
<path d="M38 70h24"/>
|
||||||
|
<circle cx="72" cy="51" r="3" fill="currentColor" stroke="none"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 775 B |
17
static/icon-security.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M50 14l30 12v20c0 20-12 33-30 40-18-7-30-20-30-40V26z"/>
|
||||||
|
<path d="M37 51l9 9 18-23"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 664 B |
23
static/icon-service.svg
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
.icon-fill{fill:#ad1d1c;stroke:none}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape">
|
||||||
|
<path d="M30 24h40a6 6 0 0 1 6 6v12H24V30a6 6 0 0 1 6-6z"/>
|
||||||
|
<path d="M24 42h52v28a6 6 0 0 1-6 6H30a6 6 0 0 1-6-6z"/>
|
||||||
|
<path d="M34 34h2M45 34h2M56 34h2"/>
|
||||||
|
<path d="M37 58h26"/>
|
||||||
|
<path d="M50 49v18"/>
|
||||||
|
<path d="M68 54l7 7-7 7"/>
|
||||||
|
<path d="M32 54l-7 7 7 7"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 843 B |
18
static/icon-smart.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M34 63c-12-10-11-29 4-39 13-9 34-2 37 15 3 19-16 29-25 40"/>
|
||||||
|
<path d="M36 63h28M39 74h22"/>
|
||||||
|
<path d="M41 39c6-7 15-7 21 0M43 50c5 4 10 4 15 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 723 B |
22
static/icon-start.svg
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
.icon-fill{fill:#ad1d1c;stroke:none}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape">
|
||||||
|
<path d="M24 62c0-20 14-38 34-43"/>
|
||||||
|
<path d="M58 19l-6 13 15-4"/>
|
||||||
|
<path d="M28 70h44"/>
|
||||||
|
<path d="M35 80h30"/>
|
||||||
|
<path d="M50 38v18"/>
|
||||||
|
<path d="M41 47l9 9 13-17"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 751 B |
19
static/icon-stations.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<circle cx="50" cy="50" r="8"/>
|
||||||
|
<path d="M35 35a22 22 0 0 0 0 30M65 35a22 22 0 0 1 0 30"/>
|
||||||
|
<path d="M25 25a36 36 0 0 0 0 50M75 25a36 36 0 0 1 0 50"/>
|
||||||
|
<path d="M50 58v22"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 741 B |
18
static/icon-susers.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<circle cx="50" cy="32" r="14"/>
|
||||||
|
<path d="M25 78c3-18 14-28 25-28s22 10 25 28z"/>
|
||||||
|
<path d="M34 78h32"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 673 B |
19
static/icon-users.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<circle cx="38" cy="34" r="12"/>
|
||||||
|
<circle cx="65" cy="36" r="10"/>
|
||||||
|
<path d="M16 78c3-17 12-26 23-26s20 9 23 26z"/>
|
||||||
|
<path d="M52 78c2-12 9-20 18-20 7 0 13 5 16 20z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 734 B |
19
static/icon-volumes.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 100 100" role="img" aria-hidden="true">
|
||||||
|
<style>
|
||||||
|
.icon-bg{fill:url(#g);stroke:#d6bf9c;stroke-width:1.8}
|
||||||
|
.icon-shape{fill:none;stroke:#ad1d1c;stroke-width:5.2;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#fffaf2"/>
|
||||||
|
<stop offset="1" stop-color="#ead9bd"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect class="icon-bg" x="6" y="6" width="88" height="88" rx="20"/>
|
||||||
|
<g class="icon-shape" color="#ad1d1c">
|
||||||
|
<path d="M25 18h50v64H25z"/>
|
||||||
|
<path d="M34 18v24h32V18"/>
|
||||||
|
<path d="M36 64h28"/>
|
||||||
|
<circle cx="66" cy="70" r="4" fill="currentColor" stroke="none"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 714 B |
995
static/menu.html
BIN
static/smart.jpg
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 4.6 KiB |
@@ -1,44 +0,0 @@
|
|||||||
<HTML>
|
|
||||||
<BODY BGCOLOR=#F0F0FF>
|
|
||||||
|
|
||||||
<TABLE BORDER=0 CELLSPACING=0 WIDTH=100%>
|
|
||||||
<TR BGCOLOR=#C0C0FF>
|
|
||||||
<TD>
|
|
||||||
<B><FONT SIZE=+2>SMArT v0.99 (Perl)</FONT></B>
|
|
||||||
</TD>
|
|
||||||
<TD ALIGN=RIGHT>
|
|
||||||
<IMG SRC="/static/smart_icon.jpg"><BR>
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
<TR BGCOLOR=#D0D0FF>
|
|
||||||
<TD COLSPAN=2>
|
|
||||||
Welcome to SMArT, the easy way to configure mars_nwe, <B>Mar</B>tin <B>S</B>tover's <B>N</B>et<B>W</B>are <B>e</B>mulator.
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
<TR BGCOLOR=#E0E0FF>
|
|
||||||
<TD COLSPAN=2>
|
|
||||||
To apply the changes you have made to the configuration, you have to restart <TT>MARS_NWE</TT>
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
<TR BGCOLOR=#E0E0FF>
|
|
||||||
<TH ALIGN=CENTER>
|
|
||||||
<A HREF="/cgi-bin/control?start">Start <TT>MARS_NWE</TT>
|
|
||||||
</TH>
|
|
||||||
<TH ALIGN=CENTER>
|
|
||||||
<A HREF="/cgi-bin/control?stop">Stop <TT>MARS_NWE</TT>
|
|
||||||
</TH>
|
|
||||||
</TR>
|
|
||||||
<TR BGCOLOR=#E0E0FF>
|
|
||||||
<TD COLSPAN=2 ALIGN=CENTER>
|
|
||||||
<BR>The newest version of SMArT can be downloaded from <A HREF="http://www.lintux.cx/" TARGET="_parent">my website</A>.
|
|
||||||
</TD>
|
|
||||||
</TR>
|
|
||||||
<TR BGCOLOR=#E0E0FF>
|
|
||||||
<TH COLSPAN=2>
|
|
||||||
<BR>© Copyright 2001 <A HREF="mailto:lintux@lintux.cx">Wilmer van der Gaast</A>
|
|
||||||
</TH>
|
|
||||||
</TR>
|
|
||||||
</TABLE>
|
|
||||||
|
|
||||||
</BODY>
|
|
||||||
</HTML>
|
|
||||||
152
static/start.html.cmake
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>SMArT</TITLE>
|
||||||
|
<META CHARSET="utf-8">
|
||||||
|
<META NAME="viewport" CONTENT="width=device-width, initial-scale=1">
|
||||||
|
<LINK REL="icon" HREF="/static/favicon.ico" TYPE="image/x-icon">
|
||||||
|
<LINK REL="icon" TYPE="image/png" SIZES="32x32" HREF="/static/favicon-32x32.png">
|
||||||
|
<LINK REL="icon" TYPE="image/png" SIZES="16x16" HREF="/static/favicon-16x16.png">
|
||||||
|
<LINK REL="apple-touch-icon" HREF="/static/apple-touch-icon.png">
|
||||||
|
<STYLE TYPE="text/css">
|
||||||
|
BODY {
|
||||||
|
margin: 0;
|
||||||
|
padding: 22px;
|
||||||
|
background: #f6f1ea;
|
||||||
|
color: #342a25;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
A { color: #9f1f1f; text-decoration: none; }
|
||||||
|
A:hover { text-decoration: underline; }
|
||||||
|
.wrapper {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #fffdfa;
|
||||||
|
border: 1px solid #e5d6c6;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10px 30px rgba(60, 30, 10, 0.10);
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
padding: 22px 24px;
|
||||||
|
background: linear-gradient(135deg, #7d1018, #b82929 60%, #d7b169);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
.hero TABLE { width: 100%; border-collapse: collapse; }
|
||||||
|
.hero-title {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.hero-sub {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #f8e6c8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.hero IMG { max-height: 56px; }
|
||||||
|
.body {
|
||||||
|
padding: 22px;
|
||||||
|
}
|
||||||
|
.notice {
|
||||||
|
padding: 14px 16px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
border: 1px solid #ecdcc8;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fbf6ef;
|
||||||
|
}
|
||||||
|
.actions TABLE { width: 100%; border-collapse: separate; border-spacing: 12px; }
|
||||||
|
.action {
|
||||||
|
display: block;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
background: #9f1f1f;
|
||||||
|
}
|
||||||
|
.action.secondary {
|
||||||
|
background: #6f5b4f;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
padding-top: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6c5b52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.runtime-info{margin:18px 12px 0;border:1px solid #ddcfba;border-radius:16px;overflow:hidden;background:#fbf7f1;box-shadow:0 6px 18px rgba(80,55,30,.06)}.runtime-info-title{background:#d7c0a0;padding:10px 14px;font-weight:bold;color:#2e261d}.runtime-info-row{display:grid;grid-template-columns:190px minmax(0,1fr);gap:12px;padding:8px 14px;border-top:1px solid #eadfce;align-items:center}.runtime-info-row code{color:#5b4b38;white-space:normal;word-break:break-word}.runtime-info-note{padding:10px 14px;border-top:1px solid #eadfce;color:#6b5b50;font-size:13px;line-height:1.35}
|
||||||
|
|
||||||
|
.project-footer{margin:16px auto 0;text-align:center;color:#6b5b50;font-size:14px;line-height:1.45;max-width:760px}.project-footer a{color:#9f2f26;text-decoration:none}.project-footer a:hover{text-decoration:underline}.copyright{margin:10px auto 0;text-align:center;color:#6b5b50;font-size:13px;max-width:760px}.copyright a{color:#9f2f26;text-decoration:none}.copyright a:hover{text-decoration:underline}
|
||||||
|
</STYLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
<DIV CLASS="wrapper">
|
||||||
|
<DIV CLASS="hero">
|
||||||
|
<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
|
||||||
|
<TR>
|
||||||
|
<TD>
|
||||||
|
<DIV CLASS="hero-title">SMArT v@MARS_NWE_VERSION@</DIV>
|
||||||
|
<DIV CLASS="hero-sub">Perl interface for configuring MARS_NWE</DIV>
|
||||||
|
</TD>
|
||||||
|
<TD ALIGN="RIGHT">
|
||||||
|
<IMG SRC="/static/smart_icon.jpg" ALT="SMArT">
|
||||||
|
</TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="body">
|
||||||
|
<DIV CLASS="notice">
|
||||||
|
Welcome to SMArT, the easy way to configure <B>MARS_NWE</B>.
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="notice">
|
||||||
|
To apply changes to the configuration, restart <TT>MARS_NWE</TT> after saving your settings.
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="actions">
|
||||||
|
<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
|
||||||
|
<TR>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<DIV CLASS="runtime-info">
|
||||||
|
<DIV CLASS="runtime-info-title">MARS_NWE runtime information</DIV>
|
||||||
|
<DIV CLASS="runtime-info-row">
|
||||||
|
<DIV>Configuration file</DIV>
|
||||||
|
<DIV><CODE>@MARS_NWE_INSTALL_FULL_CONFDIR@/nwserv.conf</CODE></DIV>
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="runtime-info-row">
|
||||||
|
<DIV>SMArT configuration</DIV>
|
||||||
|
<DIV><CODE>@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf</CODE></DIV>
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="runtime-info-row">
|
||||||
|
<DIV>WebUI scripts</DIV>
|
||||||
|
<DIV><CODE>@MARS_NWE_INSTALL_FULL_LIBEXECDIR@</CODE></DIV>
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="runtime-info-row">
|
||||||
|
<DIV>MARS_NWE service</DIV>
|
||||||
|
<DIV><CODE>mars-nwe-serv.service</CODE></DIV>
|
||||||
|
</DIV>
|
||||||
|
<DIV CLASS="runtime-info-note">
|
||||||
|
User, group and queue operations need a running <B>MARS_NWE</B> service and a reachable bindery.
|
||||||
|
Configuration changes require write access to the SMArT configuration directory.
|
||||||
|
</DIV>
|
||||||
|
</DIV>
|
||||||
|
|
||||||
|
<DIV CLASS="footer">
|
||||||
|
<DIV CLASS="project-footer">
|
||||||
|
<DIV>SMArT is shipped as part of the <B>MARS_NWE</B> package.</DIV>
|
||||||
|
<DIV>Project sources are available from the <A HREF="https://gitea.disconnected-by-peer.at/mars_nwe/mars-nwe" TARGET="_blank">MARS_NWE repository</A>.</DIV>
|
||||||
|
</DIV><BR><BR>
|
||||||
|
<DIV CLASS="copyright">© Copyright 2026 <A HREF="mailto:mario.fetka@disconnected-by-peer.at">Mario Fetka</A></DIV>
|
||||||
|
</DIV>
|
||||||
|
</DIV>
|
||||||
|
</DIV>
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
||||||