Exemple #1
0
void    qmgr_active_done(QMGR_MESSAGE *message)
{
    char   *myname = "qmgr_active_done";
    struct stat st;

    if (msg_verbose)
	msg_info("%s: %s", myname, message->queue_id);

    /*
     * During a previous iteration, an attempt to bounce this message may
     * have failed, so there may still be a bounce log lying around. XXX By
     * groping around in the bounce queue, we're trespassing on the bounce
     * service's territory. But doing so is more robust than depending on the
     * bounce daemon to do the lookup for us, and for us to do the deleting
     * after we have received a successful status from the bounce service.
     * The bounce queue directory blocks are most likely in memory anyway. If
     * these lookups become a performance problem we will have to build an
     * in-core cache into the bounce daemon.
     * 
     * Don't bounce when the bounce log is empty. The bounce process obviously
     * failed, and the delivery agent will have requested that the message be
     * deferred.
     * 
     * Bounces are sent asynchronously to avoid stalling while the cleanup
     * daemon waits for the qmgr to accept the "new mail" trigger.
     */
    if (stat(mail_queue_path((VSTRING *) 0, MAIL_QUEUE_BOUNCE, message->queue_id), &st) == 0) {
	if (st.st_size == 0) {
	    if (mail_queue_remove(MAIL_QUEUE_BOUNCE, message->queue_id))
		msg_fatal("remove %s %s: %m",
			  MAIL_QUEUE_BOUNCE, message->queue_id);
	} else {
	    if (msg_verbose)
		msg_info("%s: bounce %s", myname, message->queue_id);
	    if (message->verp_delims == 0 || var_verp_bounce_off)
		abounce_flush(BOUNCE_FLAG_KEEP,
			      message->queue_name,
			      message->queue_id,
			      message->encoding,
			      message->errors_to,
			      qmgr_active_done_2_bounce_flush,
			      (char *) message);
	    else
		abounce_flush_verp(BOUNCE_FLAG_KEEP,
				   message->queue_name,
				   message->queue_id,
				   message->encoding,
				   message->errors_to,
				   message->verp_delims,
				   qmgr_active_done_2_bounce_flush,
				   (char *) message);
	    return;
	}
    }

    /*
     * Asynchronous processing does not reach this point.
     */
    qmgr_active_done_2_generic(message);
}
Exemple #2
0
int     mail_open_ok(const char *queue_name, const char *queue_id,
		             struct stat * statp, const char **path)
{
    if (mail_queue_name_ok(queue_name) == 0) {
	msg_warn("bad mail queue name: %s", queue_name);
	return (MAIL_OPEN_NO);
    }
    if (mail_queue_id_ok(queue_id) == 0)
	return (MAIL_OPEN_NO);


    /*
     * I really would like to look up the file attributes *after* opening the
     * file so that we could save one directory traversal on systems without
     * name-to-inode cache. However, we don't necessarily always want to open
     * the file.
     */
    *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);

    if (lstat(*path, statp) < 0) {
	if (errno != ENOENT)
	    msg_warn("%s: %m", *path);
	return (MAIL_OPEN_NO);
    }
    if (!S_ISREG(statp->st_mode)) {
	msg_warn("%s: uid %ld: not a regular file", *path, (long) statp->st_uid);
	return (MAIL_OPEN_NO);
    }
    if ((statp->st_mode & S_IRWXU) != MAIL_QUEUE_STAT_READY)
	return (MAIL_OPEN_NO);

    /*
     * Workaround for spurious "file has 2 links" warnings in showq. As
     * kernels have evolved from non-interruptible system calls towards
     * fine-grained locks, the showq command has become likely to observe a
     * file while the queue manager is in the middle of renaming it, at a
     * time when the file has links to both the old and new name. We now log
     * the warning only when the condition appears to be persistent.
     */
#define MINUTE_SECONDS	60			/* XXX should be centralized */

    if (statp->st_nlink > 1) {
	if (msg_verbose)
	    msg_info("%s: uid %ld: file has %d links", *path,
		     (long) statp->st_uid, (int) statp->st_nlink);
	else if (statp->st_ctime < time((time_t *) 0) - MINUTE_SECONDS)
	    msg_warn("%s: uid %ld: file has %d links", *path,
		     (long) statp->st_uid, (int) statp->st_nlink);
    }
    return (MAIL_OPEN_YES);
}
Exemple #3
0
void    qmgr_move(const char *src_queue, const char *dst_queue,
		          time_t time_stamp)
{
    const char *myname = "qmgr_move";
    SCAN_DIR *queue_dir;
    char   *queue_id;
    struct utimbuf tbuf;
    const char *path;

    if (strcmp(src_queue, dst_queue) == 0)
	msg_panic("%s: source queue %s is destination", myname, src_queue);
    if (msg_verbose)
	msg_info("start move queue %s -> %s", src_queue, dst_queue);

    queue_dir = scan_dir_open(src_queue);
    while ((queue_id = mail_scan_dir_next(queue_dir)) != 0) {
	if (mail_queue_id_ok(queue_id)) {
	    if (time_stamp > 0) {
		tbuf.actime = tbuf.modtime = time_stamp;
		path = mail_queue_path((VSTRING *) 0, src_queue, queue_id);
		if (utime(path, &tbuf) < 0) {
		    if (errno != ENOENT)
			msg_fatal("%s: update %s time stamps: %m", myname, path);
		    msg_warn("%s: update %s time stamps: %m", myname, path);
		    continue;
		}
	    }
	    if (mail_queue_rename(queue_id, src_queue, dst_queue)) {
		if (errno != ENOENT)
		    msg_fatal("%s: rename %s from %s to %s: %m",
			      myname, queue_id, src_queue, dst_queue);
		msg_warn("%s: rename %s from %s to %s: %m",
			 myname, queue_id, src_queue, dst_queue);
		continue;
	    }
	    if (msg_verbose)
		msg_info("%s: moved %s from %s to %s",
			 myname, queue_id, src_queue, dst_queue);
	} else {
	    msg_warn("%s: ignored: queue %s id %s",
		     myname, src_queue, queue_id);
	}
    }
    scan_dir_close(queue_dir);

    if (msg_verbose)
	msg_info("end move queue %s -> %s", src_queue, dst_queue);
}
Exemple #4
0
static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
				         DSN *dsn)
{
    MSG_STATS stats;

    if (cleanup_trace_path == 0) {
	cleanup_trace_path = vstring_alloc(10);
	mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE,
			state->queue_id);
    }
    if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id,
		     CLEANUP_MSG_STATS(&stats, state),
		     rcpt, "none", dsn) != 0) {
	msg_warn("%s: trace logfile update error", state->queue_id);
	state->errs |= CLEANUP_STAT_WRITE;
    }
}
Exemple #5
0
static int flush_refresh_service(int max_age)
{
    const char *myname = "flush_refresh_service";
    SCAN_DIR *scan;
    char   *site_path;
    struct stat st;
    VSTRING *path = vstring_alloc(10);

    scan = scan_dir_open(MAIL_QUEUE_FLUSH);
    while ((site_path = mail_scan_dir_next(scan)) != 0) {
	if (!mail_queue_id_ok(site_path))
	    continue;				/* XXX grumble. */
	mail_queue_path(path, MAIL_QUEUE_FLUSH, site_path);
	if (stat(STR(path), &st) < 0) {
	    if (errno != ENOENT)
		msg_warn("%s: stat %s: %m", myname, STR(path));
	    else if (msg_verbose)
		msg_info("%s: %s: %m", myname, STR(path));
	    continue;
	}
	if (st.st_size == 0) {
	    if (st.st_mtime + var_fflush_purge < event_time()) {
		if (unlink(STR(path)) < 0)
		    msg_warn("remove logfile %s: %m", STR(path));
		else if (msg_verbose)
		    msg_info("%s: unlink %s, empty and unchanged for %d days",
			     myname, STR(path), var_fflush_purge / 86400);
	    } else if (msg_verbose)
		msg_info("%s: skip logfile %s - empty log", myname, site_path);
	} else if (st.st_atime + max_age < event_time()) {
	    if (msg_verbose)
		msg_info("%s: flush logfile %s", myname, site_path);
	    flush_send_path(site_path, REFRESH_ONLY);
	} else {
	    if (msg_verbose)
		msg_info("%s: skip logfile %s, unread for <%d hours(s) ",
			 myname, site_path, max_age / 3600);
	}
    }
    scan_dir_close(scan);
    vstring_free(path);

    return (FLUSH_STAT_OK);
}
Exemple #6
0
void    bounce_cleanup_register(char *service, char *queue_id)
{
    char   *myname = "bounce_cleanup_register";

    /*
     * Sanity checks.
     */
    if (bounce_cleanup_path)
	msg_panic("%s: nested call", myname);

    /*
     * Save a copy of the logfile path, and of the last callback function
     * pointer registered with the run-time error handler.
     */
    bounce_cleanup_path = vstring_alloc(10);
    (void) mail_queue_path(bounce_cleanup_path, service, queue_id);
    bounce_cleanup_func = msg_cleanup(bounce_cleanup_callback);
    signal(SIGTERM, bounce_cleanup_sig);
}
static void qmgr_active_defer(const char *queue_name, const char *queue_id,
			              const char *dest_queue, int delay)
{
    const char *myname = "qmgr_active_defer";
    const char *path;
    struct utimbuf tbuf;

    if (msg_verbose)
	msg_info("wakeup %s after %ld secs", queue_id, (long) delay);

    tbuf.actime = tbuf.modtime = event_time() + delay;
    path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
    if (utime(path, &tbuf) < 0 && errno != ENOENT)
	msg_fatal("%s: update %s time stamps: %m", myname, path);
    if (mail_queue_rename(queue_id, queue_name, dest_queue)) {
	if (errno != ENOENT)
	    msg_fatal("%s: rename %s from %s to %s: %m", myname,
		      queue_id, queue_name, dest_queue);
	msg_warn("%s: rename %s from %s to %s: %m", myname,
		 queue_id, queue_name, dest_queue);
    } else if (msg_verbose) {
	msg_info("%s: defer %s", myname, queue_id);
    }
}
Exemple #8
0
static int flush_one_file(const char *queue_id, VSTRING *queue_file,
			          struct utimbuf * tbuf, int how)
{
    const char *myname = "flush_one_file";
    const char *queue_name;
    const char *path;

    /*
     * Some other instance of this program may flush some logfile and may
     * just have moved this queue file to the incoming queue.
     */
    for (queue_name = MAIL_QUEUE_DEFERRED; /* see below */ ;
	 queue_name = MAIL_QUEUE_INCOMING) {
	path = mail_queue_path(queue_file, queue_name, queue_id);
	if (utime(path, tbuf) == 0)
	    break;
	if (errno != ENOENT)
	    msg_warn("%s: update %s time stamps: %m", myname, path);
	if (STREQ(queue_name, MAIL_QUEUE_INCOMING))
	    return (0);
    }

    /*
     * With the UNTHROTTLE_AFTER strategy, we leave it up to the queue
     * manager to unthrottle transports and queues as it reads recipients
     * from a queue file. We request this unthrottle operation by setting the
     * group read permission bit.
     * 
     * Note: we must avoid using chmod(). It is not only slower than fchmod()
     * but it is also less secure. With chmod(), an attacker could repeatedly
     * send requests to the flush server and trick it into changing
     * permissions of non-queue files, by exploiting a race condition.
     * 
     * We use safe_open() because we don't validate the file content before
     * modifying the file status.
     */
    if (how & UNTHROTTLE_AFTER) {
	VSTRING *why;
	struct stat st;
	VSTREAM *fp;

	for (why = vstring_alloc(1); /* see below */ ;
	     queue_name = MAIL_QUEUE_INCOMING,
	     path = mail_queue_path(queue_file, queue_name, queue_id)) {
	    if ((fp = safe_open(path, O_RDWR, 0, &st, -1, -1, why)) != 0)
		break;
	    if (errno != ENOENT)
		msg_warn("%s: open %s: %s", myname, path, STR(why));
	    if (errno != ENOENT || STREQ(queue_name, MAIL_QUEUE_INCOMING)) {
		vstring_free(why);
		return (0);
	    }
	}
	vstring_free(why);
	if ((st.st_mode & MAIL_QUEUE_STAT_READY) != MAIL_QUEUE_STAT_READY) {
	    (void) vstream_fclose(fp);
	    return (0);
	}
	if (fchmod(vstream_fileno(fp), st.st_mode | MAIL_QUEUE_STAT_UNTHROTTLE) < 0)
	    msg_warn("%s: fchmod %s: %m", myname, path);
	(void) vstream_fclose(fp);
    }

    /*
     * Move the file to the incoming queue, if it isn't already there.
     */
    if (STREQ(queue_name, MAIL_QUEUE_INCOMING) == 0
	&& mail_queue_rename(queue_id, queue_name, MAIL_QUEUE_INCOMING) < 0
	&& errno != ENOENT)
	msg_warn("%s: rename from %s to %s: %m",
		 path, queue_name, MAIL_QUEUE_INCOMING);

    /*
     * If we got here, we achieved something, so let's claim succes.
     */
    return (1);
}