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