summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-05-05 08:54:40 +0200
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-05-05 08:54:40 +0200
commit29420f774dc55c794cebefe24aadc90280601b79 (patch)
treeb2f371bebd836a564de30c5fbec7a659c90515ad
parentc9d0018b9e527168292f8d40273a9e97223c0bc8 (diff)
downloadlatrace-29420f774dc55c794cebefe24aadc90280601b79.tar.gz
latrace-29420f774dc55c794cebefe24aadc90280601b79.tar.xz
latrace-29420f774dc55c794cebefe24aadc90280601b79.zip
adding OUTPUT_TTY config file option
- added OUTPUT_TTY config file option it is possible to catch traced program tty output and storing it to the configured file (0/1/2 descriptors are monitored) - added automated test for the option - refactoring process method a bit - disabling connection between -R and -q options
-rw-r--r--ChangeLog4
-rw-r--r--doc/latrace.txt2
-rw-r--r--etc/latrace.d/latrace.conf.in4
-rw-r--r--src/Makefile3
-rw-r--r--src/config-bison.y7
-rw-r--r--src/config-flex.l1
-rw-r--r--src/config.c14
-rw-r--r--src/config.h10
-rw-r--r--src/run.c106
-rw-r--r--src/tty.c137
-rw-r--r--test/script/test_tty_output.sh34
-rwxr-xr-xtest/test.sh11
12 files changed, 299 insertions, 34 deletions
diff --git a/ChangeLog b/ChangeLog
index 5a238ea..8b491b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-05-05 Jiri Olsa <olsajiri@gmail.com>
+ * adding OUTPUT_TTY config file option
+ * disabling connection between -R and -q options
+
2011-04-20 Jiri Olsa <olsajiri@gmail.com>
* prevent gcc warning with single printf like arg passing
* adding automated test support, so far for x86 and x86_64,
diff --git a/doc/latrace.txt b/doc/latrace.txt
index 7a8f2fa..aa3d759 100644
--- a/doc/latrace.txt
+++ b/doc/latrace.txt
@@ -114,7 +114,7 @@ OPTIONS
controled config feature
*-q, --disable*::
- run with disabled auditing (enabled -R)
+ run with disabled auditing
EXAMPLES
diff --git a/etc/latrace.d/latrace.conf.in b/etc/latrace.d/latrace.conf.in
index ec2481d..1c547df 100644
--- a/etc/latrace.d/latrace.conf.in
+++ b/etc/latrace.d/latrace.conf.in
@@ -57,4 +57,8 @@ OPTIONS {
# -D, --detail-args
# display struct arguments in more detail
DETAIL_ARGS = NO
+
+ # no command line option equivalent
+ # stores terminal output to the file
+ # OUTPUT_TTY = "output-tty"
}
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]; \
diff --git a/src/run.c b/src/run.c
index f9f4f32..f7a1ddd 100644
--- a/src/run.c
+++ b/src/run.c
@@ -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;
+}
diff --git a/test/script/test_tty_output.sh b/test/script/test_tty_output.sh
new file mode 100644
index 0000000..d1014d6
--- /dev/null
+++ b/test/script/test_tty_output.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+function tto_init
+{
+ cat > /tmp/tto_latrace.conf <<EOF
+OPTIONS {
+ OUTPUT_TTY = "/tmp/tto_latrace.output"
+}
+EOF
+}
+
+function tto_cleanup
+{
+ rm -f /tmp/tto_latrace.conf
+ rm -f /tmp/tto_latrace.output
+}
+
+function test_tty_output
+{
+ tto_init
+
+ LD_LIBRARY_PATH=$PWD ./latrace -N /tmp/tto_latrace.conf -q /bin/cat \
+ $PWD/test/script/test_tty_output.sh > /dev/null
+
+ diff /tmp/tto_latrace.output $PWD/test/script/test_tty_output.sh
+ if [ $? -ne 0 ]; then
+ echo "FAILED test_tty_output"
+ exit
+ fi
+
+ tto_cleanup
+
+ echo .
+}
diff --git a/test/test.sh b/test/test.sh
index 055a449..efcd545 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -1,7 +1,14 @@
#!/bin/sh
# common tests
-LD_LIBRARY_PATH=$PWD ./latrace -q ./test-common
+echo "[APP test-common]"
+LD_LIBRARY_PATH=$PWD ./latrace -qR ./test-common
# arguments tests
-LD_LIBRARY_PATH=$PWD ./latrace -q -a $PWD/test/lib-test-args.conf ./test-args
+echo "[APP test-args]"
+LD_LIBRARY_PATH=$PWD ./latrace -qR -a $PWD/test/lib-test-args.conf ./test-args
+
+# script tests
+echo "[SCRIPTS]"
+. $PWD/test/script/test_tty_output.sh
+test_tty_output