#include "super.h" #include "struct.h" #define LL() fprintf(stderr, "[%s, line %i]: ", __FILE__, __LINE__) struct s_file { FILE *f; int fd; int line; char *ptr; char str[BUFSIZ]; }; struct s_session { char to[BUFSIZ]; char from[BUFSIZ]; char type[BUFSIZ]; char randstr[BUFSIZ]; char date[BUFSIZ]; char id[BUFSIZ]; long secs; time_t e; struct s_session *next; struct s_session *prev; struct s_file input; struct s_file script; struct s_file time; }; typedef struct s_session session; extern config *c_top; extern void parse(option *, const char *); extern void show_config(config *); int getopt(int, char *const[], const char *); extern char *optarg; extern int optind; char match[BUFSIZ]; char *progname; session *s_top; option config_option; void replay(const char *, const char *, int, int); void link_session(session *); void unlink_session(session *); void count_dur(session *); char *int2smalltime(int); session *session_malloc(void); session *find_session(const char *, const char *, time_t e, const char *); session *sort_list(session *); void show_sessions(void); int main(int argc, char **argv, char **environ) { int c; char *p = (char *) 0; char sysconfdir[BUFSIZ]; struct dirent *dirp; DIR *dp; session *scan; session *last; progname = argv[0]; if ((p = (char *) strrchr(progname, '/')) != (char *) 0) progname = p + 1; snprintf(sysconfdir, BUFSIZ - 1, "%s/sudosh.conf", SYSCONFDIR); parse(&config_option, sysconfdir); snprintf(match, BUFSIZ - 1, "%%32[^%c]%c%%32[^%c]%c%%32[^%c]%c%%ld%c%%64[A-Za-z0-9]", config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl); while (1) { c = getopt(argc, argv, "d:hVv"); if (c == EOF) break; switch (c) { case 'V': case 'v': fprintf(stdout, "%s version %s\n", progname, VERSION); exit(EXIT_SUCCESS); break; case 'h': fprintf(stdout, "Usage: %s ID [MULTIPLIER] [MAXWAIT]\n", progname); fprintf(stdout, "Replay %s sessions as they were originally recorded.\n\n", progname); fprintf(stdout, "-d Specify alternate LOGDIR where sessions are stored.\n"); fprintf(stdout, "-h Show help synopsis.\n"); fprintf(stdout, "-v,-V Show version.\n\n"); fprintf(stdout, "ID\n The session id of which you wish to view. The format is user-id\n"); fprintf(stdout, " You may obtain a list of ids by just running %s\n\n", progname); fprintf(stdout, "MULTIPLIER\n"); fprintf(stdout, " This is optional. If you wish to \"speed up\" the playback of the\n"); fprintf(stdout, " session, specify a rate to increase it. For example if you used\n"); fprintf(stdout, " a multiplier of \"2\" it would playback the session 2x the original\n"); fprintf(stdout, " recording speed.\n\n"); fprintf(stdout, "MAXWAIT\n"); fprintf(stdout, " This is optional. If you do not want to wait during any long pauses\n"); fprintf(stdout, " use this option to specify a maximum wait time. For example a MAXWAIT\n"); fprintf(stdout, " of 2 would wait a maximum of 2 seconds then continue.\n\n"); fprintf(stdout, "Example: how to get a list of sessions to replay.\n"); fprintf(stdout, "# %s\n\n", progname); fprintf(stdout, "Example: how to replay a session as it was recorded.\n"); fprintf(stdout, "# %s ID\n", progname); fprintf(stdout, "Example: how to replay a session, but just echo the contains to the screen.\n"); fprintf(stdout, "# %s ID 0\n\n", progname); fprintf(stdout, "This is %s version %s\n", progname, VERSION); fprintf(stdout, "Report bugs to <%s>\n", PACKAGE_BUGREPORT); exit(EXIT_SUCCESS); break; case 'd': strncpy(config_option.logdir, optarg, BUFSIZ - 1); break; default: fprintf(stderr, "%s: unrecognized option `%c'\n", progname, c); fprintf(stderr, "Try `%s -h' for more information.\n", progname); exit(EXIT_FAILURE); break; } } argc -= optind; argv += optind; if ((dp = opendir(config_option.logdir)) == (DIR *) 0) { LL(); fprintf(stderr, "%s: can't opendir(%s): %s (%i)\n", progname, config_option.logdir, strerror(errno), errno); exit(EXIT_FAILURE); } while ((dirp = readdir(dp)) != (struct dirent *) 0) { session *s; char from[BUFSIZ]; char to[BUFSIZ]; char type[BUFSIZ]; char randstr[BUFSIZ]; time_t e; if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) continue; if (sscanf(dirp->d_name, match, from, to, type, &e, randstr) != 5) { LL(); fprintf(stderr, "[warning]: invalid file: %s\n", dirp->d_name); continue; } if (strcmp(type, "script") && strcmp(type, "input") && strcmp(type, "time") && strcmp(type, "interactive")) { fprintf(stderr, "[warning]: invalid type (%s): %s\n", type, dirp->d_name); continue; } if(!strcmp(type, "interactive")) { char *ptr; FILE *f; s = session_malloc(); s->secs = 0; s->e = e; snprintf(s->script.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); f = fopen(s->script.str, "r"); if(f == (FILE *) 0) { fprintf(stderr, "%.100s: %.100s (%i)\n", s->script.str, strerror(errno), errno); exit(EXIT_FAILURE); } fgets(s->id, BUFSIZ - 1, f); fclose(f); ptr = strchr(s->id, '\n'); if(ptr != (char *) 0) *ptr++ = '\0'; strncpy(s->from, from, BUFSIZ - 1); strncpy(s->to, to, BUFSIZ - 1); strncpy(s->type, type, BUFSIZ - 1); strncpy(s->randstr, randstr, BUFSIZ - 1); strftime(s->date, 20, "%m/%d/%Y %H:%M:%S", localtime(&s->e)); link_session(s); continue; } s = find_session(from, to, e, randstr); if (s == (session *) 0) { s = session_malloc(); s->secs = 0; if (!strcmp(type, "script")) snprintf(s->script.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); if (!strcmp(type, "input")) snprintf(s->input.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); if (!strcmp(type, "time")) { snprintf(s->time.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); count_dur(s); } s->e = e; strncpy(s->from, from, BUFSIZ - 1); strncpy(s->to, to, BUFSIZ - 1); strncpy(s->type, type, BUFSIZ - 1); strncpy(s->randstr, randstr, BUFSIZ - 1); strftime(s->date, 20, "%m/%d/%Y %H:%M:%S", localtime(&s->e)); snprintf(s->id, BUFSIZ - 1, "%s%c%s%c%ld%c%s", s->from, config_option.fdl, s->to, config_option.fdl, s->e, config_option.fdl, s->randstr); link_session(s); } else { if (!strcmp(type, "script")) snprintf(s->script.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); if (!strcmp(type, "input")) snprintf(s->input.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); if (!strcmp(type, "time")) { snprintf(s->time.str, BUFSIZ - 1, "%s/%s", config_option.logdir, dirp->d_name); count_dur(s); } } } if (closedir(dp) < 0) { LL(); fprintf(stderr, "%s: can't closedir(%s): %s (%i)\n", progname, config_option.logdir, strerror(errno), errno); exit(EXIT_FAILURE); } if (argc) { char from[BUFSIZ]; char to[BUFSIZ]; char randstr[BUFSIZ]; time_t e; int div, maxwait; session *s; snprintf(match, BUFSIZ - 1, "%%32[^%c]%c%%32[^%c]%c%%ld%c%%64[A-Za-z0-9]", config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl, config_option.fdl); if (sscanf(argv[0], match, from, to, &e, randstr) != 4) { LL(); fprintf(stderr, "%s: invalid format: %s\n", progname, argv[0]); fprintf(stderr, "%s: the appropiate format is %s\n", progname, match); exit(EXIT_FAILURE); } s = find_session(from, to, e, randstr); if (s == (session *) 0) { LL(); fprintf(stderr, "%s: no such session: %s\n", progname, argv[0]); exit(EXIT_FAILURE); } div = 1; maxwait = 1; if (argc > 1) div = atoi(argv[1]); if (argc > 2) maxwait = atoi(argv[2]); if (div == 0) { FILE *f_script = fopen(s->script.str, "r"); char buffer[BUFSIZ]; if (f_script == (FILE *) 0) { LL(); fprintf(stderr, "%s: %s: %s (%i)\n", progname, s->script.str, strerror(errno), errno); exit(EXIT_FAILURE); } while (fread(&buffer, 1, BUFSIZ, f_script)) fputs(buffer, stdout); fclose(f_script); } else replay(s->time.str, s->script.str, div, maxwait); exit(EXIT_SUCCESS); } if (!s_top) { fprintf(stderr, "No sessions are logged in %s\n", config_option.logdir); exit(EXIT_SUCCESS); } fprintf(stderr, "%-19s %-8s %-12s %-12s %s\n", "Date", "Duration", "From", "To", "ID"); fprintf(stderr, "%-19s %-8s %-12s %-12s %s\n", "====", "========", "====", "==", "=="); s_top = sort_list(s_top); last = s_top; for (scan = s_top; scan; scan = scan->next) { if ((scan->input.fd = open(scan->input.str, O_RDONLY)) == -1) if(strcmp(scan->type, "interactive")) fprintf(stderr, "[warning]: session %s is missing input information.\n", scan->id); else close(scan->input.fd); if ((scan->script.fd = open(scan->script.str, O_RDONLY)) == -1) { LL(); fprintf(stderr, "[error]: session %s is missing script information.\n", scan->id); continue; } if(!strcmp(scan->type, "interactive")) fprintf(stdout, "%-19s %-8s %-12s %-12s %s\n", scan->date, "-c arg", scan->from, scan->to, scan->id); else fprintf(stdout, "%-19s %-8s %-12s %-12s %s\n", scan->date, int2smalltime(scan->secs), scan->from, scan->to, scan->id); last = scan; } if (s_top && last) fprintf(stderr, "\nUsage: %s ID [MULTIPLIER] [MAXWAIT]\n" "See '%s -h' for more help.\n" "Example: %s %s 1 2\n", progname, progname, progname, last->id); else fprintf(stderr, "\nUsage: %s ID [MULTIPLIER] [MAXWAIT]\n" "See '%s -h' for more help.\n", progname, progname); exit(EXIT_SUCCESS); } void replay(const char *time, const char *script, int div, int maxwait) { char read_buffer[BUFSIZ]; char timebuf[BUFSIZ]; float ftime = 0; int b = 0; int r = 0; int sec, usec; char buffer[BUFSIZ]; struct s_file s_time; struct s_file s_script; struct timeval tv; if (div < 1) div = 1; if (maxwait < 1) maxwait = 1; s_time.f = fopen(time, "r"); if (s_time.f == (FILE *) 0) { LL(); fprintf(stderr, "%s: %s: %s (%i)\n", progname, time, strerror(errno), errno); exit(EXIT_FAILURE); } if ((s_script.fd = open(script, O_RDONLY)) == -1) { LL(); fprintf(stderr, "%s: %s: %s (%i)\n", progname, script, strerror(errno), errno); exit(EXIT_FAILURE); } for (s_time.line = 1; fgets(buffer, BUFSIZ - 1, s_time.f); s_time.line++) { if (sscanf(buffer, "%s %i\n", (char *) &timebuf, &b) != 2) { /* find the two numbers */ LL(); fprintf(stderr, "[error]: line %i: invalid timing information (!= %%f %%i\\n)\n", s_time.line); exit(EXIT_FAILURE); } if (sscanf(timebuf, "%f", &ftime) != 1) { LL(); fprintf(stderr, "[error]: line %i: invalid float (!= %%f)", s_time.line); exit(EXIT_FAILURE); } if (ftime / div > 0.0001) if (ftime / div > maxwait) ftime = maxwait / div; ftime = ftime / div; snprintf(timebuf, BUFSIZ - 1, "%f", ftime); if (sscanf(timebuf, "%i.%i", &sec, &usec) != 2) { LL(); fprintf(stderr, "[error]: line %i: invalid float (!= %%i.%%i)", s_time.line); exit(EXIT_FAILURE); } tv.tv_sec = (time_t) sec; tv.tv_usec = (time_t) usec; if (b > 1024 * 1024 * 8) { /* 8MB */ LL(); fprintf(stderr, "[error]: line %i: wanted to read %i bytes, but the limit is 8MB.\n", s_time.line, b); exit(EXIT_FAILURE); } memset(read_buffer, '\0', BUFSIZ); r = read(s_script.fd, read_buffer, (size_t) b); if (r != b) { LL(); fprintf(stderr, "[failure]: read %i bytes out of %i.\n", r, b); exit(EXIT_FAILURE); } select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv); fputs(read_buffer, stdout); fflush(stdout); memset(read_buffer, '\0', BUFSIZ); } fprintf(stderr, "[info]: EOF\n"); fflush(stderr); } session *session_malloc(void) { session *s = (session *) malloc(sizeof(session)); if (!s) { LL(); fprintf(stderr, "%s: session malloc() failed: %s (%i)\n", progname, strerror(errno), errno); exit(EXIT_FAILURE); } else memset(s, '\0', sizeof(session)); return s; } session *find_session(const char *from, const char *to, time_t e, const char *randstr) { session *scan; for (scan = s_top; scan; scan = scan->next) if (scan->e == e) if (!strcmp(from, scan->from)) if (!strcmp(to, scan->to)) if (!strcmp(randstr, scan->randstr)) return scan; return (session *) 0; } session *find_max(time_t e) { session *scan; for (scan = s_top; scan; scan = scan->next) if (scan->e == e) return scan; return (session *) 0; } void link_session(session * s) { s->next = s_top; if (s_top) s_top->prev = s; s_top = s; } void unlink_session(session * s) { if (s->next) s->next->prev = s->prev; if (s->prev) s->prev->next = s->next; else s_top = s->next; s->next = (session *) 0; s->prev = (session *) 0; return; } void show_sessions(void) { session *scan; for (scan = s_top; scan; scan = scan->next) { fprintf(stderr, "%s\n", scan->from); } } session *sort_list(session * list) { session *p, *q, *e, *tail, *oldhead; int insize, nmerges, psize, qsize, i; if (!list) return (session *) 0; insize = 1; while (1) { p = list; oldhead = list; list = (session *) 0; tail = (session *) 0; nmerges = 0; while (p) { nmerges++; q = p; psize = 0; for (i = 0; i < insize; i++) { psize++; q = q->next; if (!q) break; } qsize = insize; while (psize > 0 || (qsize > 0 && q)) { if (psize == 0) { e = q; q = q->next; qsize--; } else if (qsize == 0 || !q) { e = p; p = p->next; psize--; } else if (p->e < q->e) { e = p; p = p->next; psize--; } else { e = q; q = q->next; qsize--; } if (tail) tail->next = e; else list = e; tail = e; } p = q; } tail->next = (session *) 0; if (nmerges <= 1) return list; insize *= 2; } } void count_dur(session * s) { struct s_file s_time; char buffer[BUFSIZ]; float t_time = 0; s_time.f = fopen(s->time.str, "r"); if (s_time.f == (FILE *) 0) { LL(); fprintf(stderr, "%s: %s (%i)\n", s->time.str, strerror(errno), errno); exit(EXIT_FAILURE); } for (s_time.line = 1; fgets(buffer, BUFSIZ - 1, s_time.f); s_time.line++) { char time[BUFSIZ]; float ftime = 0; int b = 0; if (sscanf(buffer, "%s %i\n", (char *) &time, &b) != 2) { /* find the two numbers */ LL(); fprintf(stderr, "[error]: line %i: invalid timing information (!= %%f %%i\\n)\n", s_time.line); exit(EXIT_FAILURE); } if (sscanf(time, "%f", &ftime) != 1) { LL(); fprintf(stderr, "[error]: line %i: invalid float (!= %%f)", s_time.line); exit(EXIT_FAILURE); } t_time += ftime; } s->secs = (long) t_time; }