diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2014-01-27 15:04:15 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2014-01-27 15:20:37 +0100 |
commit | 9bacdadc14672ec31e42f3929560a583262bb892 (patch) | |
tree | 1d227896e71b1393791d5a6934aeaded5b8e4fe5 | |
download | mosh-cleaner-9bacdadc14672ec31e42f3929560a583262bb892.tar.xz mosh-cleaner-9bacdadc14672ec31e42f3929560a583262bb892.zip |
Initial commit.
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | README.md | 31 | ||||
-rw-r--r-- | clean-mosh.c | 116 |
3 files changed, 162 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b9509e1 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CFLAGS ?= -O3 -march=native -pipe -fomit-frame-pointer -fPIE -fstack-protector-all +LDFLAGS ?= -Wl,-z,now -Wl,-z,relro +PREFIX ?= /usr/local + +.PHONY: clean install + +all: clean-mosh + +clean: + rm -f clean-mosh + +install: + install -m 0100 -o 0 -g 0 -t $(PREFIX)/sbin/ clean-mosh + +clean-mosh: diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b9a9e6 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Mosh Cleaner + +Mosh cleaner kills stale mosh sessions, according to this algorithm: + + For each user: + If user only has one mosh session: + continue + For each mosh session other than the most recent of that user: + If session is currently connected: + continue + If session is older than kill-time: + kill session + +`kill-time` begins at 24 hours and splits in half each time, until a minimum of 1 hour. This amounts to the following semantics: + +* Most recent session: never killed +* Any currently connected session: never killed +* Second most recent session: killed if not used for 24 hours +* Third most recent session: killed if not used for 12 hours +* Forth most recent session: killed if not used for 6 hours +* Fifth most recent session: killed if not used for 3 hours +* Sixth most recent session: killed if not used for 1.5 hours +* Seventh most recent session: killed if not used for 1 hour +* Eighth most recent session: killed if not used for 1 hour +* Ninth most recent session: killed if not used for 1 hour + +### Usage + + $ make + $ sudo make install + $ sudo clean-mosh diff --git a/clean-mosh.c b/clean-mosh.c new file mode 100644 index 0000000..3a512c9 --- /dev/null +++ b/clean-mosh.c @@ -0,0 +1,116 @@ +#include <stdio.h> +#include <signal.h> +#include <time.h> +#include <utmp.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +struct mosh { + time_t last_used; + int has_ip; + int pid; + struct mosh *next; +}; + +struct user { + char username[UT_NAMESIZE]; + struct mosh *head; + struct user *next; +}; + +static inline struct user *new_user(char *username) +{ + struct user *user; + user = malloc(sizeof(struct user)); + if (!user) { + perror("user"); + exit(EXIT_FAILURE); + } + strcpy(user->username, username); + user->head = NULL; + user->next = NULL; + return user; +} + +#define ONE_HOUR (60 * 60) +#define ONE_DAY (ONE_HOUR * 24) + +int main(int argc, char *argv[]) +{ + FILE *utmp; + struct utmp entry; + struct user *user_head = NULL, *user; + struct mosh *parsed, *insert, **prev; + char *bracket_open, *bracket_close; + int kill_time; + + utmp = fopen("/var/run/utmp", "r"); + if (!utmp) { + perror("fopen"); + return EXIT_FAILURE; + } + while (fread(&entry, sizeof(entry), 1, utmp)) { + if (!strcmp(entry.ut_host, "mosh")) + continue; + bracket_open = strrchr(entry.ut_host, '['); + bracket_close = strrchr(entry.ut_host, ']'); + if (!bracket_open || !bracket_close || bracket_open >= bracket_close) + continue; + + parsed = malloc(sizeof(struct mosh)); + if (!parsed) { + perror("malloc"); + return EXIT_FAILURE; + } + parsed->next = NULL; + parsed->has_ip = strstr(entry.ut_host, "via mosh") != NULL; + *bracket_close = '\0'; + parsed->pid = atoi(bracket_open + 1); + parsed->last_used = entry.ut_tv.tv_sec; + + + if (!user_head) + user_head = user = new_user(entry.ut_user); + else { + for (user = user_head; user; user = user->next) { + if (!strcmp(entry.ut_user, user->username)) + break; + if (!user->next) { + user->next = new_user(entry.ut_user); + user = user->next; + break; + } + } + } + if (!user->head) + user->head = parsed; + else { + for (prev = &user->head, insert = user->head; insert; prev = &insert->next, insert = insert->next) { + if ((parsed->has_ip && !insert->has_ip) || parsed->last_used > insert->last_used) + break; + } + *prev = parsed; + parsed->next = insert; + } + } + fclose(utmp); + + for (user = user_head; user; user = user->next) { + if (!user->head->next) /* If the user only has one mosh process running, we don't kill anything. */ + continue; + for (parsed = user->head->next, kill_time = ONE_DAY; parsed; parsed = parsed->next, kill_time /= 2) { + if (kill_time < ONE_HOUR) + kill_time = ONE_HOUR; + if (parsed->has_ip) /* If the user is currently connected, we don't kill it. */ + continue; + if (time(NULL) - parsed->last_used < kill_time) /* If the connection is less than an hour old, we don't kill it. */ + continue; + printf("[+] Killing %s's mosh instance %d, last used on %s", user->username, parsed->pid, ctime(&parsed->last_used)); + if (kill(parsed->pid, SIGTERM)) + perror("kill"); + } + } + + return EXIT_SUCCESS; +} |