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