1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "locale-util.h"
4 #include "sort-util.h"
5 #include "special.h"
6 #include "systemctl-list-dependencies.h"
7 #include "systemctl-util.h"
8 #include "systemctl.h"
9 #include "terminal-util.h"
10 
list_dependencies_print(const char * name,int level,unsigned branches,bool last)11 static int list_dependencies_print(const char *name, int level, unsigned branches, bool last) {
12         _cleanup_free_ char *n = NULL;
13         size_t max_len = MAX(columns(),20u);
14         size_t len = 0;
15 
16         if (!arg_plain) {
17 
18                 for (int i = level - 1; i >= 0; i--) {
19                         len += 2;
20                         if (len > max_len - 3 && !arg_full) {
21                                 printf("%s...\n",max_len % 2 ? "" : " ");
22                                 return 0;
23                         }
24                         printf("%s", special_glyph(branches & (1 << i) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
25                 }
26                 len += 2;
27 
28                 if (len > max_len - 3 && !arg_full) {
29                         printf("%s...\n",max_len % 2 ? "" : " ");
30                         return 0;
31                 }
32 
33                 printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
34         }
35 
36         if (arg_full) {
37                 printf("%s\n", name);
38                 return 0;
39         }
40 
41         n = ellipsize(name, max_len-len, 100);
42         if (!n)
43                 return log_oom();
44 
45         printf("%s\n", n);
46         return 0;
47 }
48 
list_dependencies_compare(char * const * a,char * const * b)49 static int list_dependencies_compare(char * const *a, char * const *b) {
50         if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
51                 return 1;
52         if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET)
53                 return -1;
54 
55         return strcasecmp(*a, *b);
56 }
57 
list_dependencies_one(sd_bus * bus,const char * name,int level,char *** units,unsigned branches)58 static int list_dependencies_one(
59                 sd_bus *bus,
60                 const char *name,
61                 int level,
62                 char ***units,
63                 unsigned branches) {
64 
65         _cleanup_strv_free_ char **deps = NULL;
66         int r;
67 
68         assert(bus);
69         assert(name);
70         assert(units);
71 
72         r = strv_extend(units, name);
73         if (r < 0)
74                 return log_oom();
75 
76         r = unit_get_dependencies(bus, name, &deps);
77         if (r < 0)
78                 return r;
79 
80         typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
81 
82         STRV_FOREACH(c, deps) {
83                 if (strv_contains(*units, *c)) {
84                         if (!arg_plain) {
85                                 printf("  ");
86                                 r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1);
87                                 if (r < 0)
88                                         return r;
89                         }
90                         continue;
91                 }
92 
93                 if (arg_plain)
94                         printf("  ");
95                 else {
96                         UnitActiveState active_state = _UNIT_ACTIVE_STATE_INVALID;
97                         const char *on;
98 
99                         (void) get_state_one_unit(bus, *c, &active_state);
100 
101                         switch (active_state) {
102                         case UNIT_ACTIVE:
103                         case UNIT_RELOADING:
104                         case UNIT_ACTIVATING:
105                                 on = ansi_highlight_green();
106                                 break;
107 
108                         case UNIT_INACTIVE:
109                         case UNIT_DEACTIVATING:
110                                 on = ansi_normal();
111                                 break;
112 
113                         default:
114                                 on = ansi_highlight_red();
115                                 break;
116                         }
117 
118                         printf("%s%s%s ", on, special_glyph(unit_active_state_to_glyph(active_state)), ansi_normal());
119                 }
120 
121                 r = list_dependencies_print(*c, level, branches, c[1] == NULL);
122                 if (r < 0)
123                         return r;
124 
125                 if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) {
126                        r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1));
127                        if (r < 0)
128                                return r;
129                 }
130         }
131 
132         if (!arg_plain)
133                 strv_remove(*units, name);
134 
135         return 0;
136 }
137 
verb_list_dependencies(int argc,char * argv[],void * userdata)138 int verb_list_dependencies(int argc, char *argv[], void *userdata) {
139         _cleanup_strv_free_ char **units = NULL, **done = NULL;
140         char **patterns;
141         sd_bus *bus;
142         int r;
143 
144         r = acquire_bus(BUS_MANAGER, &bus);
145         if (r < 0)
146                 return r;
147 
148         patterns = strv_skip(argv, 1);
149         if (strv_isempty(patterns)) {
150                 units = strv_new(SPECIAL_DEFAULT_TARGET);
151                 if (!units)
152                         return log_oom();
153         } else {
154                 r = expand_unit_names(bus, patterns, NULL, &units, NULL);
155                 if (r < 0)
156                         return log_error_errno(r, "Failed to expand names: %m");
157         }
158 
159         pager_open(arg_pager_flags);
160 
161         STRV_FOREACH(u, units) {
162                 if (u != units)
163                         puts("");
164 
165                 puts(*u);
166                 r = list_dependencies_one(bus, *u, 0, &done, 0);
167                 if (r < 0)
168                         return r;
169         }
170 
171         return 0;
172 }
173