2 * auc - attendedsysUpgrade CLI
3 * Copyright (C) 2017-2021 Daniel Golle <daniel@makrotopia.org>
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
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.
17 #define AUC_VERSION "unknown"
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>
44 #define REQ_TIMEOUT 15
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"
58 #define PUBKEY_PATH "/etc/opkg/keys"
59 #define SHA256SUM "/bin/busybox sha256sum"
62 #define DPRINTF(...) if (debug) fprintf(stderr, __VA_ARGS__)
67 static const char server_issues
[]="https://github.com/aparcar/asu/issues";
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
;
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
;
87 static char *filename
= NULL
;
88 static void *dlh
= NULL
;
92 struct list_head list
;
104 static LIST_HEAD(branches
);
111 static struct avl_tree pkg_tree
= AVL_TREE_INIT(pkg_tree
, avl_strcmp
, false, NULL
);
114 static int debug
= 0;
118 * policy for ubus call system board
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
},
135 * policy for release information in system board reply
139 RELEASE_DISTRIBUTION
,
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
},
154 * policy for package list returned from rpc-sys or from server
155 * see rpcd/sys.c and ASU sources
158 PACKAGES_ARCHITECTURE
,
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
},
169 * policy for upgrade_test
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
},
184 * policy for branches.json
191 BRANCH_PATH_PACKAGES
,
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
},
210 * shared policy for target.json and server image request reply
213 TARGET_ARCH_PACKAGES
,
215 TARGET_DEVICE_PACKAGES
,
220 TARGET_METADATA_VERSION
,
222 TARGET_QUEUE_POSITION
,
229 TARGET_VERSION_NUMBER
,
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
},
254 * policy for images object in target
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
},
272 * generic policy for HTTP JSON reply
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
},
286 * policy for HTTP headers received from server
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
},
304 * load serverurl from UCI
306 static int load_config() {
307 struct uci_context
*uci_ctx
;
308 struct uci_package
*uci_attendedsysupgrade
;
309 struct uci_section
*uci_s
;
312 uci_ctx
= uci_alloc_context();
316 uci_ctx
->flags
&= ~UCI_FLAG_STRICT
;
318 if (uci_load(uci_ctx
, "attendedsysupgrade", &uci_attendedsysupgrade
) ||
319 !uci_attendedsysupgrade
) {
320 fprintf(stderr
, "Failed to load attendedsysupgrade config\n");
323 uci_s
= uci_lookup_section(uci_ctx
, uci_attendedsysupgrade
, "server");
325 fprintf(stderr
, "Failed to read server config section\n");
328 url
= uci_lookup_option_string(uci_ctx
, uci_s
, "url");
330 fprintf(stderr
, "Failed to read server url from config\n");
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");
339 serverurl
= strdup(url
);
341 uci_s
= uci_lookup_section(uci_ctx
, uci_attendedsysupgrade
, "client");
343 fprintf(stderr
, "Failed to read client config\n");
346 upgrade_packages
= atoi(uci_lookup_option_string(uci_ctx
, uci_s
, "upgrade_packages"));
348 uci_free_context(uci_ctx
);
354 * libdpkg - Debian packaging suite library routines
355 * vercmp.c - comparison of version numbers
357 * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
360 /* assume ascii; warning: evaluates x multiple times! */
361 #define order(x) ((x) == '~' ? -1 \
364 : isalpha((x)) ? (x) \
367 static int verrevcmp(const char *val
, const char *ref
)
374 while (*val
|| *ref
) {
377 while ((*val
&& !isdigit(*val
)) || (*ref
&& !isdigit(*ref
))) {
378 int vc
= order(*val
), rc
= order(*ref
);
389 while (isdigit(*val
) && isdigit(*ref
)) {
391 first_diff
= *val
- *ref
;
406 * replace '-rc' by '~' in string
408 static inline void release_replace_rc(char *ver
)
412 tmp
= strstr(ver
, "-rc");
413 if (tmp
&& strlen(tmp
) > 3) {
415 memmove(tmp
+ 1, tmp
+ 3, strlen(tmp
+ 3) + 1);
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.
424 static int openwrt_release_verrevcmp(const char *ver1
, const char *ver2
)
426 char mver1
[16], mver2
[16];
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';
433 release_replace_rc(mver1
);
434 release_replace_rc(mver2
);
436 return verrevcmp(mver1
, mver2
);
441 * UBUS response callbacks
444 * rpc-sys packagelist
445 * append array of package names to blobbuf given in req->priv
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"
455 #define PKG_UPGRADE 0x1
456 #define PKG_DOWNGRADE 0x2
457 #define PKG_NOT_FOUND 0x4
458 #define PKG_ERROR 0x8
460 static inline bool is_builtin_pkg(const char *pkgname
)
462 return !strcmp(pkgname
, "libc") ||
463 !strcmp(pkgname
, "librt") ||
464 !strcmp(pkgname
, "libpthread") ||
465 !strcmp(pkgname
, "kernel");
468 static void pkglist_check_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
470 int *status
= (int *)req
->priv
;
471 struct blob_attr
*tb
[__PACKAGES_MAX
], *cur
;
476 blobmsg_parse(packages_policy
, __PACKAGES_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
478 if (!tb
[PACKAGES_PACKAGES
])
481 blobmsg_for_each_attr(cur
, tb
[PACKAGES_PACKAGES
], rem
) {
482 if (is_builtin_pkg(blobmsg_name(cur
)))
485 pkg
= avl_find_element(&pkg_tree
, blobmsg_name(cur
), pkg
, avl
);
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
;
493 cmpres
= verrevcmp(blobmsg_get_string(cur
), pkg
->version
);
495 *status
|= PKG_UPGRADE
;
498 *status
|= PKG_DOWNGRADE
;
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
:"");
513 * rpc-sys packagelist
514 * append array of package names to blobbuf given in req->priv
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
;
524 blobmsg_parse(packages_policy
, __PACKAGES_MAX
, tb
, blob_data(msg
), blob_len(msg
));
526 if (!tb
[PACKAGES_PACKAGES
]) {
527 fprintf(stderr
, "No packagelist received\n");
531 table
= blobmsg_open_table(buf
, "packages_versions");
533 blobmsg_for_each_attr(cur
, tb
[PACKAGES_PACKAGES
], rem
) {
534 if (is_builtin_pkg(blobmsg_name(cur
)))
537 pkg
= avl_find_element(&pkg_tree
, blobmsg_name(cur
), pkg
, avl
);
541 blobmsg_add_string(buf
, blobmsg_name(cur
), pkg
->version
);
543 blobmsg_close_table(buf
, table
);
548 * append append board information to blobbuf given in req->priv
549 * populate board and release global strings
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
];
556 blobmsg_parse(board_policy
, __BOARD_MAX
, tb
, blob_data(msg
), blob_len(msg
));
559 if (!tb
[BOARD_RELEASE
]) {
560 fprintf(stderr
, "No release received\n");
565 blobmsg_parse(release_policy
, __RELEASE_MAX
, rel
,
566 blobmsg_data(tb
[BOARD_RELEASE
]), blobmsg_data_len(tb
[BOARD_RELEASE
]));
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");
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
]));
582 if (!strcmp(target
, "x86/64") || !strcmp(target
, "x86/generic")) {
584 * ugly work-around ahead:
585 * ignore board name on generic x86 targets, as image name is always 'generic'
587 board_name
= strdup("generic");
589 if (!tb
[BOARD_BOARD_NAME
]) {
590 fprintf(stderr
, "No board name received\n");
594 board_name
= strdup(blobmsg_get_string(tb
[BOARD_BOARD_NAME
]));
597 if (tb
[BOARD_ROOTFS_TYPE
])
598 rootfs_type
= strdup(blobmsg_get_string(tb
[BOARD_ROOTFS_TYPE
]));
600 blobmsg_add_string(buf
, "target", target
);
601 blobmsg_add_string(buf
, "version", version
);
602 blobmsg_add_string(buf
, "revision", revision
);
606 * rpc-sys upgrade_test
607 * check if downloaded file is accepted by sysupgrade
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
];
613 blobmsg_parse(upgtest_policy
, __UPGTEST_MAX
, tb
, blob_data(msg
), blob_len(msg
));
615 if (!tb
[UPGTEST_CODE
]) {
616 fprintf(stderr
, "No sysupgrade test return code received\n");
620 *valid
= (blobmsg_get_u32(tb
[UPGTEST_CODE
]) == 0)?1:0;
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");
627 fprintf(stderr
, "image verification succeeded\n");
633 static int open_output_file(const char *path
, uint64_t resume_offset
)
635 char *filename
= NULL
;
642 flags
= O_WRONLY
| O_EXCL
;
646 filename
= uclient_get_url_filename(path
, "firmware.bin");
648 fprintf(stderr
, "Writing to '%s'\n", filename
);
649 ret
= open(filename
, flags
, 0644);
654 lseek(ret
, resume_offset
, SEEK_SET
) < 0) {
655 fprintf(stderr
, "Failed to seek %"PRIu64
" bytes in output file\n", resume_offset
);
661 out_offset
= resume_offset
;
662 out_bytes
+= resume_offset
;
671 struct blob_buf
*outbuf
;
674 static void request_done(struct uclient
*cl
)
676 struct jsonblobber
*jsb
= (struct jsonblobber
*)cl
->priv
;
678 json_tokener_free(jsb
->tok
);
682 uclient_disconnect(cl
);
686 static void header_done_cb(struct uclient
*cl
)
688 struct blob_attr
*tb
[__H_MAX
];
689 struct jsonblobber
*jsb
= (struct jsonblobber
*)cl
->priv
;
690 struct blob_buf
*outbuf
= NULL
;
693 outbuf
= jsb
->outbuf
;
695 uint64_t resume_offset
= 0, resume_end
, resume_size
;
697 if (uclient_http_redirect(cl
)) {
698 fprintf(stderr
, "Redirected to %s on %s\n", cl
->url
->location
, cl
->url
->host
);
703 if (cl
->status_code
== 204 && cur_resume
) {
704 /* Resume attempt failed, try normal download */
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
));
714 switch (cl
->status_code
) {
720 fprintf(stderr
, "unknown package '%s' requested.\n",
721 blobmsg_get_string(tb
[H_UNKNOWN_PACKAGE
]));
731 blobmsg_add_u32(outbuf
, "status", cl
->status_code
);
733 if (tb
[H_QUEUE_POSITION
])
734 blobmsg_add_u32(outbuf
, "queue_position", blobmsg_get_u32(tb
[H_QUEUE_POSITION
]));
743 out_len
= strtoul(blobmsg_get_string(tb
[H_LEN
]), NULL
, 10);
745 output_fd
= open_output_file(cl
->url
->location
, resume_offset
);
747 perror("Cannot open output file");
752 /* server may reply JSON object */
756 DPRINTF("HTTP error %d\n", cl
->status_code
);
762 static void read_data_cb(struct uclient
*cl
)
767 struct blob_buf
*outbuf
= NULL
;
768 json_tokener
*tok
= NULL
;
769 struct jsonblobber
*jsb
= (struct jsonblobber
*)cl
->priv
;
773 len
= uclient_read(cl
, buf
, sizeof(buf
));
778 write(output_fd
, buf
, len
);
783 outbuf
= jsb
->outbuf
;
787 len
= uclient_read(cl
, buf
, sizeof(buf
));
793 jsobj
= json_tokener_parse_ex(tok
, buf
, len
);
795 if (json_tokener_get_error(tok
) == json_tokener_continue
)
798 if (json_tokener_get_error(tok
) != json_tokener_success
)
803 blobmsg_add_json_element(outbuf
, "reply", jsobj
);
805 json_object_put(jsobj
);
811 static void eof_cb(struct uclient
*cl
)
813 if (!cl
->data_eof
&& !uptodate
) {
814 fprintf(stderr
, "Connection reset prematurely\n");
819 static void handle_uclient_error(struct uclient
*cl
, int code
)
821 const char *type
= "Unknown error";
824 case UCLIENT_ERROR_CONNECT
:
825 type
= "Connection failed";
827 case UCLIENT_ERROR_TIMEDOUT
:
828 type
= "Connection timed out";
830 case UCLIENT_ERROR_SSL_INVALID_CERT
:
831 type
= "Invalid SSL certificate";
833 case UCLIENT_ERROR_SSL_CN_MISMATCH
:
834 type
= "Server hostname does not match SSL certificate";
840 fprintf(stderr
, "Connection error: %s\n", type
);
845 static const struct uclient_cb check_cb
= {
846 .header_done
= header_done_cb
,
847 .data_read
= read_data_cb
,
849 .error
= handle_uclient_error
,
852 static int server_request(const char *url
, struct blob_buf
*inbuf
, struct blob_buf
*outbuf
) {
853 struct jsonblobber
*jsb
= NULL
;
862 fprintf(stderr
, "Requesting URL: %s\n", url
);
866 jsb
= malloc(sizeof(struct jsonblobber
));
867 jsb
->outbuf
= outbuf
;
868 jsb
->tok
= json_tokener_new();
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;
876 uclient_set_url(ucl
, url
, NULL
);
881 rc
= uclient_connect(ucl
);
885 rc
= uclient_http_set_request_type(ucl
, inbuf
?"POST":"GET");
889 uclient_http_reset_headers(ucl
);
890 uclient_http_set_header(ucl
, "User-Agent", user_agent
);
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
));
896 rc
= uclient_request(ucl
);
908 static int init_ustream_ssl(void) {
912 dlh
= dlopen("libustream-ssl.so", RTLD_LAZY
| RTLD_LOCAL
);
916 ssl_ops
= dlsym(dlh
, "ustream_ssl_ops");
920 ssl_ctx
= ssl_ops
->context_new(false);
922 glob("/etc/ssl/certs/*.crt", 0, NULL
, &gl
);
926 for (i
= 0; i
< gl
.gl_pathc
; i
++)
927 ssl_ops
->context_add_ca_crt_file(ssl_ctx
, gl
.gl_pathv
[i
]);
932 static int ask_user(void)
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'))
943 static char* alloc_replace_var(char *in
, const char *var
, const char *replace
)
949 while ((tmp
= strchr(tmp
, '{'))) {
951 eptr
= strchr(tmp
, '}');
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);
968 static int request_target(struct branch
*br
, char *url
)
970 static struct blob_buf boardbuf
;
971 struct blob_attr
*tbr
[__REPLY_MAX
], *tb
[__TARGET_MAX
];
973 blobmsg_buf_init(&boardbuf
);
975 if ((rc
= server_request(url
, NULL
, &boardbuf
))) {
976 blob_buf_free(&boardbuf
);
980 blobmsg_parse(reply_policy
, __REPLY_MAX
, tbr
, blob_data(boardbuf
.head
), blob_len(boardbuf
.head
));
982 if (!tbr
[REPLY_OBJECT
])
985 blobmsg_parse(target_policy
, __TARGET_MAX
, tb
, blobmsg_data(tbr
[REPLY_OBJECT
]), blobmsg_len(tbr
[REPLY_OBJECT
]));
987 if (!tb
[TARGET_METADATA_VERSION
] ||
988 !tb
[TARGET_ARCH_PACKAGES
] ||
989 !tb
[TARGET_IMAGES
] ||
990 !tb
[TARGET_TARGET
]) {
991 blob_buf_free(&boardbuf
);
995 if (blobmsg_get_u32(tb
[TARGET_METADATA_VERSION
]) != 1) {
996 blob_buf_free(&boardbuf
);
997 return -EPFNOSUPPORT
;
1000 if (strcmp(blobmsg_get_string(tb
[TARGET_TARGET
]), target
))
1003 if (strcmp(blobmsg_get_string(tb
[TARGET_ARCH_PACKAGES
]), br
->arch_packages
))
1006 if (tb
[TARGET_VERSION_CODE
])
1007 br
->version_code
= strdup(blobmsg_get_string(tb
[TARGET_VERSION_CODE
]));
1009 if (tb
[TARGET_VERSION_NUMBER
])
1010 br
->version_number
= strdup(blobmsg_get_string(tb
[TARGET_VERSION_NUMBER
]));
1012 blob_buf_free(&boardbuf
);
1016 static char* validate_target(struct blob_attr
*branch
)
1018 struct blob_attr
*cur
;
1021 blobmsg_for_each_attr(cur
, branch
, rem
)
1022 if (!strcmp(blobmsg_name(cur
), target
))
1023 return strdup(blobmsg_get_string(cur
));
1028 static void process_branch(struct blob_attr
*branch
, bool only_active
)
1030 struct blob_attr
*tb
[__BRANCH_MAX
];
1031 struct blob_attr
*curver
;
1034 char *tmp
, *arch_packages
, *board_json_file
;
1037 blobmsg_parse(branches_policy
, __BRANCH_MAX
, tb
, blobmsg_data(branch
), blobmsg_len(branch
));
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
]))
1045 brname
= blobmsg_get_string(tb
[BRANCH_NAME
]);
1046 if (only_active
&& strncmp(brname
, version
, strlen(brname
)))
1049 /* check if target is offered in branch and get arch_packages */
1050 arch_packages
= validate_target(tb
[BRANCH_TARGETS
]);
1054 /* add each version of the branch */
1055 blobmsg_for_each_attr(curver
, tb
[BRANCH_VERSIONS
], remver
) {
1056 br
= malloc(sizeof(struct branch
));
1058 if (tb
[BRANCH_GIT_BRANCH
])
1059 br
->git_branch
= strdup(blobmsg_get_string(tb
[BRANCH_GIT_BRANCH
]));
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
]));
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
) {
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
, ',')))
1081 if (request_target(br
, board_json_file
)) {
1082 free(board_json_file
);
1087 free(board_json_file
);
1088 list_add_tail(&br
->list
, &branches
);
1092 static int request_branches(bool only_active
)
1094 static struct blob_buf brbuf
;
1095 struct blob_attr
*cur
;
1096 struct blob_attr
*tb
[__REPLY_MAX
];
1099 struct blob_attr
*data
;
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
);
1105 if ((rc
= server_request(url
, NULL
, &brbuf
))) {
1106 blob_buf_free(&brbuf
);
1110 blobmsg_parse(reply_policy
, __REPLY_MAX
, tb
, blob_data(brbuf
.head
), blob_len(brbuf
.head
));
1112 /* newer server API replies OBJECT, older API replies ARRAY... */
1113 if ((!tb
[REPLY_ARRAY
] && !tb
[REPLY_OBJECT
]))
1116 if (tb
[REPLY_OBJECT
])
1117 data
= tb
[REPLY_OBJECT
];
1119 data
= tb
[REPLY_ARRAY
];
1121 blobmsg_for_each_attr(cur
, data
, rem
)
1122 process_branch(cur
, only_active
);
1124 blob_buf_free(&brbuf
);
1129 static struct branch
*select_branch(char *name
, char *select_version
)
1131 struct branch
*br
, *abr
= NULL
;
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
)))
1141 if (select_version
) {
1142 if (!strcasecmp(br
->version
, select_version
)) {
1147 if (strcasestr(name
, "snapshot")) {
1148 /* if we are on the snapshot branch, stay there */
1154 /* on release branch, skip snapshots and pick latest release */
1158 if (!abr
|| (openwrt_release_verrevcmp(abr
->version
, br
->version
) < 0))
1167 static int add_upg_packages(struct blob_attr
*reply
, char *arch
)
1169 struct blob_attr
*tbr
[__REPLY_MAX
];
1170 struct blob_attr
*tba
[__PACKAGES_MAX
];
1171 struct blob_attr
*packages
;
1172 struct blob_attr
*cur
;
1174 struct avl_pkg
*avpk
;
1176 blobmsg_parse(reply_policy
, __REPLY_MAX
, tbr
, blob_data(reply
), blob_len(reply
));
1178 if (!tbr
[REPLY_OBJECT
])
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
])
1187 if (strcmp(blobmsg_get_string(tba
[PACKAGES_ARCHITECTURE
]), arch
))
1190 packages
= tba
[PACKAGES_PACKAGES
];
1192 packages
= tbr
[REPLY_OBJECT
];
1195 blobmsg_for_each_attr(cur
, packages
, rem
) {
1196 avpk
= malloc(sizeof(struct avl_pkg
));
1200 avpk
->name
= strdup(blobmsg_name(cur
));
1206 avpk
->version
= strdup(blobmsg_get_string(cur
));
1207 if (!avpk
->version
) {
1213 avpk
->avl
.key
= avpk
->name
;
1214 if (avl_insert(&pkg_tree
, &avpk
->avl
)) {
1218 fprintf(stderr
, "failed to insert package %s (%s)!\n", blobmsg_name(cur
), blobmsg_get_string(cur
));
1225 free(avpk
->version
);
1234 static int request_packages(struct branch
*branch
)
1236 static struct blob_buf pkgbuf
, archpkgbuf
;
1240 fprintf(stderr
, "Requesting package lists...\n");
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
);
1250 ret
= add_upg_packages(archpkgbuf
.head
, branch
->arch_packages
);
1251 blob_buf_free(&archpkgbuf
);
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
);
1266 ret
= add_upg_packages(pkgbuf
.head
, NULL
);
1267 blob_buf_free(&pkgbuf
);
1273 static int check_installed_packages(struct blob_attr
*pkgs
)
1275 static struct blob_buf allpkg
;
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
;
1291 static int req_add_selected_packages(struct blob_buf
*req
)
1293 static struct blob_buf allpkg
;
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");
1308 #if defined(__amd64__) || defined(__i386__)
1309 static int system_is_efi(void)
1311 const char efidname
[] = "/sys/firmware/efi/efivars";
1312 int fd
= open(efidname
, O_DIRECTORY
| O_PATH
);
1322 static inline int system_is_efi(void) { return 0; }
1325 static int get_image_by_type(struct blob_attr
*images
, const char *typestr
, const char *fstype
, char **image_name
, char **image_sha256
)
1327 struct blob_attr
*tb
[__IMAGES_MAX
];
1328 struct blob_attr
*cur
;
1329 int rem
, ret
= -ENOENT
;
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
] ||
1339 if (fstype
&& strcmp(blobmsg_get_string(tb
[IMAGES_FILESYSTEM
]), fstype
))
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
]));
1353 static int select_image(struct blob_attr
*images
, const char *target_fstype
, char **image_name
, char **image_sha256
)
1355 const char *combined_type
;
1356 const char *fstype
= rootfs_type
;
1360 fstype
= target_fstype
;
1362 if (system_is_efi())
1363 combined_type
= "combined-efi";
1365 combined_type
= "combined";
1367 DPRINTF("images: %s\n", blobmsg_format_json_indent(images
, true, 0));
1370 ret
= get_image_by_type(images
, "sysupgrade", fstype
, image_name
, image_sha256
);
1374 ret
= get_image_by_type(images
, combined_type
, fstype
, image_name
, image_sha256
);
1378 ret
= get_image_by_type(images
, "sdcard", fstype
, image_name
, image_sha256
);
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
);
1389 ret
= get_image_by_type(images
, combined_type
, "squashfs", image_name
, image_sha256
);
1393 ret
= get_image_by_type(images
, "sdcard", fstype
, image_name
, image_sha256
);
1399 static bool validate_sha256(char *filename
, char *sha256str
)
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
);
1407 strcpy(cmd
, SHA256SUM
);
1409 strcat(cmd
, filename
);
1411 f
= popen(cmd
, "r");
1415 if (fread(resstr
, reslen
, 1, f
) < 1)
1418 if (!strncmp(sha256str
, resstr
, 64))
1431 static inline bool status_delay(const char *status
)
1433 return !strcmp(API_STATUS_QUEUED
, status
) ||
1434 !strcmp(API_STATUS_STARTED
, status
);
1437 static void usage(const char *arg0
)
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",
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");
1451 fprintf(stdout
, " -d\t\tenable debugging output\n");
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
);
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
;
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;
1483 unsigned char argc
= 1;
1484 bool force
= false, use_get
= false, in_queue
= false, dont_ask
= false, release_only
= false;
1486 snprintf(user_agent
, sizeof(user_agent
), "%s (%s)", argv
[0], AUC_VERSION
);
1487 fprintf(stdout
, "%s\n", user_agent
);
1490 if (!strncmp(argv
[argc
], "-h", 3) ||
1491 !strncmp(argv
[argc
], "--help", 7)) {
1498 if (!strncmp(argv
[argc
], "-d", 3))
1501 if (!strncmp(argv
[argc
], "-b", 3)) {
1502 target_branch
= argv
[argc
+ 1];
1506 if (!strncmp(argv
[argc
], "-B", 3)) {
1507 target_version
= argv
[argc
+ 1];
1511 if (!strncmp(argv
[argc
], "-c", 3))
1514 if (!strncmp(argv
[argc
], "-f", 3))
1517 if (!strncmp(argv
[argc
], "-F", 3)) {
1518 target_fstype
= argv
[argc
+ 1];
1522 if (!strncmp(argv
[argc
], "-n", 3))
1525 if (!strncmp(argv
[argc
], "-r", 3))
1526 release_only
= true;
1528 if (!strncmp(argv
[argc
], "-y", 3))
1531 argc
+= 1 + addargs
;
1534 if (load_config()) {
1539 if (chdir("/tmp")) {
1544 if (!strncmp(serverurl
, "https", 5)) {
1545 rc
= init_ustream_ssl();
1547 fprintf(stderr
, "No CA certificates loaded, please install ca-certificates\n");
1552 if (rc
|| !ssl_ctx
) {
1553 fprintf(stderr
, "SSL support not available, please install ustream-ssl\n");
1554 rc
=-EPROTONOSUPPORT
;
1560 ctx
= ubus_connect(NULL
);
1562 fprintf(stderr
, "failed to connect to ubus.\n");
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);
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");
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
);
1586 if (request_branches(!(target_branch
|| target_version
))) {
1591 branch
= select_branch(target_branch
, target_version
);
1597 fprintf(stdout
, "Available: %s %s\n", branch
->version_number
, branch
->version_code
);
1599 revcmp
= verrevcmp(revision
, branch
->version_code
);
1601 upg_check
|= PKG_UPGRADE
;
1602 else if (revcmp
> 0)
1603 upg_check
|= PKG_DOWNGRADE
;
1605 if (release_only
&& !(upg_check
& PKG_UPGRADE
)) {
1606 fprintf(stderr
, "Nothing to be updated. Use '-f' to force.\n");
1611 if ((rc
= request_packages(branch
)))
1614 upg_check
|= check_installed_packages(reqbuf
.head
);
1615 if (upg_check
& PKG_ERROR
) {
1620 if (!upg_check
&& !force
) {
1621 fprintf(stderr
, "Nothing to be updated. Use '-f' to force.\n");
1626 if (!force
&& (upg_check
& PKG_DOWNGRADE
)) {
1627 fprintf(stderr
, "Refusing to downgrade. Use '-f' to force.\n");
1628 rc
=-ENOTRECOVERABLE
;
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
;
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
);
1651 sanetized_board_name
= strdup(board_name
);
1652 tmp
= sanetized_board_name
;
1653 while ((tmp
= strchr(tmp
, ',')))
1656 blobmsg_add_string(&reqbuf
, "profile", sanetized_board_name
);
1657 blobmsg_add_u8(&reqbuf
, "diff_packages", 1);
1659 req_add_selected_packages(&reqbuf
);
1661 snprintf(url
, sizeof(url
), "%s/%s", serverurl
, API_REQUEST
);
1667 DPRINTF("requesting from %s\n%s%s", url
, use_get
?"":blobmsg_format_json_indent(reqbuf
.head
, true, 0), use_get
?"":"\n");
1669 rc
= server_request(url
, use_get
?NULL
:&reqbuf
, &imgbuf
);
1673 blobmsg_parse(reply_policy
, __REPLY_MAX
, tbr
, blob_data(imgbuf
.head
), blob_len(imgbuf
.head
));
1674 if (!tbr
[REPLY_OBJECT
])
1677 blobmsg_parse(target_policy
, __TARGET_MAX
, tb
, blobmsg_data(tbr
[REPLY_OBJECT
]), blobmsg_len(tbr
[REPLY_OBJECT
]));
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
))
1686 if (tb
[TARGET_REQUEST_HASH
]) {
1689 fputs("Requesting build", stderr
);
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
]));
1699 fprintf(stderr
, "%s%s%s%s",
1700 ANSI_ESC
, ANSI_CURSOR_RESTORE
,
1701 ANSI_ESC
, ANSI_ERASE_LINE
);
1709 snprintf(url
, sizeof(url
), "%s/%s/%s", serverurl
,
1711 blobmsg_get_string(tb
[TARGET_REQUEST_HASH
]));
1712 DPRINTF("polling via GET %s\n", url
);
1715 } else if (retry_delay
) {
1720 if (debug
&& tb
[TARGET_STDOUT
])
1721 fputs(blobmsg_get_string(tb
[TARGET_STDOUT
]), stdout
);
1723 if (debug
&& tb
[TARGET_STDERR
])
1724 fputs(blobmsg_get_string(tb
[TARGET_STDERR
]), stderr
);
1728 blob_buf_free(&imgbuf
);
1729 blobmsg_buf_init(&imgbuf
);
1734 free(sanetized_board_name
);
1736 if (!tb
[TARGET_IMAGES
] || !tb
[TARGET_BINDIR
]) {
1742 if ((rc
= select_image(tb
[TARGET_IMAGES
], target_fstype
, &image_name
, &image_sha256
)))
1745 snprintf(url
, sizeof(url
), "%s/%s/%s/%s", serverurl
, API_STORE
,
1746 blobmsg_get_string(tb
[TARGET_BINDIR
]),
1750 fprintf(stderr
, "\nImage available at %s\n", url
);
1755 fprintf(stderr
, "\nDownloading image from %s\n", url
);
1756 rc
= server_request(url
, NULL
, NULL
);
1760 filename
= uclient_get_url_filename(url
, "firmware.bin");
1762 if (stat(filename
, &imgstat
)) {
1763 fprintf(stderr
, "image download failed\n");
1768 if ((intmax_t)imgstat
.st_size
!= out_len
) {
1769 fprintf(stderr
, "file size mismatch\n");
1775 if (!validate_sha256(filename
, image_sha256
)) {
1776 fprintf(stderr
, "sha256 mismatch\n");
1782 if (strcmp(filename
, "firmware.bin")) {
1783 if (rename(filename
, "firmware.bin")) {
1784 fprintf(stderr
, "can't rename to firmware.bin\n");
1792 if (ubus_lookup_id(ctx
, "rpc-sys", &id
) ||
1793 ubus_invoke(ctx
, id
, "upgrade_test", NULL
, upgtest_cb
, &valid
, 15000)) {
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);
1809 if (rc
&& tb
[TARGET_STDOUT
]
1814 fputs(blobmsg_get_string(tb
[TARGET_STDOUT
]), stdout
);
1815 if (rc
&& tb
[TARGET_STDERR
]
1820 fputs(blobmsg_get_string(tb
[TARGET_STDERR
]), stderr
);
1822 if (tb
[TARGET_DETAIL
]) {
1823 fputs(blobmsg_get_string(tb
[TARGET_DETAIL
]), stderr
);
1824 fputc('\n', stderr
);
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
);
1845 ssl_ops
->context_free(ssl_ctx
);
1861 fprintf(stderr
, "%s (%d)\n", strerror(-1 * rc
), -1 * rc
);