/* * Basic utility for reporting quotas */ #include "config.h" /* * Disk quota reporting program. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RPC #include #include "rquota.h" #endif #include "quota.h" #include "quotaops.h" #include "quotasys.h" #include "pot.h" #include "common.h" #define FL_QUIET 1 #define FL_VERBOSE 2 #define FL_USER 4 #define FL_GROUP 8 #define FL_LOCALONLY 32 #define FL_QUIETREFUSE 64 #define FL_NOAUTOFS 128 #define FL_NOWRAP 256 #define FL_FSLIST 512 #define FL_NUMNAMES 1024 #define FL_NFSALL 2048 #define FL_RAWGRACE 4096 #define FL_NO_MIXED_PATHS 8192 #define FL_SHOW_MNTPOINT 16384 #define FL_SHOW_DEVICE 32768 #define FL_PROJECT 65536 static int flags, fmt = -1; static enum s2s_unit spaceunit = S2S_NONE, inodeunit = S2S_NONE; char *progname; static void usage(void) { errstr( "%s%s%s%s%s%s", _("Usage: quota [-guPqvswim] [-l | [-Q | -A]] [-F quotaformat]\n"), _("\tquota [-qvswim] [-l | [-Q | -A]] [-F quotaformat] -u username ...\n"), _("\tquota [-qvswim] [-l | [-Q | -A]] [-F quotaformat] -g groupname ...\n"), _("\tquota [-qvswim] [-l | [-Q | -A]] [-F quotaformat] -P projectname ...\n"), _("\tquota [-qvswugPQm] [-F quotaformat] -f filesystem ...\n"), _("\n\ -u, --user display quota for user\n\ -g, --group display quota for group\n\ -P, --project display quota for project\n\ -q, --quiet print more terse message\n\ -v, --verbose print more verbose message\n\ -s, --human-readable[=units] display numbers in human friendly units (MB, GB,\n\ ...). Units can be also specified explicitely by\n\ an optional argument in format [kgt],[kgt] where\n\ the first character specifies space units and the\n\ second character specifies inode units\n\ --always-resolve always try to translate name to id, even if it is\n\ composed of only digits\n\ -w, --no-wrap do not wrap long lines\n\ -p, --raw-grace print grace time in seconds since epoch\n\ -l, --local-only do not query NFS filesystems\n\ -Q, --quiet-refuse do not print error message when NFS server does\n\ not respond\n\ -i, --no-autofs do not query autofs mountpoints\n\ -F, --format=formatname display quota of a specific format\n\ -f, --filesystem-list display quota information only for given\n\ filesystems\n\ --filesystem=path display quota information only for given\n\ filesystem, remaining command line arguments\n\ are still treated as user/group/project names\n\ -A, --all-nfs display quota for all NFS mountpoints\n\ -m, --no-mixed-pathnames trim leading slashes from NFSv4 mountpoints\n\ --show-mntpoint show mount point of the file system in output\n\ --hide-device do not show file system device in output\n\ -h, --help display this help message and exit\n\ -V, --version display version information and exit\n\n")); fprintf(stderr, _("Bugs to: %s\n"), PACKAGE_BUGREPORT); exit(1); } static void heading(int type, qid_t id, char *name, char *tag) { char *spacehdr; if (spaceunit != S2S_NONE) spacehdr = _("space"); else spacehdr = _("blocks"); printf(_("Disk quotas for %s %s (%cid %u): %s\n"), _(type2name(type)), name, *type2name(type), (uint) id, tag); if (!(flags & FL_QUIET) && !tag[0]) { printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n", _("Filesystem"), spacehdr, _("quota"), _("limit"), _("grace"), _("files"), _("quota"), _("limit"), _("grace")); } } static void print_fs_location(struct dquot *q) { struct quota_handle *h = q->dq_h; if (flags & FL_QUIET) { if (flags & FL_SHOW_DEVICE) printf(" %s", h->qh_quotadev); if (flags & FL_SHOW_MNTPOINT) printf(" %s", h->qh_dir); putchar('\n'); } else { int wrap = 0; if (flags & FL_SHOW_DEVICE && flags & FL_SHOW_MNTPOINT && !(flags & FL_NOWRAP)) wrap = 1; else if (flags & FL_SHOW_DEVICE && strlen(h->qh_quotadev) > 15 && !(flags & FL_NOWRAP)) wrap = 1; else if (flags & FL_SHOW_MNTPOINT && strlen(h->qh_dir) > 15 && !(flags & FL_NOWRAP)) wrap = 1; if (flags & FL_SHOW_DEVICE) { if (wrap || flags & FL_SHOW_MNTPOINT) printf("%s", h->qh_quotadev); else printf("%15s", h->qh_quotadev); } if (flags & FL_SHOW_MNTPOINT) { if (flags & FL_SHOW_DEVICE) putchar(' '); if (wrap || flags & FL_SHOW_DEVICE) printf("%s", h->qh_dir); else printf("%15s", h->qh_dir); } if (wrap) printf("\n%15s", ""); } } static int showquotas(int type, qid_t id, int mntcnt, char **mnt) { struct dquot *qlist, *q; char *msgi, *msgb; char timebuf[MAXTIMELEN]; char name[MAXNAMELEN]; struct quota_handle **handles; int lines = 0, bover, iover, over, unlimited; time_t now; time(&now); id2name(id, type, name); handles = create_handle_list(mntcnt, mnt, type, fmt, IOI_READONLY | ((flags & FL_NO_MIXED_PATHS) ? 0 : IOI_NFS_MIXED_PATHS), ((flags & FL_NOAUTOFS) ? MS_NO_AUTOFS : 0) | ((flags & FL_LOCALONLY) ? MS_LOCALONLY : 0) | ((flags & FL_NFSALL) ? MS_NFS_ALL : 0)); qlist = getprivs(id, handles, !mntcnt || (flags & FL_QUIETREFUSE)); if (!qlist) { over = 1; goto out_handles; } over = 0; unlimited = 1; for (q = qlist; q; q = q->dq_next) { bover = iover = 0; if (!q->dq_dqb.dqb_isoftlimit && !q->dq_dqb.dqb_ihardlimit && !q->dq_dqb.dqb_bsoftlimit && !q->dq_dqb.dqb_bhardlimit) { if (!(flags & FL_VERBOSE)) continue; } else { unlimited = 0; } msgi = NULL; if (q->dq_dqb.dqb_ihardlimit && q->dq_dqb.dqb_curinodes >= q->dq_dqb.dqb_ihardlimit) { msgi = _("File limit reached on"); iover = 1; } else if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes > q->dq_dqb.dqb_isoftlimit) { if (q->dq_dqb.dqb_itime > now) { msgi = _("In file grace period on"); iover = 2; } else { msgi = _("Over file quota on"); iover = 3; } } msgb = NULL; if (q->dq_dqb.dqb_bhardlimit && toqb(q->dq_dqb.dqb_curspace) >= q->dq_dqb.dqb_bhardlimit) { msgb = _("Block limit reached on"); bover = 1; } else if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) > q->dq_dqb.dqb_bsoftlimit) { if (q->dq_dqb.dqb_btime > now) { msgb = _("In block grace period on"); bover = 2; } else { msgb = _("Over block quota on"); bover = 3; } } over |= bover | iover; if (flags & FL_QUIET) { if ((msgi || msgb) && !lines++) heading(type, id, name, ""); if (msgi) { printf("\t%s", msgi); print_fs_location(q); } if (msgb) { printf("\t%s", msgb); print_fs_location(q); } continue; } if ((flags & FL_VERBOSE) || q->dq_dqb.dqb_curspace || q->dq_dqb.dqb_curinodes) { char numbuf[3][MAXNUMLEN]; if (!lines++) heading(type, id, name, ""); print_fs_location(q); if (!(flags & FL_RAWGRACE)) { if (bover) difftime2str(q->dq_dqb.dqb_btime, timebuf); else timebuf[0] = 0; } else { if (bover) sprintf(timebuf, "%llu", (long long unsigned int)q->dq_dqb.dqb_btime); else strcpy(timebuf, "0"); } space2str(toqb(q->dq_dqb.dqb_curspace), numbuf[0], spaceunit); space2str(q->dq_dqb.dqb_bsoftlimit, numbuf[1], spaceunit); space2str(q->dq_dqb.dqb_bhardlimit, numbuf[2], spaceunit); printf(" %7s%c %6s %7s %7s", numbuf[0], bover ? '*' : ' ', numbuf[1], numbuf[2], timebuf); if (!(flags & FL_RAWGRACE)) { if (iover) difftime2str(q->dq_dqb.dqb_itime, timebuf); else timebuf[0] = 0; } else { if (iover) sprintf(timebuf, "%llu", (long long unsigned int)q->dq_dqb.dqb_itime); else strcpy(timebuf, "0"); } number2str(q->dq_dqb.dqb_curinodes, numbuf[0], inodeunit); number2str(q->dq_dqb.dqb_isoftlimit, numbuf[1], inodeunit); number2str(q->dq_dqb.dqb_ihardlimit, numbuf[2], inodeunit); printf(" %7s%c %6s %7s %7s\n", numbuf[0], iover ? '*' : ' ', numbuf[1], numbuf[2], timebuf); continue; } } if (!(flags & FL_QUIET) && !lines && qlist) heading(type, id, name, unlimited ? _("none") : _("no limited resources used")); freeprivs(qlist); out_handles: dispose_handle_list(handles); return over > 0 ? 1 : 0; } int main(int argc, char **argv) { int ngroups; gid_t gidset[NGROUPS_MAX], *gidsetp; char **fsnames = NULL; int fscount = 0; int i, ret, type = 0; struct option long_opts[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "user", 0, NULL, 'u' }, { "group", 0, NULL, 'g' }, { "project", 0, NULL, 'P' }, { "quiet", 0, NULL, 'q' }, { "verbose", 0, NULL, 'v' }, { "human-readable", 2, NULL, 's' }, { "always-resolve", 0, NULL, 256 }, { "raw-grace", 0, NULL, 'p' }, { "local-only", 0, NULL, 'l' }, { "no-autofs", 0, NULL, 'i' }, { "quiet-refuse", 0, NULL, 'Q' }, { "format", 1, NULL, 'F' }, { "no-wrap", 0, NULL, 'w' }, { "filesystem-list", 0, NULL, 'f' }, { "all-nfs", 0, NULL, 'A' }, { "no-mixed-pathnames", 0, NULL, 'm' }, { "show-mntpoint", 0, NULL, 257 }, { "hide-device", 0, NULL, 258 }, { "filesystem", 1, NULL, 259 }, { NULL, 0, NULL, 0 } }; gettexton(); progname = basename(argv[0]); flags |= FL_SHOW_DEVICE; while ((ret = getopt_long(argc, argv, "hguqvs::PVliQF:wfApm", long_opts, NULL)) != -1) { switch (ret) { case 'g': flags |= FL_GROUP; break; case 'u': flags |= FL_USER; break; case 'P': flags |= FL_PROJECT; break; case 'q': flags |= FL_QUIET; break; case 'v': flags |= FL_VERBOSE; break; case 'F': if ((fmt = name2fmt(optarg)) == QF_ERROR) /* Error? */ exit(1); break; case 's': inodeunit = spaceunit = S2S_AUTO; if (optarg) { if (unitopt2unit(optarg, &spaceunit, &inodeunit) < 0) die(1, _("Bad output format units for human readable output: %s\n"), optarg); } break; case 'p': flags |= FL_RAWGRACE; break; case 256: flags |= FL_NUMNAMES; break; case 'l': flags |= FL_LOCALONLY; break; case 'Q': flags |= FL_QUIETREFUSE; break; case 'i': flags |= FL_NOAUTOFS; break; case 'w': flags |= FL_NOWRAP; break; case 'f': flags |= FL_FSLIST; break; case 'A': flags |= FL_NFSALL; break; case 'm': flags |= FL_NO_MIXED_PATHS; break; case 257: flags |= FL_SHOW_MNTPOINT; break; case 258: flags &= ~FL_SHOW_DEVICE; break; case 259: fscount++; fsnames = realloc(fsnames, fscount * sizeof(char *)); if (!fsnames) die(1, _("Not enough memory for filesystem names")); fsnames[fscount - 1] = optarg; break; case 'V': version(); exit(0); case 'h': default: usage(); } } argc -= optind; argv += optind; if (!(flags & FL_USER) && !(flags & FL_GROUP) && !(flags & FL_PROJECT)) flags |= FL_USER; if (((flags & FL_FSLIST) || fscount) && flags & (FL_LOCALONLY | FL_NOAUTOFS)) errstr(_("Warning: Ignoring -%c when filesystem list specified.\n"), flags & FL_LOCALONLY ? 'l' : 'i'); if (fscount && flags & FL_FSLIST) die(1, "Cannot use both --filesystem and -f"); init_kernel_interface(); ret = 0; if (argc == 0 || flags & FL_FSLIST) { if (flags & FL_FSLIST && argc == 0) die(1, _("No filesystem specified.\n")); if (flags & FL_USER) ret |= showquotas(USRQUOTA, getuid(), argc, argv); if (flags & FL_GROUP) { ngroups = sysconf(_SC_NGROUPS_MAX); if (ngroups > NGROUPS_MAX) { gidsetp = malloc(ngroups * sizeof(gid_t)); if (!gidsetp) die(1, _("Gid set allocation (%d): %s\n"), ngroups, strerror(errno)); } else { gidsetp = &gidset[0]; } ngroups = getgroups(ngroups, gidsetp); if (ngroups < 0) die(1, _("getgroups(): %s\n"), strerror(errno)); for (i = 0; i < ngroups; i++) ret |= showquotas(GRPQUOTA, gidsetp[i], argc, argv); } if (flags & FL_PROJECT) die(1, _("Project reports not supported without project name\n")); exit(ret); } if (flags & FL_USER) type++; if (flags & FL_GROUP) type++; if (flags & FL_PROJECT) type++; if (type > 1) usage(); if (flags & FL_USER) for (; argc > 0; argc--, argv++) ret |= showquotas(USRQUOTA, user2uid(*argv, !!(flags & FL_NUMNAMES), NULL), fscount, fsnames); else if (flags & FL_GROUP) for (; argc > 0; argc--, argv++) ret |= showquotas(GRPQUOTA, group2gid(*argv, !!(flags & FL_NUMNAMES), NULL), fscount, fsnames); else if (flags & FL_PROJECT) for (; argc > 0; argc--, argv++) ret |= showquotas(PRJQUOTA, project2pid(*argv, !!(flags & FL_NUMNAMES), NULL), fscount, fsnames); return ret; }