c1dd0cb83d87c2ffd451b4860d70fdd66c985f10
[ust.git] / ustd / ustd.c
1 /* Copyright (C) 2009 Pierre-Marc Fournier
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #define _GNU_SOURCE
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/shm.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <pthread.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 "ustd.h"
36 #include "usterr.h"
37 #include "ustcomm.h"
38
39 /* return value: 0 = subbuffer is finished, it won't produce data anymore
40 * 1 = got subbuffer successfully
41 * <0 = error
42 */
43
44 #define GET_SUBBUF_OK 1
45 #define GET_SUBBUF_DONE 0
46 #define GET_SUBBUF_DIED 2
47
48 #define PUT_SUBBUF_OK 1
49 #define PUT_SUBBUF_DIED 0
50 #define PUT_SUBBUF_PUSHED 2
51 #define PUT_SUBBUF_DONE 3
52
53 char *sock_path=NULL;
54 char *trace_path=NULL;
55 int daemon_mode = 0;
56 char *pidfile = NULL;
57
58 /* Number of active buffers and the mutex to protect it. */
59 int active_buffers = 0;
60 pthread_mutex_t active_buffers_mutex = PTHREAD_MUTEX_INITIALIZER;
61 /* Whether a request to end the program was received. */
62 volatile sig_atomic_t terminate_req = 0;
63
64 int get_subbuffer(struct buffer_info *buf)
65 {
66 char *send_msg=NULL;
67 char *received_msg=NULL;
68 char *rep_code=NULL;
69 int retval;
70 int result;
71
72 asprintf(&send_msg, "get_subbuffer %s", buf->name);
73 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
74 if((result == -1 && (errno == ECONNRESET || errno == EPIPE)) || result == 0) {
75 DBG("app died while being traced");
76 retval = GET_SUBBUF_DIED;
77 goto end;
78 }
79 else if(result < 0) {
80 ERR("get_subbuffer: ustcomm_send_request failed");
81 retval = -1;
82 goto end;
83 }
84
85 result = sscanf(received_msg, "%as %ld", &rep_code, &buf->consumed_old);
86 if(result != 2 && result != 1) {
87 ERR("unable to parse response to get_subbuffer");
88 retval = -1;
89 free(received_msg);
90 goto end_rep;
91 }
92
93 if(!strcmp(rep_code, "OK")) {
94 DBG("got subbuffer %s", buf->name);
95 retval = GET_SUBBUF_OK;
96 }
97 else if(nth_token_is(received_msg, "END", 0) == 1) {
98 retval = GET_SUBBUF_DONE;
99 goto end_rep;
100 }
101 else if(!strcmp(received_msg, "NOTFOUND")) {
102 DBG("For buffer %s, the trace was not found. This likely means it was destroyed by the user.", buf->name);
103 retval = GET_SUBBUF_DIED;
104 goto end_rep;
105 }
106 else {
107 DBG("error getting subbuffer %s", buf->name);
108 retval = -1;
109 }
110
111 /* FIMXE: free correctly the stuff */
112 end_rep:
113 if(rep_code)
114 free(rep_code);
115 end:
116 if(send_msg)
117 free(send_msg);
118 if(received_msg)
119 free(received_msg);
120
121 return retval;
122 }
123
124 int put_subbuffer(struct buffer_info *buf)
125 {
126 char *send_msg=NULL;
127 char *received_msg=NULL;
128 char *rep_code=NULL;
129 int retval;
130 int result;
131
132 asprintf(&send_msg, "put_subbuffer %s %ld", buf->name, buf->consumed_old);
133 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
134 if(result < 0 && (errno == ECONNRESET || errno == EPIPE)) {
135 retval = PUT_SUBBUF_DIED;
136 goto end;
137 }
138 else if(result < 0) {
139 ERR("put_subbuffer: send_message failed");
140 retval = -1;
141 goto end;
142 }
143 else if(result == 0) {
144 /* Program seems finished. However this might not be
145 * the last subbuffer that has to be collected.
146 */
147 retval = PUT_SUBBUF_DIED;
148 goto end;
149 }
150
151 result = sscanf(received_msg, "%as", &rep_code);
152 if(result != 1) {
153 ERR("unable to parse response to put_subbuffer");
154 retval = -1;
155 goto end_rep;
156 }
157
158 if(!strcmp(rep_code, "OK")) {
159 DBG("subbuffer put %s", buf->name);
160 retval = PUT_SUBBUF_OK;
161 }
162 else if(!strcmp(received_msg, "NOTFOUND")) {
163 DBG("For buffer %s, the trace was not found. This likely means it was destroyed by the user.", buf->name);
164 /* However, maybe this was not the last subbuffer. So
165 * we return the program died.
166 */
167 retval = PUT_SUBBUF_DIED;
168 goto end_rep;
169 }
170 else {
171 DBG("put_subbuffer: received error, we were pushed");
172 retval = PUT_SUBBUF_PUSHED;
173 goto end_rep;
174 }
175
176 end_rep:
177 if(rep_code)
178 free(rep_code);
179
180 end:
181 if(send_msg)
182 free(send_msg);
183 if(received_msg)
184 free(received_msg);
185
186 return retval;
187 }
188
189 void decrement_active_buffers(void *arg)
190 {
191 pthread_mutex_lock(&active_buffers_mutex);
192 active_buffers--;
193 pthread_mutex_unlock(&active_buffers_mutex);
194 }
195
196 int create_dir_if_needed(char *dir)
197 {
198 int result;
199 result = mkdir(dir, 0777);
200 if(result == -1) {
201 if(errno != EEXIST) {
202 PERROR("mkdir");
203 return -1;
204 }
205 }
206
207 return 0;
208 }
209
210 int is_directory(const char *dir)
211 {
212 int result;
213 struct stat st;
214
215 result = stat(dir, &st);
216 if(result == -1) {
217 PERROR("stat");
218 return 0;
219 }
220
221 if(!S_ISDIR(st.st_mode)) {
222 return 0;
223 }
224
225 return 1;
226 }
227
228 struct buffer_info *connect_buffer(pid_t pid, const char *bufname)
229 {
230 struct buffer_info *buf;
231 char *send_msg;
232 char *received_msg;
233 int result;
234 char *tmp;
235 int fd;
236 struct shmid_ds shmds;
237
238 buf = (struct buffer_info *) malloc(sizeof(struct buffer_info));
239 if(buf == NULL) {
240 ERR("add_buffer: insufficient memory");
241 return NULL;
242 }
243
244 buf->name = bufname;
245 buf->pid = pid;
246
247 /* connect to app */
248 result = ustcomm_connect_app(buf->pid, &buf->conn);
249 if(result) {
250 WARN("unable to connect to process, it probably died before we were able to connect");
251 return NULL;
252 }
253
254 /* get pidunique */
255 asprintf(&send_msg, "get_pidunique");
256 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
257 free(send_msg);
258 if(result == -1) {
259 ERR("problem in ustcomm_send_request(get_pidunique)");
260 return NULL;
261 }
262 if(result == 0) {
263 goto error;
264 }
265
266 result = sscanf(received_msg, "%lld", &buf->pidunique);
267 if(result != 1) {
268 ERR("unable to parse response to get_pidunique");
269 return NULL;
270 }
271 free(received_msg);
272 DBG("got pidunique %lld", buf->pidunique);
273
274 /* get shmid */
275 asprintf(&send_msg, "get_shmid %s", buf->name);
276 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
277 free(send_msg);
278 if(result == -1) {
279 ERR("problem in ustcomm_send_request(get_shmid)");
280 return NULL;
281 }
282 if(result == 0) {
283 goto error;
284 }
285
286 result = sscanf(received_msg, "%d %d", &buf->shmid, &buf->bufstruct_shmid);
287 if(result != 2) {
288 ERR("unable to parse response to get_shmid (\"%s\")", received_msg);
289 return NULL;
290 }
291 free(received_msg);
292 DBG("got shmids %d %d", buf->shmid, buf->bufstruct_shmid);
293
294 /* get n_subbufs */
295 asprintf(&send_msg, "get_n_subbufs %s", buf->name);
296 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
297 free(send_msg);
298 if(result == -1) {
299 ERR("problem in ustcomm_send_request(g_n_subbufs)");
300 return NULL;
301 }
302 if(result == 0) {
303 goto error;
304 }
305
306 result = sscanf(received_msg, "%d", &buf->n_subbufs);
307 if(result != 1) {
308 ERR("unable to parse response to get_n_subbufs");
309 return NULL;
310 }
311 free(received_msg);
312 DBG("got n_subbufs %d", buf->n_subbufs);
313
314 /* get subbuf size */
315 asprintf(&send_msg, "get_subbuf_size %s", buf->name);
316 result = ustcomm_send_request(&buf->conn, send_msg, &received_msg);
317 free(send_msg);
318 if(result == -1) {
319 ERR("problem in ustcomm_send_request(get_subbuf_size)");
320 return NULL;
321 }
322 if(result == 0) {
323 goto error;
324 }
325
326 result = sscanf(received_msg, "%d", &buf->subbuf_size);
327 if(result != 1) {
328 ERR("unable to parse response to get_subbuf_size");
329 return NULL;
330 }
331 free(received_msg);
332 DBG("got subbuf_size %d", buf->subbuf_size);
333
334 /* attach memory */
335 buf->mem = shmat(buf->shmid, NULL, 0);
336 if(buf->mem == (void *) 0) {
337 PERROR("shmat");
338 return NULL;
339 }
340 DBG("successfully attached buffer memory");
341
342 buf->bufstruct_mem = shmat(buf->bufstruct_shmid, NULL, 0);
343 if(buf->bufstruct_mem == (void *) 0) {
344 PERROR("shmat");
345 return NULL;
346 }
347 DBG("successfully attached buffer bufstruct memory");
348
349 /* obtain info on the memory segment */
350 result = shmctl(buf->shmid, IPC_STAT, &shmds);
351 if(result == -1) {
352 PERROR("shmctl");
353 return NULL;
354 }
355 buf->memlen = shmds.shm_segsz;
356
357 /* open file for output */
358 if(!trace_path) {
359 /* Only create the directory if using the default path, because
360 * of the risk of typo when using trace path override. We don't
361 * want to risk creating plenty of useless directories in that case.
362 */
363 result = create_dir_if_needed(USTD_DEFAULT_TRACE_PATH);
364 if(result == -1) {
365 ERR("could not create directory %s", USTD_DEFAULT_TRACE_PATH);
366 return NULL;
367 }
368
369 trace_path = USTD_DEFAULT_TRACE_PATH;
370 }
371
372 asprintf(&tmp, "%s/%u_%lld", trace_path, buf->pid, buf->pidunique);
373 result = create_dir_if_needed(tmp);
374 if(result == -1) {
375 ERR("could not create directory %s", tmp);
376 free(tmp);
377 return NULL;
378 }
379 free(tmp);
380
381 asprintf(&tmp, "%s/%u_%lld/%s", trace_path, buf->pid, buf->pidunique, buf->name);
382 result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
383 if(result == -1) {
384 PERROR("open");
385 ERR("failed opening trace file %s", tmp);
386 return NULL;
387 }
388 buf->file_fd = fd;
389 free(tmp);
390
391 pthread_mutex_lock(&active_buffers_mutex);
392 active_buffers++;
393 pthread_mutex_unlock(&active_buffers_mutex);
394
395 return buf;
396
397 error:
398 free(buf);
399 return NULL;
400 }
401
402 static void destroy_buffer(struct buffer_info *buf)
403 {
404 int result;
405
406 result = ustcomm_close_app(&buf->conn);
407 if(result == -1) {
408 WARN("problem calling ustcomm_close_app");
409 }
410
411 result = shmdt(buf->mem);
412 if(result == -1) {
413 PERROR("shmdt");
414 }
415
416 result = shmdt(buf->bufstruct_mem);
417 if(result == -1) {
418 PERROR("shmdt");
419 }
420
421 result = close(buf->file_fd);
422 if(result == -1) {
423 PERROR("close");
424 }
425
426 free(buf);
427 }
428
429 int unwrite_last_subbuffer(struct buffer_info *buf)
430 {
431 int result;
432
433 result = ftruncate(buf->file_fd, buf->previous_offset);
434 if(result == -1) {
435 PERROR("ftruncate");
436 return -1;
437 }
438
439 result = lseek(buf->file_fd, buf->previous_offset, SEEK_SET);
440 if(result == (int)(off_t)-1) {
441 PERROR("lseek");
442 return -1;
443 }
444
445 return 0;
446 }
447
448 int write_current_subbuffer(struct buffer_info *buf)
449 {
450 int result;
451
452 void *subbuf_mem = buf->mem + (buf->consumed_old & (buf->n_subbufs * buf->subbuf_size-1));
453
454 size_t cur_sb_size = subbuffer_data_size(subbuf_mem);
455
456 off_t cur_offset = lseek(buf->file_fd, 0, SEEK_CUR);
457 if(cur_offset == (off_t)-1) {
458 PERROR("lseek");
459 return -1;
460 }
461
462 buf->previous_offset = cur_offset;
463 DBG("previous_offset: %ld", cur_offset);
464
465 result = patient_write(buf->file_fd, subbuf_mem, cur_sb_size);
466 if(result == -1) {
467 PERROR("write");
468 return -1;
469 }
470
471 return 0;
472 }
473
474 int consumer_loop(struct buffer_info *buf)
475 {
476 int result;
477
478 pthread_cleanup_push(decrement_active_buffers, NULL);
479
480 for(;;) {
481 /* get the subbuffer */
482 result = get_subbuffer(buf);
483 if(result == -1) {
484 ERR("error getting subbuffer");
485 continue;
486 }
487 else if(result == GET_SUBBUF_DONE) {
488 /* this is done */
489 break;
490 }
491 else if(result == GET_SUBBUF_DIED) {
492 finish_consuming_dead_subbuffer(buf);
493 break;
494 }
495
496 /* write data to file */
497 result = write_current_subbuffer(buf);
498 if(result == -1) {
499 ERR("Failed writing a subbuffer to file (channel=%s). Dropping this buffer.", buf->name);
500 }
501
502 /* put the subbuffer */
503 result = put_subbuffer(buf);
504 if(result == -1) {
505 ERR("unknown error putting subbuffer (channel=%s)", buf->name);
506 break;
507 }
508 else if(result == PUT_SUBBUF_PUSHED) {
509 ERR("Buffer overflow (channel=%s), reader pushed. This channel will not be usable passed this point.", buf->name);
510 break;
511 }
512 else if(result == PUT_SUBBUF_DIED) {
513 DBG("application died while putting subbuffer");
514 /* Skip the first subbuffer. We are not sure it is trustable
515 * because the put_subbuffer() did not complete.
516 */
517 unwrite_last_subbuffer(buf);
518 finish_consuming_dead_subbuffer(buf);
519 break;
520 }
521 else if(result == PUT_SUBBUF_DONE) {
522 /* Done with this subbuffer */
523 /* FIXME: add a case where this branch is used? Upon
524 * normal trace termination, at put_subbuf time, a
525 * special last-subbuffer code could be returned by
526 * the listener.
527 */
528 break;
529 }
530 else if(result == PUT_SUBBUF_OK) {
531 }
532 }
533
534 DBG("thread for buffer %s is stopping", buf->name);
535
536 /* FIXME: destroy, unalloc... */
537
538 pthread_cleanup_pop(1);
539
540 return 0;
541 }
542
543 struct consumer_thread_args {
544 pid_t pid;
545 const char *bufname;
546 };
547
548 void *consumer_thread(void *arg)
549 {
550 struct buffer_info *buf = (struct buffer_info *) arg;
551 struct consumer_thread_args *args = (struct consumer_thread_args *) arg;
552
553 DBG("GOT ARGS: pid %d bufname %s", args->pid, args->bufname);
554
555 buf = connect_buffer(args->pid, args->bufname);
556 if(buf == NULL) {
557 ERR("failed to connect to buffer");
558 goto end;
559 }
560
561 consumer_loop(buf);
562
563 free((void *)args->bufname);
564 destroy_buffer(buf);
565
566 end:
567 free(args);
568 return NULL;
569 }
570
571 int start_consuming_buffer(pid_t pid, const char *bufname)
572 {
573 pthread_t thr;
574 struct consumer_thread_args *args;
575 int result;
576
577 DBG("beginning of start_consuming_buffer: args: pid %d bufname %s", pid, bufname);
578
579 args = (struct consumer_thread_args *) malloc(sizeof(struct consumer_thread_args));
580
581 args->pid = pid;
582 args->bufname = strdup(bufname);
583 DBG("beginning2 of start_consuming_buffer: args: pid %d bufname %s", args->pid, args->bufname);
584
585 result = pthread_create(&thr, NULL, consumer_thread, args);
586 if(result == -1) {
587 ERR("pthread_create failed");
588 return -1;
589 }
590 result = pthread_detach(thr);
591 if(result == -1) {
592 ERR("pthread_detach failed");
593 return -1;
594 }
595 DBG("end of start_consuming_buffer: args: pid %d bufname %s", args->pid, args->bufname);
596
597 return 0;
598 }
599
600 void usage(void)
601 {
602 fprintf(stderr, "Usage:\nustd OPTIONS\n\nOptions:\n"
603 "\t-h\t\tDisplay this usage.\n"
604 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
605 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
606 "\t-d\t\tStart as a daemon.\n"
607 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
608 }
609
610 int parse_args(int argc, char **argv)
611 {
612 int c;
613
614 while (1) {
615 int option_index = 0;
616 static struct option long_options[] = {
617 {"pidfile", 1, 0, 'p'},
618 {"help", 0, 0, 'h'},
619 {"version", 0, 0, 'V'},
620 {0, 0, 0, 0}
621 };
622
623 c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
624 if (c == -1)
625 break;
626
627 switch (c) {
628 case 0:
629 printf("option %s", long_options[option_index].name);
630 if (optarg)
631 printf(" with arg %s", optarg);
632 printf("\n");
633 break;
634 case 's':
635 sock_path = optarg;
636 break;
637 case 'o':
638 trace_path = optarg;
639 if(!is_directory(trace_path)) {
640 ERR("Not a valid directory. (%s)", trace_path);
641 return -1;
642 }
643 break;
644 case 'd':
645 daemon_mode = 1;
646 break;
647 case 'p':
648 pidfile = strdup(optarg);
649 break;
650 case 'h':
651 usage();
652 exit(0);
653 case 'V':
654 printf("Version 0.0\n");
655 break;
656
657 default:
658 /* unknown option or other error; error is
659 printed by getopt, just return */
660 return -1;
661 }
662 }
663
664 return 0;
665 }
666
667 void sigterm_handler(int sig)
668 {
669 terminate_req = 1;
670 }
671
672 static int write_pidfile(const char *file_name, pid_t pid)
673 {
674 FILE *pidfp;
675
676 pidfp = fopen(file_name, "w");
677 if(!pidfp) {
678 PERROR("fopen (%s)", pidfile);
679 WARN("killing child process");
680 return -1;
681 }
682
683 fprintf(pidfp, "%d\n", pid);
684
685 fclose(pidfp);
686
687 return 0;
688 }
689
690 int start_ustd(int fd)
691 {
692 struct ustcomm_ustd ustd;
693 int result;
694 sigset_t sigset;
695 struct sigaction sa;
696
697 result = sigemptyset(&sigset);
698 if(result == -1) {
699 PERROR("sigemptyset");
700 return 1;
701 }
702 sa.sa_handler = sigterm_handler;
703 sa.sa_mask = sigset;
704 sa.sa_flags = SA_RESTART;
705 result = sigaction(SIGTERM, &sa, NULL);
706 if(result == -1) {
707 PERROR("sigaction");
708 return 1;
709 }
710 result = sigaction(SIGINT, &sa, NULL);
711 if(result == -1) {
712 PERROR("sigaction");
713 return 1;
714 }
715
716 result = ustcomm_init_ustd(&ustd, sock_path);
717 if(result == -1) {
718 ERR("failed to initialize socket");
719 return 1;
720 }
721
722 /* setup handler for SIGPIPE */
723 result = sigemptyset(&sigset);
724 if(result == -1) {
725 PERROR("sigemptyset");
726 return 1;
727 }
728 result = sigaddset(&sigset, SIGPIPE);
729 if(result == -1) {
730 PERROR("sigaddset");
731 return 1;
732 }
733 result = sigprocmask(SIG_BLOCK, &sigset, NULL);
734 if(result == -1) {
735 PERROR("sigprocmask");
736 return 1;
737 }
738
739 /* Write pidfile */
740 if(pidfile) {
741 result = write_pidfile(pidfile, getpid());
742 if(result == -1) {
743 ERR("failed to write pidfile");
744 return 1;
745 }
746 }
747
748 /* Notify parent that we are successfully started. */
749 if(fd != -1) {
750 /* write any one character */
751 result = write(fd, "!", 1);
752 if(result == -1) {
753 PERROR("write");
754 return -1;
755 }
756 if(result != 1) {
757 ERR("Problem sending confirmation of daemon start to parent");
758 return -1;
759 }
760 result = close(fd);
761 if(result == -1) {
762 PERROR("close");
763 }
764 }
765
766 /* app loop */
767 for(;;) {
768 char *recvbuf;
769
770 /* check for requests on our public socket */
771 result = ustcomm_ustd_recv_message(&ustd, &recvbuf, NULL, 100);
772 if(result == -1) {
773 ERR("error in ustcomm_ustd_recv_message");
774 goto loop_end;
775 }
776 if(result > 0) {
777 if(!strncmp(recvbuf, "collect", 7)) {
778 pid_t pid;
779 char *bufname;
780 int result;
781
782 result = sscanf(recvbuf, "%*s %d %50as", &pid, &bufname);
783 if(result != 2) {
784 ERR("parsing error: %s", recvbuf);
785 goto free_bufname;
786 }
787
788 result = start_consuming_buffer(pid, bufname);
789 if(result < 0) {
790 ERR("error in add_buffer");
791 goto free_bufname;
792 }
793
794 free_bufname:
795 free(bufname);
796 }
797 else {
798 WARN("unknown command: %s", recvbuf);
799 }
800
801 free(recvbuf);
802 }
803
804 loop_end:
805
806 if(terminate_req) {
807 pthread_mutex_lock(&active_buffers_mutex);
808 if(active_buffers == 0) {
809 pthread_mutex_unlock(&active_buffers_mutex);
810 break;
811 }
812 pthread_mutex_unlock(&active_buffers_mutex);
813 }
814 }
815
816 ustcomm_fini_ustd(&ustd);
817
818 return 0;
819 }
820
821 int start_ustd_daemon()
822 {
823 int result;
824 int fd[2];
825 pid_t child_pid;
826
827 result = pipe(fd);
828
829 result = child_pid = fork();
830 if(result == -1) {
831 PERROR("fork");
832 return -1;
833 }
834 else if(result == 0) {
835 return start_ustd(fd[1]);
836 }
837 else {
838 char buf;
839
840 result = read(fd[0], &buf, 1);
841 if(result == -1) {
842 PERROR("read");
843 return -1;
844 }
845 if(result != 1) {
846 ERR("did not receive valid confirmation that the daemon is started");
847 return -1;
848 }
849
850 result = close(fd[0]);
851 if(result == -1) {
852 PERROR("close");
853 }
854
855 DBG("The daemon is now successfully started");
856 }
857
858 /* Wait for confirmation that the server is ready. */
859
860
861 return 0;
862 }
863
864 int main(int argc, char **argv)
865 {
866 int result;
867
868 result = parse_args(argc, argv);
869 if(result == -1) {
870 exit(1);
871 }
872
873 if(daemon_mode) {
874 result = start_ustd_daemon();
875 }
876 else {
877 result = start_ustd(-1);
878 }
879
880 return result;
881 }
This page took 0.04575 seconds and 3 git commands to generate.