8b0dfc164c56bb9571a1202ed8f84d40ada3999b
[ust.git] / libustctl / libustctl.c
1 /* Copyright (C) 2009 Pierre-Marc Fournier, Philippe Proulx-Barrette
2 * Copyright (C) 2011 Ericsson AB
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 #include <stdio.h>
21 #include <unistd.h>
22 #include <getopt.h>
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <dirent.h>
27
28 #include "ustcomm.h"
29 #include "ust/ustctl.h"
30 #include "usterr.h"
31
32 static int do_cmd(int sock,
33 const struct ustcomm_header *req_header,
34 const char *req_data,
35 struct ustcomm_header *res_header,
36 char **res_data)
37 {
38 int result, saved_errno = 0;
39 char *recv_buf;
40
41 recv_buf = zmalloc(USTCOMM_BUFFER_SIZE);
42 if (!recv_buf) {
43 saved_errno = ENOMEM;
44 goto out;
45 }
46
47 result = ustcomm_req(sock, req_header, req_data, res_header, recv_buf);
48 if (result > 0) {
49 saved_errno = -res_header->result;
50 if (res_header->size == 0 || saved_errno > 0) {
51 free(recv_buf);
52 } else {
53 if (res_data) {
54 *res_data = recv_buf;
55 } else {
56 free(recv_buf);
57 }
58 }
59 } else {
60 ERR("ustcomm req failed");
61 if (result == 0) {
62 saved_errno = ENOTCONN;
63 } else {
64 saved_errno = -result;
65 }
66 free(recv_buf);
67 }
68
69 out:
70 errno = saved_errno;
71 if (errno) {
72 return -1;
73 }
74
75 return 0;
76 }
77
78 int ustctl_connect_pid(pid_t pid)
79 {
80 int sock;
81
82 if (ustcomm_connect_app(pid, &sock)) {
83 ERR("could not connect to PID %u", (unsigned int) pid);
84 errno = ENOTCONN;
85 return -1;
86 }
87
88 return sock;
89 }
90
91 static int realloc_pid_list(pid_t **pid_list, unsigned int *pid_list_size)
92 {
93 pid_t *new_pid_list;
94 unsigned int new_pid_list_size = 2 * *pid_list_size;
95
96 new_pid_list = realloc(*pid_list,
97 new_pid_list_size * sizeof(pid_t));
98 if (!*new_pid_list) {
99 return -1;
100 }
101
102 *pid_list = new_pid_list;
103 *pid_list_size = new_pid_list_size;
104
105 return 0;
106 }
107
108 static int get_pids_in_dir(DIR *dir, pid_t **pid_list,
109 unsigned int *pid_list_index,
110 unsigned int *pid_list_size)
111 {
112 struct dirent *dirent;
113 long read_pid;
114
115 while ((dirent = readdir(dir))) {
116 if (!strcmp(dirent->d_name, ".") ||
117 !strcmp(dirent->d_name, "..") ||
118 !strcmp(dirent->d_name, "ust-consumer") ||
119 dirent->d_type == DT_DIR) {
120
121 continue;
122 }
123
124 errno = 0;
125 read_pid = strtol(dirent->d_name, NULL, 10);
126 if (errno) {
127 continue;
128 }
129
130 /*
131 * FIXME: Here we previously called pid_is_online, which
132 * always returned 1, now I replaced it with just 1.
133 * We need to figure out an intelligent way of solving
134 * this, maybe connect-disconnect.
135 */
136 if (1) {
137
138 (*pid_list)[(*pid_list_index)++] = read_pid;
139
140 if (*pid_list_index == *pid_list_size) {
141 if (realloc_pid_list(pid_list, pid_list_size)) {
142 return -1;
143 }
144 }
145 }
146 }
147
148 (*pid_list)[*pid_list_index] = 0; /* Array end */
149
150 return 0;
151 }
152
153 static pid_t *get_pids_non_root(void)
154 {
155 char *dir_name;
156 DIR *dir;
157 unsigned int pid_list_index = 0, pid_list_size = 1;
158 pid_t *pid_list = NULL;
159
160 dir_name = ustcomm_user_sock_dir();
161 if (!dir_name) {
162 return NULL;
163 }
164
165 dir = opendir(dir_name);
166 if (!dir) {
167 goto free_dir_name;
168 }
169
170 pid_list = malloc(pid_list_size * sizeof(pid_t));
171 if (!pid_list) {
172 goto close_dir;
173 }
174
175 if (get_pids_in_dir(dir, &pid_list, &pid_list_index, &pid_list_size)) {
176 /* if any errors are encountered, force freeing of the list */
177 pid_list[0] = 0;
178 }
179
180 close_dir:
181 closedir(dir);
182
183 free_dir_name:
184 free(dir_name);
185
186 return pid_list;
187 }
188
189 static pid_t *get_pids_root(void)
190 {
191 char *dir_name;
192 DIR *tmp_dir, *dir;
193 unsigned int pid_list_index = 0, pid_list_size = 1;
194 pid_t *pid_list = NULL;
195 struct dirent *dirent;
196 int result;
197
198 tmp_dir = opendir(USER_TMP_DIR);
199 if (!tmp_dir) {
200 return NULL;
201 }
202
203 pid_list = malloc(pid_list_size * sizeof(pid_t));
204 if (!pid_list) {
205 goto close_tmp_dir;
206 }
207
208 while ((dirent = readdir(tmp_dir))) {
209 /* Compare the dir to check for the USER_SOCK_DIR_BASE prefix */
210 if (!strncmp(dirent->d_name, USER_SOCK_DIR_BASE,
211 strlen(USER_SOCK_DIR_BASE))) {
212
213 if (asprintf(&dir_name, USER_TMP_DIR "/%s",
214 dirent->d_name) < 0) {
215 goto close_tmp_dir;
216 }
217
218 dir = opendir(dir_name);
219
220 free(dir_name);
221
222 if (!dir) {
223 continue;
224 }
225
226 result = get_pids_in_dir(dir, &pid_list, &pid_list_index,
227 &pid_list_size);
228
229 closedir(dir);
230
231 if (result) {
232 /*
233 * if any errors are encountered,
234 * force freeing of the list
235 */
236 pid_list[0] = 0;
237 break;
238 }
239 }
240 }
241
242 close_tmp_dir:
243 closedir(tmp_dir);
244
245 return pid_list;
246 }
247
248 pid_t *ustctl_get_online_pids(void)
249 {
250 pid_t *pid_list;
251
252 if (geteuid()) {
253 pid_list = get_pids_non_root();
254 } else {
255 pid_list = get_pids_root();
256 }
257
258 if (pid_list && pid_list[0] == 0) {
259 /* No PID at all */
260 free(pid_list);
261 pid_list = NULL;
262 }
263
264 return pid_list;
265 }
266
267 /**
268 * Sets ust_marker state (USTCTL_MS_ON or USTCTL_MS_OFF).
269 *
270 * @param mn Marker name
271 * @param state Marker's new state
272 * @param pid Traced process ID
273 * @return 0 if successful, or errors {USTCTL_ERR_GEN, USTCTL_ERR_ARG}
274 */
275 int ustctl_set_ust_marker_state(int sock, const char *trace, const char *channel,
276 const char *ust_marker, int state)
277 {
278 struct ustcomm_header req_header, res_header;
279 struct ustcomm_ust_marker_info ust_marker_inf;
280 int result;
281
282 result = ustcomm_pack_ust_marker_info(&req_header,
283 &ust_marker_inf,
284 trace,
285 channel,
286 ust_marker);
287 if (result < 0) {
288 errno = -result;
289 return -1;
290 }
291
292 req_header.command = state ? ENABLE_MARKER : DISABLE_MARKER;
293
294 return do_cmd(sock, &req_header, (char *)&ust_marker_inf,
295 &res_header, NULL);
296 }
297
298 /**
299 * Set subbuffer size.
300 *
301 * @param channel_size Channel name and size
302 * @param pid Traced process ID
303 * @return 0 if successful, or error
304 */
305 int ustctl_set_subbuf_size(int sock, const char *trace, const char *channel,
306 unsigned int subbuf_size)
307 {
308 struct ustcomm_header req_header, res_header;
309 struct ustcomm_channel_info ch_inf;
310 int result;
311
312 result = ustcomm_pack_channel_info(&req_header,
313 &ch_inf,
314 trace,
315 channel);
316 if (result < 0) {
317 errno = -result;
318 return -1;
319 }
320
321 req_header.command = SET_SUBBUF_SIZE;
322 ch_inf.subbuf_size = subbuf_size;
323
324 return do_cmd(sock, &req_header, (char *)&ch_inf,
325 &res_header, NULL);
326 }
327
328 /**
329 * Set subbuffer num.
330 *
331 * @param channel_num Channel name and num
332 * @param pid Traced process ID
333 * @return 0 if successful, or error
334 */
335 int ustctl_set_subbuf_num(int sock, const char *trace, const char *channel,
336 unsigned int num)
337 {
338 struct ustcomm_header req_header, res_header;
339 struct ustcomm_channel_info ch_inf;
340 int result;
341
342 result = ustcomm_pack_channel_info(&req_header,
343 &ch_inf,
344 trace,
345 channel);
346 if (result < 0) {
347 errno = -result;
348 return -1;
349 }
350
351 req_header.command = SET_SUBBUF_NUM;
352 ch_inf.subbuf_num = num;
353
354 return do_cmd(sock, &req_header, (char *)&ch_inf,
355 &res_header, NULL);
356
357 }
358
359
360 static int ustctl_get_subbuf_num_size(int sock, const char *trace, const char *channel,
361 int *num, int *size)
362 {
363 struct ustcomm_header req_header, res_header;
364 struct ustcomm_channel_info ch_inf, *ch_inf_res;
365 int result;
366
367
368 result = ustcomm_pack_channel_info(&req_header,
369 &ch_inf,
370 trace,
371 channel);
372 if (result < 0) {
373 errno = -result;
374 return -1;
375 }
376
377 req_header.command = GET_SUBBUF_NUM_SIZE;
378
379 result = do_cmd(sock, &req_header, (char *)&ch_inf,
380 &res_header, (char **)&ch_inf_res);
381 if (result < 0) {
382 return -1;
383 }
384
385 *num = ch_inf_res->subbuf_num;
386 *size = ch_inf_res->subbuf_size;
387
388 free(ch_inf_res);
389
390 return 0;
391 }
392
393 /**
394 * Get subbuffer num.
395 *
396 * @param channel Channel name
397 * @param pid Traced process ID
398 * @return subbuf cnf if successful, or error
399 */
400 int ustctl_get_subbuf_num(int sock, const char *trace, const char *channel)
401 {
402 int num, size, result;
403
404 result = ustctl_get_subbuf_num_size(sock, trace, channel,
405 &num, &size);
406 if (result < 0) {
407 errno = -result;
408 return -1;
409 }
410
411 return num;
412 }
413
414 /**
415 * Get subbuffer size.
416 *
417 * @param channel Channel name
418 * @param pid Traced process ID
419 * @return subbuf size if successful, or error
420 */
421 int ustctl_get_subbuf_size(int sock, const char *trace, const char *channel)
422 {
423 int num, size, result;
424
425 result = ustctl_get_subbuf_num_size(sock, trace, channel,
426 &num, &size);
427 if (result < 0) {
428 errno = -result;
429 return -1;
430 }
431
432 return size;
433 }
434
435 static int do_trace_cmd(int sock, const char *trace, int command)
436 {
437 struct ustcomm_header req_header, res_header;
438 struct ustcomm_single_field trace_inf;
439 int result;
440
441 result = ustcomm_pack_single_field(&req_header,
442 &trace_inf,
443 trace);
444 if (result < 0) {
445 errno = -result;
446 return -1;
447 }
448
449 req_header.command = command;
450
451 return do_cmd(sock, &req_header, (char *)&trace_inf, &res_header, NULL);
452 }
453
454 /**
455 * Destroys an UST trace according to a PID.
456 *
457 * @param pid Traced process ID
458 * @return 0 if successful, or error USTCTL_ERR_GEN
459 */
460 int ustctl_destroy_trace(int sock, const char *trace)
461 {
462 return do_trace_cmd(sock, trace, DESTROY_TRACE);
463 }
464
465 /**
466 * Starts an UST trace (and setups it) according to a PID.
467 *
468 * @param pid Traced process ID
469 * @return 0 if successful, or error USTCTL_ERR_GEN
470 */
471 int ustctl_setup_and_start(int sock, const char *trace)
472 {
473 return do_trace_cmd(sock, trace, START);
474 }
475
476 /**
477 * Creates an UST trace according to a PID.
478 *
479 * @param pid Traced process ID
480 * @return 0 if successful, or error USTCTL_ERR_GEN
481 */
482 int ustctl_create_trace(int sock, const char *trace)
483 {
484 return do_trace_cmd(sock, trace, CREATE_TRACE);
485 }
486
487 /**
488 * Starts an UST trace according to a PID.
489 *
490 * @param pid Traced process ID
491 * @return 0 if successful, or error USTCTL_ERR_GEN
492 */
493 int ustctl_start_trace(int sock, const char *trace)
494 {
495 return do_trace_cmd(sock, trace, START_TRACE);
496 }
497
498 /**
499 * Alloc an UST trace according to a PID.
500 *
501 * @param pid Traced process ID
502 * @return 0 if successful, or error USTCTL_ERR_GEN
503 */
504 int ustctl_alloc_trace(int sock, const char *trace)
505 {
506 return do_trace_cmd(sock, trace, ALLOC_TRACE);
507 }
508
509
510 int ustctl_force_switch(int sock, const char *trace)
511 {
512 return do_trace_cmd(sock, trace, FORCE_SUBBUF_SWITCH);
513 }
514
515 /**
516 * Stops an UST trace according to a PID.
517 *
518 * @param pid Traced process ID
519 * @return 0 if successful, or error USTCTL_ERR_GEN
520 */
521 int ustctl_stop_trace(int sock, const char *trace)
522 {
523 return do_trace_cmd(sock, trace, STOP_TRACE);
524 }
525
526 /**
527 * Counts newlines ('\n') in a string.
528 *
529 * @param str String to search in
530 * @return Total newlines count
531 */
532 unsigned int ustctl_count_nl(const char *str)
533 {
534 unsigned int i = 0, tot = 0;
535
536 while (str[i] != '\0') {
537 if (str[i] == '\n') {
538 ++tot;
539 }
540 ++i;
541 }
542
543 return tot;
544 }
545
546 /**
547 * Frees a CMSF array.
548 *
549 * @param cmsf CMSF array to free
550 * @return 0 if successful, or error USTCTL_ERR_ARG
551 */
552 int ustctl_free_cmsf(struct ust_marker_status *cmsf)
553 {
554 if (cmsf == NULL) {
555 return USTCTL_ERR_ARG;
556 }
557
558 unsigned int i = 0;
559 while (cmsf[i].channel != NULL) {
560 free(cmsf[i].channel);
561 free(cmsf[i].ust_marker);
562 free(cmsf[i].fs);
563 ++i;
564 }
565 free(cmsf);
566
567 return 0;
568 }
569
570 /**
571 * Gets channel/ust_marker/state/format string for a given PID.
572 *
573 * @param cmsf Pointer to CMSF array to be filled (callee allocates, caller
574 * frees with `ustctl_free_cmsf')
575 * @param pid Targeted PID
576 * @return 0 if successful, or -1 on error
577 */
578 int ustctl_get_cmsf(int sock, struct ust_marker_status **cmsf)
579 {
580 struct ustcomm_header req_header, res_header;
581 char *big_str = NULL;
582 int result;
583 struct ust_marker_status *tmp_cmsf = NULL;
584 unsigned int i = 0, cmsf_ind = 0;
585
586 if (cmsf == NULL) {
587 return -1;
588 }
589
590 req_header.command = LIST_MARKERS;
591 req_header.size = 0;
592
593 result = ustcomm_send(sock, &req_header, NULL);
594 if (result <= 0) {
595 PERROR("error while requesting ust_marker list");
596 return -1;
597 }
598
599 result = ustcomm_recv_alloc(sock, &res_header, &big_str);
600 if (result <= 0) {
601 ERR("error while receiving ust_marker list");
602 return -1;
603 }
604
605 tmp_cmsf = (struct ust_marker_status *) zmalloc(sizeof(struct ust_marker_status) *
606 (ustctl_count_nl(big_str) + 1));
607 if (tmp_cmsf == NULL) {
608 ERR("Failed to allocate CMSF array");
609 return -1;
610 }
611
612 /* Parse received reply string (format: "[chan]/[mark] [st] [fs]"): */
613 while (big_str[i] != '\0') {
614 char state;
615
616 sscanf(big_str + i, "ust_marker: %a[^/]/%a[^ ] %c %a[^\n]",
617 &tmp_cmsf[cmsf_ind].channel,
618 &tmp_cmsf[cmsf_ind].ust_marker,
619 &state,
620 &tmp_cmsf[cmsf_ind].fs);
621 tmp_cmsf[cmsf_ind].state = (state == USTCTL_MS_CHR_ON ?
622 USTCTL_MS_ON : USTCTL_MS_OFF); /* Marker state */
623
624 while (big_str[i] != '\n') {
625 ++i; /* Go to next '\n' */
626 }
627 ++i; /* Skip current pointed '\n' */
628 ++cmsf_ind;
629 }
630 tmp_cmsf[cmsf_ind].channel = NULL;
631 tmp_cmsf[cmsf_ind].ust_marker = NULL;
632 tmp_cmsf[cmsf_ind].fs = NULL;
633
634 *cmsf = tmp_cmsf;
635
636 free(big_str);
637 return 0;
638 }
639
640 /**
641 * Frees a TES array.
642 *
643 * @param tes TES array to free
644 * @return 0 if successful, or error USTCTL_ERR_ARG
645 */
646 int ustctl_free_tes(struct trace_event_status *tes)
647 {
648 if (tes == NULL) {
649 return USTCTL_ERR_ARG;
650 }
651
652 unsigned int i = 0;
653 while (tes[i].name != NULL) {
654 free(tes[i].name);
655 ++i;
656 }
657 free(tes);
658
659 return 0;
660 }
661
662 /**
663 * Gets trace_events string for a given PID.
664 *
665 * @param tes Pointer to TES array to be filled (callee allocates, caller
666 * frees with `ustctl_free_tes')
667 * @param pid Targeted PID
668 * @return 0 if successful, or -1 on error
669 */
670 int ustctl_get_tes(int sock, struct trace_event_status **tes)
671 {
672 struct ustcomm_header req_header, res_header;
673 char *big_str = NULL;
674 int result;
675 struct trace_event_status *tmp_tes = NULL;
676 unsigned int i = 0, tes_ind = 0;
677
678 if (tes == NULL) {
679 return -1;
680 }
681
682 req_header.command = LIST_TRACE_EVENTS;
683 req_header.size = 0;
684
685 result = ustcomm_send(sock, &req_header, NULL);
686 if (result != 1) {
687 ERR("error while requesting trace_event list");
688 return -1;
689 }
690
691 result = ustcomm_recv_alloc(sock, &res_header, &big_str);
692 if (result != 1) {
693 ERR("error while receiving ust_marker list");
694 return -1;
695 }
696
697 tmp_tes = (struct trace_event_status *)
698 zmalloc(sizeof(struct trace_event_status) *
699 (ustctl_count_nl(big_str) + 1));
700 if (tmp_tes == NULL) {
701 ERR("Failed to allocate TES array");
702 return -1;
703 }
704
705 /* Parse received reply string (format: "[name]"): */
706 while (big_str[i] != '\0') {
707 sscanf(big_str + i, "trace_event: %a[^\n]",
708 &tmp_tes[tes_ind].name);
709 while (big_str[i] != '\n') {
710 ++i; /* Go to next '\n' */
711 }
712 ++i; /* Skip current pointed '\n' */
713 ++tes_ind;
714 }
715 tmp_tes[tes_ind].name = NULL;
716
717 *tes = tmp_tes;
718
719 free(big_str);
720 return 0;
721 }
722
723 /**
724 * Set sock path
725 *
726 * @param sock_path Sock path
727 * @param pid Traced process ID
728 * @return 0 if successful, or error
729 */
730 int ustctl_set_sock_path(int sock, const char *sock_path)
731 {
732 int result;
733 struct ustcomm_header req_header, res_header;
734 struct ustcomm_single_field sock_path_msg;
735
736 result = ustcomm_pack_single_field(&req_header,
737 &sock_path_msg,
738 sock_path);
739 if (result < 0) {
740 errno = -result;
741 return -1;
742 }
743
744 req_header.command = SET_SOCK_PATH;
745
746 return do_cmd(sock, &req_header, (char *)&sock_path_msg,
747 &res_header, NULL);
748 }
749
750 /**
751 * Get sock path
752 *
753 * @param sock_path Pointer to where the sock path will be returned
754 * @param pid Traced process ID
755 * @return 0 if successful, or error
756 */
757 int ustctl_get_sock_path(int sock, char **sock_path)
758 {
759 int result;
760 struct ustcomm_header req_header, res_header;
761 struct ustcomm_single_field *sock_path_msg;
762
763 req_header.command = GET_SOCK_PATH;
764 req_header.size = 0;
765
766 result = do_cmd(sock, &req_header, NULL, &res_header,
767 (char **)&sock_path_msg);
768 if (result < 0) {
769 return -1;
770 }
771
772 result = ustcomm_unpack_single_field(sock_path_msg);
773 if (result < 0) {
774 return result;
775 }
776
777 *sock_path = strdup(sock_path_msg->field);
778
779 free(sock_path_msg);
780
781 return 0;
782 }
This page took 0.04274 seconds and 3 git commands to generate.