[Pkg-sysvinit-devel] Bug#457896: (fwd) Re: Using startpar in makefile mode and failing getpt()

Petter Reinholdtsen pere at hungry.com
Thu Jan 1 11:33:34 UTC 2009


Here is a patch I wrote a year ago to work around the getpt() problem.
It uses a copy of the glibc getpt() function until /dev/pts is
mounted, this working around the fact that the glibc getpt() only
probe once if /dev/pts exist.

----- Forwarded message from Petter Reinholdtsen <pere at hungry.com> -----

Date: Thu, 28 Feb 2008 12:21:16 +0100
From: Petter Reinholdtsen <pere at hungry.com>
To: Stephan Kulow
Cc: "Dr. Werner Fink"
Subject: Re: Using startpar in makefile mode and failing getpt()

[Stephan Kulow]
> /etc/init.d/boot is the script executed by inittab and this one will 
> mount important file systems before calling startpar. boot.localfs
> will remount them later.

Aha.  That explains it.  I try to work around it by replacing getpt().
The version in glibc will only check once if the required files and
partitions are present, and refuse to check again when the environment
change while the process is running.  My replacement do not have that
problem.

I've had to patch startpar a bit to get it working on Debian, and here
is the complete patch, relative to version 0.50.

 - I had to change the path used to find the rc*.d/ directories.
 - I added debug modus to get more output while figuring out what is
   wrong
 - I added the getpt() hack.  Currently protected using an #ifdef to
   be able to test without it.

When I test startpar in makefile mode with debugging enabled and the
getpt() hack disabled, it try to run the same scripts several times.
When processing rc2.d/ (skipped rcS.d/ because of the getpt()
problem), it try to run rc.local three times, and both stop-bootlogd
and sysklogd twice.  Any idea why this happen?  Is the same problem
present on SuSe when printing debug output?

Index: debian/startpar/startpar.c
===================================================================
--- debian/startpar/startpar.c	(revision 1202)
+++ debian/startpar/startpar.c	(working copy)
@@ -31,6 +31,9 @@
 #include <sys/ioctl.h>
 #include <sys/sysinfo.h>
 #include <sys/stat.h>
+#ifdef GETPT_HACK
+#include <sys/vfs.h> 
+#endif /* GETPT_HACK */
 #include <time.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -40,9 +43,13 @@
 #include "makeboot.h"
 #include "proc.h"
 
+/* SUSE and Debian */
+#define INITDDIR "/etc/init.d"
+
 typedef enum _boolean {false, true} boolean;
 extern char *optarg;
 extern int optind;
+int debug;
 
 static long int numcpu = -1;
 static char *myname;
@@ -263,10 +270,54 @@
   return checksystem(par, mode, false);
 }
 
+#ifdef GETPT_HACK
+/*
+ * Based on __posix_openpt() from glibc.  Reimplemented here to work
+ * around the problem with getpt() failing for the entire process life
+ * time if /dev/pts/ is missing the first time it is called but
+ * mounted while the process is running.  BSD style pts is not
+ * supported, but might be copied from glibc too if there is need.
+ */
+/* Constant that identifies the `devfs' filesystem.  */
+#define DEVFS_SUPER_MAGIC       0x1373
+/* Constant that identifies the `devpts' filesystem.  */
+#define DEVPTS_SUPER_MAGIC      0x1cd1
+static int my_getpt(void) {
+  static int is_working;
+  if (is_working)
+    return getpt();
+  int fd = open("/dev/ptmx", O_RDWR|O_NOCTTY);
+  if (fd != -1)
+    {
+      struct statfs fsbuf;
+
+      /* Check that the /dev/pts filesystem is mounted
+	 or if /dev is a devfs filesystem (this implies /dev/pts).  */
+      if ((statfs ("/dev/pts", &fsbuf) == 0
+	      && fsbuf.f_type == DEVPTS_SUPER_MAGIC)
+	  || (statfs ("/dev", &fsbuf) == 0
+	      && fsbuf.f_type == DEVFS_SUPER_MAGIC))
+	{
+	  /* Everything is ok, switch to the getpt() in libc.  */
+	  is_working = 1;
+	  return fd;
+	}
+      
+      /* If /dev/pts is not mounted then the UNIX98 pseudo terminals
+	 are not usable.  */
+      close (fd);
+    }
+  return -1;
+}
+#endif /* GETPT_HACK */
+
 void run(struct prg *p)
 {
   char *m = 0;
 
+  if (debug)
+    fprintf(stderr, "trying to run %s\n", p->name);
+
   p->len = 0;
   p->pid = (pid_t)0;
   p->fd = getpt();
@@ -331,7 +382,7 @@
 
   if (run_mode) {
     char path[128];
-    snprintf(path, sizeof(path), "/etc/init.d/%s", p->name);
+    snprintf(path, sizeof(path), INITDDIR "/%s", p->name);
     execlp(path, path, arg, (char *)0);
   } else if (arg)
     execlp(p->name, p->name, arg, (char *)0);
@@ -366,7 +417,7 @@
       if (run_mode)
 	{
 	  char path[128];
-	  snprintf(path, sizeof(path), "/etc/init.d/%s", prg);
+	  snprintf(path, sizeof(path), INITDDIR "/%s", prg);
 	  execlp(path, path, arg, (char *)0);
 	}
       else if (arg)
@@ -535,10 +586,13 @@
   numcpu = sysconf(_SC_NPROCESSORS_ONLN);
   myname = argv[0];
 
-  while ((c = getopt(argc, argv, "fhp:t:T:a:M:P:R:S:vi:")) != EOF)
+  while ((c = getopt(argc, argv, "dfhp:t:T:a:M:P:R:S:vi:")) != EOF)
     {
       switch(c)
         {
+	case 'd':
+	  debug = 1;
+	  break;
 	case 'p':
 	  par = atoi(optarg);
 	  break;
@@ -623,7 +677,7 @@
 	  fprintf(stderr, "invalid run mode %s\n", run_mode);
 	  exit(1);
 	}
-      snprintf(makefile, sizeof(makefile), "/etc/init.d/.depend.%s", run_mode);
+      snprintf(makefile, sizeof(makefile), INITDDIR "/.depend.%s", run_mode);
       parse_makefile(makefile);
       check_run_files(run_mode, prev_level, run_level);
 
@@ -752,7 +806,21 @@
 #endif
       for (s = 0; s < par; s++)
 	{
+#ifdef GETPT_HACK
+	  static int getpt_working = 0;
+	  /* Check if getpt() is working (yet), to decide if run() or
+	     run_single() should be used */
+	  if (!getpt_working) {
+	    int fd = my_getpt();
+	    if (fd > 0) {
+	      getpt_working = 1;
+	      close(fd);
+	    }
+	  }
+#endif /* GETPT_HACK */
+
 	  p = prgs + s;
+
 	  if (p == interactive_task)
 	    continue;				/* don't handle this here */
 	  if (p->fd || p->pid)
@@ -768,7 +836,11 @@
 		      nodevec[num] = pickup_task();
 		      if (! nodevec[num])
 			continue;
-		      if (nodevec[num]->interactive)
+		      if (nodevec[num]->interactive
+#ifdef GETPT_HACK
+			  || !getpt_working
+#endif /* GETPT_HACK */
+			  )
 			interactive_task = p;
 		      p->name = nodevec[num]->name;
 		    }
Index: debian/startpar/makeboot.c
===================================================================
--- debian/startpar/makeboot.c	(revision 1202)
+++ debian/startpar/makeboot.c	(working copy)
@@ -14,7 +14,15 @@
 #include <limits.h>
 #include "makeboot.h"
 
+#if 0 /* SUSE */
+#  define RCDBASEDIR "/etc/init.d"
+#  define BOOTDIRPREFIX "boot"
+#else /* Debian */
+#  define RCDBASEDIR "/etc"
+#  define BOOTDIRPREFIX "rcS"
+#endif
 
+extern int debug;
 int tree_entries = 0;
 struct makenode *tree_list = NULL;
 
@@ -158,6 +166,9 @@
 	char *s, *strp, *p;
 	struct makenode *node;
 
+	if (debug)
+	  fprintf(stderr, "Loading %s\n", path);
+
 	if ((fp = fopen(path, "r")) == NULL) {
 		fprintf(stderr, "Can't open %s: %s\n", path, strerror(errno));
 		exit(1);
@@ -233,7 +244,9 @@
 	struct makenode *t, *next;
 
 	filter_prefix = prefix;
-	snprintf(path, sizeof(path), "/etc/init.d/%s.d", dir);
+	snprintf(path, sizeof(path), RCDBASEDIR "/%s.d", dir);
+	if (debug)
+	  fprintf(stderr, "Loading %s/\n", path);
 	ndirs = scandir(path, &dirlist, dirfilter, alphasort);
 	/* mark all matching nodes */
 	for (i = 0; i < ndirs; i++) {
@@ -287,7 +300,7 @@
 {
 	char buf[4] = "rc0";
 	if (! strcmp(action, "boot")) {
-		filter_files("boot", 'S', 0);
+		filter_files(BOOTDIRPREFIX, 'S', 0);
 	} else if (! strcmp(action, "start")) {
 		buf[2] = *prev;
 		filter_files(buf, 'K', 1);

----- End forwarded message -----





More information about the Pkg-sysvinit-devel mailing list