xref: /linux/kernel/trace/trace_pid.c (revision 23b0f90ba871f096474e1c27c3d14f455189d2d9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "trace.h"
4 
5 /**
6  * trace_find_filtered_pid - check if a pid exists in a filtered_pid list
7  * @filtered_pids: The list of pids to check
8  * @search_pid: The PID to find in @filtered_pids
9  *
10  * Returns true if @search_pid is found in @filtered_pids, and false otherwise.
11  */
12 bool
13 trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid)
14 {
15 	return trace_pid_list_is_set(filtered_pids, search_pid);
16 }
17 
18 /**
19  * trace_ignore_this_task - should a task be ignored for tracing
20  * @filtered_pids: The list of pids to check
21  * @filtered_no_pids: The list of pids not to be traced
22  * @task: The task that should be ignored if not filtered
23  *
24  * Checks if @task should be traced or not from @filtered_pids.
25  * Returns true if @task should *NOT* be traced.
26  * Returns false if @task should be traced.
27  */
28 bool
29 trace_ignore_this_task(struct trace_pid_list *filtered_pids,
30 		       struct trace_pid_list *filtered_no_pids,
31 		       struct task_struct *task)
32 {
33 	/*
34 	 * If filtered_no_pids is not empty, and the task's pid is listed
35 	 * in filtered_no_pids, then return true.
36 	 * Otherwise, if filtered_pids is empty, that means we can
37 	 * trace all tasks. If it has content, then only trace pids
38 	 * within filtered_pids.
39 	 */
40 
41 	return (filtered_pids &&
42 		!trace_find_filtered_pid(filtered_pids, task->pid)) ||
43 		(filtered_no_pids &&
44 		 trace_find_filtered_pid(filtered_no_pids, task->pid));
45 }
46 
47 /**
48  * trace_filter_add_remove_task - Add or remove a task from a pid_list
49  * @pid_list: The list to modify
50  * @self: The current task for fork or NULL for exit
51  * @task: The task to add or remove
52  *
53  * If adding a task, if @self is defined, the task is only added if @self
54  * is also included in @pid_list. This happens on fork and tasks should
55  * only be added when the parent is listed. If @self is NULL, then the
56  * @task pid will be removed from the list, which would happen on exit
57  * of a task.
58  */
59 void trace_filter_add_remove_task(struct trace_pid_list *pid_list,
60 				  struct task_struct *self,
61 				  struct task_struct *task)
62 {
63 	if (!pid_list)
64 		return;
65 
66 	/* For forks, we only add if the forking task is listed */
67 	if (self) {
68 		if (!trace_find_filtered_pid(pid_list, self->pid))
69 			return;
70 	}
71 
72 	/* "self" is set for forks, and NULL for exits */
73 	if (self)
74 		trace_pid_list_set(pid_list, task->pid);
75 	else
76 		trace_pid_list_clear(pid_list, task->pid);
77 }
78 
79 /**
80  * trace_pid_next - Used for seq_file to get to the next pid of a pid_list
81  * @pid_list: The pid list to show
82  * @v: The last pid that was shown (+1 the actual pid to let zero be displayed)
83  * @pos: The position of the file
84  *
85  * This is used by the seq_file "next" operation to iterate the pids
86  * listed in a trace_pid_list structure.
87  *
88  * Returns the pid+1 as we want to display pid of zero, but NULL would
89  * stop the iteration.
90  */
91 void *trace_pid_next(struct trace_pid_list *pid_list, void *v, loff_t *pos)
92 {
93 	long pid = (unsigned long)v;
94 	unsigned int next;
95 
96 	(*pos)++;
97 
98 	/* pid already is +1 of the actual previous bit */
99 	if (trace_pid_list_next(pid_list, pid, &next) < 0)
100 		return NULL;
101 
102 	pid = next;
103 
104 	/* Return pid + 1 to allow zero to be represented */
105 	return (void *)(pid + 1);
106 }
107 
108 /**
109  * trace_pid_start - Used for seq_file to start reading pid lists
110  * @pid_list: The pid list to show
111  * @pos: The position of the file
112  *
113  * This is used by seq_file "start" operation to start the iteration
114  * of listing pids.
115  *
116  * Returns the pid+1 as we want to display pid of zero, but NULL would
117  * stop the iteration.
118  */
119 void *trace_pid_start(struct trace_pid_list *pid_list, loff_t *pos)
120 {
121 	unsigned long pid;
122 	unsigned int first;
123 	loff_t l = 0;
124 
125 	if (trace_pid_list_first(pid_list, &first) < 0)
126 		return NULL;
127 
128 	pid = first;
129 
130 	/* Return pid + 1 so that zero can be the exit value */
131 	for (pid++; pid && l < *pos;
132 	     pid = (unsigned long)trace_pid_next(pid_list, (void *)pid, &l))
133 		;
134 	return (void *)pid;
135 }
136 
137 /**
138  * trace_pid_show - show the current pid in seq_file processing
139  * @m: The seq_file structure to write into
140  * @v: A void pointer of the pid (+1) value to display
141  *
142  * Can be directly used by seq_file operations to display the current
143  * pid value.
144  */
145 int trace_pid_show(struct seq_file *m, void *v)
146 {
147 	unsigned long pid = (unsigned long)v - 1;
148 
149 	seq_printf(m, "%lu\n", pid);
150 	return 0;
151 }
152 
153 /* 128 should be much more than enough */
154 #define PID_BUF_SIZE		127
155 
156 int trace_pid_write(struct trace_pid_list *filtered_pids,
157 		    struct trace_pid_list **new_pid_list,
158 		    const char __user *ubuf, size_t cnt)
159 {
160 	struct trace_pid_list *pid_list;
161 	struct trace_parser parser;
162 	unsigned long val;
163 	int nr_pids = 0;
164 	ssize_t read = 0;
165 	ssize_t ret;
166 	loff_t pos;
167 	pid_t pid;
168 
169 	if (trace_parser_get_init(&parser, PID_BUF_SIZE + 1))
170 		return -ENOMEM;
171 
172 	/*
173 	 * Always recreate a new array. The write is an all or nothing
174 	 * operation. Always create a new array when adding new pids by
175 	 * the user. If the operation fails, then the current list is
176 	 * not modified.
177 	 */
178 	pid_list = trace_pid_list_alloc();
179 	if (!pid_list) {
180 		trace_parser_put(&parser);
181 		return -ENOMEM;
182 	}
183 
184 	if (filtered_pids) {
185 		/* copy the current bits to the new max */
186 		ret = trace_pid_list_first(filtered_pids, &pid);
187 		while (!ret) {
188 			ret = trace_pid_list_set(pid_list, pid);
189 			if (ret < 0)
190 				goto out;
191 
192 			ret = trace_pid_list_next(filtered_pids, pid + 1, &pid);
193 			nr_pids++;
194 		}
195 	}
196 
197 	ret = 0;
198 	while (cnt > 0) {
199 
200 		pos = 0;
201 
202 		ret = trace_get_user(&parser, ubuf, cnt, &pos);
203 		if (ret < 0)
204 			break;
205 
206 		read += ret;
207 		ubuf += ret;
208 		cnt -= ret;
209 
210 		if (!trace_parser_loaded(&parser))
211 			break;
212 
213 		ret = -EINVAL;
214 		if (kstrtoul(parser.buffer, 0, &val))
215 			break;
216 
217 		pid = (pid_t)val;
218 
219 		if (trace_pid_list_set(pid_list, pid) < 0) {
220 			ret = -1;
221 			break;
222 		}
223 		nr_pids++;
224 
225 		trace_parser_clear(&parser);
226 		ret = 0;
227 	}
228  out:
229 	trace_parser_put(&parser);
230 
231 	if (ret < 0) {
232 		trace_pid_list_free(pid_list);
233 		return ret;
234 	}
235 
236 	if (!nr_pids) {
237 		/* Cleared the list of pids */
238 		trace_pid_list_free(pid_list);
239 		pid_list = NULL;
240 	}
241 
242 	*new_pid_list = pid_list;
243 
244 	return read;
245 }
246 
247