Purpose: startpar implementation from SuSE
Author:  Dr. Werner Fink (SuSE)
Status:  Upstream release 0.58
---
diff -urN sysvinit.orig/startpar/COPYING sysvinit/startpar/COPYING
--- sysvinit.orig/startpar/COPYING	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/COPYING	2008-01-07 14:43:31.000000000 +0000
@@ -0,0 +1,341 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the
+    Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+    MA 02110-1301, USA.
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff -urN sysvinit.orig/startpar/makeboot.c sysvinit/startpar/makeboot.c
--- sysvinit.orig/startpar/makeboot.c	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/makeboot.c	2012-01-12 08:30:53.000000000 +0000
@@ -0,0 +1,572 @@
+/*
+ * very very simple makefile parser
+ *
+ * Copyright (c) 2003 SuSE Linux AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *  
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+#ifndef POSIX_FADV_SEQUENTIAL
+#define posix_fadvise(fd, off, len, adv)	(-1)
+#endif
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+static int o_flags = O_RDONLY;
+#endif
+#ifdef USE_BLOGD
+# include <libblogger.h>
+#endif
+#include "makeboot.h"
+
+
+int tree_entries = 0;
+struct makenode *tree_list = NULL;
+
+/*
+ * search for the node with the given name
+ * returns the node pointer or NULL if not found.
+ *
+ * FIXME: we should use hash for the effective search.
+ */
+static struct makenode *lookup_target(const char *name)
+{
+	struct makenode *t;
+
+	for (t = tree_list; t; t = t->next)
+		if (! strcmp(t->name, name))
+			return t;
+	return NULL;
+}
+
+/*
+ * look for the node with the given name.  if not exist,
+ * create a new one and append to the node list.
+ */
+static struct makenode *add_target(const char *name)
+{
+	struct makenode *__restrict node;
+	struct makenode *prev, *t;
+
+	node = lookup_target(name);
+	if (node)
+		return node;
+	if (posix_memalign((void*)&node, sizeof(void*), alignof(struct makenode)+strsize(name)) < 0) {
+		fprintf(stderr, "Can't malloc: %s\n", strerror(errno));
+		exit(1);
+	}
+	memset(node, 0, alignof(struct makenode)+strsize(name));
+	node->name = ((char*)node)+alignof(struct makenode);
+	strcpy(node->name, name);
+
+	/* append to the list in alphabetical order */
+	prev = NULL;
+	for (t = tree_list; t; prev = t, t = t->next)
+		if (strcmp(node->name, t->name) < 0)
+			break;
+	if (prev)
+		prev->next = node;
+	else
+		tree_list = node;
+	node->next = t;
+	tree_entries++;
+	return node;
+}
+
+/*
+ * Set and propagate importance of a node to all depencies of this node
+ */
+static void add_importance(struct makenode *node, int importance)
+{
+	struct makelist *s = node->depend;
+
+	node->importance += importance;
+	for (s = node->depend; s; s = s->next)
+		add_importance(s->node, importance);
+}
+
+/*
+ * create a dependecy/selection node
+ */
+static struct makelist *new_list(struct makenode *node, struct makelist *next)
+{
+	struct makelist *x;
+
+	x = xcalloc(1, sizeof(*x));
+	x->node = node;
+	x->next = next;
+	return x;
+}
+
+/*
+ * check whether the given target would create an infinte loop
+ */
+static int loop;
+static int check_loop(struct makenode *dep, struct makenode *src)
+{
+	struct makelist *s;
+	for (s = dep->depend; s; s = s->next) {
+		if (s->node == src) {
+			fprintf(stderr, "loop exists %s in %s!\n", dep->name, src->name);
+			return 1;
+		}
+		if (loop++ > 99999) {
+			fprintf(stderr, "too many loops! (loop=%d, dep->name=%s, src->name=%s)\n",
+				loop, dep->name, src->name);
+			return 1;
+		}
+		if (check_loop(s->node, src))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * add to the dependecy and selection lists
+ */
+static void add_depend(struct makenode *node, const char *dst)
+{
+	struct makenode *dep;
+
+	dep = add_target(dst);
+	loop = 0;
+	if (check_loop(dep, node))
+		return;
+	dep->select = new_list(node, dep->select);
+	dep->num_sels++;
+	node->depend = new_list(dep, node->depend);
+	node->num_deps++;
+}
+
+/*
+ * mark the selected service as an interactive task
+ * that should run solely
+ */
+static void mark_interactive(const char *name)
+{
+	struct makenode *node = lookup_target(name);
+	if (node)
+		node->interactive = 1;
+}
+
+
+#define DELIMITER	" \t\r\n"
+
+/*
+ * parse (pseudo) makefile
+ *
+ * it may have only the following form:
+ *
+ * TARGETS = xxx ...
+ * INTERACTIVE = yyy ...
+ * aaa:
+ * bbb: xxx ddd ...
+ *
+ * other lines are ignored.
+ */
+void parse_makefile(const char *path)
+{
+	FILE *fp;
+	char buf[LINE_MAX]; /* FIXME: is this enough big? */
+	char *s, *strp, *p;
+	struct makenode *node;
+
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+	int fd;
+
+	if (getuid() == (uid_t)0)
+		o_flags |= O_NOATIME;
+	if ((fd = open(path, o_flags)) < 0) {
+		fprintf(stderr, "Can't open %s: %s\n", path, strerror(errno));
+		exit(1);
+	}
+	(void)posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED);
+	(void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+	(void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE);
+
+	if ((fp = fdopen(fd, "r")) == NULL)
+#else
+	if ((fp = fopen(path, "r")) == NULL)
+#endif
+	{
+		fprintf(stderr, "Can't open %s: %s\n", path, strerror(errno));
+		exit(1);
+	}
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		for (s = buf; *s && isspace(*s); s++)
+			;
+		if (! *s || *s == '#')
+			continue;
+		if (! strncmp(s, "TARGETS =", 9)) {
+			s += 9;
+			strp = s;
+			while ((s = strsep(&strp, DELIMITER))) {
+				if (! *s)
+					continue;
+				add_target(s);
+			}
+		} else if (! strncmp(s, "INTERACTIVE =", 13)) {
+			s += 13;
+			strp = s;
+			while ((s = strsep(&strp, DELIMITER))) {
+				if (! *s)
+					continue;
+				mark_interactive(s);
+			}
+		} else {
+			p = strchr(s, ':');
+			if (! p)
+				continue;
+			*p = 0;
+			node = add_target(s);
+			strp = p + 1;
+			while ((s = strsep(&strp, DELIMITER))) {
+				if (! *s)
+					continue;
+				add_depend(node, s);
+			}
+		}
+	}
+
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+	(void)posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+#endif
+
+	fclose(fp);
+
+	for (node = tree_list; node; node = node->next) {
+		int importance = 0;
+
+		if (! strcmp(node->name, "xdm")
+		    || ! strncmp(node->name, "gdm", 3)
+		    || ! strncmp(node->name, "kdm", 3)
+		    || ! strcmp(node->name, "boot.udev")
+		    || ! strcmp(node->name, "udev"))
+			importance = 100;
+
+		if (! strcmp(node->name, "sshd"))
+			importance = 2000;
+
+		if (! strncmp(node->name, "early", 5))
+			importance = 8000;
+
+		if (importance)
+			add_importance(node, importance);
+	}
+}
+
+/*
+ * filter out the list targets
+ */
+
+static int filter_prefix;
+static int dirfilter(const struct dirent *d)
+{
+	return *d->d_name == filter_prefix &&
+		strlen(d->d_name) >= 4; /* to be sure */
+}
+
+static void filter_files(const char *dir, int prefix, int inverse)
+{
+	char path[64];
+	int i, ndirs;
+	static struct dirent **dirlist;
+	struct makenode *t, *next;
+
+	filter_prefix = prefix;
+#ifdef SUSE	/* SuSE */
+	snprintf(path, sizeof(path), "/etc/init.d/%s.d", dir);
+#else		/* Debian */
+	snprintf(path, sizeof(path), "/etc/%s.d", dir);
+#endif
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+	if ((i = open(path, o_flags|O_DIRECTORY|O_LARGEFILE)) >= 0) {
+		(void)posix_fadvise(i, 0, 0, POSIX_FADV_SEQUENTIAL);
+		(void)posix_fadvise(i, 0, 0, POSIX_FADV_NOREUSE);
+	}
+#endif
+	ndirs = scandir(path, &dirlist, dirfilter, alphasort);
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+	if (i >= 0) {
+		(void)posix_fadvise(i, 0, 0, POSIX_FADV_DONTNEED);
+		close(i);
+	}
+#endif
+	/* mark all matching nodes */
+	if (ndirs >= 0) {
+		for (i = 0; i < ndirs; i++) {
+			t = lookup_target(dirlist[i]->d_name + 3);
+			if (t) {
+				t->status = 1;
+				t->filter_prefix = filter_prefix;
+				if (asprintf(&t->arg0, "%s/%s", path, dirlist[i]->d_name) < 0)
+					t->arg0 = (char*)0;
+			}
+			free(dirlist[i]);
+		}
+		free(dirlist);
+	}
+	/* deselect non-matching nodes */
+	for (t = tree_list; t; t = next) {
+		next = t->next;
+		if ((! t->status && ! inverse) || (t->status && inverse)) {
+			/* remove from the list */
+			struct makelist *x, *nx;
+			struct makenode *p;
+			for (x = t->select; x; x = nx) {
+				nx = x->next;
+				x->node->num_deps--;
+				free(x);
+			}
+			for (x = t->depend; x; x = nx) {
+				nx = x->next;
+				x->node->num_sels--;
+				free(x);
+			}
+			if (t == tree_list)
+				tree_list = next;
+			else {
+				for (p = tree_list; p->next != t; p = p->next)
+					;
+				p->next = next;
+			}
+			/* don't free the node instance itself - it may be selected
+			 * by others
+			 */
+			tree_entries--;
+			continue;
+		}
+		t->status = 0;
+	}
+}
+
+/*
+ * mark the unnecessary services as finished.
+ *
+ * action is either boot, start or stop.
+ * prev and run are the previous and the next runlevel.
+ */
+void check_run_files(const char *action, const char *prev, const char *run)
+{
+	char buf[4] = "rc0";
+	if (! strcmp(action, "boot")) {
+#ifdef SUSE	/* SuSE */
+		filter_files("boot", 'S', 0);
+	} else if (! strcmp(action, "halt")) {
+		filter_files("boot", 'K', 0);
+	} else if (! strcmp(action, "start")) {
+		buf[2] = *prev;
+		filter_files(buf, 'K', 1);
+		buf[2] = *run;
+		filter_files(buf, 'S', 0);
+	} else {
+		buf[2] = *prev;
+		filter_files(buf, 'K', 0);
+		buf[2] = *run;
+		filter_files(buf, 'S', 1);
+#else		/* Debian */
+		filter_files("rcS", 'S', 0);
+	} else if (! strcmp(action, "start")) {
+		buf[2] = *prev;
+		filter_files(buf, 'S', 1);
+		buf[2] = *run;
+		filter_files(buf, 'S', 0);
+	} else {
+		buf[2] = *prev;
+		filter_files(buf, 'K', 1);
+		buf[2] = *run;
+		filter_files(buf, 'K', 0);
+#endif
+	}
+}
+
+
+/*
+ * call blogd
+ */
+#ifndef USE_BLOGD
+# define bootlog(arg...)
+# define closeblog()
+#endif
+
+/*
+ * pick up the next running task
+ * return NULL if not found.
+ */
+struct makenode *pickup_task(void)
+{
+	struct makenode *node, *best = (struct makenode*)0;
+
+	for (node = tree_list; node; node = node->next) {
+		if (node->status != T_READY)
+			continue;
+		if (node->num_deps > 0)
+			continue;
+		if (!best || (node->importance > best->importance))
+			best = node;
+	}
+	if (best) {
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+		char path[128];
+		int fd;
+		snprintf(path, sizeof(path), "/etc/init.d/%s", best->name);
+		if ((fd = open(path, o_flags|O_DIRECT)) >= 0) {
+			(void)posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED);
+			(void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+			(void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE);
+			close(fd);
+		}
+#endif
+		bootlog(B_NOTICE, "service %s %s", best->name, (best->filter_prefix == 'K') ? "stop" : "start");
+		best->status = T_RUNNING;
+	}
+	return best;
+}
+
+/*
+ * finish the running task
+ */
+void finish_task(struct makenode *node)
+{
+	struct makelist *n;
+
+	if (! node)
+		return;
+	for (n = node->select; n; n = n->next)
+		n->node->num_deps--;
+#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
+	{
+		char path[128];
+		int fd;
+		snprintf(path, sizeof(path), "/etc/init.d/%s", node->name);
+		if ((fd = open(path, o_flags|O_DIRECT)) >= 0) {
+			(void)posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+			close(fd);
+		}
+	}
+#endif
+	node->status = T_FINISHED;
+	bootlog(B_NOTICE, "service %s done", node->name);
+}
+
+
+/*
+ * Print out the status that bash can run eval.
+ * The following things will be printed:
+ * failed services, skipped services and the current progress value.
+ */
+void print_run_result(int *resvec, struct makenode **nodevec, const char *action)
+{
+	int i, r, stop = (! strcmp(action, "stop"));
+
+	printf("failed_service=\"");
+	i = r = 0;
+	for (i = 0; i < tree_entries; i++) {
+#if DEBUG
+		if (resvec[i] == 255) {
+			fprintf(stderr, "ERROR: forgotten process??\n");
+			exit(1);
+		}
+#endif
+		if (resvec[i] >= 1 && resvec[i] <= 4) {
+			if (r)
+				printf(" ");
+			printf("%s", nodevec[i]->name);
+			r++;
+		} else if (!stop && resvec[i] == 7) {
+			if (r)
+				printf(" ");
+			printf("%s", nodevec[i]->name);
+			r++;
+		}
+	}
+	printf("\"\n");
+	printf("skipped_service=\"");
+	i = r = 0;
+	for (i = 0; i < tree_entries; i++) {
+		if (resvec[i] == 5 || resvec[i] == 6) {
+			if (r)
+				printf(" ");
+			printf("%s", nodevec[i]->name);
+			r++;
+		}
+	}
+	printf("\"\n");
+}
+
+#if DEBUG
+void dump_status(void)
+{
+	struct makenode *node;
+
+	for (node = tree_list; node; node = node->next)
+		fprintf(stderr, "XXX %s: status = %d, dep = %d, int = %d, imp = %d\n",
+			node->name, node->status, node->num_deps, node->interactive, node->importance);
+}
+#endif
+
+#ifdef TEST
+void *xcalloc(size_t nmemb, size_t size)
+{
+	void *r;
+	if ((r = (void *)calloc(nmemb, size)) == 0) {
+		fprintf(stderr, "calloc: out of memory\n");
+		exit(1);
+	}
+	return r;
+}
+
+int main(int argc, char **argv)
+{
+	struct makenode *nodevec;
+	char makefile[64];
+
+	if (argc != 4) {
+		fprintf(stderr, "usage: makeboot <action> [<prev> <run>]\n");
+		goto out;
+	}
+
+	snprintf(makefile, sizeof(makefile), "depend.%s", argv[1]);
+	parse_makefile(makefile);
+
+	fprintf(stderr, "check_run_files(%s, %s, %s)\n", argv[1], argv[2],
+		argv[3]);
+	check_run_files(argv[1], argv[2], argv[3]);
+out:
+	while ((nodevec = pickup_task())) {
+		fprintf(stdout, "%s (%s)\n", nodevec->name, nodevec->arg0);
+		finish_task(nodevec);
+	}
+
+	return 0;
+}
+#endif
diff -urN sysvinit.orig/startpar/makeboot.h sysvinit/startpar/makeboot.h
--- sysvinit.orig/startpar/makeboot.h	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/makeboot.h	2011-04-19 14:32:34.000000000 +0100
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright (c) 2003 SuSE Linux AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+enum {
+	T_READY, T_RUNNING, T_FINISHED
+};
+
+/* target nodes */
+struct makenode {
+	char *name;
+	char *arg0;
+	int num_deps;
+	struct makelist *depend;
+	int num_sels;
+	struct makelist *select;
+	int status;
+	struct makenode *next;
+	int interactive;
+	int importance;
+	int filter_prefix;
+};
+
+/* dependency and selection list nodes */
+struct makelist {
+	struct makenode *node;
+	struct makelist *next;
+};
+
+extern int tree_entries;
+extern struct makenode *tree_list;
+
+extern void parse_makefile(const char *path);
+extern void check_run_files(const char *action, const char *prev, const char *run);
+extern struct makenode *pickup_task(void);
+extern void finish_task(struct makenode *n);
+extern void *xcalloc(size_t nmemb, size_t size);
+extern void print_run_result(int *resvec, struct makenode **nodevec, const char *action);
+
+#define alignof(type)		((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))
+#define strsize(string)		((strlen(string)+1)*sizeof(char))
diff -urN sysvinit.orig/startpar/Makefile sysvinit/startpar/Makefile
--- sysvinit.orig/startpar/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/Makefile	2011-04-19 17:07:52.000000000 +0100
@@ -0,0 +1,50 @@
+VERSION = 0.58
+ISSUSE	= -DSUSE
+
+INSTALL		= install -m 755
+INSTALL_DATA	= install -m 644
+DESTDIR		=
+sbindir		= /sbin
+mandir		= /usr/share/man
+man8dir		= $(mandir)/man8
+
+SRCS		= startpar.c makeboot.c proc.c
+HDRS		= makeboot.h proc.h
+REST		= COPYING Makefile startpar.8
+OBJS		= $(SRCS:.c=.o)
+
+ifneq ($(INC),)
+    LIBS	+= -lblogger
+    COPTS	+= -DUSE_BLOGD
+endif
+
+CC     = gcc
+CFLAGS = $(RPM_OPT_FLAGS) $(COPTS) -D_GNU_SOURCE $(INC) -Wall -W -pipe
+
+ifeq ($(MAKECMDGOALS),makeboot)
+CFLAGS += -DTEST
+endif
+
+.c.o:
+	$(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" $(ISSUSE) -c $<
+
+startpar: $(OBJS)
+	$(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" $(ISSUSE) -o $@ $(OBJS) $(LIBS)
+
+makeboot: makeboot.c
+
+install: startpar
+	$(INSTALL) -d $(DESTDIR)$(sbindir) $(DESTDIR)$(man8dir)
+	$(INSTALL) startpar $(DESTDIR)$(sbindir)/.
+	$(INSTALL_DATA) startpar.8 $(DESTDIR)$(man8dir)/.
+
+clean:
+	rm -f startpar makeboot $(OBJS)
+
+dest: clean
+	mkdir -p startpar-$(VERSION)
+	for file in $(SRCS) $(HDRS) $(REST) ; do \
+	    cp -p $$file startpar-$(VERSION)/; \
+	done
+	tar -cps -jf startpar-$(VERSION).tar.bz2 startpar-$(VERSION)/*
+	rm -rf startpar-$(VERSION)/
diff -urN sysvinit.orig/startpar/proc.c sysvinit/startpar/proc.c
--- sysvinit.orig/startpar/proc.c	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/proc.c	2012-01-12 08:37:35.000000000 +0000
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2004 SuSE Linux AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *  
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include "proc.h"
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+# ifndef  restrict
+#  define restrict		__restrict__
+# endif
+#endif
+
+#define alignof(type)		((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))
+
+static unsigned long int scan_one(const char* buff, const char *key)
+{
+    const char *b = strstr(buff, key);
+    unsigned long val = 0;
+
+    if (!b) {
+#if DEBUG
+	fprintf(stderr, "ERROR: no hit for %s\n", key);
+#endif
+	return ~0UL;
+    }
+    if (sscanf(b + strlen(key), " %lu", &val) != 1)
+	return 0;
+    return val;
+}
+
+int read_proc(unsigned long int * const prcs_run, unsigned long int * const prcs_blked)
+{
+    char StatBuf[2048], *ptr = &StatBuf[0];
+    unsigned long int running, blocked;
+    ssize_t len;
+    size_t skip;
+    FILE *stat;
+
+    *prcs_run = 0;
+    *prcs_blked = 0;
+
+    if ((stat = fopen("/proc/stat", "r")) == (FILE*)0) {
+	fprintf(stderr, "ERROR: could not open /proc/stat: %s\n", strerror(errno));
+	return 1;
+    }
+
+    len = sizeof(StatBuf);
+    while ((len > 0) && (fgets(ptr, len, stat))) {
+	if (ptr[0] != 'p')
+	    continue;
+	skip = strlen(ptr);
+	len -= skip;
+	ptr += skip;
+    }
+    fclose(stat);
+    
+    /* These fields are not present in /proc/stat for 2.4 kernels or GNU/kFreeBSD */
+    running = scan_one(StatBuf, "procs_running");
+    blocked = scan_one(StatBuf, "procs_blocked");
+
+    if (running == ~0UL || blocked == ~0UL)
+	return 1;
+
+    *prcs_run   = running;
+    *prcs_blked = blocked;
+
+    return 0;
+}
+
+struct console {
+    char * tty;
+    int tlock;
+    struct termios ltio, otio;
+    struct console *restrict next;
+};
+static struct console *restrict consoles;
+static dev_t comparedev;
+static char* scandev(DIR *dir)
+{
+    char *name = (char*)0;
+    struct dirent *dent;
+    int fd;
+
+    fd = dirfd(dir);
+    rewinddir(dir);
+    while ((dent = readdir(dir))) {
+	char path[PATH_MAX];
+	struct stat st;
+	if (fstatat(fd, dent->d_name, &st, 0) < 0)
+	    continue;
+	if (!S_ISCHR(st.st_mode))
+	    continue;
+	if (comparedev != st.st_rdev)
+	    continue;
+	if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path))
+	    continue;
+	name = realpath(path, NULL);
+	break;
+    }
+    return name;
+}
+
+void detect_consoles(void)
+{
+    FILE *fc;
+    if ((fc = fopen("/proc/consoles", "r"))) {
+	char fbuf[16];
+	int maj, min;
+	DIR *dir;
+	dir = opendir("/dev");
+	if (!dir)
+	    goto out;
+	while ((fscanf(fc, "%*s %*s (%[^)]) %d:%d", &fbuf[0], &maj, &min) == 3)) {
+	    struct console *restrict tail;
+	    char * name;
+
+	    if (!strchr(fbuf, 'E'))
+		continue;
+	    comparedev = makedev(maj, min);
+	    name = scandev(dir);
+
+	    if (!name)
+		continue;
+
+	    if (posix_memalign((void*)&tail, sizeof(void*), alignof(typeof(struct console))) != 0)
+		perror("memory allocation");
+
+	    tail->next = (struct console*)0;
+	    tail->tty = name;
+
+	    if (!consoles)
+		consoles = tail;
+	    else
+		consoles->next = tail;
+	}
+	closedir(dir);
+    out:
+	fclose(fc);
+    }
+}
diff -urN sysvinit.orig/startpar/proc.h sysvinit/startpar/proc.h
--- sysvinit.orig/startpar/proc.h	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/proc.h	2010-09-30 13:41:35.000000000 +0100
@@ -0,0 +1,21 @@
+/* Copyright (c) 2004 SuSE Linux AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+extern int read_proc(unsigned long int *prcs_run, unsigned long int *prcs_blked);
+extern void detect_consoles(void);
diff -urN sysvinit.orig/startpar/startpar.8 sysvinit/startpar/startpar.8
--- sysvinit.orig/startpar/startpar.8	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/startpar.8	2010-07-28 13:04:55.000000000 +0100
@@ -0,0 +1,131 @@
+.\"
+.\" SuSE man page for startpar
+.\"
+.\" Copyright (c) 2003 SuSE Linux AG, Nuernberg, Germany.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2, or (at your option)
+.\" any later version.
+.\"
+.\" This program 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 General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program (see the file COPYING); if not, write to the
+.\" Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+.\" MA 02110-1301, USA.
+.\"
+.\" Author: Michael Schroeder <mls@suse.de>
+.\"
+.TH STARTPAR 8 "Jun 2003"
+.SH NAME
+startpar \- start runlevel scripts in parallel
+
+.SH SYNOPSIS
+.B startpar
+.RB [ \-p
+.IR par ]
+.RB [ \-i
+.IR iorate ]
+.RB [ \-t
+.IR timeout ]
+.RB [ \-T
+.IR global_timeout ]
+.RB [ \-a
+.IR arg ]
+.B prg1
+.B prg2
+.B ...
+.br
+.B startpar
+.RB [ \-p
+.IR par ]
+.RB [ \-i
+.IR iorate ]
+.RB [ \-t
+.IR timeout ]
+.RB [ \-T
+.IR global_timeout ]
+.B \-M [
+.IR boot | start | stop ]
+
+.SH DESCRIPTION
+startpar is used to run multiple run\-level scripts in parallel.
+The degree of parallelism on one
+.B CPU
+can be set with the
+.B \-p
+option, the default is full parallelism. An argument to all of
+the scripts can be provided with the
+.B \-a
+option.
+Processes block by pending
+.B I/O
+will weighting by the factor
+.IR 800 .
+To change this factor the option
+.B \-i
+can be used to specify an other value.
+
+The output of each script is buffered and written when the script
+exits, so output lines of different scripts won't mix. You can
+modify this behaviour by setting a timeout.
+
+The timeout set with the
+.B \-t
+option is used as buffer timeout. If the output buffer of a
+script is not empty and the last output was
+.I timeout
+seconds ago, startpar will flush the buffer.
+
+The
+.B \-T
+option timeout works more globally. If no output is printed for
+more than
+.I global_timeout
+seconds, startpar will flush the buffer of the script with
+the oldest output. Afterwards it will only print output of this
+script until it is finished.
+
+The
+.B \-M
+option switches
+.B startpar
+into a
+.BR make (1)
+like behaviour.  This option takes three different arguments:
+.IR boot ", " start ", and " stop
+for reading
+.IR .depend.boot " or " .depend.start " or " .depend.stop
+respectively in the directory
+.IR /etc/init.d/ .
+By scanning the boot and runlevel directories in
+.I /etc/init.d/
+it then executes the appropriate scripts in parallel.
+
+.SH FILES
+.I /etc/init.d/.depend.boot
+.br
+.I /etc/init.d/.depend.start
+.br
+.I /etc/init.d/.depend.stop
+
+.SH SEE ALSO
+.BR init.d (7),
+.BR insserv (8),
+.BR startproc (8).
+
+.SH COPYRIGHT
+2003,2004 SuSE Linux AG, Nuernberg, Germany.
+.br
+2007 SuSE LINUX Products GmbH, Nuernberg, Germany.
+
+.SH AUTHOR
+Michael Schroeder <mls@suse.de>
+.br
+Takashi Iwai <tiwai@suse.de>
+.br
+Werner Fink <werner@suse.de>
diff -urN sysvinit.orig/startpar/startpar.c sysvinit/startpar/startpar.c
--- sysvinit.orig/startpar/startpar.c	1970-01-01 01:00:00.000000000 +0100
+++ sysvinit/startpar/startpar.c	2012-01-12 08:46:28.000000000 +0000
@@ -0,0 +1,1469 @@
+/* Copyright (c) 2003 SuSE Linux AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *  
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ ****************************************************************
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#ifdef SUSE
+# define USE_PRELOAD
+# undef  CHECK_FORDEVPTS
+#else
+# undef  USE_PRELOAD
+# define CHECK_FORDEVPTS
+#endif
+#include <stdio.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/sysinfo.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/signalfd.h>
+#ifdef CHECK_FORDEVPTS
+# include <sys/vfs.h>
+#endif
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef USE_BLOGD
+# include <libblogger.h>
+#else
+# define bootlog(arg...)
+# define closeblog()
+#endif
+#include "makeboot.h"
+#include "proc.h"
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+# ifndef  inline
+#  define inline		__inline__
+# endif
+# ifndef  restrict
+#  define restrict		__restrict__
+# endif
+# ifndef  volatile
+#  define volatile		__volatile__
+# endif
+# ifndef  extension
+#  define extension		__extension__
+# endif
+#endif
+#ifndef  attribute
+# define attribute(attr)	__attribute__(attr)
+#endif
+
+#define timerdiff(n,l) (extension({ (((n).tv_sec-(l).tv_sec)*1000)+(((n).tv_usec-(l).tv_usec)/1000); }))
+
+typedef enum _boolean {false, true} boolean;
+extern char *optarg;
+extern int optind;
+
+static long int numcpu = -1;
+static char *myname;
+static struct termios tio;
+static volatile struct winsize wz;
+static struct {
+  char env_row[128];
+  char env_col[128];
+} sz;
+static sig_atomic_t wzok;
+static char *arg;
+static boolean isstart;
+static struct sigaction sa;
+static struct timeval glastio;
+static struct timeval now;
+static struct timeval lastlim;
+static char *run_mode = NULL;
+static struct makenode **nodevec;
+static sig_atomic_t signaled;
+
+#define PBUF_SIZE	8192
+#define PRUNNING	0x0001
+#define PFINISHED	0x0002
+struct prg {
+  const char *name;
+  const char *arg0;
+  int num;
+  int fd;
+  pid_t pid;
+  struct timeval lastio;
+  int splashadd;
+  int status;
+  int flags;
+  size_t len;
+  char buf[PBUF_SIZE];
+};
+
+static struct prg *prgs;
+static int inpar, par;
+static double iorate = 800.0;
+
+#ifdef USE_PRELOAD
+static volatile enum { Unknown, Preload, NoPreload } ispreload = Unknown;
+
+static void sighandler_nopreload(int x attribute((unused)))
+{
+    ispreload = NoPreload;
+}
+
+static void sighandler_preload(int x attribute((unused)))
+{
+    ispreload = Preload;
+}
+
+# define SOCK_PATH "/dev/shm/preload_sock"
+#endif
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+  void *r;
+  if ((r = (void *)calloc(nmemb, size)) == 0)
+    {
+      fprintf(stderr, "calloc: out of memory\n");
+      exit(1);
+    }
+  return r;
+}
+
+static int splashpos = -1;
+static char *splashcfg;
+
+int calcsplash(int x, int n, char *opt)
+{
+  char *p;
+  int i;
+  int r, d;
+
+  if (opt == 0)
+    return -1;
+  for (i = 0; i <= x; i++)
+    {
+      if ((p = strchr(opt, ':')) == 0)
+        break;
+      if (i == x)
+	return atoi(opt);
+      opt = p + 1;
+    }
+  r = atoi(opt);
+  n -= i;
+  for (;; i++, n--)
+    {
+      if (n < 1)
+	n = 1;
+      d = r / n;
+      if (i == x)
+	return d;
+      r -= d;
+    }
+}
+
+pid_t splashpid;
+
+void waitsplash()
+{
+  int status;
+  if (!splashpid)
+    return;
+  TEMP_FAILURE_RETRY(waitpid(splashpid, &status, 0));
+}
+
+void closeall(void)
+{
+  int s;
+
+  if (!prgs)
+    return;
+  for (s = 0; s < par; s++)
+    if (prgs[s].fd)
+      close(prgs[s].fd);
+  closeblog();
+}
+
+void callsplash(int n, const char *path, char *action)
+{
+  const char *p;
+  char sbuf[32];
+  char tbuf[256];
+  pid_t pid;
+  struct stat stb;
+  sigset_t nmask;
+
+  if (n < 0 || splashpos < 0)
+    return;
+  if (splashpos + n > 65535)
+    n = 65535 - splashpos;
+  splashpos += n;
+  if (stat("/proc/splash", &stb))
+     return;
+  p = strrchr(path, '/');
+  if (p)
+    path = p + 1;
+  for (p = path; *p; p++)
+    if ((*p == 'S' || *p == 'K') && p[1] >= '0' && p[1] <= '9' && p[2] >= '0' && p[2] <= '9' && p[3])
+      break;
+  if (*p)
+    p += 3;
+  else
+    p = path;
+  if (!action)
+    action = "";
+  if (strlen(p) + strlen(action) + 2 > sizeof(tbuf))
+    return;
+  sprintf(tbuf, "%s%s%s", p, *action ? " " : "", action);
+  sprintf(sbuf, "%d:%d", splashpos - n, n);
+  waitsplash();
+  pid = fork();
+  if (pid == (pid_t)-1)
+    return;
+  if (pid)
+    {
+      splashpid = pid;
+      return;
+    }
+
+  (void)sigfillset(&nmask);
+  sigprocmask(SIG_UNBLOCK, &nmask, NULL);
+
+  (void)signal(SIGHUP,  SIG_DFL);
+  (void)signal(SIGQUIT, SIG_DFL);
+  (void)signal(SIGSEGV, SIG_DFL);
+  (void)signal(SIGTERM, SIG_DFL);
+  (void)signal(SIGCHLD, SIG_DFL);
+  (void)signal(SIGTTIN, SIG_DFL);
+  (void)signal(SIGTTOU, SIG_DFL);
+
+  TEMP_FAILURE_RETRY(dup2(2, 1));
+  closeall();
+  execl("/sbin/splash", "splash", "-p", sbuf, "-t", tbuf, splashcfg, (char *)0);
+  _exit(1);
+}
+
+void writebuf(struct prg *p)
+{
+  char *b = p->buf;
+  int r;
+
+  while (p->len > 0)
+    {
+      r = write(2, b, p->len);
+      if (r < 0)
+	{
+	  if (errno == EINTR)
+	    continue;
+	  perror("write");
+	  r = p->len;
+	}
+      p->len -= r;
+      b += r;
+    }
+  glastio = now;
+}
+
+static int checksystem(const int par, const boolean start, const boolean limit)
+{
+  const      int pg_size       = sysconf(_SC_PAGESIZE);
+  const long int minphys_bytes = (sysconf(_SC_LONG_BIT) > 32L) ? (2<<22) : (2<<21);
+  const long int avphys_pg     = sysconf(_SC_AVPHYS_PAGES);
+  long int minphys_pg;
+  unsigned long int prcs_run, prcs_blked;
+  int newpar;
+  
+  if (avphys_pg < 0)
+    return 1;
+
+  if (pg_size < 0)
+    return par;
+
+  if (!start)
+    minphys_pg = avphys_pg;
+  else
+    minphys_pg = minphys_bytes / pg_size;
+
+  if (avphys_pg < minphys_pg)
+    return 1;
+
+  if (numcpu < 1)
+    return par;
+  
+  if (!limit)
+    return (par*numcpu);
+
+  if (read_proc(&prcs_run, &prcs_blked))
+    return par;
+
+#ifdef USE_PRELOAD
+  /* if we have preload running, we expect I/O not to be a problem */
+  if (ispreload != NoPreload)
+    prcs_blked = 0;
+#endif
+
+  newpar  = (par*numcpu) - (prcs_run + prcs_blked);
+  newpar -= ((int)(((double)prcs_blked)*iorate))/1000;	/* I/O load reduction */
+
+#if DEBUG
+  fprintf(stderr, "checksystem par=%d newpar=%d (prcs_run=%lu) %ld\n", par, newpar, prcs_run, time(0));
+  dump_status();
+#endif
+  if (newpar <= 0)
+    return 1;
+  else
+    return newpar;
+}
+
+static inline int checklimit(const int par, const boolean start)
+{
+  return checksystem(par, start, true);
+}
+
+static inline int checkpar(const int par, const boolean start)
+{
+  return checksystem(par, start, false);
+}
+
+#ifdef CHECK_FORDEVPTS
+/*
+ * 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.
+ */
+# define DEVFS_SUPER_MAGIC       0x1373
+# define DEVPTS_SUPER_MAGIC      0x1cd1
+
+static int startpar_getpt(void) {
+  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.  */
+          return fd;
+        }
+
+      /* If /dev/pts is not mounted then the UNIX98 pseudo terminals
+        are not usable.  */
+      close (fd);
+    }
+
+  return -1;
+}
+
+static int checkdevpts(void)
+{
+  int ptsfd = startpar_getpt();
+
+  if (ptsfd == -1)
+    {
+      return 0;
+    }
+  else if (ptsname(ptsfd) == 0 || grantpt(ptsfd) || unlockpt(ptsfd))
+    {
+      close(ptsfd);
+      return 0;
+    }
+  else
+    {
+      close(ptsfd);
+      return 1;
+    }
+}
+#else
+static int attribute((unused)) checkdevpts(void)
+{
+  return 1;	/* /dev/pts is always mounted */
+}
+#endif
+
+void run(struct prg *p)
+{
+  const char * m = (char*)0;
+  sigset_t nmask;
+#ifdef USE_PRELOAD
+  pid_t parent;
+#endif
+
+  p->len = 0;
+  p->pid = (pid_t)0;
+  p->fd = getpt();
+  if (p->fd <= 0)
+    {
+      p->fd = 0;
+      perror("getpt");
+      fprintf(stderr, "could not get pty for %s\n", p->name);
+    }
+  else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))
+    {
+      fprintf(stderr, "could not init pty for %s\n", p->name);
+      close(p->fd);
+      p->fd = 0;
+    }
+#ifdef USE_PRELOAD
+  parent = getpid();
+#endif
+  if ((p->pid = fork()) == (pid_t)-1)
+    {
+      perror("fork");
+      fprintf(stderr, "could not fork %s\n", p->name);
+      p->pid = (pid_t)0;
+      if (p->fd)
+	{
+	  close(p->fd);
+	  p->fd = 0;
+	}
+      p->status = 0;
+      p->flags = 0;
+      return;
+    }
+  if (p->pid != 0)
+    {
+      p->flags |= PRUNNING;
+      return;
+    }
+
+  (void)sigfillset(&nmask);
+  sigprocmask(SIG_UNBLOCK, &nmask, NULL);
+
+  (void)signal(SIGHUP,  SIG_DFL);
+  (void)signal(SIGQUIT, SIG_DFL);
+  (void)signal(SIGSEGV, SIG_DFL);
+  (void)signal(SIGTERM, SIG_DFL);
+  (void)signal(SIGCHLD, SIG_DFL);
+  (void)signal(SIGTTIN, SIG_DFL);
+  (void)signal(SIGTTOU, SIG_DFL);
+
+  if (setpgid(0, 0))
+    perror("setpgid");
+
+  if (m && p->fd)
+    {
+      sigset_t smask, omask;
+      TEMP_FAILURE_RETRY(close(1));
+      if (open(m, O_RDWR) != 1)
+	{
+	  perror(m);
+	  _exit(1);
+	}
+      TEMP_FAILURE_RETRY(dup2(1, 2));
+      sigemptyset(&smask);
+      sigaddset(&smask, SIGTTOU);
+      sigprocmask(SIG_BLOCK, &smask, &omask);
+      if (tcsetattr(1, TCSANOW, &tio) && errno != ENOTTY)
+	perror("tcsetattr");
+      sigprocmask(SIG_SETMASK, &omask, NULL);
+      if (wzok)
+	ioctl(1, TIOCSWINSZ, &wz);
+      putenv(sz.env_row);
+      putenv(sz.env_col);
+    }
+  else
+    {
+      TEMP_FAILURE_RETRY(dup2(2, 1));
+    }
+
+  closeall();
+
+#ifdef USE_PRELOAD
+  if (arg && !strcmp(arg, "start")) 
+    { 
+      int s;
+
+      if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
+	{
+	  struct sockaddr_un remote;
+	  pid_t child;
+	  int t, len;
+
+	  memset(&remote, 0, sizeof(struct sockaddr_un));
+	  remote.sun_family = AF_UNIX;
+	  strcpy(remote.sun_path, SOCK_PATH);
+	  len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+
+	  if ((t = connect(s, (struct sockaddr *)&remote, len)) != -1)
+	    {
+	      char str[100];
+	      if (ispreload != Preload)
+		kill(parent, SIGUSR1);
+	      send(s, p->name, strlen(p->name), 0);
+	      recv(s, str, sizeof(str), 0);
+	    } 
+	  else if (ispreload == Unknown) 
+	    {
+	      /*
+	       * if we connected to preload once, we know it ran.
+	       * In case we can't connect to it later, it means it did
+	       * its job and we can guess I/O is no longer a problem. 
+	       */
+	      kill(parent, SIGUSR2);
+	    }
+	  close(s);
+	  /*
+	   * if we use preload, we fork again to make bootcharts easier to read.
+	   * The reason is that the name of the init script will otherwise be used
+	   * when in reality the above code waited for preload. If we fork away
+	   * before the exec, the waiting code will be folded into startpar
+	   */
+	  if ((child = fork()))
+	    {
+	      int status;
+	      int ret = waitpid(child, &status, 0);
+	      if (ret == -1)
+		perror("waitpid");
+	      exit(WEXITSTATUS(status));
+	    }
+	}
+    }
+#endif
+
+  if (run_mode)
+    {
+      char path[128];
+      snprintf(path, sizeof(path), "/etc/init.d/%s", p->name);
+      execlp(path, p->arg0, arg, (char *)0);
+    }
+  else if (arg)
+    execlp(p->name, p->arg0, arg, (char *)0);
+  else
+    execlp(p->name, p->arg0, (char *)0);
+  perror(p->name);
+  _exit(1);
+}
+
+int run_single(const char *prg, const char *arg0, int spl)
+{
+  pid_t pid;
+  int r;
+
+  if ((pid = fork()) == (pid_t)-1)
+    {
+      perror("fork");
+      fprintf(stderr, "could not fork %s\n", prg);
+      return 1;
+    }
+
+  if (pid == 0)
+    {
+      sigset_t nmask;
+
+      (void)sigfillset(&nmask);
+      sigprocmask(SIG_UNBLOCK, &nmask, NULL);
+
+      (void)signal(SIGINT,  SIG_DFL);	/* Interactive init scripts may be interrupted */
+      (void)signal(SIGHUP,  SIG_DFL);
+      (void)signal(SIGQUIT, SIG_DFL);
+      (void)signal(SIGSEGV, SIG_DFL);
+      (void)signal(SIGTERM, SIG_DFL);
+      (void)signal(SIGCHLD, SIG_DFL);
+      (void)signal(SIGTTIN, SIG_DFL);
+      (void)signal(SIGTTOU, SIG_DFL);
+
+      TEMP_FAILURE_RETRY(dup2(2, 1));
+      closeall();
+      if (run_mode)
+	{
+	  char path[128];
+	  snprintf(path, sizeof(path), "/etc/init.d/%s", prg);
+	  execlp(path, arg0 ? arg0 : path, arg, (char *)0);
+	}
+      else if (arg)
+	execlp(prg, arg0 ? arg0 : prg, arg, (char *)0);
+      else
+	execlp(prg, arg0 ? arg0 : prg, (char *)0);
+      perror(prg);
+      _exit(1);
+    }
+
+   TEMP_FAILURE_RETRY(waitpid(pid, &r, 0));
+   callsplash(spl, prg, arg);
+   return WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
+}
+
+void do_forward(void)
+{
+  char buf[4096], *b;
+  ssize_t r, rr;
+  setsid();
+  while ((r = read(0, buf, sizeof(buf))))
+    {
+      if (r < 0)
+	{
+	  if (errno == EINTR)
+	    continue;
+#if defined(DEBUG) && (DEBUG > 0)
+	  perror("\n\rstartpar: forward read");
+#endif
+	  break;
+	}
+      b = buf;
+      while (r > 0)
+	{
+	  rr = write(1, b, r);
+	  if (rr < 0)
+	    {
+	      if (errno == EINTR)
+		continue;
+	      perror("\n\rstartpar: forward write");
+	      rr = r;
+	    }
+	  r -= rr;
+	  b += rr;
+	}
+    }
+  _exit(0);
+}
+
+static char *gtimo_buf;
+static size_t gtimo_bufsize;
+static size_t gtimo_buflen;
+
+void storebuf(struct prg *p)
+{
+  if ((gtimo_buflen + p->len) > gtimo_bufsize)
+    {
+      writebuf(p);				/* In case of overflow or memory shortage */
+      return;
+    }
+
+  (void)memcpy(gtimo_buf + gtimo_buflen, p->buf, p->len);
+  gtimo_buflen += p->len;
+  p->len = 0;
+  glastio = now;
+}
+
+void flushbuf(void)
+{
+  size_t len = gtimo_buflen;
+  char * buf = gtimo_buf;
+
+  if (!buf)
+	return;					/* In case of memory shortage */
+
+  while (len > 0)
+    {
+      int r = write(2, buf, len);
+      if (r < 0)
+	{
+	  perror("write");
+	  r = len;
+	}
+      len -= r;
+      buf += r;
+    }
+  gtimo_buflen = 0;
+  *gtimo_buf = 0;
+}
+
+#define GTIMO_OFFL	0
+#define GTIMO_USED	1
+
+void detach(struct prg *p, const int store)
+{
+  int flags = fcntl(p->fd, F_GETFL);
+  ssize_t r;
+  pid_t pid;
+
+  if (flags > 0)
+    flags |= FNONBLOCK;
+  else
+    flags = FNONBLOCK;
+
+  fcntl(p->fd, F_SETFL, flags);
+  while ((r = read(p->fd, p->buf, sizeof(p->buf))))
+    {
+      if (r < 0)
+	{
+	  if (errno == EINTR)
+	    continue;
+	  break;
+	}
+      p->len = r;
+      if (store)
+	storebuf(p);
+      else
+	writebuf(p);
+    }
+  flags &= ~FNONBLOCK;
+  fcntl(p->fd, F_SETFL, flags);
+  if (r == -1 && errno == EWOULDBLOCK)
+    {
+      if ((pid = fork()) == 0)
+	{
+	  sigset_t nmask;
+	  (void)sigfillset(&nmask);
+	  sigprocmask(SIG_UNBLOCK, &nmask, NULL);
+
+	  (void)signal(SIGHUP,  SIG_DFL);
+	  (void)signal(SIGQUIT, SIG_DFL);
+	  (void)signal(SIGSEGV, SIG_DFL);
+	  (void)signal(SIGTERM, SIG_DFL);
+	  (void)signal(SIGCHLD, SIG_DFL);
+	  (void)signal(SIGTTIN, SIG_DFL);
+	  (void)signal(SIGTTOU, SIG_DFL);
+
+	  TEMP_FAILURE_RETRY(dup2(p->fd, 0));
+	  TEMP_FAILURE_RETRY(dup2(2, 1));
+	  closeall();
+
+	  execlp(myname, myname, "-f", "--", p->name, NULL);
+	  do_forward();
+	}
+      if (pid == -1)
+	perror("fork");
+    }
+  close(p->fd);
+  p->fd = 0;
+}
+
+static struct prg *interactive_task;
+static volatile int active;
+static void sigchld(int sig attribute((unused)))
+{
+  const int old_errno = errno;
+  int status;
+  pid_t pid;
+ 
+  while ((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) != 0)
+    {
+      int s;
+
+      if (errno == ECHILD)
+	break;
+
+      if (pid < 0)
+	continue;
+
+      if (pid == splashpid)
+	{
+	  splashpid = (pid_t)0;
+	  continue;
+	}
+
+      for (s = 0; s < par; s++)
+	{
+	  struct prg * p = prgs + s;
+	  if (p == interactive_task)
+	    continue;
+	  if (p->pid != pid)
+	    continue;
+	  if (p->flags & PRUNNING)
+	    {
+	      p->status = status;
+	      p->flags |= PFINISHED;
+	      break;
+	    }
+	}
+    }
+
+  signaled = 1;
+  errno = old_errno;
+}
+
+static void sigwinch(int sig attribute((unused)))
+{
+  if (ioctl(0, TIOCGWINSZ, &wz) < 0)
+    {
+      wzok = 0;
+      return;
+    }
+  if (wz.ws_row == 0) wz.ws_row = 24;
+  if (wz.ws_col == 0) wz.ws_col = 80;
+  snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d",   wz.ws_row);
+  snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
+  kill(-1, SIGWINCH);
+}
+
+void usage(int status)
+{
+  fprintf(stderr, "usage: startpar [options] [-a arg] prgs\n");
+  fprintf(stderr, "           run given programs parallel\n");
+  fprintf(stderr, "       startpar [options] [-P prev] [-R run] [-S <start>:<num>] -M mode\n");
+  fprintf(stderr, "           run parallel with Makefile\n");
+  fprintf(stderr, "       startpar -v\n");
+  fprintf(stderr, "           show version number\n");
+  fprintf(stderr, "general options:\n");
+  fprintf(stderr, "       -p parallel tasks\n");
+  fprintf(stderr, "       -t I/O timeout\n");
+  fprintf(stderr, "       -T global I/O timeout\n");
+  exit(status);
+}
+
+int main(int argc, char **argv)
+{
+  int gtimo = -1;
+  int timo = -1;
+  int isgtimo;
+  int forw = 0;
+  int c, i, num;
+  int limit;
+  int notty = 0;
+  int *resvec;
+  struct timeval tv;
+  struct prg *p;
+  struct prg *gtimo_running = 0;
+  char *prev_level = getenv("PREVLEVEL");
+  char *run_level = getenv("RUNLEVEL");
+  char *splashopt = 0;
+  sigset_t nmask, omask, smask;
+
+  detect_consoles();
+
+  (void)sigemptyset(&nmask);
+  (void)sigaddset(&nmask, SIGHUP);
+  sigprocmask(SIG_UNBLOCK, &nmask, NULL);
+
+  (void)signal(SIGINT,  SIG_IGN);	/* Init scripts should not be interruptible */
+  (void)signal(SIGCHLD, SIG_DFL);
+  numcpu = sysconf(_SC_NPROCESSORS_ONLN);
+  myname = argv[0];
+
+  while ((c = getopt(argc, argv, "fhp:t:T:a:M:P:R:S:vi:")) != EOF)
+    {
+      switch(c)
+        {
+	case 'p':
+	  par = atoi(optarg);
+	  break;
+	case 't':
+	  timo = atoi(optarg);
+	  break;
+	case 'T':
+	  gtimo = atoi(optarg);
+	  break;
+	case 'f':
+	  forw = 1;
+	  break;
+	case 'a':
+	  arg = optarg;
+	  break;
+	case 'M':
+	  run_mode = optarg;
+	  break;
+	case 'P':
+	  prev_level = optarg;
+	  break;
+	case 'R':
+	  run_level = optarg;
+	  break;
+	case 'S':
+	  splashopt = optarg;
+	  break;
+	case 'v':
+	  printf("startpar version %s\n", VERSION);
+	  exit(0);
+	case 'h':
+	  usage(0);
+	  break;
+	case 'i':
+	  iorate = atof(optarg);
+	  if (iorate < 0.0)
+	    iorate = 800.0;
+	  break;
+	default:
+	  usage(1);
+	  break;
+        }
+    }
+  if (forw)
+    do_forward();
+  argc -= optind;
+  argv += optind;
+
+  if (splashopt)
+    {
+      char *so = strchr(splashopt, ':');
+      if (!so)
+	splashopt = 0;
+      else
+	{
+	  splashpos = atoi(splashopt);
+	  splashopt = so + 1;
+	}
+      splashcfg = getenv("SPLASHCFG");
+      if (!splashcfg)
+	{
+	  splashpos = -1;
+	  splashopt = 0;
+	}
+    }
+  if (run_mode)
+    {
+      char makefile[64];
+      if (!strcmp(run_mode, "boot"))
+	arg = "start";
+      else if (!strcmp(run_mode, "halt"))
+	arg = "stop";
+      else if (!strcmp(run_mode, "start") || !strcmp(run_mode, "stop"))
+	{
+	  arg = run_mode;
+	  if (!prev_level || !run_level)
+	    {
+	      fprintf(stderr, "You must specify previous and next runlevels\n");
+	      exit(1);
+	    }
+	}
+      else
+	{
+	  fprintf(stderr, "invalid run mode %s\n", run_mode);
+	  exit(1);
+	}
+      snprintf(makefile, sizeof(makefile), "/etc/init.d/.depend.%s", run_mode);
+      parse_makefile(makefile);
+      check_run_files(run_mode, prev_level, run_level);
+
+      argc = tree_entries;			/* number of handled scripts */
+      isstart = !strcmp(arg, "start");
+
+      if (argc == 0)
+	exit(0);
+
+      if (par == 0)
+	par = 8;
+      if (par > argc)				/* not more than the number of all scripts */
+	par = argc;
+
+      inpar = par;				/* the original argument of parallel procs per cpu */
+
+      par = checkpar(inpar, isstart);		/* the number of parallel procs on all cpu's */
+
+      if (par > argc)				/* not more than the number of all scripts */
+	par = argc;
+
+      nodevec = xcalloc(argc, sizeof(*nodevec));
+    }
+  else
+    {
+      if (par < 0)
+	usage(1);
+
+      if (arg)
+	isstart = !strcmp(arg, "start");
+
+      if (argc == 0)
+	exit(0);
+
+      if (par == 0)
+	par = argc;
+      if (par > argc)				/* not more than the number of all scripts */
+	par = argc;
+
+      inpar = par;				/* the original argument of parallel procs per cpu */
+
+      par = checkpar(inpar, isstart);		/* the number of parallel procs on all cpu's */
+
+      if (par > argc)				/* not more than the number of all scripts */
+	par = argc;
+    }
+
+  num = 0;
+  resvec = (int *)xcalloc(argc, sizeof(int));
+  for (i = 0; i < argc; i++)
+    resvec[i] = 255;
+
+  if (argc == 1)
+    {
+      if (run_mode)
+	{
+	  if ((*nodevec = pickup_task()))
+	  {
+	    *resvec = run_single((*nodevec)->name, (*nodevec)->arg0, calcsplash(0, 1, splashopt));
+	    finish_task(*nodevec);
+	  }
+      } else
+	*resvec = run_single(*argv, *argv, calcsplash(0, 1, splashopt));
+      goto finished;
+    }
+
+  prgs = (struct prg *)xcalloc(par, sizeof *prgs);
+  gtimo_bufsize = par * PBUF_SIZE;
+  gtimo_buf = (char *) calloc(gtimo_bufsize, sizeof(char));
+  if (!gtimo_buf)
+    gtimo_bufsize = 0;				/* Accept error due memory shortage */
+
+  sa.sa_handler = sigwinch;
+  sa.sa_flags = SA_RESTART|SA_NODEFER;
+  (void)sigemptyset(&sa.sa_mask);
+  if (sigaction(SIGWINCH, &sa, 0))
+    {
+      perror("sigwinch sigaction");
+      exit(1);
+    }
+
+  if (tcgetattr(0, &tio))
+    {
+      if (errno != ENOTTY)
+	perror("tcgetattr");
+      tcgetattr(2, &tio);
+      if (errno == ENOTTY)
+	notty = 1;
+    }
+  cfmakeraw(&tio);
+  tio.c_lflag &= ~ECHO;
+  tio.c_lflag |= ISIG;
+  tio.c_cc[VTIME] = 0;
+  tio.c_cc[VMIN]  = CMIN;
+
+  if (ioctl(0, TIOCGWINSZ, &wz) == 0)
+    wzok = 1;
+  if (wz.ws_row == 0) wz.ws_row = 24;
+  if (wz.ws_col == 0) wz.ws_col = 80;
+
+  strcat(&sz.env_row[0], "LINES=");
+  strcat(&sz.env_col[0], "COLUMNS=");
+  snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d",   wz.ws_row);
+  snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
+
+  sa.sa_handler = sigchld;
+  sa.sa_flags = SA_RESTART;
+  (void)sigemptyset(&sa.sa_mask);
+  (void)sigaddset(&sa.sa_mask, SIGCHLD);
+  (void)sigaddset(&sa.sa_mask, SIGWINCH);
+  (void)sigaddset(&sa.sa_mask, SIGUSR1);
+  (void)sigaddset(&sa.sa_mask, SIGUSR2);
+  if (sigaction(SIGCHLD, &sa, 0))
+    {
+      perror("sigchld sigaction");
+      exit(1);
+    }
+
+#ifdef USE_PRELOAD
+  sa.sa_handler = sighandler_preload;
+  sa.sa_flags = SA_RESTART;
+  (void)sigemptyset(&sa.sa_mask);
+  if (sigaction(SIGUSR1, &sa, 0))
+    {
+      perror("sigusr1(preload) sigaction");
+      exit(1);
+    }
+
+  sa.sa_handler = sighandler_nopreload;
+  sa.sa_flags = SA_RESTART;
+  (void)sigemptyset(&sa.sa_mask);
+  if (sigaction(SIGUSR2, &sa, 0))
+    {
+      perror("sigusr2(preload) sigaction");
+      exit(1);
+    }
+#endif
+
+  /* lock us into memory */
+  if (geteuid() == 0)
+    mlockall(MCL_CURRENT|MCL_FUTURE);
+  errno = 0;
+
+  gettimeofday(&glastio, 0);
+  limit = checklimit(inpar, isstart);
+  lastlim = glastio;
+
+  (void)sigemptyset(&nmask);
+  (void)sigaddset(&nmask, SIGCHLD);
+  sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+  (void)sigfillset(&smask);
+  (void)sigdelset(&smask, SIGHUP);
+  (void)sigdelset(&smask, SIGINT);
+  (void)sigdelset(&smask, SIGQUIT);
+  (void)sigdelset(&smask, SIGTERM);
+  (void)sigdelset(&smask, SIGCHLD);
+
+  for (;;)
+    {
+#ifdef CHECK_FORDEVPTS
+      int devpts = 0;
+#endif
+      int maxfd = -1;
+      int last = -1;
+      int r, s;
+      long diff;
+      pid_t pid;
+      fd_set rset;
+
+      gettimeofday(&now, 0);
+      pid = (pid_t)-1;
+      FD_ZERO(&rset);
+      tv = now;
+
+      if ((diff = timerdiff(now, lastlim)) >= 300 || diff < 0)
+	{
+#if DEBUG
+	  fprintf(stderr, "%d: doing checklimit after %ldms %ld\n", getpid(), diff, time(0));
+#endif
+	  if ((limit = checklimit(inpar, isstart)) > argc)
+	    limit = argc;			/* not more than the number of all scripts */
+	  lastlim = now;
+	  diff = 0;
+	} 
+#if DEBUG
+      fprintf(stderr, "par=%d, inpar=%d, limit=%d (diff=%ld)\n", par, inpar, limit, diff);
+#endif
+      for (s = 0; s < par; s++)			/* never leave this with break!! */
+	{
+	  p = prgs + s;
+	  if (p == interactive_task)
+	    continue;				/* don't count this here */
+	  if (p->fd == 0)
+	    {
+	      if (interactive_task)
+		continue;			/* dont't start new processes */
+	      if (num >= argc)
+		continue;			/* nothing to do */
+	    redo:
+	      if (p->pid == 0)
+		{
+		  if (active >= limit)
+		    continue;			/* load balancing */
+		  if (run_mode)
+		    {
+		      if ((nodevec[num] = pickup_task()) == NULL)
+			continue;
+		      if (nodevec[num]->interactive)
+			interactive_task = p;
+		      p->name = nodevec[num]->name;
+		      p->arg0 = nodevec[num]->arg0 ? nodevec[num]->arg0 : nodevec[num]->name;
+		    }
+		  else {
+		    p->name = *argv++;
+		    p->arg0 = p->name;
+		  }
+		  p->splashadd = calcsplash(num, argc, splashopt);
+		  p->num = num++;
+#ifdef CHECK_FORDEVPTS
+		  if (!devpts)
+		    interactive_task = p;	/* no /dev/pts, treat as interactive */
+#endif
+		  if (notty)
+		    interactive_task = p;	/* no tty, treat as interactive */
+		  if (interactive_task)
+		    continue;			/* don't start this here */
+		  run(p);
+		  if (p->pid == 0)
+		    {
+		      resvec[p->num] = 1;
+		      if (run_mode)
+			finish_task(nodevec[p->num]);
+		      p->status = 0;
+		      p->flags = 0;
+		      if (p->fd)
+			{
+			  close(p->fd);
+			  p->fd = 0;
+			}
+		      goto redo;		/* this one is free again */
+		    }
+		  else
+		    {
+		      active++;			/* count this active job */
+		    }
+		  gettimeofday(&now, 0);
+		  tv = now;
+		}
+	    }
+
+	  FD_SET(p->fd, &rset);
+	  if (p->fd > maxfd)
+	    maxfd = p->fd;
+
+	  if (p->len == 0)
+	    continue;
+          if ((last < 0) || timercmp(&tv,&p->lastio,>))
+	    {
+	      last = s;
+	      tv = p->lastio;
+	    }
+
+	} /* for (s = 0; s < par; s++) */
+
+      if (interactive_task)
+	{
+	  if (active == 0)
+	    {
+	      p = interactive_task;
+	      p->flags |= PRUNNING;
+	      resvec[p->num] = run_single(p->name, p->arg0, p->splashadd);
+	      if (run_mode)
+		finish_task(nodevec[p->num]);
+	      p->flags = 0;
+	      p->pid = 0;
+	      p->fd = 0;
+	      interactive_task = NULL;
+	      continue;
+	    }
+	}
+
+      if (active == 0)
+	{
+	  if (num < argc)
+	    fprintf(stderr, "ERROR: not all processed (%d of %d)\n", num, argc);
+	  break;
+	}
+#if DEBUG
+      fprintf(stderr, "active = %d, maxfd = %d, last = %d\n", active, maxfd, last);
+#endif
+      if (active == 1 && last >= 0)
+	{
+	  p = prgs + last;
+	  if ((pid = waitpid(p->pid, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) == 0)
+	    {
+	      writebuf(p);
+	      continue;
+	    }
+	  else
+	    {
+	      p->status = r;
+	      p->flags |= PFINISHED;
+	      signaled = 1;
+	    }
+	}
+      else
+	{
+	  const struct timespec zero = {0,0};
+	  const int old_errno = errno;
+	  (void)pselect(0,0,0,0,&zero,&smask);
+	  errno = old_errno;
+	}
+
+      if (signaled)
+	{
+	  signaled = 0;
+
+	  for (s = 0; s < par; s++)
+	    {
+	      p = prgs + s;
+
+	      if ((p->flags & PFINISHED) == 0)
+		continue;
+	      r = p->status;
+
+	      if (WIFSTOPPED(r))
+		{
+		  pid_t pg = getpgid(p->pid);
+		  if (pg > 0)
+		    killpg(pg, SIGCONT);
+		  else
+		    kill(-p->pid, SIGCONT);
+		  p->flags &= ~PFINISHED;
+		  continue;
+		}
+
+	      callsplash(p->splashadd, p->name, arg);
+	      resvec[p->num] = WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
+	      if (run_mode)
+		finish_task(nodevec[p->num]);
+	      p->status = 0;
+	      p->flags = 0;
+	      p->pid = 0;
+	      active--;
+
+	      if (gtimo_running == p)
+		{
+		  writebuf(p);
+		  if (p->fd) detach(p, GTIMO_OFFL);
+		  flushbuf();
+		  gtimo_running = 0;
+		}
+	      else if (gtimo_running)
+		{
+		  storebuf(p);
+		  if (p->fd) detach(p, GTIMO_USED);
+		}
+	      else
+		{
+		  writebuf(p);
+		  if (p->fd) detach(p, GTIMO_OFFL);
+		}
+#ifdef START_OUTRIGHT
+	      break;		/* one free slot ... start outright new process */
+#endif
+	    } /* for (s = 0; s < par; s++) */
+
+#ifdef CHECK_FORDEVPTS
+	  if (!devpts)
+	    devpts = checkdevpts();
+#endif
+	  continue;		/* start new processes */
+	}
+
+      if (timo >= 0)
+        tv.tv_sec += timo;
+
+      isgtimo = 0;
+      if (gtimo >= 0 && !gtimo_running && last >= 0 && prgs[last].pid)
+	{
+	  struct timeval gl = glastio;
+	  gl.tv_sec += gtimo;
+	  if ((timo < 0) || timercmp(&tv,&gl,>))
+	    {
+	      tv = glastio;
+	      tv.tv_sec += gtimo;
+	      isgtimo = 1;
+	    }
+	}
+
+      r = 0;
+      if (timo >= 0 || isgtimo)
+	{
+	  struct timeval wait;
+	  struct timespec spec;
+
+	  timersub(&tv, &now, &wait);
+	  if (wait.tv_usec < 0)
+	    {
+	      wait.tv_usec += 1000000;
+	      wait.tv_sec--;
+	    }
+	  if (wait.tv_sec >= 0)
+	    {
+	      int check = limit < par && num < argc;
+
+	      if (check)			/* shorten timeout for new limit and procs  ... */
+		{
+		  wait.tv_sec = 0;
+		  wait.tv_usec = (300 - diff) * 1000;
+		}
+#if DEBUG
+	      fprintf(stderr, "going into select1 %d %ld %ld\n", last, wait.tv_sec, wait.tv_usec);
+#endif
+	      TIMEVAL_TO_TIMESPEC(&wait, &spec);
+	      r = pselect(maxfd + 1, &rset, 0, 0, (last >= 0 || check) ? &spec : 0, &smask);
+
+	      if (check && (r == 0))		/* ... but do not throw out messages to early!!! */
+		continue;
+	    }
+	  else
+	    {
+	      wait.tv_sec  = 0;			/* Avoid looping around (does this ever happen?) */
+	      wait.tv_usec = 20*1000;
+	      TIMEVAL_TO_TIMESPEC(&wait, &spec);
+	      r = pselect(maxfd + 1, &rset, 0, 0, last >= 0 ? &spec : 0, &smask);
+	    }
+	}
+      else
+	{
+	  r = pselect(maxfd + 1, &rset, 0, 0, 0, &smask);
+	}
+
+      if (r == -1)
+	{
+	  if (errno == EINTR)
+	    continue;		/* SIGCHLD has happen in most cases */
+	  perror("select");
+	  exit(1);
+	}
+      if (r == 0)
+	{
+	  if (last < 0)		/* just in case... */
+	    continue;
+	  p = prgs + last;
+	  writebuf(p);
+	  if (isgtimo && p->pid)
+	    gtimo_running = p;
+	}
+      else
+	{
+	  for (s = 0; s < par; s++)
+	    {
+	      p = prgs + s;
+	      if (p->fd == 0)
+		continue;
+	      if (!FD_ISSET(p->fd, &rset))
+		continue;
+	      r = (int)TEMP_FAILURE_RETRY(read(p->fd, p->buf + p->len, sizeof(p->buf) - p->len));
+	      if (r <= 0)
+		{
+		  if (!gtimo_running || p == gtimo_running)
+		    writebuf(p);
+		  if (r < 0)
+		    {
+		      close(p->fd);
+		      p->fd = 0;
+#ifdef START_OUTRIGHT
+		      break;		/* one free slot ... start outright new process */
+#endif
+		    }
+		  continue;
+		}
+	      p->len += r;
+	      if (p->len == sizeof(p->buf))
+		{
+		  for (i = p->len - 1; i >= 0; i--)
+		    {
+		      if (p->buf[i] == '\n')
+			break;
+		    }
+		  if (++i <= 0)
+		    i = p->len;
+		  p->len = i;
+		  writebuf(p);
+		  p->len = i;	/* writebuf clears p->len */
+		  if (p->len < sizeof(p->buf))
+		    memmove(p->buf, p->buf + p->len, sizeof(p->buf) - p->len);
+		  p->len = sizeof(p->buf) - p->len;
+		}
+	      p->lastio = now;
+
+	    } /* for (s = 0; s < par; s++) */
+	}
+
+    } /* for (;;) */
+
+  sigprocmask(SIG_SETMASK, &omask, NULL);
+
+finished:
+  waitsplash();
+  if (run_mode)
+    print_run_result(resvec, nodevec, run_mode);
+  else
+    {
+      for (i = 0; i < argc; i++)
+	{
+#if DEBUG
+	  if (resvec[i] == 255)
+	    {
+	      fprintf(stderr, "ERROR: forgotten process??\n");
+	      exit (1);
+	    }
+#endif
+#if VERBOSE
+	  printf(i ? " %d" : "%d", resvec[i]);
+#endif /* VERBOSE */
+	}
+#if VERBOSE
+      printf("\n");
+#endif /* VERBOSE */
+    }
+  return 0;
+}
