chiark / gitweb /
shared: move unit-specific code from bus-util.h to bus-unit-util.h
[elogind.git] / src / shared / bus-unit-util.c
1 /***
2   This file is part of elogind.
3
4   Copyright 2016 Lennart Poettering
5
6   elogind is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   elogind is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with elogind; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-unit-util.h"
23 #include "bus-util.h"
24 #include "cgroup-util.h"
25 #include "env-util.h"
26 #include "escape.h"
27 #include "hashmap.h"
28 #include "list.h"
29 #include "locale-util.h"
30 #include "parse-util.h"
31 #include "path-util.h"
32 #include "process-util.h"
33 #include "rlimit-util.h"
34 #include "signal-util.h"
35 #include "string-util.h"
36 #include "syslog-util.h"
37 #include "terminal-util.h"
38 #include "utf8.h"
39 #include "util.h"
40
41 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
42         assert(message);
43         assert(u);
44
45         u->machine = NULL;
46
47         return sd_bus_message_read(
48                         message,
49                         "(ssssssouso)",
50                         &u->id,
51                         &u->description,
52                         &u->load_state,
53                         &u->active_state,
54                         &u->sub_state,
55                         &u->following,
56                         &u->unit_path,
57                         &u->job_id,
58                         &u->job_type,
59                         &u->job_path);
60 }
61
62 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
63         const char *eq, *field;
64         int r, rl;
65
66         assert(m);
67         assert(assignment);
68
69         eq = strchr(assignment, '=');
70         if (!eq) {
71                 log_error("Not an assignment: %s", assignment);
72                 return -EINVAL;
73         }
74
75         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
76         if (r < 0)
77                 return bus_log_create_error(r);
78
79         field = strndupa(assignment, eq - assignment);
80         eq++;
81
82         if (streq(field, "CPUQuota")) {
83
84                 if (isempty(eq))
85                         r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
86                 else if (endswith(eq, "%")) {
87                         double percent;
88
89                         if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
90                                 log_error("CPU quota '%s' invalid.", eq);
91                                 return -EINVAL;
92                         }
93
94                         r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
95                 } else {
96                         log_error("CPU quota needs to be in percent.");
97                         return -EINVAL;
98                 }
99
100                 goto finish;
101
102         } else if (streq(field, "EnvironmentFile")) {
103
104                 r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
105                                           eq[0] == '-' ? eq + 1 : eq,
106                                           eq[0] == '-');
107                 goto finish;
108
109         } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
110                 char *n;
111                 usec_t t;
112                 size_t l;
113                 r = parse_sec(eq, &t);
114                 if (r < 0)
115                         return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
116
117                 l = strlen(field);
118                 n = newa(char, l + 2);
119                 if (!n)
120                         return log_oom();
121
122                 /* Change suffix Sec → USec */
123                 strcpy(mempcpy(n, field, l - 3), "USec");
124                 r = sd_bus_message_append(m, "sv", n, "t", t);
125                 goto finish;
126         }
127
128         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
129         if (r < 0)
130                 return bus_log_create_error(r);
131
132         rl = rlimit_from_string(field);
133         if (rl >= 0) {
134                 const char *sn;
135                 struct rlimit l;
136
137                 r = rlimit_parse(rl, eq, &l);
138                 if (r < 0)
139                         return log_error_errno(r, "Failed to parse resource limit: %s", eq);
140
141                 r = sd_bus_message_append(m, "v", "t", l.rlim_max);
142                 if (r < 0)
143                         return bus_log_create_error(r);
144
145                 r = sd_bus_message_close_container(m);
146                 if (r < 0)
147                         return bus_log_create_error(r);
148
149                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
150                 if (r < 0)
151                         return bus_log_create_error(r);
152
153                 sn = strjoina(field, "Soft");
154                 r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
155
156         } else if (STR_IN_SET(field,
157                        "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
158                        "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
159                        "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
160                        "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
161                        "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
162
163                 r = parse_boolean(eq);
164                 if (r < 0)
165                         return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
166
167                 r = sd_bus_message_append(m, "v", "b", r);
168
169         } else if (streq(field, "MemoryLimit")) {
170                 uint64_t bytes;
171
172                 if (isempty(eq) || streq(eq, "infinity"))
173                         bytes = (uint64_t) -1;
174                 else {
175                         r = parse_size(eq, 1024, &bytes);
176                         if (r < 0) {
177                                 log_error("Failed to parse bytes specification %s", assignment);
178                                 return -EINVAL;
179                         }
180                 }
181
182                 r = sd_bus_message_append(m, "v", "t", bytes);
183
184         } else if (streq(field, "TasksMax")) {
185                 uint64_t n;
186
187                 if (isempty(eq) || streq(eq, "infinity"))
188                         n = (uint64_t) -1;
189                 else {
190                         r = safe_atou64(eq, &n);
191                         if (r < 0) {
192                                 log_error("Failed to parse maximum tasks specification %s", assignment);
193                                 return -EINVAL;
194                         }
195                 }
196
197                 r = sd_bus_message_append(m, "v", "t", n);
198
199         } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
200                 uint64_t u;
201
202                 r = cg_cpu_shares_parse(eq, &u);
203                 if (r < 0) {
204                         log_error("Failed to parse %s value %s.", field, eq);
205                         return -EINVAL;
206                 }
207
208                 r = sd_bus_message_append(m, "v", "t", u);
209
210         } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
211                 uint64_t u;
212
213                 r = cg_cpu_shares_parse(eq, &u);
214                 if (r < 0) {
215                         log_error("Failed to parse %s value %s.", field, eq);
216                         return -EINVAL;
217                 }
218
219                 r = sd_bus_message_append(m, "v", "t", u);
220
221         } else if (STR_IN_SET(field,
222                               "User", "Group", "DevicePolicy", "KillMode",
223                               "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
224                               "StandardInput", "StandardOutput", "StandardError",
225                               "Description", "Slice", "Type", "WorkingDirectory",
226                               "RootDirectory", "SyslogIdentifier", "ProtectSystem",
227                               "ProtectHome"))
228                 r = sd_bus_message_append(m, "v", "s", eq);
229
230         else if (streq(field, "SyslogLevel")) {
231                 int level;
232
233                 level = log_level_from_string(eq);
234                 if (level < 0) {
235                         log_error("Failed to parse %s value %s.", field, eq);
236                         return -EINVAL;
237                 }
238
239                 r = sd_bus_message_append(m, "v", "i", level);
240
241         } else if (streq(field, "SyslogFacility")) {
242                 int facility;
243
244                 facility = log_facility_unshifted_from_string(eq);
245                 if (facility < 0) {
246                         log_error("Failed to parse %s value %s.", field, eq);
247                         return -EINVAL;
248                 }
249
250                 r = sd_bus_message_append(m, "v", "i", facility);
251
252         } else if (streq(field, "DeviceAllow")) {
253
254                 if (isempty(eq))
255                         r = sd_bus_message_append(m, "v", "a(ss)", 0);
256                 else {
257                         const char *path, *rwm, *e;
258
259                         e = strchr(eq, ' ');
260                         if (e) {
261                                 path = strndupa(eq, e - eq);
262                                 rwm = e+1;
263                         } else {
264                                 path = eq;
265                                 rwm = "";
266                         }
267
268                         if (!path_startswith(path, "/dev")) {
269                                 log_error("%s is not a device file in /dev.", path);
270                                 return -EINVAL;
271                         }
272
273                         r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
274                 }
275
276         } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
277
278                 if (isempty(eq))
279                         r = sd_bus_message_append(m, "v", "a(st)", 0);
280                 else {
281                         const char *path, *bandwidth, *e;
282                         uint64_t bytes;
283
284                         e = strchr(eq, ' ');
285                         if (e) {
286                                 path = strndupa(eq, e - eq);
287                                 bandwidth = e+1;
288                         } else {
289                                 log_error("Failed to parse %s value %s.", field, eq);
290                                 return -EINVAL;
291                         }
292
293                         if (!path_startswith(path, "/dev")) {
294                                 log_error("%s is not a device file in /dev.", path);
295                                 return -EINVAL;
296                         }
297
298                         r = parse_size(bandwidth, 1000, &bytes);
299                         if (r < 0) {
300                                 log_error("Failed to parse byte value %s.", bandwidth);
301                                 return -EINVAL;
302                         }
303
304                         r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
305                 }
306
307         } else if (streq(field, "BlockIODeviceWeight")) {
308
309                 if (isempty(eq))
310                         r = sd_bus_message_append(m, "v", "a(st)", 0);
311                 else {
312                         const char *path, *weight, *e;
313                         uint64_t u;
314
315                         e = strchr(eq, ' ');
316                         if (e) {
317                                 path = strndupa(eq, e - eq);
318                                 weight = e+1;
319                         } else {
320                                 log_error("Failed to parse %s value %s.", field, eq);
321                                 return -EINVAL;
322                         }
323
324                         if (!path_startswith(path, "/dev")) {
325                                 log_error("%s is not a device file in /dev.", path);
326                                 return -EINVAL;
327                         }
328
329                         r = safe_atou64(weight, &u);
330                         if (r < 0) {
331                                 log_error("Failed to parse %s value %s.", field, weight);
332                                 return -EINVAL;
333                         }
334                         r = sd_bus_message_append(m, "v", "a(st)", path, u);
335                 }
336
337         } else if (streq(field, "Nice")) {
338                 int32_t i;
339
340                 r = safe_atoi32(eq, &i);
341                 if (r < 0) {
342                         log_error("Failed to parse %s value %s.", field, eq);
343                         return -EINVAL;
344                 }
345
346                 r = sd_bus_message_append(m, "v", "i", i);
347
348         } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
349                 const char *p;
350
351                 r = sd_bus_message_open_container(m, 'v', "as");
352                 if (r < 0)
353                         return bus_log_create_error(r);
354
355                 r = sd_bus_message_open_container(m, 'a', "s");
356                 if (r < 0)
357                         return bus_log_create_error(r);
358
359                 p = eq;
360
361                 for (;;) {
362                         _cleanup_free_ char *word = NULL;
363
364                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
365                         if (r < 0) {
366                                 log_error("Failed to parse Environment value %s", eq);
367                                 return -EINVAL;
368                         }
369                         if (r == 0)
370                                 break;
371
372                         if (streq(field, "Environment")) {
373                                 if (!env_assignment_is_valid(word)) {
374                                         log_error("Invalid environment assignment: %s", word);
375                                         return -EINVAL;
376                                 }
377                         } else {  /* PassEnvironment */
378                                 if (!env_name_is_valid(word)) {
379                                         log_error("Invalid environment variable name: %s", word);
380                                         return -EINVAL;
381                                 }
382                         }
383
384                         r = sd_bus_message_append_basic(m, 's', word);
385                         if (r < 0)
386                                 return bus_log_create_error(r);
387                 }
388
389                 r = sd_bus_message_close_container(m);
390                 if (r < 0)
391                         return bus_log_create_error(r);
392
393                 r = sd_bus_message_close_container(m);
394
395         } else if (streq(field, "KillSignal")) {
396                 int sig;
397
398                 sig = signal_from_string_try_harder(eq);
399                 if (sig < 0) {
400                         log_error("Failed to parse %s value %s.", field, eq);
401                         return -EINVAL;
402                 }
403
404                 r = sd_bus_message_append(m, "v", "i", sig);
405
406         } else if (streq(field, "TimerSlackNSec")) {
407                 nsec_t n;
408
409                 r = parse_nsec(eq, &n);
410                 if (r < 0) {
411                         log_error("Failed to parse %s value %s", field, eq);
412                         return -EINVAL;
413                 }
414
415                 r = sd_bus_message_append(m, "v", "t", n);
416         } else if (streq(field, "OOMScoreAdjust")) {
417                 int oa;
418
419                 r = safe_atoi(eq, &oa);
420                 if (r < 0) {
421                         log_error("Failed to parse %s value %s", field, eq);
422                         return -EINVAL;
423                 }
424
425                 if (!oom_score_adjust_is_valid(oa)) {
426                         log_error("OOM score adjust value out of range");
427                         return -EINVAL;
428                 }
429
430                 r = sd_bus_message_append(m, "v", "i", oa);
431         } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
432                 const char *p;
433
434                 r = sd_bus_message_open_container(m, 'v', "as");
435                 if (r < 0)
436                         return bus_log_create_error(r);
437
438                 r = sd_bus_message_open_container(m, 'a', "s");
439                 if (r < 0)
440                         return bus_log_create_error(r);
441
442                 p = eq;
443
444                 for (;;) {
445                         _cleanup_free_ char *word = NULL;
446                         int offset;
447
448                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
449                         if (r < 0) {
450                                 log_error("Failed to parse %s value %s", field, eq);
451                                 return -EINVAL;
452                         }
453                         if (r == 0)
454                                 break;
455
456                         if (!utf8_is_valid(word)) {
457                                 log_error("Failed to parse %s value %s", field, eq);
458                                 return -EINVAL;
459                         }
460
461                         offset = word[0] == '-';
462                         if (!path_is_absolute(word + offset)) {
463                                 log_error("Failed to parse %s value %s", field, eq);
464                                 return -EINVAL;
465                         }
466
467                         path_kill_slashes(word + offset);
468
469                         r = sd_bus_message_append_basic(m, 's', word);
470                         if (r < 0)
471                                 return bus_log_create_error(r);
472                 }
473
474                 r = sd_bus_message_close_container(m);
475                 if (r < 0)
476                         return bus_log_create_error(r);
477
478                 r = sd_bus_message_close_container(m);
479
480         } else if (streq(field, "RuntimeDirectory")) {
481                 const char *p;
482
483                 r = sd_bus_message_open_container(m, 'v', "as");
484                 if (r < 0)
485                         return bus_log_create_error(r);
486
487                 r = sd_bus_message_open_container(m, 'a', "s");
488                 if (r < 0)
489                         return bus_log_create_error(r);
490
491                 p = eq;
492
493                 for (;;) {
494                         _cleanup_free_ char *word = NULL;
495
496                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
497                         if (r < 0)
498                                 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
499
500                         if (r == 0)
501                                 break;
502
503                         r = sd_bus_message_append_basic(m, 's', word);
504                         if (r < 0)
505                                 return bus_log_create_error(r);
506                 }
507
508                 r = sd_bus_message_close_container(m);
509                 if (r < 0)
510                         return bus_log_create_error(r);
511
512                 r = sd_bus_message_close_container(m);
513
514         } else {
515                 log_error("Unknown assignment %s.", assignment);
516                 return -EINVAL;
517         }
518
519 finish:
520         if (r < 0)
521                 return bus_log_create_error(r);
522
523         r = sd_bus_message_close_container(m);
524         if (r < 0)
525                 return bus_log_create_error(r);
526
527         return 0;
528 }
529
530 typedef struct BusWaitForJobs {
531         sd_bus *bus;
532         Set *jobs;
533
534         char *name;
535         char *result;
536
537         sd_bus_slot *slot_job_removed;
538         sd_bus_slot *slot_disconnected;
539 } BusWaitForJobs;
540
541 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
542         assert(m);
543
544         log_error("Warning! D-Bus connection terminated.");
545         sd_bus_close(sd_bus_message_get_bus(m));
546
547         return 0;
548 }
549
550 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
551         const char *path, *unit, *result;
552         BusWaitForJobs *d = userdata;
553         uint32_t id;
554         char *found;
555         int r;
556
557         assert(m);
558         assert(d);
559
560         r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
561         if (r < 0) {
562                 bus_log_parse_error(r);
563                 return 0;
564         }
565
566         found = set_remove(d->jobs, (char*) path);
567         if (!found)
568                 return 0;
569
570         free(found);
571
572         if (!isempty(result))
573                 d->result = strdup(result);
574
575         if (!isempty(unit))
576                 d->name = strdup(unit);
577
578         return 0;
579 }
580
581 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
582         if (!d)
583                 return;
584
585         set_free_free(d->jobs);
586
587         sd_bus_slot_unref(d->slot_disconnected);
588         sd_bus_slot_unref(d->slot_job_removed);
589
590         sd_bus_unref(d->bus);
591
592         free(d->name);
593         free(d->result);
594
595         free(d);
596 }
597
598 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
599         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
600         int r;
601
602         assert(bus);
603         assert(ret);
604
605         d = new0(BusWaitForJobs, 1);
606         if (!d)
607                 return -ENOMEM;
608
609         d->bus = sd_bus_ref(bus);
610
611         /* When we are a bus client we match by sender. Direct
612          * connections OTOH have no initialized sender field, and
613          * hence we ignore the sender then */
614         r = sd_bus_add_match(
615                         bus,
616                         &d->slot_job_removed,
617                         bus->bus_client ?
618                         "type='signal',"
619                         "sender='org.freedesktop.elogind1',"
620                         "interface='org.freedesktop.elogind1.Manager',"
621                         "member='JobRemoved',"
622                         "path='/org/freedesktop/elogind1'" :
623                         "type='signal',"
624                         "interface='org.freedesktop.elogind1.Manager',"
625                         "member='JobRemoved',"
626                         "path='/org/freedesktop/elogind1'",
627                         match_job_removed, d);
628         if (r < 0)
629                 return r;
630
631         r = sd_bus_add_match(
632                         bus,
633                         &d->slot_disconnected,
634                         "type='signal',"
635                         "sender='org.freedesktop.DBus.Local',"
636                         "interface='org.freedesktop.DBus.Local',"
637                         "member='Disconnected'",
638                         match_disconnected, d);
639         if (r < 0)
640                 return r;
641
642         *ret = d;
643         d = NULL;
644
645         return 0;
646 }
647
648 static int bus_process_wait(sd_bus *bus) {
649         int r;
650
651         for (;;) {
652                 r = sd_bus_process(bus, NULL);
653                 if (r < 0)
654                         return r;
655                 if (r > 0)
656                         return 0;
657
658                 r = sd_bus_wait(bus, (uint64_t) -1);
659                 if (r < 0)
660                         return r;
661         }
662 }
663
664 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
665         _cleanup_free_ char *dbus_path = NULL;
666
667         assert(d);
668         assert(d->name);
669         assert(result);
670
671         dbus_path = unit_dbus_path_from_name(d->name);
672         if (!dbus_path)
673                 return -ENOMEM;
674
675         return sd_bus_get_property_string(d->bus,
676                                           "org.freedesktop.elogind1",
677                                           dbus_path,
678                                           "org.freedesktop.elogind1.Service",
679                                           "Result",
680                                           NULL,
681                                           result);
682 }
683
684 static const struct {
685         const char *result, *explanation;
686 } explanations [] = {
687         { "resources",   "a configured resource limit was exceeded" },
688         { "timeout",     "a timeout was exceeded" },
689         { "exit-code",   "the control process exited with error code" },
690         { "signal",      "a fatal signal was delivered to the control process" },
691         { "core-dump",   "a fatal signal was delivered causing the control process to dump core" },
692         { "watchdog",    "the service failed to send watchdog ping" },
693         { "start-limit", "start of the service was attempted too often" }
694 };
695
696 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
697         _cleanup_free_ char *service_shell_quoted = NULL;
698         const char *systemctl = "systemctl", *journalctl = "journalctl";
699
700         assert(service);
701
702         service_shell_quoted = shell_maybe_quote(service);
703
704         if (extra_args && extra_args[1]) {
705                 _cleanup_free_ char *t;
706
707                 t = strv_join((char**) extra_args, " ");
708                 systemctl = strjoina("systemctl ", t ? : "<args>");
709                 journalctl = strjoina("journalctl ", t ? : "<args>");
710         }
711
712         if (!isempty(result)) {
713                 unsigned i;
714
715                 for (i = 0; i < ELEMENTSOF(explanations); ++i)
716                         if (streq(result, explanations[i].result))
717                                 break;
718
719                 if (i < ELEMENTSOF(explanations)) {
720                         log_error("Job for %s failed because %s.\n"
721                                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
722                                   service,
723                                   explanations[i].explanation,
724                                   systemctl,
725                                   service_shell_quoted ?: "<service>",
726                                   journalctl);
727                         goto finish;
728                 }
729         }
730
731         log_error("Job for %s failed.\n"
732                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
733                   service,
734                   systemctl,
735                   service_shell_quoted ?: "<service>",
736                   journalctl);
737
738 finish:
739         /* For some results maybe additional explanation is required */
740         if (streq_ptr(result, "start-limit"))
741                 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
742                          "followed by \"%1$s start %2$s\" again.",
743                          systemctl,
744                          service_shell_quoted ?: "<service>");
745 }
746
747 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
748         int r = 0;
749
750         assert(d->result);
751
752         if (!quiet) {
753                 if (streq(d->result, "canceled"))
754                         log_error("Job for %s canceled.", strna(d->name));
755                 else if (streq(d->result, "timeout"))
756                         log_error("Job for %s timed out.", strna(d->name));
757                 else if (streq(d->result, "dependency"))
758                         log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
759                 else if (streq(d->result, "invalid"))
760                         log_error("%s is not active, cannot reload.", strna(d->name));
761                 else if (streq(d->result, "assert"))
762                         log_error("Assertion failed on job for %s.", strna(d->name));
763                 else if (streq(d->result, "unsupported"))
764                         log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
765                 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
766                         if (d->name) {
767                                 int q;
768                                 _cleanup_free_ char *result = NULL;
769
770                                 q = bus_job_get_service_result(d, &result);
771                                 if (q < 0)
772                                         log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
773
774                                 log_job_error_with_service_result(d->name, result, extra_args);
775                         } else
776                                 log_error("Job failed. See \"journalctl -xe\" for details.");
777                 }
778         }
779
780         if (streq(d->result, "canceled"))
781                 r = -ECANCELED;
782         else if (streq(d->result, "timeout"))
783                 r = -ETIME;
784         else if (streq(d->result, "dependency"))
785                 r = -EIO;
786         else if (streq(d->result, "invalid"))
787                 r = -ENOEXEC;
788         else if (streq(d->result, "assert"))
789                 r = -EPROTO;
790         else if (streq(d->result, "unsupported"))
791                 r = -EOPNOTSUPP;
792         else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
793                 r = -EIO;
794
795         return r;
796 }
797
798 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
799         int r = 0;
800
801         assert(d);
802
803         while (!set_isempty(d->jobs)) {
804                 int q;
805
806                 q = bus_process_wait(d->bus);
807                 if (q < 0)
808                         return log_error_errno(q, "Failed to wait for response: %m");
809
810                 if (d->result) {
811                         q = check_wait_response(d, quiet, extra_args);
812                         /* Return the first error as it is most likely to be
813                          * meaningful. */
814                         if (q < 0 && r == 0)
815                                 r = q;
816
817                         log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
818                 }
819
820                 d->name = mfree(d->name);
821                 d->result = mfree(d->result);
822         }
823
824         return r;
825 }
826
827 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
828         int r;
829
830         assert(d);
831
832         r = set_ensure_allocated(&d->jobs, &string_hash_ops);
833         if (r < 0)
834                 return r;
835
836         return set_put_strdup(d->jobs, path);
837 }
838
839 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
840         int r;
841
842         r = bus_wait_for_jobs_add(d, path);
843         if (r < 0)
844                 return log_oom();
845
846         return bus_wait_for_jobs(d, quiet, NULL);
847 }
848
849 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
850         const char *type, *path, *source;
851         int r;
852
853         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
854         if (r < 0)
855                 return bus_log_parse_error(r);
856
857         while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
858                 /* We expect only "success" changes to be sent over the bus.
859                    Hence, reject anything negative. */
860                 UnitFileChangeType ch = unit_file_change_type_from_string(type);
861
862                 if (ch < 0) {
863                         log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
864                         continue;
865                 }
866
867                 r = unit_file_changes_add(changes, n_changes, ch, path, source);
868                 if (r < 0)
869                         return r;
870         }
871         if (r < 0)
872                 return bus_log_parse_error(r);
873
874         r = sd_bus_message_exit_container(m);
875         if (r < 0)
876                 return bus_log_parse_error(r);
877
878         unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
879         return 0;
880 }
881
882 struct CGroupInfo {
883         char *cgroup_path;
884         bool is_const; /* If false, cgroup_path should be free()'d */
885
886         Hashmap *pids; /* PID → process name */
887         bool done;
888
889         struct CGroupInfo *parent;
890         LIST_FIELDS(struct CGroupInfo, siblings);
891         LIST_HEAD(struct CGroupInfo, children);
892         size_t n_children;
893 };
894
895 static bool IS_ROOT(const char *p) {
896         return isempty(p) || streq(p, "/");
897 }
898
899 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
900         struct CGroupInfo *parent = NULL, *cg;
901         int r;
902
903         assert(cgroups);
904         assert(ret);
905
906         if (IS_ROOT(path))
907                 path = "/";
908
909         cg = hashmap_get(cgroups, path);
910         if (cg) {
911                 *ret = cg;
912                 return 0;
913         }
914
915         if (!IS_ROOT(path)) {
916                 const char *e, *pp;
917
918                 e = strrchr(path, '/');
919                 if (!e)
920                         return -EINVAL;
921
922                 pp = strndupa(path, e - path);
923                 if (!pp)
924                         return -ENOMEM;
925
926                 r = add_cgroup(cgroups, pp, false, &parent);
927                 if (r < 0)
928                         return r;
929         }
930
931         cg = new0(struct CGroupInfo, 1);
932         if (!cg)
933                 return -ENOMEM;
934
935         if (is_const)
936                 cg->cgroup_path = (char*) path;
937         else {
938                 cg->cgroup_path = strdup(path);
939                 if (!cg->cgroup_path) {
940                         free(cg);
941                         return -ENOMEM;
942                 }
943         }
944
945         cg->is_const = is_const;
946         cg->parent = parent;
947
948         r = hashmap_put(cgroups, cg->cgroup_path, cg);
949         if (r < 0) {
950                 if (!is_const)
951                         free(cg->cgroup_path);
952                 free(cg);
953                 return r;
954         }
955
956         if (parent) {
957                 LIST_PREPEND(siblings, parent->children, cg);
958                 parent->n_children++;
959         }
960
961         *ret = cg;
962         return 1;
963 }
964
965 static int add_process(
966                 Hashmap *cgroups,
967                 const char *path,
968                 pid_t pid,
969                 const char *name) {
970
971         struct CGroupInfo *cg;
972         int r;
973
974         assert(cgroups);
975         assert(name);
976         assert(pid > 0);
977
978         r = add_cgroup(cgroups, path, true, &cg);
979         if (r < 0)
980                 return r;
981
982         r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
983         if (r < 0)
984                 return r;
985
986         return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
987 }
988
989 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
990         assert(cgroups);
991         assert(cg);
992
993         while (cg->children)
994                 remove_cgroup(cgroups, cg->children);
995
996         hashmap_remove(cgroups, cg->cgroup_path);
997
998         if (!cg->is_const)
999                 free(cg->cgroup_path);
1000
1001         hashmap_free(cg->pids);
1002
1003         if (cg->parent)
1004                 LIST_REMOVE(siblings, cg->parent->children, cg);
1005
1006         free(cg);
1007 }
1008
1009 static int cgroup_info_compare_func(const void *a, const void *b) {
1010         const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1011
1012         assert(x);
1013         assert(y);
1014
1015         return strcmp(x->cgroup_path, y->cgroup_path);
1016 }
1017
1018 static int dump_processes(
1019                 Hashmap *cgroups,
1020                 const char *cgroup_path,
1021                 const char *prefix,
1022                 unsigned n_columns,
1023                 OutputFlags flags) {
1024
1025         struct CGroupInfo *cg;
1026         int r;
1027
1028         assert(prefix);
1029
1030         if (IS_ROOT(cgroup_path))
1031                 cgroup_path = "/";
1032
1033         cg = hashmap_get(cgroups, cgroup_path);
1034         if (!cg)
1035                 return 0;
1036
1037         if (!hashmap_isempty(cg->pids)) {
1038                 const char *name;
1039                 size_t n = 0, i;
1040                 pid_t *pids;
1041                 void *pidp;
1042                 Iterator j;
1043                 int width;
1044
1045                 /* Order processes by their PID */
1046                 pids = newa(pid_t, hashmap_size(cg->pids));
1047
1048                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1049                         pids[n++] = PTR_TO_PID(pidp);
1050
1051                 assert(n == hashmap_size(cg->pids));
1052                 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1053
1054                 width = DECIMAL_STR_WIDTH(pids[n-1]);
1055
1056                 for (i = 0; i < n; i++) {
1057                         _cleanup_free_ char *e = NULL;
1058                         const char *special;
1059                         bool more;
1060
1061                         name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1062                         assert(name);
1063
1064                         if (n_columns != 0) {
1065                                 unsigned k;
1066
1067                                 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1068
1069                                 e = ellipsize(name, k, 100);
1070                                 if (e)
1071                                         name = e;
1072                         }
1073
1074                         more = i+1 < n || cg->children;
1075                         special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
1076
1077                         fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1078                                 prefix,
1079                                 special,
1080                                 width, pids[i],
1081                                 name);
1082                 }
1083         }
1084
1085         if (cg->children) {
1086                 struct CGroupInfo **children, *child;
1087                 size_t n = 0, i;
1088
1089                 /* Order subcgroups by their name */
1090                 children = newa(struct CGroupInfo*, cg->n_children);
1091                 LIST_FOREACH(siblings, child, cg->children)
1092                         children[n++] = child;
1093                 assert(n == cg->n_children);
1094                 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1095
1096                 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1097
1098                 for (i = 0; i < n; i++) {
1099                         _cleanup_free_ char *pp = NULL;
1100                         const char *name, *special;
1101                         bool more;
1102
1103                         child = children[i];
1104
1105                         name = strrchr(child->cgroup_path, '/');
1106                         if (!name)
1107                                 return -EINVAL;
1108                         name++;
1109
1110                         more = i+1 < n;
1111                         special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT);
1112
1113                         fputs(prefix, stdout);
1114                         fputs(special, stdout);
1115                         fputs(name, stdout);
1116                         fputc('\n', stdout);
1117
1118                         special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE);
1119
1120                         pp = strappend(prefix, special);
1121                         if (!pp)
1122                                 return -ENOMEM;
1123
1124                         r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1125                         if (r < 0)
1126                                 return r;
1127                 }
1128         }
1129
1130         cg->done = true;
1131         return 0;
1132 }
1133
1134 static int dump_extra_processes(
1135                 Hashmap *cgroups,
1136                 const char *prefix,
1137                 unsigned n_columns,
1138                 OutputFlags flags) {
1139
1140         _cleanup_free_ pid_t *pids = NULL;
1141         _cleanup_hashmap_free_ Hashmap *names = NULL;
1142         struct CGroupInfo *cg;
1143         size_t n_allocated = 0, n = 0, k;
1144         Iterator i;
1145         int width, r;
1146
1147         /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1148          * combined, sorted, linear list. */
1149
1150         HASHMAP_FOREACH(cg, cgroups, i) {
1151                 const char *name;
1152                 void *pidp;
1153                 Iterator j;
1154
1155                 if (cg->done)
1156                         continue;
1157
1158                 if (hashmap_isempty(cg->pids))
1159                         continue;
1160
1161                 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1162                 if (r < 0)
1163                         return r;
1164
1165                 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1166                         return -ENOMEM;
1167
1168                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1169                         pids[n++] = PTR_TO_PID(pidp);
1170
1171                         r = hashmap_put(names, pidp, (void*) name);
1172                         if (r < 0)
1173                                 return r;
1174                 }
1175         }
1176
1177         if (n == 0)
1178                 return 0;
1179
1180         qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1181         width = DECIMAL_STR_WIDTH(pids[n-1]);
1182
1183         for (k = 0; k < n; k++) {
1184                 _cleanup_free_ char *e = NULL;
1185                 const char *name;
1186
1187                 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1188                 assert(name);
1189
1190                 if (n_columns != 0) {
1191                         unsigned z;
1192
1193                         z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1194
1195                         e = ellipsize(name, z, 100);
1196                         if (e)
1197                                 name = e;
1198                 }
1199
1200                 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1201                         prefix,
1202                         draw_special_char(DRAW_TRIANGULAR_BULLET),
1203                         width, pids[k],
1204                         name);
1205         }
1206
1207         return 0;
1208 }
1209
1210 int unit_show_processes(
1211                 sd_bus *bus,
1212                 const char *unit,
1213                 const char *cgroup_path,
1214                 const char *prefix,
1215                 unsigned n_columns,
1216                 OutputFlags flags,
1217                 sd_bus_error *error) {
1218
1219         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1220         Hashmap *cgroups = NULL;
1221         struct CGroupInfo *cg;
1222         int r;
1223
1224         assert(bus);
1225         assert(unit);
1226
1227         if (flags & OUTPUT_FULL_WIDTH)
1228                 n_columns = 0;
1229         else if (n_columns <= 0)
1230                 n_columns = columns();
1231
1232         prefix = strempty(prefix);
1233
1234         r = sd_bus_call_method(
1235                         bus,
1236                         "org.freedesktop.elogind1",
1237                         "/org/freedesktop/elogind1",
1238                         "org.freedesktop.elogind1.Manager",
1239                         "GetUnitProcesses",
1240                         error,
1241                         &reply,
1242                         "s",
1243                         unit);
1244         if (r < 0)
1245                 return r;
1246
1247         cgroups = hashmap_new(&string_hash_ops);
1248         if (!cgroups)
1249                 return -ENOMEM;
1250
1251         r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1252         if (r < 0)
1253                 goto finish;
1254
1255         for (;;) {
1256                 const char *path = NULL, *name = NULL;
1257                 uint32_t pid;
1258
1259                 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1260                 if (r < 0)
1261                         goto finish;
1262                 if (r == 0)
1263                         break;
1264
1265                 r = add_process(cgroups, path, pid, name);
1266                 if (r < 0)
1267                         goto finish;
1268         }
1269
1270         r = sd_bus_message_exit_container(reply);
1271         if (r < 0)
1272                 goto finish;
1273
1274         r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1275         if (r < 0)
1276                 goto finish;
1277
1278         r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1279
1280 finish:
1281         while ((cg = hashmap_first(cgroups)))
1282                remove_cgroup(cgroups, cg);
1283
1284         hashmap_free(cgroups);
1285
1286         return r;
1287 }