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