LCOV - code coverage report
Current view: top level - shared - bus-unit-procs.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 201 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "bus-unit-procs.h"
       4             : #include "hashmap.h"
       5             : #include "list.h"
       6             : #include "locale-util.h"
       7             : #include "macro.h"
       8             : #include "path-util.h"
       9             : #include "process-util.h"
      10             : #include "sort-util.h"
      11             : #include "string-util.h"
      12             : #include "terminal-util.h"
      13             : 
      14             : struct CGroupInfo {
      15             :         char *cgroup_path;
      16             :         bool is_const; /* If false, cgroup_path should be free()'d */
      17             : 
      18             :         Hashmap *pids; /* PID → process name */
      19             :         bool done;
      20             : 
      21             :         struct CGroupInfo *parent;
      22             :         LIST_FIELDS(struct CGroupInfo, siblings);
      23             :         LIST_HEAD(struct CGroupInfo, children);
      24             :         size_t n_children;
      25             : };
      26             : 
      27           0 : static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
      28           0 :         struct CGroupInfo *parent = NULL, *cg;
      29             :         int r;
      30             : 
      31           0 :         assert(cgroups);
      32           0 :         assert(ret);
      33             : 
      34           0 :         path = empty_to_root(path);
      35             : 
      36           0 :         cg = hashmap_get(cgroups, path);
      37           0 :         if (cg) {
      38           0 :                 *ret = cg;
      39           0 :                 return 0;
      40             :         }
      41             : 
      42           0 :         if (!empty_or_root(path)) {
      43             :                 const char *e, *pp;
      44             : 
      45           0 :                 e = strrchr(path, '/');
      46           0 :                 if (!e)
      47           0 :                         return -EINVAL;
      48             : 
      49           0 :                 pp = strndupa(path, e - path);
      50             : 
      51           0 :                 r = add_cgroup(cgroups, pp, false, &parent);
      52           0 :                 if (r < 0)
      53           0 :                         return r;
      54             :         }
      55             : 
      56           0 :         cg = new0(struct CGroupInfo, 1);
      57           0 :         if (!cg)
      58           0 :                 return -ENOMEM;
      59             : 
      60           0 :         if (is_const)
      61           0 :                 cg->cgroup_path = (char*) path;
      62             :         else {
      63           0 :                 cg->cgroup_path = strdup(path);
      64           0 :                 if (!cg->cgroup_path) {
      65           0 :                         free(cg);
      66           0 :                         return -ENOMEM;
      67             :                 }
      68             :         }
      69             : 
      70           0 :         cg->is_const = is_const;
      71           0 :         cg->parent = parent;
      72             : 
      73           0 :         r = hashmap_put(cgroups, cg->cgroup_path, cg);
      74           0 :         if (r < 0) {
      75           0 :                 if (!is_const)
      76           0 :                         free(cg->cgroup_path);
      77           0 :                 free(cg);
      78           0 :                 return r;
      79             :         }
      80             : 
      81           0 :         if (parent) {
      82           0 :                 LIST_PREPEND(siblings, parent->children, cg);
      83           0 :                 parent->n_children++;
      84             :         }
      85             : 
      86           0 :         *ret = cg;
      87           0 :         return 1;
      88             : }
      89             : 
      90           0 : static int add_process(
      91             :                 Hashmap *cgroups,
      92             :                 const char *path,
      93             :                 pid_t pid,
      94             :                 const char *name) {
      95             : 
      96             :         struct CGroupInfo *cg;
      97             :         int r;
      98             : 
      99           0 :         assert(cgroups);
     100           0 :         assert(name);
     101           0 :         assert(pid > 0);
     102             : 
     103           0 :         r = add_cgroup(cgroups, path, true, &cg);
     104           0 :         if (r < 0)
     105           0 :                 return r;
     106             : 
     107           0 :         r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
     108           0 :         if (r < 0)
     109           0 :                 return r;
     110             : 
     111           0 :         return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
     112             : }
     113             : 
     114           0 : static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
     115           0 :         assert(cgroups);
     116           0 :         assert(cg);
     117             : 
     118           0 :         while (cg->children)
     119           0 :                 remove_cgroup(cgroups, cg->children);
     120             : 
     121           0 :         hashmap_remove(cgroups, cg->cgroup_path);
     122             : 
     123           0 :         if (!cg->is_const)
     124           0 :                 free(cg->cgroup_path);
     125             : 
     126           0 :         hashmap_free(cg->pids);
     127             : 
     128           0 :         if (cg->parent)
     129           0 :                 LIST_REMOVE(siblings, cg->parent->children, cg);
     130             : 
     131           0 :         free(cg);
     132           0 : }
     133             : 
     134           0 : static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) {
     135           0 :         return strcmp((*a)->cgroup_path, (*b)->cgroup_path);
     136             : }
     137             : 
     138           0 : static int dump_processes(
     139             :                 Hashmap *cgroups,
     140             :                 const char *cgroup_path,
     141             :                 const char *prefix,
     142             :                 unsigned n_columns,
     143             :                 OutputFlags flags) {
     144             : 
     145             :         struct CGroupInfo *cg;
     146             :         int r;
     147             : 
     148           0 :         assert(prefix);
     149             : 
     150           0 :         cgroup_path = empty_to_root(cgroup_path);
     151             : 
     152           0 :         cg = hashmap_get(cgroups, cgroup_path);
     153           0 :         if (!cg)
     154           0 :                 return 0;
     155             : 
     156           0 :         if (!hashmap_isempty(cg->pids)) {
     157             :                 const char *name;
     158           0 :                 size_t n = 0, i;
     159             :                 pid_t *pids;
     160             :                 void *pidp;
     161             :                 Iterator j;
     162             :                 int width;
     163             : 
     164             :                 /* Order processes by their PID */
     165           0 :                 pids = newa(pid_t, hashmap_size(cg->pids));
     166             : 
     167           0 :                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
     168           0 :                         pids[n++] = PTR_TO_PID(pidp);
     169             : 
     170           0 :                 assert(n == hashmap_size(cg->pids));
     171           0 :                 typesafe_qsort(pids, n, pid_compare_func);
     172             : 
     173           0 :                 width = DECIMAL_STR_WIDTH(pids[n-1]);
     174             : 
     175           0 :                 for (i = 0; i < n; i++) {
     176           0 :                         _cleanup_free_ char *e = NULL;
     177             :                         const char *special;
     178             :                         bool more;
     179             : 
     180           0 :                         name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
     181           0 :                         assert(name);
     182             : 
     183           0 :                         if (n_columns != 0) {
     184             :                                 unsigned k;
     185             : 
     186           0 :                                 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
     187             : 
     188           0 :                                 e = ellipsize(name, k, 100);
     189           0 :                                 if (e)
     190           0 :                                         name = e;
     191             :                         }
     192             : 
     193           0 :                         more = i+1 < n || cg->children;
     194           0 :                         special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
     195             : 
     196           0 :                         fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
     197             :                                 prefix,
     198             :                                 special,
     199           0 :                                 width, pids[i],
     200             :                                 name);
     201             :                 }
     202             :         }
     203             : 
     204           0 :         if (cg->children) {
     205             :                 struct CGroupInfo **children, *child;
     206           0 :                 size_t n = 0, i;
     207             : 
     208             :                 /* Order subcgroups by their name */
     209           0 :                 children = newa(struct CGroupInfo*, cg->n_children);
     210           0 :                 LIST_FOREACH(siblings, child, cg->children)
     211           0 :                         children[n++] = child;
     212           0 :                 assert(n == cg->n_children);
     213           0 :                 typesafe_qsort(children, n, cgroup_info_compare_func);
     214             : 
     215           0 :                 if (n_columns != 0)
     216           0 :                         n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
     217             : 
     218           0 :                 for (i = 0; i < n; i++) {
     219           0 :                         _cleanup_free_ char *pp = NULL;
     220             :                         const char *name, *special;
     221             :                         bool more;
     222             : 
     223           0 :                         child = children[i];
     224             : 
     225           0 :                         name = strrchr(child->cgroup_path, '/');
     226           0 :                         if (!name)
     227           0 :                                 return -EINVAL;
     228           0 :                         name++;
     229             : 
     230           0 :                         more = i+1 < n;
     231           0 :                         special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT);
     232             : 
     233           0 :                         fputs(prefix, stdout);
     234           0 :                         fputs(special, stdout);
     235           0 :                         fputs(name, stdout);
     236           0 :                         fputc('\n', stdout);
     237             : 
     238           0 :                         special = special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE);
     239             : 
     240           0 :                         pp = strjoin(prefix, special);
     241           0 :                         if (!pp)
     242           0 :                                 return -ENOMEM;
     243             : 
     244           0 :                         r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
     245           0 :                         if (r < 0)
     246           0 :                                 return r;
     247             :                 }
     248             :         }
     249             : 
     250           0 :         cg->done = true;
     251           0 :         return 0;
     252             : }
     253             : 
     254           0 : static int dump_extra_processes(
     255             :                 Hashmap *cgroups,
     256             :                 const char *prefix,
     257             :                 unsigned n_columns,
     258             :                 OutputFlags flags) {
     259             : 
     260           0 :         _cleanup_free_ pid_t *pids = NULL;
     261           0 :         _cleanup_hashmap_free_ Hashmap *names = NULL;
     262             :         struct CGroupInfo *cg;
     263           0 :         size_t n_allocated = 0, n = 0, k;
     264             :         Iterator i;
     265             :         int width, r;
     266             : 
     267             :         /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
     268             :          * combined, sorted, linear list. */
     269             : 
     270           0 :         HASHMAP_FOREACH(cg, cgroups, i) {
     271             :                 const char *name;
     272             :                 void *pidp;
     273             :                 Iterator j;
     274             : 
     275           0 :                 if (cg->done)
     276           0 :                         continue;
     277             : 
     278           0 :                 if (hashmap_isempty(cg->pids))
     279           0 :                         continue;
     280             : 
     281           0 :                 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
     282           0 :                 if (r < 0)
     283           0 :                         return r;
     284             : 
     285           0 :                 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
     286           0 :                         return -ENOMEM;
     287             : 
     288           0 :                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
     289           0 :                         pids[n++] = PTR_TO_PID(pidp);
     290             : 
     291           0 :                         r = hashmap_put(names, pidp, (void*) name);
     292           0 :                         if (r < 0)
     293           0 :                                 return r;
     294             :                 }
     295             :         }
     296             : 
     297           0 :         if (n == 0)
     298           0 :                 return 0;
     299             : 
     300           0 :         typesafe_qsort(pids, n, pid_compare_func);
     301           0 :         width = DECIMAL_STR_WIDTH(pids[n-1]);
     302             : 
     303           0 :         for (k = 0; k < n; k++) {
     304           0 :                 _cleanup_free_ char *e = NULL;
     305             :                 const char *name;
     306             : 
     307           0 :                 name = hashmap_get(names, PID_TO_PTR(pids[k]));
     308           0 :                 assert(name);
     309             : 
     310           0 :                 if (n_columns != 0) {
     311             :                         unsigned z;
     312             : 
     313           0 :                         z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
     314             : 
     315           0 :                         e = ellipsize(name, z, 100);
     316           0 :                         if (e)
     317           0 :                                 name = e;
     318             :                 }
     319             : 
     320           0 :                 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
     321             :                         prefix,
     322             :                         special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET),
     323           0 :                         width, pids[k],
     324             :                         name);
     325             :         }
     326             : 
     327           0 :         return 0;
     328             : }
     329             : 
     330           0 : int unit_show_processes(
     331             :                 sd_bus *bus,
     332             :                 const char *unit,
     333             :                 const char *cgroup_path,
     334             :                 const char *prefix,
     335             :                 unsigned n_columns,
     336             :                 OutputFlags flags,
     337             :                 sd_bus_error *error) {
     338             : 
     339           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     340           0 :         Hashmap *cgroups = NULL;
     341             :         struct CGroupInfo *cg;
     342             :         int r;
     343             : 
     344           0 :         assert(bus);
     345           0 :         assert(unit);
     346             : 
     347           0 :         if (flags & OUTPUT_FULL_WIDTH)
     348           0 :                 n_columns = 0;
     349           0 :         else if (n_columns <= 0)
     350           0 :                 n_columns = columns();
     351             : 
     352           0 :         prefix = strempty(prefix);
     353             : 
     354           0 :         r = sd_bus_call_method(
     355             :                         bus,
     356             :                         "org.freedesktop.systemd1",
     357             :                         "/org/freedesktop/systemd1",
     358             :                         "org.freedesktop.systemd1.Manager",
     359             :                         "GetUnitProcesses",
     360             :                         error,
     361             :                         &reply,
     362             :                         "s",
     363             :                         unit);
     364           0 :         if (r < 0)
     365           0 :                 return r;
     366             : 
     367           0 :         cgroups = hashmap_new(&path_hash_ops);
     368           0 :         if (!cgroups)
     369           0 :                 return -ENOMEM;
     370             : 
     371           0 :         r = sd_bus_message_enter_container(reply, 'a', "(sus)");
     372           0 :         if (r < 0)
     373           0 :                 goto finish;
     374             : 
     375           0 :         for (;;) {
     376           0 :                 const char *path = NULL, *name = NULL;
     377             :                 uint32_t pid;
     378             : 
     379           0 :                 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
     380           0 :                 if (r < 0)
     381           0 :                         goto finish;
     382           0 :                 if (r == 0)
     383           0 :                         break;
     384             : 
     385           0 :                 r = add_process(cgroups, path, pid, name);
     386           0 :                 if (r == -ENOMEM)
     387           0 :                         goto finish;
     388           0 :                 if (r < 0)
     389           0 :                         log_warning_errno(r, "Invalid process description in GetUnitProcesses reply: cgroup=\"%s\" pid="PID_FMT" command=\"%s\", ignoring: %m",
     390             :                                           path, pid, name);
     391             :         }
     392             : 
     393           0 :         r = sd_bus_message_exit_container(reply);
     394           0 :         if (r < 0)
     395           0 :                 goto finish;
     396             : 
     397           0 :         r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
     398           0 :         if (r < 0)
     399           0 :                 goto finish;
     400             : 
     401           0 :         r = dump_extra_processes(cgroups, prefix, n_columns, flags);
     402             : 
     403           0 : finish:
     404           0 :         while ((cg = hashmap_first(cgroups)))
     405           0 :                remove_cgroup(cgroups, cg);
     406             : 
     407           0 :         hashmap_free(cgroups);
     408             : 
     409           0 :         return r;
     410             : }

Generated by: LCOV version 1.14