diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 3 | ||||
-rw-r--r-- | src/config-bison.y | 7 | ||||
-rw-r--r-- | src/config-flex.l | 1 | ||||
-rw-r--r-- | src/config.c | 14 | ||||
-rw-r--r-- | src/config.h | 10 | ||||
-rw-r--r-- | src/run.c | 106 | ||||
-rw-r--r-- | src/tty.c | 137 |
7 files changed, 247 insertions, 31 deletions
diff --git a/src/Makefile b/src/Makefile index 5e58da0..c9c2360 100644 --- a/src/Makefile +++ b/src/Makefile @@ -65,7 +65,8 @@ LATRACE_OBJS=\ src/output.o \ src/config-bison.o \ src/config-flex.o \ - src/lib-include.o + src/lib-include.o \ + src/tty.o OBJS+=$(LATRACE_OBJS) PROGRAMS+=$(LATRACE_BIN) diff --git a/src/config-bison.y b/src/config-bison.y index a544fa7..4f9dae5 100644 --- a/src/config-bison.y +++ b/src/config-bison.y @@ -56,7 +56,7 @@ do { \ %token OPT_TIMESTAMP OPT_FRAMESIZE OPT_FRAMESIZE_CHECK %token OPT_HIDE_TID OPT_FOLLOW_FORK OPT_FOLLOW_EXEC %token OPT_DEMANGLE OPT_BRACES OPT_ENABLE_ARGS -%token OPT_DETAIL_ARGS +%token OPT_DETAIL_ARGS OPT_OUTPUT_TTY %union { @@ -167,6 +167,11 @@ OPTIONS_DEF OPT_DETAIL_ARGS '=' BOOL OPTION_ADD(LT_OPT_DETAIL_ARGS, $4, -1); } | +OPTIONS_DEF OPT_OUTPUT_TTY '=' '"' FILENAME '"' +{ + OPTION_ADD(LT_OPT_OUTPUT_TTY, $5, -1); +} +| /* left blank intentionally */ %% diff --git a/src/config-flex.l b/src/config-flex.l index 61a775e..c2ed192 100644 --- a/src/config-flex.l +++ b/src/config-flex.l @@ -85,6 +85,7 @@ OPTIONS { BEGIN(options); return OPTIONS; } <options>BRACES { return OPT_BRACES; } <options>ENABLE_ARGS { return OPT_ENABLE_ARGS; } <options>DETAIL_ARGS { return OPT_DETAIL_ARGS; } +<options>OUTPUT_TTY { return OPT_OUTPUT_TTY; } <options>{bool} { RETURN_STR(BOOL); } <options>{value} { RETURN_LONG(VALUE); } diff --git a/src/config.c b/src/config.c index 8ba7b0b..83b031c 100644 --- a/src/config.c +++ b/src/config.c @@ -84,7 +84,7 @@ static void usage() printf(" -o, --output file store output to file\n"); printf("\n"); printf(" -R, --ctl-config controled config\n"); - printf(" -q, --disable disable auditing (enables -R)\n"); + printf(" -q, --disable disable auditing\n"); printf("\n"); printf(" -v, --verbose verbose output\n"); printf(" -V, --version display version\n"); @@ -275,6 +275,14 @@ static int process_option_val(struct lt_config_app *cfg, int idx, lt_sh(cfg, args_detailed)); break; + case LT_OPT_OUTPUT_TTY: + cfg->output_tty = 1; + strcpy(cfg->output_tty_file, sval); + + PRINT_VERBOSE(cfg, 1, "OUTPUT_TTY '%s'\n", + cfg->output_tty_file); + break; + default: return -1; } @@ -335,7 +343,6 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) lt_sh(cfg, args_detail_maxlen) = LR_ARGS_DETAIL_MAXLEN; cfg->csort = LT_CSORT_CALL; - /* read the default config file first */ if (read_config(cfg, conf_file)) { printf("failed: read config file '%s'\n", conf_file); @@ -529,7 +536,8 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) case 'q': lt_sh(cfg, disabled) = 1; - /* falling through */ + break; + case 'R': lt_sh(cfg, ctl_config) = 1; break; diff --git a/src/config.h b/src/config.h index 190a590..d800b20 100644 --- a/src/config.h +++ b/src/config.h @@ -84,6 +84,7 @@ enum { LT_OPT_BRACES, LT_OPT_ENABLE_ARGS, LT_OPT_DETAIL_ARGS, + LT_OPT_OUTPUT_TTY, }; struct lt_config_opt { @@ -165,6 +166,9 @@ struct lt_config_app { int csort; + int output_tty; + char output_tty_file[LT_MAXFILE]; + struct lt_thread *threads; struct lt_thread *iter; }; @@ -351,6 +355,12 @@ struct lt_symbol* lt_symbol_get(struct lt_config_shared *cfg, struct lt_config_opt *lt_config_opt_new(int idx, char *sval, long nval); int lt_config_opt_process(struct lt_config_app *cfg, struct lt_list_head *list); +/* tty */ +int tty_master(struct lt_config_app *cfg); +int tty_init(struct lt_config_app *cfg, int master); +int tty_restore(struct lt_config_app *cfg); +int tty_process(struct lt_config_app *cfg, int master); + #define PRINT(fmt, args...) \ do { \ char lpbuf[1024]; \ @@ -40,6 +40,12 @@ extern char **environ; extern struct timeval tv_program_start; extern struct timeval tv_program_stop; +struct lt_process_args { + pid_t pid; + char *dir; + int fd_notify; + int fd_tty_master; +}; static int store_config(struct lt_config_app *cfg, char *file) { @@ -77,13 +83,13 @@ static int get_config_dir(char *buf, int len) return 0; } -static int get_fifo(struct lt_config_app *cfg, int notify_fd, +static int get_fifo(struct lt_config_app *cfg, int fd_notify, char *dir, pid_t *pid) { unsigned char buf[1000]; struct inotify_event *event = (struct inotify_event*) buf; - if (-1 == read(notify_fd, event, 1000)) { + if (-1 == read(fd_notify, event, 1000)) { perror("read notify failed"); return -1; } @@ -140,17 +146,34 @@ static int process_fifo(struct lt_config_app *cfg, struct lt_thread *t) return 0; } -static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_fd) +static int process(struct lt_config_app *cfg, struct lt_process_args *pa) { - int finish = 0, getin = 1; - int max_fd = notify_fd; + int finish = 0, getin = 1, status; fd_set cfg_set, wrk_set; - int status; - + int fd_notify = pa->fd_notify; + int fd_tty_master = pa->fd_tty_master; + int max_fd = fd_notify; +#define MAX(a,b) ((a) < (b) ? (b) : (a)) + FD_ZERO(&cfg_set); - FD_SET(notify_fd, &cfg_set); - while(!waitpid(pid, &status, WNOHANG) || + /* We are here either because of the + * - pipe option + * - or tty output option */ + + if (lt_sh(cfg, pipe)) { + PRINT_VERBOSE(cfg, 1, "doing pipe\n"); + FD_SET(fd_notify, &cfg_set); + max_fd = fd_notify; + } + + if (cfg->output_tty) { + PRINT_VERBOSE(cfg, 1, "doing output tty\n"); + FD_SET(fd_tty_master, &cfg_set); + max_fd = MAX(fd_notify, fd_tty_master); + } + + while(!waitpid(pa->pid, &status, WNOHANG) || /* let all the thread fifo close */ (finish) || /* Get inside at least once, in case the traced program @@ -160,30 +183,37 @@ static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_f (getin)) { +#define CONTINUE_ZERO_RET() { \ +if (!ret) \ + continue; \ +if (ret < 0) \ + printf("latrace failed: select bug ret %d\n", ret); \ +} + struct timeval tv = { 0, 100 }; struct lt_thread *t; int ret; getin = 0; - wrk_set = cfg_set; - if (-1 == (ret = select(max_fd + 1, &wrk_set, NULL, NULL, &tv))) { + + ret = select(max_fd + 1, &wrk_set, NULL, NULL, &tv); + if (-1 == ret) { perror("select failed"); return -1; } - if (!ret) - continue; + CONTINUE_ZERO_RET(); /* process notify */ - if (FD_ISSET(notify_fd, &wrk_set)) { + if (FD_ISSET(fd_notify, &wrk_set)) { int fd; pid_t pid; /* try to get any event at least once again */ getin = 1; - if (-1 == (fd = get_fifo(cfg, notify_fd, dir, &pid))) + if (-1 == (fd = get_fifo(cfg, fd_notify, pa->dir, &pid))) continue; if (NULL == (t = lt_thread_add(cfg, fd, pid))) { @@ -194,10 +224,21 @@ static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_f finish++; FD_SET(fd, &cfg_set); - max_fd = (max_fd < fd ? fd : max_fd); + max_fd = MAX(max_fd, fd); + ret--; + } + + CONTINUE_ZERO_RET(); + + if (cfg->output_tty && + FD_ISSET(fd_tty_master, &wrk_set)) { + if (tty_process(cfg, fd_tty_master)) + FD_CLR(fd_tty_master, &cfg_set); ret--; } + CONTINUE_ZERO_RET(); + /* process fifo */ for(t = cfg->threads; t ; t = t->next) { if (FD_ISSET(t->fifo_fd, &wrk_set)) { @@ -209,6 +250,8 @@ static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_f ret--; } } +#undef CONTINUE_ZERO_RET +#undef MAX } return status; @@ -261,49 +304,60 @@ static int remove_dir(struct lt_config_app *cfg, char *name) int lt_run(struct lt_config_app *cfg) { - pid_t child_pid; char str_dir[LT_MAXFILE]; char str_cfg[LT_MAXFILE]; + struct lt_process_args pa = { .dir = str_dir }; int status; - int fifo_notify_fd = -1; - if (-1 == get_config_dir(str_dir, LT_MAXFILE)) + if (get_config_dir(str_dir, LT_MAXFILE)) return -1; sprintf(str_cfg, "%s/config", str_dir); - if (-1 == store_config(cfg, str_cfg)) + if (store_config(cfg, str_cfg)) return -1; + /* new thread notification descriptor */ if (lt_sh(cfg, pipe) && - (-1 == (fifo_notify_fd = lt_fifo_notify_fd(cfg, str_dir)))) + (-1 == (pa.fd_notify = lt_fifo_notify_fd(cfg, str_dir)))) + return -1; + + /* tty master descriptor */ + if (cfg->output_tty && + (-1 == (pa.fd_tty_master = tty_master(cfg)))) return -1; gettimeofday(&tv_program_start, NULL); - if (0 == (child_pid = fork())) { + if (0 == (pa.pid = fork())) { char str_audit[100]; sprintf(str_audit, "%s/libltaudit.so.%s", CONFIG_LIBDIR, CONFIG_VERSION); + setenv("LD_AUDIT", str_audit, 1); setenv("LT_DIR", str_dir, 1); + if (cfg->output_tty && + tty_init(cfg, pa.fd_tty_master)) + return -1; + PRINT_VERBOSE(cfg, 1, "executing %s\n", cfg->prog); if (-1 == execvp(cfg->prog, cfg->arg)) { + tty_restore(cfg); printf("execve failed for \"%s\" : %s\n", cfg->prog, strerror(errno)); return -1; } - } else if (child_pid < 0) { + } else if (pa.pid < 0) { perror("fork failed"); return -1; } - if (lt_sh(cfg, pipe)) - status = process(cfg, child_pid, str_dir, fifo_notify_fd); + if (lt_sh(cfg, pipe) || cfg->output_tty) + status = process(cfg, &pa); else - waitpid(child_pid, &status, 0); + waitpid(pa.pid, &status, 0); gettimeofday(&tv_program_stop, NULL); diff --git a/src/tty.c b/src/tty.c new file mode 100644 index 0000000..a3c460e --- /dev/null +++ b/src/tty.c @@ -0,0 +1,137 @@ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <setjmp.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "config.h" + +static int setup_slave(int fd) +{ + struct termios tio; + + tcgetattr(fd, &tio); + + /* disable "\n" -> "\r\n" */ + tio.c_oflag &= ~ONLCR; + + return tcsetattr(fd, TCSANOW, &tio); +} + +int tty_master(struct lt_config_app *cfg) +{ + int mfd; + + if ((mfd = getpt()) < 0) { + perror("getpt failed"); + return -1; + } + + if (unlockpt(mfd)) { + perror("unlockpt failed"); + return -1; + } + + PRINT_VERBOSE(cfg, 1, "pty master opened succesfully\n"); + return mfd; +} + +int tty_init(struct lt_config_app *cfg, int master) +{ + int i, slave, num_files = getdtablesize(); + char *sname; + jmp_buf env; + + if (setjmp(env)) { + tty_restore(cfg); + return -1; + } + + sname = (char*) ptsname(master); + if (!sname) + longjmp(env, 1); + + PRINT_VERBOSE(cfg, 1, "closing all opened descriptors\n"); + for(i = 0; i < num_files; i++) + close(i); + + /* get new session before we open new controling tty */ + if (-1 == setsid()) { + perror("setsid failed"); + return -1; + } + + slave = open(sname, O_RDWR); + if (slave != 0) + longjmp(env, 1); + + /* set controling tty */ + if (ioctl(0, TIOCSCTTY, 1)) + longjmp(env, 1); + + if (setup_slave(0)) + longjmp(env, 1); + + dup(0); + dup(0); + return 0; +} + +int tty_restore(struct lt_config_app *cfg) +{ + int i, num_files = getdtablesize(); + + for(i = 0; i < num_files; i++) + close(i); + + open("/dev/tty", O_RDWR); + dup(0); + dup(0); + + return 0; +} + +int tty_process(struct lt_config_app *cfg, int master) +{ +#define BUFSIZE 4096 + char buf[BUFSIZE]; + ssize_t ret; + static int fd = 0; + + if (!fd) { + fd = open(cfg->output_tty_file, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd < 0) { + perror("failed to open OUTPUT_TTY file," + " output is not logged"); + return -1; + } + PRINT_VERBOSE(cfg, 1, "opened tty output file %s\n", + cfg->output_tty_file); + } + + ret = read(master, buf, BUFSIZE); + /* Most likely the other side closed */ + if (ret <= 0) { + PRINT_VERBOSE(cfg, 1, + "failed to read tty, closing [ errno %d '%s']\n", + errno, strerror(errno)); + return -1; + } + + if (fd < 0) + return -1; + + if (write(fd, buf, ret) <= 0) { + perror("failed to write to OUTPUT_TTY file"); + return -1; + } + + return 0; +} |