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 /* 115 * actions_parse - add an action based on text specification 116 */ 117 int 118 actions_parse(struct actions *self, const char *trigger, const char *tracefn) 119 { 120 enum action_type type = ACTION_NONE; 121 const char *token; 122 char trigger_c[strlen(trigger) + 1]; 123 124 /* For ACTION_SIGNAL */ 125 int signal = 0, pid = 0; 126 127 /* For ACTION_TRACE_OUTPUT */ 128 const char *trace_output; 129 130 strcpy(trigger_c, trigger); 131 token = strtok(trigger_c, ","); 132 if (!token) 133 return -1; 134 135 if (strcmp(token, "trace") == 0) 136 type = ACTION_TRACE_OUTPUT; 137 else if (strcmp(token, "signal") == 0) 138 type = ACTION_SIGNAL; 139 else if (strcmp(token, "shell") == 0) 140 type = ACTION_SHELL; 141 else if (strcmp(token, "continue") == 0) 142 type = ACTION_CONTINUE; 143 else 144 /* Invalid trigger type */ 145 return -1; 146 147 token = strtok(NULL, ","); 148 149 switch (type) { 150 case ACTION_TRACE_OUTPUT: 151 /* Takes no argument */ 152 if (token == NULL) 153 trace_output = tracefn; 154 else { 155 if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) { 156 trace_output = token + 5; 157 } else { 158 /* Invalid argument */ 159 return -1; 160 } 161 162 token = strtok(NULL, ","); 163 if (token != NULL) 164 /* Only one argument allowed */ 165 return -1; 166 } 167 actions_add_trace_output(self, trace_output); 168 break; 169 case ACTION_SIGNAL: 170 /* Takes two arguments, num (signal) and pid */ 171 while (token != NULL) { 172 if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) { 173 if (strtoi(token + 4, &signal)) 174 return -1; 175 } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { 176 if (strncmp(token + 4, "parent", 7) == 0) 177 pid = -1; 178 else if (strtoi(token + 4, &pid)) 179 return -1; 180 } else { 181 /* Invalid argument */ 182 return -1; 183 } 184 185 token = strtok(NULL, ","); 186 } 187 188 if (!signal || !pid) 189 /* Missing argument */ 190 return -1; 191 192 actions_add_signal(self, signal, pid); 193 break; 194 case ACTION_SHELL: 195 if (token == NULL) 196 return -1; 197 if (strlen(token) > 8 && strncmp(token, "command=", 8)) 198 return -1; 199 actions_add_shell(self, token + 8); 200 break; 201 case ACTION_CONTINUE: 202 /* Takes no argument */ 203 if (token != NULL) 204 return -1; 205 actions_add_continue(self); 206 break; 207 default: 208 return -1; 209 } 210 211 return 0; 212 } 213 214 /* 215 * actions_perform - perform all actions 216 */ 217 int 218 actions_perform(struct actions *self) 219 { 220 int pid, retval; 221 const struct action *action; 222 223 for_each_action(self, action) { 224 switch (action->type) { 225 case ACTION_TRACE_OUTPUT: 226 retval = save_trace_to_file(self->trace_output_inst, action->trace_output); 227 if (retval) { 228 err_msg("Error saving trace\n"); 229 return retval; 230 } 231 break; 232 case ACTION_SIGNAL: 233 if (action->pid == -1) 234 pid = getppid(); 235 else 236 pid = action->pid; 237 retval = kill(pid, action->signal); 238 if (retval) { 239 err_msg("Error sending signal\n"); 240 return retval; 241 } 242 break; 243 case ACTION_SHELL: 244 retval = system(action->command); 245 if (retval) 246 return retval; 247 break; 248 case ACTION_CONTINUE: 249 self->continue_flag = true; 250 return 0; 251 default: 252 break; 253 } 254 } 255 256 return 0; 257 } 258