#include #include #include #include #include #include #include #include #include #include #include #include #include "autoconf.h" #include "intf.h" #include "storage.h" #include "misc.h" #include "debug.h" static struct tsnif_storage_opts init_storage_opts = { .flags = TSNIF_STORAGE_OPT_WRITE, .size_max = 1024*1024, }; static struct tsnif_handle handle; struct terminal { /* interface term */ struct tsnif_term term; /* storage */ char *file; struct tsnif_storage_opts storage_opts; struct tsnif_storage_handle storage_handle; }; static int foreground = 0; static int killed = 0; static char *store_dir = "/var/log/tsnid"; static char *types[TSNIF_TYPE_MAX] = { "tty", "ttys", "pty" }; static char *type_get(int type) { if ((type >= TSNIF_TYPE_MAX) || (type < 0)) return NULL; return types[type]; } static int dir_get(char *path) { struct stat st; int err; if (!stat(path, &st)) return 0; err = mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR); if (err) { perror("mkdir failed"); return err; } return 0; } static char *file_get(int type, int idx) { struct timeval tv; struct tm *tm; #define MAXPATH 1024 static char path[MAXPATH]; char *type_str; int len; if (dir_get(store_dir)) return NULL; type_str = type_get(type); if (!type_str) return NULL; len = snprintf(path, MAXPATH, "%s/%s", store_dir, type_str); if (dir_get(path)) return NULL; if (-1 == gettimeofday(&tv, NULL)) { perror("gettimeofday failed"); return NULL; } tm = localtime(&tv.tv_sec); if (!tm) { perror("localtime failed"); return NULL; } snprintf(path + len, MAXPATH - len, "/%d-%02d.%02d.%02d_%02d:%02d:%02d.%03lu-alive", idx, tm->tm_mday, tm->tm_mon + 1, (tm->tm_year + 1900) % 100, tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec / 1000); return path; } static int file_put(char *name) { char *new = strdup(name); char *p; int err; p = strrchr(new, '-'); if (!p) return -1; sprintf(p, "%s", ".tsnif"); TSNIF_DEBUG(APP, "putting to %s\n", new); err = rename(name, new); free(new); return err; } static int terminal_add(struct tsnif_term *term) { struct terminal *t; int err; char *file; TSNIF_DEBUG(APP, "type %d, idx %d\n", term->type, term->idx); /* XXX debug gets only PTY XXX */ if (tsnif_debug) { if (term->type != TSNIF_TYPE_PTY) return 0; } t = malloc(sizeof(*t)); if (!t) return -ENOMEM; memset(t, 0x0, sizeof(*t)); err = tsnif_term_add(&handle, &t->term, term->type, term->idx); if (err) { free(t); return err; } file = file_get(term->type, term->idx); if (!file) { tsnif_term_del(&handle, &t->term); free(t); return -1; } TSNIF_DEBUG(APP, "storing to %s\n", file); t->file = strdup(file); t->storage_opts = init_storage_opts; err = tsnif_storage_init(&t->storage_handle, &t->storage_opts, file); return tsnif_attach(&t->term); } static int terminal_del(struct tsnif_term *term) { struct terminal *t; TSNIF_DEBUG(APP, "type %d, idx %d\n", term->type, term->idx); tsnif_term_del(&handle, term); t = container_of(term, struct terminal, term); tsnif_storage_close(&t->storage_handle); file_put(t->file); free(t->file); free(t); return 0; } static int store_data(struct tsnif_storage_handle *h, struct tsnif_data *data) { struct tsnif_storage_rec rec = { .ptr = data->ptr, .len = data->len, .flags = data->flags, .time = data->time, .ws = data->ws, }; return tsnif_storage_write(h, &rec); } static int data_cb(struct tsnif_term* term, struct tsnif_data *data) { struct terminal *t; t = container_of(term, struct terminal, term); return store_data(&t->storage_handle, data); } static int err_cb(struct tsnif_term *term, int err) { return terminal_del(term); } static int release_cb(struct tsnif_term *term) { return terminal_del(term); } static int notify_cb(struct tsnif_term *term, int action) { int err = 0; switch(action) { case TSNIF_CMD_TTY_RELEASE: printf("not watched terminal released - type %d, idx %d\n", term->type, term->idx); break; case TSNIF_CMD_TTY_CREATE: case TSNIF_CMD_TTY_LIST: err = terminal_add(term); break; default: err = -EINVAL; } return err; } static int release_terms(void) { struct tsnif_term *term, *t; tsnif_for_each(term, t, (&handle)) { /* The ACK action (release cb) is never going * to happen, since we are going down, * so we * need to run terminal_del manually */ tsnif_detach(term); terminal_del(term); } return 0; } struct tsnif_ops ops = { .cb_data = data_cb, .cb_err = err_cb, .cb_release = release_cb, .cb_notify = notify_cb, }; static void sig_handler(int sig) { printf("killed\n"); killed = 1; } static void usage() { printf("tsnifd [-dfvhs]\n"); _exit(-1); } static int get_args(int argc, char **argv) { while (1) { int c; int option_index = 0; static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"debug", no_argument, 0, 'd'}, {"foreground", no_argument, 0, 'f'}, {"store-dir", required_argument, 0, 's'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "t:i:s:vdh", long_options, &option_index); if (c == -1) break; switch (c) { case 'v': printf("tsnif "CONFIG_TSNIF_VER"\n"); break; case 'd': tsnif_debug = 1; break; case 'f': foreground = 1; break; case 's': store_dir = optarg; break; case 'h': usage(); break; default: printf("unknown option '%c'", c); } /* switch(c) */ } /* while(1) */ return 0; } int main(int argc, char **argv) { int err, ret; jmp_buf env; if (get_args(argc, argv)) usage(); err = tsnif_init(&handle, &ops); if (err) return err; if ((ret = setjmp(env))) { if (ret > 1) release_terms(); tsnif_close(&handle); printf("done err = %d\n", err); return err; } err = tsnif_list(&handle); if (err) longjmp(env, 1); signal(SIGINT, sig_handler); while(!killed) { fd_set rfds; struct timeval tv = { 1, 0}; int ts_fd = tsnif_fd(&handle); FD_ZERO(&rfds); FD_SET(ts_fd, &rfds); err = select(ts_fd + 1, &rfds, NULL, NULL, &tv); if (err == -1) { perror("select()"); continue; } else if (!err) continue; if (FD_ISSET(ts_fd, &rfds)) { err = tsnif_process(&handle); if (err) longjmp(env, 2); } } longjmp(env, 2); }