dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / du / du.c
blob4ae37c1f2232569b9f3b7511f620576c302a63fb
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved.
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * du -- summarize disk usage
32 * du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] [file...]
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/avl.h>
38 #include <fcntl.h>
39 #include <dirent.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <locale.h>
46 #include <libcmdutils.h>
49 static int aflg = 0;
50 static int rflg = 0;
51 static int sflg = 0;
52 static int kflg = 0;
53 static int mflg = 0;
54 static int oflg = 0;
55 static int dflg = 0;
56 static int hflg = 0;
57 static int Aflg = 0;
58 static int Hflg = 0;
59 static int Lflg = 0;
60 static int cmdarg = 0; /* Command line argument */
61 static char *dot = ".";
62 static int level = 0; /* Level of recursion */
64 static char *base;
65 static char *name;
66 static size_t base_len = PATH_MAX + 1; /* # of chars for base */
67 static size_t name_len = PATH_MAX + 1; /* # of chars for name */
69 #define NUMBER_WIDTH 64
70 typedef char numbuf_t[NUMBER_WIDTH];
73 * Output formats. illumos uses a tab as separator, XPG4 a space.
75 #ifdef XPG4
76 #define FORMAT1 "%s %s\n"
77 #define FORMAT2 "%lld %s\n"
78 #else
79 #define FORMAT1 "%s\t%s\n"
80 #define FORMAT2 "%lld\t%s\n"
81 #endif
84 * convert DEV_BSIZE blocks to K blocks
86 #define DEV_BSIZE 512
87 #define DEV_KSHIFT 1
88 #define DEV_MSHIFT 11
89 #define kb(n) (((u_longlong_t)(n)) >> DEV_KSHIFT)
90 #define mb(n) (((u_longlong_t)(n)) >> DEV_MSHIFT)
92 long wait();
93 static u_longlong_t descend(char *curname, int curfd, int *retcode,
94 dev_t device);
95 static void printsize(blkcnt_t blocks, char *path);
96 static void exitdu(int exitcode);
98 static avl_tree_t *tree = NULL;
101 main(int argc, char **argv)
103 blkcnt_t blocks = 0;
104 int c;
105 extern int optind;
106 char *np;
107 pid_t pid, wpid;
108 int status, retcode = 0;
109 setbuf(stderr, NULL);
110 (void) setlocale(LC_ALL, "");
111 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
112 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
113 #endif
114 (void) textdomain(TEXT_DOMAIN);
116 #ifdef XPG4
117 rflg++; /* "-r" is not an option but ON always */
118 #endif
120 while ((c = getopt(argc, argv, "aAdhHkLmorsx")) != EOF)
121 switch (c) {
123 case 'a':
124 aflg++;
125 continue;
127 case 'h':
128 hflg++;
129 kflg = 0;
130 mflg = 0;
131 continue;
133 case 'r':
134 rflg++;
135 continue;
137 case 's':
138 sflg++;
139 continue;
141 case 'k':
142 kflg++;
143 hflg = 0;
144 mflg = 0;
145 continue;
147 case 'm':
148 mflg++;
149 hflg = 0;
150 kflg = 0;
151 continue;
153 case 'o':
154 oflg++;
155 continue;
157 case 'd':
158 dflg++;
159 continue;
161 case 'x':
162 dflg++;
163 continue;
165 case 'A':
166 Aflg++;
167 continue;
169 case 'H':
170 Hflg++;
171 /* -H and -L are mutually exclusive */
172 Lflg = 0;
173 cmdarg++;
174 continue;
176 case 'L':
177 Lflg++;
178 /* -H and -L are mutually exclusive */
179 Hflg = 0;
180 cmdarg = 0;
181 continue;
182 case '?':
183 (void) fprintf(stderr, gettext(
184 "usage: du [-Adorx] [-a|-s] [-h|-k|-m] [-H|-L] "
185 "[file...]\n"));
186 exit(2);
188 if (optind == argc) {
189 argv = &dot;
190 argc = 1;
191 optind = 0;
194 /* "-o" and "-s" don't make any sense together. */
195 if (oflg && sflg)
196 oflg = 0;
198 if ((base = (char *)calloc(base_len, sizeof (char))) == NULL) {
199 perror("du");
200 exit(1);
202 if ((name = (char *)calloc(name_len, sizeof (char))) == NULL) {
203 perror("du");
204 free(base);
205 exit(1);
207 do {
208 if (optind < argc - 1) {
209 pid = fork();
210 if (pid == (pid_t)-1) {
211 perror(gettext("du: No more processes"));
212 exitdu(1);
214 if (pid != 0) {
215 while ((wpid = wait(&status)) != pid &&
216 wpid != (pid_t)-1)
218 if (pid != (pid_t)-1 && status != 0)
219 retcode = 1;
222 if (optind == argc - 1 || pid == 0) {
223 while (base_len < (strlen(argv[optind]) + 1)) {
224 base_len = base_len * 2;
225 if ((base = reallocarray(base, base_len,
226 sizeof (char))) == NULL) {
227 if (rflg) {
228 (void) fprintf(stderr, gettext(
229 "du: can't process %s"),
230 argv[optind]);
231 perror("");
233 exitdu(1);
236 if (base_len > name_len) {
237 name_len = base_len;
238 if ((name = reallocarray(name, name_len,
239 sizeof (char))) == NULL) {
240 if (rflg) {
241 (void) fprintf(stderr, gettext(
242 "du: can't process %s"),
243 argv[optind]);
244 perror("");
246 exitdu(1);
249 (void) strcpy(base, argv[optind]);
250 (void) strcpy(name, argv[optind]);
251 if (np = strrchr(name, '/')) {
252 *np++ = '\0';
253 if (chdir(*name ? name : "/") < 0) {
254 if (rflg) {
255 (void) fprintf(stderr, "du: ");
256 perror(*name ? name : "/");
257 exitdu(1);
259 exitdu(0);
261 } else
262 np = base;
263 blocks = descend(*np ? np : ".", 0, &retcode,
264 (dev_t)0);
265 if (sflg)
266 printsize(blocks, base);
267 if (optind < argc - 1)
268 exitdu(retcode);
270 optind++;
271 } while (optind < argc);
272 exitdu(retcode);
274 return (retcode);
278 * descend recursively, adding up the allocated blocks.
279 * If curname is NULL, curfd is used.
281 static u_longlong_t
282 descend(char *curname, int curfd, int *retcode, dev_t device)
284 static DIR *dirp = NULL;
285 char *ebase0, *ebase;
286 struct stat stb, stb1;
287 int i, j, ret, fd, tmpflg;
288 int follow_symlinks;
289 blkcnt_t blocks = 0;
290 off_t curoff = 0;
291 ptrdiff_t offset;
292 ptrdiff_t offset0;
293 struct dirent *dp;
294 char dirbuf[PATH_MAX + 1];
295 u_longlong_t retval;
297 ebase0 = ebase = strchr(base, 0);
298 if (ebase > base && ebase[-1] == '/')
299 ebase--;
300 offset = ebase - base;
301 offset0 = ebase0 - base;
303 if (curname)
304 curfd = AT_FDCWD;
307 * If neither a -L or a -H was specified, don't follow symlinks.
308 * If a -H was specified, don't follow symlinks if the file is
309 * not a command line argument.
311 follow_symlinks = (Lflg || (Hflg && cmdarg));
312 if (follow_symlinks) {
313 i = fstatat(curfd, curname, &stb, 0);
314 j = fstatat(curfd, curname, &stb1, AT_SYMLINK_NOFOLLOW);
317 * Make sure any files encountered while traversing the
318 * hierarchy are not considered command line arguments.
320 if (Hflg) {
321 cmdarg = 0;
323 } else {
324 i = fstatat(curfd, curname, &stb, AT_SYMLINK_NOFOLLOW);
325 j = 0;
328 if ((i < 0) || (j < 0)) {
329 if (rflg) {
330 (void) fprintf(stderr, "du: ");
331 perror(base);
335 * POSIX states that non-zero status codes are only set
336 * when an error message is printed out on stderr
338 *retcode = (rflg ? 1 : 0);
339 *ebase0 = 0;
340 return (0);
342 if (device) {
343 if (dflg && stb.st_dev != device) {
344 *ebase0 = 0;
345 return (0);
348 else
349 device = stb.st_dev;
352 * If following links (-L) we need to keep track of all inodes
353 * visited so they are only visited/reported once and cycles
354 * are avoided. Otherwise, only keep track of files which are
355 * hard links so they only get reported once, and of directories
356 * so we don't report a directory and its hierarchy more than
357 * once in the special case in which it lies under the
358 * hierarchy of a directory which is a hard link.
359 * Note: Files with multiple links should only be counted
360 * once. Since each inode could possibly be referenced by a
361 * symbolic link, we need to keep track of all inodes when -L
362 * is specified.
364 if (Lflg || ((stb.st_mode & S_IFMT) == S_IFDIR) ||
365 (stb.st_nlink > 1)) {
366 int rc;
367 if ((rc = add_tnode(&tree, stb.st_dev, stb.st_ino)) != 1) {
368 if (rc == 0) {
370 * This hierarchy, or file with multiple
371 * links, has already been visited/reported.
373 return (0);
374 } else {
376 * An error occurred while trying to add the
377 * node to the tree.
379 if (rflg) {
380 perror("du");
382 exitdu(1);
386 blocks = Aflg ? (stb.st_size/DEV_BSIZE) : stb.st_blocks;
389 * If there are extended attributes on the current file, add their
390 * block usage onto the block count. Note: Since pathconf() always
391 * follows symlinks, only test for extended attributes using pathconf()
392 * if we are following symlinks or the current file is not a symlink.
394 if (curname && (follow_symlinks ||
395 ((stb.st_mode & S_IFMT) != S_IFLNK)) &&
396 pathconf(curname, _PC_XATTR_EXISTS) == 1) {
397 if ((fd = attropen(curname, ".", O_RDONLY)) < 0) {
398 if (rflg)
399 perror(gettext(
400 "du: can't access extended attributes"));
402 else
404 tmpflg = sflg;
405 sflg = 1;
406 blocks += descend(NULL, fd, retcode, device);
407 sflg = tmpflg;
410 if ((stb.st_mode & S_IFMT) != S_IFDIR) {
412 * Don't print twice: if sflg, file will get printed in main().
413 * Otherwise, level == 0 means this file is listed on the
414 * command line, so print here; aflg means print all files.
416 if (sflg == 0 && (aflg || level == 0))
417 printsize(blocks, base);
418 return (blocks);
420 if (dirp != NULL)
422 * Close the parent directory descriptor, we will reopen
423 * the directory when we pop up from this level of the
424 * recursion.
426 (void) closedir(dirp);
427 if (curname == NULL)
428 dirp = fdopendir(curfd);
429 else
430 dirp = opendir(curname);
431 if (dirp == NULL) {
432 if (rflg) {
433 (void) fprintf(stderr, "du: ");
434 perror(base);
436 *retcode = 1;
437 *ebase0 = 0;
438 return (0);
440 level++;
441 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode))) {
442 if (getcwd(dirbuf, PATH_MAX) == NULL) {
443 if (rflg) {
444 (void) fprintf(stderr, "du: ");
445 perror(base);
447 exitdu(1);
450 if ((curname ? (chdir(curname) < 0) : (fchdir(curfd) < 0))) {
451 if (rflg) {
452 (void) fprintf(stderr, "du: ");
453 perror(base);
455 *retcode = 1;
456 *ebase0 = 0;
457 (void) closedir(dirp);
458 dirp = NULL;
459 level--;
460 return (0);
462 while (dp = readdir(dirp)) {
463 if ((strcmp(dp->d_name, ".") == 0) ||
464 (strcmp(dp->d_name, "..") == 0))
465 continue;
467 * we're about to append "/" + dp->d_name
468 * onto end of base; make sure there's enough
469 * space
471 while ((offset + strlen(dp->d_name) + 2) > base_len) {
472 base_len = base_len * 2;
473 if ((base = reallocarray(base, base_len,
474 sizeof (char))) == NULL) {
475 if (rflg) {
476 perror("du");
478 exitdu(1);
480 ebase = base + offset;
481 ebase0 = base + offset0;
483 /* LINTED - unbounded string specifier */
484 (void) sprintf(ebase, "/%s", dp->d_name);
485 curoff = telldir(dirp);
486 retval = descend(ebase + 1, 0, retcode, device);
487 /* base may have been moved via realloc in descend() */
488 ebase = base + offset;
489 ebase0 = base + offset0;
490 *ebase = 0;
491 blocks += retval;
492 if (dirp == NULL) {
493 if ((dirp = opendir(".")) == NULL) {
494 if (rflg) {
495 (void) fprintf(stderr,
496 gettext("du: Can't reopen in "));
497 perror(base);
499 *retcode = 1;
500 level--;
501 return (0);
503 seekdir(dirp, curoff);
506 (void) closedir(dirp);
507 level--;
508 dirp = NULL;
509 if (sflg == 0)
510 printsize(blocks, base);
511 if (curname == NULL || (Lflg && S_ISLNK(stb1.st_mode)))
512 ret = chdir(dirbuf);
513 else
514 ret = chdir("..");
515 if (ret < 0) {
516 if (rflg) {
517 (void) sprintf(strchr(base, '\0'), "/..");
518 (void) fprintf(stderr,
519 gettext("du: Can't change dir to '..' in "));
520 perror(base);
522 exitdu(1);
524 *ebase0 = 0;
525 if (oflg)
526 return (0);
527 else
528 return (blocks);
532 * Convert an unsigned long long to a string representation and place the
533 * result in the caller-supplied buffer.
534 * The given number is in units of "unit_from" size,
535 * this will first be converted to a number in 1024 or 1000 byte size,
536 * depending on the scaling factor.
537 * Then the number is scaled down until it is small enough to be in a good
538 * human readable format i.e. in the range 0 thru scale-1.
539 * If it's smaller than 10 there's room enough to provide one decimal place.
540 * The value "(unsigned long long)-1" is a special case and is always
541 * converted to "-1".
542 * Returns a pointer to the caller-supplied buffer.
544 static char *
545 number_to_scaled_string(
546 numbuf_t buf, /* put the result here */
547 unsigned long long number, /* convert this number */
548 unsigned long long unit_from, /* number of bytes per input unit */
549 unsigned long long scale) /* 1024 (-h) or 1000 (-H) */
551 unsigned long long save = 0;
552 char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
553 char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */
555 if ((long long)number == (long long)-1) {
556 (void) strcpy(buf, "-1");
557 return (buf);
561 * Convert number from unit_from to given scale (1024 or 1000)
562 * This means multiply number with unit_from and divide by scale.
563 * if number is large enough, we first divide and then multiply
564 * to avoid an overflow
565 * (large enough here means 100 (rather arbitrary value)
566 * times scale in order to reduce rounding errors)
567 * otherwise, we first multiply and then divide
568 * to avoid an underflow
570 if (number >= 100L * scale) {
571 number = number / scale;
572 number = number * unit_from;
573 } else {
574 number = number * unit_from;
575 number = number / scale;
579 * Now we have number as a count of scale units.
580 * Stop scaling when we reached exa bytes, then something is
581 * probably wrong with our number.
583 while ((number >= scale) && (*uom != 'E')) {
584 uom++; /* next unit of measurement */
585 save = number;
586 number = (number + (scale / 2)) / scale;
589 /* check if we should output a decimal place after the point */
590 if (save && ((save / scale) < 10)) {
591 /* sprintf() will round for us */
592 float fnum = (float)save / scale;
593 (void) sprintf(buf, "%4.1f%c", fnum, *uom);
594 } else {
595 (void) sprintf(buf, "%4llu%c", number, *uom);
597 return (buf);
600 static void
601 printsize(blkcnt_t blocks, char *path)
603 if (hflg) {
604 numbuf_t numbuf;
605 unsigned long long scale = 1024L;
606 (void) printf(FORMAT1,
607 number_to_scaled_string(numbuf, blocks, DEV_BSIZE, scale),
608 path);
609 } else if (kflg) {
610 (void) printf(FORMAT2, (long long)kb(blocks), path);
611 } else if (mflg) {
612 (void) printf(FORMAT2, (long long)mb(blocks), path);
613 } else {
614 (void) printf(FORMAT2, (long long)blocks, path);
618 static void
619 exitdu(int exitcode)
621 free(base);
622 free(name);
623 exit(exitcode);