Permalink
Browse files

daemon: command: Copy appliance /etc/resolv.conf in before running co…

…mmands.

When you try to run commands for an Ubuntu guest, they fail because in
Ubuntu /etc/resolv.conf is a symlink to /run/...  and this turns out
to be a dangling symlink when the Ubuntu guest is mounted up under the
appliance.

Therefore even if the network is enabled, any command which tries to
do name resolution will fail.

Ideally we would like to bind-mount the appliance /etc/resolv.conf
into the sysroot.  However this is not possible because mount is buggy
(see comment).  So instead we use a complex hack to achieve the same
ends.

Note this is only done if the network is enabled and if /etc in the
guest actually exists.  The original /etc/resolv.conf is restored
as soon as the command has run.
  • Loading branch information...
rwmjones committed Oct 7, 2013
1 parent b8b1b02 commit 9521422ce60578f7196cc8b7977d998159238c19
Showing with 120 additions and 0 deletions.
  1. +108 −0 daemon/command.c
  2. +2 −0 daemon/daemon.h
  3. +3 −0 daemon/guestfsd.c
  4. +5 −0 daemon/guestfsd.pod
  5. +2 −0 src/launch.c
View
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include "guestfs_protocol.h"
#include "daemon.h"
@@ -34,8 +35,10 @@ GUESTFSD_EXT_CMD(str_umount, umount);
#ifdef HAVE_ATTRIBUTE_CLEANUP
#define CLEANUP_BIND_STATE __attribute__((cleanup(free_bind_state)))
#define CLEANUP_RESOLVER_STATE __attribute__((cleanup(free_resolver_state)))
#else
#define CLEANUP_BIND_STATE
#define CLEANUP_RESOLVER_STATE
#endif
struct bind_state {
@@ -47,6 +50,12 @@ struct bind_state {
bool dev_ok, dev_pts_ok, proc_ok, sys_ok;
};
struct resolver_state {
bool mounted;
char *sysroot_etc_resolv_conf;
char *sysroot_etc_resolv_conf_old;
};
/* While running the command, bind-mount /dev, /proc, /sys
* into the chroot. However we must be careful to unmount them
* afterwards because otherwise they would interfere with
@@ -113,13 +122,107 @@ free_bind_state (struct bind_state *bs)
}
}
/* If the network is enabled, we want <sysroot>/etc/resolv.conf to
* reflect the contents of /etc/resolv.conf so that name resolution
* works. It would be nice to bind-mount the file (single file bind
* mounts are possible). However annoyingly that doesn't work for
* Ubuntu guests where the guest resolv.conf is a dangling symlink,
* and for reasons unknown mount tries to follow the symlink and
* fails (likely a bug). So this is a hack. Note we only invoke
* this if the network is enabled.
*/
static int
set_up_etc_resolv_conf (struct resolver_state *rs)
{
struct stat statbuf;
rs->sysroot_etc_resolv_conf_old = NULL;
rs->sysroot_etc_resolv_conf = sysroot_path ("/etc/resolv.conf");
if (!rs->sysroot_etc_resolv_conf) {
reply_with_perror ("malloc");
goto error;
}
/* If /etc/resolv.conf exists, rename it to the backup file. Note
* that on Ubuntu it's a dangling symlink.
*/
if (lstat (rs->sysroot_etc_resolv_conf, &statbuf) == 0) {
size_t len = sysroot_len + 32;
char buf[len];
/* Make a random name for the backup file. */
snprintf (buf, len, "%s/etc/XXXXXXXX", sysroot);
if (random_name (buf) == -1) {
reply_with_perror ("random_name");
goto error;
}
rs->sysroot_etc_resolv_conf_old = strdup (buf);
if (!rs->sysroot_etc_resolv_conf_old) {
reply_with_perror ("strdup");
goto error;
}
if (verbose)
fprintf (stderr, "renaming %s to %s\n", rs->sysroot_etc_resolv_conf,
rs->sysroot_etc_resolv_conf_old);
if (rename (rs->sysroot_etc_resolv_conf,
rs->sysroot_etc_resolv_conf_old) == -1) {
reply_with_perror ("rename: %s to %s", rs->sysroot_etc_resolv_conf,
rs->sysroot_etc_resolv_conf_old);
goto error;
}
}
/* Now that the guest's <sysroot>/etc/resolv.conf is out the way, we
* can create our own copy of the appliance /etc/resolv.conf.
*/
ignore_value (command (NULL, NULL, "cp", "/etc/resolv.conf",
rs->sysroot_etc_resolv_conf, NULL));
rs->mounted = true;
return 0;
error:
free (rs->sysroot_etc_resolv_conf);
free (rs->sysroot_etc_resolv_conf_old);
return -1;
}
static void
free_resolver_state (struct resolver_state *rs)
{
if (rs->mounted) {
unlink (rs->sysroot_etc_resolv_conf);
if (rs->sysroot_etc_resolv_conf_old) {
if (verbose)
fprintf (stderr, "renaming %s to %s\n", rs->sysroot_etc_resolv_conf_old,
rs->sysroot_etc_resolv_conf);
if (rename (rs->sysroot_etc_resolv_conf_old,
rs->sysroot_etc_resolv_conf) == -1)
perror ("error: could not restore /etc/resolv.conf");
free (rs->sysroot_etc_resolv_conf_old);
}
free (rs->sysroot_etc_resolv_conf);
rs->mounted = false;
}
}
char *
do_command (char *const *argv)
{
char *out;
CLEANUP_FREE char *err = NULL;
int r;
CLEANUP_BIND_STATE struct bind_state bind_state = { .mounted = false };
CLEANUP_RESOLVER_STATE struct resolver_state resolver_state =
{ .mounted = false };
/* We need a root filesystem mounted to do this. */
NEED_ROOT (, return NULL);
@@ -135,12 +238,17 @@ do_command (char *const *argv)
if (bind_mount (&bind_state) == -1)
return NULL;
if (enable_network) {
if (set_up_etc_resolv_conf (&resolver_state) == -1)
return NULL;
}
CHROOT_IN;
r = commandv (&out, &err, (const char * const *) argv);
CHROOT_OUT;
free_bind_state (&bind_state);
free_resolver_state (&resolver_state);
if (r == -1) {
reply_with_error ("%s", err);
View
@@ -43,6 +43,8 @@ typedef struct {
/*-- in guestfsd.c --*/
extern int verbose;
extern int enable_network;
extern int autosync_umount;
extern const char *sysroot;
View
@@ -78,6 +78,7 @@ static char *read_cmdline (void);
static dev_t root_device = 0;
int verbose = 0;
int enable_network = 0;
static void makeraw (const char *channel, int fd);
static int print_shell_quote (FILE *stream, const struct printf_info *info, const void *const *args);
@@ -222,6 +223,8 @@ main (int argc, char *argv[])
printf ("could not read linux command line\n");
}
enable_network = cmdline && strstr (cmdline, "guestfs_network=1") != NULL;
#ifndef WIN32
/* Make sure SIGPIPE doesn't kill us. */
struct sigaction sa;
View
@@ -105,6 +105,11 @@ default (which is C</dev/virtio-ports/org.libguestfs.channel.0>).
This is used by the User-Mode Linux backend to use a regular emulated
serial port instead of virtio-serial.
=item B<guestfs_network=1>
This is set if the appliance network is enabled (see
C<guestfs_set_network>).
=back
=back
View
@@ -362,6 +362,7 @@ guestfs___appliance_command_line (guestfs_h *g, const char *appliance_dev,
"%s" /* root=appliance_dev */
" %s" /* selinux */
"%s" /* verbose */
"%s" /* network */
" TERM=%s" /* TERM environment variable */
"%s%s", /* append */
#ifdef __arm__
@@ -371,6 +372,7 @@ guestfs___appliance_command_line (guestfs_h *g, const char *appliance_dev,
root,
g->selinux ? "selinux=1 enforcing=0" : "selinux=0",
g->verbose ? " guestfs_verbose=1" : "",
g->enable_network ? " guestfs_network=1" : "",
term ? term : "linux",
g->append ? " " : "", g->append ? g->append : "");

0 comments on commit 9521422

Please sign in to comment.