openvpn: update to 2.5.6
[feed/packages.git] / utils / auc / src / auc.c
1 /*
2 * auc - attendedsysUpgrade CLI
3 * Copyright (C) 2017-2021 Daniel Golle <daniel@makrotopia.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3
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 #ifndef AUC_VERSION
17 #define AUC_VERSION "unknown"
18 #endif
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <dlfcn.h>
24 #include <glob.h>
25 #include <stdio.h>
26 #include <time.h>
27 #include <sys/stat.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include <stdbool.h>
31
32 #include <uci.h>
33 #include <uci_blob.h>
34 #include <json-c/json.h>
35 #include <libubox/ulog.h>
36 #include <libubox/list.h>
37 #include <libubox/vlist.h>
38 #include <libubox/blobmsg_json.h>
39 #include <libubox/avl-cmp.h>
40 #include <libubox/uclient.h>
41 #include <libubox/uclient-utils.h>
42 #include <libubus.h>
43
44 #define REQ_TIMEOUT 15
45
46 #define API_BRANCHES "branches"
47 #define API_INDEX "index"
48 #define API_JSON "json"
49 #define API_JSON_VERSION "v1"
50 #define API_JSON_EXT "." API_JSON
51 #define API_PACKAGES "packages"
52 #define API_REQUEST "api/v1/build"
53 #define API_STATUS_QUEUED "queued"
54 #define API_STATUS_STARTED "started"
55 #define API_STORE "store"
56 #define API_TARGETS "targets"
57
58 #define PUBKEY_PATH "/etc/opkg/keys"
59 #define SHA256SUM "/bin/busybox sha256sum"
60
61 #ifdef AUC_DEBUG
62 #define DPRINTF(...) if (debug) fprintf(stderr, __VA_ARGS__)
63 #else
64 #define DPRINTF(...)
65 #endif
66
67 static const char server_issues[]="https://github.com/aparcar/asu/issues";
68
69 static struct ubus_context *ctx;
70 static struct uclient *ucl = NULL;
71 static char user_agent[80];
72 static char *serverurl;
73 static int upgrade_packages;
74 static struct ustream_ssl_ctx *ssl_ctx;
75 static const struct ustream_ssl_ops *ssl_ops;
76 static off_t out_bytes;
77 static off_t out_len;
78 static off_t out_offset;
79 static bool cur_resume;
80 static int output_fd = -1;
81 static bool retry = false;
82 static char *board_name = NULL;
83 static char *target = NULL;
84 static char *distribution = NULL, *version = NULL, *revision = NULL;
85 static char *rootfs_type = NULL;
86 static int uptodate;
87 static char *filename = NULL;
88 static void *dlh = NULL;
89 static int rc;
90
91 struct branch {
92 struct list_head list;
93 char *name;
94 char *git_branch;
95 char *version;
96 char *version_code;
97 char *version_number;
98 bool snapshot;
99 char *path;
100 char *path_packages;
101 char *arch_packages;
102 char **repos;
103 };
104 static LIST_HEAD(branches);
105
106 struct avl_pkg {
107 struct avl_node avl;
108 char *name;
109 char *version;
110 };
111 static struct avl_tree pkg_tree = AVL_TREE_INIT(pkg_tree, avl_strcmp, false, NULL);
112
113 #ifdef AUC_DEBUG
114 static int debug = 0;
115 #endif
116
117 /*
118 * policy for ubus call system board
119 * see procd/system.c
120 */
121 enum {
122 BOARD_BOARD_NAME,
123 BOARD_RELEASE,
124 BOARD_ROOTFS_TYPE,
125 __BOARD_MAX,
126 };
127
128 static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
129 [BOARD_BOARD_NAME] = { .name = "board_name", .type = BLOBMSG_TYPE_STRING },
130 [BOARD_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_TABLE },
131 [BOARD_ROOTFS_TYPE] = { .name = "rootfs_type", .type = BLOBMSG_TYPE_STRING },
132 };
133
134 /*
135 * policy for release information in system board reply
136 * see procd/system.c
137 */
138 enum {
139 RELEASE_DISTRIBUTION,
140 RELEASE_REVISION,
141 RELEASE_TARGET,
142 RELEASE_VERSION,
143 __RELEASE_MAX,
144 };
145
146 static const struct blobmsg_policy release_policy[__RELEASE_MAX] = {
147 [RELEASE_DISTRIBUTION] = { .name = "distribution", .type = BLOBMSG_TYPE_STRING },
148 [RELEASE_REVISION] = { .name = "revision", .type = BLOBMSG_TYPE_STRING },
149 [RELEASE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
150 [RELEASE_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING },
151 };
152
153 /*
154 * policy for package list returned from rpc-sys or from server
155 * see rpcd/sys.c and ASU sources
156 */
157 enum {
158 PACKAGES_ARCHITECTURE,
159 PACKAGES_PACKAGES,
160 __PACKAGES_MAX,
161 };
162
163 static const struct blobmsg_policy packages_policy[__PACKAGES_MAX] = {
164 [PACKAGES_ARCHITECTURE] = { .name = "architecture", .type = BLOBMSG_TYPE_STRING },
165 [PACKAGES_PACKAGES] = { .name = "packages", .type = BLOBMSG_TYPE_TABLE },
166 };
167
168 /*
169 * policy for upgrade_test
170 * see rpcd/sys.c
171 */
172 enum {
173 UPGTEST_CODE,
174 UPGTEST_STDERR,
175 __UPGTEST_MAX,
176 };
177
178 static const struct blobmsg_policy upgtest_policy[__UPGTEST_MAX] = {
179 [UPGTEST_CODE] = { .name = "code", .type = BLOBMSG_TYPE_INT32 },
180 [UPGTEST_STDERR] = { .name = "stderr", .type = BLOBMSG_TYPE_STRING },
181 };
182
183 /*
184 * policy for branches.json
185 */
186 enum {
187 BRANCH_ENABLED,
188 BRANCH_GIT_BRANCH,
189 BRANCH_NAME,
190 BRANCH_PATH,
191 BRANCH_PATH_PACKAGES,
192 BRANCH_SNAPSHOT,
193 BRANCH_TARGETS,
194 BRANCH_VERSIONS,
195 __BRANCH_MAX,
196 };
197
198 static const struct blobmsg_policy branches_policy[__BRANCH_MAX] = {
199 [BRANCH_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
200 [BRANCH_GIT_BRANCH] = { .name = "git_branch", .type = BLOBMSG_TYPE_STRING },
201 [BRANCH_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
202 [BRANCH_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
203 [BRANCH_PATH_PACKAGES] = { .name = "path_packages", .type = BLOBMSG_TYPE_STRING },
204 [BRANCH_SNAPSHOT] = { .name = "snapshot", .type = BLOBMSG_TYPE_BOOL },
205 [BRANCH_TARGETS] = { .name = "targets", .type = BLOBMSG_TYPE_TABLE },
206 [BRANCH_VERSIONS] = { .name = "versions", .type = BLOBMSG_TYPE_ARRAY },
207 };
208
209 /*
210 * shared policy for target.json and server image request reply
211 */
212 enum {
213 TARGET_ARCH_PACKAGES,
214 TARGET_BINDIR,
215 TARGET_DEVICE_PACKAGES,
216 TARGET_ENQUEUED_AT,
217 TARGET_IMAGES,
218 TARGET_DETAIL,
219 TARGET_MANIFEST,
220 TARGET_METADATA_VERSION,
221 TARGET_REQUEST_HASH,
222 TARGET_QUEUE_POSITION,
223 TARGET_STATUS,
224 TARGET_STDERR,
225 TARGET_STDOUT,
226 TARGET_TARGET,
227 TARGET_TITLES,
228 TARGET_VERSION_CODE,
229 TARGET_VERSION_NUMBER,
230 __TARGET_MAX,
231 };
232
233 static const struct blobmsg_policy target_policy[__TARGET_MAX] = {
234 [TARGET_ARCH_PACKAGES] = { .name = "arch_packages", .type = BLOBMSG_TYPE_STRING },
235 [TARGET_BINDIR] = { .name = "bin_dir", .type = BLOBMSG_TYPE_STRING },
236 [TARGET_DEVICE_PACKAGES] = { .name = "device_packages", .type = BLOBMSG_TYPE_ARRAY },
237 [TARGET_ENQUEUED_AT] = { .name = "enqueued_at", .type = BLOBMSG_TYPE_STRING },
238 [TARGET_IMAGES] = { .name = "images", .type = BLOBMSG_TYPE_ARRAY },
239 [TARGET_MANIFEST] = { .name = "manifest", .type = BLOBMSG_TYPE_TABLE },
240 [TARGET_DETAIL] = { .name = "detail", .type = BLOBMSG_TYPE_STRING },
241 [TARGET_METADATA_VERSION] = { .name = "metadata_version", .type = BLOBMSG_TYPE_INT32 },
242 [TARGET_REQUEST_HASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING },
243 [TARGET_QUEUE_POSITION] = { .name = "queue_position", .type = BLOBMSG_TYPE_INT32 },
244 [TARGET_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
245 [TARGET_STDERR] = { .name = "stderr", .type = BLOBMSG_TYPE_STRING },
246 [TARGET_STDOUT] = { .name = "stdout", .type = BLOBMSG_TYPE_STRING },
247 [TARGET_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
248 [TARGET_TITLES] = { .name = "titles", .type = BLOBMSG_TYPE_ARRAY },
249 [TARGET_VERSION_CODE] = { .name = "version_code", .type = BLOBMSG_TYPE_STRING },
250 [TARGET_VERSION_NUMBER] = { .name = "version_number", .type = BLOBMSG_TYPE_STRING },
251 };
252
253 /*
254 * policy for images object in target
255 */
256 enum {
257 IMAGES_FILESYSTEM,
258 IMAGES_NAME,
259 IMAGES_SHA256,
260 IMAGES_TYPE,
261 __IMAGES_MAX,
262 };
263
264 static const struct blobmsg_policy images_policy[__IMAGES_MAX] = {
265 [IMAGES_FILESYSTEM] = { .name = "filesystem", .type = BLOBMSG_TYPE_STRING },
266 [IMAGES_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
267 [IMAGES_SHA256] = { .name = "sha256", .type = BLOBMSG_TYPE_STRING },
268 [IMAGES_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
269 };
270
271 /*
272 * generic policy for HTTP JSON reply
273 */
274 enum {
275 REPLY_ARRAY,
276 REPLY_OBJECT,
277 __REPLY_MAX,
278 };
279
280 static const struct blobmsg_policy reply_policy[__REPLY_MAX] = {
281 [REPLY_ARRAY] = { .name = "reply", .type = BLOBMSG_TYPE_ARRAY },
282 [REPLY_OBJECT] = { .name = "reply", .type = BLOBMSG_TYPE_TABLE },
283 };
284
285 /*
286 * policy for HTTP headers received from server
287 */
288 enum {
289 H_LEN,
290 H_RANGE,
291 H_UNKNOWN_PACKAGE,
292 H_QUEUE_POSITION,
293 __H_MAX
294 };
295
296 static const struct blobmsg_policy header_policy[__H_MAX] = {
297 [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
298 [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
299 [H_UNKNOWN_PACKAGE] = { .name = "x-unknown-package", .type = BLOBMSG_TYPE_STRING },
300 [H_QUEUE_POSITION] = { .name = "x-queue-position", .type = BLOBMSG_TYPE_INT32 },
301 };
302
303 /*
304 * load serverurl from UCI
305 */
306 static int load_config() {
307 struct uci_context *uci_ctx;
308 struct uci_package *uci_attendedsysupgrade;
309 struct uci_section *uci_s;
310 char *url;
311
312 uci_ctx = uci_alloc_context();
313 if (!uci_ctx)
314 return -1;
315
316 uci_ctx->flags &= ~UCI_FLAG_STRICT;
317
318 if (uci_load(uci_ctx, "attendedsysupgrade", &uci_attendedsysupgrade) ||
319 !uci_attendedsysupgrade) {
320 fprintf(stderr, "Failed to load attendedsysupgrade config\n");
321 return -1;
322 }
323 uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "server");
324 if (!uci_s) {
325 fprintf(stderr, "Failed to read server config section\n");
326 return -1;
327 }
328 url = uci_lookup_option_string(uci_ctx, uci_s, "url");
329 if (!url) {
330 fprintf(stderr, "Failed to read server url from config\n");
331 return -1;
332 }
333 if (strncmp(url, "https://", strlen("https://")) &&
334 strncmp(url, "http://", strlen("http://"))) {
335 fprintf(stderr, "Server url invalid (needs to be http://... or https://...)\n");
336 return -1;
337 }
338
339 serverurl = strdup(url);
340
341 uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "client");
342 if (!uci_s) {
343 fprintf(stderr, "Failed to read client config\n");
344 return -1;
345 }
346 upgrade_packages = atoi(uci_lookup_option_string(uci_ctx, uci_s, "upgrade_packages"));
347
348 uci_free_context(uci_ctx);
349
350 return 0;
351 }
352
353 /*
354 * libdpkg - Debian packaging suite library routines
355 * vercmp.c - comparison of version numbers
356 *
357 * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
358 */
359
360 /* assume ascii; warning: evaluates x multiple times! */
361 #define order(x) ((x) == '~' ? -1 \
362 : isdigit((x)) ? 0 \
363 : !(x) ? 0 \
364 : isalpha((x)) ? (x) \
365 : (x) + 256)
366
367 static int verrevcmp(const char *val, const char *ref)
368 {
369 if (!val)
370 val = "";
371 if (!ref)
372 ref = "";
373
374 while (*val || *ref) {
375 int first_diff = 0;
376
377 while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) {
378 int vc = order(*val), rc = order(*ref);
379 if (vc != rc)
380 return vc - rc;
381 val++;
382 ref++;
383 }
384
385 while (*val == '0')
386 val++;
387 while (*ref == '0')
388 ref++;
389 while (isdigit(*val) && isdigit(*ref)) {
390 if (!first_diff)
391 first_diff = *val - *ref;
392 val++;
393 ref++;
394 }
395 if (isdigit(*val))
396 return 1;
397 if (isdigit(*ref))
398 return -1;
399 if (first_diff)
400 return first_diff;
401 }
402 return 0;
403 }
404
405 /*
406 * replace '-rc' by '~' in string
407 */
408 static inline void release_replace_rc(char *ver)
409 {
410 char *tmp;
411
412 tmp = strstr(ver, "-rc");
413 if (tmp && strlen(tmp) > 3) {
414 *tmp = '~';
415 memmove(tmp + 1, tmp + 3, strlen(tmp + 3) + 1);
416 }
417 }
418
419 /*
420 * OpenWrt release version string comperator
421 * replaces '-rc' by '~' to fix ordering of release(s) (candidates)
422 * using the void release_replace_rc(char *ver) function above.
423 */
424 static int openwrt_release_verrevcmp(const char *ver1, const char *ver2)
425 {
426 char mver1[16], mver2[16];
427
428 strncpy(mver1, ver1, sizeof(mver1) - 1);
429 mver1[sizeof(mver1) - 1] = '\0';
430 strncpy(mver2, ver2, sizeof(mver2) - 1);
431 mver2[sizeof(mver2) - 1] = '\0';
432
433 release_replace_rc(mver1);
434 release_replace_rc(mver2);
435
436 return verrevcmp(mver1, mver2);
437 }
438
439
440 /**
441 * UBUS response callbacks
442 */
443 /*
444 * rpc-sys packagelist
445 * append array of package names to blobbuf given in req->priv
446 */
447 #define ANSI_ESC "\x1b"
448 #define ANSI_COLOR_RESET ANSI_ESC "[0m"
449 #define ANSI_COLOR_RED ANSI_ESC "[1;31m"
450 #define ANSI_COLOR_GREEN ANSI_ESC "[1;32m"
451 #define ANSI_CURSOR_SAFE "[s"
452 #define ANSI_CURSOR_RESTORE "[u"
453 #define ANSI_ERASE_LINE "[K"
454
455 #define PKG_UPGRADE 0x1
456 #define PKG_DOWNGRADE 0x2
457 #define PKG_NOT_FOUND 0x4
458 #define PKG_ERROR 0x8
459
460 static inline bool is_builtin_pkg(const char *pkgname)
461 {
462 return !strcmp(pkgname, "libc") ||
463 !strcmp(pkgname, "librt") ||
464 !strcmp(pkgname, "libpthread") ||
465 !strcmp(pkgname, "kernel");
466 }
467
468 static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg)
469 {
470 int *status = (int *)req->priv;
471 struct blob_attr *tb[__PACKAGES_MAX], *cur;
472 struct avl_pkg *pkg;
473 int rem;
474 int cmpres;
475
476 blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
477
478 if (!tb[PACKAGES_PACKAGES])
479 return;
480
481 blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
482 if (is_builtin_pkg(blobmsg_name(cur)))
483 continue;
484
485 pkg = avl_find_element(&pkg_tree, blobmsg_name(cur), pkg, avl);
486 if (!pkg) {
487 fprintf(stderr, "installed package %s%s%s cannot be found in remote list!\n",
488 ANSI_COLOR_RED, blobmsg_name(cur), ANSI_COLOR_RESET);
489 *status |= PKG_NOT_FOUND;
490 continue;
491 }
492
493 cmpres = verrevcmp(blobmsg_get_string(cur), pkg->version);
494 if (cmpres < 0)
495 *status |= PKG_UPGRADE;
496
497 if (cmpres > 0)
498 *status |= PKG_DOWNGRADE;
499
500 if (cmpres
501 #ifdef AUC_DEBUG
502 || debug
503 #endif
504 )
505 fprintf(stderr, " %s: %s%s -> %s%s\n", blobmsg_name(cur),
506 (!cmpres)?"":(cmpres > 0)?ANSI_COLOR_RED:ANSI_COLOR_GREEN,
507 blobmsg_get_string(cur), pkg->version,
508 (cmpres)?ANSI_COLOR_RESET:"");
509 }
510 }
511
512 /*
513 * rpc-sys packagelist
514 * append array of package names to blobbuf given in req->priv
515 */
516 static void pkglist_req_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
517 struct blob_buf *buf = (struct blob_buf *)req->priv;
518 struct blob_attr *tb[__PACKAGES_MAX];
519 struct blob_attr *cur;
520 int rem;
521 struct avl_pkg *pkg;
522 void *table;
523
524 blobmsg_parse(packages_policy, __PACKAGES_MAX, tb, blob_data(msg), blob_len(msg));
525
526 if (!tb[PACKAGES_PACKAGES]) {
527 fprintf(stderr, "No packagelist received\n");
528 return;
529 }
530
531 table = blobmsg_open_table(buf, "packages_versions");
532
533 blobmsg_for_each_attr(cur, tb[PACKAGES_PACKAGES], rem) {
534 if (is_builtin_pkg(blobmsg_name(cur)))
535 continue;
536
537 pkg = avl_find_element(&pkg_tree, blobmsg_name(cur), pkg, avl);
538 if (!pkg)
539 continue;
540
541 blobmsg_add_string(buf, blobmsg_name(cur), pkg->version);
542 }
543 blobmsg_close_table(buf, table);
544 };
545
546 /*
547 * system board
548 * append append board information to blobbuf given in req->priv
549 * populate board and release global strings
550 */
551 static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
552 struct blob_buf *buf = (struct blob_buf *)req->priv;
553 struct blob_attr *tb[__BOARD_MAX];
554 struct blob_attr *rel[__RELEASE_MAX];
555
556 blobmsg_parse(board_policy, __BOARD_MAX, tb, blob_data(msg), blob_len(msg));
557
558
559 if (!tb[BOARD_RELEASE]) {
560 fprintf(stderr, "No release received\n");
561 rc=-ENODATA;
562 return;
563 }
564
565 blobmsg_parse(release_policy, __RELEASE_MAX, rel,
566 blobmsg_data(tb[BOARD_RELEASE]), blobmsg_data_len(tb[BOARD_RELEASE]));
567
568 if (!rel[RELEASE_TARGET] ||
569 !rel[RELEASE_DISTRIBUTION] ||
570 !rel[RELEASE_VERSION] ||
571 !rel[RELEASE_REVISION]) {
572 fprintf(stderr, "No release information received\n");
573 rc=-ENODATA;
574 return;
575 }
576
577 target = strdup(blobmsg_get_string(rel[RELEASE_TARGET]));
578 distribution = strdup(blobmsg_get_string(rel[RELEASE_DISTRIBUTION]));
579 version = strdup(blobmsg_get_string(rel[RELEASE_VERSION]));
580 revision = strdup(blobmsg_get_string(rel[RELEASE_REVISION]));
581
582 if (!strcmp(target, "x86/64") || !strcmp(target, "x86/generic")) {
583 /*
584 * ugly work-around ahead:
585 * ignore board name on generic x86 targets, as image name is always 'generic'
586 */
587 board_name = strdup("generic");
588 } else {
589 if (!tb[BOARD_BOARD_NAME]) {
590 fprintf(stderr, "No board name received\n");
591 rc=-ENODATA;
592 return;
593 }
594 board_name = strdup(blobmsg_get_string(tb[BOARD_BOARD_NAME]));
595 }
596
597 if (tb[BOARD_ROOTFS_TYPE])
598 rootfs_type = strdup(blobmsg_get_string(tb[BOARD_ROOTFS_TYPE]));
599
600 blobmsg_add_string(buf, "target", target);
601 blobmsg_add_string(buf, "version", version);
602 blobmsg_add_string(buf, "revision", revision);
603 }
604
605 /*
606 * rpc-sys upgrade_test
607 * check if downloaded file is accepted by sysupgrade
608 */
609 static void upgtest_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
610 int *valid = (int *)req->priv;
611 struct blob_attr *tb[__UPGTEST_MAX];
612
613 blobmsg_parse(upgtest_policy, __UPGTEST_MAX, tb, blob_data(msg), blob_len(msg));
614
615 if (!tb[UPGTEST_CODE]) {
616 fprintf(stderr, "No sysupgrade test return code received\n");
617 return;
618 }
619
620 *valid = (blobmsg_get_u32(tb[UPGTEST_CODE]) == 0)?1:0;
621
622 if (tb[UPGTEST_STDERR])
623 fprintf(stderr, "%s", blobmsg_get_string(tb[UPGTEST_STDERR]));
624 else if (*valid == 0)
625 fprintf(stderr, "image verification failed\n");
626 else
627 fprintf(stderr, "image verification succeeded\n");
628 };
629
630 /**
631 * uclient stuff
632 */
633 static int open_output_file(const char *path, uint64_t resume_offset)
634 {
635 char *filename = NULL;
636 int flags;
637 int ret;
638
639 if (cur_resume)
640 flags = O_RDWR;
641 else
642 flags = O_WRONLY | O_EXCL;
643
644 flags |= O_CREAT;
645
646 filename = uclient_get_url_filename(path, "firmware.bin");
647
648 fprintf(stderr, "Writing to '%s'\n", filename);
649 ret = open(filename, flags, 0644);
650 if (ret < 0)
651 goto free;
652
653 if (resume_offset &&
654 lseek(ret, resume_offset, SEEK_SET) < 0) {
655 fprintf(stderr, "Failed to seek %"PRIu64" bytes in output file\n", resume_offset);
656 close(ret);
657 ret = -1;
658 goto free;
659 }
660
661 out_offset = resume_offset;
662 out_bytes += resume_offset;
663
664 free:
665 free(filename);
666 return ret;
667 }
668
669 struct jsonblobber {
670 json_tokener *tok;
671 struct blob_buf *outbuf;
672 };
673
674 static void request_done(struct uclient *cl)
675 {
676 struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
677 if (jsb) {
678 json_tokener_free(jsb->tok);
679 free(jsb);
680 };
681
682 uclient_disconnect(cl);
683 uloop_end();
684 }
685
686 static void header_done_cb(struct uclient *cl)
687 {
688 struct blob_attr *tb[__H_MAX];
689 struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
690 struct blob_buf *outbuf = NULL;
691
692 if (jsb)
693 outbuf = jsb->outbuf;
694
695 uint64_t resume_offset = 0, resume_end, resume_size;
696
697 if (uclient_http_redirect(cl)) {
698 fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host);
699
700 return;
701 }
702
703 if (cl->status_code == 204 && cur_resume) {
704 /* Resume attempt failed, try normal download */
705 cur_resume = false;
706 //init_request(cl);
707 return;
708 }
709
710 DPRINTF("status code: %d\n", cl->status_code);
711 DPRINTF("headers:\n%s\n", blobmsg_format_json_indent(cl->meta, true, 0));
712 blobmsg_parse(header_policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta));
713
714 switch (cl->status_code) {
715 case 400:
716 request_done(cl);
717 rc=-ESRCH;
718 break;
719 case 422:
720 fprintf(stderr, "unknown package '%s' requested.\n",
721 blobmsg_get_string(tb[H_UNKNOWN_PACKAGE]));
722 rc=-ENOPKG;
723 request_done(cl);
724 break;
725 case 201:
726 case 202:
727 retry = true;
728 if (!outbuf)
729 break;
730
731 blobmsg_add_u32(outbuf, "status", cl->status_code);
732
733 if (tb[H_QUEUE_POSITION])
734 blobmsg_add_u32(outbuf, "queue_position", blobmsg_get_u32(tb[H_QUEUE_POSITION]));
735
736 break;
737 case 200:
738 retry = false;
739 if (cl->priv)
740 break;
741
742 if (tb[H_LEN])
743 out_len = strtoul(blobmsg_get_string(tb[H_LEN]), NULL, 10);
744
745 output_fd = open_output_file(cl->url->location, resume_offset);
746 if (output_fd < 0) {
747 perror("Cannot open output file");
748 request_done(cl);
749 }
750 break;
751 case 500:
752 /* server may reply JSON object */
753 break;
754
755 default:
756 DPRINTF("HTTP error %d\n", cl->status_code);
757 request_done(cl);
758 break;
759 }
760 }
761
762 static void read_data_cb(struct uclient *cl)
763 {
764 char buf[256];
765 int len;
766 json_object *jsobj;
767 struct blob_buf *outbuf = NULL;
768 json_tokener *tok = NULL;
769 struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
770
771 if (!jsb) {
772 while (1) {
773 len = uclient_read(cl, buf, sizeof(buf));
774 if (!len)
775 return;
776
777 out_bytes += len;
778 write(output_fd, buf, len);
779 }
780 return;
781 }
782
783 outbuf = jsb->outbuf;
784 tok = jsb->tok;
785
786 while (1) {
787 len = uclient_read(cl, buf, sizeof(buf));
788 if (!len)
789 break;
790
791 out_bytes += len;
792
793 jsobj = json_tokener_parse_ex(tok, buf, len);
794
795 if (json_tokener_get_error(tok) == json_tokener_continue)
796 continue;
797
798 if (json_tokener_get_error(tok) != json_tokener_success)
799 break;
800
801 if (jsobj)
802 {
803 blobmsg_add_json_element(outbuf, "reply", jsobj);
804
805 json_object_put(jsobj);
806 break;
807 }
808 }
809 }
810
811 static void eof_cb(struct uclient *cl)
812 {
813 if (!cl->data_eof && !uptodate) {
814 fprintf(stderr, "Connection reset prematurely\n");
815 }
816 request_done(cl);
817 }
818
819 static void handle_uclient_error(struct uclient *cl, int code)
820 {
821 const char *type = "Unknown error";
822
823 switch(code) {
824 case UCLIENT_ERROR_CONNECT:
825 type = "Connection failed";
826 break;
827 case UCLIENT_ERROR_TIMEDOUT:
828 type = "Connection timed out";
829 break;
830 case UCLIENT_ERROR_SSL_INVALID_CERT:
831 type = "Invalid SSL certificate";
832 break;
833 case UCLIENT_ERROR_SSL_CN_MISMATCH:
834 type = "Server hostname does not match SSL certificate";
835 break;
836 default:
837 break;
838 }
839
840 fprintf(stderr, "Connection error: %s\n", type);
841
842 request_done(cl);
843 }
844
845 static const struct uclient_cb check_cb = {
846 .header_done = header_done_cb,
847 .data_read = read_data_cb,
848 .data_eof = eof_cb,
849 .error = handle_uclient_error,
850 };
851
852 static int server_request(const char *url, struct blob_buf *inbuf, struct blob_buf *outbuf) {
853 struct jsonblobber *jsb = NULL;
854 int rc = -ENOENT;
855 char *post_data;
856 out_offset = 0;
857 out_bytes = 0;
858 out_len = 0;
859
860 #ifdef AUC_DEBUG
861 if (debug)
862 fprintf(stderr, "Requesting URL: %s\n", url);
863 #endif
864
865 if (outbuf) {
866 jsb = malloc(sizeof(struct jsonblobber));
867 jsb->outbuf = outbuf;
868 jsb->tok = json_tokener_new();
869 };
870
871 if (!ucl) {
872 ucl = uclient_new(url, NULL, &check_cb);
873 uclient_http_set_ssl_ctx(ucl, ssl_ops, ssl_ctx, 1);
874 ucl->timeout_msecs = REQ_TIMEOUT * 1000;
875 } else {
876 uclient_set_url(ucl, url, NULL);
877 }
878
879 ucl->priv = jsb;
880
881 rc = uclient_connect(ucl);
882 if (rc)
883 return rc;
884
885 rc = uclient_http_set_request_type(ucl, inbuf?"POST":"GET");
886 if (rc)
887 return rc;
888
889 uclient_http_reset_headers(ucl);
890 uclient_http_set_header(ucl, "User-Agent", user_agent);
891 if (inbuf) {
892 uclient_http_set_header(ucl, "Content-Type", "application/json");
893 post_data = blobmsg_format_json(inbuf->head, true);
894 uclient_write(ucl, post_data, strlen(post_data));
895 }
896 rc = uclient_request(ucl);
897 if (rc)
898 return rc;
899
900 uloop_run();
901
902 return 0;
903 }
904
905 /**
906 * ustream-ssl
907 */
908 static int init_ustream_ssl(void) {
909 glob_t gl;
910 int i;
911
912 dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
913 if (!dlh)
914 return -ENOENT;
915
916 ssl_ops = dlsym(dlh, "ustream_ssl_ops");
917 if (!ssl_ops)
918 return -ENOENT;
919
920 ssl_ctx = ssl_ops->context_new(false);
921
922 glob("/etc/ssl/certs/*.crt", 0, NULL, &gl);
923 if (!gl.gl_pathc)
924 return -ENOKEY;
925
926 for (i = 0; i < gl.gl_pathc; i++)
927 ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]);
928
929 return 0;
930 }
931
932 static int ask_user(void)
933 {
934 char user_input;
935 fprintf(stderr, "Are you sure you want to continue the upgrade process? [N/y] ");
936 user_input = getchar();
937 if ((user_input != 'y') && (user_input != 'Y'))
938 return -EINTR;
939
940 return 0;
941 }
942
943 static char* alloc_replace_var(char *in, const char *var, const char *replace)
944 {
945 char *tmp = in;
946 char *res = NULL;
947 char *eptr;
948
949 while ((tmp = strchr(tmp, '{'))) {
950 ++tmp;
951 eptr = strchr(tmp, '}');
952 if (!eptr)
953 return NULL;
954
955 if (!strncmp(tmp, var, (unsigned int)(eptr - tmp))) {
956 asprintf(&res, "%.*s%s%s",
957 (unsigned int)(tmp - in) - 1, in, replace, eptr + 1);
958 break;
959 }
960 }
961
962 if (!res)
963 res = strdup(in);
964
965 return res;
966 }
967
968 static int request_target(struct branch *br, char *url)
969 {
970 static struct blob_buf boardbuf;
971 struct blob_attr *tbr[__REPLY_MAX], *tb[__TARGET_MAX];
972
973 blobmsg_buf_init(&boardbuf);
974
975 if ((rc = server_request(url, NULL, &boardbuf))) {
976 blob_buf_free(&boardbuf);
977 return rc;
978 }
979
980 blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(boardbuf.head), blob_len(boardbuf.head));
981
982 if (!tbr[REPLY_OBJECT])
983 return -ENODATA;
984
985 blobmsg_parse(target_policy, __TARGET_MAX, tb, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
986
987 if (!tb[TARGET_METADATA_VERSION] ||
988 !tb[TARGET_ARCH_PACKAGES] ||
989 !tb[TARGET_IMAGES] ||
990 !tb[TARGET_TARGET]) {
991 blob_buf_free(&boardbuf);
992 return -ENODATA;
993 }
994
995 if (blobmsg_get_u32(tb[TARGET_METADATA_VERSION]) != 1) {
996 blob_buf_free(&boardbuf);
997 return -EPFNOSUPPORT;
998 }
999
1000 if (strcmp(blobmsg_get_string(tb[TARGET_TARGET]), target))
1001 return -EINVAL;
1002
1003 if (strcmp(blobmsg_get_string(tb[TARGET_ARCH_PACKAGES]), br->arch_packages))
1004 return -EINVAL;
1005
1006 if (tb[TARGET_VERSION_CODE])
1007 br->version_code = strdup(blobmsg_get_string(tb[TARGET_VERSION_CODE]));
1008
1009 if (tb[TARGET_VERSION_NUMBER])
1010 br->version_number = strdup(blobmsg_get_string(tb[TARGET_VERSION_NUMBER]));
1011
1012 blob_buf_free(&boardbuf);
1013 return 0;
1014 };
1015
1016 static char* validate_target(struct blob_attr *branch)
1017 {
1018 struct blob_attr *cur;
1019 int rem;
1020
1021 blobmsg_for_each_attr(cur, branch, rem)
1022 if (!strcmp(blobmsg_name(cur), target))
1023 return strdup(blobmsg_get_string(cur));
1024
1025 return NULL;
1026 }
1027
1028 static void process_branch(struct blob_attr *branch, bool only_active)
1029 {
1030 struct blob_attr *tb[__BRANCH_MAX];
1031 struct blob_attr *curver;
1032 int remver;
1033 struct branch *br;
1034 char *tmp, *arch_packages, *board_json_file;
1035 const char *brname;
1036
1037 blobmsg_parse(branches_policy, __BRANCH_MAX, tb, blobmsg_data(branch), blobmsg_len(branch));
1038
1039 /* mandatory fields */
1040 if (!(tb[BRANCH_ENABLED] && blobmsg_get_bool(tb[BRANCH_ENABLED]) &&
1041 tb[BRANCH_NAME] && tb[BRANCH_PATH] && tb[BRANCH_PATH_PACKAGES] &&
1042 tb[BRANCH_VERSIONS] && tb[BRANCH_TARGETS]))
1043 return;
1044
1045 brname = blobmsg_get_string(tb[BRANCH_NAME]);
1046 if (only_active && strncmp(brname, version, strlen(brname)))
1047 return;
1048
1049 /* check if target is offered in branch and get arch_packages */
1050 arch_packages = validate_target(tb[BRANCH_TARGETS]);
1051 if (!arch_packages)
1052 return;
1053
1054 /* add each version of the branch */
1055 blobmsg_for_each_attr(curver, tb[BRANCH_VERSIONS], remver) {
1056 br = malloc(sizeof(struct branch));
1057
1058 if (tb[BRANCH_GIT_BRANCH])
1059 br->git_branch = strdup(blobmsg_get_string(tb[BRANCH_GIT_BRANCH]));
1060
1061 br->name = strdup(blobmsg_get_string(tb[BRANCH_NAME]));
1062 br->path = strdup(blobmsg_get_string(tb[BRANCH_PATH]));
1063 br->path_packages = strdup(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]));
1064
1065 br->version = strdup(blobmsg_get_string(curver));
1066 br->snapshot = !!strcasestr(blobmsg_get_string(curver), "snapshot");
1067 br->path = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH]), "version", br->version);
1068 br->path_packages = alloc_replace_var(blobmsg_get_string(tb[BRANCH_PATH_PACKAGES]), "branch", br->name);
1069 br->arch_packages = arch_packages;
1070 if (!br->path || !br->path_packages) {
1071 free(br);
1072 continue;
1073 }
1074
1075 asprintf(&board_json_file, "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
1076 API_JSON_VERSION, br->path, API_TARGETS, target, board_name, API_JSON_EXT);
1077 tmp = board_json_file;
1078 while ((tmp = strchr(tmp, ',')))
1079 *tmp = '_';
1080
1081 if (request_target(br, board_json_file)) {
1082 free(board_json_file);
1083 free(br);
1084 continue;
1085 }
1086
1087 free(board_json_file);
1088 list_add_tail(&br->list, &branches);
1089 }
1090 }
1091
1092 static int request_branches(bool only_active)
1093 {
1094 static struct blob_buf brbuf;
1095 struct blob_attr *cur;
1096 struct blob_attr *tb[__REPLY_MAX];
1097 int rem;
1098 char url[256];
1099 struct blob_attr *data;
1100
1101 blobmsg_buf_init(&brbuf);
1102 snprintf(url, sizeof(url), "%s/%s/%s/%s%s", serverurl, API_JSON,
1103 API_JSON_VERSION, API_BRANCHES, API_JSON_EXT);
1104
1105 if ((rc = server_request(url, NULL, &brbuf))) {
1106 blob_buf_free(&brbuf);
1107 return rc;
1108 };
1109
1110 blobmsg_parse(reply_policy, __REPLY_MAX, tb, blob_data(brbuf.head), blob_len(brbuf.head));
1111
1112 /* newer server API replies OBJECT, older API replies ARRAY... */
1113 if ((!tb[REPLY_ARRAY] && !tb[REPLY_OBJECT]))
1114 return -ENODATA;
1115
1116 if (tb[REPLY_OBJECT])
1117 data = tb[REPLY_OBJECT];
1118 else
1119 data = tb[REPLY_ARRAY];
1120
1121 blobmsg_for_each_attr(cur, data, rem)
1122 process_branch(cur, only_active);
1123
1124 blob_buf_free(&brbuf);
1125
1126 return 0;
1127 }
1128
1129 static struct branch *select_branch(char *name, char *select_version)
1130 {
1131 struct branch *br, *abr = NULL;
1132
1133 if (!name)
1134 name = version;
1135
1136 list_for_each_entry(br, &branches, list) {
1137 /* if branch name doesn't match version *prefix*, skip */
1138 if (strncasecmp(br->name, name, strlen(br->name)))
1139 continue;
1140
1141 if (select_version) {
1142 if (!strcasecmp(br->version, select_version)) {
1143 abr = br;
1144 break;
1145 }
1146 } else {
1147 if (strcasestr(name, "snapshot")) {
1148 /* if we are on the snapshot branch, stay there */
1149 if (br->snapshot) {
1150 abr = br;
1151 break;
1152 }
1153 } else {
1154 /* on release branch, skip snapshots and pick latest release */
1155 if (br->snapshot)
1156 continue;
1157
1158 if (!abr || (openwrt_release_verrevcmp(abr->version, br->version) < 0))
1159 abr = br;
1160 }
1161 }
1162 }
1163
1164 return abr;
1165 }
1166
1167 static int add_upg_packages(struct blob_attr *reply, char *arch)
1168 {
1169 struct blob_attr *tbr[__REPLY_MAX];
1170 struct blob_attr *tba[__PACKAGES_MAX];
1171 struct blob_attr *packages;
1172 struct blob_attr *cur;
1173 int rem;
1174 struct avl_pkg *avpk;
1175
1176 blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(reply), blob_len(reply));
1177
1178 if (!tbr[REPLY_OBJECT])
1179 return -ENODATA;
1180
1181 if (arch) {
1182 blobmsg_parse(packages_policy, __PACKAGES_MAX, tba, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
1183 if (!tba[PACKAGES_ARCHITECTURE] ||
1184 !tba[PACKAGES_PACKAGES])
1185 return -ENODATA;
1186
1187 if (strcmp(blobmsg_get_string(tba[PACKAGES_ARCHITECTURE]), arch))
1188 return -EBADMSG;
1189
1190 packages = tba[PACKAGES_PACKAGES];
1191 } else {
1192 packages = tbr[REPLY_OBJECT];
1193 }
1194
1195 blobmsg_for_each_attr(cur, packages, rem) {
1196 avpk = malloc(sizeof(struct avl_pkg));
1197 if (!avpk)
1198 return -ENOMEM;
1199
1200 avpk->name = strdup(blobmsg_name(cur));
1201 if (!avpk->name) {
1202 free(avpk);
1203 return -ENOMEM;
1204 }
1205
1206 avpk->version = strdup(blobmsg_get_string(cur));
1207 if (!avpk->version) {
1208 free(avpk->name);
1209 free(avpk);
1210 return -ENOMEM;
1211 }
1212
1213 avpk->avl.key = avpk->name;
1214 if (avl_insert(&pkg_tree, &avpk->avl)) {
1215
1216 #ifdef AUC_DEBUG
1217 if (debug)
1218 fprintf(stderr, "failed to insert package %s (%s)!\n", blobmsg_name(cur), blobmsg_get_string(cur));
1219 #endif
1220
1221 if (avpk->name)
1222 free(avpk->name);
1223
1224 if (avpk->version)
1225 free(avpk->version);
1226
1227 free(avpk);
1228 }
1229 }
1230
1231 return 0;
1232 }
1233
1234 static int request_packages(struct branch *branch)
1235 {
1236 static struct blob_buf pkgbuf, archpkgbuf;
1237 char url[256];
1238 int ret;
1239
1240 fprintf(stderr, "Requesting package lists...\n");
1241
1242 blobmsg_buf_init(&archpkgbuf);
1243 snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s/%s%s", serverurl, API_JSON,
1244 API_JSON_VERSION, branch->path, API_TARGETS, target, API_INDEX, API_JSON_EXT);
1245 if ((rc = server_request(url, NULL, &archpkgbuf))) {
1246 blob_buf_free(&archpkgbuf);
1247 return rc;
1248 };
1249
1250 ret = add_upg_packages(archpkgbuf.head, branch->arch_packages);
1251 blob_buf_free(&archpkgbuf);
1252
1253 if (ret)
1254 return ret;
1255
1256 blobmsg_buf_init(&pkgbuf);
1257 snprintf(url, sizeof(url), "%s/%s/%s/%s/%s/%s-%s%s", serverurl, API_JSON,
1258 API_JSON_VERSION, branch->path, API_PACKAGES, branch->arch_packages,
1259 API_INDEX, API_JSON_EXT);
1260 if ((rc = server_request(url, NULL, &pkgbuf))) {
1261 blob_buf_free(&archpkgbuf);
1262 blob_buf_free(&pkgbuf);
1263 return rc;
1264 };
1265
1266 ret = add_upg_packages(pkgbuf.head, NULL);
1267 blob_buf_free(&pkgbuf);
1268
1269 return ret;
1270 }
1271
1272
1273 static int check_installed_packages(struct blob_attr *pkgs)
1274 {
1275 static struct blob_buf allpkg;
1276 uint32_t id;
1277 int status = 0;
1278
1279 blob_buf_init(&allpkg, 0);
1280 blobmsg_add_u8(&allpkg, "all", 1);
1281 blobmsg_add_string(&allpkg, "dummy", "foo");
1282 if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
1283 ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_check_cb, &status, 3000)) {
1284 fprintf(stderr, "cannot request packagelist from rpcd\n");
1285 status |= PKG_ERROR;
1286 }
1287
1288 return status;
1289 }
1290
1291 static int req_add_selected_packages(struct blob_buf *req)
1292 {
1293 static struct blob_buf allpkg;
1294 uint32_t id;
1295
1296 blob_buf_init(&allpkg, 0);
1297 blobmsg_add_u8(&allpkg, "all", 0);
1298 blobmsg_add_string(&allpkg, "dummy", "foo");
1299 if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
1300 ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_req_cb, req, 3000)) {
1301 fprintf(stderr, "cannot request packagelist from rpcd\n");
1302 return -EFAULT;
1303 }
1304
1305 return 0;
1306 }
1307
1308 #if defined(__amd64__) || defined(__i386__)
1309 static int system_is_efi(void)
1310 {
1311 const char efidname[] = "/sys/firmware/efi/efivars";
1312 int fd = open(efidname, O_DIRECTORY | O_PATH);
1313
1314 if (fd != -1) {
1315 close(fd);
1316 return 1;
1317 } else {
1318 return 0;
1319 }
1320 }
1321 #else
1322 static inline int system_is_efi(void) { return 0; }
1323 #endif
1324
1325 static int get_image_by_type(struct blob_attr *images, const char *typestr, const char *fstype, char **image_name, char **image_sha256)
1326 {
1327 struct blob_attr *tb[__IMAGES_MAX];
1328 struct blob_attr *cur;
1329 int rem, ret = -ENOENT;
1330
1331 blobmsg_for_each_attr(cur, images, rem) {
1332 blobmsg_parse(images_policy, __IMAGES_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
1333 if (!tb[IMAGES_FILESYSTEM] ||
1334 !tb[IMAGES_NAME] ||
1335 !tb[IMAGES_TYPE] ||
1336 !tb[IMAGES_SHA256])
1337 continue;
1338
1339 if (fstype && strcmp(blobmsg_get_string(tb[IMAGES_FILESYSTEM]), fstype))
1340 continue;
1341
1342 if (!strcmp(blobmsg_get_string(tb[IMAGES_TYPE]), typestr)) {
1343 *image_name = strdup(blobmsg_get_string(tb[IMAGES_NAME]));
1344 *image_sha256 = strdup(blobmsg_get_string(tb[IMAGES_SHA256]));
1345 ret = 0;
1346 break;
1347 }
1348 }
1349
1350 return ret;
1351 }
1352
1353 static int select_image(struct blob_attr *images, const char *target_fstype, char **image_name, char **image_sha256)
1354 {
1355 const char *combined_type;
1356 const char *fstype = rootfs_type;
1357 int ret = -ENOENT;
1358
1359 if (target_fstype)
1360 fstype = target_fstype;
1361
1362 if (system_is_efi())
1363 combined_type = "combined-efi";
1364 else
1365 combined_type = "combined";
1366
1367 DPRINTF("images: %s\n", blobmsg_format_json_indent(images, true, 0));
1368
1369 if (fstype) {
1370 ret = get_image_by_type(images, "sysupgrade", fstype, image_name, image_sha256);
1371 if (!ret)
1372 return 0;
1373
1374 ret = get_image_by_type(images, combined_type, fstype, image_name, image_sha256);
1375 if (!ret)
1376 return 0;
1377
1378 ret = get_image_by_type(images, "sdcard", fstype, image_name, image_sha256);
1379 if (!ret)
1380 return 0;
1381 }
1382
1383 /* fallback to squashfs unless fstype requested explicitly */
1384 if (!target_fstype) {
1385 ret = get_image_by_type(images, "sysupgrade", "squashfs", image_name, image_sha256);
1386 if (!ret)
1387 return 0;
1388
1389 ret = get_image_by_type(images, combined_type, "squashfs", image_name, image_sha256);
1390 if (!ret)
1391 return 0;
1392
1393 ret = get_image_by_type(images, "sdcard", fstype, image_name, image_sha256);
1394 }
1395
1396 return ret;
1397 }
1398
1399 static bool validate_sha256(char *filename, char *sha256str)
1400 {
1401 char *cmd = calloc(strlen(SHA256SUM) + 1 + strlen(filename) + 1, sizeof(char));
1402 size_t reslen = (64 + 2 + strlen(filename) + 1) * sizeof(char);
1403 char *resstr = malloc(reslen);
1404 FILE *f;
1405 bool ret = false;
1406
1407 strcpy(cmd, SHA256SUM);
1408 strcat(cmd, " ");
1409 strcat(cmd, filename);
1410
1411 f = popen(cmd, "r");
1412 if (!f)
1413 goto sha256free;
1414
1415 if (fread(resstr, reslen, 1, f) < 1)
1416 goto sha256close;
1417
1418 if (!strncmp(sha256str, resstr, 64))
1419 ret = true;
1420
1421 sha256close:
1422 fflush(f);
1423 pclose(f);
1424 sha256free:
1425 free(cmd);
1426 free(resstr);
1427
1428 return ret;
1429 }
1430
1431 static inline bool status_delay(const char *status)
1432 {
1433 return !strcmp(API_STATUS_QUEUED, status) ||
1434 !strcmp(API_STATUS_STARTED, status);
1435 }
1436
1437 static void usage(const char *arg0)
1438 {
1439 fprintf(stdout, "%s: Attended sysUpgrade CLI client\n", arg0);
1440 fprintf(stdout, "Usage: auc [-b <branch>] [-B <ver>] [-c] %s[-f] [-h] [-r] [-y]\n",
1441 #ifdef AUC_DEBUG
1442 "[-d] "
1443 #else
1444 ""
1445 #endif
1446 );
1447 fprintf(stdout, " -b <branch>\tuse specific release branch\n");
1448 fprintf(stdout, " -B <ver>\tuse specific release version\n");
1449 fprintf(stdout, " -c\t\tonly check if system is up-to-date\n");
1450 #ifdef AUC_DEBUG
1451 fprintf(stdout, " -d\t\tenable debugging output\n");
1452 #endif
1453 fprintf(stdout, " -f\t\tuse force\n");
1454 fprintf(stdout, " -h\t\toutput help\n");
1455 fprintf(stdout, " -n\t\tdry-run (don't download or upgrade)\n");
1456 fprintf(stdout, " -r\t\tcheck only for release upgrades\n");
1457 fprintf(stdout, " -F <fstype>\toverride filesystem type\n");
1458 fprintf(stdout, " -y\t\tdon't wait for user confirmation\n");
1459 fprintf(stdout, "\n");
1460 fprintf(stdout, "Please report issues to improve the server:\n");
1461 fprintf(stdout, "%s\n", server_issues);
1462 }
1463
1464
1465 /* this main function is too big... todo: split */
1466 int main(int args, char *argv[]) {
1467 static struct blob_buf checkbuf, infobuf, reqbuf, imgbuf, upgbuf;
1468 struct branch *branch;
1469 uint32_t id;
1470 int valid;
1471 char url[256];
1472 char *sanetized_board_name, *image_name, *image_sha256, *tmp;
1473 char *target_branch = NULL, *target_version = NULL, *target_fstype = NULL;
1474 struct blob_attr *tbr[__REPLY_MAX];
1475 struct blob_attr *tb[__TARGET_MAX] = {}; /* make sure tb is NULL initialized even if blobmsg_parse isn't called */
1476 struct stat imgstat;
1477 bool check_only = false;
1478 bool retry_delay = false;
1479 bool upg_check = false;
1480 bool dry_run = false;
1481 int revcmp;
1482 int addargs;
1483 unsigned char argc = 1;
1484 bool force = false, use_get = false, in_queue = false, dont_ask = false, release_only = false;
1485
1486 snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION);
1487 fprintf(stdout, "%s\n", user_agent);
1488
1489 while (argc<args) {
1490 if (!strncmp(argv[argc], "-h", 3) ||
1491 !strncmp(argv[argc], "--help", 7)) {
1492 usage(argv[0]);
1493 return 0;
1494 }
1495
1496 addargs = 0;
1497 #ifdef AUC_DEBUG
1498 if (!strncmp(argv[argc], "-d", 3))
1499 debug = 1;
1500 #endif
1501 if (!strncmp(argv[argc], "-b", 3)) {
1502 target_branch = argv[argc + 1];
1503 addargs = 1;
1504 }
1505
1506 if (!strncmp(argv[argc], "-B", 3)) {
1507 target_version = argv[argc + 1];
1508 addargs = 1;
1509 }
1510
1511 if (!strncmp(argv[argc], "-c", 3))
1512 check_only = true;
1513
1514 if (!strncmp(argv[argc], "-f", 3))
1515 force = true;
1516
1517 if (!strncmp(argv[argc], "-F", 3)) {
1518 target_fstype = argv[argc + 1];
1519 addargs = 1;
1520 }
1521
1522 if (!strncmp(argv[argc], "-n", 3))
1523 dry_run = true;
1524
1525 if (!strncmp(argv[argc], "-r", 3))
1526 release_only = true;
1527
1528 if (!strncmp(argv[argc], "-y", 3))
1529 dont_ask = true;
1530
1531 argc += 1 + addargs;
1532 };
1533
1534 if (load_config()) {
1535 rc=-EFAULT;
1536 goto freeubus;
1537 }
1538
1539 if (chdir("/tmp")) {
1540 rc=-EFAULT;
1541 goto freeconfig;
1542 }
1543
1544 if (!strncmp(serverurl, "https", 5)) {
1545 rc = init_ustream_ssl();
1546 if (rc == -2) {
1547 fprintf(stderr, "No CA certificates loaded, please install ca-certificates\n");
1548 rc=-1;
1549 goto freessl;
1550 }
1551
1552 if (rc || !ssl_ctx) {
1553 fprintf(stderr, "SSL support not available, please install ustream-ssl\n");
1554 rc=-EPROTONOSUPPORT;
1555 goto freessl;
1556 }
1557 }
1558
1559 uloop_init();
1560 ctx = ubus_connect(NULL);
1561 if (!ctx) {
1562 fprintf(stderr, "failed to connect to ubus.\n");
1563 return -1;
1564 }
1565
1566 blobmsg_buf_init(&checkbuf);
1567 blobmsg_buf_init(&infobuf);
1568 blobmsg_buf_init(&reqbuf);
1569 blobmsg_buf_init(&imgbuf);
1570 /* ubus requires BLOBMSG_TYPE_UNSPEC */
1571 blob_buf_init(&upgbuf, 0);
1572
1573 if (ubus_lookup_id(ctx, "system", &id) ||
1574 ubus_invoke(ctx, id, "board", NULL, board_cb, &checkbuf, 3000)) {
1575 fprintf(stderr, "cannot request board info from procd\n");
1576 rc=-EFAULT;
1577 goto freebufs;
1578 }
1579
1580 fprintf(stdout, "Server: %s\n", serverurl);
1581 fprintf(stdout, "Running: %s %s on %s (%s)\n", version, revision, target, board_name);
1582 if (target_fstype && rootfs_type && strcmp(rootfs_type, target_fstype))
1583 fprintf(stderr, "WARNING: will change rootfs type from '%s' to '%s'\n",
1584 rootfs_type, target_fstype);
1585
1586 if (request_branches(!(target_branch || target_version))) {
1587 rc=-ENODATA;
1588 goto freeboard;
1589 }
1590
1591 branch = select_branch(target_branch, target_version);
1592 if (!branch) {
1593 rc=-EINVAL;
1594 goto freebranches;
1595 }
1596
1597 fprintf(stdout, "Available: %s %s\n", branch->version_number, branch->version_code);
1598
1599 revcmp = verrevcmp(revision, branch->version_code);
1600 if (revcmp < 0)
1601 upg_check |= PKG_UPGRADE;
1602 else if (revcmp > 0)
1603 upg_check |= PKG_DOWNGRADE;
1604
1605 if (release_only && !(upg_check & PKG_UPGRADE)) {
1606 fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
1607 rc=0;
1608 goto freebranches;
1609 }
1610
1611 if ((rc = request_packages(branch)))
1612 goto freebranches;
1613
1614 upg_check |= check_installed_packages(reqbuf.head);
1615 if (upg_check & PKG_ERROR) {
1616 rc=-ENOPKG;
1617 goto freebranches;
1618 }
1619
1620 if (!upg_check && !force) {
1621 fprintf(stderr, "Nothing to be updated. Use '-f' to force.\n");
1622 rc=0;
1623 goto freebranches;
1624 };
1625
1626 if (!force && (upg_check & PKG_DOWNGRADE)) {
1627 fprintf(stderr, "Refusing to downgrade. Use '-f' to force.\n");
1628 rc=-ENOTRECOVERABLE;
1629 goto freebranches;
1630 };
1631
1632 if (!force && (upg_check & PKG_NOT_FOUND)) {
1633 fprintf(stderr, "Not all installed packages found in remote lists. Use '-f' to force.\n");
1634 rc=-ENOTRECOVERABLE;
1635 goto freebranches;
1636 };
1637
1638 if (check_only)
1639 goto freebranches;
1640
1641 if (!dont_ask) {
1642 rc = ask_user();
1643 if (rc)
1644 goto freebranches;
1645 }
1646
1647 blobmsg_add_string(&reqbuf, "version", branch->version);
1648 blobmsg_add_string(&reqbuf, "version_code", branch->version_code);
1649 blobmsg_add_string(&reqbuf, "target", target);
1650
1651 sanetized_board_name = strdup(board_name);
1652 tmp = sanetized_board_name;
1653 while ((tmp = strchr(tmp, ',')))
1654 *tmp = '_';
1655
1656 blobmsg_add_string(&reqbuf, "profile", sanetized_board_name);
1657 blobmsg_add_u8(&reqbuf, "diff_packages", 1);
1658
1659 req_add_selected_packages(&reqbuf);
1660
1661 snprintf(url, sizeof(url), "%s/%s", serverurl, API_REQUEST);
1662
1663 use_get = false;
1664 do {
1665 retry = false;
1666
1667 DPRINTF("requesting from %s\n%s%s", url, use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0), use_get?"":"\n");
1668
1669 rc = server_request(url, use_get?NULL:&reqbuf, &imgbuf);
1670 if (rc)
1671 break;
1672
1673 blobmsg_parse(reply_policy, __REPLY_MAX, tbr, blob_data(imgbuf.head), blob_len(imgbuf.head));
1674 if (!tbr[REPLY_OBJECT])
1675 break;
1676
1677 blobmsg_parse(target_policy, __TARGET_MAX, tb, blobmsg_data(tbr[REPLY_OBJECT]), blobmsg_len(tbr[REPLY_OBJECT]));
1678
1679 /* for compatibility with old server version, also support status in 200 reply */
1680 if (tb[TARGET_STATUS]) {
1681 tmp = blobmsg_get_string(tb[TARGET_STATUS]);
1682 if (status_delay(tmp))
1683 retry = 1;
1684 }
1685
1686 if (tb[TARGET_REQUEST_HASH]) {
1687 if (retry) {
1688 if (!retry_delay)
1689 fputs("Requesting build", stderr);
1690
1691 retry_delay = 2;
1692 if (tb[TARGET_QUEUE_POSITION]) {
1693 fprintf(stderr, "%s%s (position in queue: %d)",
1694 ANSI_ESC, in_queue?ANSI_CURSOR_RESTORE:ANSI_CURSOR_SAFE,
1695 blobmsg_get_u32(tb[TARGET_QUEUE_POSITION]));
1696 in_queue = true;
1697 } else {
1698 if (in_queue)
1699 fprintf(stderr, "%s%s%s%s",
1700 ANSI_ESC, ANSI_CURSOR_RESTORE,
1701 ANSI_ESC, ANSI_ERASE_LINE);
1702 fputc('.', stderr);
1703 in_queue = false;
1704 }
1705 } else {
1706 retry_delay = 0;
1707 }
1708 if (!use_get) {
1709 snprintf(url, sizeof(url), "%s/%s/%s", serverurl,
1710 API_REQUEST,
1711 blobmsg_get_string(tb[TARGET_REQUEST_HASH]));
1712 DPRINTF("polling via GET %s\n", url);
1713 }
1714 use_get = true;
1715 } else if (retry_delay) {
1716 retry_delay = 0;
1717 }
1718
1719 #ifdef AUC_DEBUG
1720 if (debug && tb[TARGET_STDOUT])
1721 fputs(blobmsg_get_string(tb[TARGET_STDOUT]), stdout);
1722
1723 if (debug && tb[TARGET_STDERR])
1724 fputs(blobmsg_get_string(tb[TARGET_STDERR]), stderr);
1725 #endif
1726
1727 if (retry) {
1728 blob_buf_free(&imgbuf);
1729 blobmsg_buf_init(&imgbuf);
1730 sleep(retry_delay);
1731 }
1732 } while(retry);
1733
1734 free(sanetized_board_name);
1735
1736 if (!tb[TARGET_IMAGES] || !tb[TARGET_BINDIR]) {
1737 if (!rc)
1738 rc=-EBADMSG;
1739 goto freebranches;
1740 }
1741
1742 if ((rc = select_image(tb[TARGET_IMAGES], target_fstype, &image_name, &image_sha256)))
1743 goto freebranches;
1744
1745 snprintf(url, sizeof(url), "%s/%s/%s/%s", serverurl, API_STORE,
1746 blobmsg_get_string(tb[TARGET_BINDIR]),
1747 image_name);
1748
1749 if (dry_run) {
1750 fprintf(stderr, "\nImage available at %s\n", url);
1751 rc = 0;
1752 goto freebranches;
1753 }
1754
1755 fprintf(stderr, "\nDownloading image from %s\n", url);
1756 rc = server_request(url, NULL, NULL);
1757 if (rc)
1758 goto freebranches;
1759
1760 filename = uclient_get_url_filename(url, "firmware.bin");
1761
1762 if (stat(filename, &imgstat)) {
1763 fprintf(stderr, "image download failed\n");
1764 rc=-EPIPE;
1765 goto freebranches;
1766 }
1767
1768 if ((intmax_t)imgstat.st_size != out_len) {
1769 fprintf(stderr, "file size mismatch\n");
1770 unlink(filename);
1771 rc=-EMSGSIZE;
1772 goto freebranches;
1773 }
1774
1775 if (!validate_sha256(filename, image_sha256)) {
1776 fprintf(stderr, "sha256 mismatch\n");
1777 unlink(filename);
1778 rc=-EBADMSG;
1779 goto freebranches;
1780 }
1781
1782 if (strcmp(filename, "firmware.bin")) {
1783 if (rename(filename, "firmware.bin")) {
1784 fprintf(stderr, "can't rename to firmware.bin\n");
1785 unlink(filename);
1786 rc=-errno;
1787 goto freebranches;
1788 }
1789 }
1790
1791 valid = 0;
1792 if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
1793 ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 15000)) {
1794 rc=-EFAULT;
1795 goto freebranches;
1796 }
1797
1798 if (!valid) {
1799 rc=-EINVAL;
1800 goto freebranches;
1801 }
1802
1803 fprintf(stdout, "invoking sysupgrade\n");
1804 blobmsg_add_u8(&upgbuf, "keep", 1);
1805 ubus_invoke(ctx, id, "upgrade_start", upgbuf.head, NULL, NULL, 120000);
1806 sleep(10);
1807
1808 freebranches:
1809 if (rc && tb[TARGET_STDOUT]
1810 #ifdef AUC_DEBUG
1811 && !debug
1812 #endif
1813 )
1814 fputs(blobmsg_get_string(tb[TARGET_STDOUT]), stdout);
1815 if (rc && tb[TARGET_STDERR]
1816 #ifdef AUC_DEBUG
1817 && !debug
1818 #endif
1819 )
1820 fputs(blobmsg_get_string(tb[TARGET_STDERR]), stderr);
1821
1822 if (tb[TARGET_DETAIL]) {
1823 fputs(blobmsg_get_string(tb[TARGET_DETAIL]), stderr);
1824 fputc('\n', stderr);
1825 }
1826
1827 freeboard:
1828 if (rootfs_type)
1829 free(rootfs_type);
1830
1831 free(board_name);
1832 free(target);
1833 free(distribution);
1834 free(version);
1835
1836 freebufs:
1837 blob_buf_free(&checkbuf);
1838 blob_buf_free(&infobuf);
1839 blob_buf_free(&reqbuf);
1840 blob_buf_free(&imgbuf);
1841 blob_buf_free(&upgbuf);
1842
1843 freessl:
1844 if (ssl_ctx)
1845 ssl_ops->context_free(ssl_ctx);
1846
1847 freeconfig:
1848 free(serverurl);
1849
1850 freeubus:
1851 uloop_done();
1852 ubus_free(ctx);
1853
1854 if (ucl)
1855 uclient_free(ucl);
1856
1857 if (dlh)
1858 dlclose(dlh);
1859
1860 if (rc)
1861 fprintf(stderr, "%s (%d)\n", strerror(-1 * rc), -1 * rc);
1862
1863 return rc;
1864 }