xref: /linux/tools/tracing/rtla/src/actions.c (revision b8f7f49a23897fc772373da513a1cb6d51e5958e)
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