2013-08-08 15:36:31 +00:00
|
|
|
/*
|
|
|
|
* virt-login-shell.c: a shell to connect to a container
|
|
|
|
*
|
2013-12-23 17:15:48 +00:00
|
|
|
* Copyright (C) 2013-2014 Red Hat, Inc.
|
2013-08-08 15:36:31 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Daniel Walsh <dwalsh@redhat.com>
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fnmatch.h>
|
2013-12-24 04:07:01 +00:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2015-12-20 07:27:21 +00:00
|
|
|
#include <unistd.h>
|
2013-08-08 15:36:31 +00:00
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "virerror.h"
|
|
|
|
#include "virconf.h"
|
|
|
|
#include "virutil.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virprocess.h"
|
|
|
|
#include "configmake.h"
|
|
|
|
#include "virstring.h"
|
|
|
|
#include "viralloc.h"
|
|
|
|
#include "vircommand.h"
|
2016-04-12 22:29:52 +00:00
|
|
|
#include "virgettext.h"
|
2013-08-08 15:36:31 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
static const char *conf_file = SYSCONFDIR "/libvirt/virt-login-shell.conf";
|
|
|
|
|
|
|
|
static int virLoginShellAllowedUser(virConfPtr conf,
|
|
|
|
const char *name,
|
2016-06-17 12:50:44 +00:00
|
|
|
gid_t *groups,
|
|
|
|
size_t ngroups)
|
2013-08-08 15:36:31 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
char *gname = NULL;
|
2016-07-08 12:52:42 +00:00
|
|
|
char **users = NULL, **entries;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
if (virConfGetValueStringList(conf, "allowed_users", false, &users) < 0)
|
|
|
|
goto cleanup;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
|
|
|
|
for (entries = users; *entries; entries++) {
|
|
|
|
char *entry = *entries;
|
|
|
|
/*
|
|
|
|
If string begins with a % this indicates a linux group.
|
|
|
|
Check to see if the user is in the Linux Group.
|
|
|
|
*/
|
|
|
|
if (entry[0] == '%') {
|
|
|
|
entry++;
|
|
|
|
if (!*entry)
|
|
|
|
continue;
|
|
|
|
for (i = 0; i < ngroups; i++) {
|
|
|
|
if (!(gname = virGetGroupName(groups[i])))
|
2013-08-08 15:36:31 +00:00
|
|
|
continue;
|
2016-07-08 12:52:42 +00:00
|
|
|
if (fnmatch(entry, gname, 0) == 0) {
|
2013-08-08 15:36:31 +00:00
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-07-08 12:52:42 +00:00
|
|
|
VIR_FREE(gname);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (fnmatch(entry, name, 0) == 0) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-13 23:28:06 +00:00
|
|
|
virReportSystemError(EPERM,
|
|
|
|
_("%s not matched against 'allowed_users' in %s"),
|
|
|
|
name, conf_file);
|
2014-03-25 06:53:59 +00:00
|
|
|
cleanup:
|
2013-08-08 15:36:31 +00:00
|
|
|
VIR_FREE(gname);
|
2016-07-08 12:52:42 +00:00
|
|
|
virStringFreeList(users);
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-12 16:01:39 +00:00
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
static int virLoginShellGetShellArgv(virConfPtr conf,
|
2016-07-08 12:52:42 +00:00
|
|
|
char ***shargv,
|
|
|
|
size_t *shargvlen)
|
2013-08-08 15:36:31 +00:00
|
|
|
{
|
2016-07-18 18:07:42 +00:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((rv = virConfGetValueStringList(conf, "shell", true, shargv)) < 0)
|
2016-07-08 12:52:42 +00:00
|
|
|
return -1;
|
2016-04-12 14:18:04 +00:00
|
|
|
|
2016-07-18 18:07:42 +00:00
|
|
|
if (rv == 0) {
|
2016-07-08 12:52:42 +00:00
|
|
|
if (VIR_ALLOC_N(*shargv, 2) < 0)
|
|
|
|
return -1;
|
|
|
|
if (VIR_STRDUP((*shargv)[0], "/bin/sh") < 0) {
|
|
|
|
VIR_FREE(*shargv);
|
|
|
|
return -1;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
2016-07-08 12:52:42 +00:00
|
|
|
*shargvlen = 1;
|
|
|
|
} else {
|
|
|
|
*shargvlen = virStringListLength((const char *const *)shargv);
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
2016-04-12 14:18:04 +00:00
|
|
|
return 0;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *progname;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
2013-08-13 11:13:33 +00:00
|
|
|
fprintf(stdout,
|
|
|
|
_("\n"
|
|
|
|
"Usage:\n"
|
2013-12-24 04:26:27 +00:00
|
|
|
" %s [option]\n\n"
|
2013-08-13 11:13:33 +00:00
|
|
|
"Options:\n"
|
2013-12-24 04:26:27 +00:00
|
|
|
" -h | --help Display program help\n"
|
|
|
|
" -V | --version Display program version\n"
|
2016-04-12 14:18:04 +00:00
|
|
|
" -c CMD Run CMD via shell\n"
|
2013-08-13 11:13:33 +00:00
|
|
|
"\n"
|
|
|
|
"libvirt login shell\n"),
|
|
|
|
progname);
|
2013-08-08 15:36:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-13 11:13:33 +00:00
|
|
|
/* Display version information. */
|
|
|
|
static void
|
|
|
|
show_version(void)
|
|
|
|
{
|
|
|
|
printf("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
virConfPtr conf = NULL;
|
|
|
|
const char *login_shell_path = conf_file;
|
2013-12-24 04:07:01 +00:00
|
|
|
pid_t cpid = -1;
|
|
|
|
int ret = EXIT_CANCELED;
|
2013-08-08 15:36:31 +00:00
|
|
|
int status;
|
|
|
|
uid_t uid = getuid();
|
|
|
|
gid_t gid = getgid();
|
|
|
|
char *name = NULL;
|
|
|
|
char **shargv = NULL;
|
2016-04-12 14:18:04 +00:00
|
|
|
size_t shargvlen = 0;
|
2016-04-12 14:45:48 +00:00
|
|
|
char *shcmd = NULL;
|
2013-08-08 15:36:31 +00:00
|
|
|
virSecurityModelPtr secmodel = NULL;
|
|
|
|
virSecurityLabelPtr seclabel = NULL;
|
|
|
|
virDomainPtr dom = NULL;
|
|
|
|
virConnectPtr conn = NULL;
|
|
|
|
char *homedir = NULL;
|
|
|
|
int arg;
|
|
|
|
int longindex = -1;
|
|
|
|
int ngroups;
|
|
|
|
gid_t *groups = NULL;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
ssize_t nfdlist = 0;
|
|
|
|
int *fdlist = NULL;
|
|
|
|
int openmax;
|
|
|
|
size_t i;
|
2016-04-12 14:18:04 +00:00
|
|
|
const char *cmdstr = NULL;
|
2016-04-12 14:45:48 +00:00
|
|
|
char *tmp;
|
2016-04-12 15:52:58 +00:00
|
|
|
char *term = NULL;
|
2016-04-12 15:48:19 +00:00
|
|
|
virErrorPtr saved_err = NULL;
|
2016-04-12 16:01:39 +00:00
|
|
|
bool autoshell = false;
|
2013-08-08 15:36:31 +00:00
|
|
|
|
|
|
|
struct option opt[] = {
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
2013-08-13 11:13:33 +00:00
|
|
|
{"version", optional_argument, NULL, 'V'},
|
2013-08-08 15:36:31 +00:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
if (virInitialize() < 0) {
|
2013-12-24 04:07:01 +00:00
|
|
|
fprintf(stderr, _("Failed to initialize libvirt error handling"));
|
|
|
|
return EXIT_CANCELED;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virSetErrorFunc(NULL, NULL);
|
|
|
|
virSetErrorLogPriorityFunc(NULL);
|
|
|
|
|
|
|
|
progname = argv[0];
|
2016-04-12 22:29:52 +00:00
|
|
|
if (virGettextInitialize() < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
while ((arg = getopt_long(argc, argv, "hVc:", opt, &longindex)) != -1) {
|
2013-08-08 15:36:31 +00:00
|
|
|
switch (arg) {
|
|
|
|
case 'h':
|
|
|
|
usage();
|
|
|
|
exit(EXIT_SUCCESS);
|
2013-08-13 11:13:33 +00:00
|
|
|
|
|
|
|
case 'V':
|
|
|
|
show_version();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
case 'c':
|
|
|
|
cmdstr = optarg;
|
|
|
|
break;
|
|
|
|
|
2013-08-13 11:13:33 +00:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage();
|
2013-12-24 04:07:01 +00:00
|
|
|
exit(EXIT_CANCELED);
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > optind) {
|
|
|
|
virReportSystemError(EINVAL, _("%s takes no options"), progname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uid == 0) {
|
2013-08-13 23:28:06 +00:00
|
|
|
virReportSystemError(EPERM, _("%s must be run by non root users"),
|
|
|
|
progname);
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = virGetUserName(uid);
|
|
|
|
if (!name)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
homedir = virGetUserDirectoryByUID(uid);
|
|
|
|
if (!homedir)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(conf = virConfReadFile(login_shell_path, 0)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((ngroups = virGetGroupList(uid, gid, &groups)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-06-17 12:50:44 +00:00
|
|
|
if (virLoginShellAllowedUser(conf, name, groups, ngroups) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
if (virLoginShellGetShellArgv(conf, &shargv, &shargvlen) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2016-07-08 12:52:42 +00:00
|
|
|
if (virConfGetValueBool(conf, "auto_shell", &autoshell) < 0)
|
2016-04-12 16:01:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
conn = virConnectOpen("lxc:///");
|
|
|
|
if (!conn)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
dom = virDomainLookupByName(conn, name);
|
|
|
|
if (!dom)
|
|
|
|
goto cleanup;
|
|
|
|
|
2014-09-12 12:16:07 +00:00
|
|
|
if (!virDomainIsActive(dom) && virDomainCreate(dom) < 0) {
|
2013-08-08 15:36:31 +00:00
|
|
|
virErrorPtr last_error;
|
|
|
|
last_error = virGetLastError();
|
|
|
|
if (last_error->code != VIR_ERR_OPERATION_INVALID) {
|
2013-08-13 11:13:33 +00:00
|
|
|
virReportSystemError(last_error->code,
|
|
|
|
_("Can't create %s container: %s"),
|
|
|
|
name, last_error->message);
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
openmax = sysconf(_SC_OPEN_MAX);
|
|
|
|
if (openmax < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("sysconf(_SC_OPEN_MAX) failed"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-08-08 15:36:31 +00:00
|
|
|
if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(secmodel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(seclabel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virNodeGetSecurityModel(conn, secmodel) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virDomainGetSecurityLabel(dom, seclabel) < 0)
|
|
|
|
goto cleanup;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
if (virSetUIDGID(0, 0, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virDomainLxcEnterSecurityLabel(secmodel, seclabel, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
2016-04-13 10:00:11 +00:00
|
|
|
if (virDomainLxcEnterCGroup(dom, 0) < 0)
|
|
|
|
goto cleanup;
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
if (nfdlist > 0 &&
|
|
|
|
virDomainLxcEnterNamespace(dom, nfdlist, fdlist, NULL, NULL, 0) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virSetUIDGID(uid, gid, groups, ngroups) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (chdir(homedir) < 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to chdir(%s)"), homedir);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-08-08 15:36:31 +00:00
|
|
|
|
2016-04-12 16:01:39 +00:00
|
|
|
if (autoshell) {
|
|
|
|
tmp = virGetUserShell(uid);
|
|
|
|
if (tmp) {
|
|
|
|
virStringFreeList(shargv);
|
|
|
|
shargvlen = 1;
|
|
|
|
if (VIR_ALLOC_N(shargv[0], shargvlen + 1) < 0) {
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
shargv[0] = tmp;
|
|
|
|
shargv[1] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-12 14:18:04 +00:00
|
|
|
if (cmdstr) {
|
|
|
|
if (VIR_REALLOC_N(shargv, shargvlen + 3) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_STRDUP(shargv[shargvlen++], "-c") < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (VIR_STRDUP(shargv[shargvlen++], cmdstr) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
shargv[shargvlen] = NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-12 14:45:48 +00:00
|
|
|
/* We need to modify the first elementin shargv
|
|
|
|
* so that it has the relative filename and has
|
|
|
|
* a leading '-' to indicate it is a login shell
|
|
|
|
*/
|
|
|
|
shcmd = shargv[0];
|
|
|
|
if (shcmd[0] != '/') {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Shell '%s' should have absolute path"),
|
|
|
|
shcmd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
tmp = strrchr(shcmd, '/');
|
|
|
|
if (VIR_STRDUP(shargv[0], tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
shargv[0][0] = '-';
|
|
|
|
|
2016-04-12 15:52:58 +00:00
|
|
|
/* We're duping the string because the clearenv()
|
|
|
|
* call will shortly release the pointer we get
|
|
|
|
* back from virGetEnvAllowSUID() right here */
|
|
|
|
if (VIR_STRDUP(term, virGetEnvAllowSUID("TERM")) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
/* A fork is required to create new process in correct pid namespace. */
|
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 00:54:33 +00:00
|
|
|
if ((cpid = virFork()) < 0)
|
2013-08-08 15:36:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (cpid == 0) {
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
int tmpfd;
|
2013-10-09 14:14:34 +00:00
|
|
|
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
for (i = 3; i < openmax; i++) {
|
|
|
|
tmpfd = i;
|
2013-12-23 17:15:48 +00:00
|
|
|
VIR_MASS_CLOSE(tmpfd);
|
|
|
|
}
|
2016-04-12 15:52:58 +00:00
|
|
|
|
|
|
|
clearenv();
|
|
|
|
setenv("PATH", "/bin:/usr/bin", 1);
|
|
|
|
setenv("SHELL", shcmd, 1);
|
|
|
|
setenv("USER", name, 1);
|
|
|
|
setenv("LOGNAME", name, 1);
|
|
|
|
setenv("HOME", homedir, 1);
|
|
|
|
if (term)
|
|
|
|
setenv("TERM", term, 1);
|
|
|
|
|
2016-04-12 14:45:48 +00:00
|
|
|
if (execv(shcmd, (char *const*) shargv) < 0) {
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
virReportSystemError(errno, _("Unable to exec shell %s"),
|
2016-04-12 14:45:48 +00:00
|
|
|
shcmd);
|
2013-12-24 04:07:01 +00:00
|
|
|
virDispatchError(NULL);
|
|
|
|
return errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
|
2013-08-08 15:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
|
2013-12-24 04:07:01 +00:00
|
|
|
/* At this point, the parent is now waiting for the child to exit,
|
|
|
|
* but as that may take a long time, we release resources now. */
|
2014-03-25 06:53:59 +00:00
|
|
|
cleanup:
|
2016-04-12 15:48:19 +00:00
|
|
|
saved_err = virSaveLastError();
|
|
|
|
|
2014-03-05 18:55:27 +00:00
|
|
|
if (nfdlist > 0)
|
|
|
|
for (i = 0; i < nfdlist; i++)
|
|
|
|
VIR_FORCE_CLOSE(fdlist[i]);
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
VIR_FREE(fdlist);
|
2013-08-08 15:36:31 +00:00
|
|
|
virConfFree(conf);
|
virt-login-shell: use single instead of double fork
Note that 'virsh lxc-enter-namespace' must double-fork, for two
reasons: some namespaces can only be done from a single thread,
while virsh is multithreaded; and because virsh can be run in
batch mode where we must not corrupt the namespace of that
execution upon return from the subsidiary command.
When virt-login-shell was first written, it blindly copied from
'virsh lxc-enter-namespace', including the double-fork. But
neither of the reasons for double forking apply to
virt-login-shell (we are single-threaded, and we have nothing to
do after the child completes that would require us to preserve a
namespace), so we can simplify life by using a single fork.
In turn, this will make it easier for a future patch to pass the
child's exit status on to the invoking shell.
In flattening to a single fork, note that closing the fds must
be done after fork, because the parent process still needs to
use fds to control the virConnectPtr; meanwhile, chdir can be
done prior to forking (in fact, it's easier to report errors
on anything attempted before forking).
* tools/virt-login-shell.c (main): Single rather than double fork.
(virLoginShellFini): Delete, by inlining actions instead.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-23 16:15:16 +00:00
|
|
|
if (dom)
|
|
|
|
virDomainFree(dom);
|
|
|
|
if (conn)
|
|
|
|
virConnectClose(conn);
|
2013-08-08 15:36:31 +00:00
|
|
|
virStringFreeList(shargv);
|
2016-04-12 14:45:48 +00:00
|
|
|
VIR_FREE(shcmd);
|
2016-04-12 15:52:58 +00:00
|
|
|
VIR_FREE(term);
|
2013-08-08 15:36:31 +00:00
|
|
|
VIR_FREE(name);
|
|
|
|
VIR_FREE(homedir);
|
|
|
|
VIR_FREE(seclabel);
|
|
|
|
VIR_FREE(secmodel);
|
|
|
|
VIR_FREE(groups);
|
2013-12-24 04:07:01 +00:00
|
|
|
|
|
|
|
if (virProcessWait(cpid, &status, true) == 0)
|
|
|
|
virProcessExitWithStatus(status);
|
|
|
|
|
2016-04-12 15:48:19 +00:00
|
|
|
if (saved_err) {
|
|
|
|
virSetError(saved_err);
|
2013-08-08 15:36:31 +00:00
|
|
|
virDispatchError(NULL);
|
2016-04-12 15:48:19 +00:00
|
|
|
}
|
2013-08-08 15:36:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|