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 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 32 actions_destroy(struct actions *self) 33 { 34 /* Free any action-specific data */ 35 struct action *action; 36 37 for_each_action(self, action) { 38 if (action->type == ACTION_SHELL) 39 free(action->command); 40 if (action->type == ACTION_TRACE_OUTPUT) 41 free(action->trace_output); 42 } 43 44 /* Free action list */ 45 free(self->list); 46 } 47 48 /* 49 * actions_new - Get pointer to new action 50 */ 51 static struct action * 52 actions_new(struct actions *self) 53 { 54 if (self->len >= self->size) { 55 self->size *= 2; 56 self->list = realloc(self->list, self->size * sizeof(struct action)); 57 } 58 59 return &self->list[self->len++]; 60 } 61 62 /* 63 * actions_add_trace_output - add an action to output trace 64 */ 65 int 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 = calloc(strlen(trace_output) + 1, sizeof(char)); 73 if (!action->trace_output) 74 return -1; 75 strcpy(action->trace_output, trace_output); 76 77 return 0; 78 } 79 80 /* 81 * actions_add_trace_output - add an action to send signal to a process 82 */ 83 int 84 actions_add_signal(struct actions *self, int signal, int pid) 85 { 86 struct action *action = actions_new(self); 87 88 self->present[ACTION_SIGNAL] = true; 89 action->type = ACTION_SIGNAL; 90 action->signal = signal; 91 action->pid = pid; 92 93 return 0; 94 } 95 96 /* 97 * actions_add_shell - add an action to execute a shell command 98 */ 99 int 100 actions_add_shell(struct actions *self, const char *command) 101 { 102 struct action *action = actions_new(self); 103 104 self->present[ACTION_SHELL] = true; 105 action->type = ACTION_SHELL; 106 action->command = calloc(strlen(command) + 1, sizeof(char)); 107 if (!action->command) 108 return -1; 109 strcpy(action->command, command); 110 111 return 0; 112 } 113 114 /* 115 * actions_add_continue - add an action to resume measurement 116 */ 117 int 118 actions_add_continue(struct actions *self) 119 { 120 struct action *action = actions_new(self); 121 122 self->present[ACTION_CONTINUE] = true; 123 action->type = ACTION_CONTINUE; 124 125 return 0; 126 } 127 128 /* 129 * actions_parse - add an action based on text specification 130 */ 131 int 132 actions_parse(struct actions *self, const char *trigger, const char *tracefn) 133 { 134 enum action_type type = ACTION_NONE; 135 const char *token; 136 char trigger_c[strlen(trigger) + 1]; 137 138 /* For ACTION_SIGNAL */ 139 int signal = 0, pid = 0; 140 141 /* For ACTION_TRACE_OUTPUT */ 142 const char *trace_output; 143 144 strcpy(trigger_c, trigger); 145 token = strtok(trigger_c, ","); 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 signal = atoi(token + 4); 185 } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { 186 if (strncmp(token + 4, "parent", 7) == 0) 187 pid = -1; 188 else 189 pid = atoi(token + 4); 190 } else { 191 /* Invalid argument */ 192 return -1; 193 } 194 195 token = strtok(NULL, ","); 196 } 197 198 if (!signal || !pid) 199 /* Missing argument */ 200 return -1; 201 202 return actions_add_signal(self, signal, pid); 203 case ACTION_SHELL: 204 if (token == NULL) 205 return -1; 206 if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0) 207 return actions_add_shell(self, token + 8); 208 return -1; 209 case ACTION_CONTINUE: 210 /* Takes no argument */ 211 if (token != NULL) 212 return -1; 213 return actions_add_continue(self); 214 default: 215 return -1; 216 } 217 } 218 219 /* 220 * actions_perform - perform all actions 221 */ 222 int 223 actions_perform(struct actions *self) 224 { 225 int pid, retval; 226 const struct action *action; 227 228 for_each_action(self, action) { 229 switch (action->type) { 230 case ACTION_TRACE_OUTPUT: 231 retval = save_trace_to_file(self->trace_output_inst, action->trace_output); 232 if (retval) { 233 err_msg("Error saving trace\n"); 234 return retval; 235 } 236 break; 237 case ACTION_SIGNAL: 238 if (action->pid == -1) 239 pid = getppid(); 240 else 241 pid = action->pid; 242 retval = kill(pid, action->signal); 243 if (retval) { 244 err_msg("Error sending signal\n"); 245 return retval; 246 } 247 break; 248 case ACTION_SHELL: 249 retval = system(action->command); 250 if (retval) 251 return retval; 252 break; 253 case ACTION_CONTINUE: 254 self->continue_flag = true; 255 return 0; 256 default: 257 break; 258 } 259 } 260 261 return 0; 262 } 263