Minor code cleanup
[lttng-tools.git] / ltt-sessiond / ltt-sessiond.c
CommitLineData
826d496d
MD
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
fac6795d
DG
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
fac6795d
DG
17 */
18
19#define _GNU_SOURCE
20#include <fcntl.h>
21#include <getopt.h>
22#include <grp.h>
23#include <limits.h>
24#include <pthread.h>
25#include <signal.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/ipc.h>
30#include <sys/shm.h>
31#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <unistd.h>
35
36#include <urcu/list.h> /* URCU list library (-lurcu) */
37#include <ust/ustctl.h> /* UST control lib (-lust) */
38
39#include "liblttsessiondcomm.h"
40#include "ltt-sessiond.h"
41
686204ab
MD
42const char default_home_dir[] = DEFAULT_HOME_DIR;
43const char default_tracing_group[] = DEFAULT_TRACING_GROUP;
44const char default_ust_sock_dir[] = DEFAULT_UST_SOCK_DIR;
45const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE;
46
fac6795d
DG
47/* Static functions */
48static int set_signal_handler(void);
49static int set_socket_perms(void);
50static void sighandler(int);
51static void daemonize(void);
52static void cleanup(void);
53static int check_existing_daemon(void);
54static int notify_apps(const char*);
55static int connect_app(pid_t);
56static int init_daemon_socket(void);
57static struct lttcomm_lttng_msg *process_client_msg(struct lttcomm_session_msg*);
58
59static void *thread_manage_clients(void *);
60static void *thread_manage_apps(void *);
61
62static int create_session(const char*, uuid_t *);
63static void destroy_session(uuid_t);
64
65static struct ltt_session *find_session(uuid_t);
66
67/* Variables */
68const char *progname;
69const char *opt_tracing_group;
70static int opt_daemon;
71static int is_root; /* Set to 1 if the daemon is running as root */
72
73static char apps_unix_sock_path[PATH_MAX]; /* Global application Unix socket path */
74static char client_unix_sock_path[PATH_MAX]; /* Global client Unix socket path */
75
76static int client_socket;
77static int apps_socket;
78
79static struct ltt_session *current_session;
80static int session_count;
81
82/* Init session's list */
83static struct ltt_session_list ltt_session_list = {
84 .head = CDS_LIST_HEAD_INIT(ltt_session_list.head),
85};
86
87static struct ltt_traceable_app_list ltt_traceable_app_list = {
88 .head = CDS_LIST_HEAD_INIT(ltt_traceable_app_list.head),
89};
90
91/*
92 * thread_manage_apps
93 *
94 * This thread manage the application socket communication
95 */
96static void *thread_manage_apps(void *data)
97{
98 int sock, ret;
99 struct ltt_traceable_app *lta;
100
101 /* TODO: Something more elegant is needed but fine for now */
686204ab 102 struct {
fac6795d 103 int reg; /* 1:register, 0:unregister */
686204ab
MD
104 pid_t pid;
105 uid_t uid;
106 } reg_msg;
fac6795d
DG
107
108 /* Notify all applications to register */
109 notify_apps(default_global_apps_pipe);
110
111 ret = lttcomm_listen_unix_sock(apps_socket);
112 if (ret < 0) {
113 goto error;
114 }
115
116 while (1) {
117 /* Blocking call, waiting for transmission */
118 sock = lttcomm_accept_unix_sock(apps_socket);
119 if (sock < 0) {
120 goto error;
121 }
122
123 /* Basic recv here to handle the very simple data
124 * that the libust send to register (reg_msg).
125 */
126 ret = recv(sock, &reg_msg, sizeof(reg_msg), 0);
127 if (ret < 0) {
128 perror("recv");
129 continue;
130 }
131
132 /* Add application to the global traceable list */
133 if (reg_msg.reg == 1) {
134 /* Registering */
135 lta = malloc(sizeof(struct ltt_traceable_app));
136 lta->pid = reg_msg.pid;
137 lta->uid = reg_msg.uid;
138 cds_list_add(&lta->list, &ltt_traceable_app_list.head);
139 } else {
140 /* Unregistering */
141 lta = NULL;
142 cds_list_for_each_entry(lta, &ltt_traceable_app_list.head, list) {
143 if (lta->pid == reg_msg.pid && lta->uid == reg_msg.uid) {
144 cds_list_del(&lta->list);
145 break;
146 }
147 }
148
149 /* If an item was found, free it from memory */
150 if (lta) {
151 free(lta);
152 }
153 }
154 }
155
156error:
157
158 return NULL;
159}
160
161/*
162 * thread_manage_clients
163 *
164 * This thread manage all clients request using the unix
165 * client socket for communication.
166 */
167static void *thread_manage_clients(void *data)
168{
169 int sock, ret;
170 struct lttcomm_session_msg lsm;
171 struct lttcomm_lttng_msg *llm;
172
173 ret = lttcomm_listen_unix_sock(client_socket);
174 if (ret < 0) {
175 goto error;
176 }
177
178 while (1) {
179 /* Blocking call, waiting for transmission */
180 sock = lttcomm_accept_unix_sock(client_socket);
181 if (sock < 0) {
182 goto error;
183 }
184
185 /*
186 * Data is received from the lttng client. The struct
187 * lttcomm_session_msg (lsm) contains the command and data
188 * request of the client.
189 */
190 ret = lttcomm_recv_unix_sock(sock, &lsm, sizeof(lsm));
191 if (ret < 0) {
192 continue;
193 }
194
195 /* This function dispatch the work to the LTTng or UST libs
196 * and make sure that the reply structure (llm) is filled.
197 */
198 llm = process_client_msg(&lsm);
199
200 /* Having a valid lttcomm_lttng_msg struct, reply is sent back
201 * to the client directly.
202 */
203 if (llm != NULL) {
204 ret = lttcomm_send_unix_sock(sock, llm,
205 sizeof(struct lttcomm_lttng_msg));
206 free(llm);
207 if (ret < 0) {
208 continue;
209 }
210 } else {
211 /* The lttcomm_lttng_msg struct was not allocated
212 * correctly. Fatal error since the daemon is not able
213 * to respond. However, we still permit client connection.
214 *
215 * TODO: We should have a default llm that tells the client
216 * that the sessiond had a fatal error and thus the client could
217 * take action to restart ltt-sessiond or inform someone.
218 */
219 }
220 }
221
222error:
223 return NULL;
224}
225
226/*
227 * connect_app
228 *
229 * Return a socket connected to the libust communication socket
230 * of the application identified by the pid.
231 */
232static int connect_app(pid_t pid)
233{
234 int sock;
235
236 sock = ustctl_connect_pid(pid);
237 if (sock < 0) {
238 fprintf(stderr, "Fail connecting to the PID %d\n", pid);
239 }
240
241 return sock;
242}
243
244/*
245 * notify_apps
246 *
247 * Notify apps by writing 42 to a named pipe using name.
248 * Every applications waiting for a ltt-sessiond will be notified
249 * and re-register automatically to the session daemon.
250 *
251 * Return open or write error value.
252 */
253static int notify_apps(const char *name)
254{
255 int fd;
256 int ret = -1;
257
258 /* Try opening the global pipe */
259 fd = open(name, O_WRONLY);
260 if (fd < 0) {
261 goto error;
262 }
263
264 /* Notify by writing on the pipe */
265 ret = write(fd, "42", 2);
266 if (ret < 0) {
267 perror("write");
268 }
269
270error:
271 return ret;
272}
273
274/*
275 * find_session
276 *
277 * Return a ltt_session structure ptr that matches the uuid.
278 */
279static struct ltt_session *find_session(uuid_t session_id)
280{
281 struct ltt_session *iter = NULL;
282
283 /* Sanity check for NULL session_id */
284 if (uuid_is_null(session_id)) {
285 goto end;
286 }
287
288 cds_list_for_each_entry(iter, &ltt_session_list.head, list) {
289 if (uuid_compare(iter->uuid, session_id)) {
290 break;
291 }
292 }
293
294end:
295 return iter;
296}
297
298/*
299 * destroy_session
300 *
301 * Delete session from the global session list
302 * and free the memory.
303 */
304static void destroy_session(uuid_t session_id)
305{
306 struct ltt_session *iter = NULL;
307
308 cds_list_for_each_entry(iter, &ltt_session_list.head, list) {
309 if (uuid_compare(iter->uuid, session_id)) {
310 cds_list_del(&iter->list);
311 break;
312 }
313 }
314
315 if (iter) {
316 free(iter);
317 session_count--;
318 }
319}
320
321/*
322 * create_session
323 *
324 * Create a brand new session,
325 */
326static int create_session(const char *name, uuid_t *session_id)
327{
328 struct ltt_session *new_session;
329
330 /* Allocate session data structure */
331 new_session = malloc(sizeof(struct ltt_session));
332 if (new_session == NULL) {
333 perror("malloc");
334 goto error;
335 }
336
337 if (name != NULL) {
338 if (asprintf(&new_session->name, "%s", name) < 0) {
339 goto error;
340 }
341 } else {
342 /* Generate session name based on the session count */
343 if (asprintf(&new_session->name, "%s%d", "auto", session_count) < 0) {
344 goto error;
345 }
346 }
347
348 /* UUID generation */
349 uuid_generate(new_session->uuid);
350 uuid_copy(*session_id, new_session->uuid);
351
352 /* Set consumer (identifier) to 0. This means that there is
353 * NO consumer attach to that session yet.
354 */
355 new_session->ust_consumer = 0;
356 new_session->lttng_consumer = 0;
357
358 /* Init list */
359 CDS_INIT_LIST_HEAD(&new_session->ust_traces);
360 CDS_INIT_LIST_HEAD(&new_session->lttng_traces);
361
362 /* Add new session to the global session list */
363 cds_list_add(&new_session->list, &ltt_session_list.head);
364
365 session_count++;
366
367 return 0;
368
369error:
370 return -1;
371}
372
373/*
374 * ust_list_apps
375 *
376 * List traceable user-space application and fill an
377 * array of pids.
378 *
379 * Return size of the array.
380 */
381static size_t ust_list_apps(pid_t *pids)
382{
383 size_t size = 0;
384 struct ltt_traceable_app *iter = NULL;
385
386 cds_list_for_each_entry(iter, &ltt_traceable_app_list.head, list) {
387 if (size >= MAX_APPS_PID) {
388 break;
389 }
390
391 pids[size] = iter->pid;
392 size++;
393 }
394
395 return size;
396}
397
398/*
399 * process_client_msg
400 *
401 * This takes the lttcomm_session_msg struct and process the command requested
402 * by the client. It then creates the reply by allocating a lttcomm_lttng_msg
403 * and fill it with the necessary information.
404 *
405 * It's the caller responsability to free that structure when done with it.
406 *
407 * Return pointer to lttcomm_lttng_msg allocated struct.
408 */
409static struct lttcomm_lttng_msg *process_client_msg(struct lttcomm_session_msg *lsm)
410{
411 struct lttcomm_lttng_msg *llm;
412
413 /* Allocate the reply message structure */
414 llm = malloc(sizeof(struct lttcomm_lttng_msg));
415 if (llm == NULL) {
416 perror("malloc");
417 goto end;
418 }
419
420 /* Copy common data to identify the response
421 * on the lttng client side.
422 */
423 llm->cmd_type = lsm->cmd_type;
424 llm->pid = lsm->pid;
425 if (!uuid_is_null(lsm->session_id)) {
426 uuid_copy(llm->session_id, lsm->session_id);
427 }
428 strncpy(llm->trace_name, lsm->trace_name, sizeof(llm->trace_name));
429
430 /* Default return code.
431 * In a our world, everything is OK... right?
432 */
433 llm->ret_code = LTTCOMM_OK;
434
435 /* Process by command type */
436 switch (lsm->cmd_type) {
437 case UST_LIST_APPS:
438 {
439 llm->u.list_apps.size = ust_list_apps(llm->u.list_apps.pids);
440 break;
441 }
442 default:
443 /* Undefined command */
444 llm->ret_code = LTTCOMM_UND;
445 break;
446 }
447
448end:
449 return llm;
450}
451
452/*
453 * usage function on stderr
454 */
455static void usage(void)
456{
457 fprintf(stderr, "Usage:\n%s OPTIONS\n\nOptions:\n"
458 "\t-h, --help\t\tDisplay this usage.\n"
459 "\t-c, --client-sock PATH\t\tSpecify path for the client unix socket\n"
460 "\t-a, --apps-sock PATH\t\tSpecify path for apps unix socket.\n"
461 "\t-d, --daemonize\t\tStart as a daemon.\n"
462 "\t-g, --group NAME\t\tSpecify the tracing group name. (default: tracing)\n"
463 "\t-V, --version\t\tShow version number.\n",
464 progname);
465}
466
467/*
468 * daemon argument parsing
469 */
470static int parse_args(int argc, char **argv)
471{
472 int c;
473
474 static struct option long_options[] = {
475 { "client-sock", 1, 0, 'c' },
476 { "apps-sock", 1, 0, 'a' },
477 { "daemonize", 0, 0, 'd' },
478 { "help", 0, 0, 'h' },
479 { "group", 1, 0, 'g' },
480 { "version", 0, 0, 'V' },
481 { NULL, 0, 0, 0 }
482 };
483
484 while (1) {
485 int option_index = 0;
486 c = getopt_long(argc, argv, "dhV" "a:c:g:s:", long_options, &option_index);
487 if (c == -1) {
488 break;
489 }
490
491 switch (c) {
492 case 0:
493 fprintf(stderr, "option %s", long_options[option_index].name);
494 if (optarg) {
495 fprintf(stderr, " with arg %s\n", optarg);
496 }
497 break;
498 case 's':
499 snprintf(client_unix_sock_path, PATH_MAX, "%s", optarg);
500 break;
501 case 'a':
502 snprintf(apps_unix_sock_path, PATH_MAX, "%s", optarg);
503 break;
504 case 'd':
505 opt_daemon = 1;
506 break;
507 case 'g':
508 opt_tracing_group = strdup(optarg);
509 break;
510 case 'h':
511 usage();
512 exit(EXIT_FAILURE);
513 case 'V':
514 fprintf(stdout, "%s\n", VERSION);
515 exit(EXIT_SUCCESS);
516 default:
517 /* Unknown option or other error.
518 * Error is printed by getopt, just return */
519 return -1;
520 }
521 }
522
523 return 0;
524}
525
526/*
527 * init_daemon_socket
528 *
529 * Creates the two needed socket by the daemon.
530 * apps_socket - The communication socket for all UST apps.
531 * client_socket - The communication of the cli tool (lttng).
532 */
533static int init_daemon_socket()
534{
535 int ret = 0;
536 mode_t old_umask;
537
538 old_umask = umask(0);
539
540 /* Create client tool unix socket */
541 client_socket = lttcomm_create_unix_sock(client_unix_sock_path);
542 if (client_socket < 0) {
543 ret = -1;
544 goto end;
545 }
546
547 /* File permission MUST be 660 */
548 ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
549 if (ret < 0) {
550 perror("chmod");
551 goto end;
552 }
553
554 /* Create the application unix socket */
555 apps_socket = lttcomm_create_unix_sock(apps_unix_sock_path);
556 if (apps_socket < 0) {
557 ret = -1;
558 goto end;
559 }
560
561 /* File permission MUST be 660 */
562 ret = chmod(apps_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
563 if (ret < 0) {
564 perror("chmod");
565 goto end;
566 }
567
568end:
569 umask(old_umask);
570 return ret;
571}
572
573/*
574 * check_existing_daemon
575 *
576 * Check if the global socket is available.
577 * If yes, error is returned.
578 */
579static int check_existing_daemon()
580{
581 int ret;
582
583 ret = access(client_unix_sock_path, F_OK);
584 if (ret == 0) {
585 ret = access(apps_unix_sock_path, F_OK);
586 }
587
588 return ret;
589}
590
591/*
592 * get_home_dir
593 *
594 * Return pointer to home directory path using
595 * the env variable HOME.
596 *
597 * Default : /tmp
598 */
599static const char *get_home_dir(void)
600{
601 const char *home_path;
602
686204ab 603 if ((home_path = (const char *) getenv("HOME")) == NULL) {
fac6795d
DG
604 home_path = default_home_dir;
605 }
606
607 return home_path;
608}
609
610/*
611 * set_socket_perms
612 *
613 * Set the tracing group gid onto the client socket.
614 */
615static int set_socket_perms(void)
616{
617 int ret;
618 struct group *grp;
619
620 /* Decide which group name to use */
621 (opt_tracing_group != NULL) ?
622 (grp = getgrnam(opt_tracing_group)) :
623 (grp = getgrnam(default_tracing_group));
624
625 if (grp == NULL) {
626 fprintf(stderr, "Missing tracing group. Aborting execution.\n");
627 ret = -1;
628 goto end;
629 }
630
631 ret = chown(client_unix_sock_path, 0, grp->gr_gid);
632 if (ret < 0) {
633 perror("chown");
634 }
635
636end:
637 return ret;
638}
639
640/*
641 * daemonize
642 *
643 * Daemonize ltt-sessiond.
644 */
645static void daemonize(void)
646{
647 pid_t pid, sid;
648 const char *home_dir = get_home_dir();
649
650 /* Fork off the parent process */
651 if ((pid = fork()) < 0) {
652 perror("fork");
653 exit(EXIT_FAILURE);
654 }
655
656 /* Parent can now exit */
657 if (pid > 0) {
658 exit(EXIT_SUCCESS);
659 }
660
661 /* Change the file mode mask */
662 umask(0);
663
664 /* Create a new SID for the child process */
665 if ((sid = setsid()) < 0) {
666 perror("setsid");
667 exit(EXIT_FAILURE);
668 }
669
670 /* Change the current working directory */
671 if ((chdir(home_dir)) < 0) {
672 perror("chdir");
673 exit(EXIT_FAILURE);
674 }
675
676 /* Close out the standard file descriptors */
677 close(STDIN_FILENO);
678 close(STDOUT_FILENO);
679 close(STDERR_FILENO);
680}
681
682/*
683 * set_signal_handler
684 *
685 * Setup signal handler for :
686 * SIGINT, SIGTERM, SIGPIPE
687 */
688static int set_signal_handler(void)
689{
690 int ret = 0;
691 struct sigaction sa;
692 sigset_t sigset;
693
694 if ((ret = sigemptyset(&sigset)) < 0) {
695 perror("sigemptyset");
696 return ret;
697 }
698
699 sa.sa_handler = sighandler;
700 sa.sa_mask = sigset;
701 sa.sa_flags = 0;
702 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
703 perror("sigaction");
704 return ret;
705 }
706
707 if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
708 perror("sigaction");
709 return ret;
710 }
711
712 if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) {
713 perror("sigaction");
714 return ret;
715 }
716
717 return ret;
718}
719
720/**
721 * sighandler
722 *
723 * Signal handler for the daemon
724 */
725static void sighandler(int sig)
726{
727 switch (sig) {
728 case SIGPIPE:
729 case SIGINT:
730 case SIGTERM:
731 cleanup();
686204ab 732 break;
fac6795d
DG
733 default:
734 break;
735 }
736
737 exit(EXIT_SUCCESS);
738}
739
740/*
741 * cleanup
742 *
743 * Cleanup the daemon on exit
744 */
745static void cleanup()
746{
747 /* <fun> */
748 fprintf(stdout, "\n\n%c[%d;%dm*** assert failed *** ==> %c[%dm", 27,1,31,27,0);
749 fprintf(stdout, "%c[%d;%dm Matthew, BEET driven development works!%c[%dm\n",27,1,33,27,0);
750 /* </fun> */
751
752 unlink(client_unix_sock_path);
753 unlink(apps_unix_sock_path);
754}
755
756/*
757 * main
758 */
759int main(int argc, char **argv)
760{
761 int i;
762 int ret = 0;
763 void *status;
764 pthread_t threads[2];
765
766 /* Parse arguments */
767 progname = argv[0];
768 if ((ret = parse_args(argc, argv) < 0)) {
769 goto error;
770 }
771
772 /* Daemonize */
773 if (opt_daemon) {
774 daemonize();
775 }
776
777 /* Check if daemon is UID = 0 */
778 is_root = !getuid();
779
780 /* Set all sockets path */
781 if (is_root) {
782 if (strlen(apps_unix_sock_path) == 0) {
783 (snprintf(apps_unix_sock_path, PATH_MAX,
784 DEFAULT_GLOBAL_APPS_UNIX_SOCK));
785 }
786
787 if (strlen(client_unix_sock_path) == 0) {
788 (snprintf(client_unix_sock_path, PATH_MAX,
789 DEFAULT_GLOBAL_CLIENT_UNIX_SOCK));
790 }
791 } else {
792 if (strlen(apps_unix_sock_path) == 0) {
793 (snprintf(apps_unix_sock_path, PATH_MAX,
794 DEFAULT_HOME_APPS_UNIX_SOCK, get_home_dir()));
795 }
796
797 /* Set the cli tool unix socket path */
798 if (strlen(client_unix_sock_path) == 0) {
799 (snprintf(client_unix_sock_path, PATH_MAX,
800 DEFAULT_HOME_CLIENT_UNIX_SOCK, get_home_dir()));
801 }
802 }
803
804 /* See if daemon already exist. If any of the two
805 * socket needed by the daemon are present, this test fails
806 */
807 if ((ret = check_existing_daemon()) == 0) {
808 fprintf(stderr, "Already running daemon.\n");
809 goto error;
810 }
811
812 if (set_signal_handler() < 0) {
813 goto error;
814 }
815
816 /* Setup the two needed unix socket */
817 if (init_daemon_socket() < 0) {
818 goto error;
819 }
820
821 /* Set credentials to socket */
822 if (is_root && (set_socket_perms() < 0)) {
823 goto error;
824 }
825
826 while (1) {
827 /* Create thread to manage the client socket */
828 ret = pthread_create(&threads[0], NULL, thread_manage_clients, (void *) NULL);
829 if (ret != 0) {
830 perror("pthread_create");
831 goto error;
832 }
833
834 /* Create thread to manage application socket */
835 ret = pthread_create(&threads[1], NULL, thread_manage_apps, (void *) NULL);
836 if (ret != 0) {
837 perror("pthread_create");
838 goto error;
839 }
840
841 for (i = 0; i < 2; i++) {
842 ret = pthread_join(threads[i], &status);
843 if (ret != 0) {
844 perror("pthread_join");
845 goto error;
846 }
847 }
848 }
849
850 cleanup();
851 return 0;
852
853error:
854 cleanup();
855
856 return EXIT_FAILURE;
857}
This page took 0.085184 seconds and 4 git commands to generate.