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