chiark / gitweb /
tree-wide: rename draw_special_char to special_glyph
[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", "IOAccounting", "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, "IOWeight", "StartupIOWeight")) {
211                 uint64_t u;
212
213                 r = cg_weight_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, "BlockIOWeight", "StartupBlockIOWeight")) {
222                 uint64_t u;
223
224                 r = cg_cpu_shares_parse(eq, &u);
225                 if (r < 0) {
226                         log_error("Failed to parse %s value %s.", field, eq);
227                         return -EINVAL;
228                 }
229
230                 r = sd_bus_message_append(m, "v", "t", u);
231
232         } else if (STR_IN_SET(field,
233                               "User", "Group", "DevicePolicy", "KillMode",
234                               "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
235                               "StandardInput", "StandardOutput", "StandardError",
236                               "Description", "Slice", "Type", "WorkingDirectory",
237                               "RootDirectory", "SyslogIdentifier", "ProtectSystem",
238                               "ProtectHome"))
239                 r = sd_bus_message_append(m, "v", "s", eq);
240
241         else if (streq(field, "SyslogLevel")) {
242                 int level;
243
244                 level = log_level_from_string(eq);
245                 if (level < 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", level);
251
252         } else if (streq(field, "SyslogFacility")) {
253                 int facility;
254
255                 facility = log_facility_unshifted_from_string(eq);
256                 if (facility < 0) {
257                         log_error("Failed to parse %s value %s.", field, eq);
258                         return -EINVAL;
259                 }
260
261                 r = sd_bus_message_append(m, "v", "i", facility);
262
263         } else if (streq(field, "DeviceAllow")) {
264
265                 if (isempty(eq))
266                         r = sd_bus_message_append(m, "v", "a(ss)", 0);
267                 else {
268                         const char *path, *rwm, *e;
269
270                         e = strchr(eq, ' ');
271                         if (e) {
272                                 path = strndupa(eq, e - eq);
273                                 rwm = e+1;
274                         } else {
275                                 path = eq;
276                                 rwm = "";
277                         }
278
279                         if (!path_startswith(path, "/dev")) {
280                                 log_error("%s is not a device file in /dev.", path);
281                                 return -EINVAL;
282                         }
283
284                         r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
285                 }
286
287         } else if (STR_IN_SET(field, "IOReadBandwidthMax", "IOWriteBandwidthMax",
288                               "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
289
290                 if (isempty(eq))
291                         r = sd_bus_message_append(m, "v", "a(st)", 0);
292                 else {
293                         const char *path, *bandwidth, *e;
294                         uint64_t bytes;
295
296                         e = strchr(eq, ' ');
297                         if (e) {
298                                 path = strndupa(eq, e - eq);
299                                 bandwidth = e+1;
300                         } else {
301                                 log_error("Failed to parse %s value %s.", field, eq);
302                                 return -EINVAL;
303                         }
304
305                         if (!path_startswith(path, "/dev")) {
306                                 log_error("%s is not a device file in /dev.", path);
307                                 return -EINVAL;
308                         }
309
310                         if (streq(bandwidth, "max")) {
311                                 bytes = CGROUP_LIMIT_MAX;
312                         } else {
313                                 r = parse_size(bandwidth, 1000, &bytes);
314                                 if (r < 0) {
315                                         log_error("Failed to parse byte value %s.", bandwidth);
316                                         return -EINVAL;
317                                 }
318                         }
319
320                         r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
321                 }
322
323         } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
324
325                 if (isempty(eq))
326                         r = sd_bus_message_append(m, "v", "a(st)", 0);
327                 else {
328                         const char *path, *weight, *e;
329                         uint64_t u;
330
331                         e = strchr(eq, ' ');
332                         if (e) {
333                                 path = strndupa(eq, e - eq);
334                                 weight = e+1;
335                         } else {
336                                 log_error("Failed to parse %s value %s.", field, eq);
337                                 return -EINVAL;
338                         }
339
340                         if (!path_startswith(path, "/dev")) {
341                                 log_error("%s is not a device file in /dev.", path);
342                                 return -EINVAL;
343                         }
344
345                         r = safe_atou64(weight, &u);
346                         if (r < 0) {
347                                 log_error("Failed to parse %s value %s.", field, weight);
348                                 return -EINVAL;
349                         }
350                         r = sd_bus_message_append(m, "v", "a(st)", path, u);
351                 }
352
353         } else if (streq(field, "Nice")) {
354                 int32_t i;
355
356                 r = safe_atoi32(eq, &i);
357                 if (r < 0) {
358                         log_error("Failed to parse %s value %s.", field, eq);
359                         return -EINVAL;
360                 }
361
362                 r = sd_bus_message_append(m, "v", "i", i);
363
364         } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
365                 const char *p;
366
367                 r = sd_bus_message_open_container(m, 'v', "as");
368                 if (r < 0)
369                         return bus_log_create_error(r);
370
371                 r = sd_bus_message_open_container(m, 'a', "s");
372                 if (r < 0)
373                         return bus_log_create_error(r);
374
375                 p = eq;
376
377                 for (;;) {
378                         _cleanup_free_ char *word = NULL;
379
380                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
381                         if (r < 0) {
382                                 log_error("Failed to parse Environment value %s", eq);
383                                 return -EINVAL;
384                         }
385                         if (r == 0)
386                                 break;
387
388                         if (streq(field, "Environment")) {
389                                 if (!env_assignment_is_valid(word)) {
390                                         log_error("Invalid environment assignment: %s", word);
391                                         return -EINVAL;
392                                 }
393                         } else {  /* PassEnvironment */
394                                 if (!env_name_is_valid(word)) {
395                                         log_error("Invalid environment variable name: %s", word);
396                                         return -EINVAL;
397                                 }
398                         }
399
400                         r = sd_bus_message_append_basic(m, 's', word);
401                         if (r < 0)
402                                 return bus_log_create_error(r);
403                 }
404
405                 r = sd_bus_message_close_container(m);
406                 if (r < 0)
407                         return bus_log_create_error(r);
408
409                 r = sd_bus_message_close_container(m);
410
411         } else if (streq(field, "KillSignal")) {
412                 int sig;
413
414                 sig = signal_from_string_try_harder(eq);
415                 if (sig < 0) {
416                         log_error("Failed to parse %s value %s.", field, eq);
417                         return -EINVAL;
418                 }
419
420                 r = sd_bus_message_append(m, "v", "i", sig);
421
422         } else if (streq(field, "TimerSlackNSec")) {
423                 nsec_t n;
424
425                 r = parse_nsec(eq, &n);
426                 if (r < 0) {
427                         log_error("Failed to parse %s value %s", field, eq);
428                         return -EINVAL;
429                 }
430
431                 r = sd_bus_message_append(m, "v", "t", n);
432         } else if (streq(field, "OOMScoreAdjust")) {
433                 int oa;
434
435                 r = safe_atoi(eq, &oa);
436                 if (r < 0) {
437                         log_error("Failed to parse %s value %s", field, eq);
438                         return -EINVAL;
439                 }
440
441                 if (!oom_score_adjust_is_valid(oa)) {
442                         log_error("OOM score adjust value out of range");
443                         return -EINVAL;
444                 }
445
446                 r = sd_bus_message_append(m, "v", "i", oa);
447         } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
448                 const char *p;
449
450                 r = sd_bus_message_open_container(m, 'v', "as");
451                 if (r < 0)
452                         return bus_log_create_error(r);
453
454                 r = sd_bus_message_open_container(m, 'a', "s");
455                 if (r < 0)
456                         return bus_log_create_error(r);
457
458                 p = eq;
459
460                 for (;;) {
461                         _cleanup_free_ char *word = NULL;
462                         int offset;
463
464                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
465                         if (r < 0) {
466                                 log_error("Failed to parse %s value %s", field, eq);
467                                 return -EINVAL;
468                         }
469                         if (r == 0)
470                                 break;
471
472                         if (!utf8_is_valid(word)) {
473                                 log_error("Failed to parse %s value %s", field, eq);
474                                 return -EINVAL;
475                         }
476
477                         offset = word[0] == '-';
478                         if (!path_is_absolute(word + offset)) {
479                                 log_error("Failed to parse %s value %s", field, eq);
480                                 return -EINVAL;
481                         }
482
483                         path_kill_slashes(word + offset);
484
485                         r = sd_bus_message_append_basic(m, 's', word);
486                         if (r < 0)
487                                 return bus_log_create_error(r);
488                 }
489
490                 r = sd_bus_message_close_container(m);
491                 if (r < 0)
492                         return bus_log_create_error(r);
493
494                 r = sd_bus_message_close_container(m);
495
496         } else if (streq(field, "RuntimeDirectory")) {
497                 const char *p;
498
499                 r = sd_bus_message_open_container(m, 'v', "as");
500                 if (r < 0)
501                         return bus_log_create_error(r);
502
503                 r = sd_bus_message_open_container(m, 'a', "s");
504                 if (r < 0)
505                         return bus_log_create_error(r);
506
507                 p = eq;
508
509                 for (;;) {
510                         _cleanup_free_ char *word = NULL;
511
512                         r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
513                         if (r < 0)
514                                 return log_error_errno(r, "Failed to parse %s value %s", field, eq);
515
516                         if (r == 0)
517                                 break;
518
519                         r = sd_bus_message_append_basic(m, 's', word);
520                         if (r < 0)
521                                 return bus_log_create_error(r);
522                 }
523
524                 r = sd_bus_message_close_container(m);
525                 if (r < 0)
526                         return bus_log_create_error(r);
527
528                 r = sd_bus_message_close_container(m);
529
530         } else {
531                 log_error("Unknown assignment %s.", assignment);
532                 return -EINVAL;
533         }
534
535 finish:
536         if (r < 0)
537                 return bus_log_create_error(r);
538
539         r = sd_bus_message_close_container(m);
540         if (r < 0)
541                 return bus_log_create_error(r);
542
543         return 0;
544 }
545
546 typedef struct BusWaitForJobs {
547         sd_bus *bus;
548         Set *jobs;
549
550         char *name;
551         char *result;
552
553         sd_bus_slot *slot_job_removed;
554         sd_bus_slot *slot_disconnected;
555 } BusWaitForJobs;
556
557 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
558         assert(m);
559
560         log_error("Warning! D-Bus connection terminated.");
561         sd_bus_close(sd_bus_message_get_bus(m));
562
563         return 0;
564 }
565
566 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
567         const char *path, *unit, *result;
568         BusWaitForJobs *d = userdata;
569         uint32_t id;
570         char *found;
571         int r;
572
573         assert(m);
574         assert(d);
575
576         r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
577         if (r < 0) {
578                 bus_log_parse_error(r);
579                 return 0;
580         }
581
582         found = set_remove(d->jobs, (char*) path);
583         if (!found)
584                 return 0;
585
586         free(found);
587
588         if (!isempty(result))
589                 d->result = strdup(result);
590
591         if (!isempty(unit))
592                 d->name = strdup(unit);
593
594         return 0;
595 }
596
597 void bus_wait_for_jobs_free(BusWaitForJobs *d) {
598         if (!d)
599                 return;
600
601         set_free_free(d->jobs);
602
603         sd_bus_slot_unref(d->slot_disconnected);
604         sd_bus_slot_unref(d->slot_job_removed);
605
606         sd_bus_unref(d->bus);
607
608         free(d->name);
609         free(d->result);
610
611         free(d);
612 }
613
614 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
615         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
616         int r;
617
618         assert(bus);
619         assert(ret);
620
621         d = new0(BusWaitForJobs, 1);
622         if (!d)
623                 return -ENOMEM;
624
625         d->bus = sd_bus_ref(bus);
626
627         /* When we are a bus client we match by sender. Direct
628          * connections OTOH have no initialized sender field, and
629          * hence we ignore the sender then */
630         r = sd_bus_add_match(
631                         bus,
632                         &d->slot_job_removed,
633                         bus->bus_client ?
634                         "type='signal',"
635                         "sender='org.freedesktop.elogind1',"
636                         "interface='org.freedesktop.elogind1.Manager',"
637                         "member='JobRemoved',"
638                         "path='/org/freedesktop/elogind1'" :
639                         "type='signal',"
640                         "interface='org.freedesktop.elogind1.Manager',"
641                         "member='JobRemoved',"
642                         "path='/org/freedesktop/elogind1'",
643                         match_job_removed, d);
644         if (r < 0)
645                 return r;
646
647         r = sd_bus_add_match(
648                         bus,
649                         &d->slot_disconnected,
650                         "type='signal',"
651                         "sender='org.freedesktop.DBus.Local',"
652                         "interface='org.freedesktop.DBus.Local',"
653                         "member='Disconnected'",
654                         match_disconnected, d);
655         if (r < 0)
656                 return r;
657
658         *ret = d;
659         d = NULL;
660
661         return 0;
662 }
663
664 static int bus_process_wait(sd_bus *bus) {
665         int r;
666
667         for (;;) {
668                 r = sd_bus_process(bus, NULL);
669                 if (r < 0)
670                         return r;
671                 if (r > 0)
672                         return 0;
673
674                 r = sd_bus_wait(bus, (uint64_t) -1);
675                 if (r < 0)
676                         return r;
677         }
678 }
679
680 static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
681         _cleanup_free_ char *dbus_path = NULL;
682
683         assert(d);
684         assert(d->name);
685         assert(result);
686
687         dbus_path = unit_dbus_path_from_name(d->name);
688         if (!dbus_path)
689                 return -ENOMEM;
690
691         return sd_bus_get_property_string(d->bus,
692                                           "org.freedesktop.elogind1",
693                                           dbus_path,
694                                           "org.freedesktop.elogind1.Service",
695                                           "Result",
696                                           NULL,
697                                           result);
698 }
699
700 static const struct {
701         const char *result, *explanation;
702 } explanations [] = {
703         { "resources",   "a configured resource limit was exceeded" },
704         { "timeout",     "a timeout was exceeded" },
705         { "exit-code",   "the control process exited with error code" },
706         { "signal",      "a fatal signal was delivered to the control process" },
707         { "core-dump",   "a fatal signal was delivered causing the control process to dump core" },
708         { "watchdog",    "the service failed to send watchdog ping" },
709         { "start-limit", "start of the service was attempted too often" }
710 };
711
712 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
713         _cleanup_free_ char *service_shell_quoted = NULL;
714         const char *systemctl = "systemctl", *journalctl = "journalctl";
715
716         assert(service);
717
718         service_shell_quoted = shell_maybe_quote(service);
719
720         if (extra_args && extra_args[1]) {
721                 _cleanup_free_ char *t;
722
723                 t = strv_join((char**) extra_args, " ");
724                 systemctl = strjoina("systemctl ", t ? : "<args>");
725                 journalctl = strjoina("journalctl ", t ? : "<args>");
726         }
727
728         if (!isempty(result)) {
729                 unsigned i;
730
731                 for (i = 0; i < ELEMENTSOF(explanations); ++i)
732                         if (streq(result, explanations[i].result))
733                                 break;
734
735                 if (i < ELEMENTSOF(explanations)) {
736                         log_error("Job for %s failed because %s.\n"
737                                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
738                                   service,
739                                   explanations[i].explanation,
740                                   systemctl,
741                                   service_shell_quoted ?: "<service>",
742                                   journalctl);
743                         goto finish;
744                 }
745         }
746
747         log_error("Job for %s failed.\n"
748                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
749                   service,
750                   systemctl,
751                   service_shell_quoted ?: "<service>",
752                   journalctl);
753
754 finish:
755         /* For some results maybe additional explanation is required */
756         if (streq_ptr(result, "start-limit"))
757                 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
758                          "followed by \"%1$s start %2$s\" again.",
759                          systemctl,
760                          service_shell_quoted ?: "<service>");
761 }
762
763 static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
764         int r = 0;
765
766         assert(d->result);
767
768         if (!quiet) {
769                 if (streq(d->result, "canceled"))
770                         log_error("Job for %s canceled.", strna(d->name));
771                 else if (streq(d->result, "timeout"))
772                         log_error("Job for %s timed out.", strna(d->name));
773                 else if (streq(d->result, "dependency"))
774                         log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
775                 else if (streq(d->result, "invalid"))
776                         log_error("%s is not active, cannot reload.", strna(d->name));
777                 else if (streq(d->result, "assert"))
778                         log_error("Assertion failed on job for %s.", strna(d->name));
779                 else if (streq(d->result, "unsupported"))
780                         log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
781                 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
782                         if (d->name) {
783                                 int q;
784                                 _cleanup_free_ char *result = NULL;
785
786                                 q = bus_job_get_service_result(d, &result);
787                                 if (q < 0)
788                                         log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
789
790                                 log_job_error_with_service_result(d->name, result, extra_args);
791                         } else
792                                 log_error("Job failed. See \"journalctl -xe\" for details.");
793                 }
794         }
795
796         if (streq(d->result, "canceled"))
797                 r = -ECANCELED;
798         else if (streq(d->result, "timeout"))
799                 r = -ETIME;
800         else if (streq(d->result, "dependency"))
801                 r = -EIO;
802         else if (streq(d->result, "invalid"))
803                 r = -ENOEXEC;
804         else if (streq(d->result, "assert"))
805                 r = -EPROTO;
806         else if (streq(d->result, "unsupported"))
807                 r = -EOPNOTSUPP;
808         else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
809                 r = -EIO;
810
811         return r;
812 }
813
814 int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
815         int r = 0;
816
817         assert(d);
818
819         while (!set_isempty(d->jobs)) {
820                 int q;
821
822                 q = bus_process_wait(d->bus);
823                 if (q < 0)
824                         return log_error_errno(q, "Failed to wait for response: %m");
825
826                 if (d->result) {
827                         q = check_wait_response(d, quiet, extra_args);
828                         /* Return the first error as it is most likely to be
829                          * meaningful. */
830                         if (q < 0 && r == 0)
831                                 r = q;
832
833                         log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
834                 }
835
836                 d->name = mfree(d->name);
837                 d->result = mfree(d->result);
838         }
839
840         return r;
841 }
842
843 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
844         int r;
845
846         assert(d);
847
848         r = set_ensure_allocated(&d->jobs, &string_hash_ops);
849         if (r < 0)
850                 return r;
851
852         return set_put_strdup(d->jobs, path);
853 }
854
855 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
856         int r;
857
858         r = bus_wait_for_jobs_add(d, path);
859         if (r < 0)
860                 return log_oom();
861
862         return bus_wait_for_jobs(d, quiet, NULL);
863 }
864
865 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
866         const char *type, *path, *source;
867         int r;
868
869         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
870         if (r < 0)
871                 return bus_log_parse_error(r);
872
873         while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
874                 /* We expect only "success" changes to be sent over the bus.
875                    Hence, reject anything negative. */
876                 UnitFileChangeType ch = unit_file_change_type_from_string(type);
877
878                 if (ch < 0) {
879                         log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
880                         continue;
881                 }
882
883                 r = unit_file_changes_add(changes, n_changes, ch, path, source);
884                 if (r < 0)
885                         return r;
886         }
887         if (r < 0)
888                 return bus_log_parse_error(r);
889
890         r = sd_bus_message_exit_container(m);
891         if (r < 0)
892                 return bus_log_parse_error(r);
893
894         unit_file_dump_changes(0, NULL, *changes, *n_changes, false);
895         return 0;
896 }
897
898 struct CGroupInfo {
899         char *cgroup_path;
900         bool is_const; /* If false, cgroup_path should be free()'d */
901
902         Hashmap *pids; /* PID → process name */
903         bool done;
904
905         struct CGroupInfo *parent;
906         LIST_FIELDS(struct CGroupInfo, siblings);
907         LIST_HEAD(struct CGroupInfo, children);
908         size_t n_children;
909 };
910
911 static bool IS_ROOT(const char *p) {
912         return isempty(p) || streq(p, "/");
913 }
914
915 static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) {
916         struct CGroupInfo *parent = NULL, *cg;
917         int r;
918
919         assert(cgroups);
920         assert(ret);
921
922         if (IS_ROOT(path))
923                 path = "/";
924
925         cg = hashmap_get(cgroups, path);
926         if (cg) {
927                 *ret = cg;
928                 return 0;
929         }
930
931         if (!IS_ROOT(path)) {
932                 const char *e, *pp;
933
934                 e = strrchr(path, '/');
935                 if (!e)
936                         return -EINVAL;
937
938                 pp = strndupa(path, e - path);
939                 if (!pp)
940                         return -ENOMEM;
941
942                 r = add_cgroup(cgroups, pp, false, &parent);
943                 if (r < 0)
944                         return r;
945         }
946
947         cg = new0(struct CGroupInfo, 1);
948         if (!cg)
949                 return -ENOMEM;
950
951         if (is_const)
952                 cg->cgroup_path = (char*) path;
953         else {
954                 cg->cgroup_path = strdup(path);
955                 if (!cg->cgroup_path) {
956                         free(cg);
957                         return -ENOMEM;
958                 }
959         }
960
961         cg->is_const = is_const;
962         cg->parent = parent;
963
964         r = hashmap_put(cgroups, cg->cgroup_path, cg);
965         if (r < 0) {
966                 if (!is_const)
967                         free(cg->cgroup_path);
968                 free(cg);
969                 return r;
970         }
971
972         if (parent) {
973                 LIST_PREPEND(siblings, parent->children, cg);
974                 parent->n_children++;
975         }
976
977         *ret = cg;
978         return 1;
979 }
980
981 static int add_process(
982                 Hashmap *cgroups,
983                 const char *path,
984                 pid_t pid,
985                 const char *name) {
986
987         struct CGroupInfo *cg;
988         int r;
989
990         assert(cgroups);
991         assert(name);
992         assert(pid > 0);
993
994         r = add_cgroup(cgroups, path, true, &cg);
995         if (r < 0)
996                 return r;
997
998         r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops);
999         if (r < 0)
1000                 return r;
1001
1002         return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name);
1003 }
1004
1005 static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) {
1006         assert(cgroups);
1007         assert(cg);
1008
1009         while (cg->children)
1010                 remove_cgroup(cgroups, cg->children);
1011
1012         hashmap_remove(cgroups, cg->cgroup_path);
1013
1014         if (!cg->is_const)
1015                 free(cg->cgroup_path);
1016
1017         hashmap_free(cg->pids);
1018
1019         if (cg->parent)
1020                 LIST_REMOVE(siblings, cg->parent->children, cg);
1021
1022         free(cg);
1023 }
1024
1025 static int cgroup_info_compare_func(const void *a, const void *b) {
1026         const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b;
1027
1028         assert(x);
1029         assert(y);
1030
1031         return strcmp(x->cgroup_path, y->cgroup_path);
1032 }
1033
1034 static int dump_processes(
1035                 Hashmap *cgroups,
1036                 const char *cgroup_path,
1037                 const char *prefix,
1038                 unsigned n_columns,
1039                 OutputFlags flags) {
1040
1041         struct CGroupInfo *cg;
1042         int r;
1043
1044         assert(prefix);
1045
1046         if (IS_ROOT(cgroup_path))
1047                 cgroup_path = "/";
1048
1049         cg = hashmap_get(cgroups, cgroup_path);
1050         if (!cg)
1051                 return 0;
1052
1053         if (!hashmap_isempty(cg->pids)) {
1054                 const char *name;
1055                 size_t n = 0, i;
1056                 pid_t *pids;
1057                 void *pidp;
1058                 Iterator j;
1059                 int width;
1060
1061                 /* Order processes by their PID */
1062                 pids = newa(pid_t, hashmap_size(cg->pids));
1063
1064                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j)
1065                         pids[n++] = PTR_TO_PID(pidp);
1066
1067                 assert(n == hashmap_size(cg->pids));
1068                 qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1069
1070                 width = DECIMAL_STR_WIDTH(pids[n-1]);
1071
1072                 for (i = 0; i < n; i++) {
1073                         _cleanup_free_ char *e = NULL;
1074                         const char *special;
1075                         bool more;
1076
1077                         name = hashmap_get(cg->pids, PID_TO_PTR(pids[i]));
1078                         assert(name);
1079
1080                         if (n_columns != 0) {
1081                                 unsigned k;
1082
1083                                 k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1084
1085                                 e = ellipsize(name, k, 100);
1086                                 if (e)
1087                                         name = e;
1088                         }
1089
1090                         more = i+1 < n || cg->children;
1091                         special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1092
1093                         fprintf(stdout, "%s%s%*"PID_PRI" %s\n",
1094                                 prefix,
1095                                 special,
1096                                 width, pids[i],
1097                                 name);
1098                 }
1099         }
1100
1101         if (cg->children) {
1102                 struct CGroupInfo **children, *child;
1103                 size_t n = 0, i;
1104
1105                 /* Order subcgroups by their name */
1106                 children = newa(struct CGroupInfo*, cg->n_children);
1107                 LIST_FOREACH(siblings, child, cg->children)
1108                         children[n++] = child;
1109                 assert(n == cg->n_children);
1110                 qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func);
1111
1112                 n_columns = MAX(LESS_BY(n_columns, 2U), 20U);
1113
1114                 for (i = 0; i < n; i++) {
1115                         _cleanup_free_ char *pp = NULL;
1116                         const char *name, *special;
1117                         bool more;
1118
1119                         child = children[i];
1120
1121                         name = strrchr(child->cgroup_path, '/');
1122                         if (!name)
1123                                 return -EINVAL;
1124                         name++;
1125
1126                         more = i+1 < n;
1127                         special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT);
1128
1129                         fputs(prefix, stdout);
1130                         fputs(special, stdout);
1131                         fputs(name, stdout);
1132                         fputc('\n', stdout);
1133
1134                         special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE);
1135
1136                         pp = strappend(prefix, special);
1137                         if (!pp)
1138                                 return -ENOMEM;
1139
1140                         r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags);
1141                         if (r < 0)
1142                                 return r;
1143                 }
1144         }
1145
1146         cg->done = true;
1147         return 0;
1148 }
1149
1150 static int dump_extra_processes(
1151                 Hashmap *cgroups,
1152                 const char *prefix,
1153                 unsigned n_columns,
1154                 OutputFlags flags) {
1155
1156         _cleanup_free_ pid_t *pids = NULL;
1157         _cleanup_hashmap_free_ Hashmap *names = NULL;
1158         struct CGroupInfo *cg;
1159         size_t n_allocated = 0, n = 0, k;
1160         Iterator i;
1161         int width, r;
1162
1163         /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1164          * combined, sorted, linear list. */
1165
1166         HASHMAP_FOREACH(cg, cgroups, i) {
1167                 const char *name;
1168                 void *pidp;
1169                 Iterator j;
1170
1171                 if (cg->done)
1172                         continue;
1173
1174                 if (hashmap_isempty(cg->pids))
1175                         continue;
1176
1177                 r = hashmap_ensure_allocated(&names, &trivial_hash_ops);
1178                 if (r < 0)
1179                         return r;
1180
1181                 if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids)))
1182                         return -ENOMEM;
1183
1184                 HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) {
1185                         pids[n++] = PTR_TO_PID(pidp);
1186
1187                         r = hashmap_put(names, pidp, (void*) name);
1188                         if (r < 0)
1189                                 return r;
1190                 }
1191         }
1192
1193         if (n == 0)
1194                 return 0;
1195
1196         qsort_safe(pids, n, sizeof(pid_t), pid_compare_func);
1197         width = DECIMAL_STR_WIDTH(pids[n-1]);
1198
1199         for (k = 0; k < n; k++) {
1200                 _cleanup_free_ char *e = NULL;
1201                 const char *name;
1202
1203                 name = hashmap_get(names, PID_TO_PTR(pids[k]));
1204                 assert(name);
1205
1206                 if (n_columns != 0) {
1207                         unsigned z;
1208
1209                         z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U);
1210
1211                         e = ellipsize(name, z, 100);
1212                         if (e)
1213                                 name = e;
1214                 }
1215
1216                 fprintf(stdout, "%s%s %*" PID_PRI " %s\n",
1217                         prefix,
1218                         special_glyph(TRIANGULAR_BULLET),
1219                         width, pids[k],
1220                         name);
1221         }
1222
1223         return 0;
1224 }
1225
1226 int unit_show_processes(
1227                 sd_bus *bus,
1228                 const char *unit,
1229                 const char *cgroup_path,
1230                 const char *prefix,
1231                 unsigned n_columns,
1232                 OutputFlags flags,
1233                 sd_bus_error *error) {
1234
1235         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1236         Hashmap *cgroups = NULL;
1237         struct CGroupInfo *cg;
1238         int r;
1239
1240         assert(bus);
1241         assert(unit);
1242
1243         if (flags & OUTPUT_FULL_WIDTH)
1244                 n_columns = 0;
1245         else if (n_columns <= 0)
1246                 n_columns = columns();
1247
1248         prefix = strempty(prefix);
1249
1250         r = sd_bus_call_method(
1251                         bus,
1252                         "org.freedesktop.elogind1",
1253                         "/org/freedesktop/elogind1",
1254                         "org.freedesktop.elogind1.Manager",
1255                         "GetUnitProcesses",
1256                         error,
1257                         &reply,
1258                         "s",
1259                         unit);
1260         if (r < 0)
1261                 return r;
1262
1263         cgroups = hashmap_new(&string_hash_ops);
1264         if (!cgroups)
1265                 return -ENOMEM;
1266
1267         r = sd_bus_message_enter_container(reply, 'a', "(sus)");
1268         if (r < 0)
1269                 goto finish;
1270
1271         for (;;) {
1272                 const char *path = NULL, *name = NULL;
1273                 uint32_t pid;
1274
1275                 r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name);
1276                 if (r < 0)
1277                         goto finish;
1278                 if (r == 0)
1279                         break;
1280
1281                 r = add_process(cgroups, path, pid, name);
1282                 if (r < 0)
1283                         goto finish;
1284         }
1285
1286         r = sd_bus_message_exit_container(reply);
1287         if (r < 0)
1288                 goto finish;
1289
1290         r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags);
1291         if (r < 0)
1292                 goto finish;
1293
1294         r = dump_extra_processes(cgroups, prefix, n_columns, flags);
1295
1296 finish:
1297         while ((cg = hashmap_first(cgroups)))
1298                remove_cgroup(cgroups, cg);
1299
1300         hashmap_free(cgroups);
1301
1302         return r;
1303 }