(usage): Include one- or two-line synopsis in --help output.
[platform/upstream/coreutils.git] / src / df.c
1 /* df - summarize free disk space
2    Copyright (C) 1991, 1995 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Usage: df [-aikPT] [-t fstype] [-x fstype] [--all] [--inodes] [--print-type]
19    [--type fstype] [--exclude-type fstype] [--kilobytes] [--portability]
20    [file...]
21
22    Options:
23    -a, --all            List all filesystems, even zero-size ones.
24    -i, --inodes         List inode usage information instead of block usage.
25    -k, --kilobytes      Print sizes in 1K blocks instead of 512-byte blocks.
26    -P, --portability    Use the POSIX output format (one line per filesystem).
27    -T, --print-type     Print filesystem type.
28    -t, --type fstype    Limit the listing to filesystems of type `fstype'.
29    -x, --exclude-type=fstype
30                         Limit the listing to filesystems not of type `fstype'.
31                         Multiple -t and/or -x options can be given.
32                         By default, all filesystem types are listed.
33
34    Written by David MacKenzie <djm@gnu.ai.mit.edu> */
35
36 #include <config.h>
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <getopt.h>
40
41 #include "mountlist.h"
42 #include "fsusage.h"
43 #include "system.h"
44 #include "version.h"
45 #include "error.h"
46
47 char *xmalloc ();
48 char *xstrdup ();
49
50 static int selected_fstype ();
51 static int excluded_fstype ();
52 static void add_excluded_fs_type ();
53 static void add_fs_type ();
54 static void print_header ();
55 static void show_entry ();
56 static void show_all_entries ();
57 static void show_dev ();
58 static void show_disk ();
59 static void show_point ();
60 static void usage ();
61
62 /* Name this program was run with. */
63 char *program_name;
64
65 /* If nonzero, show inode information. */
66 static int inode_format;
67
68 /* If nonzero, show even filesystems with zero size or
69    uninteresting types. */
70 static int show_all_fs;
71
72 /* If nonzero, output data for each filesystem corresponding to a
73    command line argument -- even if it's a dummy (automounter) entry.  */
74 static int show_listed_fs;
75
76 /* If nonzero, use 1K blocks instead of 512-byte blocks. */
77 static int kilobyte_blocks;
78
79 /* If nonzero, use the POSIX output format.  */
80 static int posix_format;
81
82 /* If nonzero, invoke the `sync' system call before getting any usage data.
83    Using this option can make df very slow, especially with many or very
84    busy disks.  Default to non-zero because the sync call does make a
85    difference on some systems -- SunOs4.1.3, for one.  I have been assured
86    that it is *not* necessary on Linux, so there should be a way to
87    configure this.  FIXME.  */
88 static int require_sync = 1;
89
90 /* Nonzero if errors have occurred. */
91 static int exit_status;
92
93 /* A filesystem type to display. */
94
95 struct fs_type_list
96 {
97   char *fs_name;
98   struct fs_type_list *fs_next;
99 };
100
101 /* Linked list of filesystem types to display.
102    If `fs_select_list' is NULL, list all types.
103    This table is generated dynamically from command-line options,
104    rather than hardcoding into the program what it thinks are the
105    valid filesystem types; let the user specify any filesystem type
106    they want to, and if there are any filesystems of that type, they
107    will be shown.
108
109    Some filesystem types:
110    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
111
112 static struct fs_type_list *fs_select_list;
113
114 /* Linked list of filesystem types to omit.
115    If the list is empty, don't exclude any types.  */
116
117 static struct fs_type_list *fs_exclude_list;
118
119 /* Linked list of mounted filesystems. */
120 static struct mount_entry *mount_list;
121
122 /* If non-zero, display usage information and exit.  */
123 static int show_help;
124
125 /* If non-zero, print the version on standard output and exit.  */
126 static int show_version;
127
128 /* If non-zero, print filesystem type as well.  */
129 static int print_type;
130
131 static struct option const long_options[] =
132 {
133   {"all", no_argument, &show_all_fs, 1},
134   {"inodes", no_argument, &inode_format, 1},
135   {"kilobytes", no_argument, &kilobyte_blocks, 1},
136   {"portability", no_argument, &posix_format, 1},
137   {"print-type", no_argument, &print_type, 1},
138   {"sync", no_argument, 0, 129},
139   {"no-sync", no_argument, 0, 130},
140   {"type", required_argument, 0, 't'},
141   {"exclude-type", required_argument, 0, 'x'},
142   {"help", no_argument, &show_help, 1},
143   {"version", no_argument, &show_version, 1},
144   {NULL, 0, NULL, 0}
145 };
146
147 void
148 main (argc, argv)
149      int argc;
150      char **argv;
151 {
152   int i;
153   struct stat *stats;
154
155   program_name = argv[0];
156   fs_select_list = NULL;
157   fs_exclude_list = NULL;
158   inode_format = 0;
159   show_all_fs = 0;
160   show_listed_fs = 0;
161   kilobyte_blocks = getenv ("POSIXLY_CORRECT") == 0;
162   print_type = 0;
163   posix_format = 0;
164   exit_status = 0;
165
166   while ((i = getopt_long (argc, argv, "aikPTt:vx:", long_options, (int *) 0))
167          != EOF)
168     {
169       switch (i)
170         {
171         case 0:                 /* Long option. */
172           break;
173         case 'a':
174           show_all_fs = 1;
175           break;
176         case 'i':
177           inode_format = 1;
178           break;
179         case 'k':
180           kilobyte_blocks = 1;
181           break;
182         case 'T':
183           print_type = 1;
184           break;
185         case 'P':
186           posix_format = 1;
187           break;
188         case 129:
189           require_sync = 1;
190           break;
191         case 130:
192           require_sync = 0;
193           break;
194         case 't':
195           add_fs_type (optarg);
196           break;
197         case 'v':               /* For SysV compatibility. */
198           break;
199         case 'x':
200           add_excluded_fs_type (optarg);
201           break;
202         default:
203           usage (1);
204         }
205     }
206
207   if (show_version)
208     {
209       printf ("%s\n", version_string);
210       exit (0);
211     }
212
213   if (show_help)
214     usage (0);
215
216   if (optind == argc)
217     {
218 #ifdef lint
219       /* Suppress `used before initialized' warning.  */
220       stats = NULL;
221 #endif
222     }
223   else
224     {
225       /* stat all the given entries to make sure they get automounted,
226          if necessary, before reading the filesystem table.  */
227       stats = (struct stat *)
228         xmalloc ((argc - optind) * sizeof (struct stat));
229       for (i = optind; i < argc; ++i)
230         if (stat (argv[i], &stats[i - optind]))
231           {
232             error (0, errno, "%s", argv[i]);
233             exit_status = 1;
234             argv[i] = NULL;
235           }
236     }
237
238   mount_list =
239     read_filesystem_list ((fs_select_list != NULL || fs_exclude_list != NULL),
240                           show_all_fs);
241
242   if (mount_list == NULL)
243     error (1, errno, "cannot read table of mounted filesystems");
244
245   print_header ();
246   if (require_sync)
247     sync ();
248
249   if (optind == argc)
250     show_all_entries ();
251   else
252     {
253       /* Display explicitly requested empty filesystems. */
254       show_listed_fs = 1;
255
256       for (i = optind; i < argc; ++i)
257         if (argv[i])
258           show_entry (argv[i], &stats[i - optind]);
259     }
260
261   exit (exit_status);
262 }
263 \f
264 static void
265 print_header ()
266 {
267   printf ("Filesystem ");
268
269   if (print_type)
270     printf ("   Type");
271   else
272     printf ("       ");
273
274   if (inode_format)
275     printf ("   Inodes   IUsed   IFree  %%IUsed");
276   else
277     printf (" %s  Used Available Capacity",
278             kilobyte_blocks ? "1024-blocks" : " 512-blocks");
279   printf (" Mounted on\n");
280 }
281 \f
282 /* Show all mounted filesystems, except perhaps those that are of
283    an unselected type or are empty. */
284
285 static void
286 show_all_entries ()
287 {
288   struct mount_entry *me;
289
290   for (me = mount_list; me; me = me->me_next)
291     show_dev (me->me_devname, me->me_mountdir, me->me_type);
292 }
293
294 /* Determine what kind of node PATH is and show the disk usage
295    for it.  STATP is the results of `stat' on PATH.  */
296
297 static void
298 show_entry (path, statp)
299      char *path;
300      struct stat *statp;
301 {
302   if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
303     show_disk (path);
304   else
305     show_point (path, statp);
306 }
307
308 /* Identify the directory, if any, that device
309    DISK is mounted on, and show its disk usage.  */
310
311 static void
312 show_disk (disk)
313      char *disk;
314 {
315   struct mount_entry *me;
316
317   for (me = mount_list; me; me = me->me_next)
318     if (!strcmp (disk, me->me_devname))
319       {
320         show_dev (me->me_devname, me->me_mountdir, me->me_type);
321         return;
322       }
323   /* No filesystem is mounted on DISK. */
324   show_dev (disk, (char *) NULL, (char *) NULL);
325 }
326
327 /* Figure out which device file or directory POINT is mounted on
328    and show its disk usage.
329    STATP is the results of `stat' on POINT.  */
330
331 static void
332 show_point (point, statp)
333      char *point;
334      struct stat *statp;
335 {
336   struct stat disk_stats;
337   struct mount_entry *me;
338
339   for (me = mount_list; me; me = me->me_next)
340     {
341       if (me->me_dev == (dev_t) -1)
342         {
343           if (stat (me->me_mountdir, &disk_stats) == 0)
344             me->me_dev = disk_stats.st_dev;
345           else
346             {
347               error (0, errno, "%s", me->me_mountdir);
348               exit_status = 1;
349               me->me_dev = -2;  /* So we won't try and fail repeatedly. */
350             }
351         }
352
353       if (statp->st_dev == me->me_dev)
354         {
355           show_dev (me->me_devname, me->me_mountdir, me->me_type);
356           return;
357         }
358     }
359   error (0, 0, "cannot find mount point for %s", point);
360   exit_status = 1;
361 }
362 \f
363 /* Display a space listing for the disk device with absolute path DISK.
364    If MOUNT_POINT is non-NULL, it is the path of the root of the
365    filesystem on DISK.
366    If FSTYPE is non-NULL, it is the type of the filesystem on DISK. */
367
368 static void
369 show_dev (disk, mount_point, fstype)
370      char *disk;
371      char *mount_point;
372      char *fstype;
373 {
374   struct fs_usage fsu;
375   long blocks_used;
376   long blocks_percent_used;
377   long inodes_used;
378   long inodes_percent_used;
379   char *stat_file;
380
381   if (!selected_fstype (fstype) || excluded_fstype (fstype))
382     return;
383
384   /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
385      program reports on the filesystem that the special file is on.
386      It would be better to report on the unmounted filesystem,
387      but statfs doesn't do that on most systems.  */
388   stat_file = mount_point ? mount_point : disk;
389
390   if (get_fs_usage (stat_file, disk, &fsu))
391     {
392       error (0, errno, "%s", stat_file);
393       exit_status = 1;
394       return;
395     }
396
397   if (kilobyte_blocks)
398     {
399       fsu.fsu_blocks /= 2;
400       fsu.fsu_bfree /= 2;
401       fsu.fsu_bavail /= 2;
402     }
403
404   if (fsu.fsu_blocks == 0)
405     {
406       if (!show_all_fs && !show_listed_fs)
407         return;
408       blocks_used = fsu.fsu_bavail = blocks_percent_used = 0;
409     }
410   else
411     {
412       blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
413       blocks_percent_used = (long)
414         (blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail) + 0.5);
415     }
416
417   if (fsu.fsu_files == 0)
418     {
419       inodes_used = fsu.fsu_ffree = inodes_percent_used = 0;
420     }
421   else
422     {
423       inodes_used = fsu.fsu_files - fsu.fsu_ffree;
424       inodes_percent_used = (long)
425         (inodes_used * 100.0 / fsu.fsu_files + 0.5);
426     }
427
428   printf ((print_type ? "%-13s" : "%-20s"), disk);
429   if (strlen (disk) > (print_type ? 13 : 20) && !posix_format)
430     printf ((print_type ? "\n%13s" : "\n%20s"), "");
431
432   if (print_type)
433     printf (" %-5s ", fstype);
434
435   if (inode_format)
436     printf (" %7ld %7ld %7ld %5ld%%",
437             fsu.fsu_files, inodes_used, fsu.fsu_ffree, inodes_percent_used);
438   else
439     printf (" %7ld %7ld  %7ld  %5ld%% ",
440             fsu.fsu_blocks, blocks_used, fsu.fsu_bavail, blocks_percent_used);
441
442   if (mount_point)
443     printf ("  %s", mount_point);
444   putchar ('\n');
445 }
446 \f
447 /* Add FSTYPE to the list of filesystem types to display. */
448
449 static void
450 add_fs_type (fstype)
451      char *fstype;
452 {
453   struct fs_type_list *fsp;
454
455   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
456   fsp->fs_name = fstype;
457   fsp->fs_next = fs_select_list;
458   fs_select_list = fsp;
459 }
460
461 /* Add FSTYPE to the list of filesystem types to be omitted. */
462
463 static void
464 add_excluded_fs_type (fstype)
465      char *fstype;
466 {
467   struct fs_type_list *fsp;
468
469   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
470   fsp->fs_name = fstype;
471   fsp->fs_next = fs_exclude_list;
472   fs_exclude_list = fsp;
473 }
474
475 /* If FSTYPE is a type of filesystem that should be listed,
476    return nonzero, else zero. */
477
478 static int
479 selected_fstype (fstype)
480      char *fstype;
481 {
482   struct fs_type_list *fsp;
483
484   if (fs_select_list == NULL || fstype == NULL)
485     return 1;
486   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
487     if (!strcmp (fstype, fsp->fs_name))
488       return 1;
489   return 0;
490 }
491 \f
492
493 /* If FSTYPE is a type of filesystem that should be omitted,
494    return nonzero, else zero. */
495
496 static int
497 excluded_fstype (fstype)
498      char *fstype;
499 {
500   struct fs_type_list *fsp;
501
502   if (fs_exclude_list == NULL || fstype == NULL)
503     return 0;
504   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
505     if (!strcmp (fstype, fsp->fs_name))
506       return 1;
507   return 0;
508 }
509
510 static void
511 usage (status)
512      int status;
513 {
514   if (status != 0)
515     fprintf (stderr, "Try `%s --help' for more information.\n",
516              program_name);
517   else
518     {
519       printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
520       printf ("\
521 \n\
522   -a, --all                 include filesystems having 0 blocks\n\
523   -i, --inodes              list inode information instead of block usage\n\
524   -k, --kilobytes           use 1024 blocks, not 512 despite POSIXLY_CORRECT\n\
525       --sync                invoke sync before getting usage info (default)\n\
526       --no-sync             do not invoke sync before getting usage info\n\
527   -t, --type=TYPE           limit the listing to TYPE filesystems type\n\
528   -x, --exclude-type=TYPE   limit the listing to not TYPE filesystems type\n\
529   -v                        (ignored)\n\
530   -P, --portability         use the POSIX output format\n\
531   -T, --print-type          print filesystem type\n\
532       --help                display this help and exit\n\
533       --version             output version information and exit\n\
534 \n\
535 If no FILEs are given, list all currently mounted filesystems.\n");
536     }
537   exit (status);
538 }