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(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 self->size *= 2;
54 self->list = realloc(self->list, self->size * sizeof(struct action));
55 }
56
57 return &self->list[self->len++];
58 }
59
60 /*
61 * actions_add_trace_output - add an action to output trace
62 */
63 int
actions_add_trace_output(struct actions * self,const char * trace_output)64 actions_add_trace_output(struct actions *self, const char *trace_output)
65 {
66 struct action *action = actions_new(self);
67
68 self->present[ACTION_TRACE_OUTPUT] = true;
69 action->type = ACTION_TRACE_OUTPUT;
70 action->trace_output = calloc(strlen(trace_output) + 1, sizeof(char));
71 if (!action->trace_output)
72 return -1;
73 strcpy(action->trace_output, trace_output);
74
75 return 0;
76 }
77
78 /*
79 * actions_add_trace_output - add an action to send signal to a process
80 */
81 int
actions_add_signal(struct actions * self,int signal,int pid)82 actions_add_signal(struct actions *self, int signal, int pid)
83 {
84 struct action *action = actions_new(self);
85
86 self->present[ACTION_SIGNAL] = true;
87 action->type = ACTION_SIGNAL;
88 action->signal = signal;
89 action->pid = pid;
90
91 return 0;
92 }
93
94 /*
95 * actions_add_shell - add an action to execute a shell command
96 */
97 int
actions_add_shell(struct actions * self,const char * command)98 actions_add_shell(struct actions *self, const char *command)
99 {
100 struct action *action = actions_new(self);
101
102 self->present[ACTION_SHELL] = true;
103 action->type = ACTION_SHELL;
104 action->command = calloc(strlen(command) + 1, sizeof(char));
105 if (!action->command)
106 return -1;
107 strcpy(action->command, command);
108
109 return 0;
110 }
111
112 /*
113 * actions_add_continue - add an action to resume measurement
114 */
115 int
actions_add_continue(struct actions * self)116 actions_add_continue(struct actions *self)
117 {
118 struct action *action = actions_new(self);
119
120 self->present[ACTION_CONTINUE] = true;
121 action->type = ACTION_CONTINUE;
122
123 return 0;
124 }
125
126 /*
127 * actions_parse - add an action based on text specification
128 */
129 int
actions_parse(struct actions * self,const char * trigger,const char * tracefn)130 actions_parse(struct actions *self, const char *trigger, const char *tracefn)
131 {
132 enum action_type type = ACTION_NONE;
133 const char *token;
134 char trigger_c[strlen(trigger) + 1];
135
136 /* For ACTION_SIGNAL */
137 int signal = 0, pid = 0;
138
139 /* For ACTION_TRACE_OUTPUT */
140 const char *trace_output;
141
142 strcpy(trigger_c, trigger);
143 token = strtok(trigger_c, ",");
144 if (!token)
145 return -1;
146
147 if (strcmp(token, "trace") == 0)
148 type = ACTION_TRACE_OUTPUT;
149 else if (strcmp(token, "signal") == 0)
150 type = ACTION_SIGNAL;
151 else if (strcmp(token, "shell") == 0)
152 type = ACTION_SHELL;
153 else if (strcmp(token, "continue") == 0)
154 type = ACTION_CONTINUE;
155 else
156 /* Invalid trigger type */
157 return -1;
158
159 token = strtok(NULL, ",");
160
161 switch (type) {
162 case ACTION_TRACE_OUTPUT:
163 /* Takes no argument */
164 if (token == NULL)
165 trace_output = tracefn;
166 else {
167 if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) {
168 trace_output = token + 5;
169 } else {
170 /* Invalid argument */
171 return -1;
172 }
173
174 token = strtok(NULL, ",");
175 if (token != NULL)
176 /* Only one argument allowed */
177 return -1;
178 }
179 return actions_add_trace_output(self, trace_output);
180 case ACTION_SIGNAL:
181 /* Takes two arguments, num (signal) and pid */
182 while (token != NULL) {
183 if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) {
184 if (strtoi(token + 4, &signal))
185 return -1;
186 } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) {
187 if (strncmp(token + 4, "parent", 7) == 0)
188 pid = -1;
189 else if (strtoi(token + 4, &pid))
190 return -1;
191 } else {
192 /* Invalid argument */
193 return -1;
194 }
195
196 token = strtok(NULL, ",");
197 }
198
199 if (!signal || !pid)
200 /* Missing argument */
201 return -1;
202
203 return actions_add_signal(self, signal, pid);
204 case ACTION_SHELL:
205 if (token == NULL)
206 return -1;
207 if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0)
208 return actions_add_shell(self, token + 8);
209 return -1;
210 case ACTION_CONTINUE:
211 /* Takes no argument */
212 if (token != NULL)
213 return -1;
214 return actions_add_continue(self);
215 default:
216 return -1;
217 }
218 }
219
220 /*
221 * actions_perform - perform all actions
222 */
223 int
actions_perform(struct actions * self)224 actions_perform(struct actions *self)
225 {
226 int pid, retval;
227 const struct action *action;
228
229 for_each_action(self, action) {
230 switch (action->type) {
231 case ACTION_TRACE_OUTPUT:
232 retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
233 if (retval) {
234 err_msg("Error saving trace\n");
235 return retval;
236 }
237 break;
238 case ACTION_SIGNAL:
239 if (action->pid == -1)
240 pid = getppid();
241 else
242 pid = action->pid;
243 retval = kill(pid, action->signal);
244 if (retval) {
245 err_msg("Error sending signal\n");
246 return retval;
247 }
248 break;
249 case ACTION_SHELL:
250 retval = system(action->command);
251 if (retval)
252 return retval;
253 break;
254 case ACTION_CONTINUE:
255 self->continue_flag = true;
256 return 0;
257 default:
258 break;
259 }
260 }
261
262 return 0;
263 }
264