LCOV - code coverage report
Current view: top level - crtools - pstree.c (source / functions) Hit Total Coverage
Test: crtools.info Lines: 178 202 88.1 %
Date: 2012-12-28 Functions: 9 9 100.0 %
Branches: 119 180 66.1 %

           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                 :            : }

Generated by: LCOV version 1.9