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