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
;
57 static int recunlink(const char *dirname
)
63 dir
= opendir(dirname
);
67 perror("Error opendir()");
71 while ((entry
= readdir(dir
)) != NULL
) {
72 if (strcmp(entry
->d_name
, ".") && strcmp(entry
->d_name
, "..")) {
73 snprintf(path
, (size_t) PATH_MAX
, "%s/%s", dirname
,
75 if (entry
->d_type
== DT_DIR
)
87 static int start_tracing(void)
90 char command
[PATH_MAX
];
93 ret
= recunlink(trace_path
);
99 * Create the directory in /tmp to deal with races (refuse if fail).
100 * Only allow user and group to read the trace data (to limit
101 * information disclosure).
103 ret
= mkdir(trace_path
, S_IRWXU
|S_IRWXG
);
105 perror("Trace directory creation error");
108 ret
= system("ltt-armall > /dev/null");
112 ret
= snprintf(command
, PATH_MAX
- 1,
113 "lttctl -C -w %s autotrace1 > /dev/null",
115 ret
= ret
< 0 ? ret
: 0;
118 ret
= system(command
);
123 static int stop_tracing(uid_t uid
, gid_t egid
)
127 ret
= system("lttctl -D autotrace1 > /dev/null");
130 ret
= system("ltt-disarmall > /dev/null");
133 /* Hand the trace back to the user after tracing is over */
134 ret
= chown(trace_path
, uid
, egid
);
136 perror("chown error");
141 static int write_child_pid(pid_t pid
, uid_t uid
, gid_t gid
)
147 /* Create the file as exclusive to deal with /tmp file creation races */
148 fd
= open(trace_path_pid
, O_WRONLY
| O_CREAT
| O_EXCL
| O_TRUNC
,
149 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
);
150 fp
= fdopen(fd
, "w");
152 perror("Error writing child pid");
156 fprintf(fp
, "%u", (unsigned int) pid
);
159 perror("Error in fclose");
160 /* Hand pid information file back to user */
161 ret
= chown(trace_path_pid
, uid
, gid
);
163 perror("chown error");
167 static int parse_options(int argc
, char *argv
[], int *arg
)
173 || argv
[*arg
][0] != '-'
174 || argv
[*arg
][0] == '\0'
175 || argv
[*arg
][1] == '\0'
176 || !strcmp(argv
[*arg
], "--"))
178 switch (argv
[*arg
][1]) {
179 case 'o': if (*arg
+ 1 >= argc
) {
180 printf("Missing -o trace name\n");
184 trace_path
= argv
[*arg
+ 1];
187 default: printf("Unknown option -%c\n", argv
[*arg
][1]);
195 static int init_trace_path(void)
200 trace_path
= "/tmp/autotrace1";
203 ret
= snprintf(trace_path_pid
, PATH_MAX
- 1, "%s/%s",
205 ret
= ret
< 0 ? ret
: 0;
209 static void sighandler(int signo
, siginfo_t
*siginfo
, void *context
)
211 kill(sigfwd_pid
, signo
);
214 static int init_sighand(sigset_t
*saved_mask
)
216 sigset_t sig_all_mask
;
220 ret
= sigfillset(&sig_all_mask
);
222 perror("Error in sigfillset");
223 gret
= (gret
== 0) ? ret
: gret
;
224 ret
= sigprocmask(SIG_SETMASK
, &sig_all_mask
, saved_mask
);
226 perror("Error in sigprocmask");
227 gret
= (gret
== 0) ? ret
: gret
;
231 static int forward_signals(pid_t pid
, sigset_t
*saved_mask
)
233 struct sigaction act
;
236 /* Forward SIGINT and SIGTERM */
238 act
.sa_sigaction
= sighandler
;
239 act
.sa_flags
= SA_SIGINFO
| SA_RESTART
;
240 sigemptyset(&act
.sa_mask
);
241 ret
= sigaction(SIGINT
, &act
, NULL
);
243 perror("Error in sigaction");
244 gret
= (gret
== 0) ? ret
: gret
;
245 ret
= sigaction(SIGTERM
, &act
, NULL
);
247 perror("Error in sigaction");
248 gret
= (gret
== 0) ? ret
: gret
;
250 /* Reenable signals */
251 ret
= sigprocmask(SIG_SETMASK
, saved_mask
, NULL
);
253 perror("Error in sigprocmask");
254 gret
= (gret
== 0) ? ret
: gret
;
258 int main(int argc
, char *argv
[])
263 int gret
= 0, ret
= 0;
275 if (euid
!= 0 && uid
!= 0) {
276 printf("%s must be setuid root\n", argv
[0]);
280 printf_dbg("euid: %d\n", euid
);
281 printf_dbg("uid: %d\n", uid
);
282 printf_dbg("egid: %d\n", egid
);
283 printf_dbg("gid: %d\n", gid
);
286 ret
= parse_options(argc
, argv
, &arg
);
291 ret
= init_trace_path();
292 gret
= (gret
== 0) ? ret
: gret
;
294 ret
= init_sighand(&saved_mask
);
295 gret
= (gret
== 0) ? ret
: gret
;
297 ret
= start_tracing();
302 if (pid
> 0) { /* parent */
305 ret
= forward_signals(pid
, &saved_mask
);
306 gret
= (gret
== 0) ? ret
: gret
;
309 gret
= (gret
== 0) ? -errno
: gret
;
311 ret
= stop_tracing(uid
, egid
);
312 gret
= (gret
== 0) ? ret
: gret
;
313 ret
= write_child_pid(pid
, uid
, egid
);
314 gret
= (gret
== 0) ? ret
: gret
;
315 } else if (pid
== 0) { /* child */
316 /* Drop root euid before executing child program */
318 /* Reenable signals */
319 ret
= sigprocmask(SIG_SETMASK
, &saved_mask
, NULL
);
321 perror("Error in sigprocmask");
324 ret
= execvp(argv
[arg
], &argv
[arg
]);
326 perror("Execution error");
329 perror("Error in fork");
This page took 0.036912 seconds and 5 git commands to generate.