GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/shutdown/shutdown.c Lines: 0 240 0.0 %
Date: 2016-12-06 Branches: 0 175 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: shutdown.c,v 1.44 2015/11/16 18:35:31 deraadt Exp $	*/
2
/*	$NetBSD: shutdown.c,v 1.9 1995/03/18 15:01:09 cgd Exp $	*/
3
4
/*
5
 * Copyright (c) 1988, 1990, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/types.h>
34
#include <sys/resource.h>
35
#include <sys/syslog.h>
36
#include <sys/types.h>
37
#include <sys/wait.h>
38
39
#include <ctype.h>
40
#include <fcntl.h>
41
#include <sys/termios.h>
42
#include <pwd.h>
43
#include <setjmp.h>
44
#include <signal.h>
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <time.h>
49
#include <unistd.h>
50
#include <limits.h>
51
#include <errno.h>
52
#include <err.h>
53
54
#include "pathnames.h"
55
56
#ifdef DEBUG
57
#undef _PATH_NOLOGIN
58
#define	_PATH_NOLOGIN	"./nologin"
59
#undef _PATH_FASTBOOT
60
#define	_PATH_FASTBOOT	"./fastboot"
61
#endif
62
63
#define	H		*60*60
64
#define	M		*60
65
#define	S		*1
66
#define	NOLOG_TIME	5*60
67
struct interval {
68
	int timeleft, timetowait;
69
} tlist[] = {
70
	{ 10 H,  5 H },
71
	{  5 H,  3 H },
72
	{  2 H,  1 H },
73
	{  1 H, 30 M },
74
	{ 30 M, 10 M },
75
	{ 20 M, 10 M },
76
	{ 10 M,  5 M },
77
	{  5 M,  3 M },
78
	{  2 M,  1 M },
79
	{  1 M, 30 S },
80
	{ 30 S, 30 S },
81
	{    0,    0 }
82
};
83
#undef H
84
#undef M
85
#undef S
86
87
static time_t offset, shuttime;
88
static int dofast, dohalt, doreboot, dopower, dodump, mbuflen, nosync;
89
static sig_atomic_t killflg;
90
static char *whom, mbuf[BUFSIZ];
91
92
void badtime(void);
93
void __dead die_you_gravy_sucking_pig_dog(void);
94
void doitfast(void);
95
void __dead finish(int);
96
void getoffset(char *);
97
void __dead loop(void);
98
void nolog(void);
99
void timeout(int);
100
void timewarn(int);
101
void usage(void);
102
103
int
104
main(int argc, char *argv[])
105
{
106
	int arglen, ch, len, readstdin = 0;
107
	struct passwd *pw;
108
	char *p, *endp;
109
	pid_t forkpid;
110
111
	if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1)
112
		err(1, "pledge");
113
114
#ifndef DEBUG
115
	if (geteuid())
116
		errx(1, "NOT super-user");
117
#endif
118
	while ((ch = getopt(argc, argv, "dfhknpr-")) != -1)
119
		switch (ch) {
120
		case '-':
121
			readstdin = 1;
122
			break;
123
		case 'd':
124
			dodump = 1;
125
			break;
126
		case 'f':
127
			dofast = 1;
128
			break;
129
		case 'h':
130
			dohalt = 1;
131
			break;
132
		case 'k':
133
			killflg = 1;
134
			break;
135
		case 'n':
136
			nosync = 1;
137
			break;
138
		case 'p':
139
			dopower = 1;
140
			break;
141
		case 'r':
142
			doreboot = 1;
143
			break;
144
		default:
145
			usage();
146
		}
147
	argc -= optind;
148
	argv += optind;
149
150
	if (argc < 1)
151
		usage();
152
153
	if (dofast && nosync) {
154
		(void)fprintf(stderr,
155
		    "shutdown: incompatible switches -f and -n.\n");
156
		usage();
157
	}
158
	if (doreboot && dohalt) {
159
		(void)fprintf(stderr,
160
		    "shutdown: incompatible switches -h and -r.\n");
161
		usage();
162
	}
163
	if (doreboot && dopower) {
164
		(void)fprintf(stderr,
165
		    "shutdown: incompatible switches -p and -r.\n");
166
		usage();
167
	}
168
	getoffset(*argv++);
169
170
	if (*argv) {
171
		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
172
			arglen = strlen(*argv);
173
			if ((len -= arglen) <= 2)
174
				break;
175
			if (p != mbuf)
176
				*p++ = ' ';
177
			memcpy(p, *argv, arglen);
178
			p += arglen;
179
		}
180
		*p = '\n';
181
		*++p = '\0';
182
	}
183
184
	if (readstdin) {
185
		p = mbuf;
186
		endp = mbuf + sizeof(mbuf) - 2;
187
		for (;;) {
188
			if (!fgets(p, endp - p + 1, stdin))
189
				break;
190
			for (; *p &&  p < endp; ++p)
191
				;
192
			if (p == endp) {
193
				*p = '\n';
194
				*++p = '\0';
195
				break;
196
			}
197
		}
198
	}
199
	mbuflen = strlen(mbuf);
200
201
	if (offset)
202
		(void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
203
	else
204
		(void)printf("Shutdown NOW!\n");
205
206
	if (!(whom = getlogin()))
207
		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
208
209
#ifdef DEBUG
210
	(void)putc('\n', stdout);
211
#else
212
	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
213
214
	forkpid = fork();
215
	if (forkpid == -1)
216
		err(1, "fork");
217
	if (forkpid) {
218
		(void)printf("shutdown: [pid %ld]\n", (long)forkpid);
219
		exit(0);
220
	}
221
	setsid();
222
#endif
223
	openlog("shutdown", LOG_CONS, LOG_AUTH);
224
	loop();
225
	/* NOTREACHED */
226
}
227
228
void
229
loop(void)
230
{
231
	struct interval *tp;
232
	u_int sltime;
233
	int logged;
234
235
	if (offset <= NOLOG_TIME) {
236
		logged = 1;
237
		nolog();
238
	} else
239
		logged = 0;
240
	tp = tlist;
241
	if (tp->timeleft < offset)
242
		(void)sleep((u_int)(offset - tp->timeleft));
243
	else {
244
		while (offset < tp->timeleft)
245
			++tp;
246
		/*
247
		 * Warn now, if going to sleep more than a fifth of
248
		 * the next wait time.
249
		 */
250
		if ((sltime = offset - tp->timeleft)) {
251
			if (sltime > tp->timetowait / 5)
252
				timewarn(offset);
253
			(void)sleep(sltime);
254
		}
255
	}
256
	for (;; ++tp) {
257
		timewarn(tp->timeleft);
258
		if (!logged && tp->timeleft <= NOLOG_TIME) {
259
			logged = 1;
260
			nolog();
261
		}
262
		(void)sleep((u_int)tp->timetowait);
263
		if (!tp->timeleft)
264
			break;
265
	}
266
	die_you_gravy_sucking_pig_dog();
267
}
268
269
static jmp_buf alarmbuf;
270
271
static char *restricted_environ[] = {
272
	"PATH=" _PATH_STDPATH,
273
	NULL
274
};
275
276
void
277
timewarn(int timeleft)
278
{
279
	static char hostname[HOST_NAME_MAX+1];
280
	char wcmd[PATH_MAX + 4];
281
	extern char **environ;
282
	static int first;
283
	FILE *pf;
284
285
	if (!first++)
286
		(void)gethostname(hostname, sizeof(hostname));
287
288
	/* undoc -n option to wall suppresses normal wall banner */
289
	(void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
290
	environ = restricted_environ;
291
	if (!(pf = popen(wcmd, "w"))) {
292
		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
293
		return;
294
	}
295
296
	(void)fprintf(pf,
297
	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
298
	    timeleft ? "": "FINAL ", whom, hostname);
299
300
	if (timeleft > 10*60)
301
		(void)fprintf(pf, "System going down at %5.5s\n\n",
302
		    ctime(&shuttime) + 11);
303
	else if (timeleft > 59)
304
		(void)fprintf(pf, "System going down in %d minute%s\n\n",
305
		    timeleft / 60, (timeleft > 60) ? "s" : "");
306
	else if (timeleft)
307
		(void)fprintf(pf, "System going down in 30 seconds\n\n");
308
	else
309
		(void)fprintf(pf, "System going down IMMEDIATELY\n\n");
310
311
	if (mbuflen)
312
		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
313
314
	/*
315
	 * play some games, just in case wall doesn't come back
316
	 * probably unnecessary, given that wall is careful.
317
	 */
318
	if (!setjmp(alarmbuf)) {
319
		(void)signal(SIGALRM, timeout);
320
		(void)alarm((u_int)30);
321
		(void)pclose(pf);
322
		(void)alarm((u_int)0);
323
		(void)signal(SIGALRM, SIG_DFL);
324
	}
325
}
326
327
void
328
timeout(int signo)
329
{
330
	longjmp(alarmbuf, 1);		/* XXX signal/longjmp resource leaks */
331
}
332
333
void
334
die_you_gravy_sucking_pig_dog(void)
335
{
336
337
	syslog(LOG_NOTICE, "%s by %s: %s",
338
	    doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" :
339
	    "shutdown", whom, mbuf);
340
	(void)sleep(2);
341
342
	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
343
	if (killflg) {
344
		(void)printf("\rbut you'll have to do it yourself\r\n");
345
		finish(0);
346
	}
347
	if (dofast)
348
		doitfast();
349
350
	if (pledge("stdio rpath wpath cpath tty id proc exec", NULL) == -1)
351
		err(1, "pledge");
352
353
#ifdef DEBUG
354
	if (doreboot)
355
		(void)printf("reboot");
356
	else if (dopower)
357
		(void)printf("power-down");
358
	else if (dohalt)
359
		(void)printf("halt");
360
	if (nosync)
361
		(void)printf(" no sync");
362
	if (dofast)
363
		(void)printf(" no fsck");
364
	if (dodump)
365
		(void)printf(" with dump");
366
	(void)printf("\nkill -HUP 1\n");
367
#else
368
	if (dohalt || dopower || doreboot) {
369
		char *args[10];
370
		char **arg, *path;
371
372
		if (pledge("stdio exec rpath cpath wpath", NULL) == -1)
373
			err(1, "pledge");
374
375
		arg = &args[0];
376
		if (doreboot) {
377
			path = _PATH_REBOOT;
378
			*arg++ = "reboot";
379
		} else {
380
			path = _PATH_HALT;
381
			*arg++ = "halt";
382
		}
383
		*arg++ = "-l";
384
		if (dopower)
385
			*arg++ = "-p";
386
		if (nosync)
387
			*arg++ = "-n";
388
		if (dodump)
389
			*arg++ = "-d";
390
		*arg++ = NULL;
391
		execve(path, args, NULL);
392
		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path);
393
		warn(path);
394
	}
395
	if (access(_PATH_RC, R_OK) != -1) {
396
		pid_t pid;
397
		struct termios t;
398
		int fd;
399
400
		switch ((pid = fork())) {
401
		case -1:
402
			break;
403
		case 0:
404
			if (revoke(_PATH_CONSOLE) == -1)
405
				perror("revoke");
406
			if (setsid() == -1)
407
				perror("setsid");
408
			fd = open(_PATH_CONSOLE, O_RDWR);
409
			if (fd == -1)
410
				perror("open");
411
			dup2(fd, 0);
412
			dup2(fd, 1);
413
			dup2(fd, 2);
414
			if (fd > 2)
415
				close(fd);
416
417
			/* At a minimum... */
418
			tcgetattr(0, &t);
419
			t.c_oflag |= (ONLCR | OPOST);
420
			tcsetattr(0, TCSANOW, &t);
421
422
			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
423
			_exit(1);
424
		default:
425
			waitpid(pid, NULL, 0);
426
		}
427
	}
428
	(void)kill(1, SIGTERM);		/* to single user */
429
#endif
430
	finish(0);
431
}
432
433
#define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
434
435
void
436
getoffset(char *timearg)
437
{
438
	struct tm *lt;
439
	int this_year;
440
	time_t now;
441
	char *p;
442
443
	if (!strcasecmp(timearg, "now")) {		/* now */
444
		offset = 0;
445
		return;
446
	}
447
448
	(void)time(&now);
449
	if (*timearg == '+') {				/* +minutes */
450
		const char *errstr;
451
452
		offset = strtonum(++timearg, 0, INT_MAX, &errstr);
453
		if (errstr)
454
			badtime();
455
		offset *= 60;
456
		shuttime = now + offset;
457
		return;
458
	}
459
460
	/* handle hh:mm by getting rid of the colon */
461
	for (p = timearg; *p; ++p) {
462
		if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) {
463
			if (*p == ':' && strlen(p) == 3) {
464
				p[0] = p[1];
465
				p[1] = p[2];
466
				p[2] = '\0';
467
			} else
468
				badtime();
469
		}
470
	}
471
472
	unsetenv("TZ");					/* OUR timezone */
473
	lt = localtime(&now);				/* current time val */
474
475
	switch (strlen(timearg)) {
476
	case 10:
477
		this_year = lt->tm_year;
478
		lt->tm_year = ATOI2(timearg);
479
		/*
480
		 * check if the specified year is in the next century.
481
		 * allow for one year of user error as many people will
482
		 * enter n - 1 at the start of year n.
483
		 */
484
		if (lt->tm_year < (this_year % 100) - 1)
485
			lt->tm_year += 100;
486
		/* adjust for the year 2000 and beyond */
487
		lt->tm_year += (this_year - (this_year % 100));
488
		/* FALLTHROUGH */
489
	case 8:
490
		lt->tm_mon = ATOI2(timearg);
491
		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
492
			badtime();
493
		/* FALLTHROUGH */
494
	case 6:
495
		lt->tm_mday = ATOI2(timearg);
496
		if (lt->tm_mday < 1 || lt->tm_mday > 31)
497
			badtime();
498
		/* FALLTHROUGH */
499
	case 4:
500
		lt->tm_hour = ATOI2(timearg);
501
		if (lt->tm_hour < 0 || lt->tm_hour > 23)
502
			badtime();
503
		lt->tm_min = ATOI2(timearg);
504
		if (lt->tm_min < 0 || lt->tm_min > 59)
505
			badtime();
506
		lt->tm_sec = 0;
507
		if ((shuttime = mktime(lt)) == -1)
508
			badtime();
509
		if ((offset = shuttime - now) < 0)
510
			errx(1, "that time is already past.");
511
		break;
512
	default:
513
		badtime();
514
	}
515
}
516
517
#define	FSMSG	"fastboot file for fsck\n"
518
void
519
doitfast(void)
520
{
521
	int fastfd;
522
523
	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
524
	    0664)) >= 0) {
525
		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
526
		(void)close(fastfd);
527
	}
528
}
529
530
#define	NOMSG	"\n\nNO LOGINS: System going down at "
531
void
532
nolog(void)
533
{
534
	int logfd;
535
	char *ct;
536
537
	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
538
	(void)signal(SIGINT, finish);
539
	(void)signal(SIGHUP, finish);
540
	(void)signal(SIGQUIT, finish);
541
	(void)signal(SIGTERM, finish);
542
	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
543
	    0664)) >= 0) {
544
		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
545
		ct = ctime(&shuttime);
546
		(void)write(logfd, ct + 11, 5);
547
		(void)write(logfd, "\n\n", 2);
548
		(void)write(logfd, mbuf, strlen(mbuf));
549
		(void)close(logfd);
550
	}
551
}
552
553
void
554
finish(int signo)
555
{
556
	if (!killflg)
557
		(void)unlink(_PATH_NOLOGIN);
558
	if (signo == 0)
559
		exit(0);
560
	else
561
		_exit(0);
562
}
563
564
void
565
badtime(void)
566
{
567
	errx(1, "bad time format.");
568
}
569
570
void
571
usage(void)
572
{
573
	fprintf(stderr,
574
	    "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n");
575
	exit(1);
576
}