summaryrefslogtreecommitdiff
path: root/simpleinit/shutdown.c
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2010-12-10 17:28:32 +0100
committerKarel Zak <kzak@redhat.com>2010-12-10 17:28:32 +0100
commit5502d92b28e442c8b9294608eaf67df9127d11e9 (patch)
tree924b81cc9b93c860c1f72298b0afbe4100a50052 /simpleinit/shutdown.c
parentcda8458b4d1e6f2772b77a448b342806057f0a3a (diff)
downloadutil-linux-old-5502d92b28e442c8b9294608eaf67df9127d11e9.tar.gz
simpleinit: move old init stuff to separate directory
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'simpleinit/shutdown.c')
-rw-r--r--simpleinit/shutdown.c747
1 files changed, 747 insertions, 0 deletions
diff --git a/simpleinit/shutdown.c b/simpleinit/shutdown.c
new file mode 100644
index 00000000..9bb121cf
--- /dev/null
+++ b/simpleinit/shutdown.c
@@ -0,0 +1,747 @@
+/* shutdown.c - shutdown a Linux system
+ * Initially written by poe@daimi.aau.dk
+ * Currently maintained at ftp://ftp.daimi.aau.dk/pub/Software/Linux/
+ */
+
+/*
+ * Modified by jrs@world.std.com to try to exec "umount -a" and if
+ * that doesn't work, then umount filesystems ourselves in reverse
+ * order. The old-way was in forward order. Also if the device
+ * field of the mtab does not start with a "/" then give umount
+ * the mount point instead. This is needed for the nfs and proc
+ * filesystems and yet is compatible with older systems.
+ *
+ * We also use the mntent library interface to read the mtab file
+ * instead of trying to parse it directly and no longer give a
+ * warning about not being able to umount the root.
+ *
+ * The reason "umount -a" should be tried first is because it may do
+ * special processing for some filesystems (such as informing an
+ * nfs server about nfs umounts) that we don't want to cope with here.
+ */
+
+/*
+ * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8)
+ * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18.
+ * (I butchered Scotts patches somewhat. - poe)
+ *
+ * Changes by Richard Gooch <rgooch@atnf.csiro.au> (butchered by aeb)
+ * introducing shutdown.conf.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2000-03-02 Richard Gooch <rgooch@atnf.csiro.au>
+ * - pause forever if (pid == 1) and send SIGQUIT to pid = 1
+ *
+ * 2000-11-04 Richard Gooch <rgooch@atnf.csiro.au>
+ * - continue reaping if (pid == 1)
+ *
+ * 2000-11-06 Richard Gooch <rgooch@atnf.csiro.au>
+ * - shut down "finalprog" from /etc/inittab
+ * - kill normal user (non-root and non-daemon) processes first with SIGTERM
+ *
+ * 2000-11-08 Richard Gooch <rgooch@atnf.csiro.au>
+ * - rollback services
+ * - do not unmount devfs (otherwise get harmless but annoying messages)
+ * - created syncwait() for faster shutting down
+ * - kill getty processes
+ * 2001-05-12 Richard Gooch <rgooch@atnf.csiro.au>
+ * - unblock all signals (sigmask from simpleinit(8) stopped sleep(3))
+ * - close all files
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include "linux_reboot.h"
+#include "pathnames.h"
+#include "strutils.h"
+#include "nls.h"
+#include "usleep.h"
+
+static void usage(void), int_handler(int), write_user(struct utmp *);
+static void wall(void), write_wtmp(void), unmount_disks(void);
+static void unmount_disks_ourselves(void);
+static void swap_off(void), do_halt(char *);
+static void kill_mortals (int sig);
+static void stop_finalprog (void);
+static void syncwait (int timeval);
+
+
+char *prog; /* name of the program */
+int opt_reboot; /* true if -r option or reboot command */
+int timeout; /* number of seconds to shutdown */
+int opt_quiet; /* true if no message is wanted */
+int opt_fast; /* true if fast boot */
+char message[90]; /* reason for shutdown if any... */
+int opt_single = 0; /* true is we want to boot singleuser */
+char *whom; /* who is shutting the system down */
+int opt_msgset = 0; /* message set on command line */
+ /* change 1 to 0 if no file is to be used by default */
+int opt_use_config_file = 1; /* read _PATH_SHUTDOWN_CONF */
+char halt_action[256]; /* to find out what to do upon halt */
+
+/* #define DEBUGGING */
+
+#define WR(s) write(fd, s, strlen(s))
+#define WRCRLF write(fd, "\r\n", 2)
+#define ERRSTRING strerror(errno)
+
+#define UMOUNT_ARGS "umount", "-a", "-t", "nodevfs,devtmpfs"
+#define SWAPOFF_ARGS "swapoff", "-a"
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ _("Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n"));
+ exit(EXIT_FAILURE);
+}
+
+static void
+my_puts(char *s)
+{
+ /* Use a fresh stdout after forking */
+ freopen(_PATH_CONSOLE, "w", stdout);
+ puts(s);
+ fflush(stdout);
+}
+
+void
+int_handler(int sig)
+{
+ unlink(_PATH_NOLOGIN);
+ signal(SIGINT, SIG_DFL);
+ my_puts(_("Shutdown process aborted"));
+ _exit(EXIT_FAILURE);
+}
+
+static int
+iswhitespace(int a) {
+ return (a == ' ' || a == '\t');
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, i, fd;
+ char *ptr;
+
+ i = getdtablesize ();
+ for (fd = 3; fd < i; fd++) close (fd);
+ if (getpid () == 1)
+ {
+ for (fd = 0; fd < 3; fd++) close (fd);
+ while (1) wait (NULL); /* Grim reaper never stops */
+ }
+ sigsetmask (0); /* simpleinit(8) blocks all signals: undo for ALRM */
+ for (i = 1; i < NSIG; i++) signal (i, SIG_DFL);
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+#ifndef DEBUGGING
+ if(setreuid (0, 0))
+ errx(EXIT_FAILURE, _("only root can shut a system down."));
+#endif
+
+ if(*argv[0] == '-') argv[0]++; /* allow shutdown as login shell */
+ prog = argv[0];
+ if((ptr = strrchr(argv[0], '/'))) prog = ++ptr;
+
+ /* All names (halt, reboot, fasthalt, fastboot, shutdown)
+ refer to the same program with the same options,
+ only the defaults differ. */
+ if(!strcmp("halt", prog)) {
+ opt_reboot = 0;
+ opt_quiet = 1;
+ opt_fast = 0;
+ timeout = 0;
+ } else if(!strcmp("fasthalt", prog)) {
+ opt_reboot = 0;
+ opt_quiet = 1;
+ opt_fast = 1;
+ timeout = 0;
+ } else if(!strcmp("reboot", prog)) {
+ opt_reboot = 1;
+ opt_quiet = 1;
+ opt_fast = 0;
+ timeout = 0;
+ } else if(!strcmp("fastboot", prog)) {
+ opt_reboot = 1;
+ opt_quiet = 1;
+ opt_fast = 1;
+ timeout = 0;
+ } else {
+ /* defaults */
+ opt_reboot = 0;
+ opt_quiet = 0;
+ opt_fast = 0;
+ timeout = 2*60;
+ }
+
+ c = 0;
+ while(++c < argc) {
+ if(argv[c][0] == '-') {
+ for(i = 1; argv[c][i]; i++) {
+ switch(argv[c][i]) {
+ case 'C':
+ opt_use_config_file = 1;
+ break;
+ case 'h':
+ opt_reboot = 0;
+ break;
+ case 'r':
+ opt_reboot = 1;
+ break;
+ case 'f':
+ opt_fast = 1;
+ break;
+ case 'q':
+ opt_quiet = 1;
+ break;
+ case 's':
+ opt_single = 1;
+
+ default:
+ usage();
+ }
+ }
+ } else if(!strcmp("now", argv[c])) {
+ timeout = 0;
+ } else if(argv[c][0] == '+') {
+ timeout = 60 * atoi(&argv[c][1]);
+ } else if (isdigit(argv[c][0])) {
+ char *colon;
+ int hour = 0;
+ int minute = 0;
+ time_t tics;
+ struct tm *tt;
+ int now, then;
+
+ if((colon = strchr(argv[c], ':'))) {
+ *colon = '\0';
+ hour = atoi(argv[c]);
+ minute = atoi(++colon);
+ } else usage();
+
+ (void) time(&tics);
+ tt = localtime(&tics);
+
+ now = 3600 * tt->tm_hour + 60 * tt->tm_min;
+ then = 3600 * hour + 60 * minute;
+ timeout = then - now;
+ if(timeout < 0)
+ errx(EXIT_FAILURE, _("that must be tomorrow, "
+ "can't you wait till then?"));
+ } else {
+ xstrncpy(message, argv[c], sizeof(message));
+ opt_msgset = 1;
+ }
+ }
+
+ halt_action[0] = 0;
+
+ /* No doubt we shall want to extend this some day
+ and register a series of commands to be executed
+ at various points during the shutdown sequence,
+ and to define the number of milliseconds to sleep, etc. */
+ if (opt_use_config_file) {
+ char line[256], *p;
+ FILE *fp;
+
+ /* Read and parse the config file */
+ halt_action[0] = '\0';
+ if ((fp = fopen (_PATH_SHUTDOWN_CONF, "r")) != NULL) {
+ if (fgets (line, sizeof(line), fp) != NULL &&
+ strncasecmp (line, "HALT_ACTION", 11) == 0 &&
+ iswhitespace(line[11])) {
+ p = strchr(line, '\n');
+ if (p)
+ *p = 0; /* strip final '\n' */
+ p = line+11;
+ while(iswhitespace(*p))
+ p++;
+ strcpy(halt_action, p);
+ }
+ fclose (fp);
+ }
+ }
+
+ if(!opt_quiet && !opt_msgset) {
+ /* now ask for message, gets() is insecure */
+ int cnt = sizeof(message)-1;
+ char *ptr;
+
+ printf("Why? "); fflush(stdout);
+
+ ptr = message;
+ while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') {
+ ptr++;
+ }
+ *ptr = '\0';
+ } else if (!opt_msgset) {
+ strcpy(message, _("for maintenance; bounce, bounce"));
+ }
+
+#ifdef DEBUGGING
+ printf("timeout = %d, quiet = %d, reboot = %d\n",
+ timeout, opt_quiet, opt_reboot);
+#endif
+
+ /* so much for option-processing, now begin termination... */
+ if(!(whom = getlogin()) || !*whom) whom = "ghost";
+ if(strlen(whom) > 40) whom[40] = 0; /* see write_user() */
+
+ setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+ signal(SIGINT, int_handler);
+ signal(SIGHUP, int_handler);
+ signal(SIGQUIT, int_handler);
+ signal(SIGTERM, int_handler);
+
+ chdir("/");
+
+ if(timeout > 5*60) {
+ sleep(timeout - 5*60);
+ timeout = 5*60;
+ }
+
+ if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT, 0644)) >= 0) {
+ /* keep xgettext happy and leave \r\n outside strings */
+ WRCRLF;
+ WR(_("The system is being shut down within 5 minutes"));
+ WRCRLF;
+ write(fd, message, strlen(message));
+ WRCRLF;
+ WR(_("Login is therefore prohibited."));
+ WRCRLF;
+ close(fd);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if(timeout > 0) {
+ wall();
+ sleep(timeout);
+ }
+
+ timeout = 0;
+ wall();
+ sleep(3);
+
+ /* now there's no turning back... */
+ signal(SIGINT, SIG_IGN);
+
+ /* do syslog message... */
+ openlog(prog, LOG_CONS, LOG_AUTH);
+ if (opt_reboot)
+ syslog(LOG_NOTICE, _("rebooted by %s: %s"),
+ whom, message);
+ else
+ syslog(LOG_NOTICE, _("halted by %s: %s"),
+ whom, message);
+ closelog();
+
+ if(opt_fast)
+ if((fd = open("/fastboot", O_WRONLY|O_CREAT, 0644)) >= 0)
+ close(fd);
+
+ kill(1, SIGTSTP); /* tell init not to spawn more getty's */
+ write_wtmp();
+ if(opt_single)
+ if((fd = open(_PATH_SINGLE, O_CREAT|O_WRONLY, 0644)) >= 0)
+ close(fd);
+
+ sync();
+
+ signal(SIGTERM, SIG_IGN);
+ if(fork() > 0) sleep(1000); /* the parent will die soon... */
+ setpgrp(); /* so the shell wont kill us in the fall */
+
+#ifndef DEBUGGING
+ /* a gentle kill of all other processes except init */
+ kill_mortals (SIGTERM);
+ for (fd = 0; fd < 3; fd++) close (fd);
+ stop_finalprog ();
+ sleep (1); /* Time for saves to start */
+ kill (1, SIGTERM); /* Tell init to kill spawned gettys */
+ usleep (100000); /* Wait for gettys to die */
+ my_puts (""); /* Get past the login prompt */
+ system ("/sbin/initctl -r"); /* Roll back services */
+ syncwait (1);
+ my_puts ("Sending SIGTERM to all remaining processes...");
+ kill (-1, SIGTERM);
+ sleep (2); /* Default 2, some people need 5 */
+
+ kill (-1, SIGKILL); /* Now use brute force... */
+
+ /* turn off accounting */
+ acct(NULL);
+#endif
+ /* RedHat and SuSE like to remove /etc/nologin.
+ Perhaps the usual sequence is
+ touch nologin; shutdown -h; fiddle with hardware;
+ boot; fiddle with software; rm nologin
+ and removing it here will be counterproductive.
+ Let us see whether people complain. */
+ unlink(_PATH_NOLOGIN);
+
+ /* Tell init(8) to exec so that the old inode may be freed cleanly if
+ required. Need to sleep before remounting root read-only */
+ kill (1, SIGQUIT);
+
+ sleep (1); /* Time for processes to die and close files */
+ syncwait (2);
+
+ /* remove swap files and partitions using swapoff */
+ swap_off();
+
+ /* unmount disks... */
+ unmount_disks();
+ syncwait (1);
+
+ if(opt_reboot) {
+ my_reboot(LINUX_REBOOT_CMD_RESTART); /* RB_AUTOBOOT */
+ my_puts(_("\nWhy am I still alive after reboot?"));
+ } else {
+ my_puts(_("\nNow you can turn off the power..."));
+
+ /* allow C-A-D now, faith@cs.unc.edu, re-fixed 8-Jul-96 */
+ my_reboot(LINUX_REBOOT_CMD_CAD_ON); /* RB_ENABLE_CAD */
+ sleep (1); /* Wait for devices to finish writing to media */
+ do_halt(halt_action);
+ }
+ /* NOTREACHED */
+ exit(EXIT_SUCCESS); /* to quiet gcc */
+}
+
+/*** end of main() ***/
+
+void
+do_halt(char *action) {
+ if (strcasecmp (action, "power_off") == 0) {
+ printf(_("Calling kernel power-off facility...\n"));
+ fflush(stdout);
+ my_reboot(LINUX_REBOOT_CMD_POWER_OFF);
+ printf(_("Error powering off\t%s\n"), ERRSTRING);
+ fflush(stdout);
+ sleep (2);
+ } else
+
+ /* This should be improved; e.g. Mike Jagdis wants "/sbin/mdstop -a" */
+ /* Maybe we should also fork and wait */
+ if (action[0] == '/') {
+ printf(_("Executing the program \"%s\" ...\n"), action);
+ fflush(stdout);
+ execl(action, action, NULL);
+ printf(_("Error executing\t%s\n"), ERRSTRING);
+ fflush(stdout);
+ sleep (2);
+ }
+
+ my_reboot(LINUX_REBOOT_CMD_HALT); /* RB_HALT_SYSTEM */
+}
+
+void
+write_user(struct utmp *ut)
+{
+ int fd;
+ int minutes, hours;
+ char term[40] = {'/','d','e','v','/',0};
+ char msg[100];
+
+ minutes = timeout / 60;
+ hours = minutes / 60;
+ minutes %= 60;
+
+ (void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
+
+ /* try not to get stuck on a mangled ut_line entry... */
+ if((fd = open(term, O_WRONLY|O_NONBLOCK)) < 0)
+ return;
+
+ msg[0] = '\007'; /* gettext crashes on \a */
+ sprintf(msg+1, _("URGENT: broadcast message from %s:"), whom);
+ WRCRLF;
+ WR(msg);
+ WRCRLF;
+
+ if (hours > 1)
+ sprintf(msg, _("System going down in %d hours %d minutes"),
+ hours, minutes);
+ else if (hours == 1)
+ sprintf(msg, _("System going down in 1 hour %d minutes"),
+ minutes);
+ else if (minutes > 1)
+ sprintf(msg, _("System going down in %d minutes\n"),
+ minutes);
+ else if (minutes == 1)
+ sprintf(msg, _("System going down in 1 minute\n"));
+ else
+ sprintf(msg, _("System going down IMMEDIATELY!\n"));
+
+ WR(msg);
+ WRCRLF;
+
+ sprintf(msg, _("\t... %s ...\n"), message);
+ WR(msg);
+ WRCRLF;
+
+ close(fd);
+}
+
+void
+wall(void)
+{
+ /* write to all users, that the system is going down. */
+ struct utmp *ut;
+
+ utmpname(_PATH_UTMP);
+ setutent();
+
+ while((ut = getutent())) {
+ if(ut->ut_type == USER_PROCESS)
+ write_user(ut);
+ }
+ endutent();
+}
+
+void
+write_wtmp(void)
+{
+ /* write in wtmp that we are dying */
+ int fd;
+ struct utmp ut;
+
+ memset((char *)&ut, 0, sizeof(ut));
+ strcpy(ut.ut_line, "~");
+ memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
+
+ time(&ut.ut_time);
+ ut.ut_type = BOOT_TIME;
+
+ if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0644)) >= 0) {
+ write(fd, (char *)&ut, sizeof(ut));
+ close(fd);
+ }
+}
+
+void
+swap_off(void)
+{
+ /* swapoff esp. swap FILES so the underlying partition can be
+ unmounted. It you don't have swapoff(1) or use mount to
+ add swapspace, this may not be necessary, but I guess it
+ won't hurt */
+
+ int pid;
+ int result;
+ int status;
+
+ sync();
+ if ((pid = fork()) < 0) {
+ my_puts(_("Cannot fork for swapoff. Shrug!"));
+ return;
+ }
+ if (!pid) {
+ execl("/sbin/swapoff", SWAPOFF_ARGS, NULL);
+ execl("/etc/swapoff", SWAPOFF_ARGS, NULL);
+ execl("/bin/swapoff", SWAPOFF_ARGS, NULL);
+ execlp("swapoff", SWAPOFF_ARGS, NULL);
+ my_puts(_("Cannot exec swapoff, "
+ "hoping umount will do the trick."));
+ exit(EXIT_SUCCESS);
+ }
+ while ((result = wait(&status)) != -1 && result != pid)
+ ;
+}
+
+void
+unmount_disks(void)
+{
+ /* better to use umount directly because it may be smarter than us */
+
+ int pid;
+ int result;
+ int status;
+
+ sync();
+ if ((pid = fork()) < 0) {
+ my_puts(_("Cannot fork for umount, trying manually."));
+ unmount_disks_ourselves();
+ return;
+ }
+ if (!pid) {
+ execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
+
+ /* need my_printf instead of my_puts here */
+ freopen(_PATH_CONSOLE, "w", stdout);
+ printf(_("Cannot exec %s, trying umount.\n"), _PATH_UMOUNT);
+ fflush(stdout);
+
+ execlp("umount", UMOUNT_ARGS, NULL);
+ my_puts(_("Cannot exec umount, giving up on umount."));
+ exit(EXIT_SUCCESS);
+ }
+ while ((result = wait(&status)) != -1 && result != pid)
+ ;
+ my_puts(_("Unmounting any remaining filesystems..."));
+ unmount_disks_ourselves();
+}
+
+void
+unmount_disks_ourselves(void)
+{
+ /* unmount all disks */
+
+ FILE *mtab;
+ struct mntent *mnt;
+ char *mntlist[128];
+ int i;
+ int n;
+ char *filesys;
+
+ sync();
+ if (!(mtab = setmntent(_PATH_MOUNTED, "r"))) {
+ my_puts("shutdown: Cannot open " _PATH_MOUNTED ".");
+ return;
+ }
+ n = 0;
+ while (n < 100 && (mnt = getmntent(mtab))) {
+ /*
+ * Neil Phillips: trying to unmount temporary / kernel
+ * filesystems is pointless and may cause error messages;
+ * /dev can be a ramfs managed by udev.
+ */
+ if (strcmp(mnt->mnt_type, "devfs") == 0 ||
+ strcmp(mnt->mnt_type, "proc") == 0 ||
+ strcmp(mnt->mnt_type, "sysfs") == 0 ||
+ strcmp(mnt->mnt_type, "ramfs") == 0 ||
+ strcmp(mnt->mnt_type, "tmpfs") == 0 ||
+ strcmp(mnt->mnt_type, "devpts") == 0)
+ continue;
+ mntlist[n++] = strdup(mnt->mnt_dir);
+ }
+ endmntent(mtab);
+
+ /* we are careful to do this in reverse order of the mtab file */
+
+ for (i = n - 1; i >= 0; i--) {
+ filesys = mntlist[i];
+#ifdef DEBUGGING
+ printf("umount %s\n", filesys);
+#else
+ if (umount(mntlist[i]) < 0)
+ printf(_("shutdown: Couldn't umount %s: %s\n"),
+ filesys, ERRSTRING);
+#endif
+ }
+}
+
+static void kill_mortals (int sig)
+{
+ int npids = 0;
+ int index = 0;
+ int pid;
+ struct stat statbuf;
+ DIR *dp;
+ struct dirent *de;
+ pid_t *pids = NULL;
+ char path[256];
+
+ if ( ( dp = opendir ("/proc") ) == NULL ) return;
+ while ( ( de = readdir (dp) ) != NULL )
+ {
+ if ( !isdigit (de->d_name[0]) ) continue;
+ pid = atoi (de->d_name);
+ sprintf (path, "/proc/%d", pid);
+ if (stat (path, &statbuf) != 0) continue;
+ if (statbuf.st_uid < 100) continue;
+ if (index <= npids)
+ {
+ pids = realloc (pids, npids + 16384);
+ if (pids == NULL) return;
+ npids += 16384;
+ }
+ pids[index++] = pid;
+ }
+ fputs ("Sending SIGTERM to mortals...", stderr);
+ for (--index; index >= 0; --index) kill (pids[index], sig);
+ free (pids);
+ closedir (dp);
+} /* End Function kill_mortals */
+
+static void stop_finalprog (void)
+{
+ char *p1, *p2;
+ FILE *fp;
+ char line[256];
+
+ if ( ( fp = fopen (_PATH_INITTAB, "r") ) == NULL ) return;
+ while (fgets (line, 256, fp) != NULL)
+ {
+ pid_t pid;
+
+ line[strlen (line) - 1] = '\0';
+ p1 = line;
+ while ( isspace (*p1) ) ++p1;
+ if (strncmp (p1, "finalprog", 9) != 0) continue;
+ if ( ( p1 = strchr (p1 + 9, '=') ) == NULL ) continue;
+ for (++p1; isspace (*p1); ++p1);
+ if (*p1 == '\0') continue;
+ for (p2 = p1; !isspace (*p2); ++p2);
+ *p2 = '\0';
+ switch ( pid = fork () )
+ {
+ case 0: /* Child */
+ execl (p1, p1, "stop", NULL);
+ break;
+ case -1: /* Error */
+ break;
+ default: /* Parent */
+ waitpid (pid, NULL, 0);
+ break;
+ }
+ fclose (fp);
+ return;
+ }
+ fclose (fp);
+} /* End Function stop_finalprog */
+
+static void syncwait (int timeval)
+{
+ static int do_wait = 0;
+ static int first_time = 1;
+
+ sync ();
+ /* Kernel version 1.3.20 and after are supposed to wait automatically */
+ if (first_time)
+ {
+ struct utsname uts;
+
+ first_time = 0;
+ uname (&uts);
+ if (uts.release[0] < '2') do_wait = 1;
+ }
+ if (do_wait) sleep (timeval);
+} /* End Function syncwait */