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 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 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 * 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 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 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 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 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 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 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 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