GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/calendar/calendar.c Lines: 37 102 36.3 %
Date: 2017-11-07 Branches: 28 86 32.6 %

Line Branch Exec Source
1
/*	$OpenBSD: calendar.c,v 1.35 2015/12/07 18:46:35 espie Exp $	*/
2
3
/*
4
 * Copyright (c) 1989, 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include <sys/stat.h>
33
#include <sys/types.h>
34
#include <sys/wait.h>
35
#include <err.h>
36
#include <errno.h>
37
#include <locale.h>
38
#include <login_cap.h>
39
#include <pwd.h>
40
#include <signal.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <limits.h>
45
#include <time.h>
46
#include <unistd.h>
47
48
#include "pathnames.h"
49
#include "calendar.h"
50
51
char *calendarFile = "calendar";  /* default calendar file */
52
char *calendarHome = ".calendar"; /* HOME */
53
char *calendarNoMail = "nomail";  /* don't sent mail if this file exists */
54
55
struct passwd *pw;
56
int doall = 0;
57
int daynames = 0;
58
time_t f_time = 0;
59
int bodun_always = 0;
60
61
int f_dayAfter = 0; /* days after current date */
62
int f_dayBefore = 0; /* days before current date */
63
int f_SetdayAfter = 0; /* calendar invoked with -A */
64
65
struct specialev spev[NUMEV];
66
67
void childsig(int);
68
69
int
70
main(int argc, char *argv[])
71
{
72
	int ch;
73
13160
	const char *errstr;
74
	char *caldir;
75
76
6580
	(void)setlocale(LC_ALL, "");
77
78
26319
	while ((ch = getopt(argc, argv, "abwf:t:A:B:-")) != -1)
79


13159
		switch (ch) {
80
		case '-':		/* backward contemptible */
81
		case 'a':
82
1
			if (getuid())
83
				errx(1, "%s", strerror(EPERM));
84
1
			doall = 1;
85
1
			break;
86
87
		case 'b':
88
			bodun_always = 1;
89
			break;
90
91
		case 'f': /* other calendar file */
92
6579
		        calendarFile = optarg;
93
6579
			break;
94
95
		case 't': /* other date, undocumented, for tests */
96
6579
			if ((f_time = Mktime(optarg)) <= 0)
97
				errx(1, "specified date is outside allowed range");
98
			break;
99
100
		case 'A': /* days after current date */
101
			f_dayAfter = strtonum(optarg, 0, INT_MAX, &errstr);
102
			if (errstr)
103
				errx(1, "-A %s: %s", optarg, errstr);
104
			f_SetdayAfter = 1;
105
			break;
106
107
		case 'B': /* days before current date */
108
			f_dayBefore = strtonum(optarg, 0, INT_MAX, &errstr);
109
			if (errstr)
110
				errx(1, "-B %s: %s", optarg, errstr);
111
			break;
112
113
		case 'w':
114
			daynames = 1;
115
			break;
116
117
		default:
118
			usage();
119
		}
120
6580
	argc -= optind;
121
6580
	argv += optind;
122
123
6580
	if (argc)
124
		usage();
125
126
6580
	if (doall) {
127
2
		if (pledge("stdio rpath tmppath fattr getpw id proc exec flock cpath wpath", NULL)
128
1
		    == -1)
129
			err(1, "pledge");
130
	} else {
131
6579
		if (pledge("stdio rpath proc exec flock cpath wpath", NULL) == -1)
132
			err(1, "pledge");
133
	}
134
135
	/* use current time */
136
6580
	if (f_time <= 0)
137
1
	    (void)time(&f_time);
138
139
6580
	if (f_dayBefore) {
140
		/* Move back in time and only look forwards */
141
		f_dayAfter += f_dayBefore;
142
		f_time -= SECSPERDAY * f_dayBefore;
143
		f_dayBefore = 0;
144
	}
145
6580
	settime(&f_time);
146
147
6580
	if (doall) {
148
		pid_t kid, deadkid;
149
1
		int kidstat, kidreaped, runningkids;
150
		int acstat;
151
1
		struct stat sbuf;
152
		time_t t;
153
		unsigned int sleeptime;
154
155
1
		signal(SIGCHLD, childsig);
156
		runningkids = 0;
157
1
		t = time(NULL);
158
68
		while ((pw = getpwent()) != NULL) {
159
			acstat = 0;
160
			/* Avoid unnecessary forks.  The calendar file is only
161
			 * opened as the user later; if it can't be opened,
162
			 * it's no big deal.  Also, get to correct directory.
163
			 * Note that in an NFS environment root may get EACCES
164
			 * on a chdir(), in which case we have to fork.  As long as
165
			 * we can chdir() we can stat(), unless the user is
166
			 * modifying permissions while this is running.
167
			 */
168
66
			if (chdir(pw->pw_dir)) {
169
9
				if (errno == EACCES)
170
					acstat = 1;
171
				else
172
					continue;
173
			}
174
57
			if (stat(calendarFile, &sbuf) != 0) {
175
57
				if (chdir(calendarHome)) {
176
57
					if (errno == EACCES)
177
						acstat = 1;
178
					else
179
						continue;
180
				}
181
				if (stat(calendarNoMail, &sbuf) == 0 ||
182
				    stat(calendarFile, &sbuf) != 0)
183
					continue;
184
			}
185
			sleeptime = USERTIMEOUT;
186
			switch ((kid = fork())) {
187
			case -1:	/* error */
188
				warn("fork");
189
				continue;
190
			case 0:	/* child */
191
				(void)setpgid(getpid(), getpid());
192
				(void)setlocale(LC_ALL, "");
193
				if (setusercontext(NULL, pw, pw->pw_uid,
194
				    LOGIN_SETALL ^ LOGIN_SETLOGIN))
195
					err(1, "unable to set user context (uid %u)",
196
					    pw->pw_uid);
197
				if (acstat) {
198
					if (chdir(pw->pw_dir) ||
199
					    stat(calendarFile, &sbuf) != 0 ||
200
					    chdir(calendarHome) ||
201
					    stat(calendarNoMail, &sbuf) == 0 ||
202
					    stat(calendarFile, &sbuf) != 0)
203
						exit(0);
204
				}
205
				cal();
206
				exit(0);
207
			}
208
			/* parent: wait a reasonable time, then kill child if
209
			 * necessary.
210
			 */
211
			runningkids++;
212
			kidreaped = 0;
213
			do {
214
				sleeptime = sleep(sleeptime);
215
				/* Note that there is the possibility, if the sleep
216
				 * stops early due to some other signal, of the child
217
				 * terminating and not getting detected during the next
218
				 * sleep.  In that unlikely worst case, we just sleep
219
				 * too long for that user.
220
				 */
221
				for (;;) {
222
					deadkid = waitpid(-1, &kidstat, WNOHANG);
223
					if (deadkid <= 0)
224
						break;
225
					runningkids--;
226
					if (deadkid == kid) {
227
						kidreaped = 1;
228
						sleeptime = 0;
229
					}
230
				}
231
			} while (sleeptime);
232
233
			if (!kidreaped) {
234
				/* It doesn't _really_ matter if the kill fails, e.g.
235
				 * if there's only a zombie now.
236
				 */
237
				if (getpgid(kid) != getpgrp())
238
					(void)killpg(getpgid(kid), SIGTERM);
239
				else
240
					(void)kill(kid, SIGTERM);
241
				warnx("uid %u did not finish in time", pw->pw_uid);
242
			}
243
			if (time(NULL) - t >= SECSPERDAY)
244
				errx(2, "'calendar -a' took more than a day; "
245
				    "stopped at uid %u",
246
				    pw->pw_uid);
247
		}
248
		for (;;) {
249
1
			deadkid = waitpid(-1, &kidstat, WNOHANG);
250
1
			if (deadkid <= 0)
251
				break;
252
			runningkids--;
253
		}
254
1
		if (runningkids)
255
			warnx("%d child processes still running when "
256
			    "'calendar -a' finished", runningkids);
257
6580
	} else if ((caldir = getenv("CALENDAR_DIR")) != NULL) {
258
		if(!chdir(caldir))
259
			cal();
260
	} else
261
6579
		cal();
262
263
	exit(0);
264
}
265
266
267
void
268
usage(void)
269
{
270
	(void)fprintf(stderr,
271
	    "usage: calendar [-abw] [-A num] [-B num] [-f calendarfile] "
272
	    "[-t [[[cc]yy]mm]dd]\n");
273
	exit(1);
274
}
275
276
277
void
278
childsig(int signo)
279
{
280
}