chiark / gitweb /
New state diagrams whitespace changes
[inn-innduct.git] / backends / inndf.c
1 /*  $Id: inndf.c 6677 2004-03-03 18:36:07Z hkehoe $
2 **
3 **  Reports free kilobytes (not disk blocks) or free inodes.
4 **
5 **  Written by Ian Dickinson <idickins@fore.com>
6 **  Wed Jul 26 10:11:38 BST 1995 (My birthday - 27 today!)
7 **
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.
11 **
12 **  Usage: inndf [-i] <directory> [<directory> ...]
13 **         inndf -n
14 **         inndf -o
15 **
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.
20 **
21 **  Compile with -DHAVE_STATVFS for these systems:
22 **          System V Release 4.x
23 **          Solaris 2.x
24 **          HP-UX 10.x
25 **          OSF1
26 **  
27 **  Compile with -DHAVE_STATFS for these systems:
28 **          SunOS 4.x/Solaris 1.x
29 **          HP-UX 9.x
30 **          Linux
31 **          NeXTstep 3.x
32 **
33 **  (Or even better, let autoconf take care of it.)
34 **  
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>
44 */
45
46 #include "config.h"
47 #include "clibrary.h"
48
49 #include "inn/innconf.h"
50 #include "inn/messages.h"
51 #include "inn/qio.h"
52 #include "libinn.h"
53 #include "ov.h"
54 #include "paths.h"
55
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).
59
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. */
65 #if HAVE_STATVFS
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)
74 #elif HAVE_STATFS
75 # if HAVE_SYS_VFS_H
76 #  include <sys/vfs.h>
77 # endif
78 # if HAVE_SYS_PARAM_H
79 #  include <sys/param.h>
80 # endif
81 # if HAVE_SYS_MOUNT_H
82 #  include <sys/mount.h>
83 # endif
84 # ifdef __ultrix__
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)
92 # else
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)
100 # endif
101 #else
102 # error "Platform not supported.  Neither statvfs nor statfs available."
103 #endif
104
105 static const char usage[] = "\
106 Usage: inndf [-i] [-f filename] [-F] <directory> [<directory> ...]\n\
107        inndf -n\n\
108        inndf -o\n\
109 \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\
115 \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).";
119
120 /*
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.
125 */
126 static void
127 printspace(const char *path, bool inode, bool fancy)
128 {
129     df_declare(info);
130     unsigned long amount;
131     double percent;
132
133     if (df_stat(path, &info)) {
134         if (inode) {
135             amount = df_favail(info);
136
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;
144
145             /* 2.6 kernels show 0 available and used inodes, instead. */
146             if (amount == 0 && df_files(info) == 0)
147                 amount = (1UL << 31) - 1;
148         } else {
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).
154
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);
160         }
161     } else {
162         /* On error, free space is zero. */
163         amount = 0;
164     }
165     printf(fancy ? "%10lu" : "%lu", amount);
166     if (fancy) {
167         printf(inode ? " inodes available " : " Kbytes available ");
168         if (inode)
169             percent = 100 * ((double) df_favail(info) / df_files(info));
170         else
171             percent = 100 * ((double) df_avail(info) / df_total(info));
172         if (percent < 9.95)
173             printf("  (%3.1f%%)", percent);
174         else if (percent < 99.95)
175             printf(" (%4.1f%%)", percent);
176         else
177             printf("(%5.1f%%)", percent);
178     }
179 }
180
181 static void
182 printspace_formatted(const char *path, bool inode)
183 {
184     printf("%-40s ", path);
185     printspace(path, inode, true);
186     printf("\n");
187 }
188
189 static char *
190 readline(QIOSTATE *qp)
191 {
192     char *line, *p;
193
194     for (line = QIOread(qp); line != NULL; line = QIOread(qp)) {
195         p = strchr(line, '#');
196         if (p != NULL)
197             *p = '\0';
198         for (; *line == ' ' || *line == '\t'; line++)
199             ;
200         if (*line != '\0') {
201             for (p = line; *p != '\0' && *p != ' ' && *p != '\t'; p++)
202                 ;
203             *p = '\0';
204             return line;
205         }
206     }
207     return NULL;
208 }
209
210 int
211 main(int argc, char *argv[])
212 {
213     int option, i, count;
214     unsigned long total;
215     QIOSTATE *qp;
216     char *active, *group, *line, *p;
217     char *file = NULL;
218     bool inode = false;
219     bool overview = false;
220     bool ovcount = false;
221     bool use_filesystems = false;
222
223     while ((option = getopt(argc, argv, "hinof:F")) != EOF) {
224         switch (option) {
225         default:
226             die(usage);
227         case 'h':
228             printf("%s\n", usage);
229             exit(0);
230         case 'i':
231             inode = true;
232             break;
233         case 'n':
234             ovcount = true;
235             break;
236         case 'o':
237             overview = true;
238             break;
239         case 'f':
240             if (file != NULL)
241                 die("inndf: Only one of -f or -F may be given");
242             file = xstrdup(optarg);
243             break;
244         case 'F':
245             if (file != NULL)
246                 die("inndf: Only one of -f or -F may be given");
247             if (!innconf_read(NULL))
248                 exit(1);
249             file = concatpath(innconf->pathetc, INN_PATH_FILESYSTEMS);
250             use_filesystems = true;
251             break;
252         }
253     }
254     argc -= optind;
255     argv += optind;
256
257     if (argc == 0 && !overview && !ovcount && file == NULL)
258         die(usage);
259
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";
263
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);
270         printf("\n");
271     } else {
272         for (i = 0; i < argc; i++)
273             printspace_formatted(argv[i], inode);
274         if (file != NULL) {
275             qp = QIOopen(file);
276             if (qp == NULL) {
277                 if (!use_filesystems)
278                     sysdie("can't open %s", file);
279             } else {
280                 line = readline(qp);
281                 while (line != NULL) {
282                     printspace_formatted(line, inode);
283                     line = readline(qp);
284                 }
285                 QIOclose(qp);
286             }
287             free(file);
288         }
289     }
290
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))
296                 exit(1);
297         if (!OVopen(OV_READ))
298             die("OVopen failed");
299     }
300
301     /* For the count, we have to troll through the active file and query the
302        overview backend for each group. */
303     if (ovcount) {
304         active = concatpath(innconf->pathdb, _PATH_ACTIVE);
305         qp = QIOopen(active);
306         if (qp == NULL)
307             sysdie("can't open %s", active);
308
309         total = 0;
310         group = QIOread(qp);
311         while (group != NULL) {
312             p = strchr(group, ' ');
313             if (p != NULL)
314                 *p = '\0';
315             if (OVgroupstats(group, NULL, NULL, &count, NULL))
316                 total += count;
317             group = QIOread(qp);
318         }
319         QIOclose(qp);
320         printf("%lu overview records stored\n", total);
321     }
322
323     /* Percentage used is simpler, but only some overview methods understand
324        that query. */
325     if (overview) {
326         if (OVctl(OVSPACE, &count)) {
327             if (count == -1)
328                 printf("Space used is meaningless for the %s method\n",
329                        innconf->ovmethod);
330             else
331                 printf("%d%% overview space used\n", count);
332         }
333     }
334     exit(0);
335 }