You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1172 lines
26 KiB

  1. /*
  2. * auc - attendedsysUpgrade CLI
  3. * Copyright (C) 2017 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. #define _GNU_SOURCE
  15. #define AUC_VERSION "0.0.9"
  16. #include <fcntl.h>
  17. #include <dlfcn.h>
  18. #include <glob.h>
  19. #include <stdio.h>
  20. #include <sys/stat.h>
  21. #include <sys/wait.h>
  22. #include <unistd.h>
  23. #include <uci.h>
  24. #include <uci_blob.h>
  25. #include <json-c/json.h>
  26. #include <libubox/ulog.h>
  27. #include <libubox/list.h>
  28. #include <libubox/vlist.h>
  29. #include <libubox/blobmsg_json.h>
  30. #include <libubox/avl-cmp.h>
  31. #include <libubox/uclient.h>
  32. #include <libubox/uclient-utils.h>
  33. #include <libubus.h>
  34. #define REQ_TIMEOUT 15
  35. #define APIOBJ_CHECK "api/upgrade-check"
  36. #define APIOBJ_REQUEST "api/upgrade-request"
  37. #define PUBKEY_PATH "/etc/opkg/keys"
  38. #ifdef AUC_DEBUG
  39. #define DPRINTF(...) if (debug) fprintf(stderr, __VA_ARGS__)
  40. #else
  41. #define DPRINTF(...)
  42. #endif
  43. static const char server_issues[]="https://github.com/aparcar/attendedsysupgrade-server/issues";
  44. static char user_agent[80];
  45. static char *serverurl;
  46. static int upgrade_packages;
  47. static struct ustream_ssl_ctx *ssl_ctx;
  48. static const struct ustream_ssl_ops *ssl_ops;
  49. static off_t out_bytes;
  50. static off_t out_len;
  51. static off_t out_offset;
  52. static bool cur_resume;
  53. static int output_fd = -1;
  54. static int retry, imagebuilder, building, ibready;
  55. static char *board_name = NULL;
  56. static char *target = NULL, *subtarget = NULL;
  57. static char *distribution = NULL, *version = NULL;
  58. static int uptodate;
  59. static char *filename = NULL;
  60. static int rc;
  61. #ifdef AUC_DEBUG
  62. static int debug = 0;
  63. #endif
  64. /*
  65. * policy for ubus call system board
  66. * see procd/system.c
  67. */
  68. enum {
  69. BOARD_BOARD_NAME,
  70. BOARD_RELEASE,
  71. __BOARD_MAX,
  72. };
  73. static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
  74. [BOARD_BOARD_NAME] = { .name = "board_name", .type = BLOBMSG_TYPE_STRING },
  75. [BOARD_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_TABLE },
  76. };
  77. /*
  78. * policy for release information in system board reply
  79. * see procd/system.c
  80. */
  81. enum {
  82. RELEASE_DISTRIBUTION,
  83. RELEASE_VERSION,
  84. RELEASE_TARGET,
  85. __RELEASE_MAX,
  86. };
  87. static const struct blobmsg_policy release_policy[__RELEASE_MAX] = {
  88. [RELEASE_DISTRIBUTION] = { .name = "distribution", .type = BLOBMSG_TYPE_STRING },
  89. [RELEASE_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING },
  90. [RELEASE_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
  91. };
  92. /*
  93. * policy for packagelist
  94. * see rpcd/sys.c
  95. */
  96. enum {
  97. PACKAGELIST_PACKAGES,
  98. __PACKAGELIST_MAX,
  99. };
  100. static const struct blobmsg_policy packagelist_policy[__PACKAGELIST_MAX] = {
  101. [PACKAGELIST_PACKAGES] = { .name = "packages", .type = BLOBMSG_TYPE_TABLE },
  102. };
  103. /*
  104. * policy for upgrade_test
  105. * see rpcd/sys.c
  106. */
  107. enum {
  108. UPGTEST_CODE,
  109. UPGTEST_STDOUT,
  110. __UPGTEST_MAX,
  111. };
  112. static const struct blobmsg_policy upgtest_policy[__UPGTEST_MAX] = {
  113. [UPGTEST_CODE] = { .name = "code", .type = BLOBMSG_TYPE_INT32 },
  114. [UPGTEST_STDOUT] = { .name = "stdout", .type = BLOBMSG_TYPE_STRING },
  115. };
  116. /*
  117. * policy to extract version from upgrade-check response
  118. */
  119. enum {
  120. CHECK_VERSION,
  121. CHECK_UPGRADES,
  122. __CHECK_MAX,
  123. };
  124. static const struct blobmsg_policy check_policy[__CHECK_MAX] = {
  125. [CHECK_VERSION] = { .name = "version", .type = BLOBMSG_TYPE_STRING },
  126. [CHECK_UPGRADES] = { .name = "upgrades", .type = BLOBMSG_TYPE_TABLE },
  127. };
  128. static const struct blobmsg_policy pkg_upgrades_policy[2] = {
  129. { .type = BLOBMSG_TYPE_STRING },
  130. { .type = BLOBMSG_TYPE_STRING },
  131. };
  132. /*
  133. * policy for upgrade-request response
  134. * parse download information for the ready image.
  135. */
  136. enum {
  137. IMAGE_REQHASH,
  138. IMAGE_FILESIZE,
  139. IMAGE_URL,
  140. IMAGE_CHECKSUM,
  141. IMAGE_FILES,
  142. IMAGE_SYSUPGRADE,
  143. __IMAGE_MAX,
  144. };
  145. static const struct blobmsg_policy image_policy[__IMAGE_MAX] = {
  146. [IMAGE_REQHASH] = { .name = "request_hash", .type = BLOBMSG_TYPE_STRING },
  147. [IMAGE_URL] = { .name = "url", .type = BLOBMSG_TYPE_STRING },
  148. [IMAGE_FILES] = { .name = "files", .type = BLOBMSG_TYPE_STRING },
  149. [IMAGE_SYSUPGRADE] = { .name = "sysupgrade", .type = BLOBMSG_TYPE_STRING },
  150. };
  151. /*
  152. * policy for HTTP headers received from server
  153. */
  154. enum {
  155. H_RANGE,
  156. H_LEN,
  157. H_IBSTATUS,
  158. H_IBQUEUEPOS,
  159. H_UNKNOWN_PACKAGE,
  160. __H_MAX
  161. };
  162. static const struct blobmsg_policy policy[__H_MAX] = {
  163. [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
  164. [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
  165. [H_IBSTATUS] = { .name = "x-imagebuilder-status", .type = BLOBMSG_TYPE_STRING },
  166. [H_IBQUEUEPOS] = { .name = "x-build-queue-position", .type = BLOBMSG_TYPE_STRING },
  167. [H_UNKNOWN_PACKAGE] = { .name = "x-unknown-package", .type = BLOBMSG_TYPE_STRING },
  168. };
  169. /*
  170. * load serverurl from UCI
  171. */
  172. static int load_config() {
  173. struct uci_context *uci_ctx;
  174. struct uci_package *uci_attendedsysupgrade;
  175. struct uci_section *uci_s;
  176. uci_ctx = uci_alloc_context();
  177. if (!uci_ctx)
  178. return -1;
  179. uci_ctx->flags &= ~UCI_FLAG_STRICT;
  180. if (uci_load(uci_ctx, "attendedsysupgrade", &uci_attendedsysupgrade) ||
  181. !uci_attendedsysupgrade) {
  182. fprintf(stderr, "Failed to load attendedsysupgrade config\n");
  183. return -1;
  184. }
  185. uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "server");
  186. if (!uci_s) {
  187. fprintf(stderr, "Failed to read server url from config\n");
  188. return -1;
  189. }
  190. serverurl = strdup(uci_lookup_option_string(uci_ctx, uci_s, "url"));
  191. uci_s = uci_lookup_section(uci_ctx, uci_attendedsysupgrade, "client");
  192. if (!uci_s) {
  193. fprintf(stderr, "Failed to read client config\n");
  194. return -1;
  195. }
  196. upgrade_packages = atoi(uci_lookup_option_string(uci_ctx, uci_s, "upgrade_packages"));
  197. uci_free_context(uci_ctx);
  198. return 0;
  199. }
  200. /**
  201. * UBUS response callbacks
  202. */
  203. /*
  204. * rpc-sys packagelist
  205. * append packagelist response to blobbuf given in req->priv
  206. */
  207. static void pkglist_check_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
  208. struct blob_buf *buf = (struct blob_buf *)req->priv;
  209. struct blob_attr *tb[__PACKAGELIST_MAX];
  210. blobmsg_parse(packagelist_policy, __PACKAGELIST_MAX, tb, blob_data(msg), blob_len(msg));
  211. if (!tb[PACKAGELIST_PACKAGES]) {
  212. fprintf(stderr, "No packagelist received\n");
  213. rc=-1;
  214. return;
  215. }
  216. blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "packages", blobmsg_data(tb[PACKAGELIST_PACKAGES]), blobmsg_data_len(tb[PACKAGELIST_PACKAGES]));
  217. };
  218. /*
  219. * rpc-sys packagelist
  220. * append array of package names to blobbuf given in req->priv
  221. */
  222. static void pkglist_req_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
  223. struct blob_buf *buf = (struct blob_buf *)req->priv;
  224. struct blob_attr *tb[__PACKAGELIST_MAX];
  225. struct blob_attr *cur;
  226. int rem;
  227. void *array;
  228. blobmsg_parse(packagelist_policy, __PACKAGELIST_MAX, tb, blob_data(msg), blob_len(msg));
  229. if (!tb[PACKAGELIST_PACKAGES]) {
  230. fprintf(stderr, "No packagelist received\n");
  231. return;
  232. }
  233. array = blobmsg_open_array(buf, "packages");
  234. blobmsg_for_each_attr(cur, tb[PACKAGELIST_PACKAGES], rem)
  235. blobmsg_add_string(buf, NULL, blobmsg_name(cur));
  236. blobmsg_close_array(buf, array);
  237. };
  238. /*
  239. * system board
  240. * append append board information to blobbuf given in req->priv
  241. * populate board and release global strings
  242. */
  243. static void board_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
  244. struct blob_buf *buf = (struct blob_buf *)req->priv;
  245. struct blob_attr *tb[__BOARD_MAX];
  246. struct blob_attr *rel[__RELEASE_MAX];
  247. blobmsg_parse(board_policy, __BOARD_MAX, tb, blob_data(msg), blob_len(msg));
  248. if (!tb[BOARD_BOARD_NAME]) {
  249. fprintf(stderr, "No board name received\n");
  250. rc=-1;
  251. return;
  252. }
  253. board_name = strdup(blobmsg_get_string(tb[BOARD_BOARD_NAME]));
  254. if (!tb[BOARD_RELEASE]) {
  255. fprintf(stderr, "No release received\n");
  256. rc=-1;
  257. return;
  258. }
  259. blobmsg_parse(release_policy, __RELEASE_MAX, rel,
  260. blobmsg_data(tb[BOARD_RELEASE]), blobmsg_data_len(tb[BOARD_RELEASE]));
  261. if (!rel[RELEASE_TARGET]) {
  262. fprintf(stderr, "No target received\n");
  263. rc=-1;
  264. return;
  265. }
  266. target = strdup(blobmsg_get_string(rel[RELEASE_TARGET]));
  267. subtarget = strchr(target, '/');
  268. *subtarget++ = '\0';
  269. distribution = strdup(blobmsg_get_string(rel[RELEASE_DISTRIBUTION]));
  270. version = strdup(blobmsg_get_string(rel[RELEASE_VERSION]));
  271. blobmsg_add_string(buf, "distro", distribution);
  272. blobmsg_add_string(buf, "target", target);
  273. blobmsg_add_string(buf, "subtarget", subtarget);
  274. blobmsg_add_string(buf, "version", version);
  275. }
  276. /*
  277. * rpc-sys upgrade_test
  278. * check if downloaded file is accepted by sysupgrade
  279. */
  280. static void upgtest_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
  281. int *valid = (int *)req->priv;
  282. struct blob_attr *tb[__UPGTEST_MAX];
  283. blobmsg_parse(upgtest_policy, __UPGTEST_MAX, tb, blob_data(msg), blob_len(msg));
  284. if (!tb[UPGTEST_CODE]) {
  285. fprintf(stderr, "No sysupgrade test return code received\n");
  286. return;
  287. }
  288. *valid = (blobmsg_get_u32(tb[UPGTEST_CODE]) == 0)?1:0;
  289. if (*valid == 0)
  290. fprintf(stderr, "%s", blobmsg_get_string(tb[UPGTEST_STDOUT]));
  291. };
  292. /**
  293. * uclient stuff
  294. */
  295. static int open_output_file(const char *path, uint64_t resume_offset)
  296. {
  297. char *filename = NULL;
  298. int flags;
  299. int ret;
  300. if (cur_resume)
  301. flags = O_RDWR;
  302. else
  303. flags = O_WRONLY | O_EXCL;
  304. flags |= O_CREAT;
  305. filename = uclient_get_url_filename(path, "firmware.bin");
  306. fprintf(stderr, "Writing to '%s'\n", filename);
  307. ret = open(filename, flags, 0644);
  308. if (ret < 0)
  309. goto free;
  310. if (resume_offset &&
  311. lseek(ret, resume_offset, SEEK_SET) < 0) {
  312. fprintf(stderr, "Failed to seek %"PRIu64" bytes in output file\n", resume_offset);
  313. close(ret);
  314. ret = -1;
  315. goto free;
  316. }
  317. out_offset = resume_offset;
  318. out_bytes += resume_offset;
  319. free:
  320. free(filename);
  321. return ret;
  322. }
  323. struct jsonblobber {
  324. json_tokener *tok;
  325. struct blob_buf *outbuf;
  326. };
  327. static void request_done(struct uclient *cl)
  328. {
  329. struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
  330. if (jsb) {
  331. json_tokener_free(jsb->tok);
  332. free(jsb);
  333. };
  334. uclient_disconnect(cl);
  335. uloop_end();
  336. }
  337. static void header_done_cb(struct uclient *cl)
  338. {
  339. struct blob_attr *tb[__H_MAX];
  340. uint64_t resume_offset = 0, resume_end, resume_size;
  341. char *ibstatus;
  342. unsigned int queuepos = 0;
  343. if (uclient_http_redirect(cl)) {
  344. fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host);
  345. return;
  346. }
  347. if (cl->status_code == 204 && cur_resume) {
  348. /* Resume attempt failed, try normal download */
  349. cur_resume = false;
  350. //init_request(cl);
  351. return;
  352. }
  353. DPRINTF("headers:\n%s\n", blobmsg_format_json_indent(cl->meta, true, 0));
  354. blobmsg_parse(policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta));
  355. switch (cl->status_code) {
  356. case 400:
  357. request_done(cl);
  358. rc=-1;
  359. break;
  360. case 412:
  361. fprintf(stderr, "%s target %s/%s (%s) not found. Please report this at %s\n",
  362. distribution, target, subtarget, board_name, server_issues);
  363. request_done(cl);
  364. rc=-2;
  365. break;
  366. case 413:
  367. fprintf(stderr, "image too big.\n");
  368. rc=-1;
  369. request_done(cl);
  370. break;
  371. case 416:
  372. fprintf(stderr, "File download already fully retrieved; nothing to do.\n");
  373. request_done(cl);
  374. break;
  375. case 422:
  376. fprintf(stderr, "unknown package '%s' requested.\n",
  377. blobmsg_get_string(tb[H_UNKNOWN_PACKAGE]));
  378. rc=-1;
  379. request_done(cl);
  380. break;
  381. case 501:
  382. fprintf(stderr, "ImageBuilder didn't produce sysupgrade file.\n");
  383. rc=-2;
  384. request_done(cl);
  385. break;
  386. case 204:
  387. fprintf(stdout, "system is up to date.\n");
  388. uptodate=1;
  389. request_done(cl);
  390. break;
  391. case 206:
  392. if (!cur_resume) {
  393. fprintf(stderr, "Error: Partial content received, full content requested\n");
  394. request_done(cl);
  395. break;
  396. }
  397. if (!tb[H_RANGE]) {
  398. fprintf(stderr, "Content-Range header is missing\n");
  399. break;
  400. }
  401. if (sscanf(blobmsg_get_string(tb[H_RANGE]),
  402. "bytes %"PRIu64"-%"PRIu64"/%"PRIu64,
  403. &resume_offset, &resume_end, &resume_size) != 3) {
  404. fprintf(stderr, "Content-Range header is invalid\n");
  405. break;
  406. }
  407. case 202:
  408. if (!tb[H_IBSTATUS])
  409. break;
  410. ibstatus = blobmsg_get_string(tb[H_IBSTATUS]);
  411. if (!strncmp(ibstatus, "queue", 6)) {
  412. if (!imagebuilder) {
  413. fprintf(stderr, "server is dispatching build job\n");
  414. imagebuilder=1;
  415. } else {
  416. if (tb[H_IBQUEUEPOS]) {
  417. queuepos = atoi(blobmsg_get_string(tb[H_IBQUEUEPOS]));
  418. fprintf(stderr, "build is in queue position %u.\n", queuepos);
  419. }
  420. }
  421. retry=1;
  422. } else if (!strncmp(ibstatus, "building", 9)) {
  423. if (!building) {
  424. fprintf(stderr, "server is now building image...\n");
  425. building=1;
  426. }
  427. retry=1;
  428. } else if (!strncmp(ibstatus, "initialize", 11)) {
  429. if (!ibready) {
  430. fprintf(stderr, "server is setting up ImageBuilder...\n");
  431. ibready=1;
  432. }
  433. retry=1;
  434. } else {
  435. fprintf(stderr, "unrecognized remote imagebuilder status '%s'\n", ibstatus);
  436. rc=-2;
  437. }
  438. // fall through
  439. case 200:
  440. if (cl->priv)
  441. break;
  442. if (tb[H_LEN])
  443. out_len = strtoul(blobmsg_get_string(tb[H_LEN]), NULL, 10);
  444. output_fd = open_output_file(cl->url->location, resume_offset);
  445. if (output_fd < 0) {
  446. perror("Cannot open output file");
  447. request_done(cl);
  448. }
  449. break;
  450. default:
  451. fprintf(stderr, "HTTP error %d\n", cl->status_code);
  452. request_done(cl);
  453. break;
  454. }
  455. }
  456. static void read_data_cb(struct uclient *cl)
  457. {
  458. char buf[256];
  459. int len;
  460. json_object *jsobj;
  461. struct blob_buf *outbuf = NULL;
  462. json_tokener *tok = NULL;
  463. struct jsonblobber *jsb = (struct jsonblobber *)cl->priv;
  464. if (!jsb) {
  465. while (1) {
  466. len = uclient_read(cl, buf, sizeof(buf));
  467. if (!len)
  468. return;
  469. out_bytes += len;
  470. write(output_fd, buf, len);
  471. }
  472. return;
  473. }
  474. outbuf = jsb->outbuf;
  475. tok = jsb->tok;
  476. while (1) {
  477. len = uclient_read(cl, buf, sizeof(buf));
  478. if (!len)
  479. break;
  480. out_bytes += len;
  481. jsobj = json_tokener_parse_ex(tok, buf, len);
  482. if (json_tokener_get_error(tok) == json_tokener_continue)
  483. continue;
  484. if (json_tokener_get_error(tok) != json_tokener_success)
  485. break;
  486. if (jsobj)
  487. {
  488. if (json_object_get_type(jsobj) == json_type_object)
  489. blobmsg_add_object(outbuf, jsobj);
  490. json_object_put(jsobj);
  491. break;
  492. }
  493. }
  494. }
  495. static void eof_cb(struct uclient *cl)
  496. {
  497. if (!cl->data_eof && !uptodate) {
  498. fprintf(stderr, "Connection reset prematurely\n");
  499. }
  500. request_done(cl);
  501. }
  502. static void handle_uclient_error(struct uclient *cl, int code)
  503. {
  504. const char *type = "Unknown error";
  505. switch(code) {
  506. case UCLIENT_ERROR_CONNECT:
  507. type = "Connection failed";
  508. break;
  509. case UCLIENT_ERROR_TIMEDOUT:
  510. type = "Connection timed out";
  511. break;
  512. case UCLIENT_ERROR_SSL_INVALID_CERT:
  513. type = "Invalid SSL certificate";
  514. break;
  515. case UCLIENT_ERROR_SSL_CN_MISMATCH:
  516. type = "Server hostname does not match SSL certificate";
  517. break;
  518. default:
  519. break;
  520. }
  521. fprintf(stderr, "Connection error: %s\n", type);
  522. request_done(cl);
  523. }
  524. static const struct uclient_cb check_cb = {
  525. .header_done = header_done_cb,
  526. .data_read = read_data_cb,
  527. .data_eof = eof_cb,
  528. .error = handle_uclient_error,
  529. };
  530. static int server_request(const char *url, struct blob_buf *inbuf, struct blob_buf *outbuf) {
  531. struct uclient *ucl;
  532. struct jsonblobber *jsb = NULL;
  533. int rc = -1;
  534. char *post_data;
  535. out_offset = 0;
  536. out_bytes = 0;
  537. out_len = 0;
  538. uloop_init();
  539. ucl = uclient_new(url, NULL, &check_cb);
  540. if (outbuf) {
  541. jsb = malloc(sizeof(struct jsonblobber));
  542. jsb->outbuf = outbuf;
  543. jsb->tok = json_tokener_new();
  544. };
  545. uclient_http_set_ssl_ctx(ucl, ssl_ops, ssl_ctx, 1);
  546. ucl->timeout_msecs = REQ_TIMEOUT * 1000;
  547. ucl->priv = jsb;
  548. rc = uclient_connect(ucl);
  549. if (rc)
  550. return rc;
  551. rc = uclient_http_set_request_type(ucl, inbuf?"POST":"GET");
  552. if (rc)
  553. return rc;
  554. uclient_http_reset_headers(ucl);
  555. uclient_http_set_header(ucl, "User-Agent", user_agent);
  556. if (inbuf) {
  557. uclient_http_set_header(ucl, "Content-Type", "text/json");
  558. post_data = blobmsg_format_json(inbuf->head, true);
  559. uclient_write(ucl, post_data, strlen(post_data));
  560. }
  561. rc = uclient_request(ucl);
  562. if (rc)
  563. return rc;
  564. uloop_run();
  565. uloop_done();
  566. uclient_free(ucl);
  567. return 0;
  568. }
  569. /**
  570. * ustream-ssl
  571. */
  572. static int init_ustream_ssl(void) {
  573. void *dlh;
  574. glob_t gl;
  575. int i;
  576. dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
  577. if (!dlh)
  578. return -1;
  579. ssl_ops = dlsym(dlh, "ustream_ssl_ops");
  580. if (!ssl_ops)
  581. return -1;
  582. ssl_ctx = ssl_ops->context_new(false);
  583. glob("/etc/ssl/certs/*.crt", 0, NULL, &gl);
  584. if (!gl.gl_pathc)
  585. return -2;
  586. for (i = 0; i < gl.gl_pathc; i++)
  587. ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]);
  588. return 0;
  589. }
  590. /**
  591. * use busybox sha256sum to verify sha256sums file
  592. */
  593. static int sha256sum_v(const char *sha256file, const char *msgfile) {
  594. pid_t pid;
  595. int fds[2];
  596. int status;
  597. FILE *f = fopen(sha256file, "r");
  598. char sumline[512] = {};
  599. char *fname;
  600. unsigned int fnlen;
  601. unsigned int cnt = 0;
  602. if (pipe(fds))
  603. return -1;
  604. if (!f)
  605. return -1;
  606. pid = fork();
  607. switch (pid) {
  608. case -1:
  609. return -1;
  610. case 0:
  611. uloop_done();
  612. dup2(fds[0], 0);
  613. close(1);
  614. close(2);
  615. close(fds[0]);
  616. close(fds[1]);
  617. if (execl("/bin/busybox", "/bin/busybox", "sha256sum", "-s", "-c", NULL));
  618. return -1;
  619. break;
  620. default:
  621. while (fgets(sumline, sizeof(sumline), f)) {
  622. fname = &sumline[66];
  623. fnlen = strlen(fname);
  624. fname[fnlen-1] = '\0';
  625. if (!strcmp(fname, msgfile)) {
  626. fname[fnlen-1] = '\n';
  627. write(fds[1], sumline, strlen(sumline));
  628. cnt++;
  629. }
  630. }
  631. fclose(f);
  632. close(fds[1]);
  633. waitpid(pid, &status, 0);
  634. close(fds[0]);
  635. if (cnt == 1)
  636. return WEXITSTATUS(status);
  637. else
  638. return -1;
  639. }
  640. return -1;
  641. }
  642. /**
  643. * use usign to verify sha256sums.sig
  644. */
  645. static int usign_v(const char *file) {
  646. pid_t pid;
  647. int status;
  648. pid = fork();
  649. switch (pid) {
  650. case -1:
  651. return -1;
  652. case 0:
  653. uloop_done();
  654. if (execl("/usr/bin/usign", "/usr/bin/usign",
  655. "-V", "-q", "-P", PUBKEY_PATH, "-m", file, NULL));
  656. return -1;
  657. break;
  658. default:
  659. waitpid(pid, &status, 0);
  660. return WEXITSTATUS(status);
  661. }
  662. return -1;
  663. }
  664. static int ask_user(void)
  665. {
  666. fprintf(stderr, "Are you sure you want to continue the upgrade process? [N/y] ");
  667. if (getchar() != 'y')
  668. return -1;
  669. return 0;
  670. }
  671. static void print_package_updates(struct blob_attr *upgrades) {
  672. struct blob_attr *cur;
  673. struct blob_attr *tb[2];
  674. int rem;
  675. blobmsg_for_each_attr(cur, upgrades, rem) {
  676. blobmsg_parse_array(pkg_upgrades_policy, ARRAY_SIZE(policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
  677. if (!tb[0] || !tb[1])
  678. continue;
  679. fprintf(stdout, "\t%s (%s -> %s)\n", blobmsg_name(cur),
  680. blobmsg_get_string(tb[1]), blobmsg_get_string(tb[0]));
  681. };
  682. }
  683. /* this main function is too big... todo: split */
  684. int main(int args, char *argv[]) {
  685. static struct blob_buf allpkg, checkbuf, infobuf, reqbuf, imgbuf, upgbuf;
  686. struct ubus_context *ctx = ubus_connect(NULL);
  687. uint32_t id;
  688. int valid, use_get;
  689. char url[256];
  690. char *newversion = NULL;
  691. struct blob_attr *tb[__IMAGE_MAX];
  692. struct blob_attr *tbc[__CHECK_MAX];
  693. char *tmp;
  694. struct stat imgstat;
  695. int check_only = 0;
  696. int ignore_sig = 0;
  697. unsigned char argc = 1;
  698. snprintf(user_agent, sizeof(user_agent), "%s (%s)", argv[0], AUC_VERSION);
  699. fprintf(stdout, "%s\n", user_agent);
  700. while (argc<args) {
  701. if (!strncmp(argv[argc], "-h", 3) ||
  702. !strncmp(argv[argc], "--help", 7)) {
  703. fprintf(stdout, "%s: Attended sysUpgrade CLI client\n", argv[0]);
  704. fprintf(stdout, "Usage: auc [-d] [-h]\n");
  705. fprintf(stdout, " -c\tonly check if system is up-to-date\n");
  706. fprintf(stdout, " -F\tignore result of signature verification\n");
  707. #ifdef AUC_DEBUG
  708. fprintf(stdout, " -d\tenable debugging output\n");
  709. #endif
  710. fprintf(stdout, " -h\toutput help\n");
  711. return 0;
  712. }
  713. #ifdef AUC_DEBUG
  714. if (!strncmp(argv[argc], "-d", 3))
  715. debug = 1;
  716. #endif
  717. if (!strncmp(argv[argc], "-c", 3))
  718. check_only = 1;
  719. if (!strncmp(argv[argc], "-F", 3))
  720. ignore_sig = 1;
  721. argc++;
  722. };
  723. if (!ctx) {
  724. fprintf(stderr, "failed to connect to ubus.\n");
  725. return -1;
  726. }
  727. if (load_config()) {
  728. rc=-1;
  729. goto freeubus;
  730. }
  731. if (chdir("/tmp")) {
  732. rc=-1;
  733. goto freeconfig;
  734. }
  735. rc = init_ustream_ssl();
  736. if (rc == -2) {
  737. fprintf(stderr, "No CA certificates loaded, please install ca-certificates\n");
  738. rc=-1;
  739. goto freessl;
  740. }
  741. if (rc || !ssl_ctx) {
  742. fprintf(stderr, "SSL support not available, please install ustream-ssl\n");
  743. rc=-1;
  744. goto freessl;
  745. }
  746. blobmsg_buf_init(&checkbuf);
  747. blobmsg_buf_init(&infobuf);
  748. blobmsg_buf_init(&reqbuf);
  749. blobmsg_buf_init(&imgbuf);
  750. /* ubus requires BLOBMSG_TYPE_UNSPEC */
  751. blob_buf_init(&allpkg, 0);
  752. blob_buf_init(&upgbuf, 0);
  753. if (ubus_lookup_id(ctx, "system", &id) ||
  754. ubus_invoke(ctx, id, "board", NULL, board_cb, &checkbuf, 3000)) {
  755. fprintf(stderr, "cannot request board info from procd\n");
  756. rc=-1;
  757. goto freebufs;
  758. }
  759. if (rc)
  760. goto freebufs;
  761. blobmsg_add_u8(&allpkg, "all", 1);
  762. blobmsg_add_string(&allpkg, "dummy", "foo");
  763. if (ubus_lookup_id(ctx, "rpc-sys", &id) ||
  764. ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_check_cb, &checkbuf, 3000)) {
  765. fprintf(stderr, "cannot request packagelist from rpcd\n");
  766. rc=-1;
  767. goto freeboard;
  768. }
  769. if (rc)
  770. goto freeboard;
  771. blobmsg_add_u32(&checkbuf, "upgrade_packages", upgrade_packages);
  772. fprintf(stdout, "running %s %s on %s/%s (%s)\n", distribution,
  773. version, target, subtarget, board_name);
  774. fprintf(stdout, "checking %s for release upgrade%s\n", serverurl,
  775. upgrade_packages?" or updated packages":"");
  776. snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_CHECK);
  777. uptodate=0;
  778. do {
  779. retry=0;
  780. DPRINTF("requesting:\n%s\n", blobmsg_format_json_indent(checkbuf.head, true, 0));
  781. if (server_request(url, &checkbuf, &infobuf)) {
  782. fprintf(stderr, "failed to connect to server\n");
  783. rc=-1;
  784. goto freeboard;
  785. };
  786. if (retry)
  787. sleep(3);
  788. } while(retry);
  789. DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(infobuf.head, true, 0));
  790. blobmsg_parse(check_policy, __CHECK_MAX, tbc, blob_data(infobuf.head), blob_len(infobuf.head));
  791. if (!tbc[CHECK_VERSION] && !tbc[CHECK_UPGRADES]) {
  792. if (uptodate) {
  793. rc=0;
  794. } else if (!rc) {
  795. fprintf(stderr, "server reply invalid.\n");
  796. rc=-2;
  797. }
  798. goto freeboard;
  799. }
  800. if (tbc[CHECK_VERSION]) {
  801. newversion = blobmsg_get_string(tbc[CHECK_VERSION]);
  802. fprintf(stdout, "new %s release %s found.\n", distribution, newversion);
  803. } else {
  804. newversion = version;
  805. fprintf(stdout, "staying on %s release version %s\n", distribution, version);
  806. };
  807. blobmsg_add_string(&reqbuf, "version", newversion);
  808. if (tbc[CHECK_UPGRADES]) {
  809. fprintf(stdout, "package updates:\n");
  810. print_package_updates(tbc[CHECK_UPGRADES]);
  811. }
  812. if (check_only) {
  813. rc=1;
  814. goto freeboard;
  815. };
  816. rc = ask_user();
  817. if (rc)
  818. goto freeboard;
  819. blobmsg_add_string(&reqbuf, "distro", distribution);
  820. blobmsg_add_string(&reqbuf, "target", target);
  821. blobmsg_add_string(&reqbuf, "subtarget", subtarget);
  822. blobmsg_add_string(&reqbuf, "board", board_name);
  823. blob_buf_init(&allpkg, 0);
  824. blobmsg_add_u8(&allpkg, "all", 0);
  825. blobmsg_add_string(&allpkg, "dummy", "foo");
  826. if (ubus_invoke(ctx, id, "packagelist", allpkg.head, pkglist_req_cb, &reqbuf, 3000)) {
  827. fprintf(stderr, "cannot request packagelist from rpcd\n");
  828. rc=-1;
  829. goto freeboard;
  830. }
  831. snprintf(url, sizeof(url), "%s/%s", serverurl, APIOBJ_REQUEST);
  832. imagebuilder = 0;
  833. building = 0;
  834. use_get = 0;
  835. do {
  836. retry = 0;
  837. DPRINTF("requesting:\n%s\n", use_get?"":blobmsg_format_json_indent(reqbuf.head, true, 0));
  838. server_request(url, use_get?NULL:&reqbuf, &imgbuf);
  839. blobmsg_parse(image_policy, __IMAGE_MAX, tb, blob_data(imgbuf.head), blob_len(imgbuf.head));
  840. if (!use_get && tb[IMAGE_REQHASH]) {
  841. snprintf(url, sizeof(url), "%s/%s/%s", serverurl,
  842. APIOBJ_REQUEST,
  843. blobmsg_get_string(tb[IMAGE_REQHASH]));
  844. DPRINTF("polling via GET %s\n", url);
  845. retry=1;
  846. use_get=1;
  847. }
  848. if (retry) {
  849. blob_buf_free(&imgbuf);
  850. blobmsg_buf_init(&imgbuf);
  851. sleep(3);
  852. }
  853. } while(retry);
  854. DPRINTF("reply:\n%s\n", blobmsg_format_json_indent(imgbuf.head, true, 0));
  855. if (!tb[IMAGE_SYSUPGRADE]) {
  856. if (!rc) {
  857. fprintf(stderr, "no sysupgrade image returned\n");
  858. rc=-1;
  859. }
  860. goto freeboard;
  861. }
  862. strncpy(url, blobmsg_get_string(tb[IMAGE_SYSUPGRADE]), sizeof(url));
  863. server_request(url, NULL, NULL);
  864. filename = uclient_get_url_filename(url, "firmware.bin");
  865. if (stat(filename, &imgstat)) {
  866. fprintf(stderr, "image download failed\n");
  867. rc=-1;
  868. goto freeboard;
  869. }
  870. if ((intmax_t)imgstat.st_size != out_len) {
  871. fprintf(stderr, "file size mismatch\n");
  872. unlink(filename);
  873. rc=-1;
  874. goto freeboard;
  875. }
  876. tmp=strrchr(url, '/');
  877. strcpy(tmp, "/sha256sums");
  878. server_request(url, NULL, NULL);
  879. if (stat("sha256sums", &imgstat)) {
  880. fprintf(stderr, "sha256sums download failed\n");
  881. rc=-1;
  882. goto freeboard;
  883. }
  884. if ((intmax_t)imgstat.st_size != out_len) {
  885. fprintf(stderr, "sha256sums download incomplete\n");
  886. unlink("sha256sums");
  887. rc=-1;
  888. goto freeboard;
  889. }
  890. if (out_len < 68) {
  891. fprintf(stderr, "sha256sums size mismatch\n");
  892. unlink("sha256sums");
  893. rc=-1;
  894. goto freeboard;
  895. }
  896. if (sha256sum_v("sha256sums", filename)) {
  897. fprintf(stderr, "checksum verification failed\n");
  898. unlink(filename);
  899. unlink("sha256sums");
  900. rc=-1;
  901. goto freeboard;
  902. }
  903. strcpy(tmp, "/sha256sums.sig");
  904. server_request(url, NULL, NULL);
  905. if (stat("sha256sums.sig", &imgstat)) {
  906. fprintf(stderr, "sha256sums.sig download failed\n");
  907. rc=-1;
  908. goto freeboard;
  909. }
  910. if ((intmax_t)imgstat.st_size != out_len) {
  911. fprintf(stderr, "sha256sums.sig download incomplete\n");
  912. unlink("sha256sums.sig");
  913. rc=-1;
  914. goto freeboard;
  915. }
  916. if (out_len < 16) {
  917. fprintf(stderr, "sha256sums.sig size mismatch\n");
  918. unlink("sha256sums.sig");
  919. rc=-1;
  920. goto freeboard;
  921. }
  922. if (usign_v("sha256sums")) {
  923. fprintf(stderr, "signature verification failed\n");
  924. if (!ignore_sig) {
  925. unlink(filename);
  926. unlink("sha256sums");
  927. unlink("sha256sums.sig");
  928. rc=-1;
  929. goto freeboard;
  930. }
  931. };
  932. if (strcmp(filename, "firmware.bin")) {
  933. if (rename(filename, "firmware.bin")) {
  934. fprintf(stderr, "can't rename to firmware.bin\n");
  935. unlink(filename);
  936. rc=-1;
  937. goto freeboard;
  938. }
  939. }
  940. valid = 0;
  941. ubus_invoke(ctx, id, "upgrade_test", NULL, upgtest_cb, &valid, 3000);
  942. if (!valid) {
  943. rc=-1;
  944. goto freeboard;
  945. }
  946. blobmsg_add_u8(&upgbuf, "keep", 1);
  947. fprintf(stdout, "invoking sysupgrade\n");
  948. ubus_invoke(ctx, id, "upgrade_start", upgbuf.head, NULL, NULL, 3000);
  949. freeboard:
  950. free(board_name);
  951. free(target);
  952. /* subtarget is a pointer within target, don't free */
  953. free(distribution);
  954. free(version);
  955. freebufs:
  956. blob_buf_free(&checkbuf);
  957. blob_buf_free(&infobuf);
  958. blob_buf_free(&reqbuf);
  959. blob_buf_free(&imgbuf);
  960. blob_buf_free(&upgbuf);
  961. freessl:
  962. if (ssl_ctx)
  963. ssl_ops->context_free(ssl_ctx);
  964. freeconfig:
  965. free(serverurl);
  966. freeubus:
  967. ubus_free(ctx);
  968. return rc;
  969. }