+ /*
+ * Expand references to `/./` and `/../`. This function does not check
+ * if the file exists. This call returns an allocated buffer on
+ * success.
+ */
+ real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
+ if (!real_target_path) {
+ ERR("Error expanding the path to binary.");
+ ret = CMD_ERROR;
+ goto end_destroy_lookup_method;
+ }
+
+ /*
+ * Check if the file exists using access(2), If it does not,
+ * return an error.
+ */
+ ret = access(real_target_path, F_OK);
+ if (ret) {
+ ERR("Cannot find binary at path: %s.", real_target_path);
+ ret = CMD_ERROR;
+ goto end_destroy_lookup_method;
+ }
+ }
+
+ switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+ /*
+ * Check for common mistakes in userspace probe description syntax.
+ */
+ ret = warn_userspace_probe_syntax(symbol_name);
+ if (ret) {
+ goto end_destroy_lookup_method;
+ }
+
+ probe_location = lttng_userspace_probe_location_function_create(
+ real_target_path, symbol_name, lookup_method);
+ if (!probe_location) {
+ WARN("Failed to create function probe location");
+ ret = CMD_ERROR;
+ goto end_destroy_lookup_method;
+ }
+
+ /* Ownership transferred to probe_location. */
+ lookup_method = NULL;
+
+ ret = lttng_event_set_userspace_probe_location(ev, probe_location);
+ if (ret) {
+ WARN("Failed to set probe location on event");
+ ret = CMD_ERROR;
+ goto end_destroy_location;
+ }
+ break;
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+ probe_location = lttng_userspace_probe_location_tracepoint_create(
+ real_target_path, provider_name, probe_name, lookup_method);
+ if (!probe_location) {
+ WARN("Failed to create function probe location");
+ ret = CMD_ERROR;
+ goto end_destroy_lookup_method;
+ }
+
+ /* Ownership transferred to probe_location. */
+ lookup_method = NULL;
+
+ ret = lttng_event_set_userspace_probe_location(ev, probe_location);
+ if (ret) {
+ WARN("Failed to set probe location on event");
+ ret = CMD_ERROR;
+ goto end_destroy_location;
+ }
+ break;
+ default:
+ ret = CMD_ERROR;
+ goto end_destroy_lookup_method;
+ }
+
+ /* Successful parsing, now clean up everything and return. */
+ goto end_string;
+
+end_destroy_location:
+ lttng_userspace_probe_location_destroy(probe_location);
+end_destroy_lookup_method:
+ lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
+end_string:
+ strutils_free_null_terminated_array_of_strings(tokens);
+ /*
+ * Freeing both char * here makes the error handling simplier. free()
+ * performs not action if the pointer is NULL.
+ */
+ free(real_target_path);
+ free(unescaped_target_path);
+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(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;
+ }
+
+ length += sizeof(preamble);
+ ret = zmalloc(length);
+ 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:
+ ret = -1;
+ strutils_free_null_terminated_array_of_strings(exclusions);
+
+end:
+ return ret;
+}
+
+static void warn_on_truncated_exclusion_names(char * const *exclusion_list,
+ int *warn)
+{
+ char * const *exclusion;
+
+ for (exclusion = exclusion_list; *exclusion; exclusion++) {
+ if (strlen(*exclusion) >= LTTNG_SYMBOL_NAME_LEN) {
+ WARN("Event exclusion \"%s\" will be truncated",
+ *exclusion);
+ *warn = 1;
+ }