1. Home
  2. /
  3. RMMmax Agent Service
  4. /
  5. How It Works
  6. /
  7. Linux Agent Service Opera...

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.

Still stuck? Contact

How can we help?