4 * lttngtrace starts/stop system wide tracing around program execution.
6 * Copyright (c) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * This file should be setuid root, and belong to a "tracing" group. Only users
23 * part of the tracing group can trace and view the traces gathered.
25 * TODO: LTTng should support per-session tracepoint activation.
26 * TODO: use mkstemp() and save last trace name in user's home directory.
27 * TODO: drop priv + reenable standard signal handlers + call lttv at the end.
41 #include <sys/types.h>
44 #define printf_dbg(fmt, args...) printf(fmt, args)
46 #define printf_dbg(fmt, ...)
49 static char *trace_path
;
50 static char trace_path_pid
[PATH_MAX
];
51 static int autotrace
; /*
52 * Is the trace_path automatically chosen in /tmp ? Can
53 * we unlink if needed ?
55 static int sigfwd_pid
;
56 static char *progname
= NULL
;
59 fprintf(stderr
, "usage : %s [-o trace_name] command\n", progname
);
60 fprintf(stderr
, "\nTracing tool for LTTng and UST\n\
63 -o trace_name\t\tOutput file of the trace\n\
67 static int recunlink(const char *dirname
)
73 dir
= opendir(dirname
);
77 perror("Error opendir()");
81 while ((entry
= readdir(dir
)) != NULL
) {
82 if (strcmp(entry
->d_name
, ".") && strcmp(entry
->d_name
, "..")) {
83 snprintf(path
, (size_t) PATH_MAX
, "%s/%s", dirname
,
85 if (entry
->d_type
== DT_DIR
)
97 static int start_tracing(void)
100 char command
[PATH_MAX
];
103 ret
= recunlink(trace_path
);
109 * Create the directory in /tmp to deal with races (refuse if fail).
110 * Only allow user and group to read the trace data (to limit
111 * information disclosure).
113 ret
= mkdir(trace_path
, S_IRWXU
|S_IRWXG
);
115 perror("Trace directory creation error");
118 ret
= system("ltt-armall > /dev/null");
122 ret
= snprintf(command
, PATH_MAX
- 1,
123 "lttctl -C -w %s autotrace1 > /dev/null",
125 ret
= ret
< 0 ? ret
: 0;
128 ret
= system(command
);
133 static int stop_tracing(uid_t uid
, gid_t egid
)
137 ret
= system("lttctl -D autotrace1 > /dev/null");
140 ret
= system("ltt-disarmall > /dev/null");
143 /* Hand the trace back to the user after tracing is over */
144 ret
= chown(trace_path
, uid
, egid
);
146 perror("chown error");
151 static int write_child_pid(pid_t pid
, uid_t uid
, gid_t gid
)
157 /* Create the file as exclusive to deal with /tmp file creation races */
158 fd
= open(trace_path_pid
, O_WRONLY
| O_CREAT
| O_EXCL
| O_TRUNC
,
159 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
);
160 fp
= fdopen(fd
, "w");
162 perror("Error writing child pid");
166 fprintf(fp
, "%u", (unsigned int) pid
);
169 perror("Error in fclose");
170 /* Hand pid information file back to user */
171 ret
= chown(trace_path_pid
, uid
, gid
);
173 perror("chown error");
177 static int parse_options(int argc
, char **argv
, int *arg
)
181 while ((c
= getopt(argc
, argv
, "ho:")) != -1) {
184 trace_path
= strdup(optarg
);
197 if (argc
- optind
> 0) {
198 for (i
= optind
+ 1; i
< argc
; i
++) {
199 printf ("Non-option argument %s\n", argv
[i
]);
211 static int init_trace_path(void)
216 trace_path
= "/tmp/autotrace1";
219 ret
= snprintf(trace_path_pid
, PATH_MAX
- 1, "%s/%s",
221 ret
= ret
< 0 ? ret
: 0;
225 static void sighandler(int signo
, siginfo_t
*siginfo
, void *context
)
227 kill(sigfwd_pid
, signo
);
230 static int init_sighand(sigset_t
*saved_mask
)
232 sigset_t sig_all_mask
;
236 ret
= sigfillset(&sig_all_mask
);
238 perror("Error in sigfillset");
239 gret
= (gret
== 0) ? ret
: gret
;
240 ret
= sigprocmask(SIG_SETMASK
, &sig_all_mask
, saved_mask
);
242 perror("Error in sigprocmask");
243 gret
= (gret
== 0) ? ret
: gret
;
247 static int forward_signals(pid_t pid
, sigset_t
*saved_mask
)
249 struct sigaction act
;
252 /* Forward SIGINT and SIGTERM */
254 act
.sa_sigaction
= sighandler
;
255 act
.sa_flags
= SA_SIGINFO
| SA_RESTART
;
256 sigemptyset(&act
.sa_mask
);
257 ret
= sigaction(SIGINT
, &act
, NULL
);
259 perror("Error in sigaction");
260 gret
= (gret
== 0) ? ret
: gret
;
261 ret
= sigaction(SIGTERM
, &act
, NULL
);
263 perror("Error in sigaction");
264 gret
= (gret
== 0) ? ret
: gret
;
266 /* Reenable signals */
267 ret
= sigprocmask(SIG_SETMASK
, saved_mask
, NULL
);
269 perror("Error in sigprocmask");
270 gret
= (gret
== 0) ? ret
: gret
;
274 int main(int argc
, char *argv
[])
279 int gret
= 0, ret
= 0;
295 if (euid
!= 0 && uid
!= 0) {
296 printf("%s must be setuid root\n", progname
);
300 printf_dbg("euid: %d\n", euid
);
301 printf_dbg("uid: %d\n", uid
);
302 printf_dbg("egid: %d\n", egid
);
303 printf_dbg("gid: %d\n", gid
);
305 ret
= parse_options(argc
, argv
, &arg
);
309 ret
= init_trace_path();
310 gret
= (gret
== 0) ? ret
: gret
;
312 ret
= init_sighand(&saved_mask
);
313 gret
= (gret
== 0) ? ret
: gret
;
315 ret
= start_tracing();
320 if (pid
> 0) { /* parent */
323 ret
= forward_signals(pid
, &saved_mask
);
324 gret
= (gret
== 0) ? ret
: gret
;
327 gret
= (gret
== 0) ? -errno
: gret
;
329 ret
= stop_tracing(uid
, egid
);
330 gret
= (gret
== 0) ? ret
: gret
;
331 ret
= write_child_pid(pid
, uid
, egid
);
332 gret
= (gret
== 0) ? ret
: gret
;
333 } else if (pid
== 0) { /* child */
334 /* Drop root euid before executing child program */
336 /* Reenable signals */
337 ret
= sigprocmask(SIG_SETMASK
, &saved_mask
, NULL
);
339 perror("Error in sigprocmask");
342 ret
= execvp(argv
[arg
], &argv
[arg
]);
344 perror("Execution error");
347 perror("Error in fork");
This page took 0.038764 seconds and 4 git commands to generate.