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