X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fvdu.c;h=a0c87513096b0701633b5b81c496b8674dba03fa;hb=95e2774070e989fe9cf9f48dae5fa054e55e2a3e;hp=55f6082465d44eb378fd90a5d8716a6cb03f81c8;hpb=06e1018272502e1d15d6d8f32b80fa96420785b8;p=util-vserver.git diff --git a/src/vdu.c b/src/vdu.c index 55f6082..a0c8751 100644 --- a/src/vdu.c +++ b/src/vdu.c @@ -1,12 +1,10 @@ -// $Id: vdu.c,v 1.1 2003/09/29 22:01:57 ensc Exp $ +// $Id: vdu.c 2260 2006-01-22 11:56:28Z ensc $ --*- 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,98 +15,319 @@ // 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 + +#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 +#define ENSC_WRAPPERS_STAT 1 +#include + +#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 -__extension__ typedef long long longlong; +int wrapper_exit_code = 1; -static int vdu_onedir (char const *path, longlong *size) +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; + unsigned long blocksize; +}; + +struct Result { + uint_least64_t blocks; + uint_least64_t inodes; +}; + +struct TraversalParams { + struct Arguments const * const args; + struct Result * const result; +}; + +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 +visitDirEntry(char const *name, dev_t const dir_dev, + struct TraversalParams *params); + +static void +visitDir(char const *name, struct stat const *expected_stat, struct TraversalParams *params) +{ + int fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); + DIR * dir; + + EsafeChdir(name, expected_stat); + + dir = Eopendir("."); + + for (;;) { + struct dirent *ent = Ereaddir(dir); + if (ent==0) break; + + if (isDotfile(ent->d_name)) continue; + visitDirEntry(ent->d_name, expected_stat->st_dev, params); + } + + Eclosedir(dir); + + Efchdir(fd); + Eclose(fd); +} + +static void +visitDirEntry(char const *name, dev_t const dir_dev, + struct TraversalParams *params) +{ + struct stat st; + xid_t xid; + + ElstatD(name, &st); + + xid = vc_getfilecontext(name); + if (xid == params->args->xid && + (st.st_nlink == 1 || hash_insert(st.st_ino) != -1)) { + params->result->blocks += st.st_blocks; + params->result->inodes += 1; + } + + if (S_ISDIR(st.st_mode) && dir_dev == st.st_dev) + visitDir(name, &st, params); +} + +static void +visitDirStart(char const *name, struct TraversalParams *params) +{ + struct stat st; + int fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); + + Estat(name, &st); + Echdir(name); + + visitDirEntry(".", st.st_dev, params); + + Efchdir(fd); + Eclose(fd); +} + +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: + if (!isNumberUnsigned(optarg, &args.blocksize, false)) { + 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 (optind==argc) + WRITE_MSG(2, "No directory specified; try '--help' for more information\n"); + else { + int i; + size_t len; + struct Result result; + struct TraversalParams params = { + .args = &args, + .result = &result + }; + + for (i = optind; i < argc; i++) { + uint_least64_t size; + char buf[sizeof(size)*3 + 3]; + char const * delim = ""; + + result.blocks = 0; + result.inodes = 0; + + hash_init(); + visitDirStart(argv[i], ¶ms); + hash_free(); + + if (!args.script) { + WRITE_STR(1, argv[i]); + WRITE_MSG(1, " "); + } + + if (args.space) { + len = utilvserver_fmt_uint64(buf, result.blocks*512 / args.blocksize); + if (*delim) WRITE_STR(1, delim); + Vwrite(1, buf, len); + delim = " "; + } + if (args.inodes) { + len = utilvserver_fmt_uint64(buf, result.inodes); + if (*delim) WRITE_STR(1, delim); + Vwrite(1, buf, len); + delim = " "; + } + WRITE_MSG(1, "\n"); + } + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +}