ustd: get_subbuffer: assume prog died if trace cannot be found
[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 /* FIXME: maybe drop this trace */
469 return -1;
470 }
471
472 return 0;
473 }
474
475 int consumer_loop(struct buffer_info *buf)
476 {
477 int result;
478
479 pthread_cleanup_push(decrement_active_buffers, NULL);
480
481 for(;;) {
482 /* get the subbuffer */
483 result = get_subbuffer(buf);
484 if(result == -1) {
485 ERR("error getting subbuffer");
486 continue;
487 }
488 else if(result == GET_SUBBUF_DONE) {
489 /* this is done */
490 break;
491 }
492 else if(result == GET_SUBBUF_DIED) {
493 finish_consuming_dead_subbuffer(buf);
494 break;
495 }
496
497 /* write data to file */
498 write_current_subbuffer(buf);
499 /* FIXME: handle return value? */
500
501 /* put the subbuffer */
502 result = put_subbuffer(buf);
503 if(result == -1) {
504 ERR("unknown error putting subbuffer (channel=%s)", buf->name);
505 break;
506 }
507 else if(result == PUT_SUBBUF_PUSHED) {
508 ERR("Buffer overflow (channel=%s), reader pushed. This channel will not be usable passed this point.", buf->name);
509 break;
510 }
511 else if(result == PUT_SUBBUF_DIED) {
512 DBG("application died while putting subbuffer");
513 /* Skip the first subbuffer. We are not sure it is trustable
514 * because the put_subbuffer() did not complete.
515 */
516 unwrite_last_subbuffer(buf);
517 finish_consuming_dead_subbuffer(buf);
518 break;
519 }
520 else if(result == PUT_SUBBUF_DONE) {
521 /* Done with this subbuffer */
522 /* FIXME: add a case where this branch is used? Upon
523 * normal trace termination, at put_subbuf time, a
524 * special last-subbuffer code could be returned by
525 * the listener.
526 */
527 break;
528 }
529 else if(result == PUT_SUBBUF_OK) {
530 }
531 }
532
533 DBG("thread for buffer %s is stopping", buf->name);
534
535 /* FIXME: destroy, unalloc... */
536
537 pthread_cleanup_pop(1);
538
539 return 0;
540 }
541
542 struct consumer_thread_args {
543 pid_t pid;
544 const char *bufname;
545 };
546
547 void *consumer_thread(void *arg)
548 {
549 struct buffer_info *buf = (struct buffer_info *) arg;
550 struct consumer_thread_args *args = (struct consumer_thread_args *) arg;
551
552 DBG("GOT ARGS: pid %d bufname %s", args->pid, args->bufname);
553
554 buf = connect_buffer(args->pid, args->bufname);
555 if(buf == NULL) {
556 ERR("failed to connect to buffer");
557 goto end;
558 }
559
560 consumer_loop(buf);
561
562 free((void *)args->bufname);
563 destroy_buffer(buf);
564
565 end:
566 free(args);
567 return NULL;
568 }
569
570 int start_consuming_buffer(pid_t pid, const char *bufname)
571 {
572 pthread_t thr;
573 struct consumer_thread_args *args;
574 int result;
575
576 DBG("beginning of start_consuming_buffer: args: pid %d bufname %s", pid, bufname);
577
578 args = (struct consumer_thread_args *) malloc(sizeof(struct consumer_thread_args));
579
580 args->pid = pid;
581 args->bufname = strdup(bufname);
582 DBG("beginning2 of start_consuming_buffer: args: pid %d bufname %s", args->pid, args->bufname);
583
584 result = pthread_create(&thr, NULL, consumer_thread, args);
585 if(result == -1) {
586 ERR("pthread_create failed");
587 return -1;
588 }
589 result = pthread_detach(thr);
590 if(result == -1) {
591 ERR("pthread_detach failed");
592 return -1;
593 }
594 DBG("end of start_consuming_buffer: args: pid %d bufname %s", args->pid, args->bufname);
595
596 return 0;
597 }
598
599 void usage(void)
600 {
601 fprintf(stderr, "Usage:\nustd OPTIONS\n\nOptions:\n"
602 "\t-h\t\tDisplay this usage.\n"
603 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
604 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
605 "\t-d\t\tStart as a daemon.\n"
606 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
607 }
608
609 int parse_args(int argc, char **argv)
610 {
611 int c;
612
613 while (1) {
614 int option_index = 0;
615 static struct option long_options[] = {
616 {"pidfile", 1, 0, 'p'},
617 {"help", 0, 0, 'h'},
618 {"version", 0, 0, 'V'},
619 {0, 0, 0, 0}
620 };
621
622 c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
623 if (c == -1)
624 break;
625
626 switch (c) {
627 case 0:
628 printf("option %s", long_options[option_index].name);
629 if (optarg)
630 printf(" with arg %s", optarg);
631 printf("\n");
632 break;
633 case 's':
634 sock_path = optarg;
635 break;
636 case 'o':
637 trace_path = optarg;
638 if(!is_directory(trace_path)) {
639 ERR("Not a valid directory. (%s)", trace_path);
640 return -1;
641 }
642 break;
643 case 'd':
644 daemon_mode = 1;
645 break;
646 case 'p':
647 pidfile = strdup(optarg);
648 break;
649 case 'h':
650 usage();
651 exit(0);
652 case 'V':
653 printf("Version 0.0\n");
654 break;
655
656 default:
657 /* unknown option or other error; error is
658 printed by getopt, just return */
659 return -1;
660 }
661 }
662
663 return 0;
664 }
665
666 void sigterm_handler(int sig)
667 {
668 terminate_req = 1;
669 }
670
671 static int write_pidfile(const char *file_name, pid_t pid)
672 {
673 FILE *pidfp;
674
675 pidfp = fopen(file_name, "w");
676 if(!pidfp) {
677 PERROR("fopen (%s)", pidfile);
678 WARN("killing child process");
679 return -1;
680 }
681
682 fprintf(pidfp, "%d\n", pid);
683
684 fclose(pidfp);
685
686 return 0;
687 }
688
689 int start_ustd(int fd)
690 {
691 struct ustcomm_ustd ustd;
692 int result;
693 sigset_t sigset;
694 struct sigaction sa;
695
696 result = sigemptyset(&sigset);
697 if(result == -1) {
698 PERROR("sigemptyset");
699 return 1;
700 }
701 sa.sa_handler = sigterm_handler;
702 sa.sa_mask = sigset;
703 sa.sa_flags = SA_RESTART;
704 result = sigaction(SIGTERM, &sa, NULL);
705 if(result == -1) {
706 PERROR("sigaction");
707 return 1;
708 }
709 result = sigaction(SIGINT, &sa, NULL);
710 if(result == -1) {
711 PERROR("sigaction");
712 return 1;
713 }
714
715 result = ustcomm_init_ustd(&ustd, sock_path);
716 if(result == -1) {
717 ERR("failed to initialize socket");
718 return 1;
719 }
720
721 /* setup handler for SIGPIPE */
722 result = sigemptyset(&sigset);
723 if(result == -1) {
724 PERROR("sigemptyset");
725 return 1;
726 }
727 result = sigaddset(&sigset, SIGPIPE);
728 if(result == -1) {
729 PERROR("sigaddset");
730 return 1;
731 }
732 result = sigprocmask(SIG_BLOCK, &sigset, NULL);
733 if(result == -1) {
734 PERROR("sigprocmask");
735 return 1;
736 }
737
738 /* Write pidfile */
739 if(pidfile) {
740 result = write_pidfile(pidfile, getpid());
741 if(result == -1) {
742 ERR("failed to write pidfile");
743 return 1;
744 }
745 }
746
747 /* Notify parent that we are successfully started. */
748 if(fd != -1) {
749 /* write any one character */
750 result = write(fd, "!", 1);
751 if(result == -1) {
752 PERROR("write");
753 return -1;
754 }
755 if(result != 1) {
756 ERR("Problem sending confirmation of daemon start to parent");
757 return -1;
758 }
759 result = close(fd);
760 if(result == -1) {
761 PERROR("close");
762 }
763 }
764
765 /* app loop */
766 for(;;) {
767 char *recvbuf;
768
769 /* check for requests on our public socket */
770 result = ustcomm_ustd_recv_message(&ustd, &recvbuf, NULL, 100);
771 if(result == -1) {
772 ERR("error in ustcomm_ustd_recv_message");
773 goto loop_end;
774 }
775 if(result > 0) {
776 if(!strncmp(recvbuf, "collect", 7)) {
777 pid_t pid;
778 char *bufname;
779 int result;
780
781 result = sscanf(recvbuf, "%*s %d %50as", &pid, &bufname);
782 if(result != 2) {
783 ERR("parsing error: %s", recvbuf);
784 goto free_bufname;
785 }
786
787 result = start_consuming_buffer(pid, bufname);
788 if(result < 0) {
789 ERR("error in add_buffer");
790 goto free_bufname;
791 }
792
793 free_bufname:
794 free(bufname);
795 }
796 else {
797 WARN("unknown command: %s", recvbuf);
798 }
799
800 free(recvbuf);
801 }
802
803 loop_end:
804
805 if(terminate_req) {
806 pthread_mutex_lock(&active_buffers_mutex);
807 if(active_buffers == 0) {
808 pthread_mutex_unlock(&active_buffers_mutex);
809 break;
810 }
811 pthread_mutex_unlock(&active_buffers_mutex);
812 }
813 }
814
815 ustcomm_fini_ustd(&ustd);
816
817 return 0;
818 }
819
820 int start_ustd_daemon()
821 {
822 int result;
823 int fd[2];
824 pid_t child_pid;
825
826 result = pipe(fd);
827
828 result = child_pid = fork();
829 if(result == -1) {
830 PERROR("fork");
831 return -1;
832 }
833 else if(result == 0) {
834 return start_ustd(fd[1]);
835 }
836 else {
837 char buf;
838
839 result = read(fd[0], &buf, 1);
840 if(result == -1) {
841 PERROR("read");
842 return -1;
843 }
844 if(result != 1) {
845 ERR("did not receive valid confirmation that the daemon is started");
846 return -1;
847 }
848
849 result = close(fd[0]);
850 if(result == -1) {
851 PERROR("close");
852 }
853
854 DBG("The daemon is now successfully started");
855 }
856
857 /* Wait for confirmation that the server is ready. */
858
859
860 return 0;
861 }
862
863 int main(int argc, char **argv)
864 {
865 int result;
866
867 result = parse_args(argc, argv);
868 if(result == -1) {
869 exit(1);
870 }
871
872 if(daemon_mode) {
873 result = start_ustd_daemon();
874 }
875 else {
876 result = start_ustd(-1);
877 }
878
879 return result;
880 }
This page took 0.046012 seconds and 4 git commands to generate.