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 memset(&self->present, 0, sizeof(self->present));
23
24 /* This has to be set by the user */
25 self->trace_output_inst = NULL;
26 }
27
28 /*
29 * actions_destroy - destroy struct actions
30 */
31 void
actions_destroy(struct actions * self)32 actions_destroy(struct actions *self)
33 {
34 /* Free any action-specific data */
35 for (struct action *action = self->list; action < self->list + self->len; 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->size >= self->len) {
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)130 actions_parse(struct actions *self, const char *trigger)
131 {
132 enum action_type type = ACTION_NONE;
133 char *token;
134 char trigger_c[strlen(trigger)];
135
136 /* For ACTION_SIGNAL */
137 int signal = 0, pid = 0;
138
139 /* For ACTION_TRACE_OUTPUT */
140 char *trace_output;
141
142 strcpy(trigger_c, trigger);
143 token = strtok(trigger_c, ",");
144
145 if (strcmp(token, "trace") == 0)
146 type = ACTION_TRACE_OUTPUT;
147 else if (strcmp(token, "signal") == 0)
148 type = ACTION_SIGNAL;
149 else if (strcmp(token, "shell") == 0)
150 type = ACTION_SHELL;
151 else if (strcmp(token, "continue") == 0)
152 type = ACTION_CONTINUE;
153 else
154 /* Invalid trigger type */
155 return -1;
156
157 token = strtok(NULL, ",");
158
159 switch (type) {
160 case ACTION_TRACE_OUTPUT:
161 /* Takes no argument */
162 if (token == NULL)
163 trace_output = "timerlat_trace.txt";
164 else {
165 if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) {
166 trace_output = token + 5;
167 } else {
168 /* Invalid argument */
169 return -1;
170 }
171
172 token = strtok(NULL, ",");
173 if (token != NULL)
174 /* Only one argument allowed */
175 return -1;
176 }
177 return actions_add_trace_output(self, trace_output);
178 case ACTION_SIGNAL:
179 /* Takes two arguments, num (signal) and pid */
180 while (token != NULL) {
181 if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) {
182 signal = atoi(token + 4);
183 } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) {
184 if (strncmp(token + 4, "parent", 7) == 0)
185 pid = -1;
186 else
187 pid = atoi(token + 4);
188 } else {
189 /* Invalid argument */
190 return -1;
191 }
192
193 token = strtok(NULL, ",");
194 }
195
196 if (!signal || !pid)
197 /* Missing argument */
198 return -1;
199
200 return actions_add_signal(self, signal, pid);
201 case ACTION_SHELL:
202 if (token == NULL)
203 return -1;
204 if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0)
205 return actions_add_shell(self, token + 8);
206 return -1;
207 case ACTION_CONTINUE:
208 /* Takes no argument */
209 if (token != NULL)
210 return -1;
211 return actions_add_continue(self);
212 default:
213 return -1;
214 }
215 }
216
217 /*
218 * actions_perform - perform all actions
219 */
220 int
actions_perform(struct actions * self)221 actions_perform(struct actions *self)
222 {
223 int pid, retval;
224 const struct action *action;
225
226 for (action = self->list; action < self->list + self->len; action++) {
227 switch (action->type) {
228 case ACTION_TRACE_OUTPUT:
229 retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
230 if (retval) {
231 err_msg("Error saving trace\n");
232 return retval;
233 }
234 break;
235 case ACTION_SIGNAL:
236 if (action->pid == -1)
237 pid = getppid();
238 else
239 pid = action->pid;
240 retval = kill(pid, action->signal);
241 if (retval) {
242 err_msg("Error sending signal\n");
243 return retval;
244 }
245 break;
246 case ACTION_SHELL:
247 retval = system(action->command);
248 if (retval)
249 return retval;
250 break;
251 case ACTION_CONTINUE:
252 self->continue_flag = true;
253 return 0;
254 default:
255 break;
256 }
257 }
258
259 return 0;
260 }
261