static void writefile(time_t runtimer, char queue) { /* This does most of the work if at or batch are invoked for writing a job. */ long jobno; char *ap, *ppos, *mailname; struct passwd *pass_entry; struct stat statbuf; int fd, lockdes, fd2; FILE *fp, *fpin; struct sigaction act; char **atenv; int ch; mode_t cmask; struct flock lock; struct tm *runtime; char timestr[TIMESIZE]; pid_t pid; int istty; int kill_errno; int rc; int mailsize = 128; /* Install the signal handler for SIGINT; terminate after removing the * spool file if necessary */ memset(&act, 0, sizeof act); act.sa_handler = sigc; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); ppos = atfile + strlen(ATJOB_DIR) + 1; #ifdef _SC_LOGIN_NAME_MAX errno = 0; rc = sysconf(_SC_LOGIN_NAME_MAX); if (rc > 0) mailsize = rc; #else # ifdef LOGIN_NAME_MAX mailsize = LOGIN_NAME_MAX; # endif #endif /* Loop over all possible file names for running something at this * particular time, see if a file is there; the first empty slot at any * particular time is used. Lock the file LFILE first to make sure * we're alone when doing this. */ PRIV_START if ((lockdes = open(LFILE, O_WRONLY)) < 0) perr("Cannot open lockfile " LFILE); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; act.sa_handler = alarmc; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; /* Set an alarm so a timeout occurs after ALARMC seconds, in case * something is seriously broken. */ sigaction(SIGALRM, &act, NULL); alarm(ALARMC); fcntl(lockdes, F_SETLKW, &lock); alarm(0); if ((jobno = nextjob()) == EOF) perr("Cannot generate job number"); (void)snprintf(ppos, sizeof(atfile) - (ppos - atfile), "%c%5lx%8lx", queue, jobno, (unsigned long) (runtimer / 60)); for (ap = ppos; *ap != '\0'; ap++) if (*ap == ' ') *ap = '0'; if (stat(atfile, &statbuf) != 0) if (errno != ENOENT) perr("Cannot access " ATJOB_DIR); /* Create the file. The x bit is only going to be set after it has * been completely written out, to make sure it is not executed in the * meantime. To make sure they do not get deleted, turn off their r * bit. Yes, this is a kluge. */ cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); seteuid(real_uid); if ((fd = open(atfile, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, S_IRUSR)) == -1) perr("Cannot create atjob file %.500s", atfile); seteuid(effective_uid); if ((fd2 = dup(fd)) < 0) perr("Error in dup() of job file"); /* if (fchown(fd2, real_uid, real_gid) != 0) perr("Cannot give away file"); */ PRIV_END /* We no longer need suid root; now we just need to be able to write * to the directory, if necessary. */ REDUCE_PRIV(daemon_uid, daemon_gid) /* We've successfully created the file; let's set the flag so it * gets removed in case of an interrupt or error. */ fcreated = 1; /* Now we can release the lock, so other people can access it */ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; fcntl(lockdes, F_SETLKW, &lock); close(lockdes); if ((fp = fdopen(fd, "w")) == NULL) panic("Cannot reopen atjob file"); /* Get the userid to mail to, first by trying getlogin(), which reads * /var/run/utmp, then from LOGNAME, finally from getpwuid(). */ mailname = getlogin(); if (mailname == NULL) mailname = getenv("LOGNAME"); if (mailname == NULL || mailname[0] == '\0' || getpwnam(mailname) == NULL) { pass_entry = getpwuid(real_uid); if (pass_entry != NULL) mailname = pass_entry->pw_name; } if ((mailname == NULL) || (mailname[0] == '\0') || (strlen(mailname) > mailsize) ) { panic("Cannot find username to mail output to"); } if (atinput != (char *) NULL) { fpin = freopen(atinput, "r", stdin); if (fpin == NULL) perr("Cannot open input file %.500s", atinput); } fprintf(fp, "#!/bin/sh\n# atrun uid=%d gid=%d\n# mail %s %d\n", real_uid, real_gid, mailname, send_mail); /* Write out the umask at the time of invocation */ fprintf(fp, "umask %lo\n", (unsigned long) cmask); /* Write out the environment. Anything that may look like a * special character to the shell is quoted, except for \n, which is * done with a pair of ""'s. Dont't export the no_export list (such * as TERM or DISPLAY) because we don't want these. */ for (atenv = environ; *atenv != NULL; atenv++) { int export = 1; char *eqp; /* Only accept alphanumerics and underscore in variable names. * Also require the name to not start with a digit. * Some shells don't like other variable names. */ { char *p = *atenv; if (isdigit(*p)) export = 0; for (; *p != '=' && *p != '\0'; ++p) { if (!isalnum(*p) && *p != '_') { export = 0; break; } } } eqp = strchr(*atenv, '='); if (ap == NULL) eqp = *atenv; else { unsigned int i; for (i = 0; i < sizeof(no_export) / sizeof(no_export[0]); i++) { export = export && (strncmp(*atenv, no_export[i], (size_t) (eqp - *atenv)) != 0); } eqp++; }
static void writefile(time_t runtimer, char queue) { /* This does most of the work if at or batch are invoked for writing a job. */ long jobno; char *ap, *ppos, *mailname; struct passwd *pass_entry; struct stat statbuf; int fdes, lockdes, fd2; FILE *fp, *fpin; struct sigaction act; char **atenv; int ch; mode_t cmask; struct flock lock; #ifdef __FreeBSD__ (void) setlocale(LC_TIME, ""); #endif /* Install the signal handler for SIGINT; terminate after removing the * spool file if necessary */ act.sa_handler = sigc; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); ppos = atfile + strlen(ATJOB_DIR); /* Loop over all possible file names for running something at this * particular time, see if a file is there; the first empty slot at any * particular time is used. Lock the file LFILE first to make sure * we're alone when doing this. */ PRIV_START if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) perr("cannot open lockfile " LFILE); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; act.sa_handler = alarmc; sigemptyset(&(act.sa_mask)); act.sa_flags = 0; /* Set an alarm so a timeout occurs after ALARMC seconds, in case * something is seriously broken. */ sigaction(SIGALRM, &act, NULL); alarm(ALARMC); fcntl(lockdes, F_SETLKW, &lock); alarm(0); if ((jobno = nextjob()) == EOF) perr("cannot generate job number"); sprintf(ppos, "%c%5lx%8lx", queue, jobno, (unsigned long) (runtimer/60)); for(ap=ppos; *ap != '\0'; ap ++) if (*ap == ' ') *ap = '0'; if (stat(atfile, &statbuf) != 0) if (errno != ENOENT) perr("cannot access " ATJOB_DIR); /* Create the file. The x bit is only going to be set after it has * been completely written out, to make sure it is not executed in the * meantime. To make sure they do not get deleted, turn off their r * bit. Yes, this is a kluge. */ cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); if ((fdes = creat(atfile, O_WRONLY)) == -1) perr("cannot create atjob file"); if ((fd2 = dup(fdes)) <0) perr("error in dup() of job file"); if(fchown(fd2, real_uid, real_gid) != 0) perr("cannot give away file"); PRIV_END /* We no longer need suid root; now we just need to be able to write * to the directory, if necessary. */ REDUCE_PRIV(DAEMON_UID, DAEMON_GID) /* We've successfully created the file; let's set the flag so it * gets removed in case of an interrupt or error. */ fcreated = 1; /* Now we can release the lock, so other people can access it */ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; fcntl(lockdes, F_SETLKW, &lock); close(lockdes); if((fp = fdopen(fdes, "w")) == NULL) panic("cannot reopen atjob file"); /* Get the userid to mail to, first by trying getlogin(), * then from LOGNAME, finally from getpwuid(). */ mailname = getlogin(); if (mailname == NULL) mailname = getenv("LOGNAME"); if ((mailname == NULL) || (mailname[0] == '\0') || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL)) { pass_entry = getpwuid(real_uid); if (pass_entry != NULL) mailname = pass_entry->pw_name; } if (atinput != (char *) NULL) { fpin = freopen(atinput, "r", stdin); if (fpin == NULL) perr("cannot open input file"); } fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", (long) real_uid, (long) real_gid, MAXLOGNAME - 1, mailname, send_mail); /* Write out the umask at the time of invocation */ fprintf(fp, "umask %lo\n", (unsigned long) cmask); /* Write out the environment. Anything that may look like a * special character to the shell is quoted, except for \n, which is * done with a pair of "'s. Don't export the no_export list (such * as TERM or DISPLAY) because we don't want these. */ for (atenv= environ; *atenv != NULL; atenv++) { int export = 1; char *eqp; eqp = strchr(*atenv, '='); if (ap == NULL) eqp = *atenv; else { size_t i; for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) { export = export && (strncmp(*atenv, no_export[i], (size_t) (eqp-*atenv)) != 0); } eqp++; } if (export) { fwrite(*atenv, sizeof(char), eqp-*atenv, fp); for(ap = eqp; *ap != '\0'; ap++) { if (*ap == '\n') fprintf(fp, "\"\n\""); else { if (!isalnum(*ap)) { switch (*ap) { case '%': case '/': case '{': case '[': case ']': case '=': case '}': case '@': case '+': case '#': case ',': case '.': case ':': case '-': case '_': break; default: fputc('\\', fp); break; } } fputc(*ap, fp); } } fputs("; export ", fp); fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); fputc('\n', fp); } }