blob: 8e3316ed4298d478f3e48fa8d429dd761d586b43 [file] [log] [blame]
/*
* Copyright (c) 2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <xfs/command.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <utmp.h>
#include "init.h"
#include "quota.h"
static cmdinfo_t dump_cmd;
static cmdinfo_t report_cmd;
static void
dump_help(void)
{
dump_cmd.args = _("[-gpu] [-f file]");
dump_cmd.oneline = _("dump quota information for backup utilities");
printf(_(
"\n"
" create a backup file which contains quota limits information\n"
" -g -- dump out group quota limits\n"
" -p -- dump out project quota limits\n"
" -u -- dump out user quota limits (default)\n"
" -f -- write the dump out to the specified file\n"
"\n"));
}
static void
report_help(void)
{
report_cmd.args = _("[-bir] [-gpu] [-ahntLNU] [-f file]");
report_cmd.oneline = _("report filesystem quota information");
printf(_(
"\n"
" report used space and inodes, and quota limits, for a filesystem\n"
" Example:\n"
" 'report -igh'\n"
" (reports inode usage for all groups, in an easy-to-read format)\n"
" This command is the equivalent of the traditional repquota command, which\n"
" prints a summary of the disk usage and quotas for the current filesystem,\n"
" or all filesystems.\n"
" -a -- report for all mounted filesystems with quota enabled\n"
" -h -- report in a human-readable format\n"
" -n -- skip identifier-to-name translations, just report IDs\n"
" -N -- suppress the header from the output\n"
" -t -- terse output format, hides rows which are all zero\n"
" -L -- lower ID bound to report on\n"
" -U -- upper ID bound to report on\n"
" -g -- report group usage and quota information\n"
" -p -- report project usage and quota information\n"
" -u -- report user usage and quota information\n"
" -b -- report blocks-used information only\n"
" -i -- report inodes-used information only\n"
" -r -- report realtime-blocks-used information only\n"
"\n"));
}
static void
dump_file(
FILE *fp,
uint id,
uint type,
char *dev)
{
fs_disk_quota_t d;
if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) {
if (errno != ENOENT && errno != ENOSYS && errno != ESRCH)
perror("XFS_GETQUOTA");
return;
}
if (!d.d_blk_softlimit && !d.d_blk_hardlimit &&
!d.d_ino_softlimit && !d.d_ino_hardlimit &&
!d.d_rtb_softlimit && !d.d_rtb_hardlimit)
return;
fprintf(fp, "fs = %s\n", dev);
/* this branch is for backward compatibility reasons */
if (d.d_rtb_softlimit || d.d_rtb_hardlimit)
fprintf(fp, "%-10d %7llu %7llu %7llu %7llu %7llu %7llu\n", id,
(unsigned long long)d.d_blk_softlimit,
(unsigned long long)d.d_blk_hardlimit,
(unsigned long long)d.d_ino_softlimit,
(unsigned long long)d.d_ino_hardlimit,
(unsigned long long)d.d_rtb_softlimit,
(unsigned long long)d.d_rtb_hardlimit);
else
fprintf(fp, "%-10d %7llu %7llu %7llu %7llu\n", id,
(unsigned long long)d.d_blk_softlimit,
(unsigned long long)d.d_blk_hardlimit,
(unsigned long long)d.d_ino_softlimit,
(unsigned long long)d.d_ino_hardlimit);
}
static void
dump_limits_any_type(
FILE *fp,
uint type,
char *dir,
uint lower,
uint upper)
{
fs_path_t *mount;
uint id;
if ((mount = fs_table_lookup(dir, FS_MOUNT_POINT)) == NULL) {
exitcode = 1;
fprintf(stderr, "%s: cannot find mount point %s\n",
progname, dir);
return;
}
if (upper) {
for (id = lower; id <= upper; id++)
dump_file(fp, id, type, mount->fs_name);
return;
}
switch (type) {
case XFS_GROUP_QUOTA: {
struct group *g;
setgrent();
while ((g = getgrent()) != NULL)
dump_file(fp, g->gr_gid, type, mount->fs_name);
endgrent();
break;
}
case XFS_PROJ_QUOTA: {
struct fs_project *p;
setprent();
while ((p = getprent()) != NULL)
dump_file(fp, p->pr_prid, type, mount->fs_name);
endprent();
break;
}
case XFS_USER_QUOTA: {
struct passwd *u;
setpwent();
while ((u = getpwent()) != NULL)
dump_file(fp, u->pw_uid, type, mount->fs_name);
endpwent();
break;
}
}
}
static int
dump_f(
int argc,
char **argv)
{
FILE *fp;
char *fname = NULL;
uint lower = 0, upper = 0;
int c, type = XFS_USER_QUOTA;
while ((c = getopt(argc, argv, "f:gpuL:U:")) != EOF) {
switch(c) {
case 'f':
fname = optarg;
break;
case 'g':
type = XFS_GROUP_QUOTA;
break;
case 'p':
type = XFS_PROJ_QUOTA;
break;
case 'u':
type = XFS_USER_QUOTA;
break;
case 'L':
lower = (uint)atoi(optarg);
break;
case 'U':
upper = (uint)atoi(optarg);
break;
default:
return command_usage(&dump_cmd);
}
}
if (argc != optind)
return command_usage(&dump_cmd);
if ((fp = fopen_write_secure(fname)) == NULL)
return 0;
dump_limits_any_type(fp, type, fs_path->fs_dir, lower, upper);
if (fname)
fclose(fp);
return 0;
}
static void
report_header(
FILE *fp,
uint form,
uint type,
fs_path_t *mount,
int flags)
{
char *typename = type_to_string(type);
char scratch[64];
uint i, count;
if (flags & NO_HEADER_FLAG)
return;
/* line 1 */
fprintf(fp, _("%s quota on %s (%s)\n"),
typename, mount->fs_dir, mount->fs_name);
/* line 2 */
for (i = 0; i < 10; i++)
fputc(' ', fp);
if (form & XFS_BLOCK_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
"%13c %s %13c" : "%20c %s %20c",
' ', form_to_string(XFS_BLOCK_QUOTA), ' ');
if (form & XFS_INODE_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
"%13c %s %13c" : "%20c %s %20c",
' ', form_to_string(XFS_INODE_QUOTA), ' ');
if (form & XFS_RTBLOCK_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
"%9c %s %9c" : "%15c %s %15c",
' ', form_to_string(XFS_RTBLOCK_QUOTA), ' ');
fputc('\n', fp);
/* line 3 */
snprintf(scratch, sizeof(scratch), "%s ID", typename);
fprintf(fp, "%-10s ", scratch);
if (form & XFS_BLOCK_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
_(" Used Soft Hard Warn/Grace ") :
_(" Used Soft Hard Warn/Grace "));
if (form & XFS_INODE_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
_(" Used Soft Hard Warn/Grace ") :
_(" Used Soft Hard Warn/ Grace "));
if (form & XFS_RTBLOCK_QUOTA)
fprintf(fp, (flags & HUMAN_FLAG) ?
_(" Used Soft Hard Warn/Grace ") :
_(" Used Soft Hard Warn/Grace "));
fputc('\n', fp);
/* line 4 */
for (i = 0; i < 10; i++)
fputc('-', fp);
fputc(' ', fp);
count = (flags & HUMAN_FLAG) ? 33 : 50;
if (form & XFS_BLOCK_QUOTA) {
for (i = 0; i < count; i++)
fputc('-', fp);
fputc(' ', fp);
}
if (form & XFS_INODE_QUOTA) {
for (i = 0; i < count; i++)
fputc('-', fp);
fputc(' ', fp);
}
if (form & XFS_RTBLOCK_QUOTA) {
for (i = 0; i < count; i++)
fputc('-', fp);
fputc(' ', fp);
}
fputc('\n', fp);
}
static int
report_mount(
FILE *fp,
__uint32_t id,
char *name,
uint form,
uint type,
fs_path_t *mount,
uint flags)
{
fs_disk_quota_t d;
char *dev = mount->fs_name;
char c[8], h[8], s[8];
uint qflags;
int count;
if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) {
if (errno != ENOENT && errno != ENOSYS && errno != ESRCH)
perror("XFS_GETQUOTA");
return 0;
}
if (flags & TERSE_FLAG) {
count = 0;
if ((form & XFS_BLOCK_QUOTA) && d.d_bcount)
count++;
if ((form & XFS_INODE_QUOTA) && d.d_icount)
count++;
if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount)
count++;
if (!count)
return 0;
}
if (!(flags & NO_HEADER_FLAG))
report_header(fp, form, type, mount, flags);
fprintf(fp, "%-10s", name);
if (form & XFS_BLOCK_QUOTA) {
qflags = (flags & HUMAN_FLAG);
if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
qflags |= LIMIT_FLAG;
if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
qflags |= QUOTA_FLAG;
if (flags & HUMAN_FLAG)
fprintf(fp, " %6s %6s %6s %02d %8s",
bbs_to_string(d.d_bcount, c, sizeof(c)),
bbs_to_string(d.d_blk_softlimit, s, sizeof(s)),
bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)),
d.d_bwarns,
time_to_string(d.d_btimer, qflags));
else
fprintf(fp, " %10llu %10llu %10llu %02d %9s",
(unsigned long long)d.d_bcount >> 1,
(unsigned long long)d.d_blk_softlimit >> 1,
(unsigned long long)d.d_blk_hardlimit >> 1,
d.d_bwarns,
time_to_string(d.d_btimer, qflags));
}
if (form & XFS_INODE_QUOTA) {
qflags = (flags & HUMAN_FLAG);
if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
qflags |= LIMIT_FLAG;
if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
qflags |= QUOTA_FLAG;
if (flags & HUMAN_FLAG)
fprintf(fp, " %6s %6s %6s %02d %8s",
num_to_string(d.d_icount, c, sizeof(c)),
num_to_string(d.d_ino_softlimit, s, sizeof(s)),
num_to_string(d.d_ino_hardlimit, h, sizeof(h)),
d.d_iwarns,
time_to_string(d.d_itimer, qflags));
else
fprintf(fp, " %10llu %10llu %10llu %02d %9s",
(unsigned long long)d.d_icount,
(unsigned long long)d.d_ino_softlimit,
(unsigned long long)d.d_ino_hardlimit,
d.d_iwarns,
time_to_string(d.d_itimer, qflags));
}
if (form & XFS_RTBLOCK_QUOTA) {
qflags = (flags & HUMAN_FLAG);
if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
qflags |= LIMIT_FLAG;
if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
qflags |= QUOTA_FLAG;
if (flags & HUMAN_FLAG)
fprintf(fp, " %6s %6s %6s %02d %8s",
bbs_to_string(d.d_rtbcount, c, sizeof(c)),
bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)),
bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)),
d.d_rtbwarns,
time_to_string(d.d_rtbtimer, qflags));
else
fprintf(fp, " %10llu %10llu %10llu %02d %9s",
(unsigned long long)d.d_rtbcount >> 1,
(unsigned long long)d.d_rtb_softlimit >> 1,
(unsigned long long)d.d_rtb_hardlimit >> 1,
d.d_rtbwarns,
time_to_string(d.d_rtbtimer, qflags));
}
fputc('\n', fp);
return 1;
}
static void
report_user_mount(
FILE *fp,
uint form,
fs_path_t *mount,
uint lower,
uint upper,
uint flags)
{
struct passwd *u;
char n[NMAX];
uint id;
if (upper) { /* identifier range specified */
for (id = lower; id <= upper; id++) {
snprintf(n, sizeof(n)-1, "#%u", id);
if (report_mount(fp, id, n,
form, XFS_USER_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
} else {
setpwent();
while ((u = getpwent()) != NULL) {
if (flags & NO_LOOKUP_FLAG)
snprintf(n, sizeof(n)-1, "#%u", u->pw_uid);
else
strncpy(n, u->pw_name, sizeof(n)-1);
if (report_mount(fp, u->pw_uid, n,
form, XFS_USER_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
endpwent();
}
if (flags & NO_HEADER_FLAG)
fputc('\n', fp);
}
static void
report_group_mount(
FILE *fp,
uint form,
fs_path_t *mount,
uint lower,
uint upper,
uint flags)
{
struct group *g;
char n[NMAX];
uint id;
if (upper) { /* identifier range specified */
for (id = lower; id <= upper; id++) {
snprintf(n, sizeof(n)-1, "#%u", id);
if (report_mount(fp, id, n,
form, XFS_GROUP_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
} else {
setgrent();
while ((g = getgrent()) != NULL) {
if (flags & NO_LOOKUP_FLAG)
snprintf(n, sizeof(n)-1, "#%u", g->gr_gid);
else
strncpy(n, g->gr_name, sizeof(n)-1);
if (report_mount(fp, g->gr_gid, n,
form, XFS_GROUP_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
}
if (flags & NO_HEADER_FLAG)
fputc('\n', fp);
endgrent();
}
static void
report_project_mount(
FILE *fp,
uint form,
fs_path_t *mount,
uint lower,
uint upper,
uint flags)
{
fs_project_t *p;
char n[NMAX];
uint id;
if (upper) { /* identifier range specified */
for (id = lower; id <= upper; id++) {
snprintf(n, sizeof(n)-1, "#%u", id);
if (report_mount(fp, id, n,
form, XFS_PROJ_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
} else {
setprent();
while ((p = getprent()) != NULL) {
if (flags & NO_LOOKUP_FLAG)
snprintf(n, sizeof(n)-1, "#%u",
(unsigned int)p->pr_prid);
else
strncpy(n, p->pr_name, sizeof(n)-1);
if (report_mount(fp, p->pr_prid, n,
form, XFS_PROJ_QUOTA, mount, flags))
flags |= NO_HEADER_FLAG;
}
endprent();
}
if (flags & NO_HEADER_FLAG)
fputc('\n', fp);
}
static void
report_any_type(
FILE *fp,
uint form,
uint type,
char *dir,
uint lower,
uint upper,
uint flags)
{
fs_cursor_t cursor;
fs_path_t *mount;
if (type & XFS_USER_QUOTA) {
fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
while ((mount = fs_cursor_next_entry(&cursor))) {
if (xfsquotactl(XFS_QSYNC, mount->fs_name,
XFS_USER_QUOTA, 0, NULL) < 0
&& errno != ENOENT && errno != ENOSYS)
perror("XFS_QSYNC user quota");
report_user_mount(fp, form, mount,
lower, upper, flags);
}
}
if (type & XFS_GROUP_QUOTA) {
fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
while ((mount = fs_cursor_next_entry(&cursor))) {
if (xfsquotactl(XFS_QSYNC, mount->fs_name,
XFS_GROUP_QUOTA, 0, NULL) < 0
&& errno != ENOENT && errno != ENOSYS)
perror("XFS_QSYNC group quota");
report_group_mount(fp, form, mount,
lower, upper, flags);
}
}
if (type & XFS_PROJ_QUOTA) {
fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
while ((mount = fs_cursor_next_entry(&cursor))) {
if (xfsquotactl(XFS_QSYNC, mount->fs_name,
XFS_PROJ_QUOTA, 0, NULL) < 0
&& errno != ENOENT && errno != ENOSYS)
perror("XFS_QSYNC proj quota");
report_project_mount(fp, form, mount,
lower, upper, flags);
}
}
}
static int
report_f(
int argc,
char **argv)
{
FILE *fp = NULL;
char *fname = NULL;
uint lower = 0, upper = 0;
int c, flags = 0, type = 0, form = 0;
while ((c = getopt(argc, argv, "abf:ghiL:NnprtuU:")) != EOF) {
switch (c) {
case 'f':
fname = optarg;
break;
case 'b':
form |= XFS_BLOCK_QUOTA;
break;
case 'i':
form |= XFS_INODE_QUOTA;
break;
case 'r':
form |= XFS_RTBLOCK_QUOTA;
break;
case 'g':
type |= XFS_GROUP_QUOTA;
break;
case 'p':
type |= XFS_PROJ_QUOTA;
break;
case 'u':
type |= XFS_USER_QUOTA;
break;
case 'a':
flags |= ALL_MOUNTS_FLAG;
break;
case 'h':
flags |= HUMAN_FLAG;
break;
case 'n':
flags |= NO_LOOKUP_FLAG;
break;
case 'N':
flags |= NO_HEADER_FLAG;
break;
case 't':
flags |= TERSE_FLAG;
break;
case 'L':
lower = (uint)atoi(optarg);
break;
case 'U':
upper = (uint)atoi(optarg);
break;
default:
return command_usage(&report_cmd);
}
}
if (!form)
form = XFS_BLOCK_QUOTA;
if (!type)
type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
if ((fp = fopen_write_secure(fname)) == NULL)
return 0;
if (argc == optind) {
if (flags & ALL_MOUNTS_FLAG)
report_any_type(fp, form, type, NULL,
lower, upper, flags);
else if (fs_path && (fs_path->fs_flags & FS_MOUNT_POINT))
report_any_type(fp, form, type, fs_path->fs_dir,
lower, upper, flags);
} else while (argc > optind) {
report_any_type(fp, form, type, argv[optind++],
lower, upper, flags);
}
if (fname)
fclose(fp);
return 0;
}
void
report_init(void)
{
dump_cmd.name = "dump";
dump_cmd.cfunc = dump_f;
dump_cmd.argmin = 0;
dump_cmd.argmax = -1;
dump_cmd.args = _("[-gpu] [-f file]");
dump_cmd.oneline = _("dump quota information for backup utilities");
dump_cmd.help = dump_help;
report_cmd.name = "report";
report_cmd.altname = "repquota";
report_cmd.cfunc = report_f;
report_cmd.argmin = 0;
report_cmd.argmax = -1;
report_cmd.flags = CMD_FLAG_GLOBAL;
report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]");
report_cmd.oneline = _("report filesystem quota information");
report_cmd.help = report_help;
if (expert) {
add_command(&dump_cmd);
add_command(&report_cmd);
}
}