fix some logic flaws
[util-vserver.git] / src / vdu.c
1 // $Id: vdu.c 2260 2006-01-22 11:56:28Z ensc $    --*- c -*--
2
3 // Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #ifdef HAVE_CONFIG_H
19 #  include <config.h>
20 #endif
21
22 #include "util.h"
23 #include <lib/vserver.h>
24 #include <lib/fmt.h>
25
26 #include <stdlib.h>
27 #include <getopt.h>
28 #include <stdint.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33
34 #define ENSC_WRAPPERS_PREFIX    "vdu: "
35 #define ENSC_WRAPPERS_VSERVER   1
36 #define ENSC_WRAPPERS_UNISTD    1
37 #define ENSC_WRAPPERS_DIRENT    1
38 #define ENSC_WRAPPERS_FCNTL     1
39 #define ENSC_WRAPPERS_STAT      1
40 #include <wrappers.h>
41
42 #define CMD_HELP                0x1000
43 #define CMD_VERSION             0x1001
44 #define CMD_XID                 0x2000
45 #define CMD_SPACE               0x2001
46 #define CMD_INODES              0x2002
47 #define CMD_SCRIPT              0x2003
48 #define CMD_BLOCKSIZE           0x2005
49
50 int                     wrapper_exit_code = 1;
51
52 struct option const
53 CMDLINE_OPTIONS[] = {
54   { "help",           no_argument,       0, CMD_HELP },
55   { "version",        no_argument,       0, CMD_VERSION },
56   { "xid",            required_argument, 0, CMD_XID },
57   { "space",          no_argument,       0, CMD_SPACE },
58   { "inodes",         no_argument,       0, CMD_INODES },
59   { "script",         no_argument,       0, CMD_SCRIPT },
60   { "blocksize",      required_argument, 0, CMD_BLOCKSIZE },
61   {0,0,0,0}
62 };
63
64 struct Arguments {
65     xid_t               xid;
66     bool                space;
67     bool                inodes;
68     bool                script;
69     unsigned long       blocksize;
70 };
71
72 struct Result {
73     uint_least64_t      blocks;
74     uint_least64_t      inodes;
75 };
76
77 struct TraversalParams {
78     struct Arguments const * const      args;
79     struct Result * const               result;
80 };
81
82 static void
83 showHelp(int fd, char const *cmd, int res)
84 {
85   WRITE_MSG(fd, "Usage:\n    ");
86   WRITE_STR(fd, cmd);
87   WRITE_MSG(fd,
88             " --xid <xid> (--space|--inodes) [--blocksize <blocksize>] [--script] <directory>*\n"
89             "\n"
90             "Please report bugs to " PACKAGE_BUGREPORT "\n");
91
92   exit(res);
93 }
94
95 static void
96 showVersion()
97 {
98   WRITE_MSG(1,
99             "vdu " VERSION " -- calculates the size of a directory\n"
100             "This program is part of " PACKAGE_STRING "\n\n"
101             "Copyright (C) 2006 Enrico Scholz\n"
102             VERSION_COPYRIGHT_DISCLAIMER);
103   exit(0);
104 }
105
106 /* basic hash table implementation for inode tracking */
107 #define HASH_SIZE 103
108 typedef struct hash_entry {
109   struct hash_entry *next;
110   ino_t inode;
111 } hash_entry;
112
113 typedef struct hash_table {
114   hash_entry *entries[HASH_SIZE];
115 } hash_table;
116
117 static hash_table ht;
118
119 static void
120 hash_init(void)
121 {
122   memset(&ht, 0, sizeof(hash_table));
123 }
124
125 static void
126 hash_free(void)
127 {
128   int i;
129   hash_entry *e, *p;
130   for (i = 0; i < HASH_SIZE; i++) {
131     for (e = ht.entries[i], p = NULL; e; e = e->next) {
132       free(p);
133       p = e;
134     }
135     free(p);
136   }
137 }
138
139 static int
140 hash_insert(ino_t inode)
141 {
142   hash_entry *e, *p;
143   unsigned int hashval = inode % HASH_SIZE;
144
145   /* no one else here */
146   if (ht.entries[hashval] == NULL) {
147     ht.entries[hashval]        = malloc(sizeof(hash_entry));
148     ht.entries[hashval]->next  = NULL;
149     ht.entries[hashval]->inode = inode;
150     return 0;
151   }
152
153   for (e = ht.entries[hashval], p = NULL; e; e = e->next) {
154     /* already in the hash table */
155     if (e->inode == inode)
156       return -1;
157     else if (e->inode > inode) {
158       /* we're first */
159       if (p == NULL) {
160         ht.entries[hashval]        = malloc(sizeof(hash_entry));
161         ht.entries[hashval]->next  = e;
162         ht.entries[hashval]->inode = inode;
163       }
164       /* we're in the middle */
165       else {
166         p->next        = malloc(sizeof(hash_entry));
167         p->next->next  = e;
168         p->next->inode = inode;
169       }
170       return 0;
171     }
172     p = e;
173   }
174   /* we're last */
175   p->next        = malloc(sizeof(hash_entry));
176   p->next->next  = NULL;
177   p->next->inode = inode;
178
179   return 0;
180 }
181
182 static void
183 visitDirEntry(char const *name, dev_t const dir_dev,
184               struct TraversalParams *params);
185
186 static void
187 visitDir(char const *name, struct stat const *expected_stat, struct TraversalParams *params)
188 {
189   int           fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
190   DIR *         dir;
191
192   EsafeChdir(name, expected_stat);
193
194   dir = Eopendir(".");
195
196   for (;;) {
197     struct dirent               *ent = Ereaddir(dir);
198     if (ent==0) break;
199
200     if (isDotfile(ent->d_name)) continue;
201     visitDirEntry(ent->d_name, expected_stat->st_dev, params);
202   }
203
204   Eclosedir(dir);
205
206   Efchdir(fd);
207   Eclose(fd);
208 }
209
210 static void
211 visitDirEntry(char const *name, dev_t const dir_dev,
212               struct TraversalParams *params)
213 {
214   struct stat           st;
215   xid_t                 xid;
216   
217   ElstatD(name, &st);
218
219   xid = vc_getfilecontext(name);
220   if (xid == params->args->xid &&
221       (st.st_nlink == 1 || hash_insert(st.st_ino) != -1)) {
222     params->result->blocks += st.st_blocks;
223     params->result->inodes += 1;
224   }
225
226   if (S_ISDIR(st.st_mode) && dir_dev == st.st_dev)
227     visitDir(name, &st, params);
228 }
229
230 static void
231 visitDirStart(char const *name, struct TraversalParams *params)
232 {
233   struct stat   st;
234   int           fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
235
236   Estat(name, &st);
237   Echdir(name);
238
239   visitDirEntry(".", st.st_dev, params);
240
241   Efchdir(fd);
242   Eclose(fd);  
243 }
244
245 int main(int argc, char *argv[])
246 {
247   struct Arguments              args = {
248     .xid       = VC_NOCTX,
249     .space     = false,
250     .inodes    = false,
251     .script    = false,
252     .blocksize = 1024,
253   };
254   
255   while (1) {
256     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
257     if (c==-1) break;
258
259     switch (c) {
260       case CMD_HELP     :  showHelp(1, argv[0], 0);
261       case CMD_VERSION  :  showVersion();
262       case CMD_XID      :  args.xid = Evc_xidopt2xid(optarg,true); break;
263       case CMD_SPACE    :  args.space  = true;                     break;
264       case CMD_INODES   :  args.inodes = true;                     break;
265       case CMD_SCRIPT   :  args.script = true;                     break;
266       case CMD_BLOCKSIZE:
267         if (!isNumberUnsigned(optarg, &args.blocksize, false)) {
268           WRITE_MSG(2, "Invalid block size argument: '");
269           WRITE_STR(2, optarg);
270           WRITE_MSG(2, "'; try '--help' for more information\n");
271           return EXIT_FAILURE;
272         }
273         break;
274       default           :
275         WRITE_MSG(2, "Try '");
276         WRITE_STR(2, argv[0]);
277         WRITE_MSG(2, " --help' for more information.\n");
278         return 255;
279         break;
280     }
281   }
282
283   if (args.xid==VC_NOCTX)
284     WRITE_MSG(2, "No xid specified; try '--help' for more information\n");
285   else if (!args.space && !args.inodes)
286     WRITE_MSG(2, "Must specify --space or --inodes; try '--help' for more information\n");
287   else if (optind==argc)
288     WRITE_MSG(2, "No directory specified; try '--help' for more information\n");
289   else {
290     int         i;
291     size_t      len;
292     struct Result               result;
293     struct TraversalParams      params   = {
294       .args   = &args,
295       .result = &result
296     };
297
298     for (i = optind; i < argc; i++) {
299       uint_least64_t            size;
300       char                      buf[sizeof(size)*3 + 3];
301       char const *              delim = "";
302       
303       result.blocks = 0;
304       result.inodes = 0;
305
306       hash_init();
307       visitDirStart(argv[i], &params);
308       hash_free();
309
310       if (!args.script) {
311         WRITE_STR(1, argv[i]);
312         WRITE_MSG(1, " ");
313       }
314
315       if (args.space) {
316         len = utilvserver_fmt_uint64(buf, result.blocks*512 / args.blocksize);
317         if (*delim) WRITE_STR(1, delim);
318         Vwrite(1, buf, len);
319         delim = " ";
320       }
321       if (args.inodes) {
322         len = utilvserver_fmt_uint64(buf, result.inodes);
323         if (*delim) WRITE_STR(1, delim);
324         Vwrite(1, buf, len);
325         delim = " ";
326       } 
327       WRITE_MSG(1, "\n");
328     }
329     return EXIT_SUCCESS;
330   }
331
332   return EXIT_FAILURE;
333 }