lttngtrace update todo
[ltt-control.git] / lttng / lttngtrace.c
CommitLineData
6f3a6fc5
MD
1/*
2 * lttngtrace.c
3 *
4 * lttngtrace starts/stop system wide tracing around program execution.
5 *
6 * Copyright (c) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
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.
12 *
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.
17 *
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.
21 *
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.
24 *
25 * TODO: LTTng should support per-session tracepoint activation.
26 * TODO: use mkstemp() and save last trace name in user's home directory.
c94203ed 27 * TODO: drop priv + reenable standard signal handlers + call lttv at the end.
432a1b04
MD
28 * TODO: -o option should be limited to creation of files in locations that the
29 * calling user has file creation access.
6f3a6fc5
MD
30 */
31
32#include <errno.h>
33#include <stdio.h>
34#include <fcntl.h>
35#include <signal.h>
36#include <unistd.h>
37#include <stdlib.h>
38#include <string.h>
39#include <limits.h>
40#include <dirent.h>
41#include <sys/wait.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44
45#if DEBUG
46#define printf_dbg(fmt, args...) printf(fmt, args)
47#else
48#define printf_dbg(fmt, ...)
49#endif
50
51static char *trace_path;
52static char trace_path_pid[PATH_MAX];
53static int autotrace; /*
54 * Is the trace_path automatically chosen in /tmp ? Can
55 * we unlink if needed ?
56 */
57static int sigfwd_pid;
8f48e913
MD
58static char *progname = NULL;
59
60void usage(void) {
61 fprintf(stderr, "usage : %s [-o trace_name] command\n", progname);
62 fprintf(stderr, "\nTracing tool for LTTng and UST\n\
63\n\
64Options:\n\
65 -o trace_name\t\tOutput file of the trace\n\
66");
67}
6f3a6fc5
MD
68
69static int recunlink(const char *dirname)
70{
71 DIR *dir;
72 struct dirent *entry;
73 char path[PATH_MAX];
74
75 dir = opendir(dirname);
76 if (dir == NULL) {
77 if (errno == ENOENT)
78 return 0;
79 perror("Error opendir()");
80 return -errno;
81 }
82
83 while ((entry = readdir(dir)) != NULL) {
84 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
85 snprintf(path, (size_t) PATH_MAX, "%s/%s", dirname,
86 entry->d_name);
87 if (entry->d_type == DT_DIR)
88 recunlink(path);
89 else
90 unlink(path);
91 }
92 }
93 closedir(dir);
94 rmdir(dirname);
95
96 return 0;
97}
98
99static int start_tracing(void)
100{
101 int ret;
102 char command[PATH_MAX];
103
104 if (autotrace) {
105 ret = recunlink(trace_path);
106 if (ret)
107 return ret;
108 }
109
110 /*
111 * Create the directory in /tmp to deal with races (refuse if fail).
112 * Only allow user and group to read the trace data (to limit
113 * information disclosure).
114 */
115 ret = mkdir(trace_path, S_IRWXU|S_IRWXG);
116 if (ret) {
117 perror("Trace directory creation error");
118 return ret;
119 }
120 ret = system("ltt-armall > /dev/null");
121 if (ret)
122 return ret;
123
124 ret = snprintf(command, PATH_MAX - 1,
125 "lttctl -C -w %s autotrace1 > /dev/null",
126 trace_path);
127 ret = ret < 0 ? ret : 0;
128 if (ret)
129 return ret;
130 ret = system(command);
131 if (ret)
132 return ret;
133}
134
135static int stop_tracing(uid_t uid, gid_t egid)
136{
137 int ret;
138
139 ret = system("lttctl -D autotrace1 > /dev/null");
140 if (ret)
141 return ret;
142 ret = system("ltt-disarmall > /dev/null");
143 if (ret)
144 return ret;
145 /* Hand the trace back to the user after tracing is over */
146 ret = chown(trace_path, uid, egid);
147 if (ret) {
148 perror("chown error");
149 return ret;
150 }
151}
152
153static int write_child_pid(pid_t pid, uid_t uid, gid_t gid)
154{
155 int fd;
156 FILE *fp;
157 int ret;
158
159 /* Create the file as exclusive to deal with /tmp file creation races */
160 fd = open(trace_path_pid, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
161 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
162 fp = fdopen(fd, "w");
163 if (!fp) {
164 perror("Error writing child pid");
165 return -errno;
166 }
8f48e913 167
6f3a6fc5
MD
168 fprintf(fp, "%u", (unsigned int) pid);
169 ret = fclose(fp);
170 if (ret)
171 perror("Error in fclose");
172 /* Hand pid information file back to user */
173 ret = chown(trace_path_pid, uid, gid);
174 if (ret)
175 perror("chown error");
176 return ret;
177}
178
8f48e913 179static int parse_options(int argc, char **argv, int *arg)
6f3a6fc5 180{
8f48e913
MD
181 int i, c;
182
183 while ((c = getopt(argc, argv, "ho:")) != -1) {
184 switch (c) {
185 case 'o':
186 trace_path = strdup(optarg);
6f3a6fc5 187 break;
8f48e913
MD
188 case 'h':
189 usage();
190 exit(EXIT_SUCCESS);
191 case '?':
192 usage();
193 exit(EXIT_FAILURE);
194 default:
195 exit(EXIT_FAILURE);
6f3a6fc5
MD
196 }
197 }
8f48e913
MD
198
199 if (argc - optind > 0) {
200 for (i = optind + 1; i < argc; i++) {
201 printf ("Non-option argument %s\n", argv[i]);
202 }
203 } else {
204 usage();
205 exit(EXIT_FAILURE);
206 }
207
208 *arg = optind;
209
210 return 0;
6f3a6fc5
MD
211}
212
213static int init_trace_path(void)
214{
215 int ret;
216
217 if (!trace_path) {
218 trace_path = "/tmp/autotrace1";
219 autotrace = 1;
220 }
221 ret = snprintf(trace_path_pid, PATH_MAX - 1, "%s/%s",
222 trace_path, "pid");
223 ret = ret < 0 ? ret : 0;
224 return ret;
225}
226
227static void sighandler(int signo, siginfo_t *siginfo, void *context)
228{
229 kill(sigfwd_pid, signo);
230}
231
232static int init_sighand(sigset_t *saved_mask)
233{
234 sigset_t sig_all_mask;
235 int gret = 0, ret;
236
237 /* Block signals */
238 ret = sigfillset(&sig_all_mask);
239 if (ret)
240 perror("Error in sigfillset");
241 gret = (gret == 0) ? ret : gret;
242 ret = sigprocmask(SIG_SETMASK, &sig_all_mask, saved_mask);
243 if (ret)
244 perror("Error in sigprocmask");
245 gret = (gret == 0) ? ret : gret;
246 return gret;
247}
248
249static int forward_signals(pid_t pid, sigset_t *saved_mask)
250{
251 struct sigaction act;
252 int gret = 0, ret;
253
254 /* Forward SIGINT and SIGTERM */
255 sigfwd_pid = pid;
256 act.sa_sigaction = sighandler;
257 act.sa_flags = SA_SIGINFO | SA_RESTART;
258 sigemptyset(&act.sa_mask);
259 ret = sigaction(SIGINT, &act, NULL);
260 if (ret)
261 perror("Error in sigaction");
262 gret = (gret == 0) ? ret : gret;
263 ret = sigaction(SIGTERM, &act, NULL);
264 if (ret)
265 perror("Error in sigaction");
266 gret = (gret == 0) ? ret : gret;
267
268 /* Reenable signals */
269 ret = sigprocmask(SIG_SETMASK, saved_mask, NULL);
270 if (ret)
271 perror("Error in sigprocmask");
272 gret = (gret == 0) ? ret : gret;
273 return gret;
274}
275
276int main(int argc, char *argv[])
277{
278 uid_t euid, uid;
279 gid_t egid, gid;
280 pid_t pid;
281 int gret = 0, ret = 0;
282 int arg = 1;
283 sigset_t saved_mask;
284
8f48e913
MD
285 progname = argv[0];
286
287 if (argc < 2) {
288 usage();
6f3a6fc5 289 return -ENOENT;
8f48e913 290 }
6f3a6fc5
MD
291
292 euid = geteuid();
293 uid = getuid();
294 egid = getegid();
295 gid = geteuid();
296
297 if (euid != 0 && uid != 0) {
8f48e913 298 printf("%s must be setuid root\n", progname);
6f3a6fc5
MD
299 return -EPERM;
300 }
301
302 printf_dbg("euid: %d\n", euid);
303 printf_dbg("uid: %d\n", uid);
304 printf_dbg("egid: %d\n", egid);
305 printf_dbg("gid: %d\n", gid);
306
8f48e913
MD
307 ret = parse_options(argc, argv, &arg);
308 if (ret)
309 return ret;
6f3a6fc5
MD
310
311 ret = init_trace_path();
312 gret = (gret == 0) ? ret : gret;
313
314 ret = init_sighand(&saved_mask);
315 gret = (gret == 0) ? ret : gret;
316
317 ret = start_tracing();
318 if (ret)
319 return ret;
320
321 pid = fork();
322 if (pid > 0) { /* parent */
323 int status;
324
325 ret = forward_signals(pid, &saved_mask);
326 gret = (gret == 0) ? ret : gret;
327 pid = wait(&status);
328 if (pid == -1)
329 gret = (gret == 0) ? -errno : gret;
330
331 ret = stop_tracing(uid, egid);
332 gret = (gret == 0) ? ret : gret;
333 ret = write_child_pid(pid, uid, egid);
334 gret = (gret == 0) ? ret : gret;
335 } else if (pid == 0) { /* child */
336 /* Drop root euid before executing child program */
337 seteuid(uid);
338 /* Reenable signals */
339 ret = sigprocmask(SIG_SETMASK, &saved_mask, NULL);
340 if (ret) {
341 perror("Error in sigprocmask");
342 return ret;
343 }
344 ret = execvp(argv[arg], &argv[arg]);
345 if (ret)
346 perror("Execution error");
347 return ret;
348 } else { /* error */
349 perror("Error in fork");
350 return -errno;
351 }
352 return ret;
353}
This page took 0.034253 seconds and 4 git commands to generate.