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 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_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 argc -= optind; 121 argv += optind; 122 123 if (argc) 124 usage(); 125 126 if (doall) { 127 if (pledge("stdio rpath tmppath fattr getpw id proc exec", NULL) 128 == -1) 129 err(1, "pledge"); 130 } else { 131 if (pledge("stdio rpath proc exec", NULL) == -1) 132 err(1, "pledge"); 133 } 134 135 /* use current time */ 136 if (f_time <= 0) 137 (void)time(&f_time); 138 139 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 settime(&f_time); 146 147 if (doall) { 148 pid_t kid, deadkid; 149 int kidstat, kidreaped, runningkids; 150 int acstat; 151 struct stat sbuf; 152 time_t t; 153 unsigned int sleeptime; 154 155 signal(SIGCHLD, childsig); 156 runningkids = 0; 157 t = time(NULL); 158 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 if (chdir(pw->pw_dir)) { 169 if (errno == EACCES) 170 acstat = 1; 171 else 172 continue; 173 } 174 if (stat(calendarFile, &sbuf) != 0) { 175 if (chdir(calendarHome)) { 176 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 deadkid = waitpid(-1, &kidstat, WNOHANG); 250 if (deadkid <= 0) 251 break; 252 runningkids--; 253 } 254 if (runningkids) 255 warnx("%d child processes still running when " 256 "'calendar -a' finished", runningkids); 257 } else if ((caldir = getenv("CALENDAR_DIR")) != NULL) { 258 if(!chdir(caldir)) 259 cal(); 260 } else 261 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 } 281