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