summaryrefslogtreecommitdiff
path: root/usr.bin/calendar/calendar.c
diff options
context:
space:
mode:
authorPaul Janzen <pjanzen@cvs.openbsd.org>2001-09-03 15:53:01 +0000
committerPaul Janzen <pjanzen@cvs.openbsd.org>2001-09-03 15:53:01 +0000
commitfab13a4f25f936a15bc8dae81a9c6b453b0fb21d (patch)
treeb9b86ba2628755704c16e2a136f8676ed6437653 /usr.bin/calendar/calendar.c
parent6448f697b9dd29cf29b43afce0b32fbc8b385e9f (diff)
- Prevent users from hanging or killing 'calendar -a', at the cost of
one additional fork() per user who has a calendar file. Side effect is that root runs much less code. - Use login caps so we don't, for example, give a user more processes than he's allowed. - tmpfile() rather than mkstemp() means never leaving junk behind. - Don't let 'calendar -a' take more than a day. Reviewed by millert@
Diffstat (limited to 'usr.bin/calendar/calendar.c')
-rw-r--r--usr.bin/calendar/calendar.c134
1 files changed, 121 insertions, 13 deletions
diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c
index abef570e653..553da968cb7 100644
--- a/usr.bin/calendar/calendar.c
+++ b/usr.bin/calendar/calendar.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: calendar.c,v 1.14 2000/11/21 14:01:38 aaron Exp $ */
+/* $OpenBSD: calendar.c,v 1.15 2001/09/03 15:53:00 pjanzen Exp $ */
/*
* Copyright (c) 1989, 1993, 1994
@@ -43,14 +43,19 @@ static const char copyright[] =
#if 0
static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
#else
-static char rcsid[] = "$OpenBSD: calendar.c,v 1.14 2000/11/21 14:01:38 aaron Exp $";
+static char rcsid[] = "$OpenBSD: calendar.c,v 1.15 2001/09/03 15:53:00 pjanzen Exp $";
#endif
#endif /* not lint */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <locale.h>
+#include <login_cap.h>
#include <pwd.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -61,6 +66,10 @@ static char rcsid[] = "$OpenBSD: calendar.c,v 1.14 2000/11/21 14:01:38 aaron Exp
#include "pathnames.h"
#include "calendar.h"
+char *calendarFile = "calendar"; /* default calendar file */
+char *calendarHome = ".calendar"; /* HOME */
+char *calendarNoMail = "nomail"; /* don't sent mail if this file exists */
+
struct passwd *pw;
int doall = 0;
time_t f_time = 0;
@@ -70,12 +79,14 @@ int f_dayBefore = 0; /* days before current date */
struct specialev spev[NUMEV];
+void childsig __P((int));
+
int
main(argc, argv)
int argc;
char *argv[];
{
- int ch, i;
+ int ch;
char *caldir;
(void)setlocale(LC_ALL, "");
@@ -128,20 +139,110 @@ main(argc, argv)
settime(&f_time);
if (doall) {
+ pid_t kid, deadkid;
+ int kidstat, kidreaped, runningkids;
+ int acstat;
+ struct stat sbuf;
+ time_t t;
+ unsigned int sleeptime;
+
+ signal(SIGCHLD, childsig);
+ runningkids = 0;
+ t = time(NULL);
while ((pw = getpwent()) != NULL) {
- (void)setlocale(LC_ALL, "");
- (void)setegid(pw->pw_gid);
- (void)initgroups(pw->pw_name, pw->pw_gid);
- (void)seteuid(pw->pw_uid);
- if (!chdir(pw->pw_dir)) {
+ acstat = 0;
+ /* Avoid unnecessary forks. The calendar file is only
+ * opened as the user later; if it can't be opened,
+ * it's no big deal. Also, get to correct directory.
+ * Note that in an NFS environment root may get EACCES
+ * on a chdir(), in which case we have to fork. As long as
+ * we can chdir() we can stat(), unless the user is
+ * modifying permissions while this is running.
+ */
+ if (chdir(pw->pw_dir)) {
+ if (errno == EACCES)
+ acstat = 1;
+ else
+ continue;
+ }
+ if (stat(calendarFile, &sbuf) != 0) {
+ if (chdir(calendarHome)) {
+ if (errno == EACCES)
+ acstat = 1;
+ else
+ continue;
+ }
+ if (stat(calendarNoMail, &sbuf) == 0 ||
+ stat(calendarFile, &sbuf) != 0)
+ continue;
+ }
+ sleeptime = USERTIMEOUT;
+ switch ((kid = fork())) {
+ case -1: /* error */
+ warn("fork");
+ continue;
+ case 0: /* child */
+ (void)setlocale(LC_ALL, "");
+ if (setusercontext(NULL, pw, pw->pw_uid,
+ LOGIN_SETALL ^ LOGIN_SETLOGIN))
+ err(1, "unable to set user context (uid %d)",
+ (int)pw->pw_uid);
+ if (acstat) {
+ if (chdir(pw->pw_dir) ||
+ stat(calendarFile, &sbuf) != 0 ||
+ chdir(calendarHome) ||
+ stat(calendarNoMail, &sbuf) == 0 ||
+ stat(calendarFile, &sbuf) != 0)
+ exit(0);
+ }
cal();
- /* Keep user settings from propogating */
- for (i = 0; i < NUMEV; i++)
- if (spev[i].uname != NULL)
- free(spev[i].uname);
+ exit(0);
+ }
+ /* parent: wait a reasonable time, then kill child if
+ * necessary.
+ */
+ runningkids++;
+ kidreaped = 0;
+ do {
+ sleeptime = sleep(sleeptime);
+ /* Note that there is the possibility, if the sleep
+ * stops early due to some other signal, of the child
+ * terminating and not getting detected during the next
+ * sleep. In that unlikely worst case, we just sleep
+ * too long for that user.
+ */
+ for (;;) {
+ deadkid = waitpid(-1, &kidstat, WNOHANG);
+ if (deadkid <= 0)
+ break;
+ runningkids--;
+ if (deadkid == kid) {
+ kidreaped = 1;
+ sleeptime = 0;
+ }
+ }
+ } while (sleeptime);
+
+ if (!kidreaped) {
+ /* It doesn't _really_ matter if the kill fails, e.g.
+ * if there's only a zombie now.
+ */
+ (void)kill(kid, SIGTERM);
+ warnx("uid %d did not finish in time", (int)pw->pw_uid);
}
- (void)seteuid(0);
+ if (time(NULL) - t >= SECSPERDAY)
+ errx(2, "'calendar -a' took more than a day; stopped at uid %d",
+ (int)pw->pw_uid);
}
+ for (;;) {
+ deadkid = waitpid(-1, &kidstat, WNOHANG);
+ if (deadkid <= 0)
+ break;
+ runningkids--;
+ }
+ if (runningkids)
+ warnx(
+"%d child processes still running when 'calendar -a' finished", runningkids);
}
else if ((caldir = getenv("CALENDAR_DIR")) != NULL) {
if(!chdir(caldir))
@@ -161,3 +262,10 @@ usage()
"[-f calendarfile]\n");
exit(1);
}
+
+
+void
+childsig(sig)
+ int sig;
+{
+}