r22521 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r22520‎ | r22521 | r22522 >
Date:03:32, 29 May 2007
Author:river
Status:old
Tags:
Comment:
- watcherd: monitor MySQL server and notify users running long queries.
- move toolserver utils (whodo, acctexp, listlogins, lsexp) into a subdirectory
with a unified build system
Modified paths:
  • /trunk/tools/tsutils (added) (history)
  • /trunk/tools/tsutils/Makefile (added) (history)
  • /trunk/tools/tsutils/acctexp (added) (history)
  • /trunk/tools/tsutils/acctexp/Makefile (added) (history)
  • /trunk/tools/tsutils/acctexp/acctexp.c (added) (history)
  • /trunk/tools/tsutils/config.mk (added) (history)
  • /trunk/tools/tsutils/libtsutils (added) (history)
  • /trunk/tools/tsutils/libtsutils/Makefile (added) (history)
  • /trunk/tools/tsutils/libtsutils/mail.c (added) (history)
  • /trunk/tools/tsutils/libtsutils/sendmail.h (added) (history)
  • /trunk/tools/tsutils/libtsutils/subst.c (added) (history)
  • /trunk/tools/tsutils/libtsutils/subst.h (added) (history)
  • /trunk/tools/tsutils/libtsutils/tsutils.c (added) (history)
  • /trunk/tools/tsutils/libtsutils/tsutils.h (added) (history)
  • /trunk/tools/tsutils/libtsutils/write.c (added) (history)
  • /trunk/tools/tsutils/listlogins (added) (history)
  • /trunk/tools/tsutils/listlogins/Makefile (added) (history)
  • /trunk/tools/tsutils/listlogins/listlogins.c (added) (history)
  • /trunk/tools/tsutils/lsexp (added) (history)
  • /trunk/tools/tsutils/lsexp/Makefile (added) (history)
  • /trunk/tools/tsutils/lsexp/lsexp.c (added) (history)
  • /trunk/tools/tsutils/mk (added) (history)
  • /trunk/tools/tsutils/mk/lib.mk (added) (history)
  • /trunk/tools/tsutils/mk/prog.mk (added) (history)
  • /trunk/tools/tsutils/mk/rules.mk (added) (history)
  • /trunk/tools/tsutils/watcherd (added) (history)
  • /trunk/tools/tsutils/watcherd/Makefile (added) (history)
  • /trunk/tools/tsutils/watcherd/watcherd.c (added) (history)
  • /trunk/tools/tsutils/whodo (added) (history)
  • /trunk/tools/tsutils/whodo/Makefile (added) (history)
  • /trunk/tools/tsutils/whodo/whodo.c (added) (history)

Diff [purge]

Index: trunk/tools/tsutils/Makefile
@@ -0,0 +1,10 @@
 2+SUBDIRS = libtsutils watcherd acctexp listlogins lsexp whodo
 3+MAKEFLAGS = --no-print-directory
 4+
 5+all clean depend install lint:
 6+ @for d in $(SUBDIRS); do \
 7+ echo "$@ ==> $$d"; \
 8+ $(MAKE) -C $$d $@ || exit 1; \
 9+ echo "$@ <== $$d"; \
 10+ done
 11+
Index: trunk/tools/tsutils/libtsutils/Makefile
@@ -0,0 +1,5 @@
 2+LIB = tsutils
 3+SRCS = tsutils.c mail.c write.c subst.c
 4+OBJS = $(SRCS:.c=.o)
 5+
 6+include ../mk/lib.mk
Index: trunk/tools/tsutils/libtsutils/tsutils.h
@@ -0,0 +1,23 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef TSUTILS_H
 13+#define TSUTILS_H
 14+
 15+char *realloc_strcat(char *str, char const *add);
 16+char *realloc_strncat(char *str, char const *add, size_t len);
 17+void strdup_free(char **s, char const *n);
 18+int sendmail(char const *username, char const *message);
 19+void logmsg(char const *msg, ...);
 20+int daemon_detach(char const *progname);
 21+int get_user_tty(char const *);
 22+char *file_to_string(char const *);
 23+
 24+#endif /* !TSUTILS_H */
Index: trunk/tools/tsutils/libtsutils/subst.c
@@ -0,0 +1,138 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <stdlib.h>
 13+#include <string.h>
 14+#include <stdio.h>
 15+#include "tsutils.h"
 16+#include "subst.h"
 17+
 18+typedef struct subst_entry {
 19+ struct subst_entry *ss_next;
 20+ char *ss_var;
 21+ char *ss_val;
 22+} subst_entry;
 23+
 24+typedef struct subst_state {
 25+ subst_entry head;
 26+} subst_state;
 27+
 28+static char *find_value(subst_t, char const *, size_t);
 29+
 30+subst_t
 31+subst_new()
 32+{
 33+ return calloc(1, sizeof(subst_state));
 34+}
 35+
 36+void
 37+subst_free(s)
 38+ subst_t s;
 39+{
 40+subst_entry *ent, *next;
 41+ for (ent = s->head.ss_next, next = (ent ? ent->ss_next : NULL); ent;
 42+ ent = next, next = (ent ? ent->ss_next : NULL)) {
 43+ free(ent->ss_var);
 44+ free(ent->ss_val);
 45+ free(ent);
 46+ }
 47+ free(s);
 48+}
 49+
 50+void
 51+subst_add_var(s, var, val)
 52+ subst_t s;
 53+ char const *var, *val;
 54+{
 55+subst_entry *ent;
 56+ ent = calloc(1, sizeof(*ent));
 57+ if (ent == NULL)
 58+ return;
 59+
 60+ ent->ss_val = strdup(val);
 61+ if (ent->ss_val == NULL) {
 62+ free(ent);
 63+ return;
 64+ }
 65+
 66+ ent->ss_var = strdup(var);
 67+ if (ent->ss_var == NULL) {
 68+ free(ent->ss_val);
 69+ free(ent);
 70+ return;
 71+ }
 72+
 73+ ent->ss_next = s->head.ss_next;
 74+ s->head.ss_next = ent;
 75+ return;
 76+}
 77+
 78+char *
 79+subst_run(s, src)
 80+ subst_t s;
 81+ char const *src;
 82+{
 83+char const *p = src, *start, *end;
 84+char *result = strdup("");
 85+
 86+ while ((start = strchr(p, '%')) != NULL) {
 87+ char *value;
 88+
 89+ start++;
 90+ if ((end = strchr(start, '%')) == NULL)
 91+ break;
 92+
 93+ /*LINTED ptrdiff_t overflow */
 94+ result = realloc_strncat(result, p, start - p - 1);
 95+
 96+ p = end + 1;
 97+
 98+ /*LINTED ptrdiff_t overflow */
 99+ value = find_value(s, start, end - start);
 100+ if (value)
 101+ result = realloc_strcat(result, value);
 102+ }
 103+
 104+ result = realloc_strcat(result, p);
 105+ return result;
 106+}
 107+
 108+static char *
 109+find_value(s, var, len)
 110+ subst_t s;
 111+ char const *var;
 112+ size_t len;
 113+{
 114+subst_entry *e;
 115+ for (e = s->head.ss_next; e; e = e->ss_next)
 116+ if (strlen(e->ss_var) == len && !memcmp(e->ss_var, var, len))
 117+ return e->ss_val;
 118+ return NULL;
 119+}
 120+
 121+#ifdef TEST
 122+int
 123+main()
 124+{
 125+ char *r;
 126+ subst_t s = subst_new();
 127+ if (s == NULL)
 128+ return 1;
 129+
 130+ subst_add_var(s, "one", "1");
 131+ subst_add_var(s, "two", "2");
 132+ subst_add_var(s, "three", "3");
 133+
 134+ r = subst_run(s, "Testing %one%, %two% and %three...\n");
 135+ printf("[%s]\n", r);
 136+ subst_free(s);
 137+ return 0;
 138+}
 139+#endif /* TEST */
Index: trunk/tools/tsutils/libtsutils/sendmail.h
@@ -0,0 +1,17 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef TSUTILS_H
 13+#define TSUTILS_H
 14+
 15+char *realloc_strcat(char *str, char const *add);
 16+char *realloc_strncat(char *str, char const *add, size_t len);
 17+
 18+#endif /* !TSUTILS_H */
Index: trunk/tools/tsutils/libtsutils/mail.c
@@ -0,0 +1,82 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/types.h>
 13+#include <sys/wait.h>
 14+#include <string.h>
 15+#include <errno.h>
 16+#include <unistd.h>
 17+#include "tsutils.h"
 18+
 19+#define SENDMAIL "/usr/lib/sendmail"
 20+
 21+int
 22+sendmail(username, message)
 23+ char const *username, *message;
 24+{
 25+ char const *args[] = {
 26+ "/usr/lib/sendmail",
 27+ "-oi",
 28+ "-bm",
 29+ "--",
 30+ NULL,
 31+ NULL
 32+ };
 33+ int fds[2];
 34+ int status;
 35+
 36+ args[4] = username;
 37+
 38+ if (pipe(fds) == -1) {
 39+ logmsg("while sending mail: pipe: %s", strerror(errno));
 40+ return -1;
 41+ }
 42+
 43+ switch (fork()) {
 44+ case 0:
 45+ if (dup2(fds[0], 0) == -1) {
 46+ logmsg("mail child: dup2: %s", strerror(errno));
 47+ _exit(1);
 48+ }
 49+
 50+ (void) close(fds[0]);
 51+ (void) close(fds[1]);
 52+ (void) execv(SENDMAIL, (char *const *) args);
 53+ logmsg("mail child: execv: %s", strerror(errno));
 54+ _exit(1);
 55+
 56+ case -1:
 57+ logmsg("sending mail: fork: %s", strerror(errno));
 58+ return -1;
 59+
 60+ default:
 61+ (void) close(fds[0]);
 62+ (void) write(fds[1], message, strlen(message));
 63+ (void) close(fds[1]);
 64+ }
 65+
 66+ while (wait(&status) == -1)
 67+ if (errno != EINTR)
 68+ return -1;
 69+
 70+ if (WIFEXITED(status)) {
 71+ int ret = WEXITSTATUS(status);
 72+ if (ret != 0) {
 73+ logmsg("sending mail: child exited with status %d", ret);
 74+ return -1;
 75+ }
 76+ } else if (WIFSIGNALED(status)) {
 77+ logmsg("sending mail: child exited with signal %d",
 78+ WTERMSIG(status));
 79+ return -1;
 80+ }
 81+ return 0;
 82+}
 83+
Index: trunk/tools/tsutils/libtsutils/subst.h
@@ -0,0 +1,22 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef SUBST_H
 13+#define SUBST_H
 14+
 15+struct subst_state;
 16+typedef struct subst_state *subst_t;
 17+
 18+subst_t subst_new(void);
 19+void subst_free(subst_t);
 20+void subst_add_var(subst_t, char const *var, char const *val);
 21+char *subst_run(subst_t, char const *src);
 22+
 23+#endif /* !SUBST_H */
Index: trunk/tools/tsutils/libtsutils/tsutils.c
@@ -0,0 +1,116 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/mman.h>
 13+#include <sys/stat.h>
 14+#include <fcntl.h>
 15+#include <stdlib.h>
 16+#include <string.h>
 17+#include <stdarg.h>
 18+#include <syslog.h>
 19+#include <stdio.h>
 20+#include <unistd.h>
 21+#include "tsutils.h"
 22+
 23+char *
 24+realloc_strncat(str, add, len)
 25+ char *str;
 26+ char const *add;
 27+ size_t len;
 28+{
 29+size_t newlen;
 30+
 31+ newlen = len;
 32+ if (str)
 33+ newlen += strlen(str);
 34+ str = realloc(str, newlen + 1);
 35+ (void) strncat(str, add, len);
 36+ return str;
 37+}
 38+
 39+char *
 40+realloc_strcat(str, add)
 41+ char *str;
 42+ char const *add;
 43+{
 44+ return realloc_strncat(str, add, strlen(add));
 45+}
 46+
 47+void
 48+strdup_free(s, new)
 49+ char **s;
 50+ char const *new;
 51+{
 52+ if (*s)
 53+ free(*s);
 54+
 55+ *s = strdup(new);
 56+}
 57+
 58+static int foreground = 1;
 59+
 60+void
 61+logmsg(char const *msg, ...)
 62+{
 63+va_list ap;
 64+ va_start(ap, msg);
 65+
 66+ if (foreground) {
 67+ (void) vfprintf(stderr, msg, ap);
 68+ (void) fputs("\n", stderr);
 69+ } else
 70+ vsyslog(LOG_NOTICE, msg, ap);
 71+
 72+ va_end(ap);
 73+}
 74+
 75+int
 76+daemon_detach(progname)
 77+ char const *progname;
 78+{
 79+ if (daemon(0, 0) < 0)
 80+ return -1;
 81+ openlog(progname, LOG_PID, LOG_DAEMON);
 82+ foreground = 0;
 83+ return 0;
 84+}
 85+
 86+char *
 87+file_to_string(path)
 88+ char const *path;
 89+{
 90+int i;
 91+void *addr;
 92+struct stat st;
 93+char *str;
 94+
 95+ if ((i = open(path, O_RDONLY)) == -1) {
 96+ return NULL;
 97+ }
 98+
 99+ if (fstat(i, &st) == -1) {
 100+ (void) close(i);
 101+ return NULL;
 102+ }
 103+
 104+ if ((addr = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, i, 0)) == MAP_FAILED) {
 105+ (void) close(i);
 106+ return NULL;
 107+ }
 108+
 109+ str = malloc(st.st_size + 1);
 110+ (void) memcpy(str, addr, st.st_size);
 111+ str[st.st_size] = '\0';
 112+
 113+ (void) munmap(addr, st.st_size);
 114+ (void) close(i);
 115+
 116+ return str;
 117+}
Index: trunk/tools/tsutils/libtsutils/write.c
@@ -0,0 +1,72 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/types.h>
 13+#include <sys/stat.h>
 14+#include <string.h>
 15+#include <fcntl.h>
 16+#include <stdio.h>
 17+#include <utmpx.h>
 18+#include <unistd.h>
 19+
 20+int
 21+get_user_tty(user)
 22+ char const *user;
 23+{
 24+struct utmpx *ut;
 25+char ttydev[32];
 26+struct stat st;
 27+time_t idle = 0;
 28+int fd = -1;
 29+
 30+ setutxent();
 31+ while ((ut = getutxent()) != NULL) {
 32+ int tmp;
 33+ if (ut->ut_type != USER_PROCESS)
 34+ continue;
 35+
 36+ if (strcmp(ut->ut_user, user))
 37+ continue;
 38+
 39+ (void) snprintf(ttydev, sizeof(ttydev), "/dev/%s", ut->ut_line);
 40+ if ((tmp = open(ttydev, O_WRONLY)) == -1)
 41+ continue;
 42+
 43+ if (fstat(tmp, &st) == -1) {
 44+ (void) close(tmp);
 45+ continue;
 46+ }
 47+
 48+ if (st.st_atime > idle) {
 49+ fd = tmp;
 50+ idle = st.st_atime;
 51+ } else {
 52+ (void) close(tmp);
 53+ }
 54+ }
 55+
 56+ return fd;
 57+}
 58+
 59+#ifdef TEST
 60+int
 61+main() {
 62+ sleep(5);
 63+ int i = get_user_tty("river");
 64+ if (i == -1) {
 65+ perror("get_user_tty");
 66+ return 0;
 67+ }
 68+#define MESSAGE "\n\ntesting!\n\n"
 69+ write(i, MESSAGE, sizeof(MESSAGE) - 1);
 70+ close(i);
 71+ return 0;
 72+}
 73+#endif /* TEST */
Index: trunk/tools/tsutils/whodo/Makefile
@@ -0,0 +1,5 @@
 2+PROG = whodo
 3+SRCS = whodo.c
 4+OBJS = $(SRCS:.c=.o)
 5+
 6+include ../mk/prog.mk
Index: trunk/tools/tsutils/whodo/whodo.c
@@ -0,0 +1,221 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id: whodo.c 22465 2007-05-27 03:21:02Z river $ */
 11+
 12+/*
 13+ * whodo: report logged in users and their activities.
 14+ */
 15+
 16+#include <sys/types.h>
 17+#include <stdio.h>
 18+#include <stdlib.h>
 19+#include <string.h>
 20+#include <ctype.h>
 21+#include <unistd.h>
 22+#include <dirent.h>
 23+#include <utmpx.h>
 24+
 25+/*
 26+ * Process tree entry.
 27+ */
 28+typedef struct uproc {
 29+ char *pe_cmd;
 30+ char *pe_cmdline;
 31+ pid_t pe_pid;
 32+ struct uproc *pe_child; /* first child */
 33+ struct uproc *pe_next; /* root list next */
 34+ struct uproc *pe_sib; /* next sibling */
 35+} uproc_t;
 36+
 37+uproc_t head;
 38+
 39+void build_uproc_tree (void);
 40+void print_uproc_children (uproc_t *, int indent);
 41+void show_uproc (uproc_t *, int pad);
 42+void add_uproc (pid_t pid, pid_t ppid, char const *cmd, char const *cmdline);
 43+uproc_t *find_uproc (pid_t pid, int create);
 44+void make_safe (char *s);
 45+
 46+int
 47+main()
 48+{
 49+struct utmpx *ut;
 50+
 51+ build_uproc_tree();
 52+
 53+ setutxent();
 54+ while ((ut = getutxent()) != NULL) {
 55+ uproc_t *pp;
 56+
 57+ if (ut->ut_type != USER_PROCESS)
 58+ continue;
 59+
 60+ if ((pp = find_uproc(ut->ut_pid, 0)) != NULL) {
 61+ /*
 62+ * If we can't find ut_pid, we don't even print the
 63+ * utmp entry; it's probably stale.
 64+ */
 65+ (void) printf("%-8s %-16s\n", ut->ut_line, ut->ut_user);
 66+ show_uproc(pp, 0);
 67+ print_uproc_children(pp, 1);
 68+ (void) printf("\n");
 69+ }
 70+ }
 71+ endutxent();
 72+ return 0;
 73+}
 74+
 75+uproc_t *
 76+find_uproc(pid, create)
 77+ pid_t pid;
 78+ int create;
 79+{
 80+ uproc_t *pp;
 81+ for (pp = head.pe_next; pp; pp = pp->pe_next)
 82+ if (pp->pe_pid == pid)
 83+ return pp;
 84+
 85+ if (create == 0)
 86+ return NULL;
 87+
 88+ pp = calloc(1, sizeof(*pp));
 89+ if (pp == NULL) {
 90+ (void) fprintf(stderr, "out of memory\n");
 91+ _exit(1);
 92+ }
 93+
 94+ pp->pe_pid = pid;
 95+ pp->pe_next = head.pe_next;
 96+ head.pe_next = pp;
 97+ return pp;
 98+}
 99+
 100+void
 101+add_uproc(pid, ppid, comm, cmdline)
 102+ pid_t pid, ppid;
 103+ char const *comm, *cmdline;
 104+{
 105+ uproc_t *pp;
 106+ uproc_t *proc = find_uproc(pid, 1),
 107+ *parent = find_uproc(ppid, 1);
 108+
 109+ proc->pe_cmd = strdup(comm);
 110+ proc->pe_cmdline = strdup(cmdline);
 111+
 112+ make_safe(proc->pe_cmd);
 113+ make_safe(proc->pe_cmdline);
 114+
 115+ /* add this process to its parent's child list */
 116+ if (parent->pe_child == NULL) {
 117+ parent->pe_child = proc;
 118+ } else {
 119+ for (pp = parent->pe_child; pp->pe_sib; pp = pp->pe_sib)
 120+ ;
 121+ pp->pe_sib = proc;
 122+ }
 123+}
 124+
 125+void
 126+build_uproc_tree()
 127+{
 128+ DIR *pdir;
 129+struct dirent *dent;
 130+
 131+ if ((pdir = opendir("/proc")) == NULL) {
 132+ perror("/proc");
 133+ exit(1);
 134+ }
 135+
 136+ while ((dent = readdir(pdir)) != NULL) {
 137+ char const *s;
 138+ int pid, ppid;
 139+ FILE *f = NULL;
 140+ char statpath[128], cmd[128], cmdline[128];
 141+ size_t sz;
 142+
 143+ for (s = dent->d_name; *s; ++s)
 144+ if (!isdigit(*s))
 145+ goto next;
 146+
 147+ (void) snprintf(statpath, sizeof(statpath), "/proc/%s/stat",
 148+ dent->d_name);
 149+ if ((f = fopen(statpath, "r")) == NULL)
 150+ goto next;
 151+
 152+ if (fscanf(f, "%d %127s %*c %d", &pid, cmd, &ppid) != 3)
 153+ goto next;
 154+
 155+ (void) fclose(f);
 156+
 157+ (void) snprintf(statpath, sizeof(statpath), "/proc/%s/cmdline",
 158+ dent->d_name);
 159+ if ((f = fopen(statpath, "r")) == NULL)
 160+ goto next;
 161+
 162+ sz = fread(cmdline, 1, sizeof(cmdline) - 1, f);
 163+ if (sz == 0)
 164+ cmdline[0] = '\0';
 165+ else {
 166+ char *s = cmdline;
 167+ while (sz--) {
 168+ if (*s == '\0')
 169+ *s = ' ';
 170+ s++;
 171+ }
 172+ *s = '\0';
 173+ }
 174+
 175+ add_uproc(pid, ppid, cmd, cmdline);
 176+
 177+ next:
 178+ if (f)
 179+ (void) fclose(f);
 180+ }
 181+
 182+ (void) closedir(pdir);
 183+}
 184+
 185+void
 186+print_uproc_children(pp, indent)
 187+ uproc_t *pp;
 188+ int indent;
 189+{
 190+ for (pp = pp->pe_child; pp; pp = pp->pe_sib) {
 191+ show_uproc(pp, indent * 2);
 192+ print_uproc_children(pp, indent + 1);
 193+ }
 194+}
 195+
 196+void
 197+show_uproc(pp, pad)
 198+ uproc_t *pp;
 199+ int pad;
 200+{
 201+ (void) printf(" %-5d ", pp->pe_pid);
 202+
 203+ if (pp->pe_cmd)
 204+ (void) printf(" %-*s", 10 + pad, pp->pe_cmd);
 205+ else
 206+ (void) printf(" ???");
 207+
 208+ if (pp->pe_cmdline)
 209+ (void) printf(" %s", pp->pe_cmdline);
 210+ (void) printf("\n");
 211+}
 212+
 213+void
 214+make_safe(s)
 215+ char *s;
 216+{
 217+ while (*s) {
 218+ if (!isprint(*s))
 219+ *s = '?';
 220+ s++;
 221+ }
 222+}
Index: trunk/tools/tsutils/watcherd/Makefile
@@ -0,0 +1,9 @@
 2+PROG = watcherd
 3+LIBTSUTILS = YES
 4+LIBS = -lmysqlclient -lz
 5+INCLUDES = -I/usr/include/mysql
 6+
 7+SRCS = watcherd.c
 8+OBJS = $(SRCS:.c=.o)
 9+
 10+include ../mk/prog.mk
Index: trunk/tools/tsutils/watcherd/watcherd.c
@@ -0,0 +1,446 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+/*
 13+ * watcherd: monitor MySQL server and notify users running long queries.
 14+ */
 15+
 16+#include <sys/types.h>
 17+#include <sys/mman.h>
 18+#include <sys/stat.h>
 19+#include <stdint.h>
 20+#include <stdio.h>
 21+#include <errno.h>
 22+#include <string.h>
 23+#include <unistd.h>
 24+#include <stdlib.h>
 25+#include <ctype.h>
 26+#include <time.h>
 27+#include <mysql.h>
 28+#include <fcntl.h>
 29+#include "subst.h"
 30+#include "tsutils.h"
 31+
 32+#define SLOWSTR "/* SLOW_OK */"
 33+
 34+static char const *cfgfile = "/etc/watcherd/watcherd.conf";
 35+static char const *msgfile = "/etc/watcherd/mailmessage";
 36+static char const *writefile = "/etc/watcherd/writemessage";
 37+static int debug;
 38+static char *dbhost, *dbuser, *dbpass, *thishost;
 39+static int warntime;
 40+static MYSQL *conn;
 41+static char hostname[128];
 42+static int iter;
 43+
 44+static void usage(void);
 45+static void read_configuration(void);
 46+static int run_check(void);
 47+static void notify_query(char const *id, char const *user, char const *db, char const *query, int t);
 48+static int seen_query(int64_t);
 49+static void clean_seen_queries(void);
 50+static void add_seen_query(int64_t);
 51+static int has_warned(int64_t);
 52+static void set_warned(int64_t);
 53+static void add_alias(char const *alias, char const *user);
 54+static char const *find_alias(char const *user);
 55+static void sanitise_query(char *query);
 56+
 57+int
 58+main(argc, argv)
 59+ int argc;
 60+ char *argv[];
 61+{
 62+int c;
 63+char *progname = argv[0];
 64+
 65+ while ((c = getopt(argc, argv, "f:d")) != -1) {
 66+ switch (c) {
 67+ case 'f':
 68+ cfgfile = optarg;
 69+ break;
 70+ case 'd':
 71+ debug = 1;
 72+ break;
 73+ default:
 74+ usage();
 75+ exit(1);
 76+ }
 77+ }
 78+
 79+ read_configuration();
 80+
 81+ if (debug == 0) {
 82+ if (daemon_detach(progname) == -1) {
 83+ (void) fprintf(stderr, "daemon: %s\n",
 84+ strerror(errno));
 85+ exit(1);
 86+ }
 87+ }
 88+
 89+ if (gethostname(hostname, sizeof(hostname) - 1) == -1) {
 90+ logmsg("warning: could not get hostname: %s", strerror(errno));
 91+ (void) strcpy(hostname, "unknown");
 92+ }
 93+
 94+ for (;;) {
 95+ int i;
 96+ if (conn == NULL) {
 97+ if ((conn = mysql_init(NULL)) == NULL) {
 98+ logmsg("out of memory in mysql_init");
 99+ exit(1);
 100+ }
 101+
 102+ if (mysql_real_connect(conn, dbhost, dbuser, dbpass, NULL, 0, NULL,
 103+ 0) == NULL) {
 104+ logmsg("mysql_connect: %s", mysql_error(conn));
 105+ mysql_close(conn);
 106+ conn = NULL;
 107+ (void) sleep(300);
 108+ continue;
 109+ }
 110+ }
 111+
 112+ if ((i = run_check()) < 0)
 113+ break;
 114+ (void) sleep(i);
 115+ }
 116+
 117+ return 0;
 118+}
 119+
 120+static void
 121+usage()
 122+{
 123+ (void) fprintf(stderr,
 124+"usage: watcherd [-d] [-f <config>]\n"
 125+ );
 126+}
 127+
 128+static void
 129+read_configuration()
 130+{
 131+ FILE *f;
 132+ char line[1024];
 133+ int lineno = 0;
 134+
 135+ if ((f = fopen(cfgfile, "r")) == NULL) {
 136+ (void) fprintf(stderr, "watcherd: could not open configuration file "
 137+ "%s: %s\n", cfgfile, strerror(errno));
 138+ exit(1);
 139+ }
 140+
 141+ while (fgets(line, sizeof(line), f)) {
 142+ char *opt, *value;
 143+ size_t n = strlen(line) - 1;
 144+
 145+ if (line[n] == '\n')
 146+ line[n] = '\0';
 147+
 148+ lineno++;
 149+
 150+ opt = line;
 151+ if ((value = strchr(opt, ' ')) != NULL) {
 152+ *value++ = '\0';
 153+
 154+ while (*value == ' ')
 155+ value++;
 156+ }
 157+
 158+ if (!strcmp(opt, "dbhost")) {
 159+ strdup_free(&dbhost, value);
 160+ } else if (!strcmp(opt, "dbuser")) {
 161+ strdup_free(&dbuser, value);
 162+ } else if (!strcmp(opt, "dbpass")) {
 163+ strdup_free(&dbpass, value);
 164+ } else if (!strcmp(opt, "warntime")) {
 165+ warntime = atoi(value);
 166+ } else if (!strcmp(opt, "thishost")) {
 167+ strdup_free(&thishost, value);
 168+ } else if (!strcmp(opt, "alias")) {
 169+ char *user, *realuser;
 170+ user = value;
 171+ if ((realuser = strchr(user, ' ')) == NULL) {
 172+ (void) fprintf(stderr, "\"%s\", line %d: "
 173+ "invalid alias syntax\n",
 174+ cfgfile, lineno);
 175+ exit(1);
 176+ }
 177+
 178+ *realuser++ = '\0';
 179+ add_alias(user, realuser);
 180+ } else {
 181+ (void) fprintf(stderr, "\"%s\", line %d: "
 182+ "unknown option \"%s\"\n",
 183+ cfgfile, lineno, opt);
 184+ exit(1);
 185+ }
 186+ }
 187+
 188+ (void) fclose(f);
 189+}
 190+
 191+static int
 192+run_check()
 193+{
 194+MYSQL_RES *res;
 195+MYSQL_ROW row;
 196+
 197+ if (mysql_query(conn, "SHOW FULL PROCESSLIST") != 0) {
 198+ logmsg("error retrieving processlist: %s", mysql_error(conn));
 199+ mysql_close(conn);
 200+ conn = NULL;
 201+ return 300;
 202+ }
 203+
 204+ if ((res = mysql_use_result(conn)) == NULL) {
 205+ logmsg("error retrieving result set: %s", mysql_error(conn));
 206+ mysql_close(conn);
 207+ conn = NULL;
 208+ return 300;
 209+ }
 210+
 211+ ++iter;
 212+
 213+ printf("check\n");
 214+ while ((row = mysql_fetch_row(res)) != NULL) {
 215+ uint64_t qid;
 216+ int t;
 217+ char *p;
 218+
 219+ /*
 220+ * 0: id
 221+ * 1: user
 222+ * 2: host
 223+ * 3: db
 224+ * 4: command
 225+ * 5: time
 226+ * 6: state
 227+ * 7: info
 228+ */
 229+
 230+ if (row[7] == NULL)
 231+ continue;
 232+
 233+ qid = strtol(row[0], NULL, 10);
 234+ add_seen_query(qid);
 235+
 236+ t = atoi(row[5]);
 237+ if (t < warntime)
 238+ continue;
 239+
 240+ p = strchr(row[2], ':');
 241+ if (p != NULL)
 242+ *p = '\0';
 243+
 244+ if (strcmp(row[2], thishost))
 245+ continue;
 246+
 247+ if (strstr(row[7], SLOWSTR) != NULL)
 248+ continue;
 249+
 250+ if (!has_warned(qid)) {
 251+ sanitise_query(row[7]);
 252+ notify_query(row[0], find_alias(row[1]), row[3], row[7], t);
 253+ set_warned(qid);
 254+ }
 255+ }
 256+
 257+ mysql_free_result(res);
 258+ clean_seen_queries();
 259+ return 6;
 260+}
 261+
 262+static void
 263+notify_query(id, user, db, query, t)
 264+ char const *id, *user, *db, *query;
 265+ int t;
 266+{
 267+subst_t s;
 268+char *msg, timebuf[32], limitbuf[32], *template;
 269+int term;
 270+char curtime[64];
 271+time_t now;
 272+
 273+ logmsg("warning \"%s\" about query %s", user, id);
 274+
 275+ (void) time(&now);
 276+ (void) strftime(curtime, sizeof(curtime),
 277+ "%Y-%m-%d %H:%M:%S UTC", gmtime(&now));
 278+
 279+ s = subst_new();
 280+ subst_add_var(s, "id", id);
 281+ subst_add_var(s, "user", user);
 282+ subst_add_var(s, "db", db);
 283+ subst_add_var(s, "query", query);
 284+ (void) snprintf(timebuf, sizeof(timebuf), "%d", t);
 285+ subst_add_var(s, "time", timebuf);
 286+ (void) snprintf(limitbuf, sizeof(limitbuf), "%d", warntime);
 287+ subst_add_var(s, "limit", limitbuf);
 288+ subst_add_var(s, "hostname", hostname);
 289+ subst_add_var(s, "curtime", curtime);
 290+ subst_add_var(s, "slowstr", SLOWSTR);
 291+
 292+ if ((term = get_user_tty(user)) != -1) {
 293+ template = file_to_string(writefile);
 294+ if (template != NULL) {
 295+ msg = subst_run(s, template);
 296+ (void) write(term, msg, strlen(msg));
 297+ free(template);
 298+ free(msg);
 299+ subst_free(s);
 300+ return;
 301+ } else {
 302+ logmsg("warning: cannot open writemsg \"%s\": %s",
 303+ writefile, strerror(errno));
 304+ }
 305+ }
 306+
 307+ if ((template = file_to_string(msgfile)) == NULL) {
 308+ logmsg("warning: cannot open msgfile \"%s\": %s",
 309+ msgfile, strerror(errno));
 310+ subst_free(s);
 311+ return;
 312+ }
 313+
 314+ msg = subst_run(s, template);
 315+ subst_free(s);
 316+ free(template);
 317+
 318+ (void) sendmail(user, msg);
 319+ free(msg);
 320+}
 321+
 322+typedef struct query {
 323+ int64_t q_id;
 324+ int q_iter;
 325+ int q_warned;
 326+ struct query *q_next;
 327+} query;
 328+
 329+static query querylist;
 330+
 331+static void
 332+add_seen_query(qid)
 333+ int64_t qid;
 334+{
 335+query *q;
 336+ for (q = querylist.q_next; q; q = q->q_next)
 337+ if (q->q_id == qid) {
 338+ q->q_iter = iter;
 339+ return;
 340+ }
 341+
 342+ q = calloc(1, sizeof(*q));
 343+ q->q_id = qid;
 344+ q->q_iter = iter;
 345+ q->q_next = querylist.q_next;
 346+ querylist.q_next = q;
 347+}
 348+
 349+static int
 350+seen_query(qid)
 351+ int64_t qid;
 352+{
 353+query *q;
 354+ for (q = querylist.q_next; q; q = q->q_next)
 355+ if (q->q_id == qid)
 356+ return 1;
 357+ return 0;
 358+}
 359+
 360+static void
 361+set_warned(qid)
 362+ int64_t qid;
 363+{
 364+query *q;
 365+ for (q = querylist.q_next; q; q = q->q_next)
 366+ if (q->q_id == qid) {
 367+ q->q_warned = 1;
 368+ return;
 369+ }
 370+}
 371+
 372+static int
 373+has_warned(qid)
 374+ int64_t qid;
 375+{
 376+query *q;
 377+ for (q = querylist.q_next; q; q = q->q_next)
 378+ if (q->q_id == qid)
 379+ return q->q_warned;
 380+ return 0;
 381+}
 382+
 383+static void
 384+clean_seen_queries()
 385+{
 386+query *newlist = NULL;
 387+
 388+ while (querylist.q_next) {
 389+ query *tmp, *q = querylist.q_next;
 390+
 391+ if (q->q_iter == iter) {
 392+ tmp = q->q_next;
 393+ q->q_next = newlist;
 394+ newlist = q;
 395+ querylist.q_next = tmp;
 396+ continue;
 397+ }
 398+
 399+ tmp = q;
 400+ querylist.q_next = q->q_next;
 401+ free(q);
 402+ }
 403+
 404+ querylist.q_next = newlist;
 405+}
 406+
 407+typedef struct alias {
 408+ char *alias;
 409+ char *user;
 410+ struct alias *next;
 411+} alias_t;
 412+
 413+static alias_t aliaslist;
 414+
 415+static void
 416+add_alias(alias, user)
 417+ char const *alias, *user;
 418+{
 419+alias_t *a;
 420+ a = calloc(1, sizeof(*a));
 421+ a->alias = strdup(alias);
 422+ a->user = strdup(user);
 423+ a->next = aliaslist.next;
 424+ aliaslist.next = a->next;
 425+}
 426+
 427+static char const *
 428+find_alias(user)
 429+ char const *user;
 430+{
 431+alias_t *a;
 432+ for (a = aliaslist.next; a; a = a->next)
 433+ if (!strcmp(a->alias, user))
 434+ return a->user;
 435+ return user;
 436+}
 437+
 438+static void
 439+sanitise_query(str)
 440+ char *str;
 441+{
 442+ while (*str) {
 443+ if (!isascii(*str) || !isprint(*str))
 444+ *str = '?';
 445+ str++;
 446+ }
 447+}
Index: trunk/tools/tsutils/acctexp/acctexp.c
@@ -0,0 +1,53 @@
 2+/*
 3+ * Print account expiry date.
 4+ * $Id: acctexp.c 11210 2005-10-06 09:59:28Z kateturner $
 5+ */
 6+
 7+#include <sys/types.h>
 8+#include <sys/time.h>
 9+#include <stdio.h>
 10+#include <pwd.h>
 11+#include <time.h>
 12+#include <unistd.h>
 13+#include <errno.h>
 14+#include <string.h>
 15+#include <shadow.h>
 16+
 17+int
 18+main()
 19+{
 20+struct spwd *spwent;
 21+struct passwd *pwent;
 22+uid_t uid;
 23+time_t when;
 24+char res[256];
 25+struct tm *whentm;
 26+ uid = getuid();
 27+ if ((pwent = getpwuid(uid)) == NULL) {
 28+ (void) fprintf(stderr, "getpwuid: %s\n", strerror(errno));
 29+ return 1;
 30+ }
 31+ if ((spwent = getspnam(pwent->pw_name)) == NULL) {
 32+ (void) fprintf(stderr, "getspnam: %s\n", strerror(errno));
 33+ return 1;
 34+ }
 35+ (void) seteuid(getuid());
 36+ if (spwent->sp_expire == 0) {
 37+ return 0;
 38+ }
 39+ when = spwent->sp_expire * 24 * 60 * 60;
 40+ if (when < 0) {
 41+ return 0;
 42+ }
 43+ if ((whentm = localtime(&when)) == NULL) {
 44+ (void) fprintf(stderr, "localtime: %s\n", strerror(errno));
 45+ return 1;
 46+ }
 47+ (void) strftime(res, sizeof(res) - 1, "%A, %d %B %Y", whentm);
 48+ res[sizeof(res) - 1] = '\0';
 49+ (void) printf("Your account will expire on %s.\n", res);
 50+ return 0;
 51+}
 52+
 53+
 54+
Index: trunk/tools/tsutils/acctexp/Makefile
@@ -0,0 +1,6 @@
 2+PROG = acctext
 3+
 4+SRCS = acctexp.c
 5+OBJS = $(SRCS:.c=.o)
 6+
 7+include ../mk/prog.mk
Index: trunk/tools/tsutils/listlogins/listlogins.c
@@ -0,0 +1,96 @@
 2+/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+/*
 13+ * listlogins: report other login sessions from this user.
 14+ */
 15+
 16+#include <sys/types.h>
 17+#include <stdio.h>
 18+#include <string.h>
 19+#include <stdlib.h>
 20+#include <unistd.h>
 21+#include <utmpx.h>
 22+#include <pwd.h>
 23+
 24+typedef struct ttyent {
 25+ struct ttyent *te_next;
 26+ char te_name[16];
 27+} ttyent_t;
 28+
 29+typedef struct utent {
 30+ struct utent *ute_next;
 31+ char ute_host[256];
 32+ ttyent_t ute_lines;
 33+} utent_t;
 34+
 35+int
 36+main()
 37+{
 38+ struct utmpx *ut;
 39+ struct passwd *pwd;
 40+ utent_t ents, *utp;
 41+ ttyent_t *ttye;
 42+ char *tty = ttyname(0);
 43+
 44+ if (strlen(tty) < 5)
 45+ tty = 0;
 46+
 47+ if ((pwd = getpwuid(getuid())) == NULL)
 48+ return 0;
 49+
 50+ (void) memset(&ents, 0, sizeof(ents));
 51+
 52+ setutxent();
 53+ while ((ut = getutxent()) != NULL) {
 54+ if (ut->ut_type != USER_PROCESS)
 55+ continue;
 56+
 57+ if (strcmp(ut->ut_user, pwd->pw_name))
 58+ continue;
 59+
 60+ if (tty && !strcmp(ut->ut_line, tty + 5))
 61+ continue;
 62+
 63+ utp = NULL;
 64+ /* If we already saw this host, add the line to the list */
 65+ for (utp = ents.ute_next; utp; utp = utp->ute_next) {
 66+ if (!strcmp(utp->ute_host, ut->ut_host))
 67+ break;
 68+ }
 69+
 70+ if (utp == NULL) {
 71+ utp = calloc(1, sizeof(*utp));
 72+ (void) strncpy(utp->ute_host, ut->ut_host, sizeof(utp->ute_host) - 1);
 73+ utp->ute_next = ents.ute_next;
 74+ ents.ute_next = utp;
 75+ }
 76+
 77+ ttye = calloc(1, sizeof(*ttye));
 78+ (void) strncpy(ttye->te_name, ut->ut_line, sizeof(ttye->te_name) - 1);
 79+ ttye->te_next = utp->ute_lines.te_next;
 80+ utp->ute_lines.te_next = ttye;
 81+ }
 82+ endutxent();
 83+
 84+ if (ents.ute_next)
 85+ (void) printf("You are already logged in from the following host(s):\n");
 86+
 87+ for (utp = ents.ute_next; utp; utp = utp->ute_next) {
 88+ (void) printf(" %s (", utp->ute_host);
 89+ for (ttye = utp->ute_lines.te_next; ttye; ttye = ttye->te_next) {
 90+ (void) printf("%s", ttye->te_name);
 91+ if (ttye->te_next)
 92+ (void) printf(", ");
 93+ }
 94+ (void) printf(")\n");
 95+ }
 96+ return 0;
 97+}
Index: trunk/tools/tsutils/listlogins/Makefile
@@ -0,0 +1,6 @@
 2+PROG = listlogins
 3+
 4+SRCS = listlogins.c
 5+OBJS = $(SRCS:.c=.o)
 6+
 7+include ../mk/prog.mk
Index: trunk/tools/tsutils/mk/prog.mk
@@ -0,0 +1,11 @@
 2+include ../mk/rules.mk
 3+
 4+all: $(PROG)
 5+$(PROG): $(OBJS)
 6+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $@ $(LIBS)
 7+lint:
 8+ $(LINT) $(LINTFLAGS) $(INCLUDES) $(SRCS) $(LIBS)
 9+clean:
 10+ rm -f $(PROG) $(OBJS)
 11+
 12+.PHONY: clean
Index: trunk/tools/tsutils/mk/rules.mk
@@ -0,0 +1,9 @@
 2+include ../config.mk
 3+
 4+ifeq ($(LIBTSUTILS),YES)
 5+INCLUDES += -I../libtsutils
 6+LIBS += -L../libtsutils -ltsutils
 7+endif
 8+
 9+.c.o:
 10+ $(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) -c $<
Index: trunk/tools/tsutils/mk/lib.mk
@@ -0,0 +1,11 @@
 2+include ../mk/rules.mk
 3+
 4+all: lib$(LIB).a
 5+lib$(LIB).a: $(OBJS)
 6+ rm -f $@
 7+ ar crv $@ $(OBJS)
 8+lint:
 9+ $(LINT) -o$(LIB) $(LINTFLAGS) $(SRCS)
 10+clean:
 11+ rm -f lib$(LIB).a $(OBJS)
 12+.PHONY: clean
Index: trunk/tools/tsutils/config.mk
@@ -0,0 +1,7 @@
 2+LINT = lint
 3+CC = suncc
 4+CFLAGS = -m64 -O -g -errwarn -xc99=none
 5+LINTFLAGS = -axsm -u -errtags=yes -s -Xc99=%none -Xarch=amd64 -errsecurity=core -erroff=E_INCONS_ARG_DECL2
 6+
 7+# CC = gcc
 8+# CFLAGS = -W -Wall -Werror
Index: trunk/tools/tsutils/lsexp/lsexp.c
@@ -0,0 +1,37 @@
 2+/*
 3+ * Print all expired accounts.
 4+ * $Id: lsexp.c 21740 2007-05-01 00:39:42Z river $
 5+ */
 6+
 7+#include <sys/types.h>
 8+#include <sys/time.h>
 9+#include <stdio.h>
 10+#include <pwd.h>
 11+#include <time.h>
 12+#include <unistd.h>
 13+#include <errno.h>
 14+#include <string.h>
 15+#include <shadow.h>
 16+
 17+int
 18+main(void)
 19+{
 20+struct spwd *spwent;
 21+time_t when, now;
 22+
 23+ (void) time(&now);
 24+
 25+ while ((spwent = getspent()) != NULL) {
 26+ if (spwent->sp_expire <= 0)
 27+ continue;
 28+
 29+ when = spwent->sp_expire * 24 * 60 * 60;
 30+
 31+ if (when > now)
 32+ continue;
 33+
 34+ (void) printf("%s\n", spwent->sp_namp);
 35+ }
 36+
 37+ return 0;
 38+}
Index: trunk/tools/tsutils/lsexp/Makefile
@@ -0,0 +1,6 @@
 2+PROG = lsexp
 3+
 4+SRCS = lsexp.c
 5+OBJS = $(SRCS:.c=.o)
 6+
 7+include ../mk/prog.mk