1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdlib.h>
3 #include <string.h>
4 #include <signal.h>
5 #include <unistd.h>
6
7 #include "actions.h"
8 #include "trace.h"
9 #include "utils.h"
10
11 /*
12 * actions_init - initialize struct actions
13 */
14 void
actions_init(struct actions * self)15 actions_init(struct actions *self)
16 {
17 self->size = action_default_size;
18 self->list = calloc_fatal(self->size, sizeof(struct action));
19 self->len = 0;
20 self->continue_flag = false;
21
22 /* This has to be set by the user */
23 self->trace_output_inst = NULL;
24 }
25
26 /*
27 * actions_destroy - destroy struct actions
28 */
29 void
actions_destroy(struct actions * self)30 actions_destroy(struct actions *self)
31 {
32 /* Free any action-specific data */
33 struct action *action;
34
35 for_each_action(self, action) {
36 if (action->type == ACTION_SHELL)
37 free(action->command);
38 if (action->type == ACTION_TRACE_OUTPUT)
39 free(action->trace_output);
40 }
41
42 /* Free action list */
43 free(self->list);
44 }
45
46 /*
47 * actions_new - Get pointer to new action
48 */
49 static struct action *
actions_new(struct actions * self)50 actions_new(struct actions *self)
51 {
52 if (self->len >= self->size) {
53 const size_t new_size = self->size * 2;
54
55 self->list = reallocarray_fatal(self->list, new_size, sizeof(struct action));
56 self->size = new_size;
57 }
58
59 return &self->list[self->len++];
60 }
61
62 /*
63 * actions_add_trace_output - add an action to output trace
64 */
65 void
actions_add_trace_output(struct actions * self,const char * trace_output)66 actions_add_trace_output(struct actions *self, const char *trace_output)
67 {
68 struct action *action = actions_new(self);
69
70 self->present[ACTION_TRACE_OUTPUT] = true;
71 action->type = ACTION_TRACE_OUTPUT;
72 action->trace_output = strdup_fatal(trace_output);
73 }
74
75 /*
76 * actions_add_trace_output - add an action to send signal to a process
77 */
78 void
actions_add_signal(struct actions * self,int signal,int pid)79 actions_add_signal(struct actions *self, int signal, int pid)
80 {
81 struct action *action = actions_new(self);
82
83 self->present[ACTION_SIGNAL] = true;
84 action->type = ACTION_SIGNAL;
85 action->signal = signal;
86 action->pid = pid;
87 }
88
89 /*
90 * actions_add_shell - add an action to execute a shell command
91 */
92 void
actions_add_shell(struct actions * self,const char * command)93 actions_add_shell(struct actions *self, const char *command)
94 {
95 struct action *action = actions_new(self);
96
97 self->present[ACTION_SHELL] = true;
98 action->type = ACTION_SHELL;
99 action->command = strdup_fatal(command);
100 }
101
102 /*
103 * actions_add_continue - add an action to resume measurement
104 */
105 void
actions_add_continue(struct actions * self)106 actions_add_continue(struct actions *self)
107 {
108 struct action *action = actions_new(self);
109
110 self->present[ACTION_CONTINUE] = true;
111 action->type = ACTION_CONTINUE;
112 }
113
__extract_arg(const char * token,const char * opt,size_t opt_len)114 static inline const char *__extract_arg(const char *token, const char *opt, size_t opt_len)
115 {
116 const size_t tok_len = strlen(token);
117
118 if (tok_len <= opt_len)
119 return NULL;
120
121 if (strncmp(token, opt, opt_len))
122 return NULL;
123
124 return token + opt_len;
125 }
126
127 /*
128 * extract_arg - extract argument value from option token
129 * @token: option token (e.g., "file=trace.txt")
130 * @opt: option name to match (e.g., "file")
131 *
132 * Returns pointer to argument value after "=" if token matches "opt=",
133 * otherwise returns NULL.
134 */
135 #define extract_arg(token, opt) __extract_arg(token, opt "=", STRING_LENGTH(opt "="))
136
137 /*
138 * actions_parse - add an action based on text specification
139 */
140 int
actions_parse(struct actions * self,const char * trigger,const char * tracefn)141 actions_parse(struct actions *self, const char *trigger, const char *tracefn)
142 {
143 enum action_type type = ACTION_NONE;
144 const char *token;
145 char trigger_c[strlen(trigger) + 1];
146 const char *arg_value;
147
148 /* For ACTION_SIGNAL */
149 int signal = 0, pid = 0;
150
151 /* For ACTION_TRACE_OUTPUT */
152 const char *trace_output;
153
154 strcpy(trigger_c, trigger);
155 token = strtok(trigger_c, ",");
156 if (!token)
157 return -1;
158
159 if (strcmp(token, "trace") == 0)
160 type = ACTION_TRACE_OUTPUT;
161 else if (strcmp(token, "signal") == 0)
162 type = ACTION_SIGNAL;
163 else if (strcmp(token, "shell") == 0)
164 type = ACTION_SHELL;
165 else if (strcmp(token, "continue") == 0)
166 type = ACTION_CONTINUE;
167 else
168 /* Invalid trigger type */
169 return -1;
170
171 token = strtok(NULL, ",");
172
173 switch (type) {
174 case ACTION_TRACE_OUTPUT:
175 /* Takes no argument */
176 if (token == NULL)
177 trace_output = tracefn;
178 else {
179 trace_output = extract_arg(token, "file");
180 if (!trace_output)
181 /* Invalid argument */
182 return -1;
183
184 token = strtok(NULL, ",");
185 if (token != NULL)
186 /* Only one argument allowed */
187 return -1;
188 }
189 actions_add_trace_output(self, trace_output);
190 break;
191 case ACTION_SIGNAL:
192 /* Takes two arguments, num (signal) and pid */
193 while (token != NULL) {
194 arg_value = extract_arg(token, "num");
195 if (arg_value) {
196 if (strtoi(arg_value, &signal))
197 return -1;
198 } else {
199 arg_value = extract_arg(token, "pid");
200 if (arg_value) {
201 if (strncmp_static(arg_value, "parent") == 0)
202 pid = -1;
203 else if (strtoi(arg_value, &pid))
204 return -1;
205 } else {
206 /* Invalid argument */
207 return -1;
208 }
209 }
210
211 token = strtok(NULL, ",");
212 }
213
214 if (!signal || !pid)
215 /* Missing argument */
216 return -1;
217
218 actions_add_signal(self, signal, pid);
219 break;
220 case ACTION_SHELL:
221 if (token == NULL)
222 return -1;
223 arg_value = extract_arg(token, "command");
224 if (!arg_value)
225 return -1;
226 actions_add_shell(self, arg_value);
227 break;
228 case ACTION_CONTINUE:
229 /* Takes no argument */
230 if (token != NULL)
231 return -1;
232 actions_add_continue(self);
233 break;
234 default:
235 return -1;
236 }
237
238 return 0;
239 }
240
241 /*
242 * actions_perform - perform all actions
243 */
244 int
actions_perform(struct actions * self)245 actions_perform(struct actions *self)
246 {
247 int pid, retval;
248 const struct action *action;
249
250 for_each_action(self, action) {
251 switch (action->type) {
252 case ACTION_TRACE_OUTPUT:
253 retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
254 if (retval) {
255 err_msg("Error saving trace\n");
256 return retval;
257 }
258 break;
259 case ACTION_SIGNAL:
260 if (action->pid == -1)
261 pid = getppid();
262 else
263 pid = action->pid;
264 retval = kill(pid, action->signal);
265 if (retval) {
266 err_msg("Error sending signal\n");
267 return retval;
268 }
269 break;
270 case ACTION_SHELL:
271 retval = system(action->command);
272 if (retval)
273 return retval;
274 break;
275 case ACTION_CONTINUE:
276 self->continue_flag = true;
277 return 0;
278 default:
279 break;
280 }
281 }
282
283 return 0;
284 }
285