Added $spool_size, $log_size, $spool_inodes, $log_inodes.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 17 Nov 2004 14:32:25 +0000 (14:32 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 17 Nov 2004 14:32:25 +0000 (14:32 +0000)
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/expand.c
src/src/functions.h
src/src/receive.c

index 249ef7cfeb3e8408bce0a5b8cbaf4284ff7ff2b1..4f49ef8e5598561b0c57668643c45a649cb231bd 100644 (file)
@@ -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
 -------------------------------------------
 
 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.
 
     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
 -----------------
 
 Exim version 4.43
 -----------------
index 6de8938141f1b16386c79d65d158c11ec65e4e27..7421078bc6b93a9b79bdfba6553c74849f54137a 100644 (file)
@@ -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
 --------------------
 
 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.
 
  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
 
 
 Version 4.43
index 2575247b3c4e2b1c813e810f5a7977263f4ad7a4..0ca5b4cc2b0eb917621433f623a9275207bebdcb 100644 (file)
@@ -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    *
 
 /*************************************************
 *     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_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. */
   };
 
 /* 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 },
   { "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 },
   { "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 },
   { "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 },
   { "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;
       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;
     }
   }
 
     }
   }
 
index 017ec87206942ca584f043acfb0881643d8bcaa9..85fc1760707b570a8abee12f71fae0e4d07efe4a 100644 (file)
@@ -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    *
 
 /*************************************************
 *     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 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);
 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);
index 0483bd5f5b052364b22d0ffc3983a16e00fef529..f9a2d3c69e4c82ac515cfa9c43f7ce671f8119e3 100644 (file)
@@ -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    *
 
 /*************************************************
 *     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
 {
 #ifdef HAVE_STATFS
-BOOL rc = TRUE;
 struct STATVFS statbuf;
 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. */
 
 /* 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 */
   int sep = ':';              /* Not variable - outside scripts use */
-  uschar *cp;
   uschar *p = log_file_path;
   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. */
 
   /* 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 (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(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;
     }
   }
 
     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;
 }
 
 return TRUE;
 }