Beispiel #1
0
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);
}
Beispiel #2
0
/* 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);
}
Beispiel #3
0
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 */
}
Beispiel #4
0
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);
}