+ ret = CMD_ERROR;
+
+end:
+ return ret;
+}
+
+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(const struct lttng_dynamic_pointer_array *exclusions)
+{
+ int ret;
+ size_t i;
+ const size_t count = lttng_dynamic_pointer_array_get_count(exclusions);
+
+ 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++) {
+ const char *exclusion = lttng_dynamic_pointer_array_get_pointer(
+ exclusions, i);
+
+ ret = mi_lttng_writer_write_element_string(writer,
+ config_element_exclusion, exclusion);
+ 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(const struct lttng_dynamic_pointer_array *exclusions)
+{
+ int length = 0;
+ size_t i;
+ const char preamble[] = " excluding ";
+ char *ret;
+ const size_t count = lttng_dynamic_pointer_array_get_count(exclusions);
+
+ if (count == 0) {
+ return strdup("");
+ }
+
+ /* Calculate total required length. */
+ for (i = 0; i < count; i++) {
+ const char *exclusion = lttng_dynamic_pointer_array_get_pointer(
+ exclusions, i);
+
+ length += strlen(exclusion) + 4;
+ }
+
+ length += sizeof(preamble);
+ ret = zmalloc(length);
+ if (!ret) {
+ return NULL;
+ }
+
+ strncpy(ret, preamble, length);
+ for (i = 0; i < count; i++) {
+ const char *exclusion = lttng_dynamic_pointer_array_get_pointer(
+ exclusions, i);
+
+ strcat(ret, "\"");
+ strcat(ret, exclusion);
+ 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;
+}
+
+int validate_exclusion_list(const char *event_name,
+ const struct lttng_dynamic_pointer_array *exclusions)
+{
+ int ret;
+
+ /* 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;
+ }
+
+ /*
+ * 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)) {
+ size_t i, num_exclusions;
+
+ num_exclusions = lttng_dynamic_pointer_array_get_count(exclusions);
+
+ for (i = 0; i < num_exclusions; i++) {
+ const char *exclusion =
+ lttng_dynamic_pointer_array_get_pointer(
+ exclusions, i);
+
+ 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;
+ }
+ }
+ }
+ }
+
+ ret = 0;
+ goto end;
+
+error: