Branch data Line data Source code
1 : : #include <sys/mman.h>
2 : : #include <unistd.h>
3 : : #include <stdlib.h>
4 : :
5 : : #include "pstree.h"
6 : : #include "restorer.h"
7 : : #include "util.h"
8 : :
9 : : #include "protobuf.h"
10 : : #include "protobuf/pstree.pb-c.h"
11 : :
12 : : struct pstree_item *root_item;
13 : :
14 : 166 : void free_pstree(struct pstree_item *root_item)
15 : : {
16 : 166 : struct pstree_item *item = root_item, *parent;
17 : :
18 [ + + ]: 718 : while (item) {
19 [ + + ]: 552 : if (!list_empty(&item->children)) {
20 : 193 : item = list_first_entry(&item->children, struct pstree_item, sibling);
21 : 193 : continue;
22 : : }
23 : :
24 : 359 : parent = item->parent;
25 : 359 : list_del(&item->sibling);
26 [ + - ]: 359 : xfree(item->threads);
27 [ + - ]: 359 : xfree(item);
28 : 552 : item = parent;
29 : : }
30 : 166 : }
31 : :
32 : 1377 : struct pstree_item *__alloc_pstree_item(bool rst)
33 : : {
34 : : struct pstree_item *item;
35 : :
36 [ + + ][ - + ]: 1377 : item = xzalloc(sizeof(*item) + (rst ? sizeof(item->rst[0]) : 0));
[ # # ]
37 [ + - ]: 1377 : if (!item)
38 : : return NULL;
39 : :
40 : 1377 : INIT_LIST_HEAD(&item->children);
41 : 1377 : INIT_LIST_HEAD(&item->sibling);
42 : :
43 : 1377 : item->pid.virt = -1;
44 : 1377 : item->pid.real = -1;
45 : 1377 : item->born_sid = -1;
46 : :
47 : 1377 : return item;
48 : : }
49 : :
50 : : /* Deep first search on children */
51 : 2610 : struct pstree_item *pstree_item_next(struct pstree_item *item)
52 : : {
53 [ + + ][ + + ]: 5209 : if (!list_empty(&item->children))
[ + + ][ # # ]
[ # # ][ + + ]
[ + + ]
54 : 1908 : return list_first_entry(&item->children, struct pstree_item, sibling);
55 : :
56 [ + + ][ + + ]: 6840 : while (item->parent) {
[ + + ][ # # ]
[ # # ][ + + ]
[ + + ]
57 [ + + ][ + + ]: 3302 : if (item->sibling.next != &item->parent->children)
[ + + ][ # # ]
[ # # ][ + + ]
[ + + ]
58 : 4137 : return list_entry(item->sibling.next, struct pstree_item, sibling);
59 : : item = item->parent;
60 : : }
61 : :
62 : : return NULL;
63 : : }
64 : :
65 : 166 : int dump_pstree(struct pstree_item *root_item)
66 : : {
67 : 166 : struct pstree_item *item = root_item;
68 : 166 : PstreeEntry e = PSTREE_ENTRY__INIT;
69 : 166 : int ret = -1, i;
70 : : int pstree_fd;
71 : :
72 : 166 : pr_info("\n");
73 : 166 : pr_info("Dumping pstree (pid: %d)\n", root_item->pid.real);
74 : 166 : pr_info("----------------------------------------\n");
75 : :
76 : : /*
77 : : * Make sure we're dumping session leader, if not an
78 : : * appropriate option must be passed.
79 : : *
80 : : * Also note that if we're not a session leader we
81 : : * can't get the situation where the leader sits somewhere
82 : : * deeper in process tree, thus top-level checking for
83 : : * leader is enough.
84 : : */
85 [ - + ]: 166 : if (root_item->pid.virt != root_item->sid) {
86 [ # # ]: 0 : if (!opts.shell_job) {
87 : 0 : pr_err("The root process %d is not a session leader,"
88 : : "miss option?\n", item->pid.virt);
89 : : return -1;
90 : : }
91 : : }
92 : :
93 : 166 : pstree_fd = open_image(CR_FD_PSTREE, O_DUMP);
94 [ + - ]: 166 : if (pstree_fd < 0)
95 : : return -1;
96 : :
97 [ + + ]: 525 : for_each_pstree_item(item) {
98 : 359 : pr_info("Process: %d(%d)\n", item->pid.virt, item->pid.real);
99 : :
100 : 359 : e.pid = item->pid.virt;
101 [ + + ]: 359 : e.ppid = item->parent ? item->parent->pid.virt : 0;
102 : 359 : e.pgid = item->pgid;
103 : 359 : e.sid = item->sid;
104 : 359 : e.n_threads = item->nr_threads;
105 : :
106 [ - + ]: 359 : e.threads = xmalloc(sizeof(e.threads[0]) * e.n_threads);
107 [ + - ]: 359 : if (!e.threads)
108 : : goto err;
109 : :
110 [ + + ]: 750 : for (i = 0; i < item->nr_threads; i++)
111 : 391 : e.threads[i] = item->threads[i].virt;
112 : :
113 : 359 : ret = pb_write_one(pstree_fd, &e, PB_PSTREE);
114 [ + - ]: 359 : xfree(e.threads);
115 : :
116 [ + - ]: 359 : if (ret)
117 : : goto err;
118 : : }
119 : : ret = 0;
120 : :
121 : : err:
122 : 166 : pr_info("----------------------------------------\n");
123 : 166 : close(pstree_fd);
124 : : return ret;
125 : : }
126 : :
127 : : static int max_pid = 0;
128 : :
129 : 380 : static int prepare_pstree_for_shell_job(void)
130 : : {
131 : 380 : pid_t current_sid = getsid(getpid());
132 : 380 : pid_t current_gid = getpgid(getpid());
133 : :
134 : : struct pstree_item *pi;
135 : :
136 : : pid_t old_sid;
137 : : pid_t old_gid;
138 : :
139 [ - + ]: 380 : if (!opts.shell_job)
140 : : return 0;
141 : :
142 : : /*
143 : : * Migration of a root task group leader is a bit tricky.
144 : : * When a task yields SIGSTOP, the kernel notifies the parent
145 : : * with SIGCHLD. This means when task is running in a
146 : : * shell, the shell obtains SIGCHLD and sends a task to
147 : : * the background.
148 : : *
149 : : * The situation gets changed once we restore the
150 : : * program -- our tool become an additional stub between
151 : : * the restored program and the shell. So to be able to
152 : : * notify the shell with SIGCHLD from our restored
153 : : * program -- we make the root task to inherit the
154 : : * process group from us.
155 : : *
156 : : * Not that clever solution but at least it works.
157 : : */
158 : :
159 : 0 : old_sid = root_item->sid;
160 : 0 : old_gid = root_item->pgid;
161 : :
162 : 0 : pr_info("Migrating process tree (GID %d->%d SID %d->%d)\n",
163 : : old_gid, current_gid, old_sid, current_sid);
164 : :
165 [ # # ]: 0 : for_each_pstree_item(pi) {
166 [ # # ]: 0 : if (pi->pgid == old_gid)
167 : 0 : pi->pgid = current_gid;
168 [ # # ]: 0 : if (pi->sid == old_sid)
169 : 0 : pi->sid = current_sid;
170 : : }
171 : :
172 : 0 : max_pid = max((int)current_sid, max_pid);
173 : 0 : max_pid = max((int)current_gid, max_pid);
174 : :
175 : 380 : return 0;
176 : : }
177 : :
178 : 380 : static int read_pstree_image(void)
179 : : {
180 : 380 : int ret = 0, i, ps_fd;
181 : 380 : struct pstree_item *pi, *parent = NULL;
182 : :
183 : 380 : pr_info("Reading image tree\n");
184 : :
185 : 380 : ps_fd = open_image_ro(CR_FD_PSTREE);
186 [ + - ]: 380 : if (ps_fd < 0)
187 : : return ps_fd;
188 : :
189 : : while (1) {
190 : : PstreeEntry *e;
191 : :
192 : 1388 : ret = pb_read_one_eof(ps_fd, &e, PB_PSTREE);
193 [ + + ]: 1388 : if (ret <= 0)
194 : : break;
195 : :
196 : 1008 : ret = -1;
197 : 1008 : pi = alloc_pstree_item_with_rst();
198 [ + - ]: 1008 : if (pi == NULL)
199 : : break;
200 : :
201 : 1008 : pi->pid.virt = e->pid;
202 : 1008 : max_pid = max((int)e->pid, max_pid);
203 : :
204 : 1008 : pi->pgid = e->pgid;
205 : 1008 : max_pid = max((int)e->pgid, max_pid);
206 : :
207 : 1008 : pi->sid = e->sid;
208 : 1008 : max_pid = max((int)e->sid, max_pid);
209 : :
210 [ + + ]: 1008 : if (e->ppid == 0) {
211 [ - + ]: 380 : if (root_item) {
212 : 0 : pr_err("Parent missed on non-root task "
213 : : "with pid %d, image corruption!\n", e->pid);
214 : : goto err;
215 : : }
216 : 380 : root_item = pi;
217 : 380 : pi->parent = NULL;
218 : : } else {
219 : : /*
220 : : * Fast path -- if the pstree image is not edited, the
221 : : * parent of any item should have already being restored
222 : : * and sit among the last item's ancestors.
223 : : */
224 [ + - ]: 947 : while (parent) {
225 [ + + ]: 947 : if (parent->pid.virt == e->ppid)
226 : : break;
227 : 319 : parent = parent->parent;
228 : : }
229 : :
230 [ - + ]: 628 : if (parent == NULL) {
231 [ # # ]: 0 : for_each_pstree_item(parent) {
232 [ # # ]: 0 : if (parent->pid.virt == e->ppid)
233 : : break;
234 : : }
235 : :
236 [ # # ]: 0 : if (parent == NULL) {
237 : 0 : pr_err("Can't find a parent for %d\n", pi->pid.virt);
238 : 0 : pstree_entry__free_unpacked(e, NULL);
239 [ # # ]: 0 : xfree(pi);
240 : : goto err;
241 : : }
242 : : }
243 : :
244 : 628 : pi->parent = parent;
245 : 628 : list_add(&pi->sibling, &parent->children);
246 : : }
247 : :
248 : 1008 : parent = pi;
249 : :
250 : 1008 : pi->nr_threads = e->n_threads;
251 [ - + ]: 1008 : pi->threads = xmalloc(e->n_threads * sizeof(struct pid));
252 [ + - ]: 1008 : if (!pi->threads)
253 : : break;
254 : :
255 : : ret = 0;
256 [ + + ]: 2089 : for (i = 0; i < e->n_threads; i++)
257 : 1081 : pi->threads[i].virt = e->threads[i];
258 : :
259 : 1008 : task_entries->nr_threads += e->n_threads;
260 : 1008 : task_entries->nr_tasks++;
261 : :
262 : 1008 : pstree_entry__free_unpacked(e, NULL);
263 : 1388 : }
264 : : err:
265 : 380 : close(ps_fd);
266 : 380 : return ret;
267 : : }
268 : :
269 : 380 : static int prepare_pstree_ids(void)
270 : : {
271 : : struct pstree_item *item, *child, *helper, *tmp;
272 : 380 : LIST_HEAD(helpers);
273 : :
274 : 380 : pid_t current_pgid = getpgid(getpid());
275 : :
276 : : /*
277 : : * Some task can be reparented to init. A helper task should be added
278 : : * for restoring sid of such tasks. The helper tasks will be exited
279 : : * immediately after forking children and all children will be
280 : : * reparented to init.
281 : : */
282 [ + + ]: 877 : list_for_each_entry(item, &root_item->children, sibling) {
283 : :
284 : : /*
285 : : * If a child belongs to the root task's session or it's
286 : : * a session leader himself -- this is a simple case, we
287 : : * just proceed in a normal way.
288 : : */
289 [ + + ][ + + ]: 497 : if (item->sid == root_item->sid || item->sid == item->pid.virt)
290 : 489 : continue;
291 : :
292 : 8 : helper = alloc_pstree_item();
293 [ + - ]: 8 : if (helper == NULL)
294 : : return -1;
295 : 8 : helper->sid = item->sid;
296 : 8 : helper->pgid = item->sid;
297 : 8 : helper->pid.virt = item->sid;
298 : 8 : helper->state = TASK_HELPER;
299 : 8 : helper->parent = root_item;
300 : 8 : list_add_tail(&helper->sibling, &helpers);
301 : 8 : task_entries->nr_helpers++;
302 : :
303 : 8 : pr_info("Add a helper %d for restoring SID %d\n",
304 : : helper->pid.virt, helper->sid);
305 : :
306 : 8 : child = list_entry(item->sibling.prev, struct pstree_item, sibling);
307 : 8 : item = child;
308 : :
309 : : /*
310 : : * Stack on helper task all children with target sid.
311 : : */
312 [ + + ]: 20 : list_for_each_entry_safe_continue(child, tmp, &root_item->children, sibling) {
313 [ + + ]: 12 : if (child->sid != helper->sid)
314 : 4 : continue;
315 [ - + ]: 8 : if (child->sid == child->pid.virt)
316 : 0 : continue;
317 : :
318 : 8 : pr_info("Attach %d to the temporary task %d\n",
319 : : child->pid.virt, helper->pid.virt);
320 : :
321 : 8 : child->parent = helper;
322 : 8 : list_move(&child->sibling, &helper->children);
323 : : }
324 : : }
325 : :
326 : : /* Try to connect helpers to session leaders */
327 [ + + ]: 1388 : for_each_pstree_item(item) {
328 [ + + ]: 1008 : if (!item->parent) /* skip the root task */
329 : 380 : continue;
330 : :
331 [ + + ]: 628 : if (item->state == TASK_HELPER)
332 : 4 : continue;
333 : :
334 [ + + ]: 624 : if (item->sid != item->pid.virt) {
335 : : struct pstree_item *parent;
336 : :
337 [ + + ]: 442 : if (item->parent->sid == item->sid)
338 : 434 : continue;
339 : :
340 : : /* the task could fork a child before and after setsid() */
341 : : parent = item->parent;
342 [ + - ][ + + ]: 20 : while (parent && parent->pid.virt != item->sid) {
343 [ - + ][ # # ]: 12 : if (parent->born_sid != -1 && parent->born_sid != item->sid) {
344 : 0 : pr_err("Can't determinate with which sid (%d or %d)"
345 : : "the process %d was born\n",
346 : : parent->born_sid, item->sid, parent->pid.virt);
347 : : return -1;
348 : : }
349 : 12 : parent->born_sid = item->sid;
350 : 12 : pr_info("%d was born with sid %d\n", parent->pid.virt, item->sid);
351 : 12 : parent = parent->parent;
352 : : }
353 : :
354 [ - + ]: 8 : if (parent == NULL) {
355 : 0 : pr_err("Can't find a session leader for %d\n", item->sid);
356 : : return -1;
357 : : }
358 : :
359 : 8 : continue;
360 : : }
361 : :
362 : 182 : pr_info("Session leader %d\n", item->sid);
363 : :
364 : : /* Try to find helpers, who should be connected to the leader */
365 [ + + ]: 200 : list_for_each_entry(child, &helpers, sibling) {
366 [ - + ]: 22 : if (child->state != TASK_HELPER)
367 : 0 : continue;
368 : :
369 [ + + ]: 22 : if (child->sid != item->sid)
370 : 18 : continue;
371 : :
372 : 4 : child->pgid = item->pgid;
373 : 4 : child->pid.virt = ++max_pid;
374 : 4 : child->parent = item;
375 : 4 : list_move(&child->sibling, &item->children);
376 : :
377 : 4 : pr_info("Attach %d to the task %d\n",
378 : : child->pid.virt, item->pid.virt);
379 : :
380 : 4 : break;
381 : : }
382 : : }
383 : :
384 : : /* All other helpers are session leaders for own sessions */
385 : 380 : list_splice(&helpers, &root_item->children);
386 : :
387 : : /* Add a process group leader if it is absent */
388 [ + + ]: 1398 : for_each_pstree_item(item) {
389 : : struct pstree_item *gleader;
390 : :
391 [ + - ][ + + ]: 1018 : if (!item->pgid || item->pid.virt == item->pgid)
392 : 577 : continue;
393 : :
394 [ + + ]: 655 : for_each_pstree_item(gleader) {
395 [ + + ]: 653 : if (gleader->pid.virt == item->pgid)
396 : : break;
397 : : }
398 : :
399 [ + + ]: 441 : if (gleader)
400 : 439 : continue;
401 : :
402 : : /*
403 : : * If the PGID is eq to current one -- this
404 : : * means we're inheriting group from the current
405 : : * task so we need to escape creating a helper here.
406 : : */
407 [ - + ]: 2 : if (current_pgid == item->pgid)
408 : 0 : continue;
409 : :
410 : 2 : helper = alloc_pstree_item();
411 [ + - ]: 2 : if (helper == NULL)
412 : : return -1;
413 : 2 : helper->sid = item->sid;
414 : 2 : helper->pgid = item->pgid;
415 : 2 : helper->pid.virt = item->pgid;
416 : 2 : helper->state = TASK_HELPER;
417 : 2 : helper->parent = item;
418 : 2 : list_add(&helper->sibling, &item->children);
419 : 2 : task_entries->nr_helpers++;
420 : :
421 : 2 : pr_info("Add a helper %d for restoring PGID %d\n",
422 : : helper->pid.virt, helper->pgid);
423 : : }
424 : :
425 : : return 0;
426 : : }
427 : :
428 : 380 : int prepare_pstree(void)
429 : : {
430 : : int ret;
431 : :
432 : 380 : ret = read_pstree_image();
433 [ + - ]: 380 : if (!ret)
434 : : /*
435 : : * Shell job may inherit sid/pgid from the current
436 : : * shell, not from image. Set things up for this.
437 : : */
438 : 380 : ret = prepare_pstree_for_shell_job();
439 [ + - ]: 380 : if (!ret)
440 : : /*
441 : : * Session/Group leaders might be dead. Need to fix
442 : : * pstree with properly injected helper tasks.
443 : : */
444 : 380 : ret = prepare_pstree_ids();
445 : :
446 : 380 : return ret;
447 : : }
448 : :
449 : 1660 : bool restore_before_setsid(struct pstree_item *child)
450 : : {
451 [ + + ]: 1660 : int csid = child->born_sid == -1 ? child->sid : child->born_sid;
452 : :
453 [ + + ]: 1660 : if (child->parent->born_sid == csid)
454 : : return true;
455 : :
456 : 1660 : return false;
457 : : }
|