1. Home
  2. RMMmax Agent Service
  3. How It Works
  4. Linux Agent Service Operation

Linux Agent Service Operation

RMMMax Linux Agent Service

**Version:** 1.0.0  

**File:** `rmmmax_agent.py`  

**Installed to:** `/var/rmmmax/agentservice/`  

**Command:** `rmmmax-agent`

—

Overview

The RMMMax Linux Agent Service is a lightweight Python 3 daemon that connects a Linux machine to the RMMMax platform. It runs as a background systemd service, checks in with the RMMMax API every five minutes, receives commands dispatched from the platform, executes them, and reports results back. It requires no external Python packages — only the Python 3 standard library.

It is the Linux equivalent of the Windows RMMMax Agent Service and communicates with the same backend API endpoints.

—

How It Works

1. Registration

Before the service can operate, the machine must be registered with RMMMax using:

bash rmmmax-agent register

During registration the agent:

1. Collects local system information — hostname, IP address, OS name and version (read from `/etc/os-release`), current user, and UTC offset.

2. Posts this data along with the operator’s RMMMax username and password to `POST /api/v1/agentservice/register/`.

3. The server authenticates the credentials, enforces the plan’s agent limit, auto-creates the Client record if it does not exist, and returns a unique `computerID` (UUID) and a `token` (64-character hex string).

4. Both values are saved immediately to `/var/rmmmax/agentservice/config.json` with permissions set to `root:root 600` so no other user or process can read the token.

5. The server automatically queues a `management_package` command so the management scripts (`lum.sh`, `upgrades.sh`) are deployed to the agent on its first check-in.

The agent is considered registered when `config.json` contains a non-empty `computerID` and `token`.

—

2. The Check-In Loop

Once the service is started (`systemctl start rmmmax-agent`), it enters a continuous check-in cycle:

“`

Service starts

│

├── Check-in immediately on startup

│     POST /api/v1/agentservice/checkin/

│     ↓

│     Server returns: newToken + command (or “sleep”)

│     ↓

│     New token saved to config.json immediately

│     ↓

│     If command received → execute it → loop back with result

│     If “sleep” → wait 5 minutes → check-in again

│

└── Repeat every 5 minutes indefinitely

“`

Every check-in payload includes the current token, hostname, IP, OS, username, UTC offset, client name, location name, and agent version. This keeps the server’s agent record up to date on every cycle.

**Token rotation** occurs on every single check-in. The server issues a new token with each response and stores the old one as a `previous_token` for one-shot recovery. The agent saves the new token to disk before doing anything else, so a crash between receiving the response and the next check-in does not permanently break the authentication chain.

—

3. Command Execution

When the server has work queued, it returns a command object with three fields: `id`, `commandType`, and `payload.script`. The agent executes the script and reports the result (`exitCode`, `stdout`, `stderr`) on the immediately following check-in.

There are two execution modes:

#### Mode A — WORK= commands (management scripts)

When `payload.script` starts with `WORK=`, the agent routes the command to one of two dedicated shell handlers installed at `/var/rmmmax/agentservice/`:

| `commandType` contains | Handler called |

|—|—|

| `lum` | `lum.sh` — Linux Update Manager (apt / yum / zypper) |

| `update` or `upgrade` | `upgrades.sh` — Cross-platform Update Manager |

The handler is called with the full `WORK=` string as its argument, which it decodes to retrieve the auth token, computer ID, API base URL, command name, and any command-specific data. The handler posts its own results directly to the respective tool endpoint (e.g. `/api/v1/lum/post_lum_data_from_agent/`).

#### Mode B — Raw bash scripts

For all other command types, the agent:

1. Writes the script to a uniquely-named temporary file at `/tmp/rmmmax_cmd_<uuid>.sh`.

2. Prepends `#!/bin/bash` and `set -euo pipefail`.

3. Sets permissions to `700` (root-only execute).

4. Runs it with `/bin/bash` using `subprocess.run()`, capturing both stdout and stderr.

5. Deletes the temp file whether the script succeeded or failed.

6. Returns `{exitCode, stdout, stderr}` to the server on the next check-in.

Scripts have a **10-minute execution timeout**. If a script runs longer, it is terminated and the result `{exitCode: -1, error: “Script execution timed out”}` is reported.

A threading lock (`_busy_lock`) prevents two command executions from overlapping. If a command is still running when the 5-minute timer fires again, that check-in cycle is skipped and logged as a warning.

—

4. Failure Handling

The agent is designed to keep running through failures rather than crash.

| Scenario | Behaviour |

|—|—|

| Network unreachable / timeout | HTTP call returns `None`; current cycle is abandoned; retries at next 5-minute tick |

| HTTP 4xx / 5xx errors | Logged as a warning; cycle abandoned; retries at next tick |

| Response JSON parse failure | Logged as a warning; cycle abandoned; retries at next tick |

| Token missing from response | Logged as warning; cycle abandoned; retries at next tick |

| Script execution timeout | Script process killed after 10 minutes; error result reported to server |

| Config write failure | Logged as an error; not fatal — service continues with in-memory config |

| Stale token (missed response) | Server recognises `previous_token`; performs a fresh rotation automatically; no manual intervention needed |

| Service crash (any reason) | systemd restarts the service after 30 seconds (`Restart=always`) |

| Machine rebooted | systemd auto-starts the service on every boot (`WantedBy=multi-user.target`, enabled on install) |

—

5. Configuration File

All persistent identity data is stored at:

“`

/var/rmmmax/agentservice/config.json

“`

| Field | Description |

|—|—|

| `computerID` | UUID assigned by the server at registration |

| `token` | Current rotating authentication token (64-char hex) |

| `clientID` | UUID of the associated client on the server |

| `clientName` | Client organisation name sent on every check-in |

| `locationName` | Location/site name sent on every check-in |

| `apiBaseUrl` | API endpoint base (default: `https://api.rmmmax.com/api/v1/agentservice`) |

| `agentVersion` | Agent version string reported to the server |

The file is written **atomically** — the agent writes to `config.json.tmp` then calls `os.replace()` to swap it in. This means a crash or power failure mid-write can never produce a corrupt config file.

File permissions: `600` (readable and writable by root only).

—

6. Logging

Log file: `/var/rmmmax/agentservice/activity.log`

Every log entry is timestamped and tagged with a severity level:

“`

2026-04-17 09:15:02 [INFO] RMMMax Agent Service v1.0.0 starting (PID 1234) …

2026-04-17 09:15:03 [INFO] Check-in OK — no pending commands.

2026-04-17 09:20:03 [INFO] Received command #42 type=’lum_scan’

2026-04-17 09:20:07 [INFO] Command #42 finished: exitCode=0

“`

Log management rules:

– **Rotation:** When `activity.log` reaches 500 KB it is renamed to `activity.log.1` and a fresh log is started. Only one backup is kept.

– **Daily truncation:** At midnight the log is cleared and the backup is removed, preventing unbounded growth.

– All log entries are also written to **stdout**, which systemd captures in the system journal (`journalctl -u rmmmax-agent`).

—

7. CLI Management Commands

All commands require root or `sudo`. The service does not need to be stopped to run management commands; config changes take effect on the next check-in.

| Command | What it does |

|—|—|

| `rmmmax-agent register` | Register this machine for the first time |

| `rmmmax-agent reregister` | Re-authenticate using credentials without losing the existing `computerID` or historical data. Used after a token is lost (e.g. config file deleted). |

| `rmmmax-agent update –client-name “X” –location-name “Y”` | Change the client name and/or location stored locally. The change reaches the server on the next check-in. |

| `rmmmax-agent status` | Print registration state and the 20 most recent log lines. |

| `rmmmax-agent deregister` | Remove the agent from the RMMMax server and wipe the local config. Requires interactive confirmation. |

| `rmmmax-agent run` | Start the service loop (called internally by systemd — not for interactive use). |

—

8. systemd Integration

The service unit file (`/etc/systemd/system/rmmmax-agent.service`) configures the following behaviour:

| Setting | Value | Effect |

|—|—|—|

| `After=network-online.target` | — | Waits for full network connectivity before starting, preventing failed check-ins immediately after boot |

| `Restart=always` | — | Restarts after any exit — crash, signal, or non-zero exit code |

| `RestartSec=30` | 30 seconds | Delay before restart, giving the network time to recover |

| `KillMode=mixed` | — | Sends SIGTERM to the process group, cleaning up any running child scripts |

| `TimeoutStopSec=15` | 15 seconds | Allows in-progress scripts up to 15 seconds to finish before a forced kill |

| `WantedBy=multi-user.target` | — | Auto-starts on every normal system boot |

The agent handles `SIGTERM` and `SIGINT` gracefully — it sets an exit flag and waits for the current sleep interval to expire before shutting down cleanly.

—

9. File Layout

“`

/var/rmmmax/agentservice/

├── rmmmax_agent.py       ← the agent itself

├── config.json           ← registration state (root:root 600)

├── activity.log          ← current log

├── activity.log.1        ← previous log (rotation backup)

├── lum.sh                ← Linux Update Manager handler (deployed by server)

└── upgrades.sh           ← Cross-platform Update Manager handler (deployed by server)

/usr/local/bin/

└── rmmmax-agent          ← CLI wrapper (exec python3 …/rmmmax_agent.py “$@”)

/etc/systemd/system/

└── rmmmax-agent.service  ← systemd unit file

“`

—

10. Security Notes

– The config file (`config.json`) contains the authentication token and is restricted to `root:root 600`.

– Temporary script files are created in `/tmp` with permissions `700`, executed, then immediately deleted.

– The service runs as `root` only because management scripts (package installs, system updates) require root privileges.

– The authentication token rotates on every single check-in — a captured token is only valid until the agent next contacts the server (at most 5 minutes).

– Credentials (username and password) are used only during `register` and `reregister` — they are never stored on disk.

How can we help?