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