-/* $Cambridge: exim/src/src/queue.c,v 1.2 2004/11/05 14:59:12 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions that operate on the input queue. */
+/* Routines with knowlege of spool layout */
+
+static void
+spool_pname_buf(uschar * buf, int len)
+{
+snprintf(CS buf, len, "%s/%s/input", spool_directory, queue_name);
+}
+
+static uschar *
+spool_dname(const uschar * purpose, uschar * subdir)
+{
+return string_sprintf("%s/%s/%s/%s",
+ spool_directory, queue_name, purpose, subdir);
+}
+
+uschar *
+spool_sname(const uschar * purpose, uschar * subdir)
+{
+return string_sprintf("%s%s%s%s%s",
+ queue_name, *queue_name ? "/" : "",
+ purpose,
+ *subdir ? "/" : "", subdir);
+}
+
+uschar *
+spool_fname(const uschar * purpose, uschar * subdir, uschar * fname, uschar * suffix)
+{
+return string_sprintf("%s/%s/%s/%s/%s%s",
+ spool_directory, queue_name, purpose, subdir, fname, suffix);
+}
+
+
+
+
+#ifndef COMPILE_UTILITY
+
/* The number of nodes to use for the bottom-up merge sort when a list of queue
items is to be ordered. The code for this sort was contributed as a patch by
Michael Haardt. */
/* Set up prototype for the directory name. */
-sprintf(CS buffer, "%s/input", spool_directory);
+spool_pname_buf(buffer, sizeof(buffer));
+buffer[sizeof(buffer) - 3] = 0;
subptr = Ustrlen(buffer);
buffer[subptr+2] = 0; /* terminator for lengthened name */
buffer[subptr+1] = subdirchar;
}
- dd = opendir(CS buffer);
- if (dd == NULL) continue;
+ DEBUG(D_queue_run) debug_printf("looking in %s\n", buffer);
+ if (!(dd = opendir(CS buffer)))
+ continue;
/* Now scan the directory. */
{
if (!split_spool_directory && count <= 2)
{
+ uschar subdir[2];
+
rmdir(CS buffer);
- sprintf(CS big_buffer, "%s/msglog/%c", spool_directory, subdirchar);
- rmdir(CS big_buffer);
+ subdir[0] = subdirchar; subdir[1] = 0;
+ rmdir(CS spool_dname(US"msglog", subdir));
}
if (subdiroffset > 0) break; /* Single sub-directory */
}
}
log_detail = string_copy(big_buffer);
- log_write(L_queue_run, LOG_MAIN, "Start queue run: %s", log_detail);
+ if (*queue_name)
+ log_write(L_queue_run, LOG_MAIN, "Start '%s' queue run: %s",
+ queue_name, log_detail);
+ else
+ log_write(L_queue_run, LOG_MAIN, "Start queue run: %s", log_detail);
}
/* If deliver_selectstring is a regex, compile it. */
load_average = os_getloadavg();
if (load_average > deliver_queue_load_max)
{
- log_write(0, LOG_MAIN, "abandon queue run (load %.2f, max %.2f)",
+ log_write(L_queue_run, LOG_MAIN, "Abandon queue run: %s (load %.2f, max %.2f)",
+ log_detail,
(double)load_average/1000.0,
(double)deliver_queue_load_max/1000.0);
i = subcount; /* Don't process other directories */
/* Check that the message still exists */
message_subdir[0] = f->dir_uschar;
- sprintf(CS buffer, "%s/input/%s/%s", spool_directory, message_subdir,
- f->text);
- if (Ustat(buffer, &statbuf) < 0) continue;
+ if (Ustat(spool_fname(US"input", message_subdir, f->text, US""), &statbuf) < 0)
+ continue;
/* There are some tests that require the reading of the header file. Ensure
the store used is scavenged afterwards so that this process doesn't keep
if (queue_run_pipe == 0)
{
queue_run_pipe = dup(queue_run_pipe);
- close(0);
+ (void)close(0);
}
/* Before forking to deliver the message, ensure any open and cached
if ((pid = fork()) == 0)
{
int rc;
+ if (running_in_test_harness) millisleep(100);
(void)close(pfd[pipe_read]);
rc = deliver_message(f->text, force_delivery, FALSE);
_exit(rc == DELIVER_NOT_ATTEMPTED);
/* Close the writing end of the synchronizing pipe in this process,
then wait for the first level process to terminate. */
- close(pfd[pipe_write]);
+ (void)close(pfd[pipe_write]);
set_process_info("running queue: waiting for %s (%d)", f->text, pid);
while (wait(&status) != pid);
the mere fact that read() unblocks is enough. */
set_process_info("running queue: waiting for children of %d", pid);
- (void)read(pfd[pipe_read], buffer, sizeof(buffer));
+ if (read(pfd[pipe_read], buffer, sizeof(buffer)) > 0)
+ log_write(0, LOG_MAIN|LOG_PANIC, "queue run: unexpected data on pipe");
(void)close(pfd[pipe_read]);
set_process_info("running queue");
/* At top level, log the end of the run. */
-if (!recurse) log_write(L_queue_run, LOG_MAIN, "End queue run: %s", log_detail);
+if (!recurse)
+ if (*queue_name)
+ log_write(L_queue_run, LOG_MAIN, "End '%s' queue run: %s",
+ queue_name, log_detail);
+ else
+ log_write(L_queue_run, LOG_MAIN, "End queue run: %s", log_detail);
}
int ptr;
FILE *jread;
struct stat statbuf;
+ uschar * fname = spool_fname(US"input", message_subdir, f->text, US"");
- sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
- f->text);
- ptr = Ustrlen(big_buffer)-1;
- big_buffer[ptr] = 'D';
+ ptr = Ustrlen(fname)-1;
+ fname[ptr] = 'D';
/* Add the data size to the header size; don't count the file name
at the start of the data file, but add one for the notional blank line
that precedes the data. */
- if (Ustat(big_buffer, &statbuf) == 0)
+ if (Ustat(fname, &statbuf) == 0)
size = message_size + statbuf.st_size - SPOOL_DATA_START_OFFSET + 1;
i = (now - received_time)/60; /* minutes on queue */
if (i > 90)
/* Collect delivered addresses from any J file */
- big_buffer[ptr] = 'J';
- jread = Ufopen(big_buffer, "rb");
+ fname[ptr] = 'J';
+ jread = Ufopen(fname, "rb");
if (jread != NULL)
{
while (Ufgets(big_buffer, big_buffer_size, jread) != NULL)
big_buffer[n-1] = 0;
tree_add_nonrecipient(big_buffer);
}
- fclose(jread);
+ (void)fclose(jread);
}
}
if (save_errno == ERRNO_SPOOLFORMAT)
{
struct stat statbuf;
- sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
- f->text);
- if (Ustat(big_buffer, &statbuf) == 0)
- {
- int size = statbuf.st_size; /* Because might be a long */
- printf("*** spool format error: size=%d ***", size);
- }
+ uschar * fname = spool_fname(US"input", message_subdir, f->text, US"");
+
+ if (Ustat(fname, &statbuf) == 0)
+ printf("*** spool format error: size=" OFF_T_FMT " ***",
+ statbuf.st_size);
else printf("*** spool format error ***");
}
else printf("*** spool read error: %s ***", strerror(save_errno));
* Act on a specific message *
*************************************************/
-/* Actions that require a list of addresses make use of
-argv/argc/recipients_arg. Other actions do not. This function does its
-own authority checking.
+/* Actions that require a list of addresses make use of argv/argc/
+recipients_arg. Other actions do not. This function does its own
+authority checking.
Arguments:
id id of the message to work on
uschar *doing = NULL;
uschar *username;
uschar *errmsg;
-uschar spoolname[256];
+uschar spoolname[32];
/* Set the global message_id variable, used when re-writing spool files. This
also causes message ids to be added to log messages. */
for (i = 0; i < 2; i++)
{
- message_subdir[0] = (split_spool_directory == (i == 0))? id[5] : 0;
- sprintf(CS spoolname, "%s/%s/%s/%s%s", spool_directory, subdirectory,
- message_subdir, id, suffix);
- fd = Uopen(spoolname, O_RDONLY, 0);
- if (fd >= 0) break;
- if (i == 0) continue;
+ message_subdir[0] = split_spool_directory == (i == 0) ? id[5] : 0;
+ if ((fd = Uopen(spool_fname(subdirectory, message_subdir, id, suffix),
+ O_RDONLY, 0)) >= 0)
+ break;
+ if (i == 0)
+ continue;
+
printf("Failed to open %s file for %s%s: %s\n", subdirectory, id, suffix,
strerror(errno));
if (action == MSG_SHOW_LOG && !message_logs)
}
while((rc = read(fd, big_buffer, big_buffer_size)) > 0)
- write(fileno(stdout), big_buffer, rc);
+ rc = write(fileno(stdout), big_buffer, rc);
- close(fd);
+ (void)close(fd);
return TRUE;
}
only if the action is remove and the user is an admin user, to allow for
tidying up broken states. */
-if (!spool_open_datafile(id))
- {
+if ((deliver_datafile = spool_open_datafile(id)) < 0)
if (errno == ENOENT)
{
yield = FALSE;
strerror(errno));
return FALSE;
}
- }
/* Read the spool header file for the message. Again, continue after an
error only in the case of deleting by an administrator. Setting the third
printf("Spool format error for %s\n", spoolname);
if (action != MSG_REMOVE || !admin_user)
{
- close(deliver_datafile);
+ (void)close(deliver_datafile);
deliver_datafile = -1;
return FALSE;
}
if (!admin_user && (action != MSG_REMOVE || real_uid != originator_uid))
{
printf("Permission denied\n");
- close(deliver_datafile);
+ (void)close(deliver_datafile);
deliver_datafile = -1;
return FALSE;
}
/* Take the necessary action. */
-printf("Message %s ", id);
+if (action != MSG_SHOW_COPY) printf("Message %s ", id);
switch(action)
{
+ case MSG_SHOW_COPY:
+ deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
+ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
+ transport_write_message(1, NULL, 0);
+ break;
+
+
case MSG_FREEZE:
if (deliver_freeze)
{
operation, just run everything twice. */
case MSG_REMOVE:
- message_subdir[0] = id[5];
- for (j = 0; j < 2; message_subdir[0] = 0, j++)
{
- sprintf(CS spoolname, "%s/msglog/%s/%s", spool_directory, message_subdir, id);
- if (Uunlink(spoolname) < 0)
+ uschar suffix[3];
+
+ suffix[0] = '-';
+ suffix[2] = 0;
+ message_subdir[0] = id[5];
+
+ for (j = 0; j < 2; message_subdir[0] = 0, j++)
{
- if (errno != ENOENT)
- {
- yield = FALSE;
- printf("Error while removing %s: %s\n", spoolname,
- strerror(errno));
- }
+ uschar * fname = spool_fname(US"msglog", message_subdir, id, US"");
+
+ DEBUG(D_any) debug_printf(" removing %s", fname);
+ if (Uunlink(fname) < 0)
+ {
+ if (errno != ENOENT)
+ {
+ yield = FALSE;
+ printf("Error while removing %s: %s\n", fname, strerror(errno));
+ }
+ else DEBUG(D_any) debug_printf(" (no file)\n");
+ }
+ else
+ {
+ removed = TRUE;
+ DEBUG(D_any) debug_printf(" (ok)\n");
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ uschar * fname;
+
+ suffix[1] = (US"DHJ")[i];
+ fname = spool_fname(US"input", message_subdir, id, suffix);
+
+ DEBUG(D_any) debug_printf(" removing %s", fname);
+ if (Uunlink(fname) < 0)
+ {
+ if (errno != ENOENT)
+ {
+ yield = FALSE;
+ printf("Error while removing %s: %s\n", fname, strerror(errno));
+ }
+ else DEBUG(D_any) debug_printf(" (no file)\n");
+ }
+ else
+ {
+ removed = TRUE;
+ DEBUG(D_any) debug_printf(" (done)\n");
+ }
+ }
}
- else removed = TRUE;
- for (i = 0; i < 3; i++)
+ /* In the common case, the datafile is open (and locked), so give the
+ obvious message. Otherwise be more specific. */
+
+ if (deliver_datafile >= 0) printf("has been removed\n");
+ else printf("has been removed or did not exist\n");
+ if (removed)
{
- sprintf(CS spoolname, "%s/input/%s/%s-%c", spool_directory, message_subdir,
- id, "DHJ"[i]);
- if (Uunlink(spoolname) < 0)
- {
- if (errno != ENOENT)
- {
- yield = FALSE;
- printf("Error while removing %s: %s\n", spoolname,
- strerror(errno));
- }
- }
- else removed = TRUE;
+ log_write(0, LOG_MAIN, "removed by %s", username);
+ log_write(0, LOG_MAIN, "Completed");
}
+ break;
}
- /* In the common case, the datafile is open (and locked), so give the
- obvious message. Otherwise be more specific. */
-
- if (deliver_datafile >= 0) printf("has been removed\n");
- else printf("has been removed or did not exist\n");
- if (removed)
- {
- log_write(0, LOG_MAIN, "removed by %s", username);
- log_write(0, LOG_MAIN, "Completed");
- }
- break;
-
case MSG_MARK_ALL_DELIVERED:
for (i = 0; i < recipients_count; i++)
{
if (action == MSG_ADD_RECIPIENT)
{
+#ifdef SUPPORT_I18N
+ if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE;
+#endif
receive_add_recipient(recipient, -1);
log_write(0, LOG_MAIN, "recipient <%s> added by %s",
recipient, username);
}
else /* MSG_EDIT_SENDER */
{
+#ifdef SUPPORT_I18N
+ if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE;
+#endif
sender_address = recipient;
log_write(0, LOG_MAIN, "sender address changed to <%s> by %s",
recipient, username);
}
if (yield)
- {
if (spool_write_header(id, SW_MODIFYING, &errmsg) >= 0)
printf("has been modified\n");
else
yield = FALSE;
printf("- while %s: %s\n", doing, errmsg);
}
- }
break;
}
/* Closing the datafile releases the lock and permits other processes
to operate on the message (if it still exists). */
-close(deliver_datafile);
-deliver_datafile = -1;
+if (deliver_datafile >= 0)
+ {
+ (void)close(deliver_datafile);
+ deliver_datafile = -1;
+ }
return yield;
}
BOOL *set;
int sep = 0;
struct stat statbuf;
-uschar *s, *ss, *name;
+const uschar *s;
+uschar *ss, *name;
uschar buffer[1024];
if (queue_only_file == NULL) return;
}
}
+#endif /*!COMPILE_UTILITY*/
+
/* End of queue.c */