update lttngtrace todo
[ltt-control.git] / lttng / lttngtrace.c
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.
27 * TODO: drop priv + reenable standard signal handlers + call lttv at the end.
28 */
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <limits.h>
38 #include <dirent.h>
39 #include <sys/wait.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42
43 #if DEBUG
44 #define printf_dbg(fmt, args...) printf(fmt, args)
45 #else
46 #define printf_dbg(fmt, ...)
47 #endif
48
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 ?
54 */
55 static int sigfwd_pid;
56
57 static int recunlink(const char *dirname)
58 {
59 DIR *dir;
60 struct dirent *entry;
61 char path[PATH_MAX];
62
63 dir = opendir(dirname);
64 if (dir == NULL) {
65 if (errno == ENOENT)
66 return 0;
67 perror("Error opendir()");
68 return -errno;
69 }
70
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,
74 entry->d_name);
75 if (entry->d_type == DT_DIR)
76 recunlink(path);
77 else
78 unlink(path);
79 }
80 }
81 closedir(dir);
82 rmdir(dirname);
83
84 return 0;
85 }
86
87 static int start_tracing(void)
88 {
89 int ret;
90 char command[PATH_MAX];
91
92 if (autotrace) {
93 ret = recunlink(trace_path);
94 if (ret)
95 return ret;
96 }
97
98 /*
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).
102 */
103 ret = mkdir(trace_path, S_IRWXU|S_IRWXG);
104 if (ret) {
105 perror("Trace directory creation error");
106 return ret;
107 }
108 ret = system("ltt-armall > /dev/null");
109 if (ret)
110 return ret;
111
112 ret = snprintf(command, PATH_MAX - 1,
113 "lttctl -C -w %s autotrace1 > /dev/null",
114 trace_path);
115 ret = ret < 0 ? ret : 0;
116 if (ret)
117 return ret;
118 ret = system(command);
119 if (ret)
120 return ret;
121 }
122
123 static int stop_tracing(uid_t uid, gid_t egid)
124 {
125 int ret;
126
127 ret = system("lttctl -D autotrace1 > /dev/null");
128 if (ret)
129 return ret;
130 ret = system("ltt-disarmall > /dev/null");
131 if (ret)
132 return ret;
133 /* Hand the trace back to the user after tracing is over */
134 ret = chown(trace_path, uid, egid);
135 if (ret) {
136 perror("chown error");
137 return ret;
138 }
139 }
140
141 static int write_child_pid(pid_t pid, uid_t uid, gid_t gid)
142 {
143 int fd;
144 FILE *fp;
145 int ret;
146
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");
151 if (!fp) {
152 perror("Error writing child pid");
153 return -errno;
154 }
155
156 fprintf(fp, "%u", (unsigned int) pid);
157 ret = fclose(fp);
158 if (ret)
159 perror("Error in fclose");
160 /* Hand pid information file back to user */
161 ret = chown(trace_path_pid, uid, gid);
162 if (ret)
163 perror("chown error");
164 return ret;
165 }
166
167 static int parse_options(int argc, char *argv[], int *arg)
168 {
169 int ret = 0;
170
171 for (;;) {
172 if (*arg >= argc
173 || argv[*arg][0] != '-'
174 || argv[*arg][0] == '\0'
175 || argv[*arg][1] == '\0'
176 || !strcmp(argv[*arg], "--"))
177 break;
178 switch (argv[*arg][1]) {
179 case 'o': if (*arg + 1 >= argc) {
180 printf("Missing -o trace name\n");
181 ret = -EINVAL;
182 break;
183 }
184 trace_path = argv[*arg + 1];
185 (*arg) += 2;
186 break;
187 default: printf("Unknown option -%c\n", argv[*arg][1]);
188 ret = -EINVAL;
189 return ret;
190 }
191 }
192 return ret;
193 }
194
195 static int init_trace_path(void)
196 {
197 int ret;
198
199 if (!trace_path) {
200 trace_path = "/tmp/autotrace1";
201 autotrace = 1;
202 }
203 ret = snprintf(trace_path_pid, PATH_MAX - 1, "%s/%s",
204 trace_path, "pid");
205 ret = ret < 0 ? ret : 0;
206 return ret;
207 }
208
209 static void sighandler(int signo, siginfo_t *siginfo, void *context)
210 {
211 kill(sigfwd_pid, signo);
212 }
213
214 static int init_sighand(sigset_t *saved_mask)
215 {
216 sigset_t sig_all_mask;
217 int gret = 0, ret;
218
219 /* Block signals */
220 ret = sigfillset(&sig_all_mask);
221 if (ret)
222 perror("Error in sigfillset");
223 gret = (gret == 0) ? ret : gret;
224 ret = sigprocmask(SIG_SETMASK, &sig_all_mask, saved_mask);
225 if (ret)
226 perror("Error in sigprocmask");
227 gret = (gret == 0) ? ret : gret;
228 return gret;
229 }
230
231 static int forward_signals(pid_t pid, sigset_t *saved_mask)
232 {
233 struct sigaction act;
234 int gret = 0, ret;
235
236 /* Forward SIGINT and SIGTERM */
237 sigfwd_pid = pid;
238 act.sa_sigaction = sighandler;
239 act.sa_flags = SA_SIGINFO | SA_RESTART;
240 sigemptyset(&act.sa_mask);
241 ret = sigaction(SIGINT, &act, NULL);
242 if (ret)
243 perror("Error in sigaction");
244 gret = (gret == 0) ? ret : gret;
245 ret = sigaction(SIGTERM, &act, NULL);
246 if (ret)
247 perror("Error in sigaction");
248 gret = (gret == 0) ? ret : gret;
249
250 /* Reenable signals */
251 ret = sigprocmask(SIG_SETMASK, saved_mask, NULL);
252 if (ret)
253 perror("Error in sigprocmask");
254 gret = (gret == 0) ? ret : gret;
255 return gret;
256 }
257
258 int main(int argc, char *argv[])
259 {
260 uid_t euid, uid;
261 gid_t egid, gid;
262 pid_t pid;
263 int gret = 0, ret = 0;
264 int arg = 1;
265 sigset_t saved_mask;
266
267 if (argc < 2)
268 return -ENOENT;
269
270 euid = geteuid();
271 uid = getuid();
272 egid = getegid();
273 gid = geteuid();
274
275 if (euid != 0 && uid != 0) {
276 printf("%s must be setuid root\n", argv[0]);
277 return -EPERM;
278 }
279
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);
284
285 if (arg < argc) {
286 ret = parse_options(argc, argv, &arg);
287 if (ret)
288 return ret;
289 }
290
291 ret = init_trace_path();
292 gret = (gret == 0) ? ret : gret;
293
294 ret = init_sighand(&saved_mask);
295 gret = (gret == 0) ? ret : gret;
296
297 ret = start_tracing();
298 if (ret)
299 return ret;
300
301 pid = fork();
302 if (pid > 0) { /* parent */
303 int status;
304
305 ret = forward_signals(pid, &saved_mask);
306 gret = (gret == 0) ? ret : gret;
307 pid = wait(&status);
308 if (pid == -1)
309 gret = (gret == 0) ? -errno : gret;
310
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 */
317 seteuid(uid);
318 /* Reenable signals */
319 ret = sigprocmask(SIG_SETMASK, &saved_mask, NULL);
320 if (ret) {
321 perror("Error in sigprocmask");
322 return ret;
323 }
324 ret = execvp(argv[arg], &argv[arg]);
325 if (ret)
326 perror("Execution error");
327 return ret;
328 } else { /* error */
329 perror("Error in fork");
330 return -errno;
331 }
332 return ret;
333 }
This page took 0.03606 seconds and 4 git commands to generate.