diff options
| author | auric <auric@japegames.com> | 2026-02-21 15:11:51 -0600 |
|---|---|---|
| committer | auric <auric@japegames.com> | 2026-02-21 15:11:51 -0600 |
| commit | 52f92ea70f74008d82d21fef5085fb7380314ea1 (patch) | |
| tree | 357ec1f0ec75779fc945d3b7460e976fe677ae31 /clients/umbrella-cli | |
| parent | af012ffe7594350021741c62bd1205b65dfec07f (diff) | |
| parent | fc10d8a0818bb87001a64a72552ed28fe60931ee (diff) | |
Merge pull request #2 from ihateamongus/claude/trusting-dirac
State probing overhaul, A2S queries, tail/broadcast, bot audit log
Diffstat (limited to 'clients/umbrella-cli')
| -rw-r--r-- | clients/umbrella-cli/main.c | 145 |
1 files changed, 122 insertions, 23 deletions
diff --git a/clients/umbrella-cli/main.c b/clients/umbrella-cli/main.c index 69357e4..8562e92 100644 --- a/clients/umbrella-cli/main.c +++ b/clients/umbrella-cli/main.c @@ -4,9 +4,11 @@ * Usage: * umbrella-cli list * umbrella-cli status <unit> - * umbrella-cli attach <unit> # interactive console session - * umbrella-cli input <unit> <cmd> # send a single command + * umbrella-cli tail <unit> # print recent output, then exit + * umbrella-cli attach <unit> # interactive console session + * umbrella-cli input <unit> <cmd> # send a single command * umbrella-cli action <unit> <action> + * umbrella-cli broadcast <message> */ #include <stdio.h> @@ -124,30 +126,44 @@ static int cmd_list(int fd) { char buf[PROTO_MAX]; if (recv_msg(fd, buf, sizeof(buf)) <= 0) return 1; - /* Simple display: find all "name"/"display"/"state" triplets */ - printf("%-24s %-32s %s\n", "NAME", "DISPLAY", "STATE"); - printf("%-24s %-32s %s\n", + printf("%-24s %-32s %-14s %-8s %s\n", + "NAME", "DISPLAY", "STATE", "PLAYERS", "MAP"); + printf("%-24s %-32s %-14s %-8s %s\n", "────────────────────────", "────────────────────────────────", - "───────"); + "──────────────", + "────────", + "───────────────────"); - /* Walk the units array manually */ const char *p = buf; while ((p = strstr(p, "\"name\":")) != NULL) { char name[64] = {0}, display[128] = {0}, state[32] = {0}; + char players_s[16] = {0}, max_players_s[16] = {0}, map[64] = {0}; - /* Extract from this position forward */ char snippet[512]; strncpy(snippet, p, sizeof(snippet) - 1); - jget(snippet, "name", name, sizeof(name)); - jget(snippet, "display", display, sizeof(display)); - jget(snippet, "state", state, sizeof(state)); + jget(snippet, "name", name, sizeof(name)); + jget(snippet, "display", display, sizeof(display)); + jget(snippet, "state", state, sizeof(state)); + jget(snippet, "players", players_s, sizeof(players_s)); + jget(snippet, "max_players", max_players_s, sizeof(max_players_s)); + jget(snippet, "map", map, sizeof(map)); - if (name[0]) - printf("%-24s %-32s %s\n", name, display, state); + if (!name[0]) { p++; continue; } - p++; /* advance past current match */ + /* Format player count as "N/M" or "-" */ + char players_fmt[16] = "-"; + int pl = players_s[0] ? atoi(players_s) : -1; + int mx = max_players_s[0] ? atoi(max_players_s) : -1; + if (pl >= 0 && mx >= 0) + snprintf(players_fmt, sizeof(players_fmt), "%d/%d", pl, mx); + + printf("%-24s %-32s %-14s %-8s %s\n", + name, display, state, players_fmt, + map[0] ? map : "-"); + + p++; } return 0; } @@ -171,15 +187,88 @@ static int cmd_status(int fd, const char *unit) { } char display[128] = {0}, state[32] = {0}, pid[16] = {0}; - jget(buf, "display", display, sizeof(display)); - jget(buf, "state", state, sizeof(state)); - jget(buf, "pid", pid, sizeof(pid)); + char players_s[16] = {0}, max_players_s[16] = {0}, map[64] = {0}; + jget(buf, "display", display, sizeof(display)); + jget(buf, "state", state, sizeof(state)); + jget(buf, "pid", pid, sizeof(pid)); + jget(buf, "players", players_s, sizeof(players_s)); + jget(buf, "max_players", max_players_s, sizeof(max_players_s)); + jget(buf, "map", map, sizeof(map)); printf("Unit : %s\n", unit); printf("Display : %s\n", display); printf("State : %s\n", state); if (pid[0] && strcmp(pid, "0") != 0) printf("PID : %s\n", pid); + + int pl = players_s[0] ? atoi(players_s) : -1; + int mx = max_players_s[0] ? atoi(max_players_s) : -1; + if (pl >= 0 && mx >= 0) + printf("Players : %d/%d\n", pl, mx); + if (map[0]) + printf("Map : %s\n", map); + + return 0; +} + +static int cmd_tail(int fd, const char *unit) { + char msg[256]; + snprintf(msg, sizeof(msg), + "{\"cmd\":\"tail\",\"unit\":\"%s\"}", unit); + if (send_msg(fd, msg) != 0) return 1; + + char buf[PROTO_MAX]; + if (recv_msg(fd, buf, sizeof(buf)) <= 0) return 1; + + char type[32] = {0}; + jget(buf, "type", type, sizeof(type)); + if (strcmp(type, "error") == 0) { + char errmsg[256] = {0}; + jget(buf, "message", errmsg, sizeof(errmsg)); + fprintf(stderr, "Error: %s\n", errmsg); + return 1; + } + + char data[PROTO_MAX] = {0}; + jget(buf, "data", data, sizeof(data)); + if (data[0]) + printf("%s", data); + return 0; +} + +static int cmd_broadcast(int fd, const char *message) { + char msg[1024]; + /* Simple JSON escaping for the message */ + char escaped[900] = {0}; + int j = 0; + for (int i = 0; message[i] && j < (int)sizeof(escaped) - 2; i++) { + if (message[i] == '"') { escaped[j++] = '\\'; escaped[j++] = '"'; } + else if (message[i] == '\\') { escaped[j++] = '\\'; escaped[j++] = '\\'; } + else escaped[j++] = message[i]; + } + escaped[j] = '\0'; + + snprintf(msg, sizeof(msg), "{\"cmd\":\"broadcast\",\"message\":\"%s\"}", escaped); + if (send_msg(fd, msg) != 0) return 1; + + char buf[PROTO_MAX]; + if (recv_msg(fd, buf, sizeof(buf)) <= 0) return 1; + + char type[32] = {0}; + jget(buf, "type", type, sizeof(type)); + if (strcmp(type, "error") == 0) { + char errmsg[256] = {0}; + jget(buf, "message", errmsg, sizeof(errmsg)); + fprintf(stderr, "Error: %s\n", errmsg); + return 1; + } + + char sent_s[16] = {0}, failed_s[16] = {0}; + jget(buf, "sent", sent_s, sizeof(sent_s)); + jget(buf, "failed", failed_s, sizeof(failed_s)); + printf("Broadcast sent to %s unit(s), %s failed\n", + sent_s[0] ? sent_s : "0", + failed_s[0] ? failed_s : "0"); return 0; } @@ -363,17 +452,21 @@ static void usage(void) { "Usage: umbrella-cli <command> [args]\n" "\n" "Commands:\n" - " list List all units\n" - " status <unit> Show unit status\n" - " attach <unit> Interactive console session\n" - " input <unit> <cmd> Send a single command\n" - " action <unit> <action> Run a named action\n" + " list List all units\n" + " status <unit> Show unit status\n" + " tail <unit> Print recent output and exit\n" + " attach <unit> Interactive console session\n" + " input <unit> <cmd> Send a single command\n" + " action <unit> <action> Run a named action\n" + " broadcast <message> Send message to all running units\n" "\n" "Examples:\n" " umbrella-cli list\n" + " umbrella-cli tail tf2-novemen\n" " umbrella-cli attach tf2-novemen\n" " umbrella-cli input tf2-novemen \"say Server restarting soon\"\n" - " umbrella-cli action tf2-novemen update\n"); + " umbrella-cli action tf2-novemen update\n" + " umbrella-cli broadcast \"Server restart in 5 minutes\"\n"); } int main(int argc, char *argv[]) { @@ -390,6 +483,9 @@ int main(int argc, char *argv[]) { } else if (strcmp(cmd, "status") == 0) { if (argc < 3) { fprintf(stderr, "status: need unit name\n"); ret = 1; } else ret = cmd_status(fd, argv[2]); + } else if (strcmp(cmd, "tail") == 0) { + if (argc < 3) { fprintf(stderr, "tail: need unit name\n"); ret = 1; } + else ret = cmd_tail(fd, argv[2]); } else if (strcmp(cmd, "attach") == 0) { if (argc < 3) { fprintf(stderr, "attach: need unit name\n"); ret = 1; } else ret = cmd_attach(fd, argv[2]); @@ -399,6 +495,9 @@ int main(int argc, char *argv[]) { } else if (strcmp(cmd, "action") == 0) { if (argc < 4) { fprintf(stderr, "action: need unit and action name\n"); ret = 1; } else ret = cmd_action(fd, argv[2], argv[3]); + } else if (strcmp(cmd, "broadcast") == 0) { + if (argc < 3) { fprintf(stderr, "broadcast: need message\n"); ret = 1; } + else ret = cmd_broadcast(fd, argv[2]); } else { fprintf(stderr, "Unknown command: %s\n\n", cmd); usage(); |
