xenconsole: Ensure exclusive access to console using locks
authorMartin Lucina <martin@lucina.net>
Fri, 24 Jul 2015 15:29:41 +0000 (17:29 +0200)
committerIan Campbell <ian.campbell@citrix.com>
Mon, 27 Jul 2015 13:54:55 +0000 (14:54 +0100)
If more than one instance of xenconsole is run against the same DOMID
then each instance will only get some data. This change ensures
exclusive access to the console by obtaining an exclusive lock on
<XEN_LOCK_DIR>/xenconsole.<DOMID>.

The locking strategy used is based on
tools/libxl/libxl_internal.c:libxl__lock_domain_userdata().

Signed-off-by: Martin Lucina <martin@lucina.net>
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Cc: Ian Campbell <ian.campbell@citrix.com>
Cc: Wei Liu <wei.liu2@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
.gitignore
tools/console/Makefile
tools/console/client/main.c

index f6ddb00ef0241c1427c459861cde037ff9803131..bccbd120169d2103e914125a6a3e7a39308d8c41 100644 (file)
@@ -100,6 +100,7 @@ tools/blktap2/vhd/vhd-update
 tools/blktap2/vhd/vhd-util
 tools/console/xenconsole
 tools/console/xenconsoled
+tools/console/client/_paths.h
 tools/debugger/gdb/gdb-6.2.1-linux-i386-xen/*
 tools/debugger/gdb/gdb-6.2.1/*
 tools/debugger/gdb/gdb-6.2.1.tar.bz2
index 71f80880b106ce8ac39aa6a187e26dffcdb89939..77e8f299c05cba6582b847477adadc3ffff115d0 100644 (file)
@@ -21,6 +21,7 @@ all: $(BIN)
 clean:
        $(RM) *.a *.so *.o *.rpm $(BIN) $(DEPS)
        $(RM) client/*.o daemon/*.o
+       $(RM) client/_paths.h
 
 .PHONY: distclean
 distclean: clean
@@ -28,9 +29,12 @@ distclean: clean
 xenconsoled: $(patsubst %.c,%.o,$(wildcard daemon/*.c))
        $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(LDLIBS_xenconsoled) $(APPEND_LDFLAGS)
 
-xenconsole: $(patsubst %.c,%.o,$(wildcard client/*.c))
+xenconsole: client/_paths.h $(patsubst %.c,%.o,$(wildcard client/*.c))
        $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(LDLIBS_xenconsole) $(APPEND_LDFLAGS)
 
+genpath-target = $(call buildmakevars2header,client/_paths.h)
+$(eval $(genpath-target))
+
 .PHONY: install
 install: $(BIN)
        $(INSTALL_DIR) $(DESTDIR)/$(sbindir)
index 753b3aaab1bbfcac714550283867a756e9bd7c03..ff9ac7d1045194884d164ea12a90a72394ec8926 100644 (file)
@@ -18,7 +18,9 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 \*/
 
+#include <sys/file.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <stdio.h>
 
 #include <xenstore.h>
 #include "xenctrl.h"
+#include "_paths.h"
 
 #define ESCAPE_CHARACTER 0x1d
 
 static volatile sig_atomic_t received_signal = 0;
+static char lockfile[sizeof (XEN_LOCK_DIR "/xenconsole.") + 8] = { 0 };
+static int lockfd = -1;
 
 static void sighandler(int signum)
 {
@@ -267,6 +272,53 @@ static void restore_term_stdin(void)
        restore_term(STDIN_FILENO, &stdin_old_attr);
 }
 
+/* The following locking strategy is based on that from
+ * libxl__domain_userdata_lock(), with the difference that we want to fail if we
+ * cannot acquire the lock rather than wait indefinitely.
+ */
+static void console_lock(int domid)
+{
+       struct stat stab, fstab;
+       int fd;
+
+       snprintf(lockfile, sizeof lockfile, "%s%d", XEN_LOCK_DIR "/xenconsole.", domid);
+
+       while (true) {
+               fd = open(lockfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+               if (fd < 0)
+                       err(errno, "Could not open %s", lockfile);
+
+               while (flock(fd, LOCK_EX | LOCK_NB)) {
+                       if (errno == EINTR)
+                               continue;
+                       else
+                               err(errno, "Could not lock %s", lockfile);
+               }
+               if (fstat(fd, &fstab))
+                       err(errno, "Could not fstat %s", lockfile);
+               if (stat(lockfile, &stab)) {
+                       if (errno != ENOENT)
+                               err(errno, "Could not stat %s", lockfile);
+               } else {
+                       if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
+                               break;
+               }
+
+               close(fd);
+       }
+
+       lockfd = fd;
+       return;
+}
+
+static void console_unlock(void)
+{
+       if (lockfile[0] && lockfd != -1) {
+               unlink(lockfile);
+               close(lockfd);
+       }
+}
+
 int main(int argc, char **argv)
 {
        struct termios attr;
@@ -382,6 +434,9 @@ int main(int argc, char **argv)
                exit(EINVAL);
        }
 
+       console_lock(domid);
+       atexit(console_unlock);
+
        /* Set a watch on this domain's console pty */
        if (!xs_watch(xs, path, ""))
                err(errno, "Can't set watch for console pty");