applied http://savannah.nongnu.org/patch/?func=detailitem&item_id=4814
[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 #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     uint32_t    blocksize;
70 };
71
72 static void
73 showHelp(int fd, char const *cmd, int res)
74 {
75   WRITE_MSG(fd, "Usage:\n    ");
76   WRITE_STR(fd, cmd);
77   WRITE_MSG(fd,
78             " --xid <xid> (--space|--inodes) [--blocksize <blocksize>] [--script] <directory>*\n"
79             "\n"
80             "Please report bugs to " PACKAGE_BUGREPORT "\n");
81
82   exit(res);
83 }
84
85 static void
86 showVersion()
87 {
88   WRITE_MSG(1,
89             "vdu " VERSION " -- calculates the size of a directory\n"
90             "This program is part of " PACKAGE_STRING "\n\n"
91             "Copyright (C) 2006 Enrico Scholz\n"
92             VERSION_COPYRIGHT_DISCLAIMER);
93   exit(0);
94 }
95
96 /* basic hash table implementation for inode tracking */
97 #define HASH_SIZE 103
98 typedef struct hash_entry {
99   struct hash_entry *next;
100   ino_t inode;
101 } hash_entry;
102
103 typedef struct hash_table {
104   hash_entry *entries[HASH_SIZE];
105 } hash_table;
106
107 static hash_table ht;
108
109 static void
110 hash_init(void)
111 {
112   memset(&ht, 0, sizeof(hash_table));
113 }
114
115 static void
116 hash_free(void)
117 {
118   int i;
119   hash_entry *e, *p;
120   for (i = 0; i < HASH_SIZE; i++) {
121     for (e = ht.entries[i], p = NULL; e; e = e->next) {
122       free(p);
123       p = e;
124     }
125     free(p);
126   }
127 }
128
129 static int
130 hash_insert(ino_t inode)
131 {
132   hash_entry *e, *p;
133   unsigned int hashval = inode % HASH_SIZE;
134
135   /* no one else here */
136   if (ht.entries[hashval] == NULL) {
137     ht.entries[hashval]        = malloc(sizeof(hash_entry));
138     ht.entries[hashval]->next  = NULL;
139     ht.entries[hashval]->inode = inode;
140     return 0;
141   }
142
143   for (e = ht.entries[hashval], p = NULL; e; e = e->next) {
144     /* already in the hash table */
145     if (e->inode == inode)
146       return -1;
147     else if (e->inode > inode) {
148       /* we're first */
149       if (p == NULL) {
150         ht.entries[hashval]        = malloc(sizeof(hash_entry));
151         ht.entries[hashval]->next  = e;
152         ht.entries[hashval]->inode = inode;
153       }
154       /* we're in the middle */
155       else {
156         p->next        = malloc(sizeof(hash_entry));
157         p->next->next  = e;
158         p->next->inode = inode;
159       }
160       return 0;
161     }
162     p = e;
163   }
164   /* we're last */
165   p->next        = malloc(sizeof(hash_entry));
166   p->next->next  = NULL;
167   p->next->inode = inode;
168
169   return 0;
170 }
171
172 static void
173 vdu_onedir(struct Arguments const *args, char const *path, uint64_t *size)
174 {
175   DIR *dir;
176   struct dirent *ent;
177   struct stat dirst, st;
178   char entpath[PATH_MAX];
179
180   if (lstat(path, &dirst) == -1) {
181     WRITE_MSG(2, "lstat(");
182     WRITE_STR(2, path);
183     WRITE_MSG(2, ")");
184     perror("");
185     exit(EXIT_FAILURE);
186   }
187
188   dir = Eopendir(path);
189   while ((ent = Ereaddir(dir)) != NULL) {
190     if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
191       continue;
192
193     strcpy(entpath, path);
194     strcat(entpath, "/");
195     strcat(entpath, ent->d_name);
196
197     if (lstat(entpath, &st) == -1) {
198       WRITE_MSG(2, "lstat(");
199       WRITE_STR(2, entpath);
200       WRITE_MSG(2, ")");
201       perror("");
202       exit(EXIT_FAILURE);
203     }
204
205     if (vc_getfilecontext(entpath) != args->xid)
206       continue;
207
208     if (st.st_nlink > 1 && hash_insert(st.st_ino) == -1)
209       continue;
210
211     if (args->space)
212       *size += st.st_blocks << 9; /* * 512 */
213     else
214       (*size)++;
215
216     if (S_ISDIR(st.st_mode) && dirst.st_dev == st.st_dev)
217       vdu_onedir(args, entpath, size);
218   }
219   Eclosedir(dir);
220 }
221
222 int main(int argc, char *argv[])
223 {
224   struct Arguments              args = {
225     .xid       = VC_NOCTX,
226     .space     = false,
227     .inodes    = false,
228     .script    = false,
229     .blocksize = 1024,
230   };
231   
232   while (1) {
233     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
234     if (c==-1) break;
235
236     switch (c) {
237       case CMD_HELP     :  showHelp(1, argv[0], 0);
238       case CMD_VERSION  :  showVersion();
239       case CMD_XID      :  args.xid = Evc_xidopt2xid(optarg,true); break;
240       case CMD_SPACE    :  args.space = true;                      break;
241       case CMD_INODES   :  args.inodes = true;                     break;
242       case CMD_SCRIPT   :  args.script = true;                     break;
243       case CMD_BLOCKSIZE:
244         {
245           char *endptr;
246           args.blocksize = strtol(optarg, &endptr, 0);
247           if ((args.blocksize == 0 && errno != 0) || *endptr != '\0') {
248             WRITE_MSG(2, "Invalid block size argument: '");
249             WRITE_STR(2, optarg);
250             WRITE_MSG(2, "'; try '--help' for more information\n");
251             return EXIT_FAILURE;
252           }
253           break;
254         }
255       default           :
256         WRITE_MSG(2, "Try '");
257         WRITE_STR(2, argv[0]);
258         WRITE_MSG(2, " --help' for more information.\n");
259         return 255;
260         break;
261     }
262   }
263
264   if (args.xid==VC_NOCTX)
265     WRITE_MSG(2, "No xid specified; try '--help' for more information\n");
266   else if (!args.space && !args.inodes)
267     WRITE_MSG(2, "Must specify --space or --inodes; try '--help' for more information\n");
268   else if (args.space && args.inodes)
269     WRITE_MSG(2, "Can only do one thing at a time; try '--help' for more information\n");
270   else if (optind==argc)
271     WRITE_MSG(2, "No directory specified; try '--help' for more information\n");
272   else {
273     int         i;
274     uint64_t    size;
275     char        buf[sizeof(size)*3 + 2];
276     size_t      len;
277
278     for (i = optind; i < argc; i++) {
279       size = 0;
280
281       hash_init();
282       vdu_onedir(&args, argv[i], &size);
283       hash_free();
284
285       if (!args.script) {
286         WRITE_STR(1, argv[i]);
287         WRITE_MSG(1, " ");
288       }
289       if (args.space)
290         size /= args.blocksize;
291       len = utilvserver_fmt_uint64(buf, size);
292       Vwrite(1, buf, len);
293       WRITE_MSG(1, "\n");
294     }
295     return EXIT_SUCCESS;
296   }
297
298   return EXIT_FAILURE;
299 }