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 |