c96139455d24ecdbb54014f9f829b42819ef2f5f
[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 again:
214 result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
215 if (result == -1 && errno == EINTR)
216 goto again;
217
218 if(result == -1) {
219 PERROR("open");
220 ERR("failed opening trace file %s", tmp);
221 return 1;
222 }
223 buf_local->file_fd = fd;
224 free(tmp);
225
226 return 0;
227 }
228
229 int on_close_buffer(struct ustconsumer_callbacks *data, struct buffer_info *buf)
230 {
231 struct buffer_info_local *buf_local = buf->user_data;
232 int result;
233
234 again:
235 result = close(buf_local->file_fd);
236 if (result == -1 && errno == EINTR)
237 goto again;
238 free(buf_local);
239 if(result == -1) {
240 PERROR("close");
241 }
242 return 0;
243 }
244
245 int on_put_error(struct ustconsumer_callbacks *data, struct buffer_info *buf)
246 {
247 return unwrite_last_subbuffer(buf);
248 }
249
250 struct ustconsumer_callbacks *new_callbacks()
251 {
252 struct ustconsumer_callbacks *callbacks =
253 zmalloc(sizeof(struct ustconsumer_callbacks));
254
255 if(!callbacks)
256 return NULL;
257
258 callbacks->on_open_buffer = on_open_buffer;
259 callbacks->on_close_buffer = on_close_buffer;
260 callbacks->on_read_subbuffer = on_read_subbuffer;
261 callbacks->on_read_partial_subbuffer = on_read_partial_subbuffer;
262 callbacks->on_put_error = on_put_error;
263 callbacks->on_new_thread = NULL;
264 callbacks->on_close_thread = NULL;
265 callbacks->on_trace_end = NULL;
266
267 return callbacks;
268
269 }
270
271 int is_directory(const char *dir)
272 {
273 int result;
274 struct stat st;
275
276 result = stat(dir, &st);
277 if(result == -1) {
278 PERROR("stat");
279 return 0;
280 }
281
282 if(!S_ISDIR(st.st_mode)) {
283 return 0;
284 }
285
286 return 1;
287 }
288
289 void usage(void)
290 {
291 fprintf(stderr, "Usage:\nust-consumerd OPTIONS\n\nOptions:\n"
292 "\t-h\t\tDisplay this usage.\n"
293 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
294 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
295 "\t-d\t\tStart as a daemon.\n"
296 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
297 }
298
299 int parse_args(int argc, char **argv)
300 {
301 int c;
302
303 while (1) {
304 int option_index = 0;
305 static struct option long_options[] = {
306 {"pidfile", 1, 0, 'p'},
307 {"help", 0, 0, 'h'},
308 {"version", 0, 0, 'V'},
309 {0, 0, 0, 0}
310 };
311
312 c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
313 if (c == -1)
314 break;
315
316 switch (c) {
317 case 0:
318 printf("option %s", long_options[option_index].name);
319 if (optarg)
320 printf(" with arg %s", optarg);
321 printf("\n");
322 break;
323 case 's':
324 sock_path = optarg;
325 break;
326 case 'o':
327 trace_path = optarg;
328 if(!is_directory(trace_path)) {
329 ERR("Not a valid directory. (%s)", trace_path);
330 return -1;
331 }
332 break;
333 case 'd':
334 daemon_mode = 1;
335 break;
336 case 'p':
337 pidfile = strdup(optarg);
338 break;
339 case 'h':
340 usage();
341 exit(0);
342 case 'V':
343 printf("Version 0.0\n");
344 break;
345
346 default:
347 /* unknown option or other error; error is
348 printed by getopt, just return */
349 return -1;
350 }
351 }
352
353 return 0;
354 }
355
356 void sigterm_handler(int sig)
357 {
358 ustconsumer_stop_instance(instance, 0);
359 }
360
361 int start_ustconsumer(int fd)
362 {
363 int result;
364 sigset_t sigset;
365 struct sigaction sa;
366
367 struct ustconsumer_callbacks *callbacks = new_callbacks();
368 if(!callbacks) {
369 PERROR("new_callbacks");
370 return 1;
371 }
372
373 result = sigemptyset(&sigset);
374 if(result == -1) {
375 PERROR("sigemptyset");
376 return 1;
377 }
378 sa.sa_handler = sigterm_handler;
379 sa.sa_mask = sigset;
380 sa.sa_flags = 0;
381 result = sigaction(SIGTERM, &sa, NULL);
382 if(result == -1) {
383 PERROR("sigaction");
384 return 1;
385 }
386 result = sigaction(SIGINT, &sa, NULL);
387 if(result == -1) {
388 PERROR("sigaction");
389 return 1;
390 }
391
392 instance = ustconsumer_new_instance(callbacks, sock_path);
393 if(!instance) {
394 ERR("failed to create ustconsumer instance");
395 return 1;
396 }
397
398 result = ustconsumer_init_instance(instance);
399 if(result) {
400 ERR("failed to initialize ustconsumer instance");
401 return 1;
402 }
403
404 /* setup handler for SIGPIPE */
405 result = sigemptyset(&sigset);
406 if(result == -1) {
407 PERROR("sigemptyset");
408 return 1;
409 }
410 result = sigaddset(&sigset, SIGPIPE);
411 if(result == -1) {
412 PERROR("sigaddset");
413 return 1;
414 }
415 result = sigprocmask(SIG_BLOCK, &sigset, NULL);
416 if(result == -1) {
417 PERROR("sigprocmask");
418 return 1;
419 }
420
421 /* Write pidfile */
422 if(pidfile) {
423 result = write_pidfile(pidfile, getpid());
424 if(result == -1) {
425 ERR("failed to write pidfile");
426 return 1;
427 }
428 }
429
430 /* Notify parent that we are successfully started. */
431 if(fd != -1) {
432 /* write any one character */
433 result = write(fd, "!", 1);
434 if(result == -1) {
435 PERROR("write");
436 return -1;
437 }
438 if(result != 1) {
439 ERR("Problem sending confirmation of daemon start to parent");
440 return -1;
441 }
442 result = close(fd);
443 if(result == -1) {
444 PERROR("close");
445 }
446 }
447
448 ustconsumer_start_instance(instance);
449
450 free(callbacks);
451
452 return 0;
453 }
454
455 int start_ustconsumer_daemon()
456 {
457 int result;
458 int fd[2];
459 pid_t child_pid;
460
461 result = pipe(fd);
462
463 result = child_pid = fork();
464 if(result == -1) {
465 PERROR("fork");
466 return -1;
467 }
468 else if(result == 0) {
469 return start_ustconsumer(fd[1]);
470 }
471 else {
472 char buf;
473
474 result = read(fd[0], &buf, 1);
475 if(result == -1) {
476 PERROR("read");
477 return -1;
478 }
479 if(result != 1) {
480 ERR("did not receive valid confirmation that the daemon is started");
481 return -1;
482 }
483
484 result = close(fd[0]);
485 if(result == -1) {
486 PERROR("close");
487 }
488
489 DBG("The daemon is now successfully started");
490 }
491
492 /* Wait for confirmation that the server is ready. */
493
494
495 return 0;
496 }
497
498 int main(int argc, char **argv)
499 {
500 int result;
501
502 result = parse_args(argc, argv);
503 if(result == -1) {
504 exit(1);
505 }
506
507 if(daemon_mode) {
508 result = start_ustconsumer_daemon();
509 }
510 else {
511 result = start_ustconsumer(-1);
512 }
513
514 return result;
515 }
This page took 0.037982 seconds and 3 git commands to generate.