blob: c8cc9b569a30e38193cbf8dc6cc1282fee7c440c [file] [log] [blame]
/*
* Copyright (c) 1980, 1990 Regents of the University of California. All
* rights reserved.
*
* This code is derived from software contributed to Berkeley by Robert Elz at
* The University of Melbourne.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. 3. All advertising
* materials mentioning features or use of this software must display the
* following acknowledgement: This product includes software developed by the
* University of California, Berkeley and its contributors. 4. Neither the
* name of the University nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
/*
* Disk quota reporting program.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#ifdef RPC
#include <rpc/rpc.h>
#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_SMARTSIZE 16
#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;
char *progname;
static void usage(void)
{
errstr( "%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 [-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 display numbers in human friendly units (MB, GB...)\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 filesystems\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 (flags & FL_SMARTSIZE)
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;
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, !!(flags & FL_QUIETREFUSE));
over = 0;
for (q = qlist; q; q = q->dq_next) {
bover = iover = 0;
if (!(flags & FL_VERBOSE) && !q->dq_dqb.dqb_isoftlimit && !q->dq_dqb.dqb_ihardlimit
&& !q->dq_dqb.dqb_bsoftlimit && !q->dq_dqb.dqb_bhardlimit)
continue;
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], !!(flags & FL_SMARTSIZE));
space2str(q->dq_dqb.dqb_bsoftlimit, numbuf[1], !!(flags & FL_SMARTSIZE));
space2str(q->dq_dqb.dqb_bhardlimit, numbuf[2], !!(flags & FL_SMARTSIZE));
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], !!(flags & FL_SMARTSIZE));
number2str(q->dq_dqb.dqb_isoftlimit, numbuf[1], !!(flags & FL_SMARTSIZE));
number2str(q->dq_dqb.dqb_ihardlimit, numbuf[2], !!(flags & FL_SMARTSIZE));
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, _("none"));
freeprivs(qlist);
dispose_handle_list(handles);
return over > 0 ? 1 : 0;
}
int main(int argc, char **argv)
{
int ngroups;
gid_t gidset[NGROUPS_MAX], *gidsetp;
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", 0, 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 },
{ NULL, 0, NULL, 0 }
};
gettexton();
progname = basename(argv[0]);
flags |= FL_SHOW_DEVICE;
while ((ret = getopt_long(argc, argv, "hguqvsPVliQF: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':
flags |= FL_SMARTSIZE;
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 '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 && flags & (FL_LOCALONLY | FL_NOAUTOFS))
errstr(_("Warning: Ignoring -%c when filesystem list specified.\n"), flags & FL_LOCALONLY ? 'l' : 'i');
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), 0, NULL);
else if (flags & FL_GROUP)
for (; argc > 0; argc--, argv++)
ret |= showquotas(GRPQUOTA, group2gid(*argv, !!(flags & FL_NUMNAMES), NULL), 0, NULL);
else if (flags & FL_PROJECT)
for (; argc > 0; argc--, argv++)
ret |= showquotas(PRJQUOTA, project2pid(*argv, !!(flags & FL_NUMNAMES), NULL), 0, NULL);
return ret;
}