Upload Tizen:Base source
[framework/base/util-linux-ng.git] / login-utils / shutdown.c
1 /* shutdown.c - shutdown a Linux system
2  * Initially written by poe@daimi.aau.dk 
3  * Currently maintained at ftp://ftp.daimi.aau.dk/pub/Software/Linux/
4  */
5
6 /*
7  * Modified by jrs@world.std.com to try to exec "umount -a" and if
8  * that doesn't work, then umount filesystems ourselves in reverse
9  * order.  The old-way was in forward order.  Also if the device
10  * field of the mtab does not start with a "/" then give umount
11  * the mount point instead.  This is needed for the nfs and proc
12  * filesystems and yet is compatible with older systems.
13  *
14  * We also use the mntent library interface to read the mtab file
15  * instead of trying to parse it directly and no longer give a
16  * warning about not being able to umount the root.
17  *
18  * The reason "umount -a" should be tried first is because it may do
19  * special processing for some filesystems (such as informing an
20  * nfs server about nfs umounts) that we don't want to cope with here.
21  */
22
23 /*
24  * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8)
25  * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18.
26  * (I butchered Scotts patches somewhat. - poe)
27  *
28  * Changes by Richard Gooch <rgooch@atnf.csiro.au> (butchered by aeb)
29  * introducing shutdown.conf.
30  *
31  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
32  * - added Native Language Support
33  *
34  * 2000-03-02 Richard Gooch <rgooch@atnf.csiro.au>
35  * - pause forever if (pid == 1) and send SIGQUIT to pid = 1
36  *
37  * 2000-11-04 Richard Gooch <rgooch@atnf.csiro.au>
38  * - continue reaping if (pid == 1)
39  *
40  * 2000-11-06 Richard Gooch <rgooch@atnf.csiro.au>
41  * - shut down "finalprog" from /etc/inittab
42  * - kill normal user (non-root and non-daemon) processes first with SIGTERM
43  *
44  * 2000-11-08 Richard Gooch <rgooch@atnf.csiro.au>
45  * - rollback services
46  * - do not unmount devfs (otherwise get harmless but annoying messages)
47  * - created syncwait() for faster shutting down
48  * - kill getty processes
49  * 2001-05-12 Richard Gooch <rgooch@atnf.csiro.au>
50  * - unblock all signals (sigmask from simpleinit(8) stopped sleep(3))
51  * - close all files
52  */
53
54 #include <stdio.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57 #include <stdlib.h>
58 #include <utmp.h>
59 #include <time.h>
60 #include <string.h>
61 #include <ctype.h>
62 #include <signal.h>
63 #include <errno.h>
64 #include <sys/param.h>
65 #include <termios.h>
66 #include <mntent.h>
67 #include <sys/mount.h>
68 #include <sys/wait.h>
69 #include <syslog.h>
70 #include <sys/resource.h>
71 #include <sys/types.h>
72 #include <dirent.h>
73 #include <sys/stat.h>
74 #include <sys/utsname.h>
75 #include "linux_reboot.h"
76 #include "pathnames.h"
77 #include "xstrncpy.h"
78 #include "nls.h"
79
80 static void usage(void), int_handler(int), write_user(struct utmp *);
81 static void wall(void), write_wtmp(void), unmount_disks(void);
82 static void unmount_disks_ourselves(void);
83 static void swap_off(void), do_halt(char *);
84 static void kill_mortals (int sig);
85 static void stop_finalprog (void);
86 static void syncwait (int timeval);
87
88
89 char    *prog;          /* name of the program */
90 int     opt_reboot;     /* true if -r option or reboot command */
91 int     timeout;        /* number of seconds to shutdown */
92 int     opt_quiet;      /* true if no message is wanted */
93 int     opt_fast;       /* true if fast boot */
94 char    message[90];    /* reason for shutdown if any... */
95 int     opt_single = 0; /* true is we want to boot singleuser */
96 char    *whom;          /* who is shutting the system down */
97 int     opt_msgset = 0; /* message set on command line */
98                         /* change 1 to 0 if no file is to be used by default */
99 int     opt_use_config_file = 1;        /* read _PATH_SHUTDOWN_CONF */
100 char    halt_action[256];               /* to find out what to do upon halt */
101
102 /* #define DEBUGGING */
103
104 #define WR(s) write(fd, s, strlen(s))
105 #define WRCRLF  write(fd, "\r\n", 2)
106 #define ERRSTRING strerror(errno)
107
108 #define UMOUNT_ARGS             "umount", "-a", "-t", "nodevfs"
109 #define SWAPOFF_ARGS            "swapoff", "-a"
110
111 void
112 usage(void)
113 {
114         fprintf(stderr,
115                 _("Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n"));
116         exit(1);
117 }
118
119 static void
120 my_puts(char *s)
121 {
122         /* Use a fresh stdout after forking */
123         freopen(_PATH_CONSOLE, "w", stdout);
124         puts(s);
125         fflush(stdout);
126 }
127
128 void 
129 int_handler(int sig)
130 {
131         unlink(_PATH_NOLOGIN);
132         signal(SIGINT, SIG_DFL);
133         my_puts(_("Shutdown process aborted"));
134         exit(1);
135 }
136
137 static int
138 iswhitespace(int a) {
139         return (a == ' ' || a == '\t');
140 }
141
142 int
143 main(int argc, char *argv[])
144 {
145         int c, i, fd;
146         char *ptr;
147
148         i = getdtablesize ();
149         for (fd = 3; fd < i; fd++) close (fd);
150         if (getpid () == 1)
151         {
152             for (fd = 0; fd < 3; fd++) close (fd);
153             while (1) wait (NULL);  /*  Grim reaper never stops  */
154         }
155         sigsetmask (0); /*  simpleinit(8) blocks all signals: undo for ALRM  */
156         for (i = 1; i < NSIG; i++) signal (i, SIG_DFL);
157
158         setlocale(LC_ALL, "");
159         bindtextdomain(PACKAGE, LOCALEDIR);
160         textdomain(PACKAGE);
161
162 #ifndef DEBUGGING
163         if(setreuid (0, 0)) {
164                 fprintf(stderr, _("%s: Only root can shut a system down.\n"),
165                         argv[0]);
166                 exit(1);
167         }
168 #endif
169
170         if(*argv[0] == '-') argv[0]++;  /* allow shutdown as login shell */
171         prog = argv[0];
172         if((ptr = strrchr(argv[0], '/'))) prog = ++ptr;
173
174         /* All names (halt, reboot, fasthalt, fastboot, shutdown)
175            refer to the same program with the same options,
176            only the defaults differ. */
177         if(!strcmp("halt", prog)) {
178                 opt_reboot = 0;
179                 opt_quiet = 1;
180                 opt_fast = 0;
181                 timeout = 0;
182         } else if(!strcmp("fasthalt", prog)) {
183                 opt_reboot = 0;
184                 opt_quiet = 1;
185                 opt_fast = 1;
186                 timeout = 0;
187         } else if(!strcmp("reboot", prog)) {
188                 opt_reboot = 1;
189                 opt_quiet = 1;
190                 opt_fast = 0;
191                 timeout = 0;
192         } else if(!strcmp("fastboot", prog)) {
193                 opt_reboot = 1;
194                 opt_quiet = 1;
195                 opt_fast = 1;
196                 timeout = 0;
197         } else {
198                 /* defaults */
199                 opt_reboot = 0;
200                 opt_quiet = 0;
201                 opt_fast = 0;
202                 timeout = 2*60;
203         }
204                 
205         c = 0;
206         while(++c < argc) {
207                 if(argv[c][0] == '-') {
208                         for(i = 1; argv[c][i]; i++) {
209                                 switch(argv[c][i]) {
210                                 case 'C':
211                                         opt_use_config_file = 1;
212                                         break;
213                                 case 'h': 
214                                         opt_reboot = 0;
215                                         break;
216                                 case 'r':
217                                         opt_reboot = 1;
218                                         break;
219                                 case 'f':
220                                         opt_fast = 1;
221                                         break;
222                                 case 'q':
223                                         opt_quiet = 1;
224                                         break;
225                                 case 's':
226                                         opt_single = 1;
227                                         break;
228                                     
229                                 default:
230                                         usage();
231                                 }
232                         }
233                 } else if(!strcmp("now", argv[c])) {
234                         timeout = 0;
235                 } else if(argv[c][0] == '+') {
236                         timeout = 60 * atoi(&argv[c][1]);
237                 } else if (isdigit(argv[c][0])) {
238                         char *colon;
239                         int hour = 0;
240                         int minute = 0;
241                         time_t tics;
242                         struct tm *tt;
243                         int now, then;
244                                 
245                         if((colon = strchr(argv[c], ':'))) {
246                                 *colon = '\0';
247                                 hour = atoi(argv[c]);
248                                 minute = atoi(++colon);
249                         } else usage();
250                                 
251                         (void) time(&tics);
252                         tt = localtime(&tics);
253                                 
254                         now = 3600 * tt->tm_hour + 60 * tt->tm_min;
255                         then = 3600 * hour + 60 * minute;
256                         timeout = then - now;
257                         if(timeout < 0) {
258                                 fprintf(stderr, _("That must be tomorrow, "
259                                                   "can't you wait till then?\n"));
260                                 exit(1);
261                         }
262                 } else {
263                         xstrncpy(message, argv[c], sizeof(message));
264                         opt_msgset = 1;
265                 }
266         }
267
268         halt_action[0] = 0;
269
270         /* No doubt we shall want to extend this some day
271            and register a series of commands to be executed
272            at various points during the shutdown sequence,
273            and to define the number of milliseconds to sleep, etc. */
274         if (opt_use_config_file) {
275                 char line[256], *p;
276                 FILE *fp;
277
278                 /*  Read and parse the config file */
279                 halt_action[0] = '\0';
280                 if ((fp = fopen (_PATH_SHUTDOWN_CONF, "r")) != NULL) {
281                         if (fgets (line, sizeof(line), fp) != NULL &&
282                             strncasecmp (line, "HALT_ACTION", 11) == 0 &&
283                             iswhitespace(line[11])) {
284                                 p = strchr(line, '\n');
285                                 if (p)
286                                         *p = 0;         /* strip final '\n' */
287                                 p = line+11;
288                                 while(iswhitespace(*p))
289                                         p++;
290                                 strcpy(halt_action, p);
291                         }
292                         fclose (fp);
293                 }
294         }
295
296         if(!opt_quiet && !opt_msgset) {
297                 /* now ask for message, gets() is insecure */
298                 int cnt = sizeof(message)-1;
299                 char *ptr;
300                 
301                 printf("Why? "); fflush(stdout);
302                 
303                 ptr = message;
304                 while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') { 
305                         ptr++;
306                 }
307                 *ptr = '\0';
308         } else if (!opt_msgset) {
309                 strcpy(message, _("for maintenance; bounce, bounce"));
310         }
311
312 #ifdef DEBUGGING
313         printf("timeout = %d, quiet = %d, reboot = %d\n",
314                 timeout, opt_quiet, opt_reboot);
315 #endif
316         
317         /* so much for option-processing, now begin termination... */
318         if(!(whom = getlogin()) || !*whom) whom = "ghost";
319         if(strlen(whom) > 40) whom[40] = 0; /* see write_user() */
320
321         setpriority(PRIO_PROCESS, 0, PRIO_MIN);
322         signal(SIGINT,  int_handler);
323         signal(SIGHUP,  int_handler);
324         signal(SIGQUIT, int_handler);
325         signal(SIGTERM, int_handler);
326
327         chdir("/");
328
329         if(timeout > 5*60) {
330                 sleep(timeout - 5*60);
331                 timeout = 5*60;
332         }
333
334         
335         if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT, 0644)) >= 0) {
336                 /* keep xgettext happy and leave \r\n outside strings */
337                 WRCRLF;
338                 WR(_("The system is being shut down within 5 minutes"));
339                 WRCRLF;
340                 write(fd, message, strlen(message));
341                 WRCRLF;
342                 WR(_("Login is therefore prohibited."));
343                 WRCRLF;
344                 close(fd);
345         }
346         
347         signal(SIGPIPE, SIG_IGN);
348
349         if(timeout > 0) {
350                 wall();
351                 sleep(timeout);
352         }
353
354         timeout = 0;
355         wall();
356         sleep(3);
357
358         /* now there's no turning back... */
359         signal(SIGINT,  SIG_IGN);
360
361         /* do syslog message... */
362         openlog(prog, LOG_CONS, LOG_AUTH);
363         if (opt_reboot)
364                 syslog(LOG_NOTICE, _("rebooted by %s: %s"), 
365                        whom, message);
366         else
367                 syslog(LOG_NOTICE, _("halted by %s: %s"), 
368                        whom, message);
369         closelog();
370
371         if(opt_fast)
372                 if((fd = open("/fastboot", O_WRONLY|O_CREAT, 0644)) >= 0)
373                         close(fd);
374
375         kill(1, SIGTSTP);       /* tell init not to spawn more getty's */
376         write_wtmp();
377         if(opt_single)
378                 if((fd = open(_PATH_SINGLE, O_CREAT|O_WRONLY, 0644)) >= 0)
379                         close(fd);
380                 
381         sync();
382
383         signal(SIGTERM, SIG_IGN);
384         if(fork() > 0) sleep(1000); /* the parent will die soon... */
385         setpgrp();              /* so the shell wont kill us in the fall */
386
387 #ifndef DEBUGGING
388         /* a gentle kill of all other processes except init */
389         kill_mortals (SIGTERM);
390         for (fd = 0; fd < 3; fd++) close (fd);
391         stop_finalprog ();
392         sleep (1);                    /*  Time for saves to start           */
393         kill (1, SIGTERM);            /*  Tell init to kill spawned gettys  */
394         usleep (100000);              /*  Wait for gettys to die            */
395         my_puts ("");                 /*  Get past the login prompt         */
396         system ("/sbin/initctl -r");  /*  Roll back services                */
397         syncwait (1);
398         my_puts ("Sending SIGTERM to all remaining processes...");
399         kill (-1, SIGTERM);
400         sleep (2);                    /*  Default 2, some people need 5     */
401
402         kill (-1, SIGKILL);           /*  Now use brute force...            */
403
404         /* turn off accounting */
405         acct(NULL);
406 #endif
407         /* RedHat and SuSE like to remove /etc/nologin.
408            Perhaps the usual sequence is
409               touch nologin; shutdown -h; fiddle with hardware;
410               boot; fiddle with software; rm nologin
411            and removing it here will be counterproductive.
412            Let us see whether people complain. */
413         unlink(_PATH_NOLOGIN);
414
415         /*  Tell init(8) to exec so that the old inode may be freed cleanly if
416             required. Need to sleep before remounting root read-only  */
417         kill (1, SIGQUIT);
418
419         sleep (1);      /* Time for processes to die and close files */
420         syncwait (2);
421
422         /* remove swap files and partitions using swapoff */
423         swap_off();
424
425         /* unmount disks... */
426         unmount_disks();
427         syncwait (1);
428
429         if(opt_reboot) {
430                 my_reboot(LINUX_REBOOT_CMD_RESTART); /* RB_AUTOBOOT */
431                 my_puts(_("\nWhy am I still alive after reboot?"));
432         } else {
433                 my_puts(_("\nNow you can turn off the power..."));
434
435                 /* allow C-A-D now, faith@cs.unc.edu, re-fixed 8-Jul-96 */
436                 my_reboot(LINUX_REBOOT_CMD_CAD_ON); /* RB_ENABLE_CAD */
437                 sleep (1);  /*  Wait for devices to finish writing to media  */
438                 do_halt(halt_action);
439         }
440         /* NOTREACHED */
441         exit(0); /* to quiet gcc */
442 }
443
444 /*** end of main() ***/
445
446 void
447 do_halt(char *action) {
448         if (strcasecmp (action, "power_off") == 0) {
449                 printf(_("Calling kernel power-off facility...\n"));
450                 fflush(stdout);
451                 my_reboot(LINUX_REBOOT_CMD_POWER_OFF);
452                 printf(_("Error powering off\t%s\n"), ERRSTRING);
453                 fflush(stdout);
454                 sleep (2);
455         } else
456
457         /* This should be improved; e.g. Mike Jagdis wants "/sbin/mdstop -a" */
458         /* Maybe we should also fork and wait */
459         if (action[0] == '/') {
460                 printf(_("Executing the program \"%s\" ...\n"), action);
461                 fflush(stdout);
462                 execl(action, action, NULL);
463                 printf(_("Error executing\t%s\n"), ERRSTRING);
464                 fflush(stdout);
465                 sleep (2);
466         }
467
468         my_reboot(LINUX_REBOOT_CMD_HALT); /* RB_HALT_SYSTEM */
469 }
470
471 void
472 write_user(struct utmp *ut)
473 {
474         int fd;
475         int minutes, hours;
476         char term[40] = {'/','d','e','v','/',0};
477         char msg[100];
478
479         minutes = timeout / 60;
480         hours = minutes / 60;
481         minutes %= 60;
482
483         (void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
484
485         /* try not to get stuck on a mangled ut_line entry... */
486         if((fd = open(term, O_WRONLY|O_NONBLOCK)) < 0)
487                 return;
488
489         msg[0] = '\007';        /* gettext crashes on \a */
490         sprintf(msg+1, _("URGENT: broadcast message from %s:"), whom);
491         WRCRLF;
492         WR(msg);
493         WRCRLF;
494
495         if (hours > 1)
496                 sprintf(msg, _("System going down in %d hours %d minutes"),
497                         hours, minutes);
498         else if (hours == 1)
499                 sprintf(msg, _("System going down in 1 hour %d minutes"),
500                         minutes);
501         else if (minutes > 1)
502                 sprintf(msg, _("System going down in %d minutes\n"),
503                         minutes);
504         else if (minutes == 1)
505                 sprintf(msg, _("System going down in 1 minute\n"));
506         else
507                 sprintf(msg, _("System going down IMMEDIATELY!\n"));
508
509         WR(msg);
510         WRCRLF;
511
512         sprintf(msg, _("\t... %s ...\n"), message);
513         WR(msg);
514         WRCRLF;
515
516         close(fd);
517 }
518
519 void
520 wall(void)
521 {
522         /* write to all users, that the system is going down. */
523         struct utmp *ut;
524                 
525         utmpname(_PATH_UTMP);
526         setutent();
527         
528         while((ut = getutent())) {
529                 if(ut->ut_type == USER_PROCESS)
530                         write_user(ut);
531         }
532         endutent();
533 }
534
535 void
536 write_wtmp(void)
537 {
538         /* write in wtmp that we are dying */
539         int fd;
540         struct utmp ut;
541         
542         memset((char *)&ut, 0, sizeof(ut));
543         strcpy(ut.ut_line, "~");
544         memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
545
546         time(&ut.ut_time);
547         ut.ut_type = BOOT_TIME;
548         
549         if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0644)) >= 0) {
550                 write(fd, (char *)&ut, sizeof(ut));
551                 close(fd);
552         }
553 }
554
555 void
556 swap_off(void)
557 {
558         /* swapoff esp. swap FILES so the underlying partition can be
559            unmounted. It you don't have swapoff(1) or use mount to
560            add swapspace, this may not be necessary, but I guess it
561            won't hurt */
562
563         int pid;
564         int result;
565         int status;
566
567         sync();
568         if ((pid = fork()) < 0) {
569                 my_puts(_("Cannot fork for swapoff. Shrug!"));
570                 return;
571         }
572         if (!pid) {
573                 execl("/sbin/swapoff", SWAPOFF_ARGS, NULL);
574                 execl("/etc/swapoff", SWAPOFF_ARGS, NULL);
575                 execl("/bin/swapoff", SWAPOFF_ARGS, NULL);
576                 execlp("swapoff", SWAPOFF_ARGS, NULL);
577                 my_puts(_("Cannot exec swapoff, "
578                           "hoping umount will do the trick."));
579                 exit(0);
580         }
581         while ((result = wait(&status)) != -1 && result != pid)
582                 ;
583 }
584
585 void
586 unmount_disks(void)
587 {
588         /* better to use umount directly because it may be smarter than us */
589
590         int pid;
591         int result;
592         int status;
593
594         sync();
595         if ((pid = fork()) < 0) {
596                 my_puts(_("Cannot fork for umount, trying manually."));
597                 unmount_disks_ourselves();
598                 return;
599         }
600         if (!pid) {
601                 execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
602
603                 /* need my_printf instead of my_puts here */
604                 freopen(_PATH_CONSOLE, "w", stdout);
605                 printf(_("Cannot exec %s, trying umount.\n"), _PATH_UMOUNT);
606                 fflush(stdout);
607
608                 execlp("umount", UMOUNT_ARGS, NULL);
609                 my_puts(_("Cannot exec umount, giving up on umount."));
610                 exit(0);
611         }
612         while ((result = wait(&status)) != -1 && result != pid)
613                 ;
614         my_puts(_("Unmounting any remaining filesystems..."));
615         unmount_disks_ourselves();
616 }
617
618 void
619 unmount_disks_ourselves(void)
620 {
621         /* unmount all disks */
622
623         FILE *mtab;
624         struct mntent *mnt;
625         char *mntlist[128];
626         int i;
627         int n;
628         char *filesys;
629         
630         sync();
631         if (!(mtab = setmntent(_PATH_MOUNTED, "r"))) {
632                 my_puts("shutdown: Cannot open " _PATH_MOUNTED ".");
633                 return;
634         }
635         n = 0;
636         while (n < 100 && (mnt = getmntent(mtab))) {
637                 /*
638                  * Neil Phillips: trying to unmount temporary / kernel
639                  * filesystems is pointless and may cause error messages;
640                  * /dev can be a ramfs managed by udev.
641                  */
642                 if (strcmp(mnt->mnt_type, "devfs") == 0 ||
643                     strcmp(mnt->mnt_type, "proc") == 0 ||
644                     strcmp(mnt->mnt_type, "sysfs") == 0 ||
645                     strcmp(mnt->mnt_type, "ramfs") == 0 ||
646                     strcmp(mnt->mnt_type, "tmpfs") == 0 ||
647                     strcmp(mnt->mnt_type, "devpts") == 0)
648                         continue;
649                 mntlist[n++] = strdup(mnt->mnt_dir);
650         }
651         endmntent(mtab);
652
653         /* we are careful to do this in reverse order of the mtab file */
654
655         for (i = n - 1; i >= 0; i--) {
656                 filesys = mntlist[i];
657 #ifdef DEBUGGING
658                 printf("umount %s\n", filesys);
659 #else
660                 if (umount(mntlist[i]) < 0)
661                         printf(_("shutdown: Couldn't umount %s: %s\n"),
662                                filesys, ERRSTRING);
663 #endif
664         }
665 }
666
667 static void kill_mortals (int sig)
668 {
669     int npids = 0;
670     int index = 0;
671     int pid;
672     struct stat statbuf;
673     DIR *dp;
674     struct dirent *de;
675     pid_t *pids = NULL;
676     char path[256];
677
678     if ( ( dp = opendir ("/proc") ) == NULL ) return;
679     while ( ( de = readdir (dp) ) != NULL )
680     {
681         if ( !isdigit (de->d_name[0]) ) continue;
682         pid = atoi (de->d_name);
683         sprintf (path, "/proc/%d", pid);
684         if (stat (path, &statbuf) != 0) continue;
685         if (statbuf.st_uid < 100) continue;
686         if (index <= npids)
687         {
688             pids = realloc (pids, npids + 16384);
689             if (pids == NULL) return;
690             npids += 16384;
691         }
692         pids[index++] = pid;
693     }
694     fputs ("Sending SIGTERM to mortals...", stderr);
695     for (--index; index >= 0; --index) kill (pids[index], sig);
696     free (pids);
697     closedir (dp);
698 }   /*  End Function kill_mortals  */
699
700 static void stop_finalprog (void)
701 {
702     char *p1, *p2;
703     FILE *fp;
704     char line[256];
705
706     if ( ( fp = fopen (_PATH_INITTAB, "r") ) == NULL ) return;
707     while (fgets (line, 256, fp) != NULL)
708     {
709         pid_t pid;
710
711         line[strlen (line) - 1] = '\0';
712         p1 = line;
713         while ( isspace (*p1) ) ++p1;
714         if (strncmp (p1, "finalprog", 9) != 0) continue;
715         if ( ( p1 = strchr (p1 + 9, '=') ) == NULL ) continue;
716         for (++p1; isspace (*p1); ++p1);
717         if (*p1 == '\0') continue;
718         for (p2 = p1; !isspace (*p2); ++p2);
719         *p2 = '\0';
720         switch ( pid = fork () )
721         {
722           case 0:   /*  Child   */
723             execl (p1, p1, "stop", NULL);
724             break;
725           case -1:  /*  Error   */
726             break;
727           default:  /*  Parent  */
728             waitpid (pid, NULL, 0);
729             break;
730         }
731         fclose (fp);
732         return;
733     }
734     fclose (fp);
735 }   /*  End Function stop_finalprog  */
736
737 static void syncwait (int timeval)
738 {
739     static int do_wait = 0;
740     static int first_time = 1;
741
742     sync ();
743     /*  Kernel version 1.3.20 and after are supposed to wait automatically  */
744     if (first_time)
745     {
746         struct utsname uts;
747
748         first_time = 0;
749         uname (&uts);
750         if (uts.release[0] < '2') do_wait = 1;
751     }
752     if (do_wait) sleep (timeval);
753 }   /*  End Function syncwait  */