f24afc235075ec2f02e1c5b548435339b5c1aa11
[util-vserver.git] / util-vserver / 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(struct dirent const *ent, 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     visitDirEntry(ent, expected_stat->st_dev, params);
202   }
203
204   Eclosedir(dir);
205
206   Efchdir(fd);
207   Eclose(fd);
208 }
209
210 static void
211 visitDirEntry(struct dirent const *ent, dev_t const dir_dev,
212               struct TraversalParams *params)
213 {
214   struct stat           st;
215   char const * const    name = ent->d_name;
216   xid_t                 xid;
217   
218   if (isDotfile(name)) return;
219
220   ElstatD(name, &st);
221
222   xid = vc_getfilecontext(name);
223   if (xid == params->args->xid &&
224       (st.st_nlink == 1 || hash_insert(st.st_ino) != -1)) {
225     params->result->blocks += st.st_blocks;
226     params->result->inodes += 1;
227   }
228
229   if (S_ISDIR(st.st_mode) && dir_dev == st.st_dev)
230     visitDir(name, &st, params);
231 }
232
233 static void
234 visitDirStart(char const *name, struct TraversalParams *params)
235 {
236   struct stat   st;
237   int           fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
238
239   Estat(name, &st);
240   Echdir(name);
241
242   visitDir(".", &st, params);
243
244   Efchdir(fd);
245   Eclose(fd);  
246 }
247
248 int main(int argc, char *argv[])
249 {
250   struct Arguments              args = {
251     .xid       = VC_NOCTX,
252     .space     = false,
253     .inodes    = false,
254     .script    = false,
255     .blocksize = 1024,
256   };
257   
258   while (1) {
259     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
260     if (c==-1) break;
261
262     switch (c) {
263       case CMD_HELP     :  showHelp(1, argv[0], 0);
264       case CMD_VERSION  :  showVersion();
265       case CMD_XID      :  args.xid = Evc_xidopt2xid(optarg,true); break;
266       case CMD_SPACE    :  args.space  = true;                     break;
267       case CMD_INODES   :  args.inodes = true;                     break;
268       case CMD_SCRIPT   :  args.script = true;                     break;
269       case CMD_BLOCKSIZE:
270         if (!isNumberUnsigned(optarg, &args.blocksize, false)) {
271           WRITE_MSG(2, "Invalid block size argument: '");
272           WRITE_STR(2, optarg);
273           WRITE_MSG(2, "'; try '--help' for more information\n");
274           return EXIT_FAILURE;
275         }
276         break;
277       default           :
278         WRITE_MSG(2, "Try '");
279         WRITE_STR(2, argv[0]);
280         WRITE_MSG(2, " --help' for more information.\n");
281         return 255;
282         break;
283     }
284   }
285
286   if (args.xid==VC_NOCTX)
287     WRITE_MSG(2, "No xid specified; try '--help' for more information\n");
288   else if (!args.space && !args.inodes)
289     WRITE_MSG(2, "Must specify --space or --inodes; try '--help' for more information\n");
290   else if (optind==argc)
291     WRITE_MSG(2, "No directory specified; try '--help' for more information\n");
292   else {
293     int         i;
294     size_t      len;
295     struct Result               result;
296     struct TraversalParams      params   = {
297       .args   = &args,
298       .result = &result
299     };
300
301     for (i = optind; i < argc; i++) {
302       uint_least64_t            size;
303       char                      buf[sizeof(size)*3 + 3];
304       char const *              delim = "";
305       
306       result.blocks = 0;
307       result.inodes = 0;
308
309       hash_init();
310       visitDirStart(argv[i], &params);
311       hash_free();
312
313       if (!args.script) {
314         WRITE_STR(1, argv[i]);
315         WRITE_MSG(1, " ");
316       }
317
318       if (args.space) {
319         len = utilvserver_fmt_uint64(buf, result.blocks*512 / args.blocksize);
320         if (*delim) WRITE_STR(1, delim);
321         Vwrite(1, buf, len);
322         delim = " ";
323       }
324       if (args.inodes) {
325         len = utilvserver_fmt_uint64(buf, result.inodes);
326         if (*delim) WRITE_STR(1, delim);
327         Vwrite(1, buf, len);
328         delim = " ";
329       } 
330       WRITE_MSG(1, "\n");
331     }
332     return EXIT_SUCCESS;
333   }
334
335   return EXIT_FAILURE;
336 }