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