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