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