Skip to content

Instantly share code, notes, and snippets.

@oskarirauta
Created February 22, 2024 16:07
Show Gist options
  • Save oskarirauta/19b43709147d39d8d2d4231a94bc212a to your computer and use it in GitHub Desktop.
Save oskarirauta/19b43709147d39d8d2d4231a94bc212a to your computer and use it in GitHub Desktop.
add exec function to openwrt's uxc
--- a/uxc.c
+++ b/uxc.c
@@ -27,6 +27,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sched.h>
#include <libubus.h>
#include <libubox/avl-cmp.h>
@@ -71,6 +72,7 @@ struct settings {
enum uxc_cmd {
CMD_ATTACH,
+ CMD_EXEC,
CMD_LIST,
CMD_BOOT,
CMD_START,
@@ -112,6 +114,7 @@ static int usage(void) {
printf("commands:\n");
printf("\tlist [--json]\t\t\t\tlist all configured containers\n");
printf("\tattach <conf>\t\t\t\tattach to container console\n");
+ printf("\texec <conf> [cmd]\t\t\texecute command or shell in container\n");
printf("\tcreate <conf>\t\t\t\t(re-)create <conf>\n");
printf("\t\t[--bundle <path>]\t\t\tOCI bundle at <path>\n");
printf("\t\t[--autostart]\t\t\t\tstart on boot\n");
@@ -150,6 +153,29 @@ static const struct blobmsg_policy conf_
[CONF_VOLUMES] = { .name = "volumes", .type = BLOBMSG_TYPE_ARRAY },
};
+static int open_ns(int pid, char *name) {
+
+ char *path;
+
+ if (asprintf(&path, "/proc/%d/%s", pid, name) == -1 ) {
+ fprintf(stderr, "cannot allocate path /proc/%d/%s\n", pid, name);
+ return -1;
+ }
+
+ struct stat st;
+ if (stat(path, &st) == 0 && S_ISLNK(st.st_mode)) {
+ fprintf(stderr, "file /proc/%d/%s does not exists or is not a symbolic link\n", pid, name);
+ return -1;
+ }
+
+ int fd = open(path, O_RDONLY);
+ if ( fd < 0 ) {
+ fprintf(stderr, "cannot open /proc/%d/%s\n", pid, name);
+ return fd;
+ }
+ return fd;
+}
+
static int conf_load(bool load_settings)
{
int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
@@ -613,6 +639,94 @@ static int uxc_attach(const char *contai
return 0;
}
+static int uxc_exec(const char *container_name, char **args)
+{
+ struct blob_attr *cur, *tb[__CONF_MAX], *ts[__STATE_MAX];
+ struct runtime_state *rsstate = NULL;
+ int rem, container_pid;
+ bool found = false;
+
+ blobmsg_for_each_attr(cur, blob_data(conf.head), rem) {
+ blobmsg_parse(conf_policy, __CONF_MAX, tb, blobmsg_data(cur), blobmsg_len(cur));
+ if (!tb[CONF_NAME] || !tb[CONF_PATH])
+ continue;
+
+ if (strcmp(container_name, blobmsg_get_string(tb[CONF_NAME])))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return -ENOENT;
+
+ rsstate = avl_find_element(&runtime, container_name, rsstate, avl);
+ container_pid = 0;
+
+ if (rsstate && rsstate->ocistate) {
+ blobmsg_parse(state_policy, __STATE_MAX, ts, blobmsg_data(rsstate->ocistate), blobmsg_len(rsstate->ocistate));
+ container_pid = blobmsg_get_u32(ts[STATE_PID]);
+ }
+
+ if (container_pid < 2) {
+ fprintf(stderr, "failed to container pid for %s\n", container_name);
+ return -ENOENT;
+ }
+
+ int ns_ipc = open_ns(container_pid, "ns/ipc");
+ int ns_mnt = open_ns(container_pid, "ns/mnt");
+ int ns_net = open_ns(container_pid, "ns/net");
+ int ns_uts = open_ns(container_pid, "ns/uts");
+ int ns_pid = open_ns(container_pid, "ns/pid");
+ int ns_root = open_ns(container_pid, "root");
+
+ if (ns_ipc == -1 || ns_mnt == -1 || ns_net == -1 || ns_uts == -1 || ns_pid == -1 || ns_root == -1)
+ return -ENXIO;
+
+ if (setns(ns_ipc, 0) == -1) {
+ fprintf(stderr, "failed to enter ipc namespace\n");
+ return -ENXIO;
+ }
+
+ if (setns(ns_mnt, 0) == -1) {
+ fprintf(stderr, "failed to enter mnt namespace\n");
+ return -ENXIO;
+ }
+
+ if (setns(ns_net, 0) == -1) {
+ fprintf(stderr, "failed to enter net namespace\n");
+ return -ENXIO;
+ }
+
+ if (setns(ns_uts, 0) == -1) {
+ fprintf(stderr, "failed to enter uts namespace\n");
+ return -ENXIO;
+ }
+
+ if (setns(ns_pid, 0) == -1) {
+ fprintf(stderr, "failed to enter pid namespace\n");
+ return -ENXIO;
+ }
+
+ if (fchdir(ns_root) == -1) {
+ fprintf(stderr, "failed to change working directory\n");
+ return -ENXIO;
+ }
+
+ if (chroot(".") == -1) {
+ fprintf(stderr, "failed to chroot\n");
+ return -ENXIO;
+ }
+
+ if (execv(args[0], args) == -1) {
+ fprintf(stderr, "failed to execute %s in container %s\n", args[0], container_name);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
static int uxc_state(char *name)
{
struct runtime_state *rsstate = avl_find_element(&runtime, name, rsstate, avl);
@@ -1413,7 +1527,10 @@ int main(int argc, char **argv)
if (ret)
goto settings_avl_out;
- while (true) {
+ if ( argc > 1 && !strcmp("exec", argv[1]))
+ cmd = CMD_EXEC;
+
+ while (cmd != CMD_EXEC) {
int option_index = 0;
c = getopt_long(argc, argv, OPT_ARGS, long_options, &option_index);
if (c == -1)
@@ -1466,13 +1583,15 @@ int main(int argc, char **argv)
}
}
- if (optind == argc)
+ if (optind == argc && cmd != CMD_EXEC)
goto usage_out;
if (!strcmp("list", argv[optind]))
cmd = CMD_LIST;
else if (!strcmp("attach", argv[optind]))
cmd = CMD_ATTACH;
+ else if (!strcmp("exec", argv[optind]))
+ cmd = CMD_EXEC;
else if (!strcmp("boot", argv[optind]))
cmd = CMD_BOOT;
else if(!strcmp("start", argv[optind]))
@@ -1498,6 +1617,24 @@ int main(int argc, char **argv)
ret = uxc_attach(argv[optind + 1]);
break;
+ case CMD_EXEC:
+ if (argc < 3)
+ goto usage_out;
+
+ int i;
+ char *cmd = argc < 4 ? "/bin/sh" : argv[3];
+ int cnt = argc < 5 ? 2 : ( argc - 2 );
+ char **args = (char **)malloc(cnt * sizeof(char*));
+
+ if (argc > 3) {
+ for (i = 0; i < cnt - 1; i++ )
+ args[i] = argv[i + 3];
+ } else args[0] = cmd;
+
+ args[cnt - 1] = NULL;
+ ret = uxc_exec(argv[optind + 1], args);
+ break;
+
case CMD_LIST:
ret = uxc_list();
break;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment