jail: netifd: check target netns fd before using it
[project/procd.git] / system.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 #include <sys/utsname.h>
16 #ifdef linux
17 #include <sys/sysinfo.h>
18 #endif
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/reboot.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27
28 #include <json-c/json_tokener.h>
29 #include <libubox/blobmsg_json.h>
30 #include <libubox/uloop.h>
31
32 #include "procd.h"
33 #include "sysupgrade.h"
34 #include "watchdog.h"
35
36 static struct blob_buf b;
37 static int notify;
38 static struct ubus_context *_ctx;
39 static int initramfs;
40
41 enum vjson_state {
42 VJSON_ERROR,
43 VJSON_CONTINUE,
44 VJSON_SUCCESS,
45 };
46
47 static const char *system_rootfs_type(void) {
48 const char proc_mounts[] = "/proc/self/mounts";
49 static char fstype[16] = { 0 };
50 char *mountstr = NULL, *mp = "/", *pos, *tmp;
51 FILE *mounts;
52 ssize_t nread;
53 size_t len = 0;
54 bool found;
55
56 if (initramfs)
57 return "initramfs";
58
59 if (fstype[0])
60 return fstype;
61
62 mounts = fopen(proc_mounts, "r");
63 if (!mounts)
64 return NULL;
65
66 while ((nread = getline(&mountstr, &len, mounts)) != -1) {
67 found = false;
68
69 pos = strchr(mountstr, ' ');
70 if (!pos)
71 continue;
72
73 tmp = pos + 1;
74 pos = strchr(tmp, ' ');
75 if (!pos)
76 continue;
77
78 *pos = '\0';
79 if (strcmp(tmp, mp))
80 continue;
81
82 tmp = pos + 1;
83 pos = strchr(tmp, ' ');
84 if (!pos)
85 continue;
86
87 *pos = '\0';
88
89 if (!strcmp(tmp, "overlay")) {
90 /*
91 * there is no point in parsing overlay option string for
92 * lowerdir, as that can point to "/" being a previous
93 * overlay mount (after firstboot or sysuprade config
94 * restore). Hence just assume the lowerdir is "/rom" and
95 * restart searching for that instead.
96 */
97 mp = "/rom";
98 fseek(mounts, 0, SEEK_SET);
99 continue;
100 }
101
102 found = true;
103 break;
104 }
105
106 if (found)
107 strncpy(fstype, tmp, sizeof(fstype) - 1);
108
109 fstype[sizeof(fstype) - 1]= '\0';
110 free(mountstr);
111 fclose(mounts);
112
113 if (found)
114 return fstype;
115 else
116 return NULL;
117 }
118
119 static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
120 struct ubus_request_data *req, const char *method,
121 struct blob_attr *msg)
122 {
123 void *c;
124 char line[256];
125 char *key, *val, *next;
126 const char *rootfs_type = system_rootfs_type();
127 struct utsname utsname;
128 FILE *f;
129
130 blob_buf_init(&b, 0);
131
132 if (uname(&utsname) >= 0)
133 {
134 blobmsg_add_string(&b, "kernel", utsname.release);
135 blobmsg_add_string(&b, "hostname", utsname.nodename);
136 }
137
138 if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
139 {
140 while(fgets(line, sizeof(line), f))
141 {
142 key = strtok(line, "\t:");
143 val = strtok(NULL, "\t\n");
144
145 if (!key || !val)
146 continue;
147
148 #ifdef __aarch64__
149 if (!strcasecmp(key, "CPU revision")) {
150 snprintf(line, sizeof(line), "ARMv8 Processor rev %lu", strtoul(val + 2, NULL, 16));
151 blobmsg_add_string(&b, "system", line);
152 break;
153 }
154 #else
155 if (!strcasecmp(key, "system type") ||
156 !strcasecmp(key, "processor") ||
157 !strcasecmp(key, "cpu") ||
158 !strcasecmp(key, "model name"))
159 {
160 strtoul(val + 2, &key, 0);
161
162 if (key == (val + 2) || *key != 0)
163 {
164 blobmsg_add_string(&b, "system", val + 2);
165 break;
166 }
167 }
168 #endif
169 }
170
171 fclose(f);
172 }
173
174 if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
175 (f = fopen("/proc/device-tree/model", "r")) != NULL)
176 {
177 if (fgets(line, sizeof(line), f))
178 {
179 val = strtok(line, "\t\n");
180
181 if (val)
182 blobmsg_add_string(&b, "model", val);
183 }
184
185 fclose(f);
186 }
187 else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
188 {
189 while(fgets(line, sizeof(line), f))
190 {
191 key = strtok(line, "\t:");
192 val = strtok(NULL, "\t\n");
193
194 if (!key || !val)
195 continue;
196
197 if (!strcasecmp(key, "machine") ||
198 !strcasecmp(key, "hardware"))
199 {
200 blobmsg_add_string(&b, "model", val + 2);
201 break;
202 }
203 }
204
205 fclose(f);
206 }
207
208 if ((f = fopen("/tmp/sysinfo/board_name", "r")) != NULL)
209 {
210 if (fgets(line, sizeof(line), f))
211 {
212 val = strtok(line, "\t\n");
213
214 if (val)
215 blobmsg_add_string(&b, "board_name", val);
216 }
217
218 fclose(f);
219 }
220 else if ((f = fopen("/proc/device-tree/compatible", "r")) != NULL)
221 {
222 if (fgets(line, sizeof(line), f))
223 {
224 val = strtok(line, "\t\n");
225
226 if (val)
227 {
228 next = val;
229 while ((next = strchr(next, ',')) != NULL)
230 {
231 *next = '-';
232 next++;
233 }
234
235 blobmsg_add_string(&b, "board_name", val);
236 }
237 }
238
239 fclose(f);
240 }
241
242 if (rootfs_type)
243 blobmsg_add_string(&b, "rootfs_type", rootfs_type);
244
245 if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
246 {
247 c = blobmsg_open_table(&b, "release");
248
249 while (fgets(line, sizeof(line), f))
250 {
251 char *dest;
252 char ch;
253
254 key = line;
255 val = strchr(line, '=');
256 if (!val)
257 continue;
258
259 *(val++) = 0;
260
261 if (!strcasecmp(key, "DISTRIB_ID"))
262 key = "distribution";
263 else if (!strcasecmp(key, "DISTRIB_RELEASE"))
264 key = "version";
265 else if (!strcasecmp(key, "DISTRIB_REVISION"))
266 key = "revision";
267 else if (!strcasecmp(key, "DISTRIB_CODENAME"))
268 key = "codename";
269 else if (!strcasecmp(key, "DISTRIB_TARGET"))
270 key = "target";
271 else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
272 key = "description";
273 else
274 continue;
275
276 dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
277 if (!dest) {
278 ERROR("Failed to allocate blob.\n");
279 continue;
280 }
281
282 while (val && (ch = *(val++)) != 0) {
283 switch (ch) {
284 case '\'':
285 case '"':
286 next = strchr(val, ch);
287 if (next)
288 *next = 0;
289
290 strcpy(dest, val);
291
292 if (next)
293 val = next + 1;
294
295 dest += strlen(dest);
296 break;
297 case '\\':
298 *(dest++) = *(val++);
299 break;
300 }
301 }
302 blobmsg_add_string_buffer(&b);
303 }
304
305 blobmsg_close_array(&b, c);
306
307 fclose(f);
308 }
309
310 ubus_send_reply(ctx, req, b.head);
311
312 return UBUS_STATUS_OK;
313 }
314
315 static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
316 struct ubus_request_data *req, const char *method,
317 struct blob_attr *msg)
318 {
319 time_t now;
320 struct tm *tm;
321 #ifdef linux
322 struct sysinfo info;
323 void *c;
324 char line[256];
325 char *key, *val;
326 unsigned long long available, cached;
327 FILE *f;
328
329 if (sysinfo(&info))
330 return UBUS_STATUS_UNKNOWN_ERROR;
331
332 if ((f = fopen("/proc/meminfo", "r")) == NULL)
333 return UBUS_STATUS_UNKNOWN_ERROR;
334
335 /* if linux < 3.14 MemAvailable is not in meminfo */
336 available = 0;
337 cached = 0;
338
339 while (fgets(line, sizeof(line), f))
340 {
341 key = strtok(line, " :");
342 val = strtok(NULL, " ");
343
344 if (!key || !val)
345 continue;
346
347 if (!strcasecmp(key, "MemAvailable"))
348 available = 1024 * atoll(val);
349 else if (!strcasecmp(key, "Cached"))
350 cached = 1024 * atoll(val);
351 }
352
353 fclose(f);
354 #endif
355
356 now = time(NULL);
357
358 if (!(tm = localtime(&now)))
359 return UBUS_STATUS_UNKNOWN_ERROR;
360
361 blob_buf_init(&b, 0);
362
363 blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
364
365 #ifdef linux
366 blobmsg_add_u32(&b, "uptime", info.uptime);
367
368 c = blobmsg_open_array(&b, "load");
369 blobmsg_add_u32(&b, NULL, info.loads[0]);
370 blobmsg_add_u32(&b, NULL, info.loads[1]);
371 blobmsg_add_u32(&b, NULL, info.loads[2]);
372 blobmsg_close_array(&b, c);
373
374 c = blobmsg_open_table(&b, "memory");
375 blobmsg_add_u64(&b, "total",
376 (uint64_t)info.mem_unit * (uint64_t)info.totalram);
377 blobmsg_add_u64(&b, "free",
378 (uint64_t)info.mem_unit * (uint64_t)info.freeram);
379 blobmsg_add_u64(&b, "shared",
380 (uint64_t)info.mem_unit * (uint64_t)info.sharedram);
381 blobmsg_add_u64(&b, "buffered",
382 (uint64_t)info.mem_unit * (uint64_t)info.bufferram);
383 blobmsg_add_u64(&b, "available", available);
384 blobmsg_add_u64(&b, "cached", cached);
385 blobmsg_close_table(&b, c);
386
387 c = blobmsg_open_table(&b, "swap");
388 blobmsg_add_u64(&b, "total",
389 (uint64_t)info.mem_unit * (uint64_t)info.totalswap);
390 blobmsg_add_u64(&b, "free",
391 (uint64_t)info.mem_unit * (uint64_t)info.freeswap);
392 blobmsg_close_table(&b, c);
393 #endif
394
395 ubus_send_reply(ctx, req, b.head);
396
397 return UBUS_STATUS_OK;
398 }
399
400 static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
401 struct ubus_request_data *req, const char *method,
402 struct blob_attr *msg)
403 {
404 procd_shutdown(RB_AUTOBOOT);
405 return 0;
406 }
407
408 enum {
409 WDT_FREQUENCY,
410 WDT_TIMEOUT,
411 WDT_MAGICCLOSE,
412 WDT_STOP,
413 __WDT_MAX
414 };
415
416 static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
417 [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
418 [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
419 [WDT_MAGICCLOSE] = { .name = "magicclose", .type = BLOBMSG_TYPE_BOOL },
420 [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
421 };
422
423 static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
424 struct ubus_request_data *req, const char *method,
425 struct blob_attr *msg)
426 {
427 struct blob_attr *tb[__WDT_MAX];
428 const char *status;
429
430 if (!msg)
431 return UBUS_STATUS_INVALID_ARGUMENT;
432
433 blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
434 if (tb[WDT_FREQUENCY]) {
435 unsigned int timeout = tb[WDT_TIMEOUT] ? blobmsg_get_u32(tb[WDT_TIMEOUT]) :
436 watchdog_timeout(0);
437 unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
438
439 if (freq) {
440 if (freq > timeout / 2)
441 freq = timeout / 2;
442 watchdog_frequency(freq);
443 }
444 }
445
446 if (tb[WDT_TIMEOUT]) {
447 unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
448 unsigned int frequency = watchdog_frequency(0);
449
450 if (timeout <= frequency)
451 timeout = frequency * 2;
452 watchdog_timeout(timeout);
453 }
454
455 if (tb[WDT_MAGICCLOSE])
456 watchdog_set_magicclose(blobmsg_get_bool(tb[WDT_MAGICCLOSE]));
457
458 if (tb[WDT_STOP])
459 watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
460
461 if (watchdog_fd() == NULL)
462 status = "offline";
463 else if (watchdog_get_stopped())
464 status = "stopped";
465 else
466 status = "running";
467
468 blob_buf_init(&b, 0);
469 blobmsg_add_string(&b, "status", status);
470 blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
471 blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
472 blobmsg_add_u8(&b, "magicclose", watchdog_get_magicclose());
473 ubus_send_reply(ctx, req, b.head);
474
475 return 0;
476 }
477
478 enum {
479 SIGNAL_PID,
480 SIGNAL_NUM,
481 __SIGNAL_MAX
482 };
483
484 static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
485 [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
486 [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
487 };
488
489 static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
490 struct ubus_request_data *req, const char *method,
491 struct blob_attr *msg)
492 {
493 struct blob_attr *tb[__SIGNAL_MAX];
494
495 if (!msg)
496 return UBUS_STATUS_INVALID_ARGUMENT;
497
498 blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
499 if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
500 return UBUS_STATUS_INVALID_ARGUMENT;
501
502 kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
503
504 return 0;
505 }
506
507 __attribute__((format (printf, 2, 3)))
508 static enum vjson_state vjson_error(char **b, const char *fmt, ...)
509 {
510 static char buf[256] = { 0 };
511 const char *pfx = "Firmware image couldn't be validated: ";
512 va_list va;
513 int r;
514
515 r = snprintf(buf, sizeof(buf), "%s", pfx);
516 if (r < 0) {
517 *b = "vjson_error() snprintf failed";
518 return VJSON_ERROR;
519 }
520
521 va_start(va, fmt);
522 r = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
523 if (r < 0) {
524 *b = "vjson_error() vsnprintf failed";
525 return VJSON_ERROR;
526 }
527 va_end(va);
528
529 *b = buf;
530 return VJSON_ERROR;
531 }
532
533 static enum vjson_state vjson_parse_token(json_tokener *tok, char *buf, ssize_t len, char **err)
534 {
535 json_object *jsobj = NULL;
536
537 jsobj = json_tokener_parse_ex(tok, buf, len);
538 if (json_tokener_get_error(tok) == json_tokener_continue)
539 return VJSON_CONTINUE;
540
541 if (json_tokener_get_error(tok) == json_tokener_success) {
542 if (json_object_get_type(jsobj) != json_type_object) {
543 json_object_put(jsobj);
544 return vjson_error(err, "result is not an JSON object");
545 }
546
547 blobmsg_add_object(&b, jsobj);
548 json_object_put(jsobj);
549 return VJSON_SUCCESS;
550 }
551
552 return vjson_error(err, "failed to parse JSON: %s (%d)",
553 json_tokener_error_desc(json_tokener_get_error(tok)),
554 json_tokener_get_error(tok));
555 }
556
557 static enum vjson_state vjson_parse(int fd, char **err)
558 {
559 enum vjson_state r = VJSON_ERROR;
560 size_t read_count = 0;
561 char buf[64] = { 0 };
562 json_tokener *tok;
563 ssize_t len;
564 int _errno;
565
566 tok = json_tokener_new();
567 if (!tok)
568 return vjson_error(err, "json_tokener_new() failed");
569
570 vjson_error(err, "incomplete JSON input");
571
572 while ((len = read(fd, buf, sizeof(buf)))) {
573 if (len < 0 && errno == EINTR)
574 continue;
575
576 if (len < 0) {
577 _errno = errno;
578 json_tokener_free(tok);
579 return vjson_error(err, "read() failed: %s (%d)",
580 strerror(_errno), _errno);
581 }
582
583 read_count += len;
584 r = vjson_parse_token(tok, buf, len, err);
585 if (r != VJSON_CONTINUE)
586 break;
587
588 memset(buf, 0, sizeof(buf));
589 }
590
591 if (read_count == 0)
592 vjson_error(err, "no JSON input");
593
594 json_tokener_free(tok);
595 return r;
596 }
597
598 /**
599 * validate_firmware_image_call - perform validation & store result in global b
600 *
601 * @file: firmware image path
602 */
603 static enum vjson_state validate_firmware_image_call(const char *file, char **err)
604 {
605 const char *path = "/usr/libexec/validate_firmware_image";
606 enum vjson_state ret = VJSON_ERROR;
607 int _errno;
608 int fds[2];
609 int fd;
610
611 blob_buf_init(&b, 0);
612 vjson_error(err, "unhandled error");
613
614 if (pipe(fds)) {
615 _errno = errno;
616 return vjson_error(err, "pipe() failed: %s (%d)",
617 strerror(_errno), _errno);
618 }
619
620 switch (fork()) {
621 case -1:
622 _errno = errno;
623
624 close(fds[0]);
625 close(fds[1]);
626
627 return vjson_error(err, "fork() failed: %s (%d)",
628 strerror(_errno), _errno);
629 case 0:
630 /* Set stdin & stderr to /dev/null */
631 fd = open("/dev/null", O_RDWR);
632 if (fd >= 0) {
633 dup2(fd, 0);
634 dup2(fd, 2);
635 close(fd);
636 }
637
638 /* Set stdout to the shared pipe */
639 dup2(fds[1], 1);
640 close(fds[0]);
641 close(fds[1]);
642
643 execl(path, path, file, NULL);
644 exit(errno);
645 }
646
647 /* Parent process */
648 close(fds[1]);
649
650 ret = vjson_parse(fds[0], err);
651 close(fds[0]);
652
653 return ret;
654 }
655
656 enum {
657 VALIDATE_FIRMWARE_IMAGE_PATH,
658 __VALIDATE_FIRMWARE_IMAGE_MAX,
659 };
660
661 static const struct blobmsg_policy validate_firmware_image_policy[__VALIDATE_FIRMWARE_IMAGE_MAX] = {
662 [VALIDATE_FIRMWARE_IMAGE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
663 };
664
665 static int validate_firmware_image(struct ubus_context *ctx,
666 struct ubus_object *obj,
667 struct ubus_request_data *req,
668 const char *method, struct blob_attr *msg)
669 {
670 struct blob_attr *tb[__VALIDATE_FIRMWARE_IMAGE_MAX];
671 enum vjson_state ret = VJSON_ERROR;
672 char *err;
673
674 if (!msg)
675 return UBUS_STATUS_INVALID_ARGUMENT;
676
677 blobmsg_parse(validate_firmware_image_policy, __VALIDATE_FIRMWARE_IMAGE_MAX, tb, blob_data(msg), blob_len(msg));
678 if (!tb[VALIDATE_FIRMWARE_IMAGE_PATH])
679 return UBUS_STATUS_INVALID_ARGUMENT;
680
681 ret = validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH]), &err);
682 if (ret != VJSON_SUCCESS)
683 return UBUS_STATUS_UNKNOWN_ERROR;
684
685 ubus_send_reply(ctx, req, b.head);
686
687 return UBUS_STATUS_OK;
688 }
689
690 enum {
691 SYSUPGRADE_PATH,
692 SYSUPGRADE_FORCE,
693 SYSUPGRADE_BACKUP,
694 SYSUPGRADE_PREFIX,
695 SYSUPGRADE_COMMAND,
696 SYSUPGRADE_OPTIONS,
697 __SYSUPGRADE_MAX
698 };
699
700 static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
701 [SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
702 [SYSUPGRADE_FORCE] = { .name = "force", .type = BLOBMSG_TYPE_BOOL },
703 [SYSUPGRADE_BACKUP] = { .name = "backup", .type = BLOBMSG_TYPE_STRING },
704 [SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
705 [SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
706 [SYSUPGRADE_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_TABLE },
707 };
708
709 static void sysupgrade_error(struct ubus_context *ctx,
710 struct ubus_request_data *req,
711 const char *message)
712 {
713 void *c;
714
715 blob_buf_init(&b, 0);
716
717 c = blobmsg_open_table(&b, "error");
718 blobmsg_add_string(&b, "message", message);
719 blobmsg_close_table(&b, c);
720
721 ubus_send_reply(ctx, req, b.head);
722 }
723
724 static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
725 struct ubus_request_data *req, const char *method,
726 struct blob_attr *msg)
727 {
728 enum {
729 VALIDATION_VALID,
730 VALIDATION_FORCEABLE,
731 VALIDATION_ALLOW_BACKUP,
732 __VALIDATION_MAX
733 };
734 static const struct blobmsg_policy validation_policy[__VALIDATION_MAX] = {
735 [VALIDATION_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_BOOL },
736 [VALIDATION_FORCEABLE] = { .name = "forceable", .type = BLOBMSG_TYPE_BOOL },
737 [VALIDATION_ALLOW_BACKUP] = { .name = "allow_backup", .type = BLOBMSG_TYPE_BOOL },
738 };
739 struct blob_attr *validation[__VALIDATION_MAX];
740 struct blob_attr *tb[__SYSUPGRADE_MAX];
741 bool valid, forceable, allow_backup;
742 enum vjson_state ret = VJSON_ERROR;
743 char *err;
744
745 if (!msg)
746 return UBUS_STATUS_INVALID_ARGUMENT;
747
748 blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
749 if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
750 return UBUS_STATUS_INVALID_ARGUMENT;
751
752 ret = validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]), &err);
753 if (ret != VJSON_SUCCESS) {
754 sysupgrade_error(ctx, req, err);
755 return UBUS_STATUS_UNKNOWN_ERROR;
756 }
757
758 blobmsg_parse(validation_policy, __VALIDATION_MAX, validation, blob_data(b.head), blob_len(b.head));
759
760 if (!validation[VALIDATION_VALID] || !validation[VALIDATION_FORCEABLE] ||
761 !validation[VALIDATION_ALLOW_BACKUP]) {
762 sysupgrade_error(ctx, req, "Validation script provided invalid input");
763 return UBUS_STATUS_INVALID_ARGUMENT;
764 }
765
766 valid = validation[VALIDATION_VALID] && blobmsg_get_bool(validation[VALIDATION_VALID]);
767 forceable = validation[VALIDATION_FORCEABLE] && blobmsg_get_bool(validation[VALIDATION_FORCEABLE]);
768 allow_backup = validation[VALIDATION_ALLOW_BACKUP] && blobmsg_get_bool(validation[VALIDATION_ALLOW_BACKUP]);
769
770 if (!valid) {
771 if (!forceable) {
772 sysupgrade_error(ctx, req, "Firmware image is broken and cannot be installed");
773 return UBUS_STATUS_NOT_SUPPORTED;
774 } else if (!tb[SYSUPGRADE_FORCE] || !blobmsg_get_bool(tb[SYSUPGRADE_FORCE])) {
775 sysupgrade_error(ctx, req, "Firmware image is invalid");
776 return UBUS_STATUS_NOT_SUPPORTED;
777 }
778 } else if (!allow_backup && tb[SYSUPGRADE_BACKUP]) {
779 sysupgrade_error(ctx, req, "Firmware image doesn't allow preserving a backup");
780 return UBUS_STATUS_NOT_SUPPORTED;
781 }
782
783 sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
784 blobmsg_get_string(tb[SYSUPGRADE_PATH]),
785 tb[SYSUPGRADE_BACKUP] ? blobmsg_get_string(tb[SYSUPGRADE_BACKUP]) : NULL,
786 tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL,
787 tb[SYSUPGRADE_OPTIONS]);
788
789 /* sysupgrade_exec_upgraded() will never return unless something has gone wrong */
790 return UBUS_STATUS_UNKNOWN_ERROR;
791 }
792
793 static void
794 procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
795 {
796 notify = obj->has_subscribers;
797 }
798
799
800 static const struct ubus_method system_methods[] = {
801 UBUS_METHOD_NOARG("board", system_board),
802 UBUS_METHOD_NOARG("info", system_info),
803 UBUS_METHOD_NOARG("reboot", system_reboot),
804 UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
805 UBUS_METHOD("signal", proc_signal, signal_policy),
806 UBUS_METHOD("validate_firmware_image", validate_firmware_image, validate_firmware_image_policy),
807 UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
808 };
809
810 static struct ubus_object_type system_object_type =
811 UBUS_OBJECT_TYPE("system", system_methods);
812
813 static struct ubus_object system_object = {
814 .name = "system",
815 .type = &system_object_type,
816 .methods = system_methods,
817 .n_methods = ARRAY_SIZE(system_methods),
818 .subscribe_cb = procd_subscribe_cb,
819 };
820
821 void
822 procd_bcast_event(char *event, struct blob_attr *msg)
823 {
824 int ret;
825
826 if (!notify)
827 return;
828
829 ret = ubus_notify(_ctx, &system_object, event, msg, -1);
830 if (ret)
831 fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
832 }
833
834 void ubus_init_system(struct ubus_context *ctx)
835 {
836 int ret;
837
838 _ctx = ctx;
839
840 initramfs = !!getenv("INITRAMFS");
841 if (initramfs)
842 unsetenv("INITRAMFS");
843
844 ret = ubus_add_object(ctx, &system_object);
845 if (ret)
846 ERROR("Failed to add object: %s\n", ubus_strerror(ret));
847 }