c860346f75a37df35309077e5fb6a9276e6c4861
[lttng-tools.git] / src / bin / lttng / commands / track-untrack.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _LGPL_SOURCE
20 #include <ctype.h>
21 #include <popt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <assert.h>
29
30 #include <urcu/list.h>
31
32 #include <common/mi-lttng.h>
33
34 #include "../command.h"
35
36 enum cmd_type {
37 CMD_TRACK,
38 CMD_UNTRACK,
39 };
40
41 enum tracker_type_state {
42 STATE_NONE = 0,
43 STATE_PID,
44 STATE_VPID,
45 STATE_UID,
46 STATE_VUID,
47 STATE_GID,
48 STATE_VGID,
49 };
50
51 struct opt_type {
52 int used;
53 int all;
54 char *string;
55 };
56
57 struct id_list {
58 size_t nr;
59 struct lttng_tracker_id *array;
60 };
61
62 static char *opt_session_name;
63 static int opt_kernel;
64 static int opt_userspace;
65
66 static struct opt_type opt_pid, opt_vpid, opt_uid, opt_vuid, opt_gid, opt_vgid;
67
68 static enum tracker_type_state type_state;
69
70 enum {
71 OPT_HELP = 1,
72 OPT_LIST_OPTIONS,
73 OPT_SESSION,
74 OPT_PID,
75 OPT_VPID,
76 OPT_UID,
77 OPT_VUID,
78 OPT_GID,
79 OPT_VGID,
80 OPT_ALL,
81 };
82
83 static struct poptOption long_options[] = {
84 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
85 { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, },
86 { "session", 's', POPT_ARG_STRING, &opt_session_name, OPT_SESSION, 0, 0, },
87 { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0, },
88 { "userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0, },
89 { "pid", 'p', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_pid.string, OPT_PID, 0, 0, },
90 { "vpid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vpid.string, OPT_VPID, 0, 0, },
91 { "uid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_uid.string, OPT_UID, 0, 0, },
92 { "vuid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vuid.string, OPT_VUID, 0, 0, },
93 { "gid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_gid.string, OPT_GID, 0, 0, },
94 { "vgid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vgid.string, OPT_VGID, 0, 0, },
95 { "all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0, },
96 { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, },
97 { 0, 0, 0, 0, 0, 0, 0, },
98 };
99
100 static
101 struct id_list *alloc_id_list(size_t nr_items)
102 {
103 struct id_list *id_list;
104 struct lttng_tracker_id *items;
105
106 id_list = zmalloc(sizeof(*id_list));
107 if (!id_list) {
108 return NULL;
109 }
110 items = zmalloc(nr_items * sizeof(*items));
111 if (!items) {
112 goto error;
113 }
114 id_list->nr = nr_items;
115 id_list->array = items;
116 return id_list;
117 error:
118 free(id_list);
119 return NULL;
120 }
121
122 static
123 void free_id_list(struct id_list *list)
124 {
125 size_t nr_items;
126 int i;
127
128 if (!list)
129 return;
130 nr_items = list->nr;
131 for (i = 0; i < nr_items; i++) {
132 struct lttng_tracker_id *item = &list->array[i];
133
134 free(item->string);
135 }
136 free(list);
137 }
138
139 static
140 int parse_id_string(const char *_id_string,
141 int all, struct id_list **_id_list)
142 {
143 const char *one_id_str;
144 char *iter;
145 int retval = CMD_SUCCESS;
146 int count = 0;
147 struct id_list *id_list = NULL;
148 char *id_string = NULL;
149 char *endptr;
150
151 if (all && _id_string) {
152 ERR("An empty ID string is expected with --all");
153 retval = CMD_ERROR;
154 goto error;
155 }
156 if (!all && !_id_string) {
157 ERR("Please specify --all with an empty ID string");
158 retval = CMD_ERROR;
159 goto error;
160 }
161 if (all) {
162 /* Empty ID string means all IDs */
163 id_list = alloc_id_list(1);
164 if (!id_list) {
165 ERR("Out of memory");
166 retval = CMD_ERROR;
167 goto error;
168 }
169 id_list->array[0].type = LTTNG_ID_ALL;
170 goto assign;
171 }
172
173 id_string = strdup(_id_string);
174 if (!id_string) {
175 ERR("Out of memory");
176 retval = CMD_ERROR;
177 goto error;
178 }
179
180 /* Count */
181 one_id_str = strtok_r(id_string, ",", &iter);
182 while (one_id_str != NULL) {
183 unsigned long v;
184
185 if (isdigit(one_id_str[0])) {
186 errno = 0;
187 v = strtoul(one_id_str, &endptr, 10);
188 if ((v == 0 && errno == EINVAL)
189 || (v == ULONG_MAX && errno == ERANGE)
190 || (*one_id_str != '\0' && *endptr != '\0')){
191 ERR("Error parsing ID %s", one_id_str);
192 retval = CMD_ERROR;
193 goto error;
194 }
195
196 if ((long) v > INT_MAX || (int) v < 0) {
197 ERR("Invalid ID value %ld", (long) v);
198 retval = CMD_ERROR;
199 goto error;
200 }
201 }
202 count++;
203
204 /* For next loop */
205 one_id_str = strtok_r(NULL, ",", &iter);
206 }
207
208 free(id_string);
209 /* Identity of delimiter has been lost in first pass. */
210 id_string = strdup(_id_string);
211 if (!id_string) {
212 ERR("Out of memory");
213 retval = CMD_ERROR;
214 goto error;
215 }
216
217 /* Allocate */
218 id_list = alloc_id_list(count);
219 if (!id_list) {
220 ERR("Out of memory");
221 retval = CMD_ERROR;
222 goto error;
223 }
224
225 /* Reparse string and populate the id list. */
226 count = 0;
227 one_id_str = strtok_r(id_string, ",", &iter);
228 while (one_id_str != NULL) {
229 struct lttng_tracker_id *item;
230
231 item = &id_list->array[count++];
232 if (isdigit(one_id_str[0])) {
233 unsigned long v;
234
235 v = strtoul(one_id_str, NULL, 10);
236 item->type = LTTNG_ID_VALUE;
237 item->value = (int) v;
238 } else {
239 item->type = LTTNG_ID_STRING;
240 item->string = strdup(one_id_str);
241 if (!item->string) {
242 ERR("Out of memory");
243 retval = CMD_ERROR;
244 goto error;
245 }
246 }
247
248 /* For next loop */
249 one_id_str = strtok_r(NULL, ",", &iter);
250 }
251
252 assign:
253 *_id_list = id_list;
254 goto end; /* SUCCESS */
255
256 /* ERROR */
257 error:
258 free_id_list(id_list);
259 end:
260 free(id_string);
261 return retval;
262 }
263
264 static
265 const char *get_tracker_str(enum lttng_tracker_type tracker_type)
266 {
267 switch (tracker_type) {
268 case LTTNG_TRACKER_PID:
269 return "PID";
270 case LTTNG_TRACKER_VPID:
271 return "VPID";
272 case LTTNG_TRACKER_UID:
273 return "UID";
274 case LTTNG_TRACKER_VUID:
275 return "VUID";
276 case LTTNG_TRACKER_GID:
277 return "GID";
278 case LTTNG_TRACKER_VGID:
279 return "VGID";
280 }
281 return NULL;
282 }
283
284 static
285 enum cmd_error_code track_untrack_id(enum cmd_type cmd_type, const char *cmd_str,
286 const char *session_name, const char *id_string,
287 int all, struct mi_writer *writer,
288 enum lttng_tracker_type tracker_type)
289 {
290 int ret, success = 1 , i;
291 enum cmd_error_code retval = CMD_SUCCESS;
292 struct id_list *id_list = NULL;
293 struct lttng_domain dom;
294 struct lttng_handle *handle = NULL;
295 int (*cmd_func)(struct lttng_handle *handle,
296 enum lttng_tracker_type tracker_type,
297 struct lttng_tracker_id *id);
298 const char *tracker_str;
299
300 switch (cmd_type) {
301 case CMD_TRACK:
302 cmd_func = lttng_track_id;
303 break;
304 case CMD_UNTRACK:
305 cmd_func = lttng_untrack_id;
306 break;
307 default:
308 ERR("Unknown command");
309 retval = CMD_ERROR;
310 goto end;
311 }
312 memset(&dom, 0, sizeof(dom));
313 if (opt_kernel) {
314 dom.type = LTTNG_DOMAIN_KERNEL;
315 } else if (opt_userspace) {
316 dom.type = LTTNG_DOMAIN_UST;
317 if (tracker_type == LTTNG_TRACKER_PID) {
318 MSG("For UST domain, the PID tracker is actually tracking VPID.");
319 tracker_type = LTTNG_TRACKER_VPID;
320 }
321 } else {
322 /* Checked by the caller. */
323 assert(0);
324 }
325 tracker_str = get_tracker_str(tracker_type);
326 if (!tracker_str) {
327 ERR("Unknown tracker type");
328 retval = CMD_ERROR;
329 goto end;
330 }
331 ret = parse_id_string(id_string, all, &id_list);
332 if (ret != CMD_SUCCESS) {
333 ERR("Error parsing %s string", tracker_str);
334 retval = CMD_ERROR;
335 goto end;
336 }
337
338 handle = lttng_create_handle(session_name, &dom);
339 if (handle == NULL) {
340 retval = CMD_ERROR;
341 goto end;
342 }
343
344 if (writer) {
345 /* Open tracker_id and targets elements */
346 ret = mi_lttng_id_tracker_open(writer, tracker_type);
347 if (ret) {
348 goto end;
349 }
350 }
351
352 for (i = 0; i < id_list->nr; i++) {
353 struct lttng_tracker_id *item = &id_list->array[i];
354
355 switch (item->type) {
356 case LTTNG_ID_ALL:
357 DBG("%s all IDs", cmd_str);
358 break;
359 case LTTNG_ID_VALUE:
360 DBG("%s ID %d", cmd_str, item->value);
361 break;
362 case LTTNG_ID_STRING:
363 DBG("%s ID '%s'", cmd_str, item->string);
364 break;
365 default:
366 retval = CMD_ERROR;
367 goto end;
368 }
369 ret = cmd_func(handle, tracker_type, item);
370 if (ret) {
371 char *msg = NULL;
372
373 switch (-ret) {
374 case LTTNG_ERR_ID_TRACKED:
375 msg = "already tracked";
376 success = 1;
377 retval = CMD_SUCCESS;
378 break;
379 case LTTNG_ERR_ID_NOT_TRACKED:
380 msg = "not tracked";
381 success = 1;
382 retval = CMD_SUCCESS;
383 break;
384 default:
385 ERR("%s", lttng_strerror(ret));
386 success = 0;
387 retval = CMD_ERROR;
388 break;
389 }
390 if (msg) {
391 switch (item->type) {
392 case LTTNG_ID_ALL:
393 WARN("All %s %s in session %s",
394 tracker_str, msg, session_name);
395 break;
396 case LTTNG_ID_VALUE:
397 WARN("%s %i %s in session %s",
398 tracker_str, item->value,
399 msg, session_name);
400 break;
401 case LTTNG_ID_STRING:
402 WARN("%s '%s' %s in session %s",
403 tracker_str, item->string,
404 msg, session_name);
405 break;
406 default:
407 retval = CMD_ERROR;
408 goto end;
409 }
410 }
411 } else {
412 switch (item->type) {
413 case LTTNG_ID_ALL:
414 MSG("All %ss %sed in session %s",
415 tracker_str, cmd_str, session_name);
416 break;
417 case LTTNG_ID_VALUE:
418 MSG("%s %i %sed in session %s",
419 tracker_str, item->value, cmd_str,
420 session_name);
421 break;
422 case LTTNG_ID_STRING:
423 MSG("%s '%s' %sed in session %s",
424 tracker_str, item->string, cmd_str,
425 session_name);
426 break;
427 default:
428 retval = CMD_ERROR;
429 goto end;
430 }
431 success = 1;
432 }
433
434 /* Mi */
435 if (writer) {
436 ret = mi_lttng_id_target(writer,
437 tracker_type, item, 1);
438 if (ret) {
439 retval = CMD_ERROR;
440 goto end;
441 }
442
443 ret = mi_lttng_writer_write_element_bool(writer,
444 mi_lttng_element_success, success);
445 if (ret) {
446 retval = CMD_ERROR;
447 goto end;
448 }
449
450 ret = mi_lttng_writer_close_element(writer);
451 if (ret) {
452 retval = CMD_ERROR;
453 goto end;
454 }
455 }
456 }
457
458 if (writer) {
459 /* Close targets and tracker_id elements */
460 ret = mi_lttng_close_multi_element(writer, 2);
461 if (ret) {
462 retval = CMD_ERROR;
463 goto end;
464 }
465 }
466
467 end:
468 if (handle) {
469 lttng_destroy_handle(handle);
470 }
471 free_id_list(id_list);
472 return retval;
473 }
474
475 static
476 const char *get_mi_element_command(enum cmd_type cmd_type)
477 {
478 switch (cmd_type) {
479 case CMD_TRACK:
480 return mi_lttng_element_command_track;
481 case CMD_UNTRACK:
482 return mi_lttng_element_command_untrack;
483 default:
484 return NULL;
485 }
486 }
487
488 /*
489 * Add/remove tracker to/from session.
490 */
491 static
492 int cmd_track_untrack(enum cmd_type cmd_type, const char *cmd_str,
493 int argc, const char **argv)
494 {
495 int opt, ret = 0;
496 enum cmd_error_code command_ret = CMD_SUCCESS;
497 int success = 1;
498 static poptContext pc;
499 char *session_name = NULL;
500 struct mi_writer *writer = NULL;
501
502 if (argc < 1) {
503 command_ret = CMD_ERROR;
504 goto end;
505 }
506
507 pc = poptGetContext(NULL, argc, argv, long_options, 0);
508 poptReadDefaultConfig(pc, 0);
509
510 while ((opt = poptGetNextOpt(pc)) != -1) {
511 switch (opt) {
512 case OPT_HELP:
513 SHOW_HELP();
514 goto end;
515 case OPT_LIST_OPTIONS:
516 list_cmd_options(stdout, long_options);
517 goto end;
518 case OPT_SESSION:
519 break;
520 case OPT_PID:
521 opt_pid.used = 1;
522 type_state = STATE_PID;
523 break;
524 case OPT_VPID:
525 opt_vpid.used = 1;
526 type_state = STATE_VPID;
527 break;
528 case OPT_UID:
529 opt_uid.used = 1;
530 type_state = STATE_UID;
531 break;
532 case OPT_VUID:
533 opt_vuid.used = 1;
534 type_state = STATE_VUID;
535 break;
536 case OPT_GID:
537 opt_gid.used = 1;
538 type_state = STATE_GID;
539 break;
540 case OPT_VGID:
541 opt_vgid.used = 1;
542 type_state = STATE_VGID;
543 break;
544 case OPT_ALL:
545 switch (type_state) {
546 case STATE_PID:
547 opt_pid.all = 1;
548 break;
549 case STATE_VPID:
550 opt_vpid.all = 1;
551 break;
552 case STATE_UID:
553 opt_uid.all = 1;
554 break;
555 case STATE_VUID:
556 opt_vuid.all = 1;
557 break;
558 case STATE_GID:
559 opt_gid.all = 1;
560 break;
561 case STATE_VGID:
562 opt_vgid.all = 1;
563 break;
564 default:
565 command_ret = CMD_ERROR;
566 goto end;
567 }
568 break;
569 default:
570 command_ret = CMD_UNDEFINED;
571 goto end;
572 }
573 }
574
575 ret = print_missing_or_multiple_domains(opt_kernel + opt_userspace);
576 if (ret) {
577 command_ret = CMD_ERROR;
578 goto end;
579 }
580
581 if (!opt_session_name) {
582 session_name = get_session_name();
583 if (session_name == NULL) {
584 command_ret = CMD_ERROR;
585 goto end;
586 }
587 } else {
588 session_name = opt_session_name;
589 }
590
591 /* Mi check */
592 if (lttng_opt_mi) {
593 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
594 if (!writer) {
595 command_ret = CMD_ERROR;
596 goto end;
597 }
598 }
599
600 if (writer) {
601 /* Open command element */
602 ret = mi_lttng_writer_command_open(writer,
603 get_mi_element_command(cmd_type));
604 if (ret) {
605 command_ret = CMD_ERROR;
606 goto end;
607 }
608
609 /* Open output element */
610 ret = mi_lttng_writer_open_element(writer,
611 mi_lttng_element_command_output);
612 if (ret) {
613 command_ret = CMD_ERROR;
614 goto end;
615 }
616
617 ret = mi_lttng_trackers_open(writer);
618 if (ret) {
619 goto end;
620 }
621 }
622
623 if (opt_pid.used) {
624 command_ret = track_untrack_id(cmd_type,
625 cmd_str, session_name, opt_pid.string,
626 opt_pid.all, writer, LTTNG_TRACKER_PID);
627 if (command_ret != CMD_SUCCESS) {
628 success = 0;
629 }
630 }
631 if (opt_vpid.used) {
632 command_ret = track_untrack_id(cmd_type,
633 cmd_str, session_name, opt_vpid.string,
634 opt_vpid.all, writer, LTTNG_TRACKER_VPID);
635 if (command_ret != CMD_SUCCESS) {
636 success = 0;
637 }
638 }
639 if (opt_uid.used) {
640 command_ret = track_untrack_id(cmd_type,
641 cmd_str, session_name, opt_uid.string,
642 opt_uid.all, writer, LTTNG_TRACKER_UID);
643 if (command_ret != CMD_SUCCESS) {
644 success = 0;
645 }
646 }
647 if (opt_vuid.used) {
648 command_ret = track_untrack_id(cmd_type,
649 cmd_str, session_name, opt_vuid.string,
650 opt_vuid.all, writer, LTTNG_TRACKER_VUID);
651 if (command_ret != CMD_SUCCESS) {
652 success = 0;
653 }
654 }
655 if (opt_gid.used) {
656 command_ret = track_untrack_id(cmd_type,
657 cmd_str, session_name, opt_gid.string,
658 opt_gid.all, writer, LTTNG_TRACKER_GID);
659 if (command_ret != CMD_SUCCESS) {
660 success = 0;
661 }
662 }
663 if (opt_vgid.used) {
664 command_ret = track_untrack_id(cmd_type,
665 cmd_str, session_name, opt_vgid.string,
666 opt_vgid.all, writer, LTTNG_TRACKER_VGID);
667 if (command_ret != CMD_SUCCESS) {
668 success = 0;
669 }
670 }
671
672 /* Mi closing */
673 if (writer) {
674 /* Close trackers and output elements */
675 ret = mi_lttng_close_multi_element(writer, 2);
676 if (ret) {
677 command_ret = CMD_ERROR;
678 goto end;
679 }
680
681 /* Success ? */
682 ret = mi_lttng_writer_write_element_bool(writer,
683 mi_lttng_element_command_success, success);
684 if (ret) {
685 command_ret = CMD_ERROR;
686 goto end;
687 }
688
689 /* Command element close */
690 ret = mi_lttng_writer_command_close(writer);
691 if (ret) {
692 command_ret = CMD_ERROR;
693 goto end;
694 }
695 }
696
697 end:
698 if (!opt_session_name) {
699 free(session_name);
700 }
701
702 /* Mi clean-up */
703 if (writer && mi_lttng_writer_destroy(writer)) {
704 /* Preserve original error code */
705 command_ret = CMD_ERROR;
706 }
707
708 poptFreeContext(pc);
709 return (int) command_ret;
710 }
711
712 int cmd_track(int argc, const char **argv)
713 {
714 return cmd_track_untrack(CMD_TRACK, "track", argc, argv);
715 }
716
717 int cmd_untrack(int argc, const char **argv)
718 {
719 return cmd_track_untrack(CMD_UNTRACK, "untrack", argc, argv);
720 }
This page took 0.04609 seconds and 4 git commands to generate.