2 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
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
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.
22 #include <libubox/avl-cmp.h>
23 #include <libubox/blobmsg.h>
24 #include <libubox/blobmsg_json.h>
32 #include <sys/types.h>
38 #define UXC_VERSION "0.2"
39 #define OCI_VERSION_STRING "1.0.2"
40 #define UXC_ETC_CONFDIR "/etc/uxc"
41 #define UXC_VOL_CONFDIR "/var/state/uxc"
43 static bool verbose
= false;
44 static char *confdir
= UXC_ETC_CONFDIR
;
46 struct runtime_state
{
54 struct blob_attr
*ocistate
;
70 #define OPT_ARGS "ab:fm:p:t:vVw:"
71 static struct option long_options
[] = {
72 {"autostart", no_argument
, 0, 'a' },
73 {"bundle", required_argument
, 0, 'b' },
74 {"force", no_argument
, 0, 'f' },
75 {"mounts", required_argument
, 0, 'm' },
76 {"pid-file", required_argument
, 0, 'p' },
77 {"temp-overlay-size", required_argument
, 0, 't' },
78 {"write-overlay-path", required_argument
, 0, 'w' },
79 {"verbose", no_argument
, 0, 'v' },
80 {"version", no_argument
, 0, 'V' },
84 AVL_TREE(runtime
, avl_strcmp
, false, NULL
);
85 static struct blob_buf conf
;
86 static struct blob_attr
*blockinfo
;
87 static struct blob_attr
*fstabinfo
;
88 static struct ubus_context
*ctx
;
90 static int usage(void) {
91 printf("syntax: uxc <command> [parameters ...]\n");
92 printf("commands:\n");
93 printf("\tlist\t\t\t\t\t\tlist all configured containers\n");
94 printf("\tcreate <conf>\t\t\t\t\t(re-)create <conf>\n");
95 printf(" [--bundle <path>]\t\t\tOCI bundle at <path>\n");
96 printf(" [--autostart]\t\t\t\tstart on boot\n");
97 printf(" [--temp-overlay-size size]\t\tuse tmpfs overlay with {size}\n");
98 printf(" [--write-overlay-path path]\t\tuse overlay on {path}\n");
99 printf(" [--mounts v1,v2,...,vN]\t\trequire filesystems to be available\n");
100 printf("\tstart <conf>\t\t\t\t\tstart container <conf>\n");
101 printf("\tstate <conf>\t\t\t\t\tget state of container <conf>\n");
102 printf("\tkill <conf> [<signal>]\t\t\t\tsend signal to container <conf>\n");
103 printf("\tenable <conf>\t\t\t\t\tstart container <conf> on boot\n");
104 printf("\tdisable <conf>\t\t\t\t\tdon't start container <conf> on boot\n");
105 printf("\tdelete <conf> [--force]\t\t\t\tdelete <conf>\n");
115 CONF_TEMP_OVERLAY_SIZE
,
116 CONF_WRITE_OVERLAY_PATH
,
121 static const struct blobmsg_policy conf_policy
[__CONF_MAX
] = {
122 [CONF_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
123 [CONF_PATH
] = { .name
= "path", .type
= BLOBMSG_TYPE_STRING
},
124 [CONF_JAIL
] = { .name
= "jail", .type
= BLOBMSG_TYPE_STRING
},
125 [CONF_AUTOSTART
] = { .name
= "autostart", .type
= BLOBMSG_TYPE_BOOL
},
126 [CONF_PIDFILE
] = { .name
= "pidfile", .type
= BLOBMSG_TYPE_STRING
},
127 [CONF_TEMP_OVERLAY_SIZE
] = { .name
= "temp-overlay-size", .type
= BLOBMSG_TYPE_STRING
},
128 [CONF_WRITE_OVERLAY_PATH
] = { .name
= "write-overlay-path", .type
= BLOBMSG_TYPE_STRING
},
129 [CONF_VOLUMES
] = { .name
= "volumes", .type
= BLOBMSG_TYPE_ARRAY
},
132 static int conf_load(void)
134 int gl_flags
= GLOB_NOESCAPE
| GLOB_MARK
;
141 if (!stat(UXC_VOL_CONFDIR
, &sb
)) {
142 if (sb
.st_mode
& S_IFDIR
)
143 confdir
= UXC_VOL_CONFDIR
;
146 if (asprintf(&globstr
, "%s/*.json", confdir
) == -1)
149 blob_buf_init(&conf
, 0);
150 c
= blobmsg_open_table(&conf
, NULL
);
152 res
= glob(globstr
, gl_flags
, NULL
, &gl
);
157 for (j
= 0; j
< gl
.gl_pathc
; j
++) {
158 o
= blobmsg_open_table(&conf
, strdup(gl
.gl_pathv
[j
]));
159 if (!blobmsg_add_json_from_file(&conf
, gl
.gl_pathv
[j
])) {
160 ERROR("uxc: failed to load %s\n", gl
.gl_pathv
[j
]);
163 blobmsg_close_table(&conf
, o
);
165 blobmsg_close_table(&conf
, c
);
176 static const struct blobmsg_policy list_policy
[__LIST_MAX
] = {
177 [LIST_INSTANCES
] = { .name
= "instances", .type
= BLOBMSG_TYPE_TABLE
},
188 static const struct blobmsg_policy instance_policy
[__INSTANCE_MAX
] = {
189 [INSTANCE_RUNNING
] = { .name
= "running", .type
= BLOBMSG_TYPE_BOOL
},
190 [INSTANCE_PID
] = { .name
= "pid", .type
= BLOBMSG_TYPE_INT32
},
191 [INSTANCE_EXITCODE
] = { .name
= "exit_code", .type
= BLOBMSG_TYPE_INT32
},
192 [INSTANCE_JAIL
] = { .name
= "jail", .type
= BLOBMSG_TYPE_TABLE
},
200 static const struct blobmsg_policy jail_policy
[__JAIL_MAX
] = {
201 [JAIL_NAME
] = { .name
= "name", .type
= BLOBMSG_TYPE_STRING
},
204 static struct runtime_state
*
205 runtime_alloc(const char *container_name
)
207 struct runtime_state
*s
;
209 s
= calloc_a(sizeof(*s
), &new_name
, strlen(container_name
) + 1);
210 strcpy(new_name
, container_name
);
211 s
->container_name
= new_name
;
212 s
->avl
.key
= s
->container_name
;
226 static const struct blobmsg_policy state_policy
[__STATE_MAX
] = {
227 [STATE_OCIVERSION
] = { .name
= "ociVersion", .type
= BLOBMSG_TYPE_STRING
},
228 [STATE_ID
] = { .name
= "id", .type
= BLOBMSG_TYPE_STRING
},
229 [STATE_STATUS
] = { .name
= "status", .type
= BLOBMSG_TYPE_STRING
},
230 [STATE_PID
] = { .name
= "pid", .type
= BLOBMSG_TYPE_INT32
},
231 [STATE_BUNDLE
] = { .name
= "bundle", .type
= BLOBMSG_TYPE_STRING
},
232 [STATE_ANNOTATIONS
] = { .name
= "annotations", .type
= BLOBMSG_TYPE_TABLE
},
236 static void ocistate_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
238 struct blob_attr
**ocistate
= (struct blob_attr
**)req
->priv
;
239 struct blob_attr
*tb
[__STATE_MAX
];
241 blobmsg_parse(state_policy
, __STATE_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
243 if (!tb
[STATE_OCIVERSION
] ||
249 *ocistate
= blob_memdup(msg
);
252 static void get_ocistate(struct blob_attr
**ocistate
, const char *name
)
259 if (asprintf(&objname
, "container.%s", name
) == -1)
262 ret
= ubus_lookup_id(ctx
, objname
, &id
);
267 ubus_invoke(ctx
, id
, "state", NULL
, ocistate_cb
, ocistate
, 3000);
270 static void list_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
272 struct blob_attr
*cur
, *curi
, *tl
[__LIST_MAX
], *ti
[__INSTANCE_MAX
], *tj
[__JAIL_MAX
];
274 const char *container_name
, *instance_name
, *jail_name
;
277 struct runtime_state
*rs
;
279 blobmsg_for_each_attr(cur
, msg
, rem
) {
280 container_name
= blobmsg_name(cur
);
281 blobmsg_parse(list_policy
, __LIST_MAX
, tl
, blobmsg_data(cur
), blobmsg_len(cur
));
282 if (!tl
[LIST_INSTANCES
])
285 blobmsg_for_each_attr(curi
, tl
[LIST_INSTANCES
], remi
) {
286 instance_name
= blobmsg_name(curi
);
287 blobmsg_parse(instance_policy
, __INSTANCE_MAX
, ti
, blobmsg_data(curi
), blobmsg_len(curi
));
289 if (!ti
[INSTANCE_JAIL
])
292 blobmsg_parse(jail_policy
, __JAIL_MAX
, tj
, blobmsg_data(ti
[INSTANCE_JAIL
]), blobmsg_len(ti
[INSTANCE_JAIL
]));
296 jail_name
= blobmsg_get_string(tj
[JAIL_NAME
]);
298 running
= ti
[INSTANCE_RUNNING
] && blobmsg_get_bool(ti
[INSTANCE_RUNNING
]);
300 if (ti
[INSTANCE_PID
])
301 pid
= blobmsg_get_u32(ti
[INSTANCE_PID
]);
305 if (ti
[INSTANCE_EXITCODE
])
306 exitcode
= blobmsg_get_u32(ti
[INSTANCE_EXITCODE
]);
310 rs
= runtime_alloc(container_name
);
311 rs
->instance_name
= strdup(instance_name
);
312 rs
->jail_name
= strdup(jail_name
);
313 rs
->runtime_pid
= pid
;
314 rs
->exitcode
= exitcode
;
315 rs
->running
= running
;
316 avl_insert(&runtime
, &rs
->avl
);
323 static int runtime_load(void)
325 struct runtime_state
*item
, *tmp
;
328 avl_init(&runtime
, avl_strcmp
, false, NULL
);
329 if (ubus_lookup_id(ctx
, "container", &id
) ||
330 ubus_invoke(ctx
, id
, "list", NULL
, list_cb
, &runtime
, 3000))
334 avl_for_each_element_safe(&runtime
, item
, avl
, tmp
)
335 get_ocistate(&item
->ocistate
, item
->jail_name
);
340 static void runtime_free(void)
342 struct runtime_state
*item
, *tmp
;
344 avl_for_each_element_safe(&runtime
, item
, avl
, tmp
) {
345 avl_delete(&runtime
, &item
->avl
);
346 free(item
->instance_name
);
347 free(item
->jail_name
);
348 free(item
->ocistate
);
355 static int uxc_state(char *name
)
357 struct runtime_state
*s
= avl_find_element(&runtime
, name
, s
, avl
);
358 struct blob_attr
*ocistate
= NULL
;
359 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
362 char *jail_name
= NULL
;
364 static struct blob_buf buf
;
367 ocistate
= s
->ocistate
;
370 state
= blobmsg_format_json_indent(ocistate
, true, 0);
374 printf("%s\n", state
);
379 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
380 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
381 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
384 if (!strcmp(name
, blobmsg_get_string(tb
[CONF_NAME
]))) {
386 jail_name
= blobmsg_get_string(tb
[CONF_JAIL
]);
390 bundle
= blobmsg_get_string(tb
[CONF_PATH
]);
398 blob_buf_init(&buf
, 0);
399 blobmsg_add_string(&buf
, "ociVersion", OCI_VERSION_STRING
);
400 blobmsg_add_string(&buf
, "id", jail_name
);
401 blobmsg_add_string(&buf
, "status", s
?"stopped":"uninitialized");
402 blobmsg_add_string(&buf
, "bundle", bundle
);
404 printf("%s\n", blobmsg_format_json_indent(buf
.head
, true, 0));
410 static int uxc_list(void)
412 struct blob_attr
*cur
, *tb
[__CONF_MAX
], *ts
[__STATE_MAX
];
414 struct runtime_state
*s
= NULL
;
417 int container_pid
= -1;
420 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
421 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
422 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
425 autostart
= tb
[CONF_AUTOSTART
] && blobmsg_get_bool(tb
[CONF_AUTOSTART
]);
428 name
= blobmsg_get_string(tb
[CONF_NAME
]);
429 s
= avl_find_element(&runtime
, name
, s
, avl
);
431 if (s
&& s
->ocistate
) {
432 blobmsg_parse(state_policy
, __STATE_MAX
, ts
, blobmsg_data(s
->ocistate
), blobmsg_len(s
->ocistate
));
433 ocistatus
= blobmsg_get_string(ts
[STATE_STATUS
]);
434 container_pid
= blobmsg_get_u32(ts
[STATE_PID
]);
437 printf("[%c] %s %s", autostart
?'*':' ', name
, ocistatus
?:(s
&& s
->running
)?"creating":"stopped");
439 if (s
&& !s
->running
&& (s
->exitcode
>= 0))
440 printf(" exitcode: %d (%s)", s
->exitcode
, strerror(s
->exitcode
));
442 if (s
&& s
->running
&& (s
->runtime_pid
>= 0))
443 printf(" runtime pid: %d", s
->runtime_pid
);
445 if (s
&& s
->running
&& (container_pid
>= 0))
446 printf(" container pid: %d", container_pid
);
454 static int uxc_create(char *name
, bool immediately
)
456 static struct blob_buf req
;
457 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
460 struct runtime_state
*s
= NULL
;
461 char *path
= NULL
, *jailname
= NULL
, *pidfile
= NULL
, *tmprwsize
= NULL
, *writepath
= NULL
;
466 s
= avl_find_element(&runtime
, name
, s
, avl
);
468 if (s
&& (s
->running
))
471 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
472 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
473 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
476 if (strcmp(name
, blobmsg_get_string(tb
[CONF_NAME
])))
485 path
= blobmsg_get_string(tb
[CONF_PATH
]);
487 if (tb
[CONF_PIDFILE
])
488 pidfile
= blobmsg_get_string(tb
[CONF_PIDFILE
]);
490 if (tb
[CONF_TEMP_OVERLAY_SIZE
])
491 tmprwsize
= blobmsg_get_string(tb
[CONF_TEMP_OVERLAY_SIZE
]);
493 if (tb
[CONF_WRITE_OVERLAY_PATH
])
494 writepath
= blobmsg_get_string(tb
[CONF_WRITE_OVERLAY_PATH
]);
498 jailname
= blobmsg_get_string(tb
[CONF_JAIL
]);
500 blob_buf_init(&req
, 0);
501 blobmsg_add_string(&req
, "name", name
);
502 ins
= blobmsg_open_table(&req
, "instances");
503 in
= blobmsg_open_table(&req
, name
);
504 blobmsg_add_string(&req
, "bundle", path
);
505 j
= blobmsg_open_table(&req
, "jail");
506 blobmsg_add_string(&req
, "name", jailname
?:name
);
507 blobmsg_add_u8(&req
, "immediately", immediately
);
510 blobmsg_add_string(&req
, "pidfile", pidfile
);
512 blobmsg_close_table(&req
, j
);
515 blobmsg_add_string(&req
, "overlaydir", writepath
);
518 blobmsg_add_string(&req
, "tmpoverlaysize", tmprwsize
);
520 blobmsg_close_table(&req
, in
);
521 blobmsg_close_table(&req
, ins
);
525 tmp
= blobmsg_format_json_indent(req
.head
, true, 1);
529 fprintf(stderr
, "adding container to procd:\n\t%s\n", tmp
);
534 if (ubus_lookup_id(ctx
, "container", &id
) ||
535 ubus_invoke(ctx
, id
, "add", req
.head
, NULL
, NULL
, 3000)) {
546 static int uxc_start(const char *name
)
551 if (asprintf(&objname
, "container.%s", name
) == -1)
554 if (ubus_lookup_id(ctx
, objname
, &id
))
557 return ubus_invoke(ctx
, id
, "start", NULL
, NULL
, NULL
, 3000);
560 static int uxc_kill(char *name
, int signal
)
562 static struct blob_buf req
;
563 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
567 struct runtime_state
*s
= NULL
;
570 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
571 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
572 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
575 if (strcmp(name
, blobmsg_get_string(tb
[CONF_NAME
])))
585 s
= avl_find_element(&runtime
, name
, s
, avl
);
587 if (!s
|| !(s
->running
))
590 blob_buf_init(&req
, 0);
591 blobmsg_add_u32(&req
, "signal", signal
);
592 blobmsg_add_string(&req
, "name", name
);
594 if (asprintf(&objname
, "container.%s", name
) == -1)
597 ret
= ubus_lookup_id(ctx
, objname
, &id
);
602 if (ubus_invoke(ctx
, id
, "kill", req
.head
, NULL
, NULL
, 3000))
609 static int uxc_set(char *name
, char *path
, bool autostart
, bool add
, char *pidfile
, char *_tmprwsize
, char *_writepath
, char *requiredmounts
)
611 static struct blob_buf req
;
612 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
616 char *keeppath
= NULL
;
617 char *tmprwsize
= _tmprwsize
;
618 char *writepath
= _writepath
;
619 char *curvol
, *tmp
, *mnttok
;
624 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
625 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
626 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
629 if (strcmp(name
, blobmsg_get_string(tb
[CONF_NAME
])))
646 if (stat(path
, &sb
) == -1)
649 if ((sb
.st_mode
& S_IFMT
) != S_IFDIR
)
653 ret
= mkdir(confdir
, 0755);
655 if (ret
&& errno
!= EEXIST
)
658 if (asprintf(&fname
, "%s/%s.json", confdir
, name
) == -1)
661 f
= open(fname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
666 keeppath
= blobmsg_get_string(tb
[CONF_PATH
]);
667 if (tb
[CONF_WRITE_OVERLAY_PATH
])
668 writepath
= blobmsg_get_string(tb
[CONF_WRITE_OVERLAY_PATH
]);
670 if (tb
[CONF_TEMP_OVERLAY_SIZE
])
671 tmprwsize
= blobmsg_get_string(tb
[CONF_TEMP_OVERLAY_SIZE
]);
674 blob_buf_init(&req
, 0);
675 blobmsg_add_string(&req
, "name", name
);
676 blobmsg_add_string(&req
, "path", path
?:keeppath
);
677 blobmsg_add_u8(&req
, "autostart", autostart
);
679 blobmsg_add_string(&req
, "pidfile", pidfile
);
682 blobmsg_add_string(&req
, "temp-overlay-size", tmprwsize
);
685 blobmsg_add_string(&req
, "write-overlay-path", writepath
);
687 if (!add
&& tb
[CONF_VOLUMES
])
688 blobmsg_add_blob(&req
, tb
[CONF_VOLUMES
]);
690 if (add
&& requiredmounts
) {
691 mntarr
= blobmsg_open_array(&req
, "volumes");
692 for (mnttok
= requiredmounts
; ; mnttok
= NULL
) {
693 curvol
= strtok_r(mnttok
, ",;", &tmp
);
697 blobmsg_add_string(&req
, NULL
, curvol
);
699 blobmsg_close_array(&req
, mntarr
);
701 tmp
= blobmsg_format_json_indent(req
.head
, true, 0);
703 dprintf(f
, "%s\n", tmp
);
721 static const struct blobmsg_policy block_info_policy
[__BLOCK_INFO_MAX
] = {
722 [BLOCK_INFO_DEVICE
] = { .name
= "device", .type
= BLOBMSG_TYPE_STRING
},
723 [BLOCK_INFO_UUID
] = { .name
= "uuid", .type
= BLOBMSG_TYPE_STRING
},
724 [BLOCK_INFO_TARGET
] = { .name
= "target", .type
= BLOBMSG_TYPE_STRING
},
725 [BLOCK_INFO_TYPE
] = { .name
= "type", .type
= BLOBMSG_TYPE_STRING
},
726 [BLOCK_INFO_MOUNT
] = { .name
= "mount", .type
= BLOBMSG_TYPE_STRING
},
730 /* check if device 'devname' is mounted according to blockd */
731 static int checkblock(const char *uuid
)
733 struct blob_attr
*tb
[__BLOCK_INFO_MAX
];
734 struct blob_attr
*cur
;
737 blobmsg_for_each_attr(cur
, blockinfo
, rem
) {
738 blobmsg_parse(block_info_policy
, __BLOCK_INFO_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
740 if (!tb
[BLOCK_INFO_UUID
] || !tb
[BLOCK_INFO_MOUNT
])
743 if (!strcmp(uuid
, blobmsg_get_string(tb
[BLOCK_INFO_UUID
])))
756 static const struct blobmsg_policy uci_fstab_policy
[__UCI_FSTAB_MAX
] = {
757 [UCI_FSTAB_UUID
] = { .name
= "uuid", .type
= BLOBMSG_TYPE_STRING
},
758 [UCI_FSTAB_ANONYMOUS
] = { .name
= ".anonymous", .type
= BLOBMSG_TYPE_BOOL
},
761 static const char *resolveuuid(const char *volname
)
763 struct blob_attr
*tb
[__UCI_FSTAB_MAX
];
764 struct blob_attr
*cur
;
766 char *tmpvolname
, *replc
;
769 blobmsg_for_each_attr(cur
, fstabinfo
, rem
) {
770 blobmsg_parse(uci_fstab_policy
, __UCI_FSTAB_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
772 if (!tb
[UCI_FSTAB_UUID
])
775 if (tb
[UCI_FSTAB_ANONYMOUS
] && blobmsg_get_bool(tb
[UCI_FSTAB_ANONYMOUS
]))
778 mntname
= blobmsg_name(cur
);
782 tmpvolname
= strdup(volname
);
783 while ((replc
= strchr(tmpvolname
, '-')))
786 res
= strcmp(tmpvolname
, mntname
);
790 return blobmsg_get_string(tb
[UCI_FSTAB_UUID
]);
796 /* check status of each required volume */
797 static int checkvolumes(struct blob_attr
*volumes
)
799 struct blob_attr
*cur
;
802 blobmsg_for_each_attr(cur
, volumes
, rem
) {
803 if (checkblock(resolveuuid(blobmsg_get_string(cur
))))
810 static void block_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
812 blockinfo
= blob_memdup(blobmsg_data(msg
));
815 static void fstab_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
817 fstabinfo
= blob_memdup(blobmsg_data(msg
));
820 static int uxc_boot(void)
822 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
823 struct runtime_state
*s
;
824 static struct blob_buf req
;
829 ret
= ubus_lookup_id(ctx
, "block", &id
);
833 ret
= ubus_invoke(ctx
, id
, "info", NULL
, block_cb
, NULL
, 3000);
837 ret
= ubus_lookup_id(ctx
, "uci", &id
);
841 blob_buf_init(&req
, 0);
842 blobmsg_add_string(&req
, "config", "fstab");
843 blobmsg_add_string(&req
, "type", "mount");
845 ret
= ubus_invoke(ctx
, id
, "get", req
.head
, fstab_cb
, NULL
, 3000);
849 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
850 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
851 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
] || !tb
[CONF_AUTOSTART
] || !blobmsg_get_bool(tb
[CONF_AUTOSTART
]))
854 s
= avl_find_element(&runtime
, blobmsg_get_string(tb
[CONF_NAME
]), s
, avl
);
858 /* make sure all volumes are ready before starting */
859 if (tb
[CONF_VOLUMES
])
860 if (checkvolumes(tb
[CONF_VOLUMES
]))
863 name
= strdup(blobmsg_get_string(tb
[CONF_NAME
]));
864 ret
+= uxc_create(name
, true);
871 static int uxc_delete(char *name
, bool force
)
873 struct blob_attr
*cur
, *tb
[__CONF_MAX
];
874 struct runtime_state
*s
= NULL
;
875 static struct blob_buf req
;
882 blobmsg_for_each_attr(cur
, blob_data(conf
.head
), rem
) {
883 blobmsg_parse(conf_policy
, __CONF_MAX
, tb
, blobmsg_data(cur
), blobmsg_len(cur
));
884 if (!tb
[CONF_NAME
] || !tb
[CONF_PATH
])
887 if (strcmp(name
, blobmsg_get_string(tb
[CONF_NAME
])))
890 fname
= strdup(blobmsg_name(cur
));
901 s
= avl_find_element(&runtime
, name
, s
, avl
);
903 if (s
&& s
->running
) {
905 ret
= uxc_kill(name
, SIGKILL
);
916 ret
= ubus_lookup_id(ctx
, "container", &id
);
920 blob_buf_init(&req
, 0);
921 blobmsg_add_string(&req
, "name", s
->container_name
);
922 blobmsg_add_string(&req
, "instance", s
->instance_name
);
924 if (ubus_invoke(ctx
, id
, "delete", req
.head
, NULL
, NULL
, 3000)) {
931 if (stat(fname
, &sb
) == -1) {
936 if (unlink(fname
) == -1)
944 static void reload_conf(void)
946 blob_buf_free(&conf
);
950 int main(int argc
, char **argv
)
952 enum uxc_cmd cmd
= CMD_UNKNOWN
;
955 char *pidfile
= NULL
;
956 char *tmprwsize
= NULL
;
957 char *writepath
= NULL
;
958 char *requiredmounts
= NULL
;
959 bool autostart
= false;
961 int signal
= SIGTERM
;
967 ctx
= ubus_connect(NULL
);
975 ret
= runtime_load();
980 int option_index
= 0;
981 c
= getopt_long(argc
, argv
, OPT_ARGS
, long_options
, &option_index
);
1011 printf("uxc %s\n", UXC_VERSION
);
1019 requiredmounts
= optarg
;
1027 if (!strcmp("list", argv
[optind
]))
1029 else if (!strcmp("boot", argv
[optind
]))
1031 else if(!strcmp("start", argv
[optind
]))
1033 else if(!strcmp("state", argv
[optind
]))
1035 else if(!strcmp("kill", argv
[optind
]))
1037 else if(!strcmp("enable", argv
[optind
]))
1039 else if(!strcmp("disable", argv
[optind
]))
1041 else if(!strcmp("delete", argv
[optind
]))
1043 else if(!strcmp("create", argv
[optind
]))
1056 if (optind
!= argc
- 2)
1059 ret
= uxc_start(argv
[optind
+ 1]);
1063 if (optind
!= argc
- 2)
1066 ret
= uxc_state(argv
[optind
+ 1]);
1070 if (optind
== (argc
- 3))
1071 signal
= atoi(argv
[optind
+ 2]);
1072 else if (optind
> argc
- 2)
1075 ret
= uxc_kill(argv
[optind
+ 1], signal
);
1079 if (optind
!= argc
- 2)
1082 ret
= uxc_set(argv
[optind
+ 1], NULL
, true, false, NULL
, NULL
, NULL
, NULL
);
1086 if (optind
!= argc
- 2)
1089 ret
= uxc_set(argv
[optind
+ 1], NULL
, false, false, NULL
, NULL
, NULL
, NULL
);
1093 if (optind
!= argc
- 2)
1096 ret
= uxc_delete(argv
[optind
+ 1], force
);
1100 if (optind
!= argc
- 2)
1104 ret
= uxc_set(argv
[optind
+ 1], bundle
, autostart
, true, pidfile
, tmprwsize
, writepath
, requiredmounts
);
1111 ret
= uxc_create(argv
[optind
+ 1], false);
1125 blob_buf_free(&conf
);
1130 fprintf(stderr
, "uxc error: %s\n", strerror(ret
));