• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt