uxc.c 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594
  1. /*
  2. * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License version 2.1
  6. * as published by the Free Software Foundation
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #ifndef _GNU_SOURCE
  14. #define _GNU_SOURCE
  15. #endif
  16. #include <errno.h>
  17. #include <fcntl.h>
  18. #include <getopt.h>
  19. #include <glob.h>
  20. #include <stdlib.h>
  21. #include <stdbool.h>
  22. #include <stdio.h>
  23. #include <signal.h>
  24. #include <termios.h>
  25. #include <unistd.h>
  26. #include <sys/stat.h>
  27. #include <sys/types.h>
  28. #include <libubus.h>
  29. #include <libubox/avl-cmp.h>
  30. #include <libubox/blobmsg.h>
  31. #include <libubox/blobmsg_json.h>
  32. #include <libubox/ustream.h>
  33. #include "log.h"
  34. #define UXC_VERSION "0.3"
  35. #define OCI_VERSION_STRING "1.0.2"
  36. #define UXC_ETC_CONFDIR "/etc/uxc"
  37. #define UXC_VOL_CONFDIR "/tmp/run/uvol/.meta/uxc"
  38. static bool verbose = false;
  39. static bool json_output = false;
  40. static char *confdir = UXC_ETC_CONFDIR;
  41. static struct ustream_fd cufd;
  42. static struct ustream_fd lufd;
  43. struct runtime_state {
  44. struct avl_node avl;
  45. char *container_name;
  46. char *instance_name;
  47. char *jail_name;
  48. bool running;
  49. int runtime_pid;
  50. int exitcode;
  51. struct blob_attr *ocistate;
  52. };
  53. struct settings {
  54. struct avl_node avl;
  55. char *container_name;
  56. const char *fname;
  57. char *tmprwsize;
  58. char *writepath;
  59. signed char autostart;
  60. struct blob_attr *volumes;
  61. };
  62. enum uxc_cmd {
  63. CMD_ATTACH,
  64. CMD_LIST,
  65. CMD_BOOT,
  66. CMD_START,
  67. CMD_STATE,
  68. CMD_KILL,
  69. CMD_ENABLE,
  70. CMD_DISABLE,
  71. CMD_DELETE,
  72. CMD_CREATE,
  73. CMD_UNKNOWN
  74. };
  75. #define OPT_ARGS "ab:fjm:p:t:vVw:"
  76. static struct option long_options[] = {
  77. {"autostart", no_argument, 0, 'a' },
  78. {"console", no_argument, 0, 'c' },
  79. {"bundle", required_argument, 0, 'b' },
  80. {"force", no_argument, 0, 'f' },
  81. {"json", no_argument, 0, 'j' },
  82. {"mounts", required_argument, 0, 'm' },
  83. {"pid-file", required_argument, 0, 'p' },
  84. {"temp-overlay-size", required_argument, 0, 't' },
  85. {"write-overlay-path", required_argument, 0, 'w' },
  86. {"verbose", no_argument, 0, 'v' },
  87. {"version", no_argument, 0, 'V' },
  88. {0, 0, 0, 0 }
  89. };
  90. AVL_TREE(runtime, avl_strcmp, false, NULL);
  91. AVL_TREE(settings, avl_strcmp, false, NULL);
  92. static struct blob_buf conf;
  93. static struct blob_buf settingsbuf;
  94. static struct blob_attr *blockinfo;
  95. static struct blob_attr *fstabinfo;
  96. static struct ubus_context *ctx;
  97. static int usage(void) {
  98. printf("syntax: uxc <command> [parameters ...]\n");
  99. printf("commands:\n");
  100. printf("\tlist [--json]\t\t\t\tlist all configured containers\n");
  101. printf("\tattach <conf>\t\t\t\tattach to container console\n");
  102. printf("\tcreate <conf>\t\t\t\t(re-)create <conf>\n");
  103. printf("\t\t[--bundle <path>]\t\t\tOCI bundle at <path>\n");
  104. printf("\t\t[--autostart]\t\t\t\tstart on boot\n");
  105. printf("\t\t[--temp-overlay-size <size>]\t\tuse tmpfs overlay with {size}\n");
  106. printf("\t\t[--write-overlay-path <path>]\t\tuse overlay on {path}\n");
  107. printf("\t\t[--mounts <v1>,<v2>,...,<vN>]\t\trequire filesystems to be available\n");
  108. printf("\tstart [--console] <conf>\t\tstart container <conf>\n");
  109. printf("\tstate <conf>\t\t\t\tget state of container <conf>\n");
  110. printf("\tkill <conf> [<signal>]\t\t\tsend signal to container <conf>\n");
  111. printf("\tenable <conf>\t\t\t\tstart container <conf> on boot\n");
  112. printf("\tdisable <conf>\t\t\t\tdon't start container <conf> on boot\n");
  113. printf("\tdelete <conf> [--force]\t\t\tdelete <conf>\n");
  114. return -EINVAL;
  115. }
  116. enum {
  117. CONF_NAME,
  118. CONF_PATH,
  119. CONF_JAIL,
  120. CONF_AUTOSTART,
  121. CONF_PIDFILE,
  122. CONF_TEMP_OVERLAY_SIZE,
  123. CONF_WRITE_OVERLAY_PATH,
  124. CONF_VOLUMES,
  125. __CONF_MAX,
  126. };
  127. static const struct blobmsg_policy conf_policy[__CONF_MAX] = {
  128. [CONF_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
  129. [CONF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
  130. [CONF_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING },
  131. [CONF_AUTOSTART] = { .name = "autostart", .type = BLOBMSG_TYPE_BOOL },
  132. [CONF_PIDFILE] = { .name = "pidfile", .type = BLOBMSG_TYPE_STRING },
  133. [CONF_TEMP_OVERLAY_SIZE] = { .name = "temp-overlay-size", .type = BLOBMSG_TYPE_STRING },
  134. [CONF_WRITE_OVERLAY_PATH] = { .name = "write-overlay-path", .type = BLOBMSG_TYPE_STRING },
  135. [CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY },
  136. };
  137. static int conf_load(bool load_settings)
  138. {
  139. int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
  140. int j, res;
  141. glob_t gl;
  142. char *globstr;
  143. void *c, *o;
  144. struct stat sb;
  145. struct blob_buf *target;
  146. if (asprintf(&globstr, "%s/%s*.json", UXC_ETC_CONFDIR, load_settings?"settings/":"") == -1)
  147. return -ENOMEM;
  148. res = glob(globstr, gl_flags, NULL, &gl);
  149. if (res == 0)
  150. gl_flags |= GLOB_APPEND;
  151. free(globstr);
  152. if (!stat(UXC_VOL_CONFDIR, &sb)) {
  153. if (sb.st_mode & S_IFDIR) {
  154. if (asprintf(&globstr, "%s/%s*.json", UXC_VOL_CONFDIR, load_settings?"settings/":"") == -1)
  155. return -ENOMEM;
  156. res = glob(globstr, gl_flags, NULL, &gl);
  157. free(globstr);
  158. }
  159. }
  160. target = load_settings ? &settingsbuf : &conf;
  161. blob_buf_init(target, 0);
  162. c = blobmsg_open_table(target, NULL);
  163. if (res < 0)
  164. return 0;
  165. for (j = 0; j < gl.gl_pathc; j++) {
  166. o = blobmsg_open_table(target, strdup(gl.gl_pathv[j]));
  167. if (!blobmsg_add_json_from_file(target, gl.gl_pathv[j])) {
  168. ERROR("uxc: failed to load %s\n", gl.gl_pathv[j]);
  169. continue;
  170. }
  171. blobmsg_close_table(target, o);
  172. }
  173. blobmsg_close_table(target, c);
  174. globfree(&gl);
  175. return 0;
  176. }
  177. static struct settings *
  178. settings_alloc(const char *container_name)
  179. {
  180. struct settings *s;
  181. char *new_name;
  182. s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
  183. strcpy(new_name, container_name);
  184. s->container_name = new_name;
  185. s->avl.key = s->container_name;
  186. s->autostart = -1;
  187. s->tmprwsize = NULL;
  188. s->writepath = NULL;
  189. s->volumes = NULL;
  190. return s;
  191. }
  192. static int settings_add(void)
  193. {
  194. struct blob_attr *cur, *tb[__CONF_MAX];
  195. struct settings *s;
  196. int rem, err;
  197. avl_init(&settings, avl_strcmp, false, NULL);
  198. blobmsg_for_each_attr(cur, blob_data(settingsbuf.head), rem) {
  199. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  200. if (!tb[CONF_NAME])
  201. continue;
  202. if (tb[CONF_TEMP_OVERLAY_SIZE] && tb[CONF_WRITE_OVERLAY_PATH])
  203. return -EINVAL;
  204. s = settings_alloc(blobmsg_get_string(tb[CONF_NAME]));
  205. if (tb[CONF_AUTOSTART])
  206. s->autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]);
  207. if (tb[CONF_TEMP_OVERLAY_SIZE])
  208. s->tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
  209. if (tb[CONF_WRITE_OVERLAY_PATH])
  210. s->writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
  211. s->volumes = tb[CONF_VOLUMES];
  212. s->fname = blobmsg_name(cur);
  213. err = avl_insert(&settings, &s->avl);
  214. if (err) {
  215. fprintf(stderr, "error adding settings for %s\n", blobmsg_get_string(tb[CONF_NAME]));
  216. free(s);
  217. }
  218. }
  219. return 0;
  220. }
  221. static void settings_free(void)
  222. {
  223. struct settings *item, *tmp;
  224. avl_for_each_element_safe(&settings, item, avl, tmp) {
  225. avl_delete(&settings, &item->avl);
  226. free(item);
  227. }
  228. return;
  229. }
  230. enum {
  231. LIST_INSTANCES,
  232. __LIST_MAX,
  233. };
  234. static const struct blobmsg_policy list_policy[__LIST_MAX] = {
  235. [LIST_INSTANCES] = { .name = "instances", .type = BLOBMSG_TYPE_TABLE },
  236. };
  237. enum {
  238. INSTANCE_RUNNING,
  239. INSTANCE_PID,
  240. INSTANCE_EXITCODE,
  241. INSTANCE_JAIL,
  242. __INSTANCE_MAX,
  243. };
  244. static const struct blobmsg_policy instance_policy[__INSTANCE_MAX] = {
  245. [INSTANCE_RUNNING] = { .name = "running", .type = BLOBMSG_TYPE_BOOL },
  246. [INSTANCE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
  247. [INSTANCE_EXITCODE] = { .name = "exit_code", .type = BLOBMSG_TYPE_INT32 },
  248. [INSTANCE_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_TABLE },
  249. };
  250. enum {
  251. JAIL_NAME,
  252. __JAIL_MAX,
  253. };
  254. static const struct blobmsg_policy jail_policy[__JAIL_MAX] = {
  255. [JAIL_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
  256. };
  257. static struct runtime_state *
  258. runtime_alloc(const char *container_name)
  259. {
  260. struct runtime_state *s;
  261. char *new_name;
  262. s = calloc_a(sizeof(*s), &new_name, strlen(container_name) + 1);
  263. strcpy(new_name, container_name);
  264. s->container_name = new_name;
  265. s->avl.key = s->container_name;
  266. return s;
  267. }
  268. enum {
  269. STATE_OCIVERSION,
  270. STATE_ID,
  271. STATE_STATUS,
  272. STATE_PID,
  273. STATE_BUNDLE,
  274. STATE_ANNOTATIONS,
  275. __STATE_MAX,
  276. };
  277. static const struct blobmsg_policy state_policy[__STATE_MAX] = {
  278. [STATE_OCIVERSION] = { .name = "ociVersion", .type = BLOBMSG_TYPE_STRING },
  279. [STATE_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING },
  280. [STATE_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
  281. [STATE_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
  282. [STATE_BUNDLE] = { .name = "bundle", .type = BLOBMSG_TYPE_STRING },
  283. [STATE_ANNOTATIONS] = { .name = "annotations", .type = BLOBMSG_TYPE_TABLE },
  284. };
  285. static void ocistate_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  286. {
  287. struct blob_attr **ocistate = (struct blob_attr **)req->priv;
  288. struct blob_attr *tb[__STATE_MAX];
  289. blobmsg_parse(state_policy, __STATE_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
  290. if (!tb[STATE_OCIVERSION] ||
  291. !tb[STATE_ID] ||
  292. !tb[STATE_STATUS] ||
  293. !tb[STATE_BUNDLE])
  294. return;
  295. *ocistate = blob_memdup(msg);
  296. }
  297. static void get_ocistate(struct blob_attr **ocistate, const char *name)
  298. {
  299. char *objname;
  300. unsigned int id;
  301. int ret;
  302. *ocistate = NULL;
  303. if (asprintf(&objname, "container.%s", name) == -1)
  304. exit(ENOMEM);
  305. ret = ubus_lookup_id(ctx, objname, &id);
  306. free(objname);
  307. if (ret)
  308. return;
  309. ubus_invoke(ctx, id, "state", NULL, ocistate_cb, ocistate, 3000);
  310. }
  311. static void list_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  312. {
  313. struct blob_attr *cur, *curi, *tl[__LIST_MAX], *ti[__INSTANCE_MAX], *tj[__JAIL_MAX];
  314. int rem, remi;
  315. const char *container_name, *instance_name, *jail_name;
  316. bool running;
  317. int pid, exitcode;
  318. struct runtime_state *rs;
  319. blobmsg_for_each_attr(cur, msg, rem) {
  320. container_name = blobmsg_name(cur);
  321. blobmsg_parse(list_policy, __LIST_MAX, tl, blobmsg_data(cur), blobmsg_len(cur));
  322. if (!tl[LIST_INSTANCES])
  323. continue;
  324. blobmsg_for_each_attr(curi, tl[LIST_INSTANCES], remi) {
  325. instance_name = blobmsg_name(curi);
  326. blobmsg_parse(instance_policy, __INSTANCE_MAX, ti, blobmsg_data(curi), blobmsg_len(curi));
  327. if (!ti[INSTANCE_JAIL])
  328. continue;
  329. blobmsg_parse(jail_policy, __JAIL_MAX, tj, blobmsg_data(ti[INSTANCE_JAIL]), blobmsg_len(ti[INSTANCE_JAIL]));
  330. if (!tj[JAIL_NAME])
  331. continue;
  332. jail_name = blobmsg_get_string(tj[JAIL_NAME]);
  333. running = ti[INSTANCE_RUNNING] && blobmsg_get_bool(ti[INSTANCE_RUNNING]);
  334. if (ti[INSTANCE_PID])
  335. pid = blobmsg_get_u32(ti[INSTANCE_PID]);
  336. else
  337. pid = -1;
  338. if (ti[INSTANCE_EXITCODE])
  339. exitcode = blobmsg_get_u32(ti[INSTANCE_EXITCODE]);
  340. else
  341. exitcode = -1;
  342. rs = runtime_alloc(container_name);
  343. rs->instance_name = strdup(instance_name);
  344. rs->jail_name = strdup(jail_name);
  345. rs->runtime_pid = pid;
  346. rs->exitcode = exitcode;
  347. rs->running = running;
  348. avl_insert(&runtime, &rs->avl);
  349. }
  350. }
  351. return;
  352. }
  353. static int runtime_load(void)
  354. {
  355. struct runtime_state *item, *tmp;
  356. uint32_t id;
  357. avl_init(&runtime, avl_strcmp, false, NULL);
  358. if (ubus_lookup_id(ctx, "container", &id) ||
  359. ubus_invoke(ctx, id, "list", NULL, list_cb, &runtime, 3000))
  360. return -EIO;
  361. avl_for_each_element_safe(&runtime, item, avl, tmp)
  362. get_ocistate(&item->ocistate, item->jail_name);
  363. return 0;
  364. }
  365. static void runtime_free(void)
  366. {
  367. struct runtime_state *item, *tmp;
  368. avl_for_each_element_safe(&runtime, item, avl, tmp) {
  369. avl_delete(&runtime, &item->avl);
  370. free(item->instance_name);
  371. free(item->jail_name);
  372. free(item->ocistate);
  373. free(item);
  374. }
  375. return;
  376. }
  377. static inline int setup_tios(int fd, struct termios *oldtios)
  378. {
  379. struct termios newtios;
  380. if (!isatty(fd)) {
  381. return -EIO;
  382. }
  383. /* Get current termios */
  384. if (tcgetattr(fd, oldtios) < 0)
  385. return -errno;
  386. newtios = *oldtios;
  387. /* We use the same settings that ssh does. */
  388. newtios.c_iflag |= IGNPAR;
  389. newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
  390. newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
  391. newtios.c_oflag &= ~ONLCR;
  392. newtios.c_oflag |= OPOST;
  393. newtios.c_cc[VMIN] = 1;
  394. newtios.c_cc[VTIME] = 0;
  395. /* Set new attributes */
  396. if (tcsetattr(fd, TCSAFLUSH, &newtios) < 0)
  397. return -errno;
  398. return 0;
  399. }
  400. static void client_cb(struct ustream *s, int bytes)
  401. {
  402. char *buf;
  403. int len, rv;
  404. do {
  405. buf = ustream_get_read_buf(s, &len);
  406. if (!buf)
  407. break;
  408. rv = ustream_write(&lufd.stream, buf, len, false);
  409. if (rv > 0)
  410. ustream_consume(s, rv);
  411. if (rv <= len)
  412. break;
  413. } while(1);
  414. }
  415. static void local_cb(struct ustream *s, int bytes)
  416. {
  417. char *buf;
  418. int len, rv;
  419. do {
  420. buf = ustream_get_read_buf(s, &len);
  421. if (!buf)
  422. break;
  423. if ((len > 0) && (buf[0] == 2))
  424. uloop_end();
  425. rv = ustream_write(&cufd.stream, buf, len, false);
  426. if (rv > 0)
  427. ustream_consume(s, rv);
  428. if (rv <= len)
  429. break;
  430. } while(1);
  431. }
  432. static int uxc_attach(const char *container_name)
  433. {
  434. struct ubus_context *ctx;
  435. uint32_t id;
  436. static struct blob_buf req;
  437. int client_fd, server_fd, tty_fd;
  438. struct termios oldtermios;
  439. ctx = ubus_connect(NULL);
  440. if (!ctx) {
  441. fprintf(stderr, "can't connect to ubus!\n");
  442. return -ECONNREFUSED;
  443. }
  444. /* open pseudo-terminal pair */
  445. client_fd = posix_openpt(O_RDWR | O_NOCTTY);
  446. if (client_fd < 0) {
  447. fprintf(stderr, "can't create virtual console!\n");
  448. ubus_free(ctx);
  449. return -EIO;
  450. }
  451. setup_tios(client_fd, &oldtermios);
  452. grantpt(client_fd);
  453. unlockpt(client_fd);
  454. server_fd = open(ptsname(client_fd), O_RDWR | O_NOCTTY);
  455. if (server_fd < 0) {
  456. fprintf(stderr, "can't open virtual console!\n");
  457. close(client_fd);
  458. ubus_free(ctx);
  459. return -EIO;
  460. }
  461. setup_tios(server_fd, &oldtermios);
  462. tty_fd = open("/dev/tty", O_RDWR);
  463. if (tty_fd < 0) {
  464. fprintf(stderr, "can't open local console!\n");
  465. close(server_fd);
  466. close(client_fd);
  467. ubus_free(ctx);
  468. return -EIO;
  469. }
  470. setup_tios(tty_fd, &oldtermios);
  471. /* register server-side with procd */
  472. blob_buf_init(&req, 0);
  473. blobmsg_add_string(&req, "name", container_name);
  474. blobmsg_add_string(&req, "instance", container_name);
  475. if (ubus_lookup_id(ctx, "container", &id) ||
  476. ubus_invoke_fd(ctx, id, "console_attach", req.head, NULL, NULL, 3000, server_fd)) {
  477. fprintf(stderr, "ubus request failed\n");
  478. close(tty_fd);
  479. close(server_fd);
  480. close(client_fd);
  481. blob_buf_free(&req);
  482. ubus_free(ctx);
  483. return -ENXIO;
  484. }
  485. close(server_fd);
  486. blob_buf_free(&req);
  487. ubus_free(ctx);
  488. uloop_init();
  489. /* forward between stdio and client_fd until detach is requested */
  490. lufd.stream.notify_read = local_cb;
  491. ustream_fd_init(&lufd, tty_fd);
  492. cufd.stream.notify_read = client_cb;
  493. /* ToDo: handle remote close and other events */
  494. // cufd.stream.notify_state = client_state_cb;
  495. ustream_fd_init(&cufd, client_fd);
  496. fprintf(stderr, "attaching to jail console. press [CTRL]+[B] to exit.\n");
  497. close(0);
  498. close(1);
  499. close(2);
  500. uloop_run();
  501. tcsetattr(tty_fd, TCSAFLUSH, &oldtermios);
  502. ustream_free(&lufd.stream);
  503. ustream_free(&cufd.stream);
  504. close(client_fd);
  505. return 0;
  506. }
  507. static int uxc_state(char *name)
  508. {
  509. struct runtime_state *rsstate = avl_find_element(&runtime, name, rsstate, avl);
  510. struct blob_attr *ocistate = NULL;
  511. struct blob_attr *cur, *tb[__CONF_MAX];
  512. int rem;
  513. char *bundle = NULL;
  514. char *jail_name = NULL;
  515. char *state = NULL;
  516. char *tmp;
  517. static struct blob_buf buf;
  518. if (rsstate)
  519. ocistate = rsstate->ocistate;
  520. if (ocistate) {
  521. state = blobmsg_format_json_indent(ocistate, true, 0);
  522. if (!state)
  523. return -ENOMEM;
  524. printf("%s\n", state);
  525. free(state);
  526. return 0;
  527. }
  528. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  529. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  530. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  531. continue;
  532. if (!strcmp(name, blobmsg_get_string(tb[CONF_NAME]))) {
  533. if (tb[CONF_JAIL])
  534. jail_name = blobmsg_get_string(tb[CONF_JAIL]);
  535. else
  536. jail_name = name;
  537. bundle = blobmsg_get_string(tb[CONF_PATH]);
  538. break;
  539. }
  540. }
  541. if (!bundle)
  542. return -ENOENT;
  543. blob_buf_init(&buf, 0);
  544. blobmsg_add_string(&buf, "ociVersion", OCI_VERSION_STRING);
  545. blobmsg_add_string(&buf, "id", jail_name);
  546. blobmsg_add_string(&buf, "status", rsstate?"stopped":"uninitialized");
  547. blobmsg_add_string(&buf, "bundle", bundle);
  548. tmp = blobmsg_format_json_indent(buf.head, true, 0);
  549. if (!tmp) {
  550. blob_buf_free(&buf);
  551. return -ENOMEM;
  552. }
  553. printf("%s\n", tmp);
  554. free(tmp);
  555. blob_buf_free(&buf);
  556. return 0;
  557. }
  558. static int uxc_list(void)
  559. {
  560. struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
  561. int rem;
  562. struct runtime_state *rsstate = NULL;
  563. struct settings *usettings = NULL;
  564. char *name, *ocistatus, *status, *tmp;
  565. int container_pid = -1;
  566. bool autostart;
  567. static struct blob_buf buf;
  568. void *arr, *obj;
  569. if (json_output) {
  570. blob_buf_init(&buf, 0);
  571. arr = blobmsg_open_array(&buf, "");
  572. }
  573. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  574. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  575. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  576. continue;
  577. autostart = tb[CONF_AUTOSTART] && blobmsg_get_bool(tb[CONF_AUTOSTART]);
  578. ocistatus = NULL;
  579. container_pid = 0;
  580. name = blobmsg_get_string(tb[CONF_NAME]);
  581. rsstate = avl_find_element(&runtime, name, rsstate, avl);
  582. if (rsstate && rsstate->ocistate) {
  583. blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(rsstate->ocistate), blobmsg_len(rsstate->ocistate));
  584. ocistatus = blobmsg_get_string(ts[STATE_STATUS]);
  585. container_pid = blobmsg_get_u32(ts[STATE_PID]);
  586. }
  587. status = ocistatus?:(rsstate && rsstate->running)?"creating":"stopped";
  588. usettings = avl_find_element(&settings, name, usettings, avl);
  589. if (usettings && (usettings->autostart >= 0))
  590. autostart = !!(usettings->autostart);
  591. if (json_output) {
  592. obj = blobmsg_open_table(&buf, "");
  593. blobmsg_add_string(&buf, "name", name);
  594. blobmsg_add_string(&buf, "status", status);
  595. blobmsg_add_u8(&buf, "autostart", autostart);
  596. } else {
  597. printf("[%c] %s %s", autostart?'*':' ', name, status);
  598. }
  599. if (rsstate && !rsstate->running && (rsstate->exitcode >= 0)) {
  600. if (json_output)
  601. blobmsg_add_u32(&buf, "exitcode", rsstate->exitcode);
  602. else
  603. printf(" exitcode: %d (%s)", rsstate->exitcode, strerror(rsstate->exitcode));
  604. }
  605. if (rsstate && rsstate->running && (rsstate->runtime_pid >= 0)) {
  606. if (json_output)
  607. blobmsg_add_u32(&buf, "runtime_pid", rsstate->runtime_pid);
  608. else
  609. printf(" runtime pid: %d", rsstate->runtime_pid);
  610. }
  611. if (rsstate && rsstate->running && (container_pid >= 0)) {
  612. if (json_output)
  613. blobmsg_add_u32(&buf, "container_pid", container_pid);
  614. else
  615. printf(" container pid: %d", container_pid);
  616. }
  617. if (!json_output)
  618. printf("\n");
  619. else
  620. blobmsg_close_table(&buf, obj);
  621. }
  622. if (json_output) {
  623. blobmsg_close_array(&buf, arr);
  624. tmp = blobmsg_format_json_indent(buf.head, true, 0);
  625. if (!tmp) {
  626. blob_buf_free(&buf);
  627. return -ENOMEM;
  628. }
  629. printf("%s\n", tmp);
  630. free(tmp);
  631. blob_buf_free(&buf);
  632. };
  633. return 0;
  634. }
  635. static int uxc_exists(char *name)
  636. {
  637. struct runtime_state *rsstate = NULL;
  638. rsstate = avl_find_element(&runtime, name, rsstate, avl);
  639. if (rsstate && (rsstate->running))
  640. return -EEXIST;
  641. return 0;
  642. }
  643. static int uxc_create(char *name, bool immediately)
  644. {
  645. static struct blob_buf req;
  646. struct blob_attr *cur, *tb[__CONF_MAX];
  647. int rem, ret = 0;
  648. uint32_t id;
  649. struct settings *usettings = NULL;
  650. char *path = NULL, *jailname = NULL, *pidfile = NULL, *tmprwsize = NULL, *writepath = NULL;
  651. void *in, *ins, *j;
  652. bool found = false;
  653. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  654. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  655. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  656. continue;
  657. if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
  658. continue;
  659. found = true;
  660. break;
  661. }
  662. if (!found)
  663. return -ENOENT;
  664. path = blobmsg_get_string(tb[CONF_PATH]);
  665. if (tb[CONF_PIDFILE])
  666. pidfile = blobmsg_get_string(tb[CONF_PIDFILE]);
  667. if (tb[CONF_TEMP_OVERLAY_SIZE])
  668. tmprwsize = blobmsg_get_string(tb[CONF_TEMP_OVERLAY_SIZE]);
  669. if (tb[CONF_WRITE_OVERLAY_PATH])
  670. writepath = blobmsg_get_string(tb[CONF_WRITE_OVERLAY_PATH]);
  671. if (tb[CONF_JAIL])
  672. jailname = blobmsg_get_string(tb[CONF_JAIL]);
  673. usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
  674. if (usettings) {
  675. if (usettings->writepath) {
  676. writepath = usettings->writepath;
  677. tmprwsize = NULL;
  678. }
  679. if (usettings->tmprwsize) {
  680. tmprwsize = usettings->tmprwsize;
  681. writepath = NULL;
  682. }
  683. }
  684. blob_buf_init(&req, 0);
  685. blobmsg_add_string(&req, "name", name);
  686. ins = blobmsg_open_table(&req, "instances");
  687. in = blobmsg_open_table(&req, name);
  688. blobmsg_add_string(&req, "bundle", path);
  689. j = blobmsg_open_table(&req, "jail");
  690. blobmsg_add_string(&req, "name", jailname?:name);
  691. blobmsg_add_u8(&req, "immediately", immediately);
  692. if (pidfile)
  693. blobmsg_add_string(&req, "pidfile", pidfile);
  694. blobmsg_close_table(&req, j);
  695. if (writepath)
  696. blobmsg_add_string(&req, "overlaydir", writepath);
  697. if (tmprwsize)
  698. blobmsg_add_string(&req, "tmpoverlaysize", tmprwsize);
  699. blobmsg_close_table(&req, in);
  700. blobmsg_close_table(&req, ins);
  701. if (verbose) {
  702. char *tmp;
  703. tmp = blobmsg_format_json_indent(req.head, true, 1);
  704. if (!tmp)
  705. return -ENOMEM;
  706. fprintf(stderr, "adding container to procd:\n\t%s\n", tmp);
  707. free(tmp);
  708. }
  709. if (ubus_lookup_id(ctx, "container", &id) ||
  710. ubus_invoke(ctx, id, "add", req.head, NULL, NULL, 3000)) {
  711. blob_buf_free(&req);
  712. ret = -EIO;
  713. }
  714. return ret;
  715. }
  716. static int uxc_start(const char *name, bool console)
  717. {
  718. char *objname;
  719. unsigned int id;
  720. pid_t pid;
  721. if (console) {
  722. pid = fork();
  723. if (pid > 0)
  724. exit(uxc_attach(name));
  725. }
  726. if (asprintf(&objname, "container.%s", name) == -1)
  727. return -ENOMEM;
  728. if (ubus_lookup_id(ctx, objname, &id))
  729. return -ENOENT;
  730. free(objname);
  731. return ubus_invoke(ctx, id, "start", NULL, NULL, NULL, 3000);
  732. }
  733. static int uxc_kill(char *name, int signal)
  734. {
  735. static struct blob_buf req;
  736. struct blob_attr *cur, *tb[__CONF_MAX];
  737. int rem, ret;
  738. char *objname;
  739. unsigned int id;
  740. struct runtime_state *rsstate = NULL;
  741. bool found = false;
  742. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  743. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  744. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  745. continue;
  746. if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
  747. continue;
  748. found = true;
  749. break;
  750. }
  751. if (!found)
  752. return -ENOENT;
  753. rsstate = avl_find_element(&runtime, name, rsstate, avl);
  754. if (!rsstate || !(rsstate->running))
  755. return -ENOENT;
  756. blob_buf_init(&req, 0);
  757. blobmsg_add_u32(&req, "signal", signal);
  758. blobmsg_add_string(&req, "name", name);
  759. if (asprintf(&objname, "container.%s", name) == -1)
  760. return -ENOMEM;
  761. ret = ubus_lookup_id(ctx, objname, &id);
  762. free(objname);
  763. if (ret)
  764. return -ENOENT;
  765. if (ubus_invoke(ctx, id, "kill", req.head, NULL, NULL, 3000))
  766. return -EIO;
  767. return 0;
  768. }
  769. static int uxc_set(char *name, char *path, signed char autostart, char *pidfile, char *tmprwsize, char *writepath, char *requiredmounts)
  770. {
  771. static struct blob_buf req;
  772. struct settings *usettings = NULL;
  773. struct blob_attr *cur, *tb[__CONF_MAX];
  774. int rem, ret;
  775. const char *cfname = NULL;
  776. const char *sfname = NULL;
  777. char *fname = NULL;
  778. char *curvol, *tmp, *mnttok;
  779. void *mntarr;
  780. int f;
  781. struct stat sb;
  782. /* nothing to do */
  783. if (!path && (autostart<0) && !pidfile && !tmprwsize && !writepath && !requiredmounts)
  784. return 0;
  785. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  786. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  787. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  788. continue;
  789. if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
  790. continue;
  791. cfname = blobmsg_name(cur);
  792. break;
  793. }
  794. if (cfname && path)
  795. return -EEXIST;
  796. if (!cfname && !path)
  797. return -ENOENT;
  798. if (path) {
  799. if (stat(path, &sb) == -1)
  800. return -ENOENT;
  801. if ((sb.st_mode & S_IFMT) != S_IFDIR)
  802. return -ENOTDIR;
  803. }
  804. usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
  805. if (path && usettings)
  806. return -EIO;
  807. if (usettings) {
  808. sfname = usettings->fname;
  809. if (!tmprwsize && !writepath) {
  810. if (usettings->tmprwsize) {
  811. tmprwsize = usettings->tmprwsize;
  812. writepath = NULL;
  813. }
  814. if (usettings->writepath) {
  815. writepath = usettings->writepath;
  816. tmprwsize = NULL;
  817. }
  818. }
  819. if (usettings->autostart >= 0 && autostart < 0)
  820. autostart = !!(usettings->autostart);
  821. }
  822. if (path) {
  823. ret = mkdir(confdir, 0755);
  824. if (ret && errno != EEXIST)
  825. return -errno;
  826. if (asprintf(&fname, "%s/%s.json", confdir, name) == -1)
  827. return -ENOMEM;
  828. f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  829. if (f < 0)
  830. return -errno;
  831. free(fname);
  832. } else {
  833. if (sfname) {
  834. f = open(sfname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  835. } else {
  836. char *t1, *t2;
  837. t1 = strdup(cfname);
  838. t2 = strrchr(t1, '/');
  839. if (!t2)
  840. return -EINVAL;
  841. *t2 = '\0';
  842. if (asprintf(&t2, "%s/settings", t1) == -1)
  843. return -ENOMEM;
  844. ret = mkdir(t2, 0755);
  845. if (ret && ret != EEXIST)
  846. return -ret;
  847. free(t2);
  848. if (asprintf(&t2, "%s/settings/%s.json", t1, name) == -1)
  849. return -ENOMEM;
  850. free(t1);
  851. f = open(t2, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  852. free(t2);
  853. }
  854. if (f < 0)
  855. return -errno;
  856. }
  857. blob_buf_init(&req, 0);
  858. blobmsg_add_string(&req, "name", name);
  859. if (path)
  860. blobmsg_add_string(&req, "path", path);
  861. if (autostart >= 0)
  862. blobmsg_add_u8(&req, "autostart", !!autostart);
  863. if (pidfile)
  864. blobmsg_add_string(&req, "pidfile", pidfile);
  865. if (tmprwsize)
  866. blobmsg_add_string(&req, "temp-overlay-size", tmprwsize);
  867. if (writepath)
  868. blobmsg_add_string(&req, "write-overlay-path", writepath);
  869. if (!requiredmounts && usettings && usettings->volumes)
  870. blobmsg_add_blob(&req, usettings->volumes);
  871. if (requiredmounts) {
  872. mntarr = blobmsg_open_array(&req, "volumes");
  873. for (mnttok = requiredmounts; ; mnttok = NULL) {
  874. curvol = strtok_r(mnttok, ",;", &tmp);
  875. if (!curvol)
  876. break;
  877. blobmsg_add_string(&req, NULL, curvol);
  878. }
  879. blobmsg_close_array(&req, mntarr);
  880. }
  881. tmp = blobmsg_format_json_indent(req.head, true, 0);
  882. if (tmp) {
  883. dprintf(f, "%s\n", tmp);
  884. free(tmp);
  885. }
  886. blob_buf_free(&req);
  887. close(f);
  888. return 1;
  889. }
  890. enum {
  891. BLOCK_INFO_DEVICE,
  892. BLOCK_INFO_UUID,
  893. BLOCK_INFO_TARGET,
  894. BLOCK_INFO_TYPE,
  895. BLOCK_INFO_MOUNT,
  896. __BLOCK_INFO_MAX,
  897. };
  898. static const struct blobmsg_policy block_info_policy[__BLOCK_INFO_MAX] = {
  899. [BLOCK_INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
  900. [BLOCK_INFO_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
  901. [BLOCK_INFO_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
  902. [BLOCK_INFO_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
  903. [BLOCK_INFO_MOUNT] = { .name = "mount", .type = BLOBMSG_TYPE_STRING },
  904. };
  905. /* check if device 'devname' is mounted according to blockd */
  906. static bool checkblock(const char *uuid)
  907. {
  908. struct blob_attr *tb[__BLOCK_INFO_MAX];
  909. struct blob_attr *cur;
  910. int rem;
  911. blobmsg_for_each_attr(cur, blockinfo, rem) {
  912. blobmsg_parse(block_info_policy, __BLOCK_INFO_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  913. if (!tb[BLOCK_INFO_UUID] || !tb[BLOCK_INFO_MOUNT])
  914. continue;
  915. if (!strcmp(uuid, blobmsg_get_string(tb[BLOCK_INFO_UUID])))
  916. return false;
  917. }
  918. return true;
  919. }
  920. enum {
  921. UCI_FSTAB_UUID,
  922. UCI_FSTAB_ANONYMOUS,
  923. __UCI_FSTAB_MAX,
  924. };
  925. static const struct blobmsg_policy uci_fstab_policy[__UCI_FSTAB_MAX] = {
  926. [UCI_FSTAB_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
  927. [UCI_FSTAB_ANONYMOUS] = { .name = ".anonymous", .type = BLOBMSG_TYPE_BOOL },
  928. };
  929. static const char *resolveuuid(const char *volname)
  930. {
  931. struct blob_attr *tb[__UCI_FSTAB_MAX];
  932. struct blob_attr *cur;
  933. const char *mntname;
  934. char *tmpvolname, *replc;
  935. int rem, res;
  936. blobmsg_for_each_attr(cur, fstabinfo, rem) {
  937. blobmsg_parse(uci_fstab_policy, __UCI_FSTAB_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  938. if (!tb[UCI_FSTAB_UUID])
  939. continue;
  940. if (tb[UCI_FSTAB_ANONYMOUS] && blobmsg_get_bool(tb[UCI_FSTAB_ANONYMOUS]))
  941. continue;
  942. mntname = blobmsg_name(cur);
  943. if (!mntname)
  944. continue;
  945. tmpvolname = strdup(volname);
  946. while ((replc = strchr(tmpvolname, '-')))
  947. *replc = '_';
  948. res = strcmp(tmpvolname, mntname);
  949. free(tmpvolname);
  950. if (!res)
  951. return blobmsg_get_string(tb[UCI_FSTAB_UUID]);
  952. };
  953. return volname;
  954. };
  955. /* check status of each required volume */
  956. static bool checkvolumes(struct blob_attr *volumes)
  957. {
  958. struct blob_attr *cur;
  959. int rem;
  960. blobmsg_for_each_attr(cur, volumes, rem) {
  961. if (checkblock(resolveuuid(blobmsg_get_string(cur))))
  962. return true;
  963. }
  964. return false;
  965. }
  966. static void block_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  967. {
  968. blockinfo = blob_memdup(blobmsg_data(msg));
  969. }
  970. static void fstab_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  971. {
  972. fstabinfo = blob_memdup(blobmsg_data(msg));
  973. }
  974. static int uxc_boot(void)
  975. {
  976. struct blob_attr *cur, *tb[__CONF_MAX];
  977. struct runtime_state *rsstate = NULL;
  978. struct settings *usettings = NULL;
  979. static struct blob_buf req;
  980. int rem, ret = 0;
  981. char *name;
  982. unsigned int id;
  983. bool autostart;
  984. ret = ubus_lookup_id(ctx, "block", &id);
  985. if (ret)
  986. return -ENOENT;
  987. ret = ubus_invoke(ctx, id, "info", NULL, block_cb, NULL, 3000);
  988. if (ret)
  989. return -ENXIO;
  990. ret = ubus_lookup_id(ctx, "uci", &id);
  991. if (ret)
  992. return -ENOENT;
  993. blob_buf_init(&req, 0);
  994. blobmsg_add_string(&req, "config", "fstab");
  995. blobmsg_add_string(&req, "type", "mount");
  996. ret = ubus_invoke(ctx, id, "get", req.head, fstab_cb, NULL, 3000);
  997. if (ret)
  998. return ret;
  999. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  1000. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  1001. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  1002. continue;
  1003. rsstate = avl_find_element(&runtime, blobmsg_get_string(tb[CONF_NAME]), rsstate, avl);
  1004. if (rsstate)
  1005. continue;
  1006. if (tb[CONF_AUTOSTART])
  1007. autostart = blobmsg_get_bool(tb[CONF_AUTOSTART]);
  1008. usettings = avl_find_element(&settings, blobmsg_get_string(tb[CONF_NAME]), usettings, avl);
  1009. if (usettings && (usettings->autostart >= 0))
  1010. autostart = !!(usettings->autostart);
  1011. if (!autostart)
  1012. continue;
  1013. /* make sure all volumes are ready before starting */
  1014. if (tb[CONF_VOLUMES])
  1015. if (checkvolumes(tb[CONF_VOLUMES]))
  1016. continue;
  1017. if (usettings && usettings->volumes)
  1018. if (checkvolumes(usettings->volumes))
  1019. continue;
  1020. name = strdup(blobmsg_get_string(tb[CONF_NAME]));
  1021. if (uxc_exists(name))
  1022. continue;
  1023. if (uxc_create(name, true))
  1024. ++ret;
  1025. free(name);
  1026. }
  1027. return ret;
  1028. }
  1029. static int uxc_delete(char *name, bool force)
  1030. {
  1031. struct blob_attr *cur, *tb[__CONF_MAX];
  1032. struct runtime_state *rsstate = NULL;
  1033. struct settings *usettings = NULL;
  1034. static struct blob_buf req;
  1035. uint32_t id;
  1036. int rem, ret = 0;
  1037. const char *cfname = NULL;
  1038. const char *sfname = NULL;
  1039. struct stat sb;
  1040. blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
  1041. blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
  1042. if (!tb[CONF_NAME] || !tb[CONF_PATH])
  1043. continue;
  1044. if (strcmp(name, blobmsg_get_string(tb[CONF_NAME])))
  1045. continue;
  1046. cfname = blobmsg_name(cur);
  1047. break;
  1048. }
  1049. if (!cfname)
  1050. return -ENOENT;
  1051. rsstate = avl_find_element(&runtime, name, rsstate, avl);
  1052. if (rsstate && rsstate->running) {
  1053. if (force) {
  1054. ret = uxc_kill(name, SIGKILL);
  1055. if (ret)
  1056. goto errout;
  1057. } else {
  1058. ret = -EWOULDBLOCK;
  1059. goto errout;
  1060. }
  1061. }
  1062. if (rsstate) {
  1063. ret = ubus_lookup_id(ctx, "container", &id);
  1064. if (ret)
  1065. goto errout;
  1066. blob_buf_init(&req, 0);
  1067. blobmsg_add_string(&req, "name", rsstate->container_name);
  1068. blobmsg_add_string(&req, "instance", rsstate->instance_name);
  1069. if (ubus_invoke(ctx, id, "delete", req.head, NULL, NULL, 3000)) {
  1070. blob_buf_free(&req);
  1071. ret = -EIO;
  1072. goto errout;
  1073. }
  1074. }
  1075. usettings = avl_find_element(&settings, name, usettings, avl);
  1076. if (usettings)
  1077. sfname = usettings->fname;
  1078. if (sfname) {
  1079. if (stat(sfname, &sb) == -1) {
  1080. ret = -ENOENT;
  1081. goto errout;
  1082. }
  1083. if (unlink(sfname) == -1) {
  1084. ret = -errno;
  1085. goto errout;
  1086. }
  1087. }
  1088. if (stat(cfname, &sb) == -1) {
  1089. ret = -ENOENT;
  1090. goto errout;
  1091. }
  1092. if (unlink(cfname) == -1)
  1093. ret = -errno;
  1094. errout:
  1095. return ret;
  1096. }
  1097. static void reload_conf(void)
  1098. {
  1099. blob_buf_free(&conf);
  1100. conf_load(false);
  1101. settings_free();
  1102. blob_buf_free(&settingsbuf);
  1103. conf_load(true);
  1104. settings_add();
  1105. }
  1106. int main(int argc, char **argv)
  1107. {
  1108. enum uxc_cmd cmd = CMD_UNKNOWN;
  1109. int ret = -EINVAL;
  1110. char *bundle = NULL;
  1111. char *pidfile = NULL;
  1112. char *tmprwsize = NULL;
  1113. char *writepath = NULL;
  1114. char *requiredmounts = NULL;
  1115. signed char autostart = -1;
  1116. bool force = false;
  1117. bool console = false;
  1118. int signal = SIGTERM;
  1119. int c;
  1120. if (argc < 2)
  1121. return usage();
  1122. ctx = ubus_connect(NULL);
  1123. if (!ctx)
  1124. return -ENODEV;
  1125. ret = conf_load(false);
  1126. if (ret < 0)
  1127. goto out;
  1128. ret = conf_load(true);
  1129. if (ret < 0)
  1130. goto conf_out;
  1131. ret = settings_add();
  1132. if (ret < 0)
  1133. goto settings_out;
  1134. ret = runtime_load();
  1135. if (ret)
  1136. goto settings_avl_out;
  1137. while (true) {
  1138. int option_index = 0;
  1139. c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
  1140. if (c == -1)
  1141. break;
  1142. switch (c) {
  1143. case 'a':
  1144. autostart = 1;
  1145. break;
  1146. case 'b':
  1147. bundle = optarg;
  1148. break;
  1149. case 'c':
  1150. console = true;
  1151. break;
  1152. case 'f':
  1153. force = true;
  1154. break;
  1155. case 'j':
  1156. json_output = true;
  1157. break;
  1158. case 'p':
  1159. pidfile = optarg;
  1160. break;
  1161. case 't':
  1162. tmprwsize = optarg;
  1163. break;
  1164. case 'v':
  1165. verbose = true;
  1166. break;
  1167. case 'V':
  1168. printf("uxc %s\n", UXC_VERSION);
  1169. exit(0);
  1170. case 'w':
  1171. writepath = optarg;
  1172. break;
  1173. case 'm':
  1174. requiredmounts = optarg;
  1175. break;
  1176. }
  1177. }
  1178. if (optind == argc)
  1179. goto usage_out;
  1180. if (!strcmp("list", argv[optind]))
  1181. cmd = CMD_LIST;
  1182. else if (!strcmp("attach", argv[optind]))
  1183. cmd = CMD_ATTACH;
  1184. else if (!strcmp("boot", argv[optind]))
  1185. cmd = CMD_BOOT;
  1186. else if(!strcmp("start", argv[optind]))
  1187. cmd = CMD_START;
  1188. else if(!strcmp("state", argv[optind]))
  1189. cmd = CMD_STATE;
  1190. else if(!strcmp("kill", argv[optind]))
  1191. cmd = CMD_KILL;
  1192. else if(!strcmp("enable", argv[optind]))
  1193. cmd = CMD_ENABLE;
  1194. else if(!strcmp("disable", argv[optind]))
  1195. cmd = CMD_DISABLE;
  1196. else if(!strcmp("delete", argv[optind]))
  1197. cmd = CMD_DELETE;
  1198. else if(!strcmp("create", argv[optind]))
  1199. cmd = CMD_CREATE;
  1200. switch (cmd) {
  1201. case CMD_ATTACH:
  1202. if (optind != argc - 2)
  1203. goto usage_out;
  1204. ret = uxc_attach(argv[optind + 1]);
  1205. break;
  1206. case CMD_LIST:
  1207. ret = uxc_list();
  1208. break;
  1209. case CMD_BOOT:
  1210. ret = uxc_boot();
  1211. break;
  1212. case CMD_START:
  1213. if (optind != argc - 2)
  1214. goto usage_out;
  1215. ret = uxc_start(argv[optind + 1], console);
  1216. break;
  1217. case CMD_STATE:
  1218. if (optind != argc - 2)
  1219. goto usage_out;
  1220. ret = uxc_state(argv[optind + 1]);
  1221. break;
  1222. case CMD_KILL:
  1223. if (optind == (argc - 3))
  1224. signal = atoi(argv[optind + 2]);
  1225. else if (optind > argc - 2)
  1226. goto usage_out;
  1227. ret = uxc_kill(argv[optind + 1], signal);
  1228. break;
  1229. case CMD_ENABLE:
  1230. if (optind != argc - 2)
  1231. goto usage_out;
  1232. ret = uxc_set(argv[optind + 1], NULL, 1, NULL, NULL, NULL, NULL);
  1233. break;
  1234. case CMD_DISABLE:
  1235. if (optind != argc - 2)
  1236. goto usage_out;
  1237. ret = uxc_set(argv[optind + 1], NULL, 0, NULL, NULL, NULL, NULL);
  1238. break;
  1239. case CMD_DELETE:
  1240. if (optind != argc - 2)
  1241. goto usage_out;
  1242. ret = uxc_delete(argv[optind + 1], force);
  1243. break;
  1244. case CMD_CREATE:
  1245. if (optind != argc - 2)
  1246. goto usage_out;
  1247. ret = uxc_exists(argv[optind + 1]);
  1248. if (ret)
  1249. goto runtime_out;
  1250. ret = uxc_set(argv[optind + 1], bundle, autostart, pidfile, tmprwsize, writepath, requiredmounts);
  1251. if (ret < 0)
  1252. goto runtime_out;
  1253. if (ret > 0)
  1254. reload_conf();
  1255. ret = uxc_create(argv[optind + 1], false);
  1256. break;
  1257. default:
  1258. goto usage_out;
  1259. }
  1260. goto runtime_out;
  1261. usage_out:
  1262. ret = usage();
  1263. runtime_out:
  1264. runtime_free();
  1265. settings_avl_out:
  1266. settings_free();
  1267. settings_out:
  1268. blob_buf_free(&settingsbuf);
  1269. conf_out:
  1270. blob_buf_free(&conf);
  1271. out:
  1272. ubus_free(ctx);
  1273. if (ret < 0)
  1274. fprintf(stderr, "uxc error: %s\n", strerror(-ret));
  1275. return ret;
  1276. }