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