projects/linux·blob master · refs

286 lines · 5.9 KiB

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 */
14void
15actions_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 */
29void
30actions_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 */
49static struct action *
50actions_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 */
65void
66actions_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 */
78void
79actions_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 */
92void
93actions_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 */
105void
106actions_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
114static 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 */
140int
141actions_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 */
244int
245actions_perform(struct actions *self)
246{
247 int pid, retval;
248 const struct action *action;
249
250 self->continue_flag = false;
251
252 for_each_action(self, action) {
253 switch (action->type) {
254 case ACTION_TRACE_OUTPUT:
255 retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
256 if (retval) {
257 err_msg("Error saving trace\n");
258 return retval;
259 }
260 break;
261 case ACTION_SIGNAL:
262 if (action->pid == -1)
263 pid = getppid();
264 else
265 pid = action->pid;
266 retval = kill(pid, action->signal);
267 if (retval) {
268 err_msg("Error sending signal\n");
269 return retval;
270 }
271 break;
272 case ACTION_SHELL:
273 retval = system(action->command);
274 if (retval)
275 return retval;
276 break;
277 case ACTION_CONTINUE:
278 self->continue_flag = true;
279 return 0;
280 default:
281 break;
282 }
283 }
284
285 return 0;
286}