lttng add-trigger: replace log level options with --log-level
[lttng-tools.git] / src / bin / lttng / commands / add_trigger.c
1 /*
2 * Copyright (C) 2021 Simon Marchi <simon.marchi@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include <ctype.h>
9 #include <stdio.h>
10
11 #include "../command.h"
12 #include "../loglevel.h"
13 #include "../uprobe.h"
14
15 #include "common/argpar/argpar.h"
16 #include "common/dynamic-array.h"
17 #include "common/string-utils/string-utils.h"
18 #include "common/utils.h"
19 /* For lttng_event_rule_type_str(). */
20 #include <lttng/event-rule/event-rule-internal.h>
21 #include <lttng/lttng.h>
22 #include "common/filter/filter-ast.h"
23 #include "common/filter/filter-ir.h"
24 #include "common/dynamic-array.h"
25
26 #if (LTTNG_SYMBOL_NAME_LEN == 256)
27 #define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
28 #endif
29
30 #ifdef LTTNG_EMBED_HELP
31 static const char help_msg[] =
32 #include <lttng-add-trigger.1.h>
33 ;
34 #endif
35
36 enum {
37 OPT_HELP,
38 OPT_LIST_OPTIONS,
39
40 OPT_CONDITION,
41 OPT_ACTION,
42 OPT_ID,
43 OPT_USER_ID,
44 OPT_RATE_POLICY,
45
46 OPT_NAME,
47 OPT_FILTER,
48 OPT_EXCLUDE_NAMES,
49 OPT_EVENT_NAME,
50 OPT_LOG_LEVEL,
51
52 OPT_DOMAIN,
53 OPT_TYPE,
54 OPT_LOCATION,
55
56 OPT_MAX_SIZE,
57 OPT_DATA_URL,
58 OPT_CTRL_URL,
59 OPT_URL,
60 OPT_PATH,
61
62 OPT_CAPTURE,
63 };
64
65 static const struct argpar_opt_descr event_rule_opt_descrs[] = {
66 { OPT_FILTER, 'f', "filter", true },
67 { OPT_NAME, 'n', "name", true },
68 { OPT_EXCLUDE_NAMES, 'x', "exclude-names", true },
69 { OPT_LOG_LEVEL, 'l', "log-level", true },
70 { OPT_EVENT_NAME, 'E', "event-name", true },
71
72 { OPT_DOMAIN, 'd', "domain", true },
73 { OPT_TYPE, 't', "type", true },
74 { OPT_LOCATION, 'L', "location", true },
75
76 /* Capture descriptor */
77 { OPT_CAPTURE, '\0', "capture", true },
78
79 ARGPAR_OPT_DESCR_SENTINEL
80 };
81
82 static
83 bool assign_domain_type(enum lttng_domain_type *dest, const char *arg)
84 {
85 bool ret;
86
87 if (*dest != LTTNG_DOMAIN_NONE) {
88 ERR("More than one `--domain` was specified.");
89 goto error;
90 }
91
92 if (strcmp(arg, "kernel") == 0) {
93 *dest = LTTNG_DOMAIN_KERNEL;
94 } else if (strcmp(arg, "user") == 0 || strcmp(arg, "userspace") == 0) {
95 *dest = LTTNG_DOMAIN_UST;
96 } else if (strcmp(arg, "jul") == 0) {
97 *dest = LTTNG_DOMAIN_JUL;
98 } else if (strcmp(arg, "log4j") == 0) {
99 *dest = LTTNG_DOMAIN_LOG4J;
100 } else if (strcmp(arg, "python") == 0) {
101 *dest = LTTNG_DOMAIN_PYTHON;
102 } else {
103 ERR("Invalid `--domain` value: %s", arg);
104 goto error;
105 }
106
107 ret = true;
108 goto end;
109
110 error:
111 ret = false;
112
113 end:
114 return ret;
115 }
116
117 static
118 bool assign_event_rule_type(enum lttng_event_rule_type *dest, const char *arg)
119 {
120 bool ret;
121
122 if (*dest != LTTNG_EVENT_RULE_TYPE_UNKNOWN) {
123 ERR("More than one `--type` was specified.");
124 goto error;
125 }
126
127 if (strcmp(arg, "tracepoint") == 0 || strcmp(arg, "logging") == 0) {
128 *dest = LTTNG_EVENT_RULE_TYPE_TRACEPOINT;
129 } else if (strcmp (arg, "kprobe") == 0 || strcmp(arg, "kernel-probe") == 0) {
130 *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE;
131 } else if (strcmp (arg, "uprobe") == 0 || strcmp(arg, "userspace-probe") == 0) {
132 *dest = LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE;
133 } else if (strcmp (arg, "function") == 0) {
134 *dest = LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION;
135 } else if (strcmp (arg, "syscall") == 0) {
136 *dest = LTTNG_EVENT_RULE_TYPE_SYSCALL;
137 } else {
138 ERR("Invalid `--type` value: %s", arg);
139 goto error;
140 }
141
142 ret = true;
143 goto end;
144
145 error:
146 ret = false;
147
148 end:
149 return ret;
150 }
151
152 static
153 bool assign_string(char **dest, const char *src, const char *opt_name)
154 {
155 bool ret;
156
157 if (*dest) {
158 ERR("Duplicate '%s' given.", opt_name);
159 goto error;
160 }
161
162 *dest = strdup(src);
163 if (!*dest) {
164 PERROR("Failed to allocate string '%s'.", opt_name);
165 goto error;
166 }
167
168 ret = true;
169 goto end;
170
171 error:
172 ret = false;
173
174 end:
175 return ret;
176 }
177
178 /* This is defined in enable_events.c. */
179 LTTNG_HIDDEN
180 int create_exclusion_list_and_validate(const char *event_name,
181 const char *exclusions_arg,
182 char ***exclusion_list);
183
184 /*
185 * Parse `str` as a log level in domain `domain_type`.
186 *
187 * Return the log level in `*log_level`. Return true in `*log_level_only` if
188 * the string specifies exactly this log level, false if it specifies at least
189 * this log level.
190 *
191 * Return true if the string was successfully parsed as a log level string.
192 */
193 static bool parse_log_level_string(const char *str,
194 enum lttng_domain_type domain_type,
195 int *log_level,
196 bool *log_level_only)
197 {
198 bool ret;
199
200 switch (domain_type) {
201 case LTTNG_DOMAIN_UST:
202 {
203 enum lttng_loglevel log_level_min, log_level_max;
204 if (!loglevel_parse_range_string(
205 str, &log_level_min, &log_level_max)) {
206 goto error;
207 }
208
209 /* Only support VAL and VAL.. for now. */
210 if (log_level_min != log_level_max &&
211 log_level_max != LTTNG_LOGLEVEL_EMERG) {
212 goto error;
213 }
214
215 *log_level = (int) log_level_min;
216 *log_level_only = log_level_min == log_level_max;
217 break;
218 }
219 case LTTNG_DOMAIN_LOG4J:
220 {
221 enum lttng_loglevel_log4j log_level_min, log_level_max;
222 if (!loglevel_log4j_parse_range_string(
223 str, &log_level_min, &log_level_max)) {
224 goto error;
225 }
226
227 /* Only support VAL and VAL.. for now. */
228 if (log_level_min != log_level_max &&
229 log_level_max != LTTNG_LOGLEVEL_LOG4J_FATAL) {
230 goto error;
231 }
232
233 *log_level = (int) log_level_min;
234 *log_level_only = log_level_min == log_level_max;
235 break;
236 }
237 case LTTNG_DOMAIN_JUL:
238 {
239 enum lttng_loglevel_jul log_level_min, log_level_max;
240 if (!loglevel_jul_parse_range_string(
241 str, &log_level_min, &log_level_max)) {
242 goto error;
243 }
244
245 /* Only support VAL and VAL.. for now. */
246 if (log_level_min != log_level_max &&
247 log_level_max != LTTNG_LOGLEVEL_JUL_SEVERE) {
248 goto error;
249 }
250
251 *log_level = (int) log_level_min;
252 *log_level_only = log_level_min == log_level_max;
253 break;
254 }
255 case LTTNG_DOMAIN_PYTHON:
256 {
257 enum lttng_loglevel_python log_level_min, log_level_max;
258 if (!loglevel_python_parse_range_string(
259 str, &log_level_min, &log_level_max)) {
260 goto error;
261 }
262
263 /* Only support VAL and VAL.. for now. */
264 if (log_level_min != log_level_max &&
265 log_level_max !=
266 LTTNG_LOGLEVEL_PYTHON_CRITICAL) {
267 goto error;
268 }
269
270 *log_level = (int) log_level_min;
271 *log_level_only = log_level_min == log_level_max;
272 break;
273 }
274 default:
275 /* Invalid domain type. */
276 abort();
277 }
278
279 ret = true;
280 goto end;
281
282 error:
283 ret = false;
284
285 end:
286 return ret;
287 }
288
289 static int parse_kernel_probe_opts(const char *source,
290 struct lttng_kernel_probe_location **location)
291 {
292 int ret = 0;
293 int match;
294 char s_hex[19];
295 char name[LTTNG_SYMBOL_NAME_LEN];
296 char *symbol_name = NULL;
297 uint64_t offset;
298
299 /* Check for symbol+offset. */
300 match = sscanf(source,
301 "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
302 "[^'+']+%18s",
303 name, s_hex);
304 if (match == 2) {
305 if (*s_hex == '\0') {
306 ERR("Kernel probe symbol offset is missing.");
307 goto error;
308 }
309
310 symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
311 if (!symbol_name) {
312 PERROR("Failed to copy kernel probe location symbol name.");
313 goto error;
314 }
315 offset = strtoul(s_hex, NULL, 0);
316
317 *location = lttng_kernel_probe_location_symbol_create(
318 symbol_name, offset);
319 if (!*location) {
320 ERR("Failed to create symbol kernel probe location.");
321 goto error;
322 }
323
324 goto end;
325 }
326
327 /* Check for symbol. */
328 if (isalpha(name[0]) || name[0] == '_') {
329 match = sscanf(source,
330 "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
331 "s",
332 name);
333 if (match == 1) {
334 symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
335 if (!symbol_name) {
336 ERR("Failed to copy kernel probe location symbol name.");
337 goto error;
338 }
339
340 *location = lttng_kernel_probe_location_symbol_create(
341 symbol_name, 0);
342 if (!*location) {
343 ERR("Failed to create symbol kernel probe location.");
344 goto error;
345 }
346
347 goto end;
348 }
349 }
350
351 /* Check for address. */
352 match = sscanf(source, "%18s", s_hex);
353 if (match > 0) {
354 uint64_t address;
355
356 if (*s_hex == '\0') {
357 ERR("Invalid kernel probe location address.");
358 goto error;
359 }
360
361 address = strtoul(s_hex, NULL, 0);
362 *location = lttng_kernel_probe_location_address_create(address);
363 if (!*location) {
364 ERR("Failed to create symbol kernel probe location.");
365 goto error;
366 }
367
368 goto end;
369 }
370
371 error:
372 /* No match */
373 ret = -1;
374 *location = NULL;
375
376 end:
377 free(symbol_name);
378 return ret;
379 }
380
381 static
382 struct lttng_event_expr *ir_op_load_expr_to_event_expr(
383 const struct ir_load_expression *load_expr,
384 const char *capture_str)
385 {
386 char *provider_name = NULL;
387 struct lttng_event_expr *event_expr = NULL;
388 const struct ir_load_expression_op *load_expr_op = load_expr->child;
389 const enum ir_load_expression_type load_expr_child_type =
390 load_expr_op->type;
391
392 switch (load_expr_child_type) {
393 case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
394 case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
395 {
396 const char *field_name;
397
398 load_expr_op = load_expr_op->next;
399 assert(load_expr_op);
400 assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
401 field_name = load_expr_op->u.symbol;
402 assert(field_name);
403
404 event_expr = load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ?
405 lttng_event_expr_event_payload_field_create(field_name) :
406 lttng_event_expr_channel_context_field_create(field_name);
407 if (!event_expr) {
408 ERR("Failed to create %s event expression: field name = `%s`.",
409 load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ?
410 "payload field" : "channel context",
411 field_name);
412 goto error;
413 }
414
415 break;
416 }
417 case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
418 {
419 const char *colon;
420 const char *type_name;
421 const char *field_name;
422
423 load_expr_op = load_expr_op->next;
424 assert(load_expr_op);
425 assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
426 field_name = load_expr_op->u.symbol;
427 assert(field_name);
428
429 /*
430 * The field name needs to be of the form PROVIDER:TYPE. We
431 * split it here.
432 */
433 colon = strchr(field_name, ':');
434 if (!colon) {
435 ERR("Invalid app-specific context field name: missing colon in `%s`.",
436 field_name);
437 goto error;
438 }
439
440 type_name = colon + 1;
441 if (*type_name == '\0') {
442 ERR("Invalid app-specific context field name: missing type name after colon in `%s`.",
443 field_name);
444 goto error;
445 }
446
447 provider_name = strndup(field_name, colon - field_name);
448 if (!provider_name) {
449 PERROR("Failed to allocate field name string");
450 goto error;
451 }
452
453 event_expr = lttng_event_expr_app_specific_context_field_create(
454 provider_name, type_name);
455 if (!event_expr) {
456 ERR("Failed to create app-specific context field event expression: provider name = `%s`, type name = `%s`",
457 provider_name, type_name);
458 goto error;
459 }
460
461 break;
462 }
463 default:
464 ERR("%s: unexpected load expr type %d.", __func__,
465 load_expr_op->type);
466 abort();
467 }
468
469 load_expr_op = load_expr_op->next;
470
471 /* There may be a single array index after that. */
472 if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) {
473 struct lttng_event_expr *index_event_expr;
474 const uint64_t index = load_expr_op->u.index;
475
476 index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index);
477 if (!index_event_expr) {
478 ERR("Failed to create array field element event expression.");
479 goto error;
480 }
481
482 event_expr = index_event_expr;
483 load_expr_op = load_expr_op->next;
484 }
485
486 switch (load_expr_op->type) {
487 case IR_LOAD_EXPRESSION_LOAD_FIELD:
488 /*
489 * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is
490 * always found at the end of the chain.
491 */
492 break;
493 case IR_LOAD_EXPRESSION_GET_SYMBOL:
494 ERR("While parsing expression `%s`: Capturing subfields is not supported.",
495 capture_str);
496 goto error;
497
498 default:
499 ERR("%s: unexpected load expression operator %s.", __func__,
500 ir_load_expression_type_str(load_expr_op->type));
501 abort();
502 }
503
504 goto end;
505
506 error:
507 lttng_event_expr_destroy(event_expr);
508 event_expr = NULL;
509
510 end:
511 free(provider_name);
512
513 return event_expr;
514 }
515
516 static
517 struct lttng_event_expr *ir_op_load_to_event_expr(
518 const struct ir_op *ir, const char *capture_str)
519 {
520 struct lttng_event_expr *event_expr = NULL;
521
522 assert(ir->op == IR_OP_LOAD);
523
524 switch (ir->data_type) {
525 case IR_DATA_EXPRESSION:
526 {
527 const struct ir_load_expression *ir_load_expr =
528 ir->u.load.u.expression;
529
530 event_expr = ir_op_load_expr_to_event_expr(
531 ir_load_expr, capture_str);
532 break;
533 }
534 default:
535 ERR("%s: unexpected data type: %s.", __func__,
536 ir_data_type_str(ir->data_type));
537 abort();
538 }
539
540 return event_expr;
541 }
542
543 static
544 const char *ir_operator_type_human_str(enum ir_op_type op)
545 {
546 const char *name;
547
548 switch (op) {
549 case IR_OP_BINARY:
550 name = "Binary";
551 break;
552 case IR_OP_UNARY:
553 name = "Unary";
554 break;
555 case IR_OP_LOGICAL:
556 name = "Logical";
557 break;
558 default:
559 abort();
560 }
561
562 return name;
563 }
564
565 static
566 struct lttng_event_expr *ir_op_root_to_event_expr(const struct ir_op *ir,
567 const char *capture_str)
568 {
569 struct lttng_event_expr *event_expr = NULL;
570
571 assert(ir->op == IR_OP_ROOT);
572 ir = ir->u.root.child;
573
574 switch (ir->op) {
575 case IR_OP_LOAD:
576 event_expr = ir_op_load_to_event_expr(ir, capture_str);
577 break;
578 case IR_OP_BINARY:
579 case IR_OP_UNARY:
580 case IR_OP_LOGICAL:
581 ERR("While parsing expression `%s`: %s operators are not allowed in capture expressions.",
582 capture_str,
583 ir_operator_type_human_str(ir->op));
584 break;
585 default:
586 ERR("%s: unexpected IR op type: %s.", __func__,
587 ir_op_type_str(ir->op));
588 abort();
589 }
590
591 return event_expr;
592 }
593
594 static
595 void destroy_event_expr(void *ptr)
596 {
597 lttng_event_expr_destroy(ptr);
598 }
599
600 struct parse_event_rule_res {
601 /* Owned by this. */
602 struct lttng_event_rule *er;
603
604 /* Array of `struct lttng_event_expr *` */
605 struct lttng_dynamic_pointer_array capture_descriptors;
606 };
607
608 static
609 struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv)
610 {
611 enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
612 enum lttng_event_rule_type event_rule_type =
613 LTTNG_EVENT_RULE_TYPE_UNKNOWN;
614 struct argpar_state *state;
615 struct argpar_item *item = NULL;
616 char *error = NULL;
617 int consumed_args = -1;
618 struct lttng_kernel_probe_location *kernel_probe_location = NULL;
619 struct lttng_userspace_probe_location *userspace_probe_location = NULL;
620 struct parse_event_rule_res res = { 0 };
621 struct lttng_event_expr *event_expr = NULL;
622 struct filter_parser_ctx *parser_ctx = NULL;
623 struct lttng_log_level_rule *log_level_rule = NULL;
624
625 /* Tracepoint and syscall options. */
626 char *name = NULL;
627 char *exclude_names = NULL;
628 char **exclusion_list = NULL;
629
630 /* For userspace / kernel probe and function. */
631 char *location = NULL;
632 char *event_name = NULL;
633
634 /* Filter. */
635 char *filter = NULL;
636
637 /* Log level. */
638 char *log_level_str = NULL;
639
640 lttng_dynamic_pointer_array_init(&res.capture_descriptors,
641 destroy_event_expr);
642 state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
643 if (!state) {
644 ERR("Failed to allocate an argpar state.");
645 goto error;
646 }
647
648 while (true) {
649 enum argpar_state_parse_next_status status;
650
651 ARGPAR_ITEM_DESTROY_AND_RESET(item);
652 status = argpar_state_parse_next(state, &item, &error);
653 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
654 ERR("%s", error);
655 goto error;
656 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
657 /* Just stop parsing here. */
658 break;
659 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
660 break;
661 }
662
663 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
664
665 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
666 const struct argpar_item_opt *item_opt =
667 (const struct argpar_item_opt *) item;
668
669 switch (item_opt->descr->id) {
670 /* Domains. */
671 case OPT_DOMAIN:
672 if (!assign_domain_type(&domain_type,
673 item_opt->arg)) {
674 goto error;
675 }
676
677 break;
678 case OPT_TYPE:
679 if (!assign_event_rule_type(&event_rule_type,
680 item_opt->arg)) {
681 goto error;
682 }
683
684 break;
685 case OPT_LOCATION:
686 if (!assign_string(&location,
687 item_opt->arg,
688 "--location/-L")) {
689 goto error;
690 }
691
692 break;
693 case OPT_EVENT_NAME:
694 if (!assign_string(&event_name,
695 item_opt->arg,
696 "--event-name/-E")) {
697 goto error;
698 }
699
700 break;
701 case OPT_FILTER:
702 if (!assign_string(&filter, item_opt->arg,
703 "--filter/-f")) {
704 goto error;
705 }
706
707 break;
708 case OPT_NAME:
709 if (!assign_string(&name, item_opt->arg,
710 "--name/-n")) {
711 goto error;
712 }
713
714 break;
715 case OPT_EXCLUDE_NAMES:
716 if (!assign_string(&exclude_names,
717 item_opt->arg,
718 "--exclude-names/-x")) {
719 goto error;
720 }
721
722 break;
723 case OPT_LOG_LEVEL:
724 if (!assign_string(&log_level_str,
725 item_opt->arg, "--log-level/-l")) {
726 goto error;
727 }
728
729 break;
730 case OPT_CAPTURE:
731 {
732 int ret;
733 const char *capture_str = item_opt->arg;
734
735 ret = filter_parser_ctx_create_from_filter_expression(
736 capture_str, &parser_ctx);
737 if (ret) {
738 ERR("Failed to parse capture expression `%s`.",
739 capture_str);
740 goto error;
741 }
742
743 event_expr = ir_op_root_to_event_expr(
744 parser_ctx->ir_root,
745 capture_str);
746 if (!event_expr) {
747 /*
748 * ir_op_root_to_event_expr has printed
749 * an error message.
750 */
751 goto error;
752 }
753
754 ret = lttng_dynamic_pointer_array_add_pointer(
755 &res.capture_descriptors,
756 event_expr);
757 if (ret) {
758 goto error;
759 }
760
761 /*
762 * The ownership of event expression was
763 * transferred to the dynamic array.
764 */
765 event_expr = NULL;
766
767 break;
768 }
769 default:
770 abort();
771 }
772 } else {
773 const struct argpar_item_non_opt *item_non_opt =
774 (const struct argpar_item_non_opt *)
775 item;
776
777 /* Don't accept non-option arguments. */
778 ERR("Unexpected argument '%s'", item_non_opt->arg);
779 goto error;
780 }
781 }
782
783 if (event_rule_type == LTTNG_EVENT_RULE_TYPE_UNKNOWN) {
784 event_rule_type = LTTNG_EVENT_RULE_TYPE_TRACEPOINT;
785 }
786
787 /*
788 * Option --name is applicable to event rules of type tracepoint
789 * and syscall. For tracepoint and syscall rules, if --name is
790 * omitted, it is implicitly "*".
791 */
792 switch (event_rule_type) {
793 case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
794 case LTTNG_EVENT_RULE_TYPE_SYSCALL:
795 if (!name) {
796 name = strdup("*");
797 }
798 break;
799
800 default:
801 if (name) {
802 ERR("Can't use --name with %s event rules.",
803 lttng_event_rule_type_str(
804 event_rule_type));
805 goto error;
806 }
807
808 if (exclude_names) {
809 ERR("Can't use --exclude-names/-x with %s event rules.",
810 lttng_event_rule_type_str(
811 event_rule_type));
812 goto error;
813 }
814 }
815
816 /*
817 * Option --location is only applicable to (and mandatory for) event
818 * rules of type {k,u}probe and function.
819 *
820 * Option --event-name is only applicable to event rules of type probe.
821 * If omitted, it defaults to the location.
822 */
823 switch (event_rule_type) {
824 case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE:
825 case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE:
826 case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION:
827 if (!location) {
828 ERR("Event rule of type %s requires a --location.",
829 lttng_event_rule_type_str(event_rule_type));
830 goto error;
831 }
832
833 if (!event_name) {
834 event_name = strdup(location);
835 }
836
837 break;
838
839 default:
840 if (location) {
841 ERR("Can't use --location with %s event rules.",
842 lttng_event_rule_type_str(event_rule_type));
843 goto error;
844 }
845
846 if (event_name) {
847 ERR("Can't use --event-name with %s event rules.",
848 lttng_event_rule_type_str(
849 event_rule_type));
850 goto error;
851 }
852 }
853
854 /*
855 * Update *argc and *argv so our caller can keep parsing what follows.
856 */
857 consumed_args = argpar_state_get_ingested_orig_args(state);
858 assert(consumed_args >= 0);
859 *argc -= consumed_args;
860 *argv += consumed_args;
861
862 /* Need to specify a domain. */
863 if (domain_type == LTTNG_DOMAIN_NONE) {
864 ERR("Please specify a domain (--domain=(kernel,user,jul,log4j,python)).");
865 goto error;
866 }
867
868 /* Validate event rule type against domain. */
869 switch (event_rule_type) {
870 case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE:
871 case LTTNG_EVENT_RULE_TYPE_KERNEL_FUNCTION:
872 case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE:
873 case LTTNG_EVENT_RULE_TYPE_SYSCALL:
874 if (domain_type != LTTNG_DOMAIN_KERNEL) {
875 ERR("Event type not available for user-space tracing.");
876 goto error;
877 }
878 break;
879
880 case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
881 break;
882
883 default:
884 abort();
885 }
886
887 /*
888 * Adding a filter to a probe, function or userspace-probe would be
889 * denied by the kernel tracer as it's not supported at the moment. We
890 * do an early check here to warn the user.
891 */
892 if (filter && domain_type == LTTNG_DOMAIN_KERNEL) {
893 switch (event_rule_type) {
894 case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
895 case LTTNG_EVENT_RULE_TYPE_SYSCALL:
896 break;
897 default:
898 ERR("Filter expressions are not supported for %s event rules.",
899 lttng_event_rule_type_str(event_rule_type));
900 goto error;
901 }
902 }
903
904 /* If --exclude/-x was passed, split it into an exclusion list. */
905 if (exclude_names) {
906 if (domain_type != LTTNG_DOMAIN_UST) {
907 ERR("Event name exclusions are not yet implemented for %s event rules.",
908 get_domain_str(domain_type));
909 goto error;
910 }
911
912 if (create_exclusion_list_and_validate(name,
913 exclude_names, &exclusion_list) != 0) {
914 ERR("Failed to create exclusion list.");
915 goto error;
916 }
917 }
918
919 if (log_level_str) {
920 if (event_rule_type != LTTNG_EVENT_RULE_TYPE_TRACEPOINT) {
921 ERR("Log levels are only applicable to tracepoint event rules.");
922 goto error;
923 }
924
925 if (domain_type == LTTNG_DOMAIN_KERNEL) {
926 ERR("Log levels are not supported by the kernel tracer.");
927 goto error;
928 }
929 }
930
931 /* Finally, create the event rule object. */
932 switch (event_rule_type) {
933 case LTTNG_EVENT_RULE_TYPE_TRACEPOINT:
934 {
935 enum lttng_event_rule_status event_rule_status;
936
937 res.er = lttng_event_rule_tracepoint_create(domain_type);
938 if (!res.er) {
939 ERR("Failed to create tracepoint event rule.");
940 goto error;
941 }
942
943 /* Set pattern. */
944 event_rule_status = lttng_event_rule_tracepoint_set_pattern(
945 res.er, name);
946 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
947 ERR("Failed to set tracepoint event rule's pattern to '%s'.",
948 name);
949 goto error;
950 }
951
952 /* Set filter. */
953 if (filter) {
954 event_rule_status = lttng_event_rule_tracepoint_set_filter(
955 res.er, filter);
956 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
957 ERR("Failed to set tracepoint event rule's filter to '%s'.",
958 filter);
959 goto error;
960 }
961 }
962
963 /* Set exclusion list. */
964 if (exclusion_list) {
965 int n;
966
967 for (n = 0; exclusion_list[n]; n++) {
968 event_rule_status = lttng_event_rule_tracepoint_add_exclusion(
969 res.er,
970 exclusion_list[n]);
971 if (event_rule_status !=
972 LTTNG_EVENT_RULE_STATUS_OK) {
973 ERR("Failed to set tracepoint exclusion list element '%s'",
974 exclusion_list[n]);
975 goto error;
976 }
977 }
978 }
979
980 /*
981 * ".." is the same as passing no log level option and
982 * correspond the the "ANY" case.
983 */
984 if (log_level_str && strcmp(log_level_str, "..") != 0) {
985 int log_level;
986 bool log_level_only;
987
988 if (!parse_log_level_string(log_level_str, domain_type,
989 &log_level, &log_level_only)) {
990 ERR("Failed to parse log level string `%s`.",
991 log_level_str);
992 goto error;
993 }
994
995 if (log_level_only) {
996 log_level_rule = lttng_log_level_rule_exactly_create(log_level);
997 } else {
998 log_level_rule = lttng_log_level_rule_at_least_as_severe_as_create(log_level);
999 }
1000
1001 if (log_level_rule == NULL) {
1002 ERR("Failed to create log level rule object.");
1003 goto error;
1004 }
1005
1006 event_rule_status =
1007 lttng_event_rule_tracepoint_set_log_level_rule(
1008 res.er, log_level_rule);
1009
1010 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
1011 ERR("Failed to set log level on event fule.");
1012 goto error;
1013 }
1014 }
1015
1016 break;
1017 }
1018 case LTTNG_EVENT_RULE_TYPE_KERNEL_PROBE:
1019 {
1020 int ret;
1021 enum lttng_event_rule_status event_rule_status;
1022
1023 ret = parse_kernel_probe_opts(
1024 location, &kernel_probe_location);
1025 if (ret) {
1026 ERR("Failed to parse kernel probe location.");
1027 goto error;
1028 }
1029
1030 assert(kernel_probe_location);
1031 res.er = lttng_event_rule_kernel_probe_create(kernel_probe_location);
1032 if (!res.er) {
1033 ERR("Failed to create kprobe event rule.");
1034 goto error;
1035 }
1036
1037 event_rule_status =
1038 lttng_event_rule_kernel_probe_set_event_name(
1039 res.er, event_name);
1040 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
1041 ERR("Failed to set kprobe event rule's name to '%s'.",
1042 event_name);
1043 goto error;
1044 }
1045
1046 break;
1047 }
1048 case LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE:
1049 {
1050 int ret;
1051 enum lttng_event_rule_status event_rule_status;
1052
1053 ret = parse_userspace_probe_opts(
1054 location, &userspace_probe_location);
1055 if (ret) {
1056 ERR("Failed to parse user space probe location.");
1057 goto error;
1058 }
1059
1060 res.er = lttng_event_rule_userspace_probe_create(userspace_probe_location);
1061 if (!res.er) {
1062 ERR("Failed to create userspace probe event rule.");
1063 goto error;
1064 }
1065
1066 event_rule_status =
1067 lttng_event_rule_userspace_probe_set_event_name(
1068 res.er, event_name);
1069 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
1070 ERR("Failed to set user space probe event rule's name to '%s'.",
1071 event_name);
1072 goto error;
1073 }
1074
1075 break;
1076 }
1077 case LTTNG_EVENT_RULE_TYPE_SYSCALL:
1078 {
1079 enum lttng_event_rule_status event_rule_status;
1080
1081 res.er = lttng_event_rule_syscall_create();
1082 if (!res.er) {
1083 ERR("Failed to create syscall event rule.");
1084 goto error;
1085 }
1086
1087 event_rule_status = lttng_event_rule_syscall_set_pattern(
1088 res.er, name);
1089 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
1090 ERR("Failed to set syscall event rule's pattern to '%s'.",
1091 name);
1092 goto error;
1093 }
1094
1095 if (filter) {
1096 event_rule_status = lttng_event_rule_syscall_set_filter(
1097 res.er, filter);
1098 if (event_rule_status != LTTNG_EVENT_RULE_STATUS_OK) {
1099 ERR("Failed to set syscall event rule's filter to '%s'.",
1100 filter);
1101 goto error;
1102 }
1103 }
1104
1105 break;
1106 }
1107 default:
1108 abort();
1109 goto error;
1110 }
1111
1112 goto end;
1113
1114 error:
1115 lttng_event_rule_destroy(res.er);
1116 res.er = NULL;
1117 lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
1118
1119 end:
1120 if (parser_ctx) {
1121 filter_parser_ctx_free(parser_ctx);
1122 }
1123
1124 lttng_event_expr_destroy(event_expr);
1125 argpar_item_destroy(item);
1126 free(error);
1127 argpar_state_destroy(state);
1128 free(filter);
1129 free(name);
1130 free(exclude_names);
1131 free(log_level_str);
1132 free(location);
1133 free(event_name);
1134
1135 strutils_free_null_terminated_array_of_strings(exclusion_list);
1136 lttng_kernel_probe_location_destroy(kernel_probe_location);
1137 lttng_userspace_probe_location_destroy(userspace_probe_location);
1138 lttng_log_level_rule_destroy(log_level_rule);
1139 return res;
1140 }
1141
1142 static
1143 struct lttng_condition *handle_condition_event(int *argc, const char ***argv)
1144 {
1145 struct parse_event_rule_res res;
1146 struct lttng_condition *c;
1147 size_t i;
1148
1149 res = parse_event_rule(argc, argv);
1150 if (!res.er) {
1151 c = NULL;
1152 goto error;
1153 }
1154
1155 c = lttng_condition_on_event_create(res.er);
1156 lttng_event_rule_destroy(res.er);
1157 res.er = NULL;
1158 if (!c) {
1159 goto error;
1160 }
1161
1162 for (i = 0; i < lttng_dynamic_pointer_array_get_count(&res.capture_descriptors);
1163 i++) {
1164 enum lttng_condition_status status;
1165 struct lttng_event_expr **expr =
1166 lttng_dynamic_array_get_element(
1167 &res.capture_descriptors.array, i);
1168
1169 assert(expr);
1170 assert(*expr);
1171 status = lttng_condition_on_event_append_capture_descriptor(
1172 c, *expr);
1173 if (status != LTTNG_CONDITION_STATUS_OK) {
1174 if (status == LTTNG_CONDITION_STATUS_UNSUPPORTED) {
1175 ERR("The capture feature is unsupported by the event-rule condition type");
1176 }
1177
1178 goto error;
1179 }
1180
1181 /* Ownership of event expression moved to `c` */
1182 *expr = NULL;
1183 }
1184
1185 goto end;
1186
1187 error:
1188 lttng_condition_destroy(c);
1189 c = NULL;
1190
1191 end:
1192 lttng_dynamic_pointer_array_reset(&res.capture_descriptors);
1193 lttng_event_rule_destroy(res.er);
1194 return c;
1195 }
1196
1197 static
1198 struct lttng_condition *handle_condition_session_consumed_size(int *argc, const char ***argv)
1199 {
1200 struct lttng_condition *cond = NULL;
1201 struct argpar_state *state = NULL;
1202 struct argpar_item *item = NULL;
1203 const char *threshold_arg = NULL;
1204 const char *session_name_arg = NULL;
1205 uint64_t threshold;
1206 char *error = NULL;
1207 enum lttng_condition_status condition_status;
1208
1209 state = argpar_state_create(*argc, *argv, event_rule_opt_descrs);
1210 if (!state) {
1211 ERR("Failed to allocate an argpar state.");
1212 goto error;
1213 }
1214
1215 while (true) {
1216 enum argpar_state_parse_next_status status;
1217
1218 ARGPAR_ITEM_DESTROY_AND_RESET(item);
1219 status = argpar_state_parse_next(state, &item, &error);
1220 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
1221 ERR("%s", error);
1222 goto error;
1223 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
1224 /* Just stop parsing here. */
1225 break;
1226 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
1227 break;
1228 }
1229
1230 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
1231
1232 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
1233 const struct argpar_item_opt *item_opt =
1234 (const struct argpar_item_opt *) item;
1235
1236 switch (item_opt->descr->id) {
1237 default:
1238 abort();
1239 }
1240 } else {
1241 const struct argpar_item_non_opt *item_non_opt;
1242
1243 assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
1244
1245 item_non_opt = (const struct argpar_item_non_opt *) item;
1246
1247 switch (item_non_opt->non_opt_index) {
1248 case 0:
1249 session_name_arg = item_non_opt->arg;
1250 break;
1251 case 1:
1252 threshold_arg = item_non_opt->arg;
1253 break;
1254 default:
1255 ERR("Unexpected argument `%s`.",
1256 item_non_opt->arg);
1257 goto error;
1258 }
1259 }
1260 }
1261
1262 *argc -= argpar_state_get_ingested_orig_args(state);
1263 *argv += argpar_state_get_ingested_orig_args(state);
1264
1265 if (!session_name_arg) {
1266 ERR("Missing session name argument.");
1267 goto error;
1268 }
1269
1270 if (!threshold_arg) {
1271 ERR("Missing threshold argument.");
1272 goto error;
1273 }
1274
1275 if (utils_parse_size_suffix(threshold_arg, &threshold) != 0) {
1276 ERR("Failed to parse `%s` as a size.", threshold_arg);
1277 goto error;
1278 }
1279
1280 cond = lttng_condition_session_consumed_size_create();
1281 if (!cond) {
1282 ERR("Failed to allocate a session consumed size condition.");
1283 goto error;
1284 }
1285
1286 condition_status = lttng_condition_session_consumed_size_set_session_name(
1287 cond, session_name_arg);
1288 if (condition_status != LTTNG_CONDITION_STATUS_OK) {
1289 ERR("Failed to set session consumed size condition's session name to '%s'.",
1290 session_name_arg);
1291 goto error;
1292 }
1293
1294 condition_status = lttng_condition_session_consumed_size_set_threshold(
1295 cond, threshold);
1296 if (condition_status != LTTNG_CONDITION_STATUS_OK) {
1297 ERR("Failed to set session consumed size condition threshold.");
1298 goto error;
1299 }
1300
1301 goto end;
1302
1303 error:
1304 lttng_condition_destroy(cond);
1305 cond = NULL;
1306
1307 end:
1308 argpar_state_destroy(state);
1309 argpar_item_destroy(item);
1310 free(error);
1311 return cond;
1312 }
1313
1314 static
1315 struct lttng_condition *handle_condition_buffer_usage_high(int *argc, const char ***argv)
1316 {
1317 ERR("High buffer usage threshold conditions are unsupported for the moment.");
1318 return NULL;
1319 }
1320
1321 static
1322 struct lttng_condition *handle_condition_buffer_usage_low(int *argc, const char ***argv)
1323 {
1324 ERR("Low buffer usage threshold conditions are unsupported for the moment.");
1325 return NULL;
1326 }
1327
1328 static
1329 struct lttng_condition *handle_condition_session_rotation_ongoing(int *argc, const char ***argv)
1330 {
1331 ERR("Session rotation ongoing conditions are unsupported for the moment.");
1332 return NULL;
1333 }
1334
1335 static
1336 struct lttng_condition *handle_condition_session_rotation_completed(int *argc, const char ***argv)
1337 {
1338 ERR("Session rotation completed conditions are unsupported for the moment.");
1339 return NULL;
1340 }
1341
1342 struct condition_descr {
1343 const char *name;
1344 struct lttng_condition *(*handler) (int *argc, const char ***argv);
1345 };
1346
1347 static const
1348 struct condition_descr condition_descrs[] = {
1349 { "event-rule-matches", handle_condition_event },
1350 { "on-session-consumed-size", handle_condition_session_consumed_size },
1351 { "on-buffer-usage-high", handle_condition_buffer_usage_high },
1352 { "on-buffer-usage-low", handle_condition_buffer_usage_low },
1353 { "on-session-rotation-ongoing", handle_condition_session_rotation_ongoing },
1354 { "on-session-rotation-completed", handle_condition_session_rotation_completed },
1355 };
1356
1357 static
1358 struct lttng_condition *parse_condition(const char *condition_name, int *argc,
1359 const char ***argv)
1360 {
1361 int i;
1362 struct lttng_condition *cond;
1363 const struct condition_descr *descr = NULL;
1364
1365 for (i = 0; i < ARRAY_SIZE(condition_descrs); i++) {
1366 if (strcmp(condition_name, condition_descrs[i].name) == 0) {
1367 descr = &condition_descrs[i];
1368 break;
1369 }
1370 }
1371
1372 if (!descr) {
1373 ERR("Unknown condition name '%s'", condition_name);
1374 goto error;
1375 }
1376
1377 cond = descr->handler(argc, argv);
1378 if (!cond) {
1379 /* The handler has already printed an error message. */
1380 goto error;
1381 }
1382
1383 goto end;
1384 error:
1385 cond = NULL;
1386 end:
1387 return cond;
1388 }
1389
1390 static struct lttng_rate_policy *parse_rate_policy(const char *policy_str)
1391 {
1392 int num_token;
1393 char **tokens = NULL;
1394 struct lttng_rate_policy *policy = NULL;
1395 enum lttng_rate_policy_type policy_type;
1396 unsigned long long value;
1397 char *policy_type_str;
1398 char *policy_value_str;
1399
1400 assert(policy_str);
1401
1402 /*
1403 * rate policy fields are separated by ':'.
1404 */
1405 tokens = strutils_split(policy_str, ':', 1);
1406 num_token = strutils_array_of_strings_len(tokens);
1407
1408 /*
1409 * Early sanity check that the number of parameter is exactly 2.
1410 * i.e : type:value
1411 */
1412 if (num_token != 2) {
1413 ERR("Rate policy format is invalid.");
1414 goto end;
1415 }
1416
1417 policy_type_str = tokens[0];
1418 policy_value_str = tokens[1];
1419
1420 /* Parse the type. */
1421 if (strcmp(policy_type_str, "once-after") == 0) {
1422 policy_type = LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N;
1423 } else if (strcmp(policy_type_str, "every") == 0) {
1424 policy_type = LTTNG_RATE_POLICY_TYPE_EVERY_N;
1425 } else {
1426 ERR("Rate policy type `%s` unknown.", policy_type_str);
1427 goto end;
1428 }
1429
1430 /* Parse the value. */
1431 if (utils_parse_unsigned_long_long(policy_value_str, &value) != 0) {
1432 ERR("Failed to parse rate policy value `%s` as an integer.",
1433 policy_value_str);
1434 goto end;
1435 }
1436
1437 if (value == 0) {
1438 ERR("Rate policy value `%s` must be > 0.", policy_value_str);
1439 goto end;
1440 }
1441
1442 switch (policy_type) {
1443 case LTTNG_RATE_POLICY_TYPE_EVERY_N:
1444 policy = lttng_rate_policy_every_n_create(value);
1445 break;
1446 case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
1447 policy = lttng_rate_policy_once_after_n_create(value);
1448 break;
1449 default:
1450 abort();
1451 }
1452
1453 if (policy == NULL) {
1454 ERR("Failed to create rate policy `%s`.", policy_str);
1455 }
1456
1457 end:
1458 strutils_free_null_terminated_array_of_strings(tokens);
1459 return policy;
1460 }
1461
1462 static const struct argpar_opt_descr notify_action_opt_descrs[] = {
1463 { OPT_RATE_POLICY, '\0', "rate-policy", true },
1464 ARGPAR_OPT_DESCR_SENTINEL
1465 };
1466
1467 static
1468 struct lttng_action *handle_action_notify(int *argc, const char ***argv)
1469 {
1470 struct lttng_action *action = NULL;
1471 struct argpar_state *state = NULL;
1472 struct argpar_item *item = NULL;
1473 char *error = NULL;
1474 struct lttng_rate_policy *policy = NULL;
1475
1476 state = argpar_state_create(*argc, *argv, notify_action_opt_descrs);
1477 if (!state) {
1478 ERR("Failed to allocate an argpar state.");
1479 goto error;
1480 }
1481
1482 while (true) {
1483 enum argpar_state_parse_next_status status;
1484
1485 ARGPAR_ITEM_DESTROY_AND_RESET(item);
1486 status = argpar_state_parse_next(state, &item, &error);
1487 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
1488 ERR("%s", error);
1489 goto error;
1490 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
1491 /* Just stop parsing here. */
1492 break;
1493 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
1494 break;
1495 }
1496
1497 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
1498
1499 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
1500 const struct argpar_item_opt *item_opt =
1501 (const struct argpar_item_opt *) item;
1502
1503 switch (item_opt->descr->id) {
1504 case OPT_RATE_POLICY:
1505 {
1506 policy = parse_rate_policy(item_opt->arg);
1507 if (!policy) {
1508 goto error;
1509 }
1510 break;
1511 }
1512 default:
1513 abort();
1514 }
1515 } else {
1516 const struct argpar_item_non_opt *item_non_opt;
1517
1518 assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
1519
1520 item_non_opt = (const struct argpar_item_non_opt *) item;
1521
1522 switch (item_non_opt->non_opt_index) {
1523 default:
1524 ERR("Unexpected argument `%s`.",
1525 item_non_opt->arg);
1526 goto error;
1527 }
1528 }
1529 }
1530
1531 *argc -= argpar_state_get_ingested_orig_args(state);
1532 *argv += argpar_state_get_ingested_orig_args(state);
1533
1534 action = lttng_action_notify_create();
1535 if (!action) {
1536 ERR("Failed to create notify action");
1537 goto error;
1538 }
1539
1540 if (policy) {
1541 enum lttng_action_status status;
1542 status = lttng_action_notify_set_rate_policy(action, policy);
1543 if (status != LTTNG_ACTION_STATUS_OK) {
1544 ERR("Failed to set rate policy");
1545 goto error;
1546 }
1547 }
1548
1549 goto end;
1550
1551 error:
1552 lttng_action_destroy(action);
1553 action = NULL;
1554 end:
1555 free(error);
1556 lttng_rate_policy_destroy(policy);
1557 argpar_state_destroy(state);
1558 argpar_item_destroy(item);
1559 return action;
1560 }
1561
1562 /*
1563 * Generic handler for a kind of action that takes a session name and an
1564 * optional rate policy.
1565 */
1566
1567 static struct lttng_action *handle_action_simple_session_with_policy(int *argc,
1568 const char ***argv,
1569 struct lttng_action *(*create_action_cb)(void),
1570 enum lttng_action_status (*set_session_name_cb)(
1571 struct lttng_action *, const char *),
1572 enum lttng_action_status (*set_rate_policy_cb)(
1573 struct lttng_action *,
1574 const struct lttng_rate_policy *),
1575 const char *action_name)
1576 {
1577 struct lttng_action *action = NULL;
1578 struct argpar_state *state = NULL;
1579 struct argpar_item *item = NULL;
1580 const char *session_name_arg = NULL;
1581 char *error = NULL;
1582 enum lttng_action_status action_status;
1583 struct lttng_rate_policy *policy = NULL;
1584
1585 assert(set_session_name_cb);
1586 assert(set_rate_policy_cb);
1587
1588 const struct argpar_opt_descr rate_policy_opt_descrs[] = {
1589 { OPT_RATE_POLICY, '\0', "rate-policy", true },
1590 ARGPAR_OPT_DESCR_SENTINEL
1591 };
1592
1593 state = argpar_state_create(*argc, *argv, rate_policy_opt_descrs);
1594 if (!state) {
1595 ERR("Failed to allocate an argpar state.");
1596 goto error;
1597 }
1598
1599 while (true) {
1600 enum argpar_state_parse_next_status status;
1601
1602 ARGPAR_ITEM_DESTROY_AND_RESET(item);
1603 status = argpar_state_parse_next(state, &item, &error);
1604 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
1605 ERR("%s", error);
1606 goto error;
1607 } else if (status ==
1608 ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
1609 /* Just stop parsing here. */
1610 break;
1611 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
1612 break;
1613 }
1614
1615 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
1616 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
1617 const struct argpar_item_opt *item_opt =
1618 (const struct argpar_item_opt *) item;
1619
1620 switch (item_opt->descr->id) {
1621 case OPT_RATE_POLICY:
1622 {
1623 policy = parse_rate_policy(item_opt->arg);
1624 if (!policy) {
1625 goto error;
1626 }
1627 break;
1628 }
1629 default:
1630 abort();
1631 }
1632 } else {
1633 const struct argpar_item_non_opt *item_non_opt;
1634 item_non_opt = (const struct argpar_item_non_opt *) item;
1635
1636 switch (item_non_opt->non_opt_index) {
1637 case 0:
1638 session_name_arg = item_non_opt->arg;
1639 break;
1640 default:
1641 ERR("Unexpected argument `%s`.",
1642 item_non_opt->arg);
1643 goto error;
1644 }
1645 }
1646 }
1647
1648 *argc -= argpar_state_get_ingested_orig_args(state);
1649 *argv += argpar_state_get_ingested_orig_args(state);
1650
1651 if (!session_name_arg) {
1652 ERR("Missing session name.");
1653 goto error;
1654 }
1655
1656 action = create_action_cb();
1657 if (!action) {
1658 ERR("Failed to allocate %s session action.", action_name);
1659 goto error;
1660 }
1661
1662 action_status = set_session_name_cb(action, session_name_arg);
1663 if (action_status != LTTNG_ACTION_STATUS_OK) {
1664 ERR("Failed to set action %s session's session name to '%s'.",
1665 action_name, session_name_arg);
1666 goto error;
1667 }
1668
1669 if (policy) {
1670 action_status = set_rate_policy_cb(action, policy);
1671 if (action_status != LTTNG_ACTION_STATUS_OK) {
1672 ERR("Failed to set rate policy");
1673 goto error;
1674 }
1675 }
1676
1677 goto end;
1678
1679 error:
1680 lttng_action_destroy(action);
1681 action = NULL;
1682 argpar_item_destroy(item);
1683 end:
1684 lttng_rate_policy_destroy(policy);
1685 free(error);
1686 argpar_state_destroy(state);
1687 return action;
1688 }
1689
1690 static
1691 struct lttng_action *handle_action_start_session(int *argc,
1692 const char ***argv)
1693 {
1694 return handle_action_simple_session_with_policy(argc, argv,
1695 lttng_action_start_session_create,
1696 lttng_action_start_session_set_session_name,
1697 lttng_action_start_session_set_rate_policy, "start");
1698 }
1699
1700 static
1701 struct lttng_action *handle_action_stop_session(int *argc,
1702 const char ***argv)
1703 {
1704 return handle_action_simple_session_with_policy(argc, argv,
1705 lttng_action_stop_session_create,
1706 lttng_action_stop_session_set_session_name,
1707 lttng_action_stop_session_set_rate_policy, "stop");
1708 }
1709
1710 static
1711 struct lttng_action *handle_action_rotate_session(int *argc,
1712 const char ***argv)
1713 {
1714 return handle_action_simple_session_with_policy(argc, argv,
1715 lttng_action_rotate_session_create,
1716 lttng_action_rotate_session_set_session_name,
1717 lttng_action_rotate_session_set_rate_policy,
1718 "rotate");
1719 }
1720
1721 static const struct argpar_opt_descr snapshot_action_opt_descrs[] = {
1722 { OPT_NAME, 'n', "name", true },
1723 { OPT_MAX_SIZE, 'm', "max-size", true },
1724 { OPT_CTRL_URL, '\0', "ctrl-url", true },
1725 { OPT_DATA_URL, '\0', "data-url", true },
1726 { OPT_URL, '\0', "url", true },
1727 { OPT_PATH, '\0', "path", true },
1728 { OPT_RATE_POLICY, '\0', "rate-policy", true },
1729 ARGPAR_OPT_DESCR_SENTINEL
1730 };
1731
1732 static
1733 struct lttng_action *handle_action_snapshot_session(int *argc,
1734 const char ***argv)
1735 {
1736 struct lttng_action *action = NULL;
1737 struct argpar_state *state = NULL;
1738 struct argpar_item *item = NULL;
1739 const char *session_name_arg = NULL;
1740 char *snapshot_name_arg = NULL;
1741 char *ctrl_url_arg = NULL;
1742 char *data_url_arg = NULL;
1743 char *max_size_arg = NULL;
1744 char *url_arg = NULL;
1745 char *path_arg = NULL;
1746 char *error = NULL;
1747 enum lttng_action_status action_status;
1748 struct lttng_snapshot_output *snapshot_output = NULL;
1749 struct lttng_rate_policy *policy = NULL;
1750 int ret;
1751 unsigned int locations_specified = 0;
1752
1753 state = argpar_state_create(*argc, *argv, snapshot_action_opt_descrs);
1754 if (!state) {
1755 ERR("Failed to allocate an argpar state.");
1756 goto error;
1757 }
1758
1759 while (true) {
1760 enum argpar_state_parse_next_status status;
1761
1762 ARGPAR_ITEM_DESTROY_AND_RESET(item);
1763 status = argpar_state_parse_next(state, &item, &error);
1764 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
1765 ERR("%s", error);
1766 goto error;
1767 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
1768 /* Just stop parsing here. */
1769 break;
1770 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
1771 break;
1772 }
1773
1774 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
1775
1776 if (item->type == ARGPAR_ITEM_TYPE_OPT) {
1777 const struct argpar_item_opt *item_opt =
1778 (const struct argpar_item_opt *) item;
1779
1780 switch (item_opt->descr->id) {
1781 case OPT_NAME:
1782 if (!assign_string(&snapshot_name_arg, item_opt->arg, "--name/-n")) {
1783 goto error;
1784 }
1785
1786 break;
1787 case OPT_MAX_SIZE:
1788 if (!assign_string(&max_size_arg, item_opt->arg, "--max-size/-m")) {
1789 goto error;
1790 }
1791
1792 break;
1793 case OPT_CTRL_URL:
1794 if (!assign_string(&ctrl_url_arg, item_opt->arg, "--ctrl-url")) {
1795 goto error;
1796 }
1797
1798 break;
1799 case OPT_DATA_URL:
1800 if (!assign_string(&data_url_arg, item_opt->arg, "--data-url")) {
1801 goto error;
1802 }
1803
1804 break;
1805 case OPT_URL:
1806 if (!assign_string(&url_arg, item_opt->arg, "--url")) {
1807 goto error;
1808 }
1809
1810 break;
1811 case OPT_PATH:
1812 if (!assign_string(&path_arg, item_opt->arg, "--path")) {
1813 goto error;
1814 }
1815
1816 break;
1817 case OPT_RATE_POLICY:
1818 {
1819 policy = parse_rate_policy(item_opt->arg);
1820 if (!policy) {
1821 goto error;
1822 }
1823 break;
1824 }
1825 default:
1826 abort();
1827 }
1828 } else {
1829 const struct argpar_item_non_opt *item_non_opt;
1830
1831 assert(item->type == ARGPAR_ITEM_TYPE_NON_OPT);
1832
1833 item_non_opt = (const struct argpar_item_non_opt *) item;
1834
1835 switch (item_non_opt->non_opt_index) {
1836 case 0:
1837 session_name_arg = item_non_opt->arg;
1838 break;
1839 default:
1840 ERR("Unexpected argument `%s`.",
1841 item_non_opt->arg);
1842 goto error;
1843 }
1844 }
1845 }
1846
1847 *argc -= argpar_state_get_ingested_orig_args(state);
1848 *argv += argpar_state_get_ingested_orig_args(state);
1849
1850 if (!session_name_arg) {
1851 ERR("Missing session name.");
1852 goto error;
1853 }
1854
1855 /* --ctrl-url and --data-url must come in pair. */
1856 if (ctrl_url_arg && !data_url_arg) {
1857 ERR("--ctrl-url is specified, but --data-url is missing.");
1858 goto error;
1859 }
1860
1861 if (!ctrl_url_arg && data_url_arg) {
1862 ERR("--data-url is specified, but --ctrl-url is missing.");
1863 goto error;
1864 }
1865
1866 locations_specified += !!(ctrl_url_arg || data_url_arg);
1867 locations_specified += !!url_arg;
1868 locations_specified += !!path_arg;
1869
1870 /* --ctrl-url/--data-url, --url and --path are mutually exclusive. */
1871 if (locations_specified > 1) {
1872 ERR("The --ctrl-url/--data-url, --url, and --path options can't be used together.");
1873 goto error;
1874 }
1875
1876 /*
1877 * Did the user specify an option that implies using a
1878 * custom/unregistered output?
1879 */
1880 if (url_arg || ctrl_url_arg || path_arg) {
1881 snapshot_output = lttng_snapshot_output_create();
1882 if (!snapshot_output) {
1883 ERR("Failed to allocate a snapshot output.");
1884 goto error;
1885 }
1886 }
1887
1888 action = lttng_action_snapshot_session_create();
1889 if (!action) {
1890 ERR("Failed to allocate snapshot session action.");
1891 goto error;
1892 }
1893
1894 action_status = lttng_action_snapshot_session_set_session_name(
1895 action, session_name_arg);
1896 if (action_status != LTTNG_ACTION_STATUS_OK) {
1897 ERR("Failed to set action snapshot session's session name to '%s'.",
1898 session_name_arg);
1899 goto error;
1900 }
1901
1902 if (snapshot_name_arg) {
1903 if (!snapshot_output) {
1904 ERR("Can't provide a snapshot output name without a snapshot output destination.");
1905 goto error;
1906 }
1907
1908 ret = lttng_snapshot_output_set_name(
1909 snapshot_name_arg, snapshot_output);
1910 if (ret != 0) {
1911 ERR("Failed to set name of snapshot output.");
1912 goto error;
1913 }
1914 }
1915
1916 if (max_size_arg) {
1917 uint64_t max_size;
1918
1919 if (!snapshot_output) {
1920 ERR("Can't provide a snapshot output max size without a snapshot output destination.");
1921 goto error;
1922 }
1923
1924 ret = utils_parse_size_suffix(max_size_arg, &max_size);
1925 if (ret != 0) {
1926 ERR("Failed to parse `%s` as a size.", max_size_arg);
1927 goto error;
1928 }
1929
1930 ret = lttng_snapshot_output_set_size(max_size, snapshot_output);
1931 if (ret != 0) {
1932 ERR("Failed to set snapshot output's max size to %" PRIu64 " bytes.",
1933 max_size);
1934 goto error;
1935 }
1936 }
1937
1938 if (url_arg) {
1939 int num_uris;
1940 struct lttng_uri *uris;
1941
1942 if (!strstr(url_arg, "://")) {
1943 ERR("Failed to parse '%s' as an URL.", url_arg);
1944 goto error;
1945 }
1946
1947 num_uris = uri_parse_str_urls(url_arg, NULL, &uris);
1948 if (num_uris < 1) {
1949 ERR("Failed to parse '%s' as an URL.", url_arg);
1950 goto error;
1951 }
1952
1953 if (uris[0].dtype == LTTNG_DST_PATH) {
1954 ret = lttng_snapshot_output_set_local_path(
1955 uris[0].dst.path, snapshot_output);
1956 free(uris);
1957 if (ret != 0) {
1958 ERR("Failed to assign '%s' as a local destination.",
1959 url_arg);
1960 goto error;
1961 }
1962 } else {
1963 ret = lttng_snapshot_output_set_network_url(
1964 url_arg, snapshot_output);
1965 free(uris);
1966 if (ret != 0) {
1967 ERR("Failed to assign '%s' as a network URL.",
1968 url_arg);
1969 goto error;
1970 }
1971 }
1972 }
1973
1974 if (path_arg) {
1975 ret = lttng_snapshot_output_set_local_path(
1976 path_arg, snapshot_output);
1977 if (ret != 0) {
1978 ERR("Failed to parse '%s' as a local path.", path_arg);
1979 goto error;
1980 }
1981 }
1982
1983 if (ctrl_url_arg) {
1984 /*
1985 * Two argument form, network output with separate control and
1986 * data URLs.
1987 */
1988 ret = lttng_snapshot_output_set_network_urls(
1989 ctrl_url_arg, data_url_arg, snapshot_output);
1990 if (ret != 0) {
1991 ERR("Failed to parse `%s` and `%s` as control and data URLs.",
1992 ctrl_url_arg, data_url_arg);
1993 goto error;
1994 }
1995 }
1996
1997 if (snapshot_output) {
1998 action_status = lttng_action_snapshot_session_set_output(
1999 action, snapshot_output);
2000 if (action_status != LTTNG_ACTION_STATUS_OK) {
2001 ERR("Failed to set snapshot session action's output.");
2002 goto error;
2003 }
2004
2005 /* Ownership of `snapshot_output` has been transferred to the action. */
2006 snapshot_output = NULL;
2007 }
2008
2009 if (policy) {
2010 enum lttng_action_status status;
2011 status = lttng_action_snapshot_session_set_rate_policy(
2012 action, policy);
2013 if (status != LTTNG_ACTION_STATUS_OK) {
2014 ERR("Failed to set rate policy");
2015 goto error;
2016 }
2017 }
2018
2019 goto end;
2020
2021 error:
2022 lttng_action_destroy(action);
2023 action = NULL;
2024 free(error);
2025 end:
2026 free(snapshot_name_arg);
2027 free(path_arg);
2028 free(url_arg);
2029 free(ctrl_url_arg);
2030 free(data_url_arg);
2031 free(snapshot_output);
2032 free(max_size_arg);
2033 lttng_rate_policy_destroy(policy);
2034 argpar_state_destroy(state);
2035 argpar_item_destroy(item);
2036 return action;
2037 }
2038
2039 struct action_descr {
2040 const char *name;
2041 struct lttng_action *(*handler) (int *argc, const char ***argv);
2042 };
2043
2044 static const
2045 struct action_descr action_descrs[] = {
2046 { "notify", handle_action_notify },
2047 { "start-session", handle_action_start_session },
2048 { "stop-session", handle_action_stop_session },
2049 { "rotate-session", handle_action_rotate_session },
2050 { "snapshot-session", handle_action_snapshot_session },
2051 };
2052
2053 static
2054 struct lttng_action *parse_action(const char *action_name, int *argc, const char ***argv)
2055 {
2056 int i;
2057 struct lttng_action *action;
2058 const struct action_descr *descr = NULL;
2059
2060 for (i = 0; i < ARRAY_SIZE(action_descrs); i++) {
2061 if (strcmp(action_name, action_descrs[i].name) == 0) {
2062 descr = &action_descrs[i];
2063 break;
2064 }
2065 }
2066
2067 if (!descr) {
2068 ERR("Unknown action name: %s", action_name);
2069 goto error;
2070 }
2071
2072 action = descr->handler(argc, argv);
2073 if (!action) {
2074 /* The handler has already printed an error message. */
2075 goto error;
2076 }
2077
2078 goto end;
2079 error:
2080 action = NULL;
2081 end:
2082 return action;
2083 }
2084
2085 static const
2086 struct argpar_opt_descr add_trigger_options[] = {
2087 { OPT_HELP, 'h', "help", false },
2088 { OPT_LIST_OPTIONS, '\0', "list-options", false },
2089 { OPT_CONDITION, '\0', "condition", true },
2090 { OPT_ACTION, '\0', "action", true },
2091 { OPT_NAME, '\0', "name", true },
2092 { OPT_USER_ID, '\0', "user-id", true },
2093 ARGPAR_OPT_DESCR_SENTINEL,
2094 };
2095
2096 static
2097 void lttng_actions_destructor(void *p)
2098 {
2099 struct lttng_action *action = p;
2100
2101 lttng_action_destroy(action);
2102 }
2103
2104 int cmd_add_trigger(int argc, const char **argv)
2105 {
2106 int ret;
2107 int my_argc = argc - 1;
2108 const char **my_argv = argv + 1;
2109 struct lttng_condition *condition = NULL;
2110 struct lttng_dynamic_pointer_array actions;
2111 struct argpar_state *argpar_state = NULL;
2112 struct argpar_item *argpar_item = NULL;
2113 struct lttng_action *action_group = NULL;
2114 struct lttng_action *action = NULL;
2115 struct lttng_trigger *trigger = NULL;
2116 char *error = NULL;
2117 char *name = NULL;
2118 int i;
2119 char *user_id = NULL;
2120
2121 lttng_dynamic_pointer_array_init(&actions, lttng_actions_destructor);
2122
2123 while (true) {
2124 enum argpar_state_parse_next_status status;
2125 const struct argpar_item_opt *item_opt;
2126 int ingested_args;
2127
2128 argpar_state_destroy(argpar_state);
2129 argpar_state = argpar_state_create(my_argc, my_argv,
2130 add_trigger_options);
2131 if (!argpar_state) {
2132 ERR("Failed to create argpar state.");
2133 goto error;
2134 }
2135
2136 ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item);
2137 status = argpar_state_parse_next(argpar_state, &argpar_item, &error);
2138 if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR) {
2139 ERR("%s", error);
2140 goto error;
2141 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_ERROR_UNKNOWN_OPT) {
2142 ERR("%s", error);
2143 goto error;
2144 } else if (status == ARGPAR_STATE_PARSE_NEXT_STATUS_END) {
2145 break;
2146 }
2147
2148 assert(status == ARGPAR_STATE_PARSE_NEXT_STATUS_OK);
2149
2150 if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) {
2151 const struct argpar_item_non_opt *item_non_opt =
2152 (const struct argpar_item_non_opt *)
2153 argpar_item;
2154
2155 ERR("Unexpected argument `%s`.", item_non_opt->arg);
2156 goto error;
2157 }
2158
2159 item_opt = (const struct argpar_item_opt *) argpar_item;
2160
2161 ingested_args = argpar_state_get_ingested_orig_args(
2162 argpar_state);
2163
2164 my_argc -= ingested_args;
2165 my_argv += ingested_args;
2166
2167 switch (item_opt->descr->id) {
2168 case OPT_HELP:
2169 SHOW_HELP();
2170 ret = 0;
2171 goto end;
2172 case OPT_LIST_OPTIONS:
2173 list_cmd_options_argpar(stdout, add_trigger_options);
2174 ret = 0;
2175 goto end;
2176 case OPT_CONDITION:
2177 {
2178 if (condition) {
2179 ERR("A --condition was already given.");
2180 goto error;
2181 }
2182
2183 condition = parse_condition(item_opt->arg, &my_argc, &my_argv);
2184 if (!condition) {
2185 /*
2186 * An error message was already printed by
2187 * parse_condition.
2188 */
2189 goto error;
2190 }
2191
2192 break;
2193 }
2194 case OPT_ACTION:
2195 {
2196 action = parse_action(item_opt->arg, &my_argc, &my_argv);
2197 if (!action) {
2198 /*
2199 * An error message was already printed by
2200 * parse_condition.
2201 */
2202 goto error;
2203 }
2204
2205 ret = lttng_dynamic_pointer_array_add_pointer(
2206 &actions, action);
2207 if (ret) {
2208 ERR("Failed to add pointer to pointer array.");
2209 goto error;
2210 }
2211
2212 /* Ownership of the action was transferred to the group. */
2213 action = NULL;
2214
2215 break;
2216 }
2217 case OPT_NAME:
2218 {
2219 if (!assign_string(&name, item_opt->arg, "--name")) {
2220 goto error;
2221 }
2222
2223 break;
2224 }
2225 case OPT_USER_ID:
2226 {
2227 if (!assign_string(&user_id, item_opt->arg,
2228 "--user-id")) {
2229 goto error;
2230 }
2231
2232 break;
2233 }
2234 default:
2235 abort();
2236 }
2237 }
2238
2239 if (!condition) {
2240 ERR("Missing --condition.");
2241 goto error;
2242 }
2243
2244 if (lttng_dynamic_pointer_array_get_count(&actions) == 0) {
2245 ERR("Need at least one --action.");
2246 goto error;
2247 }
2248
2249 action_group = lttng_action_group_create();
2250 if (!action_group) {
2251 goto error;
2252 }
2253
2254 for (i = 0; i < lttng_dynamic_pointer_array_get_count(&actions); i++) {
2255 enum lttng_action_status status;
2256
2257 action = lttng_dynamic_pointer_array_steal_pointer(&actions, i);
2258
2259 status = lttng_action_group_add_action(action_group, action);
2260 if (status != LTTNG_ACTION_STATUS_OK) {
2261 goto error;
2262 }
2263
2264 /*
2265 * The `lttng_action_group_add_action()` takes a reference to
2266 * the action. We can destroy ours.
2267 */
2268 lttng_action_destroy(action);
2269 action = NULL;
2270 }
2271
2272 trigger = lttng_trigger_create(condition, action_group);
2273 if (!trigger) {
2274 goto error;
2275 }
2276
2277 if (name) {
2278 enum lttng_trigger_status trigger_status =
2279 lttng_trigger_set_name(trigger, name);
2280
2281 if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
2282 ERR("Failed to set trigger name.");
2283 goto error;
2284 }
2285 }
2286
2287 if (user_id) {
2288 enum lttng_trigger_status trigger_status;
2289 char *end;
2290 long long uid;
2291
2292 errno = 0;
2293 uid = strtol(user_id, &end, 10);
2294 if (end == user_id || *end != '\0' || errno != 0) {
2295 ERR("Failed to parse `%s` as a user id.", user_id);
2296 }
2297
2298 trigger_status = lttng_trigger_set_owner_uid(trigger, uid);
2299 if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
2300 ERR("Failed to set trigger's user identity.");
2301 goto error;
2302 }
2303 }
2304
2305 ret = lttng_register_trigger(trigger);
2306 if (ret) {
2307 ERR("Failed to register trigger: %s.", lttng_strerror(ret));
2308 goto error;
2309 }
2310
2311 MSG("Trigger registered successfully.");
2312
2313 goto end;
2314
2315 error:
2316 ret = 1;
2317
2318 end:
2319 argpar_state_destroy(argpar_state);
2320 argpar_item_destroy(argpar_item);
2321 lttng_dynamic_pointer_array_reset(&actions);
2322 lttng_condition_destroy(condition);
2323 lttng_action_destroy(action_group);
2324 lttng_action_destroy(action);
2325 lttng_trigger_destroy(trigger);
2326 free(error);
2327 free(name);
2328 free(user_id);
2329 return ret;
2330 }
This page took 0.108076 seconds and 5 git commands to generate.