-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- *
- */
-
-#include "utils.h"
-
-#include <lttng/condition/event-rule-matches.h>
-#include <lttng/lttng.h>
-
-#include <getopt.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <time.h>
-
-static struct option long_options[] = {
- /* These options set a flag. */
- { "trigger", required_argument, 0, 't' },
- { "sync-after-notif-register", required_argument, 0, 'a' },
- /* Default alue for count is 1 */
- { "count", required_argument, 0, 'b' },
- /*
- * When end-trigger is present the reception loop is exited only when a
- * notification matching the end trigger is received.
- * Otherwise the loop is exited when the count of notification received
- * for `trigger` math the `count` argument.
- */
- { "end-trigger", required_argument, 0, 'c' },
- { 0, 0, 0, 0 }
-};
-
-static bool action_list_contains_notify(const struct lttng_action *action_list)
-{
- unsigned int i, count;
- enum lttng_action_status status = lttng_action_list_get_count(action_list, &count);
-
- if (status != LTTNG_ACTION_STATUS_OK) {
- printf("Failed to get action count from action list\n");
- exit(1);
- }
-
- for (i = 0; i < count; i++) {
- const struct lttng_action *action = lttng_action_list_get_at_index(action_list, i);
- const enum lttng_action_type action_type = lttng_action_get_type(action);
-
- if (action_type == LTTNG_ACTION_TYPE_NOTIFY) {
- return true;
- }
- }
- return false;
-}
-
-/* Only expects named triggers. */
-static bool is_trigger_name(const char *expected_trigger_name,
- struct lttng_notification *notification)
-{
- const char *trigger_name = NULL;
- enum lttng_trigger_status trigger_status;
- const struct lttng_trigger *trigger;
- bool names_match;
-
- trigger = lttng_notification_get_trigger(notification);
- if (!trigger) {
- fprintf(stderr, "Failed to get trigger from notification\n");
- names_match = false;
- goto end;
- }
-
- trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
- if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
- fprintf(stderr, "Failed to get name from notification's trigger\n");
- names_match = false;
- goto end;
- }
-
- names_match = strcmp(expected_trigger_name, trigger_name) == 0;
- if (!names_match) {
- fprintf(stderr,
- "Got an unexpected trigger name: name = '%s', expected name = '%s'\n",
- trigger_name,
- expected_trigger_name);
- }
-end:
- return names_match;
-}
-
-int main(int argc, char **argv)
-{
- int ret;
- int option;
- int option_index;
- char *expected_trigger_name = NULL;
- char *end_trigger_name = NULL;
- struct lttng_triggers *triggers = NULL;
- unsigned int count, i, subcription_count = 0;
- enum lttng_trigger_status trigger_status;
- char *after_notif_register_file_path = NULL;
- struct lttng_notification_channel *notification_channel = NULL;
- int expected_notifications = 1, notification_count = 0;
-
- while ((option = getopt_long(argc, argv, "a:b:c:t:", long_options, &option_index)) != -1) {
- switch (option) {
- case 'a':
- after_notif_register_file_path = strdup(optarg);
- break;
- case 'b':
- expected_notifications = atoi(optarg);
- break;
- case 'c':
- end_trigger_name = strdup(optarg);
- break;
- case 't':
- expected_trigger_name = strdup(optarg);
- break;
- case '?':
- /* getopt_long already printed an error message. */
- default:
- ret = -1;
- goto end;
- }
- }
-
- if (optind != argc) {
- ret = -1;
- goto end;
- }
-
- notification_channel =
- lttng_notification_channel_create(lttng_session_daemon_notification_endpoint);
- if (!notification_channel) {
- fprintf(stderr, "Failed to create notification channel\n");
- ret = -1;
- goto end;
- }
-
- ret = lttng_list_triggers(&triggers);
- if (ret != LTTNG_OK) {
- fprintf(stderr, "Failed to list triggers\n");
- ret = -1;
- goto end;
- }
-
- trigger_status = lttng_triggers_get_count(triggers, &count);
- if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
- fprintf(stderr, "Failed to get trigger count\n");
- ret = -1;
- goto end;
- }
-
- /* Look for the trigger we want to subscribe to. */
- for (i = 0; i < count; i++) {
- const struct lttng_trigger *trigger = lttng_triggers_get_at_index(triggers, i);
- const struct lttng_condition *condition =
- lttng_trigger_get_const_condition(trigger);
- const struct lttng_action *action = lttng_trigger_get_const_action(trigger);
- const enum lttng_action_type action_type = lttng_action_get_type(action);
- enum lttng_notification_channel_status channel_status;
- const char *trigger_name = NULL;
-
- lttng_trigger_get_name(trigger, &trigger_name);
- if (strcmp(trigger_name, expected_trigger_name) != 0) {
- /* Might match the end event trigger */
- if (end_trigger_name != NULL &&
- strcmp(trigger_name, end_trigger_name) != 0) {
- continue;
- }
- }
- if (!((action_type == LTTNG_ACTION_TYPE_LIST &&
- action_list_contains_notify(action)) ||
- action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
- /* "The action of trigger is not notify, skipping. */
- continue;
- }
-
- channel_status =
- lttng_notification_channel_subscribe(notification_channel, condition);
- if (channel_status) {
- fprintf(stderr,
- "Failed to subscribe to notifications of trigger \"%s\"\n",
- trigger_name);
- ret = -1;
- goto end;
- }
-
- subcription_count++;
- }
-
- if (subcription_count == 0) {
- fprintf(stderr, "No matching trigger with a notify action found.\n");
- ret = -1;
- goto end;
- }
-
- if (end_trigger_name != NULL && subcription_count != 2) {
- fprintf(stderr, "No matching end event trigger with a notify action found.\n");
- ret = -1;
- goto end;
- }
-
- /*
- * We registered to the notification of our target trigger. We can now
- * create the sync file to signify that we are ready.
- */
- ret = create_file(after_notif_register_file_path);
- if (ret != 0) {
- goto end;
- }
-
- for (;;) {
- struct lttng_notification *notification;
- enum lttng_notification_channel_status channel_status;
-
- channel_status = lttng_notification_channel_get_next_notification(
- notification_channel, ¬ification);
- switch (channel_status) {
- case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
- printf("Dropped notification\n");
- ret = -1;
- goto end;
- case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED:
- ret = -1;
- goto end;
- case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
- break;
- case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED:
- printf("Notification channel was closed by peer.\n");
- break;
- default:
- fprintf(stderr,
- "A communication error occurred on the notification channel.\n");
- ret = -1;
- goto end;
- }
-
- /* Early exit check. */
- if (end_trigger_name != NULL && is_trigger_name(end_trigger_name, notification)) {
- /* Exit the loop immediately. */
- printf("Received end event notification from trigger %s\n",
- end_trigger_name);
- lttng_notification_destroy(notification);
- goto evaluate_success;
- }
-
- ret = is_trigger_name(expected_trigger_name, notification);
- lttng_notification_destroy(notification);
- if (!ret) {
- ret = -1;
- goto end;
- }
-
- printf("Received event notification from trigger %s\n", expected_trigger_name);
- notification_count++;
- if (end_trigger_name == NULL && expected_notifications == notification_count) {
- /*
- * Here the loop exit is controlled by the number of
- * notification and not by the reception of the end
- * event trigger notification. This represent the
- * default behavior.
- *
- */
- goto evaluate_success;
- }
- }
-
-evaluate_success:
- if (expected_notifications == notification_count) {
- /* Success */
- ret = 0;
- } else {
- fprintf(stderr,
- "Expected %d notification got %d\n",
- expected_notifications,
- notification_count);
- ret = 1;
- }
-
-end:
- lttng_triggers_destroy(triggers);
- lttng_notification_channel_destroy(notification_channel);
- free(after_notif_register_file_path);
- free(end_trigger_name);
- free(expected_trigger_name);
- return !!ret;
-}