c65da5051e936a47046582144bb46db0519dd7f6
[project/procd.git] / service / instance.c
1 /*
2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #define _GNU_SOURCE
16 #include <sys/resource.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <grp.h>
21 #include <net/if.h>
22 #include <unistd.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <pwd.h>
27 #include <libgen.h>
28 #include <unistd.h>
29 #define SYSLOG_NAMES
30 #include <syslog.h>
31
32 #include <libubox/md5.h>
33
34 #include "../procd.h"
35
36 #include "service.h"
37 #include "instance.h"
38
39 #define UJAIL_BIN_PATH "/sbin/ujail"
40
41 enum {
42 INSTANCE_ATTR_COMMAND,
43 INSTANCE_ATTR_ENV,
44 INSTANCE_ATTR_DATA,
45 INSTANCE_ATTR_NETDEV,
46 INSTANCE_ATTR_FILE,
47 INSTANCE_ATTR_TRIGGER,
48 INSTANCE_ATTR_RESPAWN,
49 INSTANCE_ATTR_NICE,
50 INSTANCE_ATTR_LIMITS,
51 INSTANCE_ATTR_WATCH,
52 INSTANCE_ATTR_ERROR,
53 INSTANCE_ATTR_USER,
54 INSTANCE_ATTR_GROUP,
55 INSTANCE_ATTR_STDOUT,
56 INSTANCE_ATTR_STDERR,
57 INSTANCE_ATTR_NO_NEW_PRIVS,
58 INSTANCE_ATTR_JAIL,
59 INSTANCE_ATTR_TRACE,
60 INSTANCE_ATTR_SECCOMP,
61 INSTANCE_ATTR_PIDFILE,
62 INSTANCE_ATTR_RELOADSIG,
63 INSTANCE_ATTR_TERMTIMEOUT,
64 INSTANCE_ATTR_FACILITY,
65 INSTANCE_ATTR_EXTROOT,
66 INSTANCE_ATTR_OVERLAYDIR,
67 INSTANCE_ATTR_TMPOVERLAYSIZE,
68 INSTANCE_ATTR_BUNDLE,
69 __INSTANCE_ATTR_MAX
70 };
71
72 static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = {
73 [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY },
74 [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE },
75 [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE },
76 [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY },
77 [INSTANCE_ATTR_FILE] = { "file", BLOBMSG_TYPE_ARRAY },
78 [INSTANCE_ATTR_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY },
79 [INSTANCE_ATTR_RESPAWN] = { "respawn", BLOBMSG_TYPE_ARRAY },
80 [INSTANCE_ATTR_NICE] = { "nice", BLOBMSG_TYPE_INT32 },
81 [INSTANCE_ATTR_LIMITS] = { "limits", BLOBMSG_TYPE_TABLE },
82 [INSTANCE_ATTR_WATCH] = { "watch", BLOBMSG_TYPE_ARRAY },
83 [INSTANCE_ATTR_ERROR] = { "error", BLOBMSG_TYPE_ARRAY },
84 [INSTANCE_ATTR_USER] = { "user", BLOBMSG_TYPE_STRING },
85 [INSTANCE_ATTR_GROUP] = { "group", BLOBMSG_TYPE_STRING },
86 [INSTANCE_ATTR_STDOUT] = { "stdout", BLOBMSG_TYPE_BOOL },
87 [INSTANCE_ATTR_STDERR] = { "stderr", BLOBMSG_TYPE_BOOL },
88 [INSTANCE_ATTR_NO_NEW_PRIVS] = { "no_new_privs", BLOBMSG_TYPE_BOOL },
89 [INSTANCE_ATTR_JAIL] = { "jail", BLOBMSG_TYPE_TABLE },
90 [INSTANCE_ATTR_TRACE] = { "trace", BLOBMSG_TYPE_BOOL },
91 [INSTANCE_ATTR_SECCOMP] = { "seccomp", BLOBMSG_TYPE_STRING },
92 [INSTANCE_ATTR_PIDFILE] = { "pidfile", BLOBMSG_TYPE_STRING },
93 [INSTANCE_ATTR_RELOADSIG] = { "reload_signal", BLOBMSG_TYPE_INT32 },
94 [INSTANCE_ATTR_TERMTIMEOUT] = { "term_timeout", BLOBMSG_TYPE_INT32 },
95 [INSTANCE_ATTR_FACILITY] = { "facility", BLOBMSG_TYPE_STRING },
96 [INSTANCE_ATTR_EXTROOT] = { "extroot", BLOBMSG_TYPE_STRING },
97 [INSTANCE_ATTR_OVERLAYDIR] = { "overlaydir", BLOBMSG_TYPE_STRING },
98 [INSTANCE_ATTR_TMPOVERLAYSIZE] = { "tmpoverlaysize", BLOBMSG_TYPE_STRING },
99 [INSTANCE_ATTR_BUNDLE] = { "bundle", BLOBMSG_TYPE_STRING },
100 };
101
102 enum {
103 JAIL_ATTR_NAME,
104 JAIL_ATTR_HOSTNAME,
105 JAIL_ATTR_PROCFS,
106 JAIL_ATTR_SYSFS,
107 JAIL_ATTR_UBUS,
108 JAIL_ATTR_LOG,
109 JAIL_ATTR_RONLY,
110 JAIL_ATTR_MOUNT,
111 JAIL_ATTR_NETNS,
112 JAIL_ATTR_USERNS,
113 JAIL_ATTR_CGROUPSNS,
114 JAIL_ATTR_CONSOLE,
115 JAIL_ATTR_REQUIREJAIL,
116 __JAIL_ATTR_MAX,
117 };
118
119 static const struct blobmsg_policy jail_attr[__JAIL_ATTR_MAX] = {
120 [JAIL_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
121 [JAIL_ATTR_HOSTNAME] = { "hostname", BLOBMSG_TYPE_STRING },
122 [JAIL_ATTR_PROCFS] = { "procfs", BLOBMSG_TYPE_BOOL },
123 [JAIL_ATTR_SYSFS] = { "sysfs", BLOBMSG_TYPE_BOOL },
124 [JAIL_ATTR_UBUS] = { "ubus", BLOBMSG_TYPE_BOOL },
125 [JAIL_ATTR_LOG] = { "log", BLOBMSG_TYPE_BOOL },
126 [JAIL_ATTR_RONLY] = { "ronly", BLOBMSG_TYPE_BOOL },
127 [JAIL_ATTR_MOUNT] = { "mount", BLOBMSG_TYPE_TABLE },
128 [JAIL_ATTR_NETNS] = { "netns", BLOBMSG_TYPE_BOOL },
129 [JAIL_ATTR_USERNS] = { "userns", BLOBMSG_TYPE_BOOL },
130 [JAIL_ATTR_CGROUPSNS] = { "cgroupsns", BLOBMSG_TYPE_BOOL },
131 [JAIL_ATTR_CONSOLE] = { "console", BLOBMSG_TYPE_BOOL },
132 [JAIL_ATTR_REQUIREJAIL] = { "requirejail", BLOBMSG_TYPE_BOOL },
133 };
134
135 struct instance_netdev {
136 struct blobmsg_list_node node;
137 int ifindex;
138 };
139
140 struct instance_file {
141 struct blobmsg_list_node node;
142 uint32_t md5[4];
143 };
144
145 struct rlimit_name {
146 const char *name;
147 int resource;
148 };
149
150 static const struct rlimit_name rlimit_names[] = {
151 { "as", RLIMIT_AS },
152 { "core", RLIMIT_CORE },
153 { "cpu", RLIMIT_CPU },
154 { "data", RLIMIT_DATA },
155 { "fsize", RLIMIT_FSIZE },
156 { "memlock", RLIMIT_MEMLOCK },
157 { "nofile", RLIMIT_NOFILE },
158 { "nproc", RLIMIT_NPROC },
159 { "rss", RLIMIT_RSS },
160 { "stack", RLIMIT_STACK },
161 #ifdef linux
162 { "nice", RLIMIT_NICE },
163 { "rtprio", RLIMIT_RTPRIO },
164 { "msgqueue", RLIMIT_MSGQUEUE },
165 { "sigpending", RLIMIT_SIGPENDING },
166 #endif
167 { NULL, 0 }
168 };
169
170 static void closefd(int fd)
171 {
172 if (fd > STDERR_FILENO)
173 close(fd);
174 }
175
176 /* convert a string into numeric syslog facility or return -1 if no match found */
177 static int
178 syslog_facility_str_to_int(const char *facility)
179 {
180 CODE *p = facilitynames;
181
182 while (p->c_name && strcasecmp(p->c_name, facility))
183 p++;
184
185 return p->c_val;
186 }
187
188 static void
189 instance_limits(const char *limit, const char *value)
190 {
191 int i;
192 struct rlimit rlim;
193 unsigned long cur, max;
194
195 for (i = 0; rlimit_names[i].name != NULL; i++) {
196 if (strcmp(rlimit_names[i].name, limit))
197 continue;
198 if (!strcmp(value, "unlimited")) {
199 rlim.rlim_cur = RLIM_INFINITY;
200 rlim.rlim_max = RLIM_INFINITY;
201 } else {
202 if (getrlimit(rlimit_names[i].resource, &rlim))
203 return;
204
205 cur = rlim.rlim_cur;
206 max = rlim.rlim_max;
207
208 if (sscanf(value, "%lu %lu", &cur, &max) < 1)
209 return;
210
211 rlim.rlim_cur = cur;
212 rlim.rlim_max = max;
213 }
214
215 setrlimit(rlimit_names[i].resource, &rlim);
216 return;
217 }
218 }
219
220 static inline int
221 jail_run(struct service_instance *in, char **argv)
222 {
223 struct blobmsg_list_node *var;
224 struct jail *jail = &in->jail;
225 int argc = 0;
226
227 argv[argc++] = UJAIL_BIN_PATH;
228
229 if (jail->name) {
230 argv[argc++] = "-n";
231 argv[argc++] = jail->name;
232 }
233
234 if (jail->hostname) {
235 argv[argc++] = "-h";
236 argv[argc++] = jail->hostname;
237 }
238
239 if (in->seccomp) {
240 argv[argc++] = "-S";
241 argv[argc++] = in->seccomp;
242 }
243
244 if (in->user) {
245 argv[argc++] = "-U";
246 argv[argc++] = in->user;
247 }
248
249 if (in->group) {
250 argv[argc++] = "-G";
251 argv[argc++] = in->group;
252 }
253
254 if (in->no_new_privs)
255 argv[argc++] = "-c";
256
257 if (jail->procfs)
258 argv[argc++] = "-p";
259
260 if (jail->sysfs)
261 argv[argc++] = "-s";
262
263 if (jail->ubus)
264 argv[argc++] = "-u";
265
266 if (jail->log)
267 argv[argc++] = "-l";
268
269 if (jail->ronly)
270 argv[argc++] = "-o";
271
272 if (jail->netns)
273 argv[argc++] = "-N";
274
275 if (jail->userns)
276 argv[argc++] = "-f";
277
278 if (jail->cgroupsns)
279 argv[argc++] = "-F";
280
281 if (jail->console)
282 argv[argc++] = "-y";
283
284 if (in->extroot) {
285 argv[argc++] = "-R";
286 argv[argc++] = in->extroot;
287 }
288
289 if (in->overlaydir) {
290 argv[argc++] = "-O";
291 argv[argc++] = in->overlaydir;
292 }
293
294 if (in->tmpoverlaysize) {
295 argv[argc++] = "-T";
296 argv[argc++] = in->tmpoverlaysize;
297 }
298
299 if (in->bundle) {
300 argv[argc++] = "-J";
301 argv[argc++] = in->bundle;
302 }
303
304 if (in->require_jail)
305 argv[argc++] = "-E";
306
307 blobmsg_list_for_each(&jail->mount, var) {
308 const char *type = blobmsg_data(var->data);
309
310 if (*type == '1')
311 argv[argc++] = "-w";
312 else
313 argv[argc++] = "-r";
314 argv[argc++] = (char *) blobmsg_name(var->data);
315 }
316
317 argv[argc++] = "--";
318
319 return argc;
320 }
321
322 static int
323 instance_removepid(struct service_instance *in) {
324 if (!in->pidfile)
325 return 0;
326 if (unlink(in->pidfile)) {
327 ERROR("Failed to remove pidfile: %s: %m\n", in->pidfile);
328 return 1;
329 }
330 return 0;
331 }
332
333 static int
334 instance_writepid(struct service_instance *in)
335 {
336 FILE *_pidfile;
337
338 if (!in->pidfile) {
339 return 0;
340 }
341 _pidfile = fopen(in->pidfile, "w");
342 if (_pidfile == NULL) {
343 ERROR("failed to open pidfile for writing: %s: %m", in->pidfile);
344 return 1;
345 }
346 if (fprintf(_pidfile, "%d\n", in->proc.pid) < 0) {
347 ERROR("failed to write pidfile: %s: %m", in->pidfile);
348 fclose(_pidfile);
349 return 2;
350 }
351 if (fclose(_pidfile)) {
352 ERROR("failed to close pidfile: %s: %m", in->pidfile);
353 return 3;
354 }
355
356 return 0;
357 }
358
359 static void
360 instance_run(struct service_instance *in, int _stdout, int _stderr)
361 {
362 struct blobmsg_list_node *var;
363 struct blob_attr *cur;
364 char **argv;
365 int argc = 1; /* NULL terminated */
366 int rem, _stdin;
367 bool seccomp = !in->trace && !in->has_jail && in->seccomp;
368 bool setlbf = _stdout >= 0;
369
370 if (in->nice)
371 setpriority(PRIO_PROCESS, 0, in->nice);
372
373 blobmsg_for_each_attr(cur, in->command, rem)
374 argc++;
375
376 blobmsg_list_for_each(&in->env, var)
377 setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1);
378
379 if (seccomp)
380 setenv("SECCOMP_FILE", in->seccomp, 1);
381
382 if (setlbf)
383 setenv("LD_PRELOAD", "/lib/libsetlbf.so", 1);
384
385 blobmsg_list_for_each(&in->limits, var)
386 instance_limits(blobmsg_name(var->data), blobmsg_data(var->data));
387
388 if (in->trace || seccomp)
389 argc += 1;
390
391 argv = alloca(sizeof(char *) * (argc + in->jail.argc));
392 argc = 0;
393
394 #ifdef SECCOMP_SUPPORT
395 if (in->trace)
396 argv[argc++] = "/sbin/utrace";
397 else if (seccomp)
398 argv[argc++] = "/sbin/seccomp-trace";
399 #else
400 if (in->trace || seccomp)
401 ULOG_WARN("Seccomp support for %s::%s not available\n", in->srv->name, in->name);
402 #endif
403
404 if (in->has_jail) {
405 argc = jail_run(in, argv);
406 if (argc != in->jail.argc)
407 ULOG_WARN("expected %i jail params, used %i for %s::%s\n",
408 in->jail.argc, argc, in->srv->name, in->name);
409 }
410
411 blobmsg_for_each_attr(cur, in->command, rem)
412 argv[argc++] = blobmsg_data(cur);
413
414 argv[argc] = NULL;
415
416 _stdin = open("/dev/null", O_RDONLY);
417
418 if (_stdout == -1)
419 _stdout = open("/dev/null", O_WRONLY);
420
421 if (_stderr == -1)
422 _stderr = open("/dev/null", O_WRONLY);
423
424 if (_stdin > -1) {
425 dup2(_stdin, STDIN_FILENO);
426 closefd(_stdin);
427 }
428 if (_stdout > -1) {
429 dup2(_stdout, STDOUT_FILENO);
430 closefd(_stdout);
431 }
432 if (_stderr > -1) {
433 dup2(_stderr, STDERR_FILENO);
434 closefd(_stderr);
435 }
436
437 if (!in->has_jail && in->user && in->pw_gid && initgroups(in->user, in->pw_gid)) {
438 ERROR("failed to initgroups() for user %s: %m\n", in->user);
439 exit(127);
440 }
441 if (!in->has_jail && in->gr_gid && setgid(in->gr_gid)) {
442 ERROR("failed to set group id %d: %m\n", in->gr_gid);
443 exit(127);
444 }
445 if (!in->has_jail && in->uid && setuid(in->uid)) {
446 ERROR("failed to set user id %d: %m\n", in->uid);
447 exit(127);
448 }
449
450 execvp(argv[0], argv);
451 exit(127);
452 }
453
454 static void
455 instance_free_stdio(struct service_instance *in)
456 {
457 if (in->_stdout.fd.fd > -1) {
458 ustream_free(&in->_stdout.stream);
459 close(in->_stdout.fd.fd);
460 in->_stdout.fd.fd = -1;
461 }
462
463 if (in->_stderr.fd.fd > -1) {
464 ustream_free(&in->_stderr.stream);
465 close(in->_stderr.fd.fd);
466 in->_stderr.fd.fd = -1;
467 }
468
469 if (in->console.fd.fd > -1) {
470 ustream_free(&in->console.stream);
471 close(in->console.fd.fd);
472 in->console.fd.fd = -1;
473 }
474
475 if (in->console_client.fd.fd > -1) {
476 ustream_free(&in->console_client.stream);
477 close(in->console_client.fd.fd);
478 in->console_client.fd.fd = -1;
479 }
480 }
481
482 void
483 instance_start(struct service_instance *in)
484 {
485 int pid;
486 int opipe[2] = { -1, -1 };
487 int epipe[2] = { -1, -1 };
488
489 if (!avl_is_empty(&in->errors.avl)) {
490 LOG("Not starting instance %s::%s, an error was indicated\n", in->srv->name, in->name);
491 return;
492 }
493
494 if (!in->bundle && !in->command) {
495 LOG("Not starting instance %s::%s, command not set\n", in->srv->name, in->name);
496 return;
497 }
498
499 if (in->proc.pending) {
500 if (in->halt)
501 in->restart = true;
502 return;
503 }
504
505 instance_free_stdio(in);
506 if (in->_stdout.fd.fd > -2) {
507 if (pipe(opipe)) {
508 ULOG_WARN("pipe() failed: %m\n");
509 opipe[0] = opipe[1] = -1;
510 }
511 }
512
513 if (in->_stderr.fd.fd > -2) {
514 if (pipe(epipe)) {
515 ULOG_WARN("pipe() failed: %m\n");
516 epipe[0] = epipe[1] = -1;
517 }
518 }
519
520 in->restart = false;
521 in->halt = false;
522
523 if (!in->valid)
524 return;
525
526 pid = fork();
527 if (pid < 0)
528 return;
529
530 if (!pid) {
531 uloop_done();
532 closefd(opipe[0]);
533 closefd(epipe[0]);
534 instance_run(in, opipe[1], epipe[1]);
535 return;
536 }
537
538 DEBUG(2, "Started instance %s::%s[%d]\n", in->srv->name, in->name, pid);
539 in->proc.pid = pid;
540 instance_writepid(in);
541 clock_gettime(CLOCK_MONOTONIC, &in->start);
542 uloop_process_add(&in->proc);
543
544 if (opipe[0] > -1) {
545 ustream_fd_init(&in->_stdout, opipe[0]);
546 closefd(opipe[1]);
547 fcntl(opipe[0], F_SETFD, FD_CLOEXEC);
548 }
549
550 if (epipe[0] > -1) {
551 ustream_fd_init(&in->_stderr, epipe[0]);
552 closefd(epipe[1]);
553 fcntl(epipe[0], F_SETFD, FD_CLOEXEC);
554 }
555
556 service_event("instance.start", in->srv->name, in->name);
557 }
558
559 static void
560 instance_stdio(struct ustream *s, int prio, struct service_instance *in)
561 {
562 char *newline, *str, *arg0, ident[32];
563 int len;
564
565 arg0 = basename(blobmsg_data(blobmsg_data(in->command)));
566 snprintf(ident, sizeof(ident), "%s[%d]", arg0, in->proc.pid);
567 ulog_open(ULOG_SYSLOG, in->syslog_facility, ident);
568
569 do {
570 str = ustream_get_read_buf(s, &len);
571 if (!str)
572 break;
573
574 newline = memchr(str, '\n', len);
575 if (!newline && (s->r.buffer_len != len))
576 break;
577
578 if (newline) {
579 *newline = 0;
580 len = newline + 1 - str;
581 }
582 ulog(prio, "%s\n", str);
583
584 ustream_consume(s, len);
585 } while (1);
586
587 ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
588 }
589
590 static void
591 instance_stdout(struct ustream *s, int bytes)
592 {
593 instance_stdio(s, LOG_INFO,
594 container_of(s, struct service_instance, _stdout.stream));
595 }
596
597 static void
598 instance_console(struct ustream *s, int bytes)
599 {
600 struct service_instance *in = container_of(s, struct service_instance, console.stream);
601 char *buf;
602 int len;
603
604 do {
605 buf = ustream_get_read_buf(s, &len);
606 if (!buf)
607 break;
608
609 ulog(LOG_INFO, "out: %s\n", buf);
610
611 /* test if console client is attached */
612 if (in->console_client.fd.fd > -1)
613 ustream_write(&in->console_client.stream, buf, len, false);
614
615 ustream_consume(s, len);
616 } while (1);
617 }
618
619 static void
620 instance_console_client(struct ustream *s, int bytes)
621 {
622 struct service_instance *in = container_of(s, struct service_instance, console_client.stream);
623 char *buf;
624 int len;
625
626 do {
627 buf = ustream_get_read_buf(s, &len);
628 if (!buf)
629 break;
630
631 ulog(LOG_INFO, "in: %s\n", buf);
632 ustream_write(&in->console.stream, buf, len, false);
633 ustream_consume(s, len);
634 } while (1);
635 }
636
637 static void
638 instance_stderr(struct ustream *s, int bytes)
639 {
640 instance_stdio(s, LOG_ERR,
641 container_of(s, struct service_instance, _stderr.stream));
642 }
643
644 static void
645 instance_timeout(struct uloop_timeout *t)
646 {
647 struct service_instance *in;
648
649 in = container_of(t, struct service_instance, timeout);
650
651 if (in->halt) {
652 LOG("Instance %s::%s pid %d not stopped on SIGTERM, sending SIGKILL instead\n",
653 in->srv->name, in->name, in->proc.pid);
654 kill(in->proc.pid, SIGKILL);
655 } else if (in->restart || in->respawn)
656 instance_start(in);
657 }
658
659 static void
660 instance_delete(struct service_instance *in)
661 {
662 struct service *s = in->srv;
663
664 avl_delete(&s->instances.avl, &in->node.avl);
665 instance_free(in);
666 service_stopped(s);
667 }
668
669 static int
670 instance_exit_code(int ret)
671 {
672 if (WIFEXITED(ret)) {
673 return WEXITSTATUS(ret);
674 }
675
676 if (WIFSIGNALED(ret)) {
677 return SIGNALLED_OFFSET + WTERMSIG(ret);
678 }
679
680 if (WIFSTOPPED(ret)) {
681 return WSTOPSIG(ret);
682 }
683
684 return 1;
685 }
686
687 static void
688 instance_exit(struct uloop_process *p, int ret)
689 {
690 struct service_instance *in;
691 struct timespec tp;
692 long runtime;
693
694 in = container_of(p, struct service_instance, proc);
695
696 clock_gettime(CLOCK_MONOTONIC, &tp);
697 runtime = tp.tv_sec - in->start.tv_sec;
698
699 DEBUG(2, "Instance %s::%s exit with error code %d after %ld seconds\n", in->srv->name, in->name, ret, runtime);
700
701 in->exit_code = instance_exit_code(ret);
702 uloop_timeout_cancel(&in->timeout);
703 service_event("instance.stop", in->srv->name, in->name);
704
705 if (in->halt) {
706 instance_removepid(in);
707 if (in->restart)
708 instance_start(in);
709 else
710 instance_delete(in);
711 } else if (in->restart) {
712 instance_start(in);
713 } else if (in->respawn) {
714 if (runtime < in->respawn_threshold)
715 in->respawn_count++;
716 else
717 in->respawn_count = 0;
718 if (in->respawn_count > in->respawn_retry && in->respawn_retry > 0 ) {
719 LOG("Instance %s::%s s in a crash loop %d crashes, %ld seconds since last crash\n",
720 in->srv->name, in->name, in->respawn_count, runtime);
721 in->restart = in->respawn = 0;
722 in->halt = 1;
723 service_event("instance.fail", in->srv->name, in->name);
724 } else {
725 service_event("instance.respawn", in->srv->name, in->name);
726 uloop_timeout_set(&in->timeout, in->respawn_timeout * 1000);
727 }
728 }
729 }
730
731 void
732 instance_stop(struct service_instance *in, bool halt)
733 {
734 if (!in->proc.pending) {
735 if (halt)
736 instance_delete(in);
737 return;
738 }
739 in->halt = halt;
740 in->restart = in->respawn = false;
741 kill(in->proc.pid, SIGTERM);
742 uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
743 }
744
745 static void
746 instance_restart(struct service_instance *in)
747 {
748 if (!in->proc.pending)
749 return;
750
751 if (in->reload_signal) {
752 kill(in->proc.pid, in->reload_signal);
753 return;
754 }
755
756 in->halt = true;
757 in->restart = true;
758 kill(in->proc.pid, SIGTERM);
759 uloop_timeout_set(&in->timeout, in->term_timeout * 1000);
760 }
761
762 static bool string_changed(const char *a, const char *b)
763 {
764 return !((!a && !b) || (a && b && !strcmp(a, b)));
765 }
766
767 static bool
768 instance_config_changed(struct service_instance *in, struct service_instance *in_new)
769 {
770 if (!in->valid)
771 return true;
772
773 if (!blob_attr_equal(in->command, in_new->command))
774 return true;
775
776 if (!blobmsg_list_equal(&in->env, &in_new->env))
777 return true;
778
779 if (!blobmsg_list_equal(&in->netdev, &in_new->netdev))
780 return true;
781
782 if (!blobmsg_list_equal(&in->file, &in_new->file))
783 return true;
784
785 if (in->nice != in_new->nice)
786 return true;
787
788 if (in->syslog_facility != in_new->syslog_facility)
789 return true;
790
791 if (string_changed(in->user, in_new->user))
792 return true;
793
794 if (string_changed(in->group, in_new->group))
795 return true;
796
797 if (in->uid != in_new->uid)
798 return true;
799
800 if (in->pw_gid != in_new->pw_gid)
801 return true;
802
803 if (string_changed(in->pidfile, in_new->pidfile))
804 return true;
805
806 if (in->respawn_retry != in_new->respawn_retry)
807 return true;
808 if (in->respawn_threshold != in_new->respawn_threshold)
809 return true;
810 if (in->respawn_timeout != in_new->respawn_timeout)
811 return true;
812 if (in->bundle && in_new->bundle && strcmp(in->bundle, in_new->bundle))
813 return true;
814 if ((!in->seccomp && in_new->seccomp) ||
815 (in->seccomp && !in_new->seccomp) ||
816 (in->seccomp && in_new->seccomp && strcmp(in->seccomp, in_new->seccomp)))
817 return true;
818
819 if (!blobmsg_list_equal(&in->limits, &in_new->limits))
820 return true;
821
822 if (!blobmsg_list_equal(&in->jail.mount, &in_new->jail.mount))
823 return true;
824
825 if (!blobmsg_list_equal(&in->errors, &in_new->errors))
826 return true;
827
828 return false;
829 }
830
831 static bool
832 instance_netdev_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2)
833 {
834 struct instance_netdev *n1 = container_of(l1, struct instance_netdev, node);
835 struct instance_netdev *n2 = container_of(l2, struct instance_netdev, node);
836
837 return n1->ifindex == n2->ifindex;
838 }
839
840 static void
841 instance_netdev_update(struct blobmsg_list_node *l)
842 {
843 struct instance_netdev *n = container_of(l, struct instance_netdev, node);
844
845 n->ifindex = if_nametoindex(n->node.avl.key);
846 }
847
848 static bool
849 instance_file_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2)
850 {
851 struct instance_file *f1 = container_of(l1, struct instance_file, node);
852 struct instance_file *f2 = container_of(l2, struct instance_file, node);
853
854 return !memcmp(f1->md5, f2->md5, sizeof(f1->md5));
855 }
856
857 static void
858 instance_file_update(struct blobmsg_list_node *l)
859 {
860 struct instance_file *f = container_of(l, struct instance_file, node);
861 md5_ctx_t md5;
862 char buf[256];
863 int len, fd;
864
865 memset(f->md5, 0, sizeof(f->md5));
866
867 fd = open(l->avl.key, O_RDONLY);
868 if (fd < 0)
869 return;
870
871 md5_begin(&md5);
872 do {
873 len = read(fd, buf, sizeof(buf));
874 if (len < 0) {
875 if (errno == EINTR)
876 continue;
877
878 break;
879 }
880 if (!len)
881 break;
882
883 md5_hash(buf, len, &md5);
884 } while(1);
885
886 md5_end(f->md5, &md5);
887 close(fd);
888 }
889
890 static void
891 instance_fill_any(struct blobmsg_list *l, struct blob_attr *cur)
892 {
893 if (!cur)
894 return;
895
896 blobmsg_list_fill(l, blobmsg_data(cur), blobmsg_data_len(cur), false);
897 }
898
899 static bool
900 instance_fill_array(struct blobmsg_list *l, struct blob_attr *cur, blobmsg_update_cb cb, bool array)
901 {
902 struct blobmsg_list_node *node;
903
904 if (!cur)
905 return true;
906
907 if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
908 return false;
909
910 blobmsg_list_fill(l, blobmsg_data(cur), blobmsg_data_len(cur), array);
911 if (cb) {
912 blobmsg_list_for_each(l, node)
913 cb(node);
914 }
915 return true;
916 }
917
918 static int
919 instance_jail_parse(struct service_instance *in, struct blob_attr *attr)
920 {
921 struct blob_attr *tb[__JAIL_ATTR_MAX];
922 struct jail *jail = &in->jail;
923
924 blobmsg_parse(jail_attr, __JAIL_ATTR_MAX, tb,
925 blobmsg_data(attr), blobmsg_data_len(attr));
926
927 jail->argc = 2;
928
929 if (tb[JAIL_ATTR_REQUIREJAIL]) {
930 in->require_jail = true;
931 jail->argc++;
932 }
933 if (tb[JAIL_ATTR_NAME]) {
934 jail->name = strdup(blobmsg_get_string(tb[JAIL_ATTR_NAME]));
935 jail->argc += 2;
936 }
937 if (tb[JAIL_ATTR_HOSTNAME]) {
938 jail->hostname = strdup(blobmsg_get_string(tb[JAIL_ATTR_HOSTNAME]));
939 jail->argc += 2;
940 }
941 if (tb[JAIL_ATTR_PROCFS]) {
942 jail->procfs = blobmsg_get_bool(tb[JAIL_ATTR_PROCFS]);
943 jail->argc++;
944 }
945 if (tb[JAIL_ATTR_SYSFS]) {
946 jail->sysfs = blobmsg_get_bool(tb[JAIL_ATTR_SYSFS]);
947 jail->argc++;
948 }
949 if (tb[JAIL_ATTR_UBUS]) {
950 jail->ubus = blobmsg_get_bool(tb[JAIL_ATTR_UBUS]);
951 jail->argc++;
952 }
953 if (tb[JAIL_ATTR_LOG]) {
954 jail->log = blobmsg_get_bool(tb[JAIL_ATTR_LOG]);
955 jail->argc++;
956 }
957 if (tb[JAIL_ATTR_RONLY]) {
958 jail->ronly = blobmsg_get_bool(tb[JAIL_ATTR_RONLY]);
959 jail->argc++;
960 }
961 if (tb[JAIL_ATTR_NETNS]) {
962 jail->netns = blobmsg_get_bool(tb[JAIL_ATTR_NETNS]);
963 jail->argc++;
964 }
965 if (tb[JAIL_ATTR_USERNS]) {
966 jail->userns = blobmsg_get_bool(tb[JAIL_ATTR_USERNS]);
967 jail->argc++;
968 }
969 if (tb[JAIL_ATTR_CGROUPSNS]) {
970 jail->cgroupsns = blobmsg_get_bool(tb[JAIL_ATTR_CGROUPSNS]);
971 jail->argc++;
972 }
973 if (tb[JAIL_ATTR_CONSOLE]) {
974 jail->console = blobmsg_get_bool(tb[JAIL_ATTR_CONSOLE]);
975 jail->argc++;
976 }
977
978 if (tb[JAIL_ATTR_MOUNT]) {
979 struct blob_attr *cur;
980 int rem;
981
982 blobmsg_for_each_attr(cur, tb[JAIL_ATTR_MOUNT], rem)
983 jail->argc += 2;
984 instance_fill_array(&jail->mount, tb[JAIL_ATTR_MOUNT], NULL, false);
985 }
986 if (in->seccomp)
987 jail->argc += 2;
988
989 if (in->user)
990 jail->argc += 2;
991
992 if (in->group)
993 jail->argc += 2;
994
995 if (in->extroot)
996 jail->argc += 2;
997
998 if (in->overlaydir)
999 jail->argc += 2;
1000
1001 if (in->tmpoverlaysize)
1002 jail->argc += 2;
1003
1004 if (in->no_new_privs)
1005 jail->argc++;
1006
1007 if (in->bundle)
1008 jail->argc += 2;
1009
1010 return true;
1011 }
1012
1013 static bool
1014 instance_config_parse_command(struct service_instance *in, struct blob_attr **tb)
1015 {
1016 struct blob_attr *cur, *cur2;
1017 bool ret = false;
1018 int rem;
1019
1020 cur = tb[INSTANCE_ATTR_COMMAND];
1021 if (!cur) {
1022 in->command = NULL;
1023 return true;
1024 }
1025
1026 if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
1027 return false;
1028
1029 blobmsg_for_each_attr(cur2, cur, rem) {
1030 ret = true;
1031 break;
1032 }
1033
1034 in->command = cur;
1035 return ret;
1036 }
1037
1038 static bool
1039 instance_config_parse(struct service_instance *in)
1040 {
1041 struct blob_attr *tb[__INSTANCE_ATTR_MAX];
1042 struct blob_attr *cur, *cur2;
1043 struct stat s;
1044 int rem, r;
1045
1046 blobmsg_parse(instance_attr, __INSTANCE_ATTR_MAX, tb,
1047 blobmsg_data(in->config), blobmsg_data_len(in->config));
1048
1049 if (!tb[INSTANCE_ATTR_BUNDLE] && !instance_config_parse_command(in, tb))
1050 return false;
1051
1052 if (tb[INSTANCE_ATTR_TERMTIMEOUT])
1053 in->term_timeout = blobmsg_get_u32(tb[INSTANCE_ATTR_TERMTIMEOUT]);
1054 if (tb[INSTANCE_ATTR_RESPAWN]) {
1055 int i = 0;
1056 uint32_t vals[3] = { 3600, 5, 5};
1057
1058 blobmsg_for_each_attr(cur2, tb[INSTANCE_ATTR_RESPAWN], rem) {
1059 if ((i >= 3) && (blobmsg_type(cur2) == BLOBMSG_TYPE_STRING))
1060 continue;
1061 vals[i] = atoi(blobmsg_get_string(cur2));
1062 i++;
1063 }
1064 in->respawn = true;
1065 in->respawn_count = 0;
1066 in->respawn_threshold = vals[0];
1067 in->respawn_timeout = vals[1];
1068 in->respawn_retry = vals[2];
1069 }
1070 if (tb[INSTANCE_ATTR_TRIGGER]) {
1071 in->trigger = tb[INSTANCE_ATTR_TRIGGER];
1072 trigger_add(in->trigger, in);
1073 }
1074
1075 if (tb[INSTANCE_ATTR_WATCH]) {
1076 blobmsg_for_each_attr(cur2, tb[INSTANCE_ATTR_WATCH], rem) {
1077 if (blobmsg_type(cur2) != BLOBMSG_TYPE_STRING)
1078 continue;
1079 DEBUG(3, "watch for %s\n", blobmsg_get_string(cur2));
1080 watch_add(blobmsg_get_string(cur2), in);
1081 }
1082 }
1083
1084 if ((cur = tb[INSTANCE_ATTR_NICE])) {
1085 in->nice = (int8_t) blobmsg_get_u32(cur);
1086 if (in->nice < -20 || in->nice > 20)
1087 return false;
1088 }
1089
1090 if (tb[INSTANCE_ATTR_USER]) {
1091 const char *user = blobmsg_get_string(tb[INSTANCE_ATTR_USER]);
1092 struct passwd *p = getpwnam(user);
1093 if (p) {
1094 in->user = strdup(user);
1095 in->uid = p->pw_uid;
1096 in->gr_gid = in->pw_gid = p->pw_gid;
1097 }
1098 }
1099
1100 if (tb[INSTANCE_ATTR_GROUP]) {
1101 const char *group = blobmsg_get_string(tb[INSTANCE_ATTR_GROUP]);
1102 struct group *p = getgrnam(group);
1103 if (p) {
1104 in->group = strdup(group);
1105 in->gr_gid = p->gr_gid;
1106 }
1107 }
1108
1109 if (tb[INSTANCE_ATTR_TRACE])
1110 in->trace = blobmsg_get_bool(tb[INSTANCE_ATTR_TRACE]);
1111
1112 if (tb[INSTANCE_ATTR_NO_NEW_PRIVS])
1113 in->no_new_privs = blobmsg_get_bool(tb[INSTANCE_ATTR_NO_NEW_PRIVS]);
1114
1115 if (!in->trace && tb[INSTANCE_ATTR_SECCOMP])
1116 in->seccomp = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_SECCOMP]));
1117
1118 if (tb[INSTANCE_ATTR_EXTROOT])
1119 in->extroot = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_EXTROOT]));
1120
1121 if (tb[INSTANCE_ATTR_OVERLAYDIR])
1122 in->overlaydir = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_OVERLAYDIR]));
1123
1124 if (tb[INSTANCE_ATTR_TMPOVERLAYSIZE])
1125 in->tmpoverlaysize = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_TMPOVERLAYSIZE]));
1126
1127 if (tb[INSTANCE_ATTR_BUNDLE])
1128 in->bundle = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_BUNDLE]));
1129
1130 if (tb[INSTANCE_ATTR_PIDFILE]) {
1131 char *pidfile = blobmsg_get_string(tb[INSTANCE_ATTR_PIDFILE]);
1132 if (pidfile)
1133 in->pidfile = strdup(pidfile);
1134 }
1135
1136 if (tb[INSTANCE_ATTR_RELOADSIG])
1137 in->reload_signal = blobmsg_get_u32(tb[INSTANCE_ATTR_RELOADSIG]);
1138
1139 if (!in->trace && tb[INSTANCE_ATTR_JAIL])
1140 in->has_jail = instance_jail_parse(in, tb[INSTANCE_ATTR_JAIL]);
1141
1142 if (in->has_jail) {
1143 r = stat(UJAIL_BIN_PATH, &s);
1144 if (r < 0) {
1145 if (in->require_jail) {
1146 ERROR("Cannot jail service %s::%s. %s: %m (%d)\n",
1147 in->srv->name, in->name, UJAIL_BIN_PATH, r);
1148 return false;
1149 }
1150 DEBUG(2, "unable to find %s: %m (%d)\n", UJAIL_BIN_PATH, r);
1151 in->has_jail = false;
1152 }
1153 }
1154
1155 if (tb[INSTANCE_ATTR_STDOUT] && blobmsg_get_bool(tb[INSTANCE_ATTR_STDOUT]))
1156 in->_stdout.fd.fd = -1;
1157
1158 if (tb[INSTANCE_ATTR_STDERR] && blobmsg_get_bool(tb[INSTANCE_ATTR_STDERR]))
1159 in->_stderr.fd.fd = -1;
1160
1161 instance_fill_any(&in->data, tb[INSTANCE_ATTR_DATA]);
1162
1163 if (!instance_fill_array(&in->env, tb[INSTANCE_ATTR_ENV], NULL, false))
1164 return false;
1165
1166 if (!instance_fill_array(&in->netdev, tb[INSTANCE_ATTR_NETDEV], instance_netdev_update, true))
1167 return false;
1168
1169 if (!instance_fill_array(&in->file, tb[INSTANCE_ATTR_FILE], instance_file_update, true))
1170 return false;
1171
1172 if (!instance_fill_array(&in->limits, tb[INSTANCE_ATTR_LIMITS], NULL, false))
1173 return false;
1174
1175 if (!instance_fill_array(&in->errors, tb[INSTANCE_ATTR_ERROR], NULL, true))
1176 return false;
1177
1178 if (tb[INSTANCE_ATTR_FACILITY]) {
1179 int facility = syslog_facility_str_to_int(blobmsg_get_string(tb[INSTANCE_ATTR_FACILITY]));
1180 if (facility != -1) {
1181 in->syslog_facility = facility;
1182 DEBUG(3, "setting facility '%s'\n", blobmsg_get_string(tb[INSTANCE_ATTR_FACILITY]));
1183 } else
1184 DEBUG(3, "unknown syslog facility '%s' given, using default (LOG_DAEMON)\n", blobmsg_get_string(tb[INSTANCE_ATTR_FACILITY]));
1185 }
1186
1187 return true;
1188 }
1189
1190 static void
1191 instance_config_cleanup(struct service_instance *in)
1192 {
1193 blobmsg_list_free(&in->env);
1194 blobmsg_list_free(&in->data);
1195 blobmsg_list_free(&in->netdev);
1196 blobmsg_list_free(&in->file);
1197 blobmsg_list_free(&in->limits);
1198 blobmsg_list_free(&in->errors);
1199 blobmsg_list_free(&in->jail.mount);
1200 }
1201
1202 static void
1203 instance_config_move_strdup(char **dst, char *src)
1204 {
1205 if (*dst) {
1206 free(*dst);
1207 *dst = NULL;
1208 }
1209
1210 if (!src)
1211 return;
1212
1213 *dst = strdup(src);
1214 }
1215
1216 static void
1217 instance_config_move(struct service_instance *in, struct service_instance *in_src)
1218 {
1219 instance_config_cleanup(in);
1220 blobmsg_list_move(&in->env, &in_src->env);
1221 blobmsg_list_move(&in->data, &in_src->data);
1222 blobmsg_list_move(&in->netdev, &in_src->netdev);
1223 blobmsg_list_move(&in->file, &in_src->file);
1224 blobmsg_list_move(&in->limits, &in_src->limits);
1225 blobmsg_list_move(&in->errors, &in_src->errors);
1226 blobmsg_list_move(&in->jail.mount, &in_src->jail.mount);
1227 in->trigger = in_src->trigger;
1228 in->command = in_src->command;
1229 in->respawn = in_src->respawn;
1230 in->respawn_retry = in_src->respawn_retry;
1231 in->respawn_threshold = in_src->respawn_threshold;
1232 in->respawn_timeout = in_src->respawn_timeout;
1233 in->name = in_src->name;
1234 in->trace = in_src->trace;
1235 in->node.avl.key = in_src->node.avl.key;
1236 in->syslog_facility = in_src->syslog_facility;
1237
1238 instance_config_move_strdup(&in->pidfile, in_src->pidfile);
1239 instance_config_move_strdup(&in->seccomp, in_src->seccomp);
1240 instance_config_move_strdup(&in->jail.name, in_src->jail.name);
1241 instance_config_move_strdup(&in->jail.hostname, in_src->jail.hostname);
1242
1243 free(in->config);
1244 in->config = in_src->config;
1245 in_src->config = NULL;
1246 }
1247
1248 void
1249 instance_update(struct service_instance *in, struct service_instance *in_new)
1250 {
1251 bool changed = instance_config_changed(in, in_new);
1252 bool running = in->proc.pending;
1253 bool stopping = in->halt;
1254
1255 if (!running || stopping) {
1256 instance_config_move(in, in_new);
1257 instance_start(in);
1258 } else {
1259 if (changed)
1260 instance_restart(in);
1261 instance_config_move(in, in_new);
1262 /* restart happens in the child callback handler */
1263 }
1264 }
1265
1266 void
1267 instance_free(struct service_instance *in)
1268 {
1269 instance_free_stdio(in);
1270 uloop_process_delete(&in->proc);
1271 uloop_timeout_cancel(&in->timeout);
1272 trigger_del(in);
1273 watch_del(in);
1274 instance_config_cleanup(in);
1275 free(in->config);
1276 free(in->user);
1277 free(in->group);
1278 free(in->extroot);
1279 free(in->overlaydir);
1280 free(in->tmpoverlaysize);
1281 free(in->bundle);
1282 free(in->jail.name);
1283 free(in->jail.hostname);
1284 free(in->seccomp);
1285 free(in->pidfile);
1286 free(in);
1287 }
1288
1289 void
1290 instance_init(struct service_instance *in, struct service *s, struct blob_attr *config)
1291 {
1292 config = blob_memdup(config);
1293 in->srv = s;
1294 in->name = blobmsg_name(config);
1295 in->config = config;
1296 in->timeout.cb = instance_timeout;
1297 in->proc.cb = instance_exit;
1298 in->term_timeout = 5;
1299 in->syslog_facility = LOG_DAEMON;
1300 in->exit_code = 0;
1301 in->require_jail = false;
1302
1303 in->_stdout.fd.fd = -2;
1304 in->_stdout.stream.string_data = true;
1305 in->_stdout.stream.notify_read = instance_stdout;
1306
1307 in->_stderr.fd.fd = -2;
1308 in->_stderr.stream.string_data = true;
1309 in->_stderr.stream.notify_read = instance_stderr;
1310
1311 in->console.fd.fd = -2;
1312 in->console.stream.string_data = true;
1313 in->console.stream.notify_read = instance_console;
1314
1315 in->console_client.fd.fd = -2;
1316 in->console_client.stream.string_data = true;
1317 in->console_client.stream.notify_read = instance_console_client;
1318
1319 blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp);
1320 blobmsg_list_init(&in->file, struct instance_file, node, instance_file_cmp);
1321 blobmsg_list_simple_init(&in->env);
1322 blobmsg_list_simple_init(&in->data);
1323 blobmsg_list_simple_init(&in->limits);
1324 blobmsg_list_simple_init(&in->errors);
1325 blobmsg_list_simple_init(&in->jail.mount);
1326 in->valid = instance_config_parse(in);
1327 }
1328
1329 void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose)
1330 {
1331 void *i;
1332
1333 if (!in->valid)
1334 return;
1335
1336 i = blobmsg_open_table(b, in->name);
1337 blobmsg_add_u8(b, "running", in->proc.pending);
1338 if (in->proc.pending)
1339 blobmsg_add_u32(b, "pid", in->proc.pid);
1340 if (in->command)
1341 blobmsg_add_blob(b, in->command);
1342 if (in->bundle)
1343 blobmsg_add_string(b, "bundle", in->bundle);
1344 blobmsg_add_u32(b, "term_timeout", in->term_timeout);
1345 if (!in->proc.pending)
1346 blobmsg_add_u32(b, "exit_code", in->exit_code);
1347
1348 if (!avl_is_empty(&in->errors.avl)) {
1349 struct blobmsg_list_node *var;
1350 void *e = blobmsg_open_array(b, "errors");
1351 blobmsg_list_for_each(&in->errors, var)
1352 blobmsg_add_string(b, NULL, blobmsg_data(var->data));
1353 blobmsg_close_table(b, e);
1354 }
1355
1356 if (!avl_is_empty(&in->env.avl)) {
1357 struct blobmsg_list_node *var;
1358 void *e = blobmsg_open_table(b, "env");
1359 blobmsg_list_for_each(&in->env, var)
1360 blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data));
1361 blobmsg_close_table(b, e);
1362 }
1363
1364 if (!avl_is_empty(&in->data.avl)) {
1365 struct blobmsg_list_node *var;
1366 void *e = blobmsg_open_table(b, "data");
1367 blobmsg_list_for_each(&in->data, var)
1368 blobmsg_add_blob(b, var->data);
1369 blobmsg_close_table(b, e);
1370 }
1371
1372 if (!avl_is_empty(&in->limits.avl)) {
1373 struct blobmsg_list_node *var;
1374 void *e = blobmsg_open_table(b, "limits");
1375 blobmsg_list_for_each(&in->limits, var)
1376 blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data));
1377 blobmsg_close_table(b, e);
1378 }
1379
1380 if (in->reload_signal)
1381 blobmsg_add_u32(b, "reload_signal", in->reload_signal);
1382
1383 if (in->respawn) {
1384 void *r = blobmsg_open_table(b, "respawn");
1385 blobmsg_add_u32(b, "threshold", in->respawn_threshold);
1386 blobmsg_add_u32(b, "timeout", in->respawn_timeout);
1387 blobmsg_add_u32(b, "retry", in->respawn_retry);
1388 blobmsg_close_table(b, r);
1389 }
1390
1391 if (in->trace)
1392 blobmsg_add_u8(b, "trace", true);
1393
1394 if (in->no_new_privs)
1395 blobmsg_add_u8(b, "no_new_privs", true);
1396
1397 if (in->seccomp)
1398 blobmsg_add_string(b, "seccomp", in->seccomp);
1399
1400 if (in->pidfile)
1401 blobmsg_add_string(b, "pidfile", in->pidfile);
1402
1403 if (in->user)
1404 blobmsg_add_string(b, "user", in->user);
1405
1406 if (in->group)
1407 blobmsg_add_string(b, "group", in->group);
1408
1409 if (in->has_jail) {
1410 void *r = blobmsg_open_table(b, "jail");
1411 if (in->jail.name)
1412 blobmsg_add_string(b, "name", in->jail.name);
1413 if (!in->bundle) {
1414 if (in->jail.hostname)
1415 blobmsg_add_string(b, "hostname", in->jail.hostname);
1416
1417 blobmsg_add_u8(b, "procfs", in->jail.procfs);
1418 blobmsg_add_u8(b, "sysfs", in->jail.sysfs);
1419 blobmsg_add_u8(b, "ubus", in->jail.ubus);
1420 blobmsg_add_u8(b, "log", in->jail.log);
1421 blobmsg_add_u8(b, "ronly", in->jail.ronly);
1422 blobmsg_add_u8(b, "netns", in->jail.netns);
1423 blobmsg_add_u8(b, "userns", in->jail.userns);
1424 blobmsg_add_u8(b, "cgroupsns", in->jail.cgroupsns);
1425 }
1426 blobmsg_add_u8(b, "console", (in->console.fd.fd > -1));
1427 blobmsg_close_table(b, r);
1428 if (!avl_is_empty(&in->jail.mount.avl)) {
1429 struct blobmsg_list_node *var;
1430 void *e = blobmsg_open_table(b, "mount");
1431 blobmsg_list_for_each(&in->jail.mount, var)
1432 blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data));
1433 blobmsg_close_table(b, e);
1434 }
1435 }
1436
1437 if (in->extroot)
1438 blobmsg_add_string(b, "extroot", in->extroot);
1439 if (in->overlaydir)
1440 blobmsg_add_string(b, "overlaydir", in->overlaydir);
1441 if (in->tmpoverlaysize)
1442 blobmsg_add_string(b, "tmpoverlaysize", in->tmpoverlaysize);
1443
1444 if (verbose && in->trigger)
1445 blobmsg_add_blob(b, in->trigger);
1446
1447 blobmsg_close_table(b, i);
1448 }