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