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 249ef7c..4f49ef8 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
 -------------------------------------------
@@ -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
 -----------------
index 6de8938..7421078 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
 --------------------
@@ -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
index 2575247..0ca5b4c 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    *
@@ -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;
     }
   }
 
index 017ec87..85fc176 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    *
@@ -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);
index 0483bd5..f9a2d3c 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    *
@@ -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;
 }