2 shutdown - close down the system graciously
4 Author: Edvard Tuinder <v892231@si.hhs.NL>
6 This program informs the users that the system is going
7 down, when and why. After that a shutdown notice is written in
8 both /usr/adm/wtmp and by syslog(3). Then reboot(2) is called
9 to really close the system.
11 This actually is a ``nice'' halt(8).
13 Options are supposed to be as with BSD
14 -h: shutdown and halt the system
15 -r: shutdown and reboot
16 -k: stop an already running shutdown
17 -o: obsolete: not implemented
20 -C: crash check, i.e. is the last wtmp entry a shutdown entry?
21 -x: let the monitor execute the given code
25 #define _POSIX_SOURCE 1
26 #include <sys/types.h>
40 static char WTMP
[] = "/usr/adm/wtmp";
41 static char SHUT_PID
[] = "/usr/run/shutdown.pid";
42 static char NOLOGIN
[] = "/etc/nologin";
45 #define inform_user_time inf_time
46 #define inform_user inf_user
49 void usage
_ARGS(( void ));
50 void write_pid
_ARGS(( void ));
51 int inform_user_time
_ARGS(( void ));
52 void inform_user
_ARGS(( void ));
53 void terminate
_ARGS(( void ));
54 void wall
_ARGS(( char *when
, char *extra
));
55 int crash_check
_ARGS(( void ));
56 void parse_time
_ARGS(( char *arg
));
57 void get_message
_ARGS(( void ));
58 void main
_ARGS(( int argc
, char *argv
[] ));
59 char *itoa
_ARGS(( int n
));
64 int reboot_flag
='h'; /* default is halt */
65 char *reboot_code
=""; /* optional monitor code */
66 int info_min
, info_hour
;
79 if (p
[0] == '+') { delta
= 1; p
++; }
81 hours
= strtoul(p
, &p
, 10);
82 if (*p
== 0 && delta
) {
86 if (*p
!= ':' && *p
!= '.')
90 minutes
= strtoul(p
, &p
, 10);
94 fprintf(stderr
,"Invalid time specification `%s'\n",arg
);
102 hours
-= tm
->tm_hour
;
103 minutes
-= tm
->tm_min
;
110 if (hours
< 0) hours
+= 24; /* Time after midnight. */
112 tm
->tm_hour
+= hours
;
113 tm
->tm_min
+= minutes
;
115 info_hour
= tm
->tm_hour
;
116 info_min
= tm
->tm_min
;
119 "The system will shutdown in %d hour%s and %d minute%s at %02d:%02d\n\n",
120 hours
,hours
==1?"":"s",minutes
,minutes
==1?"":"s",info_hour
,info_min
);
122 wait_time
+= hours
* 3600 + minutes
* 60;
130 int i
, now
= 0, nologin
= 0, want_terminate
= 0, want_message
= 0, check
= 0;
133 static char HALT1
[] = "-?";
134 static char *HALT
[] = { "shutdown", HALT1
, NULL
, NULL
};
137 for (i
= 1; i
< argc
&& argv
[i
][0] == '-'; i
++) {
138 if (argv
[i
][1] == '-' && argv
[i
][2] == 0) {
143 for (opt
= argv
[i
] + 1; *opt
!= 0; opt
++) {
152 if (reboot_flag
== 'x') {
155 fprintf (stderr
,"shutdown: option '-x' requires an argument\n");
174 fprintf (stderr
,"shutdown: invalid option '-%c'\n",*opt
);
180 if ((argc
- i
) > 2) usage();
182 if (check
) exit(crash_check() ? 0 : 2);
185 /* No timespec, assume "now". */
188 if (!strcmp(argv
[i
], "now"))
194 if ((argc
- i
) == 2) {
195 /* One line message */
196 strcat(message
, argv
[i
+1]);
197 strcat(message
, "\n");
200 if (want_terminate
) terminate();
201 if (want_message
) get_message();
205 prog
= strrchr(*argv
,'/');
206 if (prog
== (char *)0)
217 fprintf(stderr
, "%s: can't fork\n", prog
);
222 /* Detach from the terminal (if any). */
223 if ((tty
= open("/dev/tty", O_RDONLY
)) != -1) {
231 if (wait_time
<= 5 * 60 && !nologin
&& !now
) {
232 close(creat(NOLOGIN
,00644));
235 if (wait_time
<= 60) break;
236 if(inform_user_time())
244 sleep (30); /* Last minute before shutdown */
247 sleep (30); /* Last 30 seconds before shutdown */
252 unlink(SHUT_PID
); /* No way of stopping anymore */
255 HALT
[1][1] = reboot_flag
;
256 if (reboot_flag
== 'x') HALT
[2] = reboot_code
;
258 execv("/usr/sbin/halt", HALT
);
260 execv("/usr/bin/halt", HALT
);
263 fprintf(stderr
, "Can't execute 'halt': %s\n", strerror(errno
));
267 fprintf(stderr
, "Reboot call failed: %s\n", strerror(errno
));
273 fputs("Usage: shutdown [-hrRmk] [-x code] [time [message]]\n", stderr
);
274 fputs(" -h -> halt system after shutdown\n", stderr
);
275 fputs(" -r -> reboot system after shutdown\n", stderr
);
276 fputs(" -R -> reset system after shutdown\n", stderr
);
277 fputs(" -x -> return to the monitor doing...\n", stderr
);
278 fputs(" -m -> read a shutdown message from standard input\n", stderr
);
279 fputs(" -k -> stop an already running shutdown\n", stderr
);
280 fputs(" code -> boot monitor code to be executed\n", stderr
);
281 fputs(" time -> keyword ``now'', minutes before shutdown ``+5'',\n", stderr
);
282 fputs(" or absolute time specification ``11:20''\n", stderr
);
283 fputs(" message -> short shutdown message\n", stderr
);
294 in
= fopen(SHUT_PID
,"r");
295 if (in
== (FILE *)0) {
296 fputs ("Can't get pid of shutdown process, probably not running shutdown\n", stderr
);
302 if (kill(pid
,9) == -1)
303 fputs("Can't kill the shutdown process, probably not running anymore\n",stderr
);
305 puts("Shutdown process terminated");
308 #ifdef not_very_useful
309 in
= fopen (SHUT_LOG
,"a");
312 sprintf (buf
, "Shutdown with pid %d terminated\n",pid
);
324 puts ("Type your message. End with ^D at an empty line");
325 fputs ("shutdown> ",stdout
);fflush(stdout
);
326 while (fgets(line
,80,stdin
) != (char *)0) {
327 strcat (message
,line
);
328 bzero(line
,strlen(line
));
329 fputs ("shutdown> ",stdout
);fflush(stdout
);
331 putc('\n',stdout
);fflush(stdout
);
334 int inform_user_time()
340 if (min
== 60 || min
== 30 || min
== 15 || min
== 10 || min
<= 5)
352 minute
= wait_time
/ 60;
353 while (minute
>= 60) {
360 "\nThe system will shutdown in %d hour%s and %d minute%s at %.02d:%.02d\n\n",
361 hour
,hour
==1?"":"s",minute
,minute
==1?"":"s",info_hour
,info_min
);
365 "\nThe system will shutdown in %d minutes at %.02d:%.02d\n\n",
366 minute
,info_hour
,info_min
);
370 "\nThe system will shutdown in %d seconds\n\n",
374 "\nThe system will shutdown NOW\n\n");
384 fd
= creat(SHUT_PID
,00600);
387 strncpy (pid
,itoa(getpid()), sizeof(pid
));
388 write (fd
,pid
,sizeof(pid
));
399 if (stat(WTMP
, &st
) < 0 || st
.st_size
== 0) return 0;
400 if ((fd
= open(WTMP
, O_RDONLY
)) < 0) return 0;
402 crashed
= (lseek(fd
, - (off_t
) sizeof(last
), SEEK_END
) == -1
403 || read(fd
, (void *) &last
, sizeof(last
)) != sizeof(last
)
404 || last
.ut_line
[0] != '~'
405 || (strncmp(last
.ut_user
, "shutdown", sizeof(last
.ut_user
))
406 && strncmp(last
.ut_user
, "halt", sizeof(last
.ut_user
))));