1 /* $Id: inndf.c 6677 2004-03-03 18:36:07Z hkehoe $
3 ** Reports free kilobytes (not disk blocks) or free inodes.
5 ** Written by Ian Dickinson <idickins@fore.com>
6 ** Wed Jul 26 10:11:38 BST 1995 (My birthday - 27 today!)
8 ** inndf is a replacement for 'df | awk' in innwatch.ctl and for reporting
9 ** free space in other INN scripts. It doesn't sync, it forks less, and
10 ** it's generally less complicated.
12 ** Usage: inndf [-i] <directory> [<directory> ...]
16 ** Compile with -lserver (ie. /usr/lib/libserver.a) if you run Sun's Online
17 ** DiskSuite under SunOS 4.x. The wrapper functions there make the system
18 ** call transparent; they copy the f_spare values to the correct spots, so
19 ** f_blocks, f_bfree, f_bavail can exceed 2GB.
21 ** Compile with -DHAVE_STATVFS for these systems:
22 ** System V Release 4.x
27 ** Compile with -DHAVE_STATFS for these systems:
28 ** SunOS 4.x/Solaris 1.x
33 ** (Or even better, let autoconf take care of it.)
35 ** Thanks to these folks for bug fixes and porting information:
36 ** Mahesh Ramachandran <rr@eel.ufl.edu>
37 ** Chuck Swiger <chuck@its.com>
38 ** Sang-yong Suh <sysuh@kigam.re.kr>
39 ** Swa Frantzen <Swa.Frantzen@Belgium.EU.net>
40 ** Brad Dickey <bdickey@haverford.edu>
41 ** Taso N. Devetzis <devetzis@snet.net>
42 ** Wei-Yeh Lee <weiyeh@columbia.edu>
43 ** Jeff Garzik <jeff.garzik@spinne.com>
49 #include "inn/innconf.h"
50 #include "inn/messages.h"
56 /* The portability mess. Hide everything in macros so that the actual code
57 is relatively clean. SysV uses statvfs, BSD uses statfs, and ULTRIX is
58 just weird (and isn't worth checking for in configure).
60 df_declare declares a variable of the appropriate type to pass to df_stat
61 along with a path; df_stat will return true on success, false on failure.
62 df_avail gives the number of free blocks, the size of those blocks given
63 in df_bsize (which handles SysV's weird fragment vs. preferred block size
64 thing). df_inodes returns the free inodes. */
66 # include <sys/statvfs.h>
67 # define df_stat(p, s) (statvfs((p), (s)) == 0)
68 # define df_declare(s) struct statvfs s
69 # define df_total(s) ((s).f_blocks)
70 # define df_avail(s) ((s).f_bavail)
71 # define df_scale(s) ((s).f_frsize == 0 ? (s).f_bsize : (s).f_frsize)
72 # define df_files(s) ((s).f_files)
73 # define df_favail(s) ((s).f_favail)
79 # include <sys/param.h>
82 # include <sys/mount.h>
85 # define df_stat(p, s) (statfs((p), (s)) >= 1)
86 # define df_declare(s) struct fs_data s
87 # define df_total(s) ((s).fd_btot)
88 # define df_avail(s) ((s).fd_bfreen)
89 # define df_scale(s) 1024
90 # define df_files(s) ((s).fd_gtot)
91 # define df_favail(s) ((s).fd_gfree)
93 # define df_stat(p, s) (statfs((p), (s)) == 0)
94 # define df_declare(s) struct statfs s
95 # define df_total(s) ((s).f_blocks)
96 # define df_avail(s) ((s).f_bavail)
97 # define df_scale(s) ((s).f_bsize)
98 # define df_files(s) ((s).f_files)
99 # define df_favail(s) ((s).f_ffree)
102 # error "Platform not supported. Neither statvfs nor statfs available."
105 static const char usage[] = "\
106 Usage: inndf [-i] [-f filename] [-F] <directory> [<directory> ...]\n\
110 The first form gives the free space in kilobytes (or the count of free\n\
111 inodes if -i is given) in the file systems given by the arguments. If\n\
112 -f is given, the corresponding file should be a list of directories to\n\
113 check in addition to the arguments. -F uses <pathetc>/filesystems as the\n\
114 file and is otherwise the same.\n\
116 The second form gives the total count of overview records stored. The\n\
117 third form gives the percentage space allocated to overview that's been\n\
118 used (if the overview method used supports this query).";
121 ** Given a path, a flag saying whether to look at inodes instead of free
122 ** disk space, and a flag saying whether to format in columns, print out
123 ** the amount of free space or inodes on that file system. Returns the
124 ** percentage free, which may be printed out by the caller.
127 printspace(const char *path, bool inode, bool fancy)
130 unsigned long amount;
133 if (df_stat(path, &info)) {
135 amount = df_favail(info);
137 /* This value is compared using the shell by innwatch, and some
138 shells can't cope with anything larger than the maximum value
139 of a signed long. ReiserFS returns 2^32 - 1, however, since it
140 has no concept of inodes. So cap the returned value at the max
141 value of a signed long. */
142 if (amount > (1UL << 31) - 1)
143 amount = (1UL << 31) - 1;
145 /* 2.6 kernels show 0 available and used inodes, instead. */
146 if (amount == 0 && df_files(info) == 0)
147 amount = (1UL << 31) - 1;
149 /* Do the multiplication in floating point to try to retain
150 accuracy if the free space in bytes would overflow an
151 unsigned long. This should be safe until file systems larger
152 than 4TB (which may not be much longer -- we should use long
153 long instead if we have it).
155 Be very careful about the order of casts here; it's too
156 easy to cast back into an unsigned long a value that
157 overflows, and one then gets silently wrong results. */
158 amount = (unsigned long)
159 (((double) df_avail(info) * df_scale(info)) / 1024.0);
162 /* On error, free space is zero. */
165 printf(fancy ? "%10lu" : "%lu", amount);
167 printf(inode ? " inodes available " : " Kbytes available ");
169 percent = 100 * ((double) df_favail(info) / df_files(info));
171 percent = 100 * ((double) df_avail(info) / df_total(info));
173 printf(" (%3.1f%%)", percent);
174 else if (percent < 99.95)
175 printf(" (%4.1f%%)", percent);
177 printf("(%5.1f%%)", percent);
182 printspace_formatted(const char *path, bool inode)
184 printf("%-40s ", path);
185 printspace(path, inode, true);
190 readline(QIOSTATE *qp)
194 for (line = QIOread(qp); line != NULL; line = QIOread(qp)) {
195 p = strchr(line, '#');
198 for (; *line == ' ' || *line == '\t'; line++)
201 for (p = line; *p != '\0' && *p != ' ' && *p != '\t'; p++)
211 main(int argc, char *argv[])
213 int option, i, count;
216 char *active, *group, *line, *p;
219 bool overview = false;
220 bool ovcount = false;
221 bool use_filesystems = false;
223 while ((option = getopt(argc, argv, "hinof:F")) != EOF) {
228 printf("%s\n", usage);
241 die("inndf: Only one of -f or -F may be given");
242 file = xstrdup(optarg);
246 die("inndf: Only one of -f or -F may be given");
247 if (!innconf_read(NULL))
249 file = concatpath(innconf->pathetc, INN_PATH_FILESYSTEMS);
250 use_filesystems = true;
257 if (argc == 0 && !overview && !ovcount && file == NULL)
260 /* Set the program name now rather than earlier so that it doesn't get
261 prepended to usage messages. */
262 message_program_name = "inndf";
264 /* If directories were specified, get statistics about them. If only
265 one was given, just print out the number without the path or any
266 explanatory text; this mode is used by e.g. innwatch. Otherwise,
267 format things nicely. */
268 if (argc == 1 && !overview && !ovcount && file == NULL) {
269 printspace(argv[0], inode, false);
272 for (i = 0; i < argc; i++)
273 printspace_formatted(argv[i], inode);
277 if (!use_filesystems)
278 sysdie("can't open %s", file);
281 while (line != NULL) {
282 printspace_formatted(line, inode);
291 /* If we're going to be getting information from overview, do the icky
292 initialization stuff. */
293 if (overview || ovcount) {
294 if (!use_filesystems)
295 if (!innconf_read(NULL))
297 if (!OVopen(OV_READ))
298 die("OVopen failed");
301 /* For the count, we have to troll through the active file and query the
302 overview backend for each group. */
304 active = concatpath(innconf->pathdb, _PATH_ACTIVE);
305 qp = QIOopen(active);
307 sysdie("can't open %s", active);
311 while (group != NULL) {
312 p = strchr(group, ' ');
315 if (OVgroupstats(group, NULL, NULL, &count, NULL))
320 printf("%lu overview records stored\n", total);
323 /* Percentage used is simpler, but only some overview methods understand
326 if (OVctl(OVSPACE, &count)) {
328 printf("Space used is meaningless for the %s method\n",
331 printf("%d%% overview space used\n", count);