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.

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