applied http://savannah.nongnu.org/patch/?func=detailitem&item_id=4814
authorEnrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Sat, 21 Jan 2006 13:22:23 +0000 (13:22 +0000)
committerEnrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
Sat, 21 Jan 2006 13:22:23 +0000 (13:22 +0000)
(Daniel Hokka Zakrisson):

This patch lets users configure disk limits in
/etc/vservers/<name>/dlimits/*/, and have them set on the start of the
vserver, as well as saved when stopping the vserver.

For counting the amount of inodes and space used by a certain vserver
on a filesystem, I modified the old and broken vdu program (vdu bin
etc would not find etc, for instance) and made it useful again.

The cache files will probably need some love, I'm a bit uncertain as
to where to put them (or rather, the symlink to their location).

This is the last piece of the disk limits puzzle. A variant of this
would be really nice to have in 0.30.210. (do you have an ETA on that,
btw?)

git-svn-id: http://svn.linux-vserver.org/svn/util-vserver/trunk@2252 94cd875c-1c1d-0410-91d2-eb244daf1a30

util-vserver/doc/configuration.xml
util-vserver/scripts/util-vserver-vars.pathsubst
util-vserver/scripts/vserver.functions
util-vserver/scripts/vserver.start
util-vserver/scripts/vserver.stop
util-vserver/src/Makefile-files
util-vserver/src/vdu.c

index f085654..86d82b2 100644 (file)
@@ -1168,5 +1168,22 @@ tools and can *not* be modified.
        <description>The NIS domainname of the vserver</description>
       </scalar>
     </collection>
+
+    <collection name="dlimits">
+      <collection name="dlimit" type="symbolic">
+        <scalar name="directory">
+          <description>The directory to which the limit should be applied</description>
+        </scalar>
+        <scalar name="inodes_total">
+          <description>The amount of inodes this vserver should be limited to</description>
+        </scalar>
+        <scalar name="space_total">
+          <description>The amount of space this vserver should be limited to (measured in blocks of 1024 bytes)</description>
+        </scalar>
+        <scalar name="reserved">
+          <description>How much space (percentage-wise) should be reserved for the root user</description>
+        </scalar>
+      </collection>
+    </collection>
   </collection>
   </database>
index 687f378..d2e34d3 100644 (file)
@@ -66,6 +66,8 @@ _VAPT_GET="$__SBINDIR/vapt-get"
 _VAPT_GET_WORKER="$__PKGLIBDIR/vapt-get-worker"
 _VATTRIBUTE="$__SBINDIR/vattribute"
 _VCONTEXT="$__SBINDIR/vcontext"
+_VDLIMIT="$__SBINDIR/vdlimit"
+_VDU="$__SBINDIR/vdu"
 _VHASHIFY="$__PKGLIBDIR/vhashify"
 _VKILL="$__SBINDIR/vkill"
 _VLIMIT="$__SBINDIR/vlimit"
index d5e0514..6bb9058 100644 (file)
@@ -980,3 +980,92 @@ WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
 procfs-security. Please read the FAQ for more details
 http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ"
 }
+
+
+function _setSingleDiskLimit
+{
+    local vdir=$1
+    local dlimit=$2
+    local space_used=
+    local space_total=
+    local inodes_used=
+    local inodes_total=
+    local reserved=
+    local directory=
+    local ctx=
+
+    getFileValue ctx          "$vdir/context"
+    getFileValue directory    "$dlimit/directory"    || return 0
+    getFileValue space_total  "$dlimit/space_total"  || return 0
+    getFileValue inodes_total "$dlimit/inodes_total" || return 0
+    getFileValue reserved     "$dlimit/reserved"     || return 0
+
+    local cachename=`echo "$ctx$directory" | $_SED 's!/!_!g'`
+
+    test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
+    # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
+    $_RM -f "$vdir/cache/$cachename"
+
+    if test -z "$inodes_used" -o -z "$space_used"; then
+       space_used=` $_VDU --xid $ctx --space  --script "$directory"`
+       inodes_used=`$_VDU --xid $ctx --inodes --script "$directory"`
+    fi
+
+    $_VDLIMIT --xid $ctx \
+       --set space_used=$space_used \
+       --set space_total=$space_total \
+       --set inodes_used=$inodes_used \
+       --set inodes_total=$inodes_total \
+       --set reserved=$reserved \
+       "$directory"
+}
+
+
+function setDiskLimits
+{
+    local vdir=$1
+    local dlimit
+
+    # Disk Limits without a static context are useless
+    test -e "$vdir"/context || return 0
+
+    for dlimit in "$vdir/dlimits/"*; do
+       test   -d "$dlimit"          || continue
+       test ! -e "$dlimit/disabled" || continue
+
+       _setSingleDiskLimit "$vdir" "$dlimit"
+    done
+}
+
+
+function _saveSingleDiskLimit
+{
+    local vdir=$1
+    local dlimit=$2
+    local ctx=
+    local directory=
+
+    getFileValue ctx       "$vdir/context"
+    getFileValue directory "$dlimit/directory" || return 0
+
+    local cachename=`echo "$ctx$directory" | $_SED 's!/!_!g'`
+
+    $_VDLIMIT --xid $ctx "$directory" | \
+       $_GREP '_used=' > "$vdir/cache/$cachename"
+}
+
+
+function saveDiskLimits
+{
+    local vdir=$1
+    local dlimit
+
+    test -e "$vdir"/context || return 0
+
+    for dlimit in "$vdir/dlimits/"*; do
+       test   -d "$dlimit"          || continue
+       test ! -e "$dlimit/disabled" || continue
+
+       _saveSingleDiskLimit "$vdir" "$dlimit"
+    done
+}
index a46b3f1..35b5446 100644 (file)
@@ -107,6 +107,8 @@ pushd "$VSERVER_DIR" >/dev/null
 execScriptlets   "$VSERVER_DIR" "$VSERVER_NAME" prepre-start
 popd >/dev/null
 
+setDiskLimits    "$VSERVER_DIR"
+
 enableInterfaces "$VSERVER_DIR" && have_interfaces=1
 
 mountVserver "$VSERVER_DIR"     && is_mounted=1
index 7db361e..79fb97a 100644 (file)
@@ -98,4 +98,6 @@ execScriptlets    "$VSERVER_DIR" "$VSERVER_NAME" post-stop
 umountVserver     "$VSERVER_DIR" || :
 disableInterfaces "$VSERVER_DIR"
 
+saveDiskLimits    "$VSERVER_DIR"
+
 execScriptlets    "$VSERVER_DIR" "$VSERVER_NAME" postpost-stop
index 277ec1e..1019331 100644 (file)
@@ -33,7 +33,6 @@ AM_INSTALLCHECK_STD_OPTIONS_EXEMPT += \
                        src/mask2prefix \
                        src/ifspec \
                        src/rebootmgr \
-                       src/vdu \
                        src/vfiles
 
 DIETPROGS +=           src/chcontext-compat \
@@ -74,6 +73,7 @@ DIETPROGS +=          src/chcontext-compat \
                        src/vserver-stat \
                        src/vserver-info \
                        src/vdlimit \
+                       src/vdu \
                        src/vwait
 
 if ENSC_CAN_BEECRYPT_WITH_DIETLIBC
@@ -209,7 +209,10 @@ src_save_ctxinfo_SOURCES   =  src/save_ctxinfo.c
 src_save_ctxinfo_LDADD         =  $(VSERVER_LDADDS)
 src_save_ctxinfo_LDFLAGS       =  $(VSERVER_LDFLGS)
 
-src_vdu_SOURCES =              src/vdu.c
+src_vdu_SOURCES                =  src/vdu.c
+src_vdu_LDADD                  =  $(VSERVER_LDADDS)
+src_vdu_LDFLAGS                        =  $(VSERVER_LDFLGS)
+
 src_vreboot_SOURCES =          src/vreboot.c
 
 src_secure_mount_SOURCES =     src/secure-mount.c
index 85ad9f9..c3c5d39 100644 (file)
@@ -1,12 +1,10 @@
-// $Id$
+// $Id$    --*- c -*--
 
-// Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
-// based on vdu.cc by Jacques Gelinas
+// Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
 //  
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2, or (at your option)
-// any later version.
+// the Free Software Foundation; version 2 of the License.
 //  
 // This program is distributed in the hope that it will be useful,
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
+#include "util.h"
+#include <lib/vserver.h>
+#include <lib/fmt.h>
+
 #include <stdlib.h>
-#include <stdio.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <errno.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <dirent.h>
-#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
 
-__extension__ typedef long long                longlong;
+#define ENSC_WRAPPERS_PREFIX   "vdu: "
+#define ENSC_WRAPPERS_VSERVER  1
+#define ENSC_WRAPPERS_UNISTD   1
+#define ENSC_WRAPPERS_DIRENT   1
+#define ENSC_WRAPPERS_FCNTL    1
+#include <wrappers.h>
 
-static int vdu_onedir (char const *path, longlong *size)
+#define CMD_HELP               0x1000
+#define CMD_VERSION            0x1001
+#define CMD_XID                        0x2000
+#define CMD_SPACE              0x2001
+#define CMD_INODES             0x2002
+#define CMD_SCRIPT             0x2003
+#define CMD_BLOCKSIZE          0x2005
+
+int                    wrapper_exit_code = 1;
+
+struct option const
+CMDLINE_OPTIONS[] = {
+  { "help",           no_argument,       0, CMD_HELP },
+  { "version",        no_argument,       0, CMD_VERSION },
+  { "xid",            required_argument, 0, CMD_XID },
+  { "space",          no_argument,       0, CMD_SPACE },
+  { "inodes",         no_argument,       0, CMD_INODES },
+  { "script",         no_argument,       0, CMD_SCRIPT },
+  { "blocksize",      required_argument, 0, CMD_BLOCKSIZE },
+  {0,0,0,0}
+};
+
+struct Arguments {
+    xid_t      xid;
+    bool       space;
+    bool       inodes;
+    bool       script;
+    uint32_t   blocksize;
+};
+
+static void
+showHelp(int fd, char const *cmd, int res)
 {
-       int ret = -1;
-       int dirfd = open (path,O_RDONLY);       // A handle to speed up
-                                                                                               // chdir
-       if (dirfd == -1){
-               fprintf (stderr,"Can't open directory %s (%s)\n",path
-                       ,strerror(errno));
-       }else{
-               DIR *dir;
-
-               fchdir (dirfd);
-               dir = opendir (".");
-               if (dir == NULL){
-                       fprintf (stderr,"Can't open (opendir) directory %s (%s)\n",path
-                               ,strerror(errno));
-               }else{
-                       struct stat dirst;
-                       struct dirent *ent;
-                       longlong dirsize = 0;
-
-                       ret = 0;
-                       lstat (".",&dirst);
-                       while ((ent=readdir(dir))!=NULL){
-                               struct stat st;
-                               if (lstat(ent->d_name,&st)==-1){
-                                       fprintf (stderr,"Can't stat %s/%s (%s)\n",path
-                                               ,ent->d_name,strerror(errno));
-                                       ret = -1;
-                                       break;
-                               }else if (S_ISREG(st.st_mode)){
-                                       if (st.st_nlink == 1){
-                                               dirsize += st.st_size;
-                                       }
-                               }else if (S_ISDIR(st.st_mode) && st.st_dev == dirst.st_dev){
-                                       if (strcmp(ent->d_name,".")!=0
-                                               && strcmp(ent->d_name,"..")!=0){
-                                               char    *tmp = malloc(strlen(path) + strlen(ent->d_name) + 2);
-                                               if (tmp==0) ret=-1;
-                                               else {
-                                                 strcpy(tmp, path);
-                                                 strcat(tmp, "/");
-                                                 strcat(tmp, ent->d_name);
-                                                 ret = vdu_onedir(tmp,&dirsize);
-                                                 free(tmp);
-                                                 fchdir (dirfd);
-                                               }
-                                       }
-                               }
-                       }
-                       closedir (dir);
-                       *size += dirsize;
-               }
-               close (dirfd);
-       }
-       return ret;
+  WRITE_MSG(fd, "Usage:\n    ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " --xid <xid> (--space|--inodes) [--blocksize <blocksize>] [--script] <directory>*\n"
+           "\n"
+           "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+  exit(res);
 }
 
-int main (int argc, char *argv[])
+static void
+showVersion()
 {
-       int ret = -1;
-       if (argc < 2){
-               fprintf (stderr,"vdu version %s\n",VERSION);
-               fprintf (stderr,"vdu directory ...\n\n");
-               fprintf (stderr
-                       ,"Compute the size of a directory tree, ignoring files\n"
-                        "with more than one link.\n");
-       }else{
-               int i;
-
-               ret = 0;
-               for (i=1; i<argc && ret != -1; i++){
-                       longlong size = 0;
-                       long ksize;
-                       
-                       ret = vdu_onedir (argv[i],&size);
-                       ksize = size >> 10;
-                       printf ("%s\t%ldK\n",argv[i],ksize);
-               }
-       }
-       return ret;
+  WRITE_MSG(1,
+           "vdu " VERSION " -- calculates the size of a directory\n"
+           "This program is part of " PACKAGE_STRING "\n\n"
+           "Copyright (C) 2006 Enrico Scholz\n"
+           VERSION_COPYRIGHT_DISCLAIMER);
+  exit(0);
+}
+
+/* basic hash table implementation for inode tracking */
+#define HASH_SIZE 103
+typedef struct hash_entry {
+  struct hash_entry *next;
+  ino_t inode;
+} hash_entry;
+
+typedef struct hash_table {
+  hash_entry *entries[HASH_SIZE];
+} hash_table;
+
+static hash_table ht;
+
+static void
+hash_init(void)
+{
+  memset(&ht, 0, sizeof(hash_table));
+}
+
+static void
+hash_free(void)
+{
+  int i;
+  hash_entry *e, *p;
+  for (i = 0; i < HASH_SIZE; i++) {
+    for (e = ht.entries[i], p = NULL; e; e = e->next) {
+      free(p);
+      p = e;
+    }
+    free(p);
+  }
+}
+
+static int
+hash_insert(ino_t inode)
+{
+  hash_entry *e, *p;
+  unsigned int hashval = inode % HASH_SIZE;
+
+  /* no one else here */
+  if (ht.entries[hashval] == NULL) {
+    ht.entries[hashval]        = malloc(sizeof(hash_entry));
+    ht.entries[hashval]->next  = NULL;
+    ht.entries[hashval]->inode = inode;
+    return 0;
+  }
+
+  for (e = ht.entries[hashval], p = NULL; e; e = e->next) {
+    /* already in the hash table */
+    if (e->inode == inode)
+      return -1;
+    else if (e->inode > inode) {
+      /* we're first */
+      if (p == NULL) {
+       ht.entries[hashval]        = malloc(sizeof(hash_entry));
+       ht.entries[hashval]->next  = e;
+       ht.entries[hashval]->inode = inode;
+      }
+      /* we're in the middle */
+      else {
+       p->next        = malloc(sizeof(hash_entry));
+       p->next->next  = e;
+       p->next->inode = inode;
+      }
+      return 0;
+    }
+    p = e;
+  }
+  /* we're last */
+  p->next        = malloc(sizeof(hash_entry));
+  p->next->next  = NULL;
+  p->next->inode = inode;
+
+  return 0;
+}
+
+static void
+vdu_onedir(struct Arguments const *args, char const *path, uint64_t *size)
+{
+  DIR *dir;
+  struct dirent *ent;
+  struct stat dirst, st;
+  char entpath[PATH_MAX];
+
+  if (lstat(path, &dirst) == -1) {
+    WRITE_MSG(2, "lstat(");
+    WRITE_STR(2, path);
+    WRITE_MSG(2, ")");
+    perror("");
+    exit(EXIT_FAILURE);
+  }
+
+  dir = Eopendir(path);
+  while ((ent = Ereaddir(dir)) != NULL) {
+    if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
+      continue;
+
+    strcpy(entpath, path);
+    strcat(entpath, "/");
+    strcat(entpath, ent->d_name);
+
+    if (lstat(entpath, &st) == -1) {
+      WRITE_MSG(2, "lstat(");
+      WRITE_STR(2, entpath);
+      WRITE_MSG(2, ")");
+      perror("");
+      exit(EXIT_FAILURE);
+    }
+
+    if (vc_getfilecontext(entpath) != args->xid)
+      continue;
+
+    if (st.st_nlink > 1 && hash_insert(st.st_ino) == -1)
+      continue;
+
+    if (args->space)
+      *size += st.st_blocks << 9; /* * 512 */
+    else
+      (*size)++;
+
+    if (S_ISDIR(st.st_mode) && dirst.st_dev == st.st_dev)
+      vdu_onedir(args, entpath, size);
+  }
+  Eclosedir(dir);
 }
 
+int main(int argc, char *argv[])
+{
+  struct Arguments             args = {
+    .xid       = VC_NOCTX,
+    .space     = false,
+    .inodes    = false,
+    .script    = false,
+    .blocksize = 1024,
+  };
+  
+  while (1) {
+    int                c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
+    if (c==-1) break;
+
+    switch (c) {
+      case CMD_HELP    :  showHelp(1, argv[0], 0);
+      case CMD_VERSION :  showVersion();
+      case CMD_XID     :  args.xid = Evc_xidopt2xid(optarg,true); break;
+      case CMD_SPACE   :  args.space = true;                      break;
+      case CMD_INODES  :  args.inodes = true;                     break;
+      case CMD_SCRIPT  :  args.script = true;                     break;
+      case CMD_BLOCKSIZE:
+       {
+         char *endptr;
+         args.blocksize = strtol(optarg, &endptr, 0);
+         if ((args.blocksize == 0 && errno != 0) || *endptr != '\0') {
+           WRITE_MSG(2, "Invalid block size argument: '");
+           WRITE_STR(2, optarg);
+           WRITE_MSG(2, "'; try '--help' for more information\n");
+           return EXIT_FAILURE;
+         }
+          break;
+       }
+      default          :
+       WRITE_MSG(2, "Try '");
+       WRITE_STR(2, argv[0]);
+       WRITE_MSG(2, " --help' for more information.\n");
+       return 255;
+       break;
+    }
+  }
+
+  if (args.xid==VC_NOCTX)
+    WRITE_MSG(2, "No xid specified; try '--help' for more information\n");
+  else if (!args.space && !args.inodes)
+    WRITE_MSG(2, "Must specify --space or --inodes; try '--help' for more information\n");
+  else if (args.space && args.inodes)
+    WRITE_MSG(2, "Can only do one thing at a time; try '--help' for more information\n");
+  else if (optind==argc)
+    WRITE_MSG(2, "No directory specified; try '--help' for more information\n");
+  else {
+    int                i;
+    uint64_t   size;
+    char       buf[sizeof(size)*3 + 2];
+    size_t     len;
+
+    for (i = optind; i < argc; i++) {
+      size = 0;
+
+      hash_init();
+      vdu_onedir(&args, argv[i], &size);
+      hash_free();
+
+      if (!args.script) {
+       WRITE_STR(1, argv[i]);
+       WRITE_MSG(1, " ");
+      }
+      if (args.space)
+        size /= args.blocksize;
+      len = utilvserver_fmt_uint64(buf, size);
+      Vwrite(1, buf, len);
+      WRITE_MSG(1, "\n");
+    }
+    return EXIT_SUCCESS;
+  }
+
+  return EXIT_FAILURE;
+}