system: support passing "options" to the "sysupgrade" ubus method
[oweals/procd.git] / sysupgrade.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License version 2.1
8  * as published by the Free Software Foundation
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16
17 #include "watchdog.h"
18 #include "sysupgrade.h"
19
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include <libubox/blobmsg.h>
26
27 void sysupgrade_exec_upgraded(const char *prefix, char *path, char *command,
28                               struct blob_attr *options)
29 {
30         char *wdt_fd = watchdog_fd();
31         char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL};
32         struct blob_attr *option;
33         int rem;
34         int ret;
35
36         ret = chroot(prefix);
37         if (ret < 0) {
38                 fprintf(stderr, "Failed to chroot for upgraded exec.\n");
39                 return;
40         }
41
42         argv[1] = path;
43         argv[2] = command;
44
45         if (wdt_fd) {
46                 watchdog_set_cloexec(false);
47                 setenv("WDTFD", wdt_fd, 1);
48         }
49
50         blobmsg_for_each_attr(option, options, rem) {
51                 const char *prefix = "UPGRADE_OPT_";
52                 char value[11];
53                 char *name;
54                 char *c;
55                 int tmp;
56
57                 if (asprintf(&name, "%s%s", prefix, blobmsg_name(option)) <= 0)
58                         continue;
59                 for (c = name + strlen(prefix); *c; c++) {
60                         if (isalnum(*c) || *c == '_') {
61                                 *c = toupper(*c);
62                         } else {
63                                 c = NULL;
64                                 break;
65                         }
66                 }
67
68                 if (!c) {
69                         fprintf(stderr, "Option \"%s\" contains invalid characters\n",
70                                 blobmsg_name(option));
71                         free(name);
72                         continue;
73                 }
74
75                 switch (blobmsg_type(option)) {
76                 case BLOBMSG_TYPE_INT32:
77                         tmp = blobmsg_get_u32(option);
78                         break;
79                 case BLOBMSG_TYPE_INT16:
80                         tmp = blobmsg_get_u16(option);
81                         break;
82                 case BLOBMSG_TYPE_INT8:
83                         tmp = blobmsg_get_u8(option);
84                         break;
85                 default:
86                         fprintf(stderr, "Option \"%s\" has unsupported type: %d\n",
87                                 blobmsg_name(option), blobmsg_type(option));
88                         free(name);
89                         continue;
90                 }
91                 snprintf(value, sizeof(value), "%u", tmp);
92
93                 setenv(name, value, 1);
94
95                 free(name);
96         }
97
98         execvp(argv[0], argv);
99
100         /* Cleanup on failure */
101         fprintf(stderr, "Failed to exec upgraded.\n");
102         unsetenv("WDTFD");
103         watchdog_set_cloexec(true);
104         ret = chroot(".");
105         if (ret < 0) {
106                 fprintf(stderr, "Failed to reset chroot, exiting.\n");
107                 exit(EXIT_FAILURE);
108         }
109 }