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