File: [Development] / xfs-cmds / xfsprogs / quota / edit.c (download)
Revision 1.1, Tue Apr 19 15:00:30 2005 UTC (12 years, 6 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
Initial version of xfs quota utility. Knows how to do user/group/project quota, provides missing freespace reporting for realtime on Linux, and for project quotas. Allows all current and future XFS quota functionality to be exposed without complicating the standard quota tools.
Merge of master-melb:xfs-cmds:22274a by kenmcd.
|
/*
* 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 version 2 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.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
*/
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <xfs/input.h>
#include <xfs/command.h>
#include "init.h"
#include "quota.h"
static cmdinfo_t limit_cmd;
static cmdinfo_t restore_cmd;
static cmdinfo_t timer_cmd;
static cmdinfo_t warn_cmd;
static void
limit_help(void)
{
printf(_(
"\n"
" modify quota limits for the specified user\n"
"\n"
" Example:\n"
" 'limit bsoft=100m bhard=110m tanya\n"
"\n"
" Changes the soft and/or hard block limits, inode limits and/or realtime\n"
" block limits that are currently being used for the specified user, group,\n"
" or project. The filesystem identified by the current path is modified.\n"
" -d -- set the default values, used the first time a file is created\n"
" -g -- modify group quota limits\n"
" -p -- modify project quota limits\n"
" -u -- modify user quota limits\n"
" The block limit values can be specified with a units suffix - accepted\n"
" units are: k (kilobytes), m (megabytes), g (gigabytes), and t (terabytes).\n"
" The user/group/project can be specified either by name or by number.\n"
"\n"));
}
static void
timer_help(void)
{
printf(_(
"\n"
" modify quota enforcement timeout for the specified user\n"
"\n"
" Example:\n"
" 'timer -i 3days sam'\n"
" (soft inode limit timer for user 'sam' is changed to 3 days)\n"
"\n"
" Changes the timeout value associated with the block limits, inode limits\n"
" and/or realtime block limits for the specified user, group, or project.\n"
" As soon as a user consumes the amount of space or number of inodes set as\n"
" the soft limit, a timer is started. If the timer expires and the user is\n"
" still over the soft limit, the soft limit is enforced as the hard limit.\n"
" The default timeout is 7 days.\n"
" -d -- set the default values, used the first time a file is created\n"
" -g -- modify group quota timer\n"
" -p -- modify project quota timer\n"
" -u -- modify user quota timer\n"
" -b -- modify the blocks-used timer\n"
" -i -- modify the inodes-used timer\n"
" -r -- modify the blocks-used timer for the (optional) realtime subvolume\n"
" The timeout value is specified as a number of seconds, by default.\n"
" However, a suffix may be used to alternatively specify minutes (m),\n"
" hours (h), days (d), or weeks (w) - either the full word or the first\n"
" letter of the word can be used.\n"
" The user/group/project can be specified either by name or by number.\n"
"\n"));
}
static void
warn_help(void)
{
printf(_(
"\n"
" modify the number of quota warnings sent to the specified user\n"
"\n"
" Example:\n"
" 'warn 2 jimmy'\n"
" (tell the quota system that two warnings have been sent to user jimmy)\n"
"\n"
" Changes the warning count associated with the block limits, inode limits\n"
" and/or realtime block limits for the specified user, group, or project.\n"
" When a user has been warned the maximum number of times allowed, the soft\n"
" limit is enforced as the hard limit. It is intended as an alternative to\n"
" the timeout system, where the system administrator updates a count of the\n"
" number of warnings issued to people, and they are penalised if the warnings\n"
" are ignored.\n"
" -d -- set maximum warning count, which triggers soft limit enforcement\n"
" -g -- set group quota warning count\n"
" -p -- set project quota warning count\n"
" -u -- set user quota warning count\n"
" -b -- set the blocks-used warning count\n"
" -i -- set the inodes-used warning count\n"
" -r -- set the blocks-used warn count for the (optional) realtime subvolume\n"
" The user/group/project can be specified either by name or by number.\n"
"\n"));
}
static void
set_limits(
__uint32_t id,
uint type,
uint mask,
char *dev,
__uint64_t *bsoft,
__uint64_t *bhard,
__uint64_t *isoft,
__uint64_t *ihard,
__uint64_t *rtbsoft,
__uint64_t *rtbhard)
{
fs_disk_quota_t d;
memset(&d, 0, sizeof(d));
d.d_version = FS_DQUOT_VERSION;
d.d_id = id;
d.d_flags = type;
d.d_fieldmask = mask;
d.d_blk_hardlimit = *bhard;
d.d_blk_softlimit = *bsoft;
d.d_ino_hardlimit = *ihard;
d.d_ino_softlimit = *isoft;
d.d_rtb_hardlimit = *rtbhard;
d.d_rtb_softlimit = *rtbsoft;
if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0)
fprintf(stderr, _("%s: cannot set limits: %s\n"),
progname, strerror(errno));
}
static void
set_user_limits(
char *name,
uint type,
uint mask,
__uint64_t *bsoft,
__uint64_t *bhard,
__uint64_t *isoft,
__uint64_t *ihard,
__uint64_t *rtbsoft,
__uint64_t *rtbhard)
{
uid_t uid = uid_from_string(name);
if (uid == -1)
fprintf(stderr, _("%s: invalid user name: %s\n"),
progname, name);
else
set_limits(uid, type, mask, fs_path->fs_name,
bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
}
static void
set_group_limits(
char *name,
uint type,
uint mask,
__uint64_t *bsoft,
__uint64_t *bhard,
__uint64_t *isoft,
__uint64_t *ihard,
__uint64_t *rtbsoft,
__uint64_t *rtbhard)
{
gid_t gid = gid_from_string(name);
if (gid == -1)
fprintf(stderr, _("%s: invalid group name: %s\n"),
progname, name);
else
set_limits(gid, type, mask, fs_path->fs_name,
bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
}
static void
set_project_limits(
char *name,
uint type,
uint mask,
__uint64_t *bsoft,
__uint64_t *bhard,
__uint64_t *isoft,
__uint64_t *ihard,
__uint64_t *rtbsoft,
__uint64_t *rtbhard)
{
prid_t prid = prid_from_string(name);
if (prid == -1)
fprintf(stderr, _("%s: invalid project name: %s\n"),
progname, name);
else
set_limits(prid, type, mask, fs_path->fs_name,
bsoft, bhard, isoft, ihard, rtbsoft, rtbhard);
}
static int
extract(
char *string,
const char *prefix,
int length,
uint blocksize,
uint sectorsize,
__uint64_t *value)
{
__uint64_t v;
char *s = string;
if (strncmp(string, prefix, length) == 0) {
s = string + length + 1;
v = (__uint64_t)cvtnum(blocksize, sectorsize, s);
*value = v >> 9; /* syscalls use basic blocks */
return 1;
}
return 0;
}
static int
limit_f(
int argc,
char **argv)
{
char *name;
__uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
int c, type = 0, mask = 0, flags = 0;
uint bsize, ssize, endoptions;
init_cvtnum(&bsize, &ssize);
bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0;
while ((c = getopt(argc, argv, "dgpu")) != EOF) {
switch (c) {
case 'd':
flags |= DEFAULTS_FLAG;
break;
case 'g':
type = XFS_GROUP_QUOTA;
break;
case 'p':
type = XFS_PROJ_QUOTA;
break;
case 'u':
type = XFS_USER_QUOTA;
break;
default:
return command_usage(&limit_cmd);
}
}
/*
* In the usual case, we need at least 2 more arguments -
* one (or more) limits and a user name/id.
* For setting defaults (-d) we don't want a user name/id.
*/
if (flags & DEFAULTS_FLAG) {
if (argc < optind + 1)
return command_usage(&limit_cmd);
endoptions = 1;
} else if (argc < optind + 2) {
return command_usage(&limit_cmd);
} else {
endoptions = 2;
}
/*
* Extract limit values from remaining optional arguments.
*/
while (argc > optind + endoptions - 1) {
char *s = argv[optind++];
if (extract(s, "bsoft=", 5, bsize, ssize, &bsoft))
mask |= FS_DQ_BSOFT;
else if (extract(s, "bhard=", 5, bsize, ssize, &bhard))
mask |= FS_DQ_BHARD;
else if (extract(s, "isoft=", 5, bsize, ssize, &isoft))
mask |= FS_DQ_ISOFT;
else if (extract(s, "ihard=", 5, bsize, ssize, &ihard))
mask |= FS_DQ_IHARD;
else if (extract(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft))
mask |= FS_DQ_RTBSOFT;
else if (extract(s, "rtbhard=", 7, bsize, ssize, &rtbhard))
mask |= FS_DQ_RTBHARD;
else {
fprintf(stderr, _("%s: unrecognised argument %s\n"),
progname, s);
return 0;
}
}
if (!mask) {
fprintf(stderr, _("%s: cannot find any valid arguments\n"),
progname);
return 0;
}
name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
if (!type)
type = XFS_USER_QUOTA;
switch (type) {
case XFS_USER_QUOTA:
set_user_limits(name, type, mask,
&bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
break;
case XFS_GROUP_QUOTA:
set_group_limits(name, type, mask,
&bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
break;
case XFS_PROJ_QUOTA:
set_project_limits(name, type, mask,
&bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
break;
}
return 0;
}
/*
* Iterate through input file, restoring the limits.
* File format is as follows:
* fs = <device>
* <numeric id> bsoft bhard isoft ihard [rtbsoft rtbhard]
*/
static void
restore_file(
FILE *fp,
uint type)
{
char buffer[512];
char devbuffer[512];
char *dev = NULL;
uint mask;
int cnt;
__uint32_t id;
__uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
if (strncmp("fs = ", buffer, 5) == 0) {
dev = strncpy(devbuffer, buffer+5, sizeof(devbuffer));
dev[strlen(dev) - 1] = '\0';
continue;
}
rtbsoft = rtbhard = 0;
cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
&id,
&bsoft, &bhard,
&isoft, &ihard,
&rtbsoft, &rtbhard);
if (cnt == 5 || cnt == 7) {
mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
if (cnt == 7)
mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
set_limits(id, type, mask, dev, &bsoft, &bhard,
&isoft, &ihard, &rtbsoft, &rtbhard);
}
}
}
static int
restore_f(
int argc,
char **argv)
{
FILE *fp = stdin;
char *fname = NULL;
int c, type = 0;
while ((c = getopt(argc, argv, "f:gpu")) != 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;
default:
return command_usage(&restore_cmd);
}
}
if (argc < optind)
return command_usage(&restore_cmd);
if (!type)
type = XFS_USER_QUOTA;
if (fname) {
if ((fp = fopen(fname, "r")) == NULL) {
fprintf(stderr, _("%s: fopen on %s failed: %s\n"),
progname, fname, strerror(errno));
return 0;
}
}
restore_file(fp, type);
if (fname)
fclose(fp);
return 0;
}
static void
set_timer(
__uint32_t id,
uint type,
uint mask,
char *dev,
uint value)
{
fs_disk_quota_t d;
memset(&d, 0, sizeof(d));
d.d_version = FS_DQUOT_VERSION;
d.d_id = id;
d.d_flags = type;
d.d_fieldmask = mask;
d.d_itimer = value;
d.d_btimer = value;
d.d_rtbtimer = value;
if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0)
fprintf(stderr, _("%s: cannot set timer: %s\n"),
progname, strerror(errno));
}
static void
set_user_timer(
char *name,
uint type,
uint mask,
uint value)
{
uid_t uid = uid_from_string(name);
if (uid == -1)
fprintf(stderr, _("%s: invalid user name: %s\n"),
progname, name);
else
set_timer(uid, type, mask, fs_path->fs_name, value);
}
static void
set_group_timer(
char *name,
uint type,
uint mask,
uint value)
{
gid_t gid = gid_from_string(name);
if (gid == -1)
fprintf(stderr, _("%s: invalid group name: %s\n"),
progname, name);
else
set_timer(gid, type, mask, fs_path->fs_name, value);
}
static void
set_project_timer(
char *name,
uint type,
uint mask,
uint value)
{
prid_t prid = prid_from_string(name);
if (prid == -1)
fprintf(stderr, _("%s: invalid project name: %s\n"),
progname, name);
else
set_timer(prid, type, mask, fs_path->fs_name, value);
}
static int
timer_f(
int argc,
char **argv)
{
char *name;
uint value;
int c, flags = 0, type = 0, mask = 0;
while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
switch (c) {
case 'd':
flags |= DEFAULTS_FLAG;
break;
case 'b':
mask |= FS_DQ_BTIMER;
break;
case 'i':
mask |= FS_DQ_ITIMER;
break;
case 'r':
mask |= FS_DQ_RTBTIMER;
break;
case 'g':
type = XFS_GROUP_QUOTA;
break;
case 'p':
type = XFS_PROJ_QUOTA;
break;
case 'u':
type = XFS_USER_QUOTA;
break;
default:
return command_usage(&timer_cmd);
}
}
/*
* In the usual case, we need at least 2 more arguments -
* one (or more) value and a user name/id.
* For setting defaults (-d) we don't want a user name/id.
*/
if (flags & DEFAULTS_FLAG) {
if (argc != optind + 1)
return command_usage(&timer_cmd);
} else if (argc != optind + 2) {
return command_usage(&timer_cmd);
}
value = cvttime(argv[optind++]);
name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
if (!mask)
mask = FS_DQ_TIMER_MASK;
if (!type)
type = XFS_USER_QUOTA;
switch (type) {
case XFS_USER_QUOTA:
set_user_timer(name, type, mask, value);
break;
case XFS_GROUP_QUOTA:
set_group_timer(name, type, mask, value);
break;
case XFS_PROJ_QUOTA:
set_project_timer(name, type, mask, value);
break;
}
return 0;
}
static void
set_warnings(
__uint32_t id,
uint type,
uint mask,
char *dev,
uint value)
{
fs_disk_quota_t d;
memset(&d, 0, sizeof(d));
d.d_version = FS_DQUOT_VERSION;
d.d_id = id;
d.d_flags = type;
d.d_fieldmask = mask;
d.d_iwarns = value;
d.d_bwarns = value;
d.d_rtbwarns = value;
if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0)
fprintf(stderr, _("%s: cannot set warnings: %s\n"),
progname, strerror(errno));
}
static void
set_user_warnings(
char *name,
uint type,
uint mask,
uint value)
{
uid_t uid = uid_from_string(name);
if (uid == -1)
fprintf(stderr, _("%s: invalid user name: %s\n"),
progname, name);
else
set_warnings(uid, type, mask, fs_path->fs_name, value);
}
static void
set_group_warnings(
char *name,
uint type,
uint mask,
uint value)
{
gid_t gid = gid_from_string(name);
if (gid == -1)
fprintf(stderr, _("%s: invalid group name: %s\n"),
progname, name);
else
set_warnings(gid, type, mask, fs_path->fs_name, value);
}
static void
set_project_warnings(
char *name,
uint type,
uint mask,
uint value)
{
prid_t prid = prid_from_string(name);
if (prid == -1)
fprintf(stderr, _("%s: invalid project name: %s\n"),
progname, name);
else
set_warnings(prid, type, mask, fs_path->fs_name, value);
}
static int
warn_f(
int argc,
char **argv)
{
char *name;
uint value;
int c, flags = 0, type = 0, mask = 0;
while ((c = getopt(argc, argv, "bdgipru")) != EOF) {
switch (c) {
case 'd':
flags |= DEFAULTS_FLAG;
break;
case 'b':
mask |= FS_DQ_BWARNS;
break;
case 'i':
mask |= FS_DQ_IWARNS;
break;
case 'r':
mask |= FS_DQ_RTBWARNS;
break;
case 'g':
type = XFS_GROUP_QUOTA;
break;
case 'p':
type = XFS_PROJ_QUOTA;
break;
case 'u':
type = XFS_USER_QUOTA;
break;
default:
return command_usage(&warn_cmd);
}
}
/*
* In the usual case, we need at least 2 more arguments -
* one (or more) value and a user name/id.
* For setting defaults (-d) we don't want a user name/id.
*/
if (flags & DEFAULTS_FLAG) {
if (argc != optind + 1)
return command_usage(&warn_cmd);
} else if (argc != optind + 2) {
return command_usage(&warn_cmd);
}
value = atoi(argv[optind++]);
name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++];
if (!mask)
mask = FS_DQ_WARNS_MASK;
if (!type)
type = XFS_USER_QUOTA;
switch (type) {
case XFS_USER_QUOTA:
set_user_warnings(name, type, mask, value);
break;
case XFS_GROUP_QUOTA:
set_group_warnings(name, type, mask, value);
break;
case XFS_PROJ_QUOTA:
set_project_warnings(name, type, mask, value);
break;
}
return 0;
}
void
edit_init(void)
{
limit_cmd.name = _("limit");
limit_cmd.cfunc = limit_f;
limit_cmd.argmin = 2;
limit_cmd.argmax = -1;
limit_cmd.args = \
_("[-gpu] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name");
limit_cmd.oneline = _("modify quota limits");
limit_cmd.help = limit_help;
restore_cmd.name = _("restore");
restore_cmd.cfunc = restore_f;
restore_cmd.argmin = 0;
restore_cmd.argmax = -1;
restore_cmd.args = _("[-gpu] [-f file]");
restore_cmd.oneline = _("restore quota limits from a backup file");
timer_cmd.name = _("timer");
timer_cmd.cfunc = timer_f;
timer_cmd.argmin = 2;
timer_cmd.argmax = -1;
timer_cmd.args = _("[-bir] [-gpu] value -d|id|name");
timer_cmd.oneline = _("get/set quota enforcement timeouts");
timer_cmd.help = timer_help;
warn_cmd.name = _("warn");
warn_cmd.cfunc = warn_f;
warn_cmd.argmin = 2;
warn_cmd.argmax = -1;
warn_cmd.args = _("[-bir] [-gpu] value -d|id|name");
warn_cmd.oneline = _("get/set enforcement warning counter");
warn_cmd.help = warn_help;
if (expert) {
add_command(&limit_cmd);
add_command(&restore_cmd);
add_command(&timer_cmd);
add_command(&warn_cmd);
}
}