f733821588a63995846cb709e7e903ab2ac8cf25
[lttng-tools.git] / tests / regression / tools / trigger / utils / notification-client.cpp
1 /*
2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 */
7
8 #include "utils.h"
9
10 #include <lttng/action/list-internal.hpp>
11 #include <lttng/condition/event-rule-matches.h>
12 #include <lttng/lttng.h>
13
14 #include <getopt.h>
15 #include <stdbool.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/time.h>
21 #include <time.h>
22
23 static struct option long_options[] = {
24 /* These options set a flag. */
25 { "trigger", required_argument, 0, 't' },
26 { "sync-after-notif-register", required_argument, 0, 'a' },
27 /* Default alue for count is 1 */
28 { "count", required_argument, 0, 'b' },
29 /*
30 * When end-trigger is present the reception loop is exited only when a
31 * notification matching the end trigger is received.
32 * Otherwise the loop is exited when the count of notification received
33 * for `trigger` math the `count` argument.
34 */
35 { "end-trigger", required_argument, 0, 'c' },
36 { 0, 0, 0, 0 }
37 };
38
39 static bool action_list_contains_notify(const struct lttng_action *action_list)
40 {
41 for (auto sub_action : lttng::ctl::const_action_list_view(action_list)) {
42 if (lttng_action_get_type(sub_action) == LTTNG_ACTION_TYPE_NOTIFY) {
43 return true;
44 }
45 }
46
47 return false;
48 }
49
50 /* Only expects named triggers. */
51 static bool is_trigger_name(const char *expected_trigger_name,
52 struct lttng_notification *notification)
53 {
54 const char *trigger_name = NULL;
55 enum lttng_trigger_status trigger_status;
56 const struct lttng_trigger *trigger;
57 bool names_match;
58
59 trigger = lttng_notification_get_trigger(notification);
60 if (!trigger) {
61 fprintf(stderr, "Failed to get trigger from notification\n");
62 names_match = false;
63 goto end;
64 }
65
66 trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
67 if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
68 fprintf(stderr, "Failed to get name from notification's trigger\n");
69 names_match = false;
70 goto end;
71 }
72
73 names_match = strcmp(expected_trigger_name, trigger_name) == 0;
74 if (!names_match) {
75 fprintf(stderr,
76 "Got an unexpected trigger name: name = '%s', expected name = '%s'\n",
77 trigger_name,
78 expected_trigger_name);
79 }
80 end:
81 return names_match;
82 }
83
84 int main(int argc, char **argv)
85 {
86 int ret;
87 int option;
88 int option_index;
89 char *expected_trigger_name = NULL;
90 char *end_trigger_name = NULL;
91 struct lttng_triggers *triggers = NULL;
92 unsigned int count, i, subcription_count = 0;
93 enum lttng_trigger_status trigger_status;
94 char *after_notif_register_file_path = NULL;
95 struct lttng_notification_channel *notification_channel = NULL;
96 int expected_notifications = 1, notification_count = 0;
97
98 while ((option = getopt_long(argc, argv, "a:b:c:t:", long_options, &option_index)) != -1) {
99 switch (option) {
100 case 'a':
101 after_notif_register_file_path = strdup(optarg);
102 break;
103 case 'b':
104 expected_notifications = atoi(optarg);
105 break;
106 case 'c':
107 end_trigger_name = strdup(optarg);
108 break;
109 case 't':
110 expected_trigger_name = strdup(optarg);
111 break;
112 case '?':
113 /* getopt_long already printed an error message. */
114 default:
115 ret = -1;
116 goto end;
117 }
118 }
119
120 if (optind != argc) {
121 ret = -1;
122 goto end;
123 }
124
125 notification_channel =
126 lttng_notification_channel_create(lttng_session_daemon_notification_endpoint);
127 if (!notification_channel) {
128 fprintf(stderr, "Failed to create notification channel\n");
129 ret = -1;
130 goto end;
131 }
132
133 ret = lttng_list_triggers(&triggers);
134 if (ret != LTTNG_OK) {
135 fprintf(stderr, "Failed to list triggers\n");
136 ret = -1;
137 goto end;
138 }
139
140 trigger_status = lttng_triggers_get_count(triggers, &count);
141 if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
142 fprintf(stderr, "Failed to get trigger count\n");
143 ret = -1;
144 goto end;
145 }
146
147 /* Look for the trigger we want to subscribe to. */
148 for (i = 0; i < count; i++) {
149 const struct lttng_trigger *trigger = lttng_triggers_get_at_index(triggers, i);
150 const struct lttng_condition *condition =
151 lttng_trigger_get_const_condition(trigger);
152 const struct lttng_action *action = lttng_trigger_get_const_action(trigger);
153 const enum lttng_action_type action_type = lttng_action_get_type(action);
154 enum lttng_notification_channel_status channel_status;
155 const char *trigger_name = NULL;
156
157 lttng_trigger_get_name(trigger, &trigger_name);
158 if (strcmp(trigger_name, expected_trigger_name) != 0) {
159 /* Might match the end event trigger */
160 if (end_trigger_name != NULL &&
161 strcmp(trigger_name, end_trigger_name) != 0) {
162 continue;
163 }
164 }
165 if (!((action_type == LTTNG_ACTION_TYPE_LIST &&
166 action_list_contains_notify(action)) ||
167 action_type == LTTNG_ACTION_TYPE_NOTIFY)) {
168 /* "The action of trigger is not notify, skipping. */
169 continue;
170 }
171
172 channel_status =
173 lttng_notification_channel_subscribe(notification_channel, condition);
174 if (channel_status) {
175 fprintf(stderr,
176 "Failed to subscribe to notifications of trigger \"%s\"\n",
177 trigger_name);
178 ret = -1;
179 goto end;
180 }
181
182 subcription_count++;
183 }
184
185 if (subcription_count == 0) {
186 fprintf(stderr, "No matching trigger with a notify action found.\n");
187 ret = -1;
188 goto end;
189 }
190
191 if (end_trigger_name != NULL && subcription_count != 2) {
192 fprintf(stderr, "No matching end event trigger with a notify action found.\n");
193 ret = -1;
194 goto end;
195 }
196
197 /*
198 * We registered to the notification of our target trigger. We can now
199 * create the sync file to signify that we are ready.
200 */
201 ret = create_file(after_notif_register_file_path);
202 if (ret != 0) {
203 goto end;
204 }
205
206 for (;;) {
207 struct lttng_notification *notification;
208 enum lttng_notification_channel_status channel_status;
209
210 channel_status = lttng_notification_channel_get_next_notification(
211 notification_channel, &notification);
212 switch (channel_status) {
213 case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED:
214 printf("Dropped notification\n");
215 ret = -1;
216 goto end;
217 case LTTNG_NOTIFICATION_CHANNEL_STATUS_INTERRUPTED:
218 ret = -1;
219 goto end;
220 case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK:
221 break;
222 case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED:
223 printf("Notification channel was closed by peer.\n");
224 break;
225 default:
226 fprintf(stderr,
227 "A communication error occurred on the notification channel.\n");
228 ret = -1;
229 goto end;
230 }
231
232 /* Early exit check. */
233 if (end_trigger_name != NULL && is_trigger_name(end_trigger_name, notification)) {
234 /* Exit the loop immediately. */
235 printf("Received end event notification from trigger %s\n",
236 end_trigger_name);
237 lttng_notification_destroy(notification);
238 goto evaluate_success;
239 }
240
241 ret = is_trigger_name(expected_trigger_name, notification);
242 lttng_notification_destroy(notification);
243 if (!ret) {
244 ret = -1;
245 goto end;
246 }
247
248 printf("Received event notification from trigger %s\n", expected_trigger_name);
249 notification_count++;
250 if (end_trigger_name == NULL && expected_notifications == notification_count) {
251 /*
252 * Here the loop exit is controlled by the number of
253 * notification and not by the reception of the end
254 * event trigger notification. This represent the
255 * default behavior.
256 *
257 */
258 goto evaluate_success;
259 }
260 }
261
262 evaluate_success:
263 if (expected_notifications == notification_count) {
264 /* Success */
265 ret = 0;
266 } else {
267 fprintf(stderr,
268 "Expected %d notification got %d\n",
269 expected_notifications,
270 notification_count);
271 ret = 1;
272 }
273
274 end:
275 lttng_triggers_destroy(triggers);
276 lttng_notification_channel_destroy(notification_channel);
277 free(after_notif_register_file_path);
278 free(end_trigger_name);
279 free(expected_trigger_name);
280 return !!ret;
281 }
This page took 0.034011 seconds and 4 git commands to generate.