chiark / gitweb /
a62f517dc63dd1003a2476ab319eb21db0faebc6
[elogind.git] / src / core / dbus-execute.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/prctl.h>
23
24 #ifdef HAVE_SECCOMP
25 #include <seccomp.h>
26 #endif
27
28 #include "bus-util.h"
29 #include "missing.h"
30 #include "ioprio.h"
31 #include "strv.h"
32 #include "fileio.h"
33 #include "execute.h"
34 #include "dbus-execute.h"
35 #include "capability.h"
36 #include "env-util.h"
37
38 #ifdef HAVE_SECCOMP
39 #include "seccomp-util.h"
40 #endif
41
42 BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
43
44 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
45
46 static int property_get_environment_files(
47                 sd_bus *bus,
48                 const char *path,
49                 const char *interface,
50                 const char *property,
51                 sd_bus_message *reply,
52                 void *userdata,
53                 sd_bus_error *error) {
54
55         ExecContext *c = userdata;
56         char **j;
57         int r;
58
59         assert(bus);
60         assert(reply);
61         assert(c);
62
63         r = sd_bus_message_open_container(reply, 'a', "(sb)");
64         if (r < 0)
65                 return r;
66
67         STRV_FOREACH(j, c->environment_files) {
68                 const char *fn = *j;
69
70                 r = sd_bus_message_append(reply, "(sb)", fn[0] == '-' ? fn + 1 : fn, fn[0] == '-');
71                 if (r < 0)
72                         return r;
73         }
74
75         return sd_bus_message_close_container(reply);
76 }
77
78 static int property_get_rlimit(
79                 sd_bus *bus,
80                 const char *path,
81                 const char *interface,
82                 const char *property,
83                 sd_bus_message *reply,
84                 void *userdata,
85                 sd_bus_error *error) {
86
87         struct rlimit *rl;
88         uint64_t u;
89
90         assert(bus);
91         assert(reply);
92         assert(userdata);
93
94         rl = *(struct rlimit**) userdata;
95         if (rl)
96                 u = (uint64_t) rl->rlim_max;
97         else {
98                 struct rlimit buf = {};
99                 int z;
100
101                 z = rlimit_from_string(property);
102                 assert(z >= 0);
103
104                 getrlimit(z, &buf);
105
106                 u = (uint64_t) buf.rlim_max;
107         }
108
109         return sd_bus_message_append(reply, "t", u);
110 }
111
112 static int property_get_oom_score_adjust(
113                 sd_bus *bus,
114                 const char *path,
115                 const char *interface,
116                 const char *property,
117                 sd_bus_message *reply,
118                 void *userdata,
119                 sd_bus_error *error) {
120
121
122         ExecContext *c = userdata;
123         int32_t n;
124
125         assert(bus);
126         assert(reply);
127         assert(c);
128
129         if (c->oom_score_adjust_set)
130                 n = c->oom_score_adjust;
131         else {
132                 _cleanup_free_ char *t = NULL;
133
134                 n = 0;
135                 if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0)
136                         safe_atoi(t, &n);
137         }
138
139         return sd_bus_message_append(reply, "i", n);
140 }
141
142 static int property_get_nice(
143                 sd_bus *bus,
144                 const char *path,
145                 const char *interface,
146                 const char *property,
147                 sd_bus_message *reply,
148                 void *userdata,
149                 sd_bus_error *error) {
150
151
152         ExecContext *c = userdata;
153         int32_t n;
154
155         assert(bus);
156         assert(reply);
157         assert(c);
158
159         if (c->nice_set)
160                 n = c->nice;
161         else {
162                 errno = 0;
163                 n = getpriority(PRIO_PROCESS, 0);
164                 if (errno != 0)
165                         n = 0;
166         }
167
168         return sd_bus_message_append(reply, "i", n);
169 }
170
171 static int property_get_ioprio(
172                 sd_bus *bus,
173                 const char *path,
174                 const char *interface,
175                 const char *property,
176                 sd_bus_message *reply,
177                 void *userdata,
178                 sd_bus_error *error) {
179
180
181         ExecContext *c = userdata;
182         int32_t n;
183
184         assert(bus);
185         assert(reply);
186         assert(c);
187
188         if (c->ioprio_set)
189                 n = c->ioprio;
190         else {
191                 n = ioprio_get(IOPRIO_WHO_PROCESS, 0);
192                 if (n < 0)
193                         n = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4);
194         }
195
196         return sd_bus_message_append(reply, "i", n);
197 }
198
199 static int property_get_cpu_sched_policy(
200                 sd_bus *bus,
201                 const char *path,
202                 const char *interface,
203                 const char *property,
204                 sd_bus_message *reply,
205                 void *userdata,
206                 sd_bus_error *error) {
207
208         ExecContext *c = userdata;
209         int32_t n;
210
211         assert(bus);
212         assert(reply);
213         assert(c);
214
215         if (c->cpu_sched_set)
216                 n = c->cpu_sched_policy;
217         else {
218                 n = sched_getscheduler(0);
219                 if (n < 0)
220                         n = SCHED_OTHER;
221         }
222
223         return sd_bus_message_append(reply, "i", n);
224 }
225
226 static int property_get_cpu_sched_priority(
227                 sd_bus *bus,
228                 const char *path,
229                 const char *interface,
230                 const char *property,
231                 sd_bus_message *reply,
232                 void *userdata,
233                 sd_bus_error *error) {
234
235         ExecContext *c = userdata;
236         int32_t n;
237
238         assert(bus);
239         assert(reply);
240         assert(c);
241
242         if (c->cpu_sched_set)
243                 n = c->cpu_sched_priority;
244         else {
245                 struct sched_param p = {};
246
247                 if (sched_getparam(0, &p) >= 0)
248                         n = p.sched_priority;
249                 else
250                         n = 0;
251         }
252
253         return sd_bus_message_append(reply, "i", n);
254 }
255
256 static int property_get_cpu_affinity(
257                 sd_bus *bus,
258                 const char *path,
259                 const char *interface,
260                 const char *property,
261                 sd_bus_message *reply,
262                 void *userdata,
263                 sd_bus_error *error) {
264
265         ExecContext *c = userdata;
266
267         assert(bus);
268         assert(reply);
269         assert(c);
270
271         if (c->cpuset)
272                 return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus));
273         else
274                 return sd_bus_message_append_array(reply, 'y', NULL, 0);
275 }
276
277 static int property_get_timer_slack_nsec(
278                 sd_bus *bus,
279                 const char *path,
280                 const char *interface,
281                 const char *property,
282                 sd_bus_message *reply,
283                 void *userdata,
284                 sd_bus_error *error) {
285
286         ExecContext *c = userdata;
287         uint64_t u;
288
289         assert(bus);
290         assert(reply);
291         assert(c);
292
293         if (c->timer_slack_nsec != (nsec_t) -1)
294                 u = (uint64_t) c->timer_slack_nsec;
295         else
296                 u = (uint64_t) prctl(PR_GET_TIMERSLACK);
297
298         return sd_bus_message_append(reply, "t", u);
299 }
300
301 static int property_get_capability_bounding_set(
302                 sd_bus *bus,
303                 const char *path,
304                 const char *interface,
305                 const char *property,
306                 sd_bus_message *reply,
307                 void *userdata,
308                 sd_bus_error *error) {
309
310         ExecContext *c = userdata;
311
312         assert(bus);
313         assert(reply);
314         assert(c);
315
316         /* We store this negated internally, to match the kernel, but
317          * we expose it normalized. */
318         return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
319 }
320
321 static int property_get_capabilities(
322                 sd_bus *bus,
323                 const char *path,
324                 const char *interface,
325                 const char *property,
326                 sd_bus_message *reply,
327                 void *userdata,
328                 sd_bus_error *error) {
329
330         ExecContext *c = userdata;
331         _cleanup_cap_free_charp_ char *t = NULL;
332         const char *s;
333
334         assert(bus);
335         assert(reply);
336         assert(c);
337
338         if (c->capabilities)
339                 s = t = cap_to_text(c->capabilities, NULL);
340         else
341                 s = "";
342
343         if (!s)
344                 return -ENOMEM;
345
346         return sd_bus_message_append(reply, "s", s);
347 }
348
349 static int property_get_syscall_filter(
350                 sd_bus *bus,
351                 const char *path,
352                 const char *interface,
353                 const char *property,
354                 sd_bus_message *reply,
355                 void *userdata,
356                 sd_bus_error *error) {
357
358         ExecContext *c = userdata;
359         _cleanup_strv_free_ char **l = NULL;
360         int r;
361
362 #ifdef HAVE_SECCOMP
363         Iterator i;
364         void *id;
365 #endif
366
367         assert(bus);
368         assert(reply);
369         assert(c);
370
371         r = sd_bus_message_open_container(reply, 'r', "bas");
372         if (r < 0)
373                 return r;
374
375         r = sd_bus_message_append(reply, "b", c->syscall_whitelist);
376         if (r < 0)
377                 return r;
378
379 #ifdef HAVE_SECCOMP
380         SET_FOREACH(id, c->syscall_filter, i) {
381                 char *name;
382
383                 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
384                 if (!name)
385                         continue;
386
387                 r = strv_push(&l, name);
388                 if (r < 0) {
389                         free(name);
390                         return -ENOMEM;
391                 }
392         }
393 #endif
394
395         strv_sort(l);
396
397         r = sd_bus_message_append_strv(reply, l);
398         if (r < 0)
399                 return r;
400
401         return sd_bus_message_close_container(reply);
402 }
403
404 static int property_get_syscall_archs(
405                 sd_bus *bus,
406                 const char *path,
407                 const char *interface,
408                 const char *property,
409                 sd_bus_message *reply,
410                 void *userdata,
411                 sd_bus_error *error) {
412
413         ExecContext *c = userdata;
414         _cleanup_strv_free_ char **l = NULL;
415         int r;
416
417 #ifdef HAVE_SECCOMP
418         Iterator i;
419         void *id;
420 #endif
421
422         assert(bus);
423         assert(reply);
424         assert(c);
425
426 #ifdef HAVE_SECCOMP
427         SET_FOREACH(id, c->syscall_archs, i) {
428                 const char *name;
429
430                 name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
431                 if (!name)
432                         continue;
433
434                 r = strv_extend(&l, name);
435                 if (r < 0)
436                         return -ENOMEM;
437         }
438 #endif
439
440         strv_sort(l);
441
442         r = sd_bus_message_append_strv(reply, l);
443         if (r < 0)
444                 return r;
445
446         return 0;
447 }
448
449 static int property_get_syscall_errno(
450                 sd_bus *bus,
451                 const char *path,
452                 const char *interface,
453                 const char *property,
454                 sd_bus_message *reply,
455                 void *userdata,
456                 sd_bus_error *error) {
457
458         ExecContext *c = userdata;
459
460         assert(bus);
461         assert(reply);
462         assert(c);
463
464         return sd_bus_message_append(reply, "i", (int32_t) c->syscall_errno);
465 }
466
467 const sd_bus_vtable bus_exec_vtable[] = {
468         SD_BUS_VTABLE_START(0),
469         SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
470         SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
471         SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
472         SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
473         SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
474         SD_BUS_PROPERTY("LimitDATA", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
475         SD_BUS_PROPERTY("LimitSTACK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
476         SD_BUS_PROPERTY("LimitCORE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
477         SD_BUS_PROPERTY("LimitRSS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
478         SD_BUS_PROPERTY("LimitNOFILE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
479         SD_BUS_PROPERTY("LimitAS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
480         SD_BUS_PROPERTY("LimitNPROC", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
481         SD_BUS_PROPERTY("LimitMEMLOCK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
482         SD_BUS_PROPERTY("LimitLOCKS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
483         SD_BUS_PROPERTY("LimitSIGPENDING", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
484         SD_BUS_PROPERTY("LimitMSGQUEUE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
485         SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
486         SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
487         SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
488         SD_BUS_PROPERTY("WorkingDirectory", "s", NULL, offsetof(ExecContext, working_directory), SD_BUS_VTABLE_PROPERTY_CONST),
489         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
490         SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
491         SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
492         SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST),
493         SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST),
494         SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST),
495         SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST),
496         SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
497         SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST),
498         SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST),
499         SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST),
500         SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST),
501         SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST),
502         SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST),
503         SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST),
504         SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST),
505         SD_BUS_PROPERTY("TTYVTDisallocate", "b", bus_property_get_bool, offsetof(ExecContext, tty_vt_disallocate), SD_BUS_VTABLE_PROPERTY_CONST),
506         SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
507         SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST),
508         SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST),
509         SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
510         SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
511         SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
512         SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
513         SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
514         SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
515         SD_BUS_PROPERTY("TCPWrapName", "s", NULL, offsetof(ExecContext, tcpwrap_name), SD_BUS_VTABLE_PROPERTY_CONST),
516         SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
517         SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
518         SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
519         SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
520         SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST),
521         SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
522         SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
523         SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
524         SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
525         SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
526         SD_BUS_PROPERTY("SELinuxContext", "s", NULL, offsetof(ExecContext, selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
527         SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST),
528         SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST),
529         SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
530         SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST),
531         SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST),
532         SD_BUS_VTABLE_END
533 };
534
535 static int append_exec_command(sd_bus_message *reply, ExecCommand *c) {
536         int r;
537
538         assert(reply);
539         assert(c);
540
541         if (!c->path)
542                 return 0;
543
544         r = sd_bus_message_open_container(reply, 'r', "sasbttttuii");
545         if (r < 0)
546                 return r;
547
548         r = sd_bus_message_append(reply, "s", c->path);
549         if (r < 0)
550                 return r;
551
552         r = sd_bus_message_append_strv(reply, c->argv);
553         if (r < 0)
554                 return r;
555
556         r = sd_bus_message_append(reply, "bttttuii",
557                                   c->ignore,
558                                   c->exec_status.start_timestamp.realtime,
559                                   c->exec_status.start_timestamp.monotonic,
560                                   c->exec_status.exit_timestamp.realtime,
561                                   c->exec_status.exit_timestamp.monotonic,
562                                   (uint32_t) c->exec_status.pid,
563                                   (int32_t) c->exec_status.code,
564                                   (int32_t) c->exec_status.status);
565         if (r < 0)
566                 return r;
567
568         return sd_bus_message_close_container(reply);
569 }
570
571 int bus_property_get_exec_command(
572                 sd_bus *bus,
573                 const char *path,
574                 const char *interface,
575                 const char *property,
576                 sd_bus_message *reply,
577                 void *userdata,
578                 sd_bus_error *ret_error) {
579
580         ExecCommand *c = (ExecCommand*) userdata;
581         int r;
582
583         assert(bus);
584         assert(reply);
585
586         r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)");
587         if (r < 0)
588                 return r;
589
590         r = append_exec_command(reply, c);
591         if (r < 0)
592                 return r;
593
594         return sd_bus_message_close_container(reply);
595 }
596
597 int bus_property_get_exec_command_list(
598                 sd_bus *bus,
599                 const char *path,
600                 const char *interface,
601                 const char *property,
602                 sd_bus_message *reply,
603                 void *userdata,
604                 sd_bus_error *ret_error) {
605
606         ExecCommand *c = *(ExecCommand**) userdata;
607         int r;
608
609         assert(bus);
610         assert(reply);
611
612         r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)");
613         if (r < 0)
614                 return r;
615
616         LIST_FOREACH(command, c, c) {
617                 r = append_exec_command(reply, c);
618                 if (r < 0)
619                         return r;
620         }
621
622         return sd_bus_message_close_container(reply);
623 }
624
625 int bus_exec_context_set_transient_property(
626                 Unit *u,
627                 ExecContext *c,
628                 const char *name,
629                 sd_bus_message *message,
630                 UnitSetPropertiesMode mode,
631                 sd_bus_error *error) {
632
633         int r;
634
635         assert(u);
636         assert(c);
637         assert(name);
638         assert(message);
639
640         if (streq(name, "User")) {
641                 const char *uu;
642
643                 r = sd_bus_message_read(message, "s", &uu);
644                 if (r < 0)
645                         return r;
646
647                 if (mode != UNIT_CHECK) {
648
649                         if (isempty(uu)) {
650                                 free(c->user);
651                                 c->user = NULL;
652                         } else {
653                                 char *t;
654
655                                 t = strdup(uu);
656                                 if (!t)
657                                         return -ENOMEM;
658
659                                 free(c->user);
660                                 c->user = t;
661                         }
662
663                         unit_write_drop_in_private_format(u, mode, name, "User=%s\n", uu);
664                 }
665
666                 return 1;
667
668         } else if (streq(name, "Group")) {
669                 const char *gg;
670
671                 r = sd_bus_message_read(message, "s", &gg);
672                 if (r < 0)
673                         return r;
674
675                 if (mode != UNIT_CHECK) {
676
677                         if (isempty(gg)) {
678                                 free(c->group);
679                                 c->group = NULL;
680                         } else {
681                                 char *t;
682
683                                 t = strdup(gg);
684                                 if (!t)
685                                         return -ENOMEM;
686
687                                 free(c->group);
688                                 c->group = t;
689                         }
690
691                         unit_write_drop_in_private_format(u, mode, name, "Group=%s\n", gg);
692                 }
693
694                 return 1;
695
696         } else if (streq(name, "Nice")) {
697                 int n;
698
699                 r = sd_bus_message_read(message, "i", &n);
700                 if (r < 0)
701                         return r;
702
703                 if (n < PRIO_MIN || n >= PRIO_MAX)
704                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range");
705
706                 if (mode != UNIT_CHECK) {
707                         c->nice = n;
708                         unit_write_drop_in_private_format(u, mode, name, "Nice=%i\n", n);
709                 }
710
711                 return 1;
712
713         } else if (streq(name, "Environment")) {
714
715                 _cleanup_strv_free_ char **l = NULL;
716
717                 r = sd_bus_message_read_strv(message, &l);
718                 if (r < 0)
719                         return r;
720
721                 if (!strv_env_is_valid(l))
722                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
723
724                 if (mode != UNIT_CHECK) {
725                         _cleanup_free_ char *joined = NULL;
726                         char **e;
727
728                         e = strv_env_merge(2, c->environment, l);
729                         if (!e)
730                                 return -ENOMEM;
731
732                         strv_free(c->environment);
733                         c->environment = e;
734
735                         joined = strv_join(c->environment, " ");
736                         if (!joined)
737                                 return -ENOMEM;
738
739                         unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined);
740                 }
741
742                 return 1;
743         }
744
745         return 0;
746 }