xref: /linux/tools/tracing/rtla/src/actions.c (revision 9e1e9d660255d7216067193d774f338d08d8528d) !
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_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
actions_destroy(struct actions * self)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 *
actions_new(struct actions * self)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
actions_add_trace_output(struct actions * self,const char * trace_output)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
actions_add_signal(struct actions * self,int signal,int pid)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
actions_add_shell(struct actions * self,const char * command)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
actions_add_continue(struct actions * self)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 
__extract_arg(const char * token,const char * opt,size_t opt_len)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
actions_parse(struct actions * self,const char * trigger,const char * tracefn)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
actions_perform(struct actions * self)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