File kbd-1.15.2-chvt-userwait.patch of Package kbd

chvt: add --userwait option

From: Daniel Drake <d.drake@mmm.com>

At http://bugs.gentoo.org/159729 we see chvt hanging in some scenario's. As
the solution to this is not immediately obvious, add a --userwait option
which repeatedly tries changing the terminal until the change has taken place.

Index: kbd-1.14.1/man/man1/chvt.1
===================================================================
--- kbd-1.14.1.orig/man/man1/chvt.1
+++ kbd-1.14.1/man/man1/chvt.1
@@ -4,6 +4,9 @@
 chvt \- change foreground virtual terminal
 .SH SYNOPSIS
 .B chvt
+[
+.B --userwait
+]
 .I N
 .SH DESCRIPTION
 The command
@@ -21,3 +24,10 @@ The key combination
 (with
 .I N
 in the range 1-12) usually has a similar effect.
+.LP
+The
+.B --userwait
+option causes the system to loop in userspace waiting for the new terminal
+to become active, as opposed to the kernel-side
+.I VT_WAITACTIVE
+ioctl.
Index: kbd-1.14.1/src/chvt.c
===================================================================
--- kbd-1.14.1.orig/src/chvt.c
+++ kbd-1.14.1/src/chvt.c
@@ -7,13 +7,43 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
 #include "getfd.h"
 #include "nls.h"
 #include "version.h"
 
+#define USER_WAIT_SLEEP_US		100000
+#define USER_WAIT_MAX_ITERATIONS	50
+
+static int fd;
+
+static void chvt(int num)
+{
+    if (ioctl(fd,VT_ACTIVATE,num)) {
+	perror("VT_ACTIVATE");
+	exit(1);
+    }
+}
+
+static int fgconsole(void)
+{
+    struct vt_stat vtstat;
+    if (ioctl(fd, VT_GETSTATE, &vtstat)) {
+	perror("VT_GETSTATE");
+	exit(1);
+    }
+    return vtstat.v_active;
+}
+
 int
 main(int argc, char *argv[]) {
-    int fd, num;
+    int c, num;
+    int user_wait = 0;
+    const struct option long_opts[] = {
+	{ "version",	no_argument, NULL, 'V' },
+	{ "userwait",	no_argument, NULL, 'u' },
+    };
 
     set_progname(argv[0]);
 
@@ -21,22 +51,46 @@ main(int argc, char *argv[]) {
     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
     textdomain(PACKAGE_NAME);
 
-    if (argc == 2 && !strcmp(argv[1], "-V"))
-	print_version_and_exit();
+    while ((c = getopt_long(argc, argv, "Vu", long_opts, NULL)) != -1) {
+	switch (c) {
+	    case 'V':
+		print_version_and_exit();
+	    case 'u':
+		user_wait = 1;
+		break;
+	}
+    }
 
-    if (argc != 2) {
-	fprintf(stderr, _("usage: chvt N\n"));
+    if (optind >= argc) {
+	fprintf(stderr, _("usage: chvt [--userwait] N\n"));
 	exit(1);
     }
+
     fd = getfd(NULL);
-    num = atoi(argv[1]);
-    if (ioctl(fd,VT_ACTIVATE,num)) {
-	perror("chvt: VT_ACTIVATE");
-	exit(1);
-    }
-    if (ioctl(fd,VT_WAITACTIVE,num)) {
-	perror("VT_WAITACTIVE");
-	exit(1);
+    num = atoi(argv[optind++]);
+    chvt(num);
+
+    if (user_wait) {
+    	int active = 0;
+	int i;
+	for (i = 0; i < USER_WAIT_MAX_ITERATIONS; i++) {
+	    if (fgconsole() == num) {
+	    	active = 1;
+	    	break;
+	    }
+
+	    chvt(num);
+	    usleep(USER_WAIT_SLEEP_US);
+	}
+	if (!active) {
+	    fprintf(stderr, _("VT change timed out\n"));
+	    exit(1);
+	}
+    } else {
+	if (ioctl(fd,VT_WAITACTIVE,num)) {
+	    perror("VT_WAITACTIVE");
+	    exit(1);
+	}
     }
     exit(0);
 }