Update version to 0.16
[ust.git] / ust-consumerd / ust-consumerd.c
1 /* Copyright (C) 2009 Pierre-Marc Fournier
2 * 2010 Alexis Halle
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #define _GNU_SOURCE
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/shm.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <inttypes.h>
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <getopt.h>
35
36 #include "ust/ustconsumer.h"
37 #include "../libustconsumer/lowlevel.h"
38 #include "usterr.h"
39
40 char *sock_path=NULL;
41 char *trace_path=NULL;
42 int daemon_mode = 0;
43 char *pidfile = NULL;
44
45 struct ustconsumer_instance *instance;
46
47 struct buffer_info_local {
48 /* output file */
49 int file_fd;
50 /* the offset we must truncate to, to unput the last subbuffer */
51 off_t previous_offset;
52 };
53
54 static int write_pidfile(const char *file_name, pid_t pid)
55 {
56 FILE *pidfp;
57
58 pidfp = fopen(file_name, "w");
59 if(!pidfp) {
60 PERROR("fopen (%s)", file_name);
61 WARN("killing child process");
62 return -1;
63 }
64
65 fprintf(pidfp, "%d\n", pid);
66
67 fclose(pidfp);
68
69 return 0;
70 }
71
72 int create_dir_if_needed(char *dir)
73 {
74 int result;
75 result = mkdir(dir, 0777);
76 if(result == -1) {
77 if(errno != EEXIST) {
78 PERROR("mkdir");
79 return -1;
80 }
81 }
82
83 return 0;
84 }
85
86 int unwrite_last_subbuffer(struct buffer_info *buf)
87 {
88 int result;
89 struct buffer_info_local *buf_local = buf->user_data;
90
91 result = ftruncate(buf_local->file_fd, buf_local->previous_offset);
92 if(result == -1) {
93 PERROR("ftruncate");
94 return -1;
95 }
96
97 result = lseek(buf_local->file_fd, buf_local->previous_offset, SEEK_SET);
98 if(result == (int)(off_t)-1) {
99 PERROR("lseek");
100 return -1;
101 }
102
103 return 0;
104 }
105
106 int write_current_subbuffer(struct buffer_info *buf)
107 {
108 int result;
109 struct buffer_info_local *buf_local = buf->user_data;
110
111 void *subbuf_mem = buf->mem + (buf->consumed_old & (buf->n_subbufs * buf->subbuf_size-1));
112
113 size_t cur_sb_size = subbuffer_data_size(subbuf_mem);
114
115 off_t cur_offset = lseek(buf_local->file_fd, 0, SEEK_CUR);
116 if(cur_offset == (off_t)-1) {
117 PERROR("lseek");
118 return -1;
119 }
120
121 buf_local->previous_offset = cur_offset;
122 DBG("previous_offset: %ld", cur_offset);
123
124 result = patient_write(buf_local->file_fd, subbuf_mem, cur_sb_size);
125 if(result == -1) {
126 PERROR("write");
127 return -1;
128 }
129
130 return 0;
131 }
132
133 int on_read_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
134 {
135 return write_current_subbuffer(buf);
136 }
137
138 int on_read_partial_subbuffer(struct ustconsumer_callbacks *data, struct buffer_info *buf,
139 long subbuf_index, unsigned long valid_length)
140 {
141 struct buffer_info_local *buf_local = buf->user_data;
142 char *tmp;
143 int result;
144 unsigned long pad_size;
145
146 result = patient_write(buf_local->file_fd, buf->mem + subbuf_index * buf->subbuf_size, valid_length);
147 if(result == -1) {
148 ERR("Error writing to buffer file");
149 return result;
150 }
151
152 /* pad with empty bytes */
153 pad_size = PAGE_ALIGN(valid_length)-valid_length;
154 if(pad_size) {
155 tmp = zmalloc(pad_size);
156 result = patient_write(buf_local->file_fd, tmp, pad_size);
157 if(result == -1) {
158 ERR("Error writing to buffer file");
159 return result;
160 }
161 free(tmp);
162 }
163 return result;
164 }
165
166 int on_open_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
167 {
168 char *tmp;
169 int result;
170 int fd;
171 struct buffer_info_local *buf_local =
172 zmalloc(sizeof(struct buffer_info_local));
173
174 if(!buf_local) {
175 ERR("could not allocate buffer_info_local struct");
176 return 1;
177 }
178
179 buf->user_data = buf_local;
180
181 /* open file for output */
182 if(!trace_path) {
183 /* Only create the directory if using the default path, because
184 * of the risk of typo when using trace path override. We don't
185 * want to risk creating plenty of useless directories in that case.
186 */
187 result = create_dir_if_needed(USTCONSUMER_DEFAULT_TRACE_PATH);
188 if(result == -1) {
189 ERR("could not create directory %s", USTCONSUMER_DEFAULT_TRACE_PATH);
190 return 1;
191 }
192
193 trace_path = USTCONSUMER_DEFAULT_TRACE_PATH;
194 }
195
196 if (asprintf(&tmp, "%s/%u_%" PRId64 "", trace_path, buf->pid, buf->pidunique) < 0) {
197 ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 ")",
198 trace_path, buf->pid, buf->pidunique);
199 return 1;
200 }
201 result = create_dir_if_needed(tmp);
202 if(result == -1) {
203 ERR("could not create directory %s", tmp);
204 free(tmp);
205 return 1;
206 }
207 free(tmp);
208
209 if (asprintf(&tmp, "%s/%u_%" PRId64 "/%s", trace_path, buf->pid, buf->pidunique, buf->name) < 0) {
210 ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64 "/%s)",
211 trace_path, buf->pid, buf->pidunique, buf->name);
212 return 1;
213 }
214 again:
215 result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
216 if (result == -1 && errno == EINTR)
217 goto again;
218
219 if(result == -1) {
220 PERROR("open");
221 ERR("failed opening trace file %s", tmp);
222 return 1;
223 }
224 buf_local->file_fd = fd;
225 free(tmp);
226
227 return 0;
228 }
229
230 int on_close_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
231 {
232 struct buffer_info_local *buf_local = buf->user_data;
233 int result;
234
235 again:
236 result = close(buf_local->file_fd);
237 if (result == -1 && errno == EINTR)
238 goto again;
239 free(buf_local);
240 if(result == -1) {
241 PERROR("close");
242 }
243 return 0;
244 }
245
246 int on_put_error(struct ustconsumer_callbacks *data, struct buffer_info *buf)
247 {
248 return unwrite_last_subbuffer(buf);
249 }
250
251 struct ustconsumer_callbacks *new_callbacks()
252 {
253 struct ustconsumer_callbacks *callbacks =
254 zmalloc(sizeof(struct ustconsumer_callbacks));
255
256 if(!callbacks)
257 return NULL;
258
259 callbacks->on_open_buffer = on_open_buffer;
260 callbacks->on_close_buffer = on_close_buffer;
261 callbacks->on_read_subbuffer = on_read_subbuffer;
262 callbacks->on_read_partial_subbuffer = on_read_partial_subbuffer;
263 callbacks->on_put_error = on_put_error;
264 callbacks->on_new_thread = NULL;
265 callbacks->on_close_thread = NULL;
266 callbacks->on_trace_end = NULL;
267
268 return callbacks;
269
270 }
271
272 int is_directory(const char *dir)
273 {
274 int result;
275 struct stat st;
276
277 result = stat(dir, &st);
278 if(result == -1) {
279 PERROR("stat");
280 return 0;
281 }
282
283 if(!S_ISDIR(st.st_mode)) {
284 return 0;
285 }
286
287 return 1;
288 }
289
290 void usage(void)
291 {
292 fprintf(stderr, "Usage:\nust-consumerd OPTIONS\n\nOptions:\n"
293 "\t-h\t\tDisplay this usage.\n"
294 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
295 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
296 "\t-d\t\tStart as a daemon.\n"
297 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
298 }
299
300 int parse_args(int argc, char **argv)
301 {
302 int c;
303
304 while (1) {
305 int option_index = 0;
306 static struct option long_options[] = {
307 {"pidfile", 1, 0, 'p'},
308 {"help", 0, 0, 'h'},
309 {"version", 0, 0, 'V'},
310 {0, 0, 0, 0}
311 };
312
313 c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
314 if (c == -1)
315 break;
316
317 switch (c) {
318 case 0:
319 printf("option %s", long_options[option_index].name);
320 if (optarg)
321 printf(" with arg %s", optarg);
322 printf("\n");
323 break;
324 case 's':
325 sock_path = optarg;
326 break;
327 case 'o':
328 trace_path = optarg;
329 if(!is_directory(trace_path)) {
330 ERR("Not a valid directory. (%s)", trace_path);
331 return -1;
332 }
333 break;
334 case 'd':
335 daemon_mode = 1;
336 break;
337 case 'p':
338 pidfile = strdup(optarg);
339 break;
340 case 'h':
341 usage();
342 exit(0);
343 case 'V':
344 printf("Version 0.0\n");
345 break;
346
347 default:
348 /* unknown option or other error; error is
349 printed by getopt, just return */
350 return -1;
351 }
352 }
353
354 return 0;
355 }
356
357 void sigterm_handler(int sig)
358 {
359 ustconsumer_stop_instance(instance, 0);
360 }
361
362 int start_ustconsumer(int fd)
363 {
364 int result;
365 sigset_t sigset;
366 struct sigaction sa;
367
368 struct ustconsumer_callbacks *callbacks = new_callbacks();
369 if(!callbacks) {
370 PERROR("new_callbacks");
371 return 1;
372 }
373
374 result = sigemptyset(&sigset);
375 if(result == -1) {
376 PERROR("sigemptyset");
377 return 1;
378 }
379 sa.sa_handler = sigterm_handler;
380 sa.sa_mask = sigset;
381 sa.sa_flags = 0;
382 result = sigaction(SIGTERM, &sa, NULL);
383 if(result == -1) {
384 PERROR("sigaction");
385 return 1;
386 }
387 result = sigaction(SIGINT, &sa, NULL);
388 if(result == -1) {
389 PERROR("sigaction");
390 return 1;
391 }
392
393 instance = ustconsumer_new_instance(callbacks, sock_path);
394 if(!instance) {
395 ERR("failed to create ustconsumer instance");
396 return 1;
397 }
398
399 result = ustconsumer_init_instance(instance);
400 if(result) {
401 ERR("failed to initialize ustconsumer instance");
402 return 1;
403 }
404
405 /* setup handler for SIGPIPE */
406 result = sigemptyset(&sigset);
407 if(result == -1) {
408 PERROR("sigemptyset");
409 return 1;
410 }
411 result = sigaddset(&sigset, SIGPIPE);
412 if(result == -1) {
413 PERROR("sigaddset");
414 return 1;
415 }
416 result = sigprocmask(SIG_BLOCK, &sigset, NULL);
417 if(result == -1) {
418 PERROR("sigprocmask");
419 return 1;
420 }
421
422 /* Write pidfile */
423 if(pidfile) {
424 result = write_pidfile(pidfile, getpid());
425 if(result == -1) {
426 ERR("failed to write pidfile");
427 return 1;
428 }
429 }
430
431 /* Notify parent that we are successfully started. */
432 if(fd != -1) {
433 /* write any one character */
434 result = write(fd, "!", 1);
435 if(result == -1) {
436 PERROR("write");
437 return -1;
438 }
439 if(result != 1) {
440 ERR("Problem sending confirmation of daemon start to parent");
441 return -1;
442 }
443 result = close(fd);
444 if(result == -1) {
445 PERROR("close");
446 }
447 }
448
449 ustconsumer_start_instance(instance);
450
451 free(callbacks);
452
453 return 0;
454 }
455
456 int start_ustconsumer_daemon()
457 {
458 int result;
459 int fd[2];
460 pid_t child_pid;
461
462 result = pipe(fd);
463
464 result = child_pid = fork();
465 if(result == -1) {
466 PERROR("fork");
467 return -1;
468 }
469 else if(result == 0) {
470 return start_ustconsumer(fd[1]);
471 }
472 else {
473 char buf;
474
475 result = read(fd[0], &buf, 1);
476 if(result == -1) {
477 PERROR("read");
478 return -1;
479 }
480 if(result != 1) {
481 ERR("did not receive valid confirmation that the daemon is started");
482 return -1;
483 }
484
485 result = close(fd[0]);
486 if(result == -1) {
487 PERROR("close");
488 }
489
490 DBG("The daemon is now successfully started");
491 }
492
493 /* Wait for confirmation that the server is ready. */
494
495
496 return 0;
497 }
498
499 int main(int argc, char **argv)
500 {
501 int result;
502
503 result = parse_args(argc, argv);
504 if(result == -1) {
505 exit(1);
506 }
507
508 if(daemon_mode) {
509 result = start_ustconsumer_daemon();
510 }
511 else {
512 result = start_ustconsumer(-1);
513 }
514
515 return result;
516 }
This page took 0.038725 seconds and 4 git commands to generate.