GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/shutdown/shutdown.c Lines: 0 245 0.0 %
Date: 2017-11-07 Branches: 0 171 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: shutdown.c,v 1.46 2017/04/03 20:59:19 fcambus 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 flock", 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
		char *ct = ctime(&shuttime);
203
204
		if (ct)
205
			printf("Shutdown at %.24s.\n", ct);
206
		else
207
			printf("Shutdown soon.\n");
208
	} else
209
		(void)printf("Shutdown NOW!\n");
210
211
	if (!(whom = getlogin()))
212
		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
213
214
#ifdef DEBUG
215
	(void)putc('\n', stdout);
216
#else
217
	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
218
219
	forkpid = fork();
220
	if (forkpid == -1)
221
		err(1, "fork");
222
	if (forkpid) {
223
		(void)printf("shutdown: [pid %ld]\n", (long)forkpid);
224
		exit(0);
225
	}
226
	setsid();
227
#endif
228
	openlog("shutdown", LOG_CONS, LOG_AUTH);
229
	loop();
230
	/* NOTREACHED */
231
}
232
233
void
234
loop(void)
235
{
236
	struct interval *tp;
237
	u_int sltime;
238
	int logged;
239
240
	if (offset <= NOLOG_TIME) {
241
		logged = 1;
242
		nolog();
243
	} else
244
		logged = 0;
245
	tp = tlist;
246
	if (tp->timeleft < offset)
247
		(void)sleep((u_int)(offset - tp->timeleft));
248
	else {
249
		while (offset < tp->timeleft)
250
			++tp;
251
		/*
252
		 * Warn now, if going to sleep more than a fifth of
253
		 * the next wait time.
254
		 */
255
		if ((sltime = offset - tp->timeleft)) {
256
			if (sltime > tp->timetowait / 5)
257
				timewarn(offset);
258
			(void)sleep(sltime);
259
		}
260
	}
261
	for (;; ++tp) {
262
		timewarn(tp->timeleft);
263
		if (!logged && tp->timeleft <= NOLOG_TIME) {
264
			logged = 1;
265
			nolog();
266
		}
267
		(void)sleep((u_int)tp->timetowait);
268
		if (!tp->timeleft)
269
			break;
270
	}
271
	die_you_gravy_sucking_pig_dog();
272
}
273
274
static jmp_buf alarmbuf;
275
276
static char *restricted_environ[] = {
277
	"PATH=" _PATH_STDPATH,
278
	NULL
279
};
280
281
void
282
timewarn(int timeleft)
283
{
284
	static char hostname[HOST_NAME_MAX+1];
285
	char wcmd[PATH_MAX + 4];
286
	extern char **environ;
287
	static int first;
288
	FILE *pf;
289
290
	if (!first++)
291
		(void)gethostname(hostname, sizeof(hostname));
292
293
	/* undoc -n option to wall suppresses normal wall banner */
294
	(void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
295
	environ = restricted_environ;
296
	if (!(pf = popen(wcmd, "w"))) {
297
		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
298
		return;
299
	}
300
301
	(void)fprintf(pf,
302
	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
303
	    timeleft ? "": "FINAL ", whom, hostname);
304
305
	if (timeleft > 10*60) {
306
		struct tm *tm = localtime(&shuttime);
307
308
		fprintf(pf, "System going down at %d:%02d\n\n",
309
		    tm->tm_hour, tm->tm_min);
310
	} else if (timeleft > 59)
311
		(void)fprintf(pf, "System going down in %d minute%s\n\n",
312
		    timeleft / 60, (timeleft > 60) ? "s" : "");
313
	else if (timeleft)
314
		(void)fprintf(pf, "System going down in 30 seconds\n\n");
315
	else
316
		(void)fprintf(pf, "System going down IMMEDIATELY\n\n");
317
318
	if (mbuflen)
319
		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
320
321
	/*
322
	 * play some games, just in case wall doesn't come back
323
	 * probably unnecessary, given that wall is careful.
324
	 */
325
	if (!setjmp(alarmbuf)) {
326
		(void)signal(SIGALRM, timeout);
327
		(void)alarm((u_int)30);
328
		(void)pclose(pf);
329
		(void)alarm((u_int)0);
330
		(void)signal(SIGALRM, SIG_DFL);
331
	}
332
}
333
334
void
335
timeout(int signo)
336
{
337
	longjmp(alarmbuf, 1);		/* XXX signal/longjmp resource leaks */
338
}
339
340
void
341
die_you_gravy_sucking_pig_dog(void)
342
{
343
344
	syslog(LOG_NOTICE, "%s by %s: %s",
345
	    doreboot ? "reboot" : dopower ? "power-down" : dohalt ? "halt" :
346
	    "shutdown", whom, mbuf);
347
	(void)sleep(2);
348
349
	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
350
	if (killflg) {
351
		(void)printf("\rbut you'll have to do it yourself\r\n");
352
		finish(0);
353
	}
354
	if (dofast)
355
		doitfast();
356
357
	if (pledge("stdio rpath wpath cpath tty id proc exec flock", NULL) == -1)
358
		err(1, "pledge");
359
360
#ifdef DEBUG
361
	if (doreboot)
362
		(void)printf("reboot");
363
	else if (dopower)
364
		(void)printf("power-down");
365
	else if (dohalt)
366
		(void)printf("halt");
367
	if (nosync)
368
		(void)printf(" no sync");
369
	if (dofast)
370
		(void)printf(" no fsck");
371
	if (dodump)
372
		(void)printf(" with dump");
373
	(void)printf("\nkill -HUP 1\n");
374
#else
375
	if (dohalt || dopower || doreboot) {
376
		char *args[10];
377
		char **arg, *path;
378
379
		if (pledge("stdio exec flock rpath cpath wpath", NULL) == -1)
380
			err(1, "pledge");
381
382
		arg = &args[0];
383
		if (doreboot) {
384
			path = _PATH_REBOOT;
385
			*arg++ = "reboot";
386
		} else {
387
			path = _PATH_HALT;
388
			*arg++ = "halt";
389
		}
390
		*arg++ = "-l";
391
		if (dopower)
392
			*arg++ = "-p";
393
		if (nosync)
394
			*arg++ = "-n";
395
		if (dodump)
396
			*arg++ = "-d";
397
		*arg++ = NULL;
398
		execve(path, args, NULL);
399
		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", path);
400
		warn("%s", path);
401
	}
402
	if (access(_PATH_RC, R_OK) != -1) {
403
		pid_t pid;
404
		struct termios t;
405
		int fd;
406
407
		switch ((pid = fork())) {
408
		case -1:
409
			break;
410
		case 0:
411
			if (revoke(_PATH_CONSOLE) == -1)
412
				perror("revoke");
413
			if (setsid() == -1)
414
				perror("setsid");
415
			fd = open(_PATH_CONSOLE, O_RDWR);
416
			if (fd == -1)
417
				perror("open");
418
			dup2(fd, 0);
419
			dup2(fd, 1);
420
			dup2(fd, 2);
421
			if (fd > 2)
422
				close(fd);
423
424
			/* At a minimum... */
425
			tcgetattr(0, &t);
426
			t.c_oflag |= (ONLCR | OPOST);
427
			tcsetattr(0, TCSANOW, &t);
428
429
			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
430
			_exit(1);
431
		default:
432
			waitpid(pid, NULL, 0);
433
		}
434
	}
435
	(void)kill(1, SIGTERM);		/* to single user */
436
#endif
437
	finish(0);
438
}
439
440
#define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
441
442
void
443
getoffset(char *timearg)
444
{
445
	struct tm *lt;
446
	int this_year;
447
	time_t now;
448
	char *p;
449
450
	if (!strcasecmp(timearg, "now")) {		/* now */
451
		offset = 0;
452
		return;
453
	}
454
455
	(void)time(&now);
456
	if (*timearg == '+') {				/* +minutes */
457
		const char *errstr;
458
459
		offset = strtonum(++timearg, 0, INT_MAX, &errstr);
460
		if (errstr)
461
			badtime();
462
		offset *= 60;
463
		shuttime = now + offset;
464
		return;
465
	}
466
467
	/* handle hh:mm by getting rid of the colon */
468
	for (p = timearg; *p; ++p) {
469
		if (!isascii((unsigned char)*p) || !isdigit((unsigned char)*p)) {
470
			if (*p == ':' && strlen(p) == 3) {
471
				p[0] = p[1];
472
				p[1] = p[2];
473
				p[2] = '\0';
474
			} else
475
				badtime();
476
		}
477
	}
478
479
	unsetenv("TZ");					/* OUR timezone */
480
	lt = localtime(&now);				/* current time val */
481
482
	switch (strlen(timearg)) {
483
	case 10:
484
		this_year = lt->tm_year;
485
		lt->tm_year = ATOI2(timearg);
486
		/*
487
		 * check if the specified year is in the next century.
488
		 * allow for one year of user error as many people will
489
		 * enter n - 1 at the start of year n.
490
		 */
491
		if (lt->tm_year < (this_year % 100) - 1)
492
			lt->tm_year += 100;
493
		/* adjust for the year 2000 and beyond */
494
		lt->tm_year += (this_year - (this_year % 100));
495
		/* FALLTHROUGH */
496
	case 8:
497
		lt->tm_mon = ATOI2(timearg);
498
		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
499
			badtime();
500
		/* FALLTHROUGH */
501
	case 6:
502
		lt->tm_mday = ATOI2(timearg);
503
		if (lt->tm_mday < 1 || lt->tm_mday > 31)
504
			badtime();
505
		/* FALLTHROUGH */
506
	case 4:
507
		lt->tm_hour = ATOI2(timearg);
508
		if (lt->tm_hour < 0 || lt->tm_hour > 23)
509
			badtime();
510
		lt->tm_min = ATOI2(timearg);
511
		if (lt->tm_min < 0 || lt->tm_min > 59)
512
			badtime();
513
		lt->tm_sec = 0;
514
		if ((shuttime = mktime(lt)) == -1)
515
			badtime();
516
		if ((offset = shuttime - now) < 0)
517
			errx(1, "that time is already past.");
518
		break;
519
	default:
520
		badtime();
521
	}
522
}
523
524
void
525
doitfast(void)
526
{
527
	int fastfd;
528
529
	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
530
	    0664)) >= 0) {
531
		dprintf(fastfd, "fastboot file for fsck\n");
532
		close(fastfd);
533
	}
534
}
535
536
void
537
nolog(void)
538
{
539
	int logfd;
540
	struct tm *tm;
541
542
	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
543
	(void)signal(SIGINT, finish);
544
	(void)signal(SIGHUP, finish);
545
	(void)signal(SIGQUIT, finish);
546
	(void)signal(SIGTERM, finish);
547
	tm = localtime(&shuttime);
548
	if (tm && (logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
549
	    0664)) >= 0) {
550
		dprintf(logfd,
551
		    "\n\nNO LOGINS: System going down at %d:%02d\n\n",
552
		    tm->tm_hour, tm->tm_min);
553
		close(logfd);
554
	}
555
}
556
557
void
558
finish(int signo)
559
{
560
	if (!killflg)
561
		(void)unlink(_PATH_NOLOGIN);
562
	if (signo == 0)
563
		exit(0);
564
	else
565
		_exit(0);
566
}
567
568
void
569
badtime(void)
570
{
571
	errx(1, "bad time format.");
572
}
573
574
void
575
usage(void)
576
{
577
	fprintf(stderr,
578
	    "usage: shutdown [-] [-dfhknpr] time [warning-message ...]\n");
579
	exit(1);
580
}