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