int deliver_mbox_deliver(struct deliver_ctx *dctx, struct actitem *ti) { struct account *a = dctx->account; struct mail *m = dctx->mail; struct deliver_mbox_data *data = ti->data; char *path, *ptr, *lptr, *from = NULL; const char *msg; size_t len, llen; int fd, saved_errno; FILE *f; gzFile gzf; long long used; sigset_t set, oset; struct stat sb; f = gzf = NULL; fd = -1; path = replacepath(&data->path, m->tags, m, &m->rml, dctx->udata->home); if (path == NULL || *path == '\0') { log_warnx("%s: empty path", a->name); goto error; } if (data->compress) { len = strlen(path); if (len < 3 || strcmp(path + len - 3, ".gz") != 0) { path = xrealloc(path, 1, len + 4); strlcat(path, ".gz", len + 4); } } log_debug2("%s: saving to mbox %s", a->name, path); /* Save the mbox path. */ add_tag(&m->tags, "mbox_file", "%s", path); /* Check permissions and ownership. */ if (stat(path, &sb) != 0) { if (conf.no_create || errno != ENOENT) goto error_log; log_debug2("%s: creating %s", a->name, xdirname(path)); if (xmkpath(xdirname(path), -1, conf.file_group, DIRMODE) != 0) goto error_log; } else { if ((msg = checkmode(&sb, UMASK(FILEMODE))) != NULL) log_warnx("%s: %s: %s", a->name, path, msg); if ((msg = checkowner(&sb, -1)) != NULL) log_warnx("%s: %s: %s", a->name, path, msg); if ((msg = checkgroup(&sb, conf.file_group)) != NULL) log_warnx("%s: %s: %s", a->name, path, msg); } /* Create or open the mbox. */ used = 0; do { if (conf.no_create) fd = openlock(path, O_WRONLY|O_APPEND, conf.lock_types); else { fd = createlock(path, O_WRONLY|O_APPEND, -1, conf.file_group, FILEMODE, conf.lock_types); } if (fd == -1 && errno == EEXIST) fd = openlock(path, O_WRONLY|O_APPEND, conf.lock_types); if (fd == -1) { if (errno == EAGAIN) { if (locksleep(a->name, path, &used) != 0) goto error; continue; } goto error_log; } } while (fd < 0); /* Open gzFile or FILE * for writing. */ if (data->compress) { if ((gzf = gzdopen(fd, "a")) == NULL) { errno = ENOMEM; goto error_log; } } else { if ((f = fdopen(fd, "a")) == NULL) goto error_log; } /* * mboxes are a pain: if we are interrupted after this we risk * having written a partial mail. So, block SIGTERM until we're * done. */ sigemptyset(&set); sigaddset(&set, SIGTERM); if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) fatal("sigprocmask failed"); /* Write the from line. */ from = make_from(m, dctx->udata->name); if (deliver_mbox_write(f, gzf, from, strlen(from)) < 0) { xfree(from); goto error_unblock; } if (deliver_mbox_write(f, gzf, "\n", 1) < 0) { xfree(from); goto error_unblock; } log_debug3("%s: using from line: %s", a->name, from); xfree(from); /* Write the mail, escaping from lines. */ line_init(m, &ptr, &len); while (ptr != NULL) { if (ptr != m->data) { /* Skip leading >s. */ lptr = ptr; llen = len; while (*lptr == '>' && llen > 0) { lptr++; llen--; } if (llen >= 5 && strncmp(lptr, "From ", 5) == 0) { log_debug2("%s: quoting from line: %.*s", a->name, (int) len - 1, ptr); if (deliver_mbox_write(f, gzf, ">", 1) < 0) goto error_unblock; } } if (deliver_mbox_write(f, gzf, ptr, len) < 0) goto error_unblock; line_next(m, &ptr, &len); } /* Append newlines. */ if (m->data[m->size - 1] == '\n') { if (deliver_mbox_write(f, gzf, "\n", 1) < 0) goto error_unblock; } else { if (deliver_mbox_write(f, gzf, "\n\n", 2) < 0) goto error_unblock; } /* Flush buffers and sync. */ if (gzf == NULL) { if (fflush(f) != 0) goto error_unblock; } else { if (gzflush(gzf, Z_FINISH) != Z_OK) { errno = EIO; goto error_unblock; } } if (fsync(fd) != 0) goto error_unblock; if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) fatal("sigprocmask failed"); if (gzf != NULL) gzclose(gzf); if (f != NULL) fclose(f); closelock(fd, path, conf.lock_types); xfree(path); return (DELIVER_SUCCESS); error_unblock: saved_errno = errno; if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) fatal("sigprocmask failed"); errno = saved_errno; error_log: log_warn("%s: %s", a->name, path); error: if (gzf != NULL) gzclose(gzf); if (f != NULL) fclose(f); if (fd != -1) closelock(fd, path, conf.lock_types); if (path != NULL) xfree(path); return (DELIVER_FAILURE); }
/* Open state. */ int fetch_mbox_state_open(struct account *a, struct fetch_ctx *fctx) { struct fetch_mbox_data *data = a->data; struct fetch_mbox_mbox *fmbox; char *ptr; struct stat sb; uintmax_t size; long long used; fmbox = ARRAY_ITEM(&data->fmboxes, data->index); log_debug2("%s: trying path: %s", a->name, fmbox->path); if (stat(fmbox->path, &sb) != 0) goto error; if (S_ISDIR(sb.st_mode)) { errno = EISDIR; goto error; } if (sb.st_size == 0) { fctx->state = fetch_mbox_state_next; return (FETCH_AGAIN); } if (sb.st_size < 5) { log_warnx("%s: %s: mbox too small", a->name, fmbox->path); return (FETCH_ERROR); } size = sb.st_size; if (size > SIZE_MAX) { log_warnx("%s: %s: mbox too big", a->name, fmbox->path); return (FETCH_ERROR); } fmbox->size = size; log_debug3("%s: opening mbox, size %ju", a->name, size); used = 0; do { fmbox->fd = openlock(fmbox->path, O_RDWR, conf.lock_types); if (fmbox->fd == -1) { if (errno == EAGAIN) { if (locksleep(a->name, fmbox->path, &used) != 0) return (FETCH_ERROR); continue; } goto error; } } while (fmbox->fd < 0); /* mmap the file. */ fmbox->base = mmap( NULL, fmbox->size, PROT_READ|PROT_WRITE, MAP_SHARED, fmbox->fd, 0); madvise(fmbox->base, fmbox->size, MADV_SEQUENTIAL); if (fmbox->base == MAP_FAILED) { fmbox->base = NULL; goto error; } data->off = 0; ptr = memchr(fmbox->base, '\n', fmbox->size); if (strncmp(fmbox->base, "From ", 5) != 0) { log_warnx("%s: %s: not an mbox", a->name, fmbox->path); return (FETCH_ERROR); } fctx->state = fetch_mbox_state_mail; return (FETCH_AGAIN); error: if (fmbox->base != NULL) { munmap(fmbox->base, fmbox->size); fmbox->base = NULL; } if (fmbox->fd != -1) { closelock(fmbox->fd, fmbox->path, conf.lock_types); fmbox->fd = -1; } log_warn("%s: %s", a->name, fmbox->path); return (FETCH_ERROR); }
static int mklock(char *file) { int fd, try; Dir *dir; fd = openlock(file); if (fd >= 0) { /* make it a lock file if it wasn't */ dir = dirfstat(fd); if (dir == nil) error("%s vanished: %r", file); dir->mode |= DMEXCL; dir->qid.type |= QTEXCL; dirfwstat(fd, dir); free(dir); /* reopen in case it wasn't a lock file at last open */ close(fd); } for (try = 0; try < 65 && (fd = openlock(file)) < 0; try++) sleep(10*1000); return fd; } void main(int argc, char *argv[]) { Job *j; Tm tm; Time t; ulong now, last; /* in seconds */ int i, lock; debug = 0; ARGBEGIN{ case 'c': createuser(); exits(0); case 'd': debug = 1; break; default: usage(); }ARGEND if(debug){ readalljobs(); printjobs(); exits(0); } initcap(); /* do this early, before cpurc removes it */ switch(fork()){ case -1: fatal("can't fork: %r"); case 0: break; default: exits(0); } /* * it can take a few minutes before the file server notices that * we've rebooted and gives up the lock. */ lock = mklock("/cron/lock"); if (lock < 0) fatal("cron already running: %r"); argv0 = "cron"; srand(getpid()*time(0)); last = time(0); for(;;){ readalljobs(); /* * the system's notion of time may have jumped forward or * backward an arbitrary amount since the last call to time(). */ now = time(0); /* * if time has jumped backward, just note it and adapt. * if time has jumped forward more than a day, * just execute one day's jobs. */ if (now < last) { clog("time went backward"); last = now; } else if (now - last > Day) { clog("time advanced more than a day"); last = now - Day; } now = minute(now); for(last = minute(last); last <= now; last += Minute){ tm = *localtime(last); t.min = 1ULL << tm.min; t.hour = 1 << tm.hour; t.wday = 1 << tm.wday; t.mday = 1 << tm.mday; t.mon = 1 << (tm.mon + 1); for(i = 0; i < nuser; i++) for(j = users[i].jobs; j; j = j->next) if(j->time.min & t.min && j->time.hour & t.hour && j->time.wday & t.wday && j->time.mday & t.mday && j->time.mon & t.mon) rexec(&users[i], j); } seek(lock, 0, 0); write(lock, "x", 1); /* keep the lock alive */ /* * if we're not at next minute yet, sleep until a second past * (to allow for sleep intervals being approximate), * which synchronises with minute roll-over as a side-effect. */ sleepuntil(now + Minute + 1); } /* not reached */ }
void main(int argc, char *argv[]) { int fd, lckfd, lckpid, cmdpid; char *cmd, *p, *lock; char **args; char *argarr[2]; Waitmsg *w; ARGBEGIN { case 'd': ++debug; break; case 'w': ++lockwait; break; default: usage(); break; } ARGEND if (argc < 1) usage(); if (argc == 1) { args = argarr; args[0] = cmd = "rc"; args[1] = nil; } else { cmd = argv[1]; args = &argv[1]; } /* set up lock and process to keep it alive */ lock = argv[0]; lckfd = openlock(lock); lckpid = fork(); switch(lckpid){ case -1: error("fork"); case 0: /* keep lock alive until killed */ for (;;) { sleep(60*1000); seek(lckfd, 0, 0); fprint(lckfd, "\n"); } } /* spawn argument command */ cmdpid = rfork(RFFDG|RFREND|RFPROC|RFENVG); switch(cmdpid){ case -1: error("fork"); case 0: fd = create("/env/prompt", OWRITE, 0666); if (fd >= 0) { fprint(fd, "%s%% ", lock); close(fd); } exec(cmd, args); if(cmd[0] != '/' && strncmp(cmd, "./", 2) != 0 && strncmp(cmd, "../", 3) != 0) exec(smprint("/bin/%s", cmd), args); error(cmd); } notify(notifyf); w = waitfor(cmdpid); if (w == nil) error("wait"); postnote(PNPROC, lckpid, "die"); waitfor(lckpid); if(w->msg[0]){ p = utfrune(w->msg, ':'); if(p && p[1]) p++; else p = w->msg; while (isspace(*p)) p++; fprint(2, "%s: %s # status=%s\n", argv0, cmd, p); } exits(w->msg); }