From 75385100bedf6d457e88bc2bb942a6f0771b0e06 Mon Sep 17 00:00:00 2001 From: Enrico Scholz Date: Sat, 21 Jan 2006 13:22:23 +0000 Subject: [PATCH] applied http://savannah.nongnu.org/patch/?func=detailitem&item_id=4814 (Daniel Hokka Zakrisson): This patch lets users configure disk limits in /etc/vservers//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 | 17 ++ util-vserver/scripts/util-vserver-vars.pathsubst | 2 + util-vserver/scripts/vserver.functions | 89 ++++++ util-vserver/scripts/vserver.start | 2 + util-vserver/scripts/vserver.stop | 2 + util-vserver/src/Makefile-files | 7 +- util-vserver/src/vdu.c | 359 +++++++++++++++++------ 7 files changed, 387 insertions(+), 91 deletions(-) diff --git a/util-vserver/doc/configuration.xml b/util-vserver/doc/configuration.xml index f085654..86d82b2 100644 --- a/util-vserver/doc/configuration.xml +++ b/util-vserver/doc/configuration.xml @@ -1168,5 +1168,22 @@ tools and can *not* be modified. The NIS domainname of the vserver + + + + + The directory to which the limit should be applied + + + The amount of inodes this vserver should be limited to + + + The amount of space this vserver should be limited to (measured in blocks of 1024 bytes) + + + How much space (percentage-wise) should be reserved for the root user + + + diff --git a/util-vserver/scripts/util-vserver-vars.pathsubst b/util-vserver/scripts/util-vserver-vars.pathsubst index 687f378..d2e34d3 100644 --- a/util-vserver/scripts/util-vserver-vars.pathsubst +++ b/util-vserver/scripts/util-vserver-vars.pathsubst @@ -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" diff --git a/util-vserver/scripts/vserver.functions b/util-vserver/scripts/vserver.functions index d5e0514..6bb9058 100644 --- a/util-vserver/scripts/vserver.functions +++ b/util-vserver/scripts/vserver.functions @@ -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 +} diff --git a/util-vserver/scripts/vserver.start b/util-vserver/scripts/vserver.start index a46b3f1..35b5446 100644 --- a/util-vserver/scripts/vserver.start +++ b/util-vserver/scripts/vserver.start @@ -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 diff --git a/util-vserver/scripts/vserver.stop b/util-vserver/scripts/vserver.stop index 7db361e..79fb97a 100644 --- a/util-vserver/scripts/vserver.stop +++ b/util-vserver/scripts/vserver.stop @@ -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 diff --git a/util-vserver/src/Makefile-files b/util-vserver/src/Makefile-files index 277ec1e..1019331 100644 --- a/util-vserver/src/Makefile-files +++ b/util-vserver/src/Makefile-files @@ -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 diff --git a/util-vserver/src/vdu.c b/util-vserver/src/vdu.c index 85ad9f9..c3c5d39 100644 --- a/util-vserver/src/vdu.c +++ b/util-vserver/src/vdu.c @@ -1,12 +1,10 @@ -// $Id$ +// $Id$ --*- c -*-- -// Copyright (C) 2003 Enrico Scholz -// based on vdu.cc by Jacques Gelinas +// Copyright (C) 2006 Enrico Scholz // // 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 @@ -17,102 +15,285 @@ // 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 #endif +#include "util.h" +#include +#include + #include -#include +#include +#include +#include #include -#include -#include -#include #include -#include -#include +#include -__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 -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 (--space|--inodes) [--blocksize ] [--script] *\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> 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; +} -- 1.8.1.5