conmon: Use conmon for exec'ing a command

Some OCI container runtimes (in particular the hypervisor
based ones) will typically create a shim process between
the hypervisor and the runtime caller, in order to not
rely on the hypervisor process for e.g. forwarding the
output streams or getting a command exit code.

With these runtimes we need to monitor a different process
than the runtime one when executing a command inside a
running container. The natural place to do so is conmon
and thus we add a new option to conmon for calling the
runtime exec command, monitor the PID and then return the
running command exit code through the sync pipe to the
parent.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2017-01-10 23:08:54 +01:00
parent 468746aa28
commit d60d0ac0c3
No known key found for this signature in database
GPG key ID: 8A803CDD4F566C4A

View file

@ -76,6 +76,7 @@ static char *runtime_path = NULL;
static char *bundle_path = NULL; static char *bundle_path = NULL;
static char *pid_file = NULL; static char *pid_file = NULL;
static bool systemd_cgroup = false; static bool systemd_cgroup = false;
static bool exec = false;
static GOptionEntry entries[] = static GOptionEntry entries[] =
{ {
{ "terminal", 't', 0, G_OPTION_ARG_NONE, &terminal, "Terminal", NULL }, { "terminal", 't', 0, G_OPTION_ARG_NONE, &terminal, "Terminal", NULL },
@ -84,12 +85,13 @@ static GOptionEntry entries[] =
{ "bundle", 'b', 0, G_OPTION_ARG_STRING, &bundle_path, "Bundle path", NULL }, { "bundle", 'b', 0, G_OPTION_ARG_STRING, &bundle_path, "Bundle path", NULL },
{ "pidfile", 'p', 0, G_OPTION_ARG_STRING, &pid_file, "PID file", NULL }, { "pidfile", 'p', 0, G_OPTION_ARG_STRING, &pid_file, "PID file", NULL },
{ "systemd-cgroup", 's', 0, G_OPTION_ARG_NONE, &systemd_cgroup, "Enable systemd cgroup manager", NULL }, { "systemd-cgroup", 's', 0, G_OPTION_ARG_NONE, &systemd_cgroup, "Enable systemd cgroup manager", NULL },
{ "exec", 'e', 0, G_OPTION_ARG_NONE, &exec, "Exec a command in a running container", NULL },
{ NULL } { NULL }
}; };
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret; int ret, runtime_status;
char cwd[PATH_MAX]; char cwd[PATH_MAX];
char default_pid_file[PATH_MAX]; char default_pid_file[PATH_MAX];
GError *err = NULL; GError *err = NULL;
@ -105,7 +107,7 @@ int main(int argc, char *argv[])
struct termios t; struct termios t;
struct epoll_event ev; struct epoll_event ev;
struct epoll_event evlist[MAX_EVENTS]; struct epoll_event evlist[MAX_EVENTS];
int child_pipe = -1; int sync_pipe_fd = -1;
char *sync_pipe, *endptr; char *sync_pipe, *endptr;
int len; int len;
GError *error = NULL; GError *error = NULL;
@ -126,7 +128,7 @@ int main(int argc, char *argv[])
if (runtime_path == NULL) if (runtime_path == NULL)
nexit("Runtime path not provided. Use --runtime"); nexit("Runtime path not provided. Use --runtime");
if (bundle_path == NULL) { if (bundle_path == NULL && !exec) {
if (getcwd(cwd, sizeof(cwd)) == NULL) { if (getcwd(cwd, sizeof(cwd)) == NULL) {
nexit("Failed to get working directory"); nexit("Failed to get working directory");
} }
@ -147,7 +149,7 @@ int main(int argc, char *argv[])
sync_pipe = getenv("_OCI_SYNCPIPE"); sync_pipe = getenv("_OCI_SYNCPIPE");
if (sync_pipe) { if (sync_pipe) {
errno = 0; errno = 0;
child_pipe = strtol(sync_pipe, &endptr, 10); sync_pipe_fd = strtol(sync_pipe, &endptr, 10);
if (errno != 0 || *endptr != '\0') if (errno != 0 || *endptr != '\0')
pexit("unable to parse _OCI_SYNCPIPE"); pexit("unable to parse _OCI_SYNCPIPE");
} }
@ -184,8 +186,9 @@ int main(int argc, char *argv[])
} }
/* Create the container */
cmd = g_string_new(runtime_path); cmd = g_string_new(runtime_path);
if (!exec) {
/* Create the container */
if (systemd_cgroup) { if (systemd_cgroup) {
g_string_append_printf(cmd, " --systemd-cgroup"); g_string_append_printf(cmd, " --systemd-cgroup");
} }
@ -194,8 +197,25 @@ int main(int argc, char *argv[])
if (terminal) { if (terminal) {
g_string_append_printf(cmd, " --console %s", slname); g_string_append_printf(cmd, " --console %s", slname);
} }
ret = system(cmd->str); } else {
if (ret != 0) { int i;
/* Exec the command */
if (terminal) {
g_string_append_printf(cmd, " exec -d --pid-file %s --console %s %s",
pid_file, slname, cid);
} else {
g_string_append_printf(cmd, " exec -d --pid-file %s %s",
pid_file, cid);
}
for (i = 1; i < argc; i++) {
g_string_append_printf(cmd, " %s", argv[i]);
}
}
runtime_status = system(cmd->str);
if (runtime_status != 0) {
nexit("Failed to create container"); nexit("Failed to create container");
} }
@ -211,9 +231,9 @@ int main(int argc, char *argv[])
printf("container PID: %d\n", cpid); printf("container PID: %d\n", cpid);
/* Send the container pid back to parent */ /* Send the container pid back to parent */
if (child_pipe > 0) { if (sync_pipe_fd > 0 && !exec) {
len = snprintf(buf, BUF_SIZE, "{\"pid\": %d}\n", cpid); len = snprintf(buf, BUF_SIZE, "{\"pid\": %d}\n", cpid);
if (len < 0 || write(child_pipe, buf, len) != len) { if (len < 0 || write(sync_pipe_fd, buf, len) != len) {
pexit("unable to send container pid to parent"); pexit("unable to send container pid to parent");
} }
} }
@ -307,6 +327,7 @@ int main(int argc, char *argv[])
printf("PID %d exited with status %d\n", pid, exit_status); printf("PID %d exited with status %d\n", pid, exit_status);
if (pid == cpid) { if (pid == cpid) {
if (!exec) {
_cleanup_free_ char *status_str = NULL; _cleanup_free_ char *status_str = NULL;
ret = asprintf(&status_str, "%d", exit_status); ret = asprintf(&status_str, "%d", exit_status);
if (ret < 0) { if (ret < 0) {
@ -321,9 +342,33 @@ int main(int argc, char *argv[])
g_error_free(err); g_error_free(err);
exit(1); exit(1);
} }
} else {
/* Send the command exec exit code back to the parent */
if (sync_pipe_fd > 0) {
len = snprintf(buf, BUF_SIZE, "{\"exit_code\": %d}\n", exit_status);
if (len < 0 || write(sync_pipe_fd, buf, len) != len) {
pexit("unable to send exit status");
exit(1);
}
}
}
break; break;
} }
} }
if (exec && pid < 0 && errno == ECHILD && sync_pipe_fd > 0) {
/*
* waitpid failed and set errno to ECHILD:
* The runtime exec call did not create any child
* process and we can send the system() exit code
* to the parent.
*/
len = snprintf(buf, BUF_SIZE, "{\"exit_code\": %d}\n", WEXITSTATUS(runtime_status));
if (len < 0 || write(sync_pipe_fd, buf, len) != len) {
pexit("unable to send exit status");
exit(1);
}
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }