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