update lttngtrace 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.
6f3a6fc5
MD
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
49static char *trace_path;
50static char trace_path_pid[PATH_MAX];
51static int autotrace; /*
52 * Is the trace_path automatically chosen in /tmp ? Can
53 * we unlink if needed ?
54 */
55static int sigfwd_pid;
56
57static 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
87static 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
123static 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
141static 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
167static 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
195static 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
209static void sighandler(int signo, siginfo_t *siginfo, void *context)
210{
211 kill(sigfwd_pid, signo);
212}
213
214static 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
231static 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
258int 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.034788 seconds and 4 git commands to generate.