+ ret = CMD_ERROR;
+
+end:
+ return ret;
+}
+
+/*
+ * Maps LOG4j loglevel from string to value
+ */
+static int loglevel_log4j_str_to_value(const char *inputstr)
+{
+ int i = 0;
+ char str[LTTNG_SYMBOL_NAME_LEN];
+
+ if (!inputstr || strlen(inputstr) == 0) {
+ return -1;
+ }
+
+ /*
+ * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is
+ * added at the end of the loop so a the upper bound we avoid the overflow.
+ */
+ while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') {
+ str[i] = toupper(inputstr[i]);
+ i++;
+ }
+ str[i] = '\0';
+
+ if (!strcmp(str, "LOG4J_OFF") || !strcmp(str, "OFF")) {
+ return LTTNG_LOGLEVEL_LOG4J_OFF;
+ } else if (!strcmp(str, "LOG4J_FATAL") || !strcmp(str, "FATAL")) {
+ return LTTNG_LOGLEVEL_LOG4J_FATAL;
+ } else if (!strcmp(str, "LOG4J_ERROR") || !strcmp(str, "ERROR")) {
+ return LTTNG_LOGLEVEL_LOG4J_ERROR;
+ } else if (!strcmp(str, "LOG4J_WARN") || !strcmp(str, "WARN")) {
+ return LTTNG_LOGLEVEL_LOG4J_WARN;
+ } else if (!strcmp(str, "LOG4J_INFO") || !strcmp(str, "INFO")) {
+ return LTTNG_LOGLEVEL_LOG4J_INFO;
+ } else if (!strcmp(str, "LOG4J_DEBUG") || !strcmp(str, "DEBUG")) {
+ return LTTNG_LOGLEVEL_LOG4J_DEBUG;
+ } else if (!strcmp(str, "LOG4J_TRACE") || !strcmp(str, "TRACE")) {
+ return LTTNG_LOGLEVEL_LOG4J_TRACE;
+ } else if (!strcmp(str, "LOG4J_ALL") || !strcmp(str, "ALL")) {
+ return LTTNG_LOGLEVEL_LOG4J_ALL;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * Maps JUL loglevel from string to value
+ */
+static int loglevel_jul_str_to_value(const char *inputstr)
+{
+ int i = 0;
+ char str[LTTNG_SYMBOL_NAME_LEN];
+
+ if (!inputstr || strlen(inputstr) == 0) {
+ return -1;
+ }
+
+ /*
+ * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is
+ * added at the end of the loop so a the upper bound we avoid the overflow.
+ */
+ while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') {
+ str[i] = toupper(inputstr[i]);
+ i++;
+ }
+ str[i] = '\0';
+
+ if (!strcmp(str, "JUL_OFF") || !strcmp(str, "OFF")) {
+ return LTTNG_LOGLEVEL_JUL_OFF;
+ } else if (!strcmp(str, "JUL_SEVERE") || !strcmp(str, "SEVERE")) {
+ return LTTNG_LOGLEVEL_JUL_SEVERE;
+ } else if (!strcmp(str, "JUL_WARNING") || !strcmp(str, "WARNING")) {
+ return LTTNG_LOGLEVEL_JUL_WARNING;
+ } else if (!strcmp(str, "JUL_INFO") || !strcmp(str, "INFO")) {
+ return LTTNG_LOGLEVEL_JUL_INFO;
+ } else if (!strcmp(str, "JUL_CONFIG") || !strcmp(str, "CONFIG")) {
+ return LTTNG_LOGLEVEL_JUL_CONFIG;
+ } else if (!strcmp(str, "JUL_FINE") || !strcmp(str, "FINE")) {
+ return LTTNG_LOGLEVEL_JUL_FINE;
+ } else if (!strcmp(str, "JUL_FINER") || !strcmp(str, "FINER")) {
+ return LTTNG_LOGLEVEL_JUL_FINER;
+ } else if (!strcmp(str, "JUL_FINEST") || !strcmp(str, "FINEST")) {
+ return LTTNG_LOGLEVEL_JUL_FINEST;
+ } else if (!strcmp(str, "JUL_ALL") || !strcmp(str, "ALL")) {
+ return LTTNG_LOGLEVEL_JUL_ALL;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * Maps Python loglevel from string to value
+ */
+static int loglevel_python_str_to_value(const char *inputstr)
+{
+ int i = 0;
+ char str[LTTNG_SYMBOL_NAME_LEN];
+
+ if (!inputstr || strlen(inputstr) == 0) {
+ return -1;
+ }
+
+ /*
+ * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is
+ * added at the end of the loop so a the upper bound we avoid the overflow.
+ */
+ while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') {
+ str[i] = toupper(inputstr[i]);
+ i++;
+ }
+ str[i] = '\0';
+
+ if (!strcmp(str, "PYTHON_CRITICAL") || !strcmp(str, "CRITICAL")) {
+ return LTTNG_LOGLEVEL_PYTHON_CRITICAL;
+ } else if (!strcmp(str, "PYTHON_ERROR") || !strcmp(str, "ERROR")) {
+ return LTTNG_LOGLEVEL_PYTHON_ERROR;
+ } else if (!strcmp(str, "PYTHON_WARNING") || !strcmp(str, "WARNING")) {
+ return LTTNG_LOGLEVEL_PYTHON_WARNING;
+ } else if (!strcmp(str, "PYTHON_INFO") || !strcmp(str, "INFO")) {
+ return LTTNG_LOGLEVEL_PYTHON_INFO;
+ } else if (!strcmp(str, "PYTNON_DEBUG") || !strcmp(str, "DEBUG")) {
+ return LTTNG_LOGLEVEL_PYTHON_DEBUG;
+ } else if (!strcmp(str, "PYTHON_NOTSET") || !strcmp(str, "NOTSET")) {
+ return LTTNG_LOGLEVEL_PYTHON_NOTSET;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * Maps loglevel from string to value
+ */
+static
+int loglevel_str_to_value(const char *inputstr)
+{
+ int i = 0;
+ char str[LTTNG_SYMBOL_NAME_LEN];
+
+ if (!inputstr || strlen(inputstr) == 0) {
+ return -1;
+ }
+
+ /*
+ * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is
+ * added at the end of the loop so a the upper bound we avoid the overflow.
+ */
+ while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') {
+ str[i] = toupper(inputstr[i]);
+ i++;
+ }
+ str[i] = '\0';
+ if (!strcmp(str, "TRACE_EMERG") || !strcmp(str, "EMERG")) {
+ return LTTNG_LOGLEVEL_EMERG;
+ } else if (!strcmp(str, "TRACE_ALERT") || !strcmp(str, "ALERT")) {
+ return LTTNG_LOGLEVEL_ALERT;
+ } else if (!strcmp(str, "TRACE_CRIT") || !strcmp(str, "CRIT")) {
+ return LTTNG_LOGLEVEL_CRIT;
+ } else if (!strcmp(str, "TRACE_ERR") || !strcmp(str, "ERR")) {
+ return LTTNG_LOGLEVEL_ERR;
+ } else if (!strcmp(str, "TRACE_WARNING") || !strcmp(str, "WARNING")) {
+ return LTTNG_LOGLEVEL_WARNING;
+ } else if (!strcmp(str, "TRACE_NOTICE") || !strcmp(str, "NOTICE")) {
+ return LTTNG_LOGLEVEL_NOTICE;
+ } else if (!strcmp(str, "TRACE_INFO") || !strcmp(str, "INFO")) {
+ return LTTNG_LOGLEVEL_INFO;
+ } else if (!strcmp(str, "TRACE_DEBUG_SYSTEM") || !strcmp(str, "DEBUG_SYSTEM") || !strcmp(str, "SYSTEM")) {
+ return LTTNG_LOGLEVEL_DEBUG_SYSTEM;
+ } else if (!strcmp(str, "TRACE_DEBUG_PROGRAM") || !strcmp(str, "DEBUG_PROGRAM") || !strcmp(str, "PROGRAM")) {
+ return LTTNG_LOGLEVEL_DEBUG_PROGRAM;
+ } else if (!strcmp(str, "TRACE_DEBUG_PROCESS") || !strcmp(str, "DEBUG_PROCESS") || !strcmp(str, "PROCESS")) {
+ return LTTNG_LOGLEVEL_DEBUG_PROCESS;
+ } else if (!strcmp(str, "TRACE_DEBUG_MODULE") || !strcmp(str, "DEBUG_MODULE") || !strcmp(str, "MODULE")) {
+ return LTTNG_LOGLEVEL_DEBUG_MODULE;
+ } else if (!strcmp(str, "TRACE_DEBUG_UNIT") || !strcmp(str, "DEBUG_UNIT") || !strcmp(str, "UNIT")) {
+ return LTTNG_LOGLEVEL_DEBUG_UNIT;
+ } else if (!strcmp(str, "TRACE_DEBUG_FUNCTION") || !strcmp(str, "DEBUG_FUNCTION") || !strcmp(str, "FUNCTION")) {
+ return LTTNG_LOGLEVEL_DEBUG_FUNCTION;
+ } else if (!strcmp(str, "TRACE_DEBUG_LINE") || !strcmp(str, "DEBUG_LINE") || !strcmp(str, "LINE")) {
+ return LTTNG_LOGLEVEL_DEBUG_LINE;
+ } else if (!strcmp(str, "TRACE_DEBUG") || !strcmp(str, "DEBUG")) {
+ return LTTNG_LOGLEVEL_DEBUG;
+ } else {
+ return -1;
+ }
+}
+
+static
+const char *print_channel_name(const char *name)
+{
+ return name ? : DEFAULT_CHANNEL_NAME;
+}
+
+static
+const char *print_raw_channel_name(const char *name)
+{
+ return name ? : "<default>";
+}
+
+/*
+ * Mi print exlcusion list
+ */
+static
+int mi_print_exclusion(char **names)
+{
+ int i, ret;
+ int count = names ? strutils_array_of_strings_len(names) : 0;
+
+ assert(writer);
+
+ if (count == 0) {
+ ret = 0;
+ goto end;
+ }
+ ret = mi_lttng_writer_open_element(writer, config_element_exclusions);
+ if (ret) {
+ goto end;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = mi_lttng_writer_write_element_string(writer,
+ config_element_exclusion, names[i]);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ /* Close exclusions element */
+ ret = mi_lttng_writer_close_element(writer);
+
+end:
+ return ret;
+}
+
+/*
+ * Return allocated string for pretty-printing exclusion names.
+ */
+static
+char *print_exclusions(char **names)
+{
+ int length = 0;
+ int i;
+ const char *preamble = " excluding ";
+ char *ret;
+ int count = names ? strutils_array_of_strings_len(names) : 0;
+
+ if (count == 0) {
+ return strdup("");
+ }
+
+ /* calculate total required length */
+ for (i = 0; i < count; i++) {
+ length += strlen(names[i]) + 4;
+ }
+
+ /* add length of preamble + one for NUL - one for last (missing) comma */
+ length += strlen(preamble);
+ ret = zmalloc(length + 1);
+ if (!ret) {
+ return NULL;
+ }
+ strncpy(ret, preamble, length);
+ for (i = 0; i < count; i++) {
+ strcat(ret, "\"");
+ strcat(ret, names[i]);
+ strcat(ret, "\"");
+ if (i != count - 1) {
+ strcat(ret, ", ");
+ }
+ }
+
+ return ret;
+}
+
+static
+int check_exclusion_subsets(const char *event_name, const char *exclusion)
+{
+ bool warn = false;
+ int ret = 0;
+ const char *e = event_name;
+ const char *x = exclusion;
+
+ /* Scan both the excluder and the event letter by letter */
+ while (true) {
+ if (*e == '\\') {
+ if (*x != *e) {
+ warn = true;
+ goto end;
+ }
+
+ e++;
+ x++;
+ goto cmp_chars;
+ }
+
+ if (*x == '*') {
+ /* Event is a subset of the excluder */
+ ERR("Event %s: %s excludes all events from %s",
+ event_name, exclusion, event_name);
+ goto error;
+ }
+
+ if (*e == '*') {
+ /*
+ * Reached the end of the event name before the
+ * end of the exclusion: this is valid.
+ */
+ goto end;
+ }
+
+cmp_chars:
+ if (*x != *e) {
+ warn = true;
+ break;
+ }
+
+ x++;
+ e++;
+ }
+
+ goto end;
+
+error:
+ ret = -1;
+
+end:
+ if (warn) {
+ WARN("Event %s: %s does not exclude any events from %s",
+ event_name, exclusion, event_name);
+ }
+
+ return ret;
+}
+
+static
+int create_exclusion_list_and_validate(const char *event_name,
+ const char *exclusions_arg,
+ char ***exclusion_list)
+{
+ int ret = 0;
+ char **exclusions = NULL;
+
+ /* Event name must be a valid globbing pattern to allow exclusions. */
+ if (!strutils_is_star_glob_pattern(event_name)) {
+ ERR("Event %s: Exclusions can only be used with a globbing pattern",
+ event_name);
+ goto error;
+ }
+
+ /* Split exclusions. */
+ exclusions = strutils_split(exclusions_arg, ',', true);
+ if (!exclusions) {
+ goto error;
+ }
+
+ /*
+ * If the event name is a star-at-end only globbing pattern,
+ * then we can validate the individual exclusions. Otherwise
+ * all exclusions are passed to the session daemon.
+ */
+ if (strutils_is_star_at_the_end_only_glob_pattern(event_name)) {
+ char * const *exclusion;
+
+ for (exclusion = exclusions; *exclusion; exclusion++) {
+ if (!strutils_is_star_glob_pattern(*exclusion) ||
+ strutils_is_star_at_the_end_only_glob_pattern(*exclusion)) {
+ ret = check_exclusion_subsets(event_name, *exclusion);
+ if (ret) {
+ goto error;
+ }
+ }
+ }
+ }
+
+ *exclusion_list = exclusions;
+
+ goto end;
+
+error: