From 5cb8cbc6b514db2972dffadc30b3c7f2b7fc1dcb Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Wed, 17 Nov 2004 14:32:25 +0000 Subject: [PATCH] Added $spool_size, $log_size, $spool_inodes, $log_inodes. --- doc/doc-txt/ChangeLog | 7 +- doc/doc-txt/NewStuff | 21 +++- src/src/expand.c | 26 ++++- src/src/functions.h | 3 +- src/src/receive.c | 232 ++++++++++++++++++++++++------------------ 5 files changed, 184 insertions(+), 105 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 249ef7cfe..4f49ef8e5 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.28 2004/11/12 16:54:55 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.29 2004/11/17 14:32:25 ph10 Exp $ Change log file for Exim from version 4.21 ------------------------------------------- @@ -117,6 +117,11 @@ Exim version 4.44 host was specified on an smtp transport, and looking it up yielded more than one IP address. +31. Re-factored the code for checking spool and log partition space into a + function that finds that data and another that does the check. The former + is then used to implement four new variables: $spool_space, $log_space, + $spool_inodes, and $log_inodes. + Exim version 4.43 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 6de893814..7421078bc 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/NewStuff,v 1.9 2004/11/11 11:40:36 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/NewStuff,v 1.10 2004/11/17 14:32:25 ph10 Exp $ New Features in Exim -------------------- @@ -70,6 +70,25 @@ Version 4.44 9. $host_address is now set to the target address during the checking of ignore_target_hosts. +10. There are four new variables called $spool_space, $log_space, + $spool_inodes, and $log_inodes. The first two contain the amount of free + space in the disk partitions where Exim has its spool directory and log + directory, respectively. (When these are in the same partition, the values + will, of course, be the same.) The second two variables contain the numbers + of free inodes in the respective partitions. + + NOTE: Because disks can nowadays be very large, the values in the space + variables are in kilobytes rather than in bytes. Thus, for example, to + check in an ACL that there is at least 50M free on the spool, you would + write: + + condition = ${if > {$spool_space}{50000}{yes}{no}} + + The values are recalculated whenever any of these variables is referenced. + If the relevant file system does not have the concept of inodes, the value + of those variables is -1. If the operating system does not have the ability + to find the amount of free space (only true for experimental systems), the + space value is -1. Version 4.43 diff --git a/src/src/expand.c b/src/src/expand.c index 2575247b3..0ca5b4cc2 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.3 2004/11/05 16:53:28 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.4 2004/11/17 14:32:25 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -283,7 +283,9 @@ enum { vtype_reply, /* value not used; get reply from headers */ vtype_pid, /* value not used; result is pid */ vtype_host_lookup, /* value not used; get host name */ - vtype_load_avg /* value not used; result is int from os_getloadavg */ + vtype_load_avg, /* value not used; result is int from os_getloadavg */ + vtype_pspace, /* partition space; value is T/F for spool/log */ + vtype_pinodes /* partition inodes; value is T/F for spool/log */ }; /* This table must be kept in alphabetical order. */ @@ -352,6 +354,8 @@ static var_entry var_table[] = { { "local_user_gid", vtype_gid, &local_user_gid }, { "local_user_uid", vtype_uid, &local_user_uid }, { "localhost_number", vtype_int, &host_number }, + { "log_inodes", vtype_pinodes, (void *)FALSE }, + { "log_space", vtype_pspace, (void *)FALSE }, { "mailstore_basename", vtype_stringptr, &mailstore_basename }, { "message_age", vtype_int, &message_age }, { "message_body", vtype_msgbody, &message_body }, @@ -421,6 +425,8 @@ static var_entry var_table[] = { { "sn8", vtype_filter_int, &filter_sn[8] }, { "sn9", vtype_filter_int, &filter_sn[9] }, { "spool_directory", vtype_stringptr, &spool_directory }, + { "spool_inodes", vtype_pinodes, (void *)TRUE }, + { "spool_space", vtype_pspace, (void *)TRUE }, { "thisaddress", vtype_stringptr, &filter_thisaddress }, { "tls_certificate_verified", vtype_int, &tls_certificate_verified }, { "tls_cipher", vtype_stringptr, &tls_cipher }, @@ -1310,6 +1316,22 @@ while (last > first) s[ptr] = 0; /* string_cat() leaves room */ } return s; + + case vtype_pspace: + { + int inodes; + sprintf(CS var_buffer, "%d", + receive_statvfs((BOOL)(var_table[middle].value), &inodes)); + } + return var_buffer; + + case vtype_pinodes: + { + int inodes; + (void) receive_statvfs((BOOL)(var_table[middle].value), &inodes); + sprintf(CS var_buffer, "%d", inodes); + } + return var_buffer; } } diff --git a/src/src/functions.h b/src/src/functions.h index 017ec8720..85fc17607 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.2 2004/11/04 12:19:48 ph10 Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.3 2004/11/17 14:32:25 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -180,6 +180,7 @@ extern void receive_bomb_out(uschar *); extern BOOL receive_check_fs(int); extern BOOL receive_check_set_sender(uschar *); extern BOOL receive_msg(BOOL); +extern int receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); extern BOOL regex_match_and_setup(const pcre *, uschar *, int, int); extern const pcre *regex_must_compile(uschar *, BOOL, BOOL); diff --git a/src/src/receive.c b/src/src/receive.c index 0483bd5f5..f9a2d3c69 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.3 2004/10/19 11:04:26 ph10 Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.4 2004/11/17 14:32:25 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -92,90 +92,55 @@ return /************************************************* -* Check space on spool and log partitions * +* Read space info for a partition * *************************************************/ -/* This function is called before accepting a message; if any thresholds are -set, it checks them. If a message_size is supplied, it checks that there is -enough space for that size plus the threshold - i.e. that the message won't -reduce the space to the threshold. Not all OS have statvfs(); for those that -don't, this function always returns TRUE. For some OS the old function and -struct name statfs is used; that is handled by a macro, defined in exim.h. +/* This function is called by receive_check_fs() below, and also by string +expansion for variables such as $spool_space. The field names for the statvfs +structure are macros, because not all OS have F_FAVAIL and it seems tidier to +have macros for F_BAVAIL and F_FILES as well. Some kinds of file system do not +have inodes, and they return -1 for the number available. -Arguments: - msg_size the (estimated) size of an incoming message +Later: It turns out that some file systems that do not have the concept of +inodes return 0 rather than -1. Such systems should also return 0 for the total +number of inodes, so we require that to be greater than zero before returning +an inode count. -Returns: FALSE if there isn't enough space, or if the information cannot - be obtained - TRUE if no check was done or there is enough space +Arguments: + isspool TRUE for spool partition, FALSE for log partition + inodeptr address of int to receive inode count; -1 if there isn't one + +Returns: available on-root space, in kilobytes + -1 for log partition if there isn't one + +All values are -1 if the STATFS functions are not available. */ -BOOL -receive_check_fs(int msg_size) +int +receive_statvfs(BOOL isspool, int *inodeptr) { #ifdef HAVE_STATFS -BOOL rc = TRUE; struct STATVFS statbuf; +uschar *path; +uschar *name; +uschar buffer[1024]; -memset(&statbuf, 0, sizeof(statbuf)); +/* The spool directory must always exist. */ -/* The field names are macros, because not all OS have F_FAVAIL and it seems -tidier to have macros for F_BAVAIL and F_FILES as well. Some kinds of file -server do not have inodes, and they return -1 for the number available, so we -do the check only when this field is non-negative. - -Later: It turns out that some file systems that do not have the concept of -inodes return 0 rather than -1. Such systems should also return 0 for the total -number of inodes, so we require that to be greater than zero before doing the -test. */ - -if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) +if (isspool) { - if (STATVFS(CS spool_directory, &statbuf) != 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat " - "spool directory %s: %s", spool_directory, strerror(errno)); - smtp_closedown(US"spool directory problem"); - exim_exit(EXIT_FAILURE); - } - - /* check_spool_space is held in K because disks are getting huge */ - - if (statbuf.F_BAVAIL < (unsigned long) - ((((double)check_spool_space) * 1024.0 + (double)msg_size) / - (double)statbuf.F_FRSIZE) - || - (statbuf.F_FILES > 0 && - statbuf.F_FAVAIL >= 0 && - statbuf.F_FAVAIL < check_spool_inodes)) - rc = FALSE; - - DEBUG(D_receive) - debug_printf("spool directory %s space = %d blocks; inodes = %d; " - "check_space = %dK (%d blocks); inodes = %d; msg_size = %d (%d blocks)\n", - spool_directory, (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL, - check_spool_space, - (int)(((double)check_spool_space * 1024.0) / (double)statbuf.F_FRSIZE), - check_spool_inodes, msg_size, (int)(msg_size / statbuf.F_FRSIZE)); - - if (!rc) - { - log_write(0, LOG_MAIN, "spool directory space check failed: space=%d " - "inodes=%d", (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL); - return FALSE; - } - } - + path = spool_directory; + name = US"spool"; + } + /* Need to cut down the log file path to the directory, and to ignore any appearance of "syslog" in it. */ -if (check_log_space > 0 || check_log_inodes > 0) +else { - uschar *path; int sep = ':'; /* Not variable - outside scripts use */ - uschar *cp; uschar *p = log_file_path; - uschar buffer[1024]; + name = US"log"; /* An empty log_file_path means "use the default". This is the same as an empty item in a list. */ @@ -186,50 +151,117 @@ if (check_log_space > 0 || check_log_inodes > 0) if (Ustrcmp(path, "syslog") != 0) break; } - if (path == NULL) return TRUE; /* No log files, so no problem */ - - /* An empty string means use the default */ + if (path == NULL) /* No log files */ + { + *inodeptr = -1; + return -1; + } - if (path[0] == 0) - path = string_sprintf("%s/log/%%slog", spool_directory); + /* An empty string means use the default, which is in the spool directory. + But don't just use the spool directory, as it is possible that the log + subdirectory has been symbolically linked elsewhere. */ - if ((cp = Ustrrchr(path, '/')) == NULL) + if (path[0] == 0) { - DEBUG(D_receive) debug_printf("cannot find slash in %s\n", path); - return FALSE; - } - *cp = 0; - - if (STATVFS(CS path, &statbuf) != 0) + sprintf(CS buffer, CS"%s/log", CS spool_directory); + path = buffer; + } + else { - log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat " - "log directory %s: %s", path, strerror(errno)); - smtp_closedown(US"log directory problem"); - exim_exit(EXIT_FAILURE); - } + uschar *cp; + if ((cp = Ustrrchr(path, '/')) != NULL) *cp = 0; + } + } + +/* We now have the patch; do the business */ - if (statbuf.F_BAVAIL < (unsigned long) - (((double)check_log_space * 1024.0) / (double)statbuf.F_FRSIZE) - || - statbuf.F_FAVAIL < check_log_inodes) rc = FALSE; +memset(&statbuf, 0, sizeof(statbuf)); +if (STATVFS(CS path, &statbuf) != 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat " + "%s directory %s: %s", name, spool_directory, strerror(errno)); + smtp_closedown(US"spool or log directory problem"); + exim_exit(EXIT_FAILURE); + } + +*inodeptr = (statbuf.F_FILES > 0)? statbuf.F_FAVAIL : -1; + +/* Disks are getting huge. Take care with computing the size in kilobytes. */ + +return (int)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0); + +/* Unable to find partition sizes in this environment. */ + +#else +*inodeptr = -1; +return -1; +#endif +} + + + + +/************************************************* +* Check space on spool and log partitions * +*************************************************/ + +/* This function is called before accepting a message; if any thresholds are +set, it checks them. If a message_size is supplied, it checks that there is +enough space for that size plus the threshold - i.e. that the message won't +reduce the space to the threshold. Not all OS have statvfs(); for those that +don't, this function always returns TRUE. For some OS the old function and +struct name statfs is used; that is handled by a macro, defined in exim.h. + +Arguments: + msg_size the (estimated) size of an incoming message + +Returns: FALSE if there isn't enough space, or if the information cannot + be obtained + TRUE if no check was done or there is enough space +*/ + +BOOL +receive_check_fs(int msg_size) +{ +int space, inodes; + +if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) + { + space = receive_statvfs(TRUE, &inodes); + DEBUG(D_receive) - debug_printf("log directory %s space = %d blocks; inodes = %d; " - "check_space = %dK (%d blocks); inodes = %d\n", - path, (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL, - check_log_space, - (int)(((double)check_log_space * 1024.0) / (double)statbuf.F_FRSIZE), - check_log_inodes); - - if (!rc) - { - log_write(0, LOG_MAIN, "log directory space check failed: space=%d " - "inodes=%d", (int)statbuf.F_BAVAIL, (int)statbuf.F_FAVAIL); + debug_printf("spool directory space = %dK inodes = %d " + "check_space = %dK inodes = %d msg_size = %d\n", + space, inodes, check_spool_space, check_spool_inodes, msg_size); + + if ((space >= 0 && space < check_spool_space) || + (inodes >= 0 && inodes < check_spool_inodes)) + { + log_write(0, LOG_MAIN, "spool directory space check failed: space=%d " + "inodes=%d", space, inodes); return FALSE; } } -#endif +if (check_log_space > 0 || check_log_inodes > 0) + { + space = receive_statvfs(FALSE, &inodes); + + DEBUG(D_receive) + debug_printf("log directory space = %dK inodes = %d " + "check_space = %dK inodes = %d\n", + space, inodes, check_log_space, check_log_inodes); + + if ((space >= 0 && space < check_log_space) || + (inodes >= 0 && inodes < check_log_inodes)) + { + log_write(0, LOG_MAIN, "log directory space check failed: space=%d " + "inodes=%d", space, inodes); + return FALSE; + } + } + return TRUE; } -- 2.25.1