xref: /linux/tools/tracing/rtla/src/actions.c (revision 5172a777248e56dbae22b55231d24c7ecc6393f9)
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(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
actions_destroy(struct actions * self)32 actions_destroy(struct actions *self)
33 {
34 	/* Free any action-specific data */
35 	for (struct action *action = self->list; action < self->list + self->len; 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->size >= self->len) {
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
actions_add_trace_output(struct actions * self,const char * trace_output)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
actions_add_signal(struct actions * self,int signal,int pid)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
actions_add_shell(struct actions * self,const char * command)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
actions_add_continue(struct actions * self)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
actions_parse(struct actions * self,const char * trigger)130 actions_parse(struct actions *self, const char *trigger)
131 {
132 	enum action_type type = ACTION_NONE;
133 	char *token;
134 	char trigger_c[strlen(trigger)];
135 
136 	/* For ACTION_SIGNAL */
137 	int signal = 0, pid = 0;
138 
139 	/* For ACTION_TRACE_OUTPUT */
140 	char *trace_output;
141 
142 	strcpy(trigger_c, trigger);
143 	token = strtok(trigger_c, ",");
144 
145 	if (strcmp(token, "trace") == 0)
146 		type = ACTION_TRACE_OUTPUT;
147 	else if (strcmp(token, "signal") == 0)
148 		type = ACTION_SIGNAL;
149 	else if (strcmp(token, "shell") == 0)
150 		type = ACTION_SHELL;
151 	else if (strcmp(token, "continue") == 0)
152 		type = ACTION_CONTINUE;
153 	else
154 		/* Invalid trigger type */
155 		return -1;
156 
157 	token = strtok(NULL, ",");
158 
159 	switch (type) {
160 	case ACTION_TRACE_OUTPUT:
161 		/* Takes no argument */
162 		if (token == NULL)
163 			trace_output = "timerlat_trace.txt";
164 		else {
165 			if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) {
166 				trace_output = token + 5;
167 			} else {
168 				/* Invalid argument */
169 				return -1;
170 			}
171 
172 			token = strtok(NULL, ",");
173 			if (token != NULL)
174 				/* Only one argument allowed */
175 				return -1;
176 		}
177 		return actions_add_trace_output(self, trace_output);
178 	case ACTION_SIGNAL:
179 		/* Takes two arguments, num (signal) and pid */
180 		while (token != NULL) {
181 			if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) {
182 				signal = atoi(token + 4);
183 			} else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) {
184 				if (strncmp(token + 4, "parent", 7) == 0)
185 					pid = -1;
186 				else
187 					pid = atoi(token + 4);
188 			} else {
189 				/* Invalid argument */
190 				return -1;
191 			}
192 
193 			token = strtok(NULL, ",");
194 		}
195 
196 		if (!signal || !pid)
197 			/* Missing argument */
198 			return -1;
199 
200 		return actions_add_signal(self, signal, pid);
201 	case ACTION_SHELL:
202 		if (token == NULL)
203 			return -1;
204 		if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0)
205 			return actions_add_shell(self, token + 8);
206 		return -1;
207 	case ACTION_CONTINUE:
208 		/* Takes no argument */
209 		if (token != NULL)
210 			return -1;
211 		return actions_add_continue(self);
212 	default:
213 		return -1;
214 	}
215 }
216 
217 /*
218  * actions_perform - perform all actions
219  */
220 int
actions_perform(struct actions * self)221 actions_perform(struct actions *self)
222 {
223 	int pid, retval;
224 	const struct action *action;
225 
226 	for (action = self->list; action < self->list + self->len; action++) {
227 		switch (action->type) {
228 		case ACTION_TRACE_OUTPUT:
229 			retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
230 			if (retval) {
231 				err_msg("Error saving trace\n");
232 				return retval;
233 			}
234 			break;
235 		case ACTION_SIGNAL:
236 			if (action->pid == -1)
237 				pid = getppid();
238 			else
239 				pid = action->pid;
240 			retval = kill(pid, action->signal);
241 			if (retval) {
242 				err_msg("Error sending signal\n");
243 				return retval;
244 			}
245 			break;
246 		case ACTION_SHELL:
247 			retval = system(action->command);
248 			if (retval)
249 				return retval;
250 			break;
251 		case ACTION_CONTINUE:
252 			self->continue_flag = true;
253 			return 0;
254 		default:
255 			break;
256 		}
257 	}
258 
259 	return 0;
260 }
261