summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjkar8572 <jkar8572>2002-07-23 15:59:27 +0000
committerjkar8572 <jkar8572>2002-07-23 15:59:27 +0000
commit8e0302ea1b0d327c66839ad738d2d5271e25f4d7 (patch)
tree544a35abe5b1978eeed817f964bfb82a0db3c89a
parent2d1fc29bbe752b590d9f320df80913c3808f7545 (diff)
* fixed quotacheck(8) to continue when old quota files were not found (Jan Kara)
* quota(1) now doesn't report failure to connect to rpc.rquotad server when -Q specified (Jan Kara) * add quota(1) option -l (report only local filesystems) (Jan Kara) * warnquota(8) now also mails specified member of the group about violation of the group quotas when -g option is specified (Jan Kara) * added options by which user can specify whether repquota(8) should translate names in big chunks by scanning all users or individually. Added automagic detection using nsswitch.conf which behaviour should lead to faster translating. (Jan Kara)
-rw-r--r--Changelog8
-rw-r--r--Makefile.in2
-rw-r--r--edquota.c8
-rw-r--r--quota.111
-rw-r--r--quota.c78
-rw-r--r--quotacheck.c4
-rw-r--r--quotaops.c10
-rw-r--r--quotaops.h2
-rw-r--r--quotasys.c28
-rw-r--r--quotasys.h6
-rw-r--r--repquota.831
-rw-r--r--repquota.c24
-rw-r--r--setquota.c4
-rw-r--r--warnquota.826
-rw-r--r--warnquota.c208
-rw-r--r--warnquota.conf9
16 files changed, 374 insertions, 85 deletions
diff --git a/Changelog b/Changelog
index f2ab1c8..ec23c54 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,14 @@ Changes in quota-tools from 3.06 to 3.07
* rised maximal number of mountpoint to 256 (Jan Kara)
* small fix in configure (Nathan Scott)
* small fix in quotacheck(8) docs (Jan Kara)
+* fixed quotacheck(8) to continue when old quota files were not found (Jan Kara)
+* quota(1) now doesn't report failure to connect to rpc.rquotad server when -Q specified (Jan Kara)
+* add quota(1) option -l (report only local filesystems) (Jan Kara)
+* warnquota(8) now also mails specified member of the group about violation of the group quotas
+ when -g option is specified (Jan Kara)
+* added options by which user can specify whether repquota(8) should translate names in
+ big chunks by scanning all users or individually. Added automagic detection using nsswitch.conf
+ which behaviour should lead to faster translating. (Jan Kara)
Changes in quota-tools from 3.05 to 3.06
* fixed caching of libwrap configure result (Jan Kara)
diff --git a/Makefile.in b/Makefile.in
index 5cbe80b..713e242 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -84,6 +84,8 @@ install: all inst_mo
$(ROOTDIR)$(sbindir)
-mkdir -p $(ROOTDIR)$(sysconfdir)
-$(INSTALL) -m $(DEF_CONF_MODE) warnquota.conf $(ROOTDIR)$(sysconfdir)
+ -$(INSTALL) -m $(DEF_CONF_MODE) quotatab $(ROOTDIR)$(sysconfdir)
+ -$(INSTALL) -m $(DEF_CONF_MODE) quotagrpadmins $(ROOTDIR)$(sysconfdir)
-mkdir -p $(ROOTDIR)$(mandir)/man1
-mkdir -p $(ROOTDIR)$(mandir)/man2
-mkdir -p $(ROOTDIR)$(mandir)/man3
diff --git a/edquota.c b/edquota.c
index f82a3a3..337a69e 100644
--- a/edquota.c
+++ b/edquota.c
@@ -34,7 +34,7 @@
#ident "$Copyright: (c) 1980, 1990 Regents of the University of California. $"
#ident "$Copyright: All rights reserved. $"
-#ident "$Id: edquota.c,v 1.8 2002/03/27 16:21:26 jkar8572 Exp $"
+#ident "$Id: edquota.c,v 1.9 2002/07/23 15:59:27 jkar8572 Exp $"
/*
* Disk quota editor.
@@ -150,10 +150,10 @@ int main(int argc, char **argv)
}
if (pflag) {
protoid = name2id(protoname, quotatype);
- protoprivs = getprivs(protoid, handles);
+ protoprivs = getprivs(protoid, handles, 0);
while (argc-- > 0) {
id = name2id(*argv++, quotatype);
- curprivs = getprivs(id, handles);
+ curprivs = getprivs(id, handles, 0);
for (pprivs = protoprivs, cprivs = curprivs; pprivs && cprivs;
pprivs = pprivs->dq_next, cprivs = cprivs->dq_next) {
@@ -206,7 +206,7 @@ int main(int argc, char **argv)
else {
for (; argc > 0; argc--, argv++) {
id = name2id(*argv, quotatype);
- curprivs = getprivs(id, handles);
+ curprivs = getprivs(id, handles, 0);
if (writeprivs(curprivs, tmpfd, *argv, quotatype) < 0) {
errstr(_("Can't write quotas to file.\n"));
continue;
diff --git a/quota.1 b/quota.1
index 3c49610..e22919c 100644
--- a/quota.1
+++ b/quota.1
@@ -7,7 +7,7 @@ quota \- display disk usage and limits
.B -F
.I format-name
] [
-.BR -guvs \ |
+.BR -guvsl \ |
.B q
]
.br
@@ -16,7 +16,7 @@ quota \- display disk usage and limits
.B -F
.I format-name
] [
-.BR -uvs \ |
+.BR -uvsl \ |
.B q
]
.I user
@@ -26,7 +26,7 @@ quota \- display disk usage and limits
.B -F
.I format-name
] [
-.BR -gvs \ |
+.BR -gvsl \ |
.B q
]
.I group
@@ -67,10 +67,13 @@ will display quotas on filesystems
where no storage is allocated.
.TP
.B \-s
-flag will make
+option will make
.BR quota (1)
try to choose units for showing limits, used space and used inodes.
.TP
+.B \-l
+report quotas only on local filesystems (ie. ignore NFS mounted filesystems).
+.TP
.B \-q
Print a more terse message,
containing only information
diff --git a/quota.c b/quota.c
index 65ced8f..33eff9d 100644
--- a/quota.c
+++ b/quota.c
@@ -34,7 +34,7 @@
#ident "$Copyright: (c) 1980, 1990 Regents of the University of California. $"
#ident "$Copyright: All rights reserved. $"
-#ident "$Id: quota.c,v 1.11 2002/05/20 12:16:31 jkar8572 Exp $"
+#ident "$Id: quota.c,v 1.12 2002/07/23 15:59:27 jkar8572 Exp $"
/*
* Disk quota reporting program.
@@ -60,7 +60,15 @@
#include "pot.h"
#include "common.h"
-int qflag, vflag, sflag, fmt = -1;
+#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
+
+int flags, fmt = -1;
char *progname;
void usage(void);
@@ -71,31 +79,37 @@ int main(int argc, char **argv)
{
int ngroups;
gid_t gidset[NGROUPS];
- int i, ret, gflag = 0, uflag = 0;
+ int i, ret;
gettexton();
progname = basename(argv[0]);
- while ((ret = getopt(argc, argv, "guqvsVF:")) != -1) {
+ while ((ret = getopt(argc, argv, "guqvsVlQF:")) != -1) {
switch (ret) {
case 'g':
- gflag++;
+ flags |= FL_GROUP;
break;
case 'u':
- uflag++;
+ flags |= FL_USER;
break;
case 'q':
- qflag++;
+ flags |= FL_QUIET;
break;
case 'v':
- vflag++;
+ flags |= FL_VERBOSE;
break;
case 'F':
if ((fmt = name2fmt(optarg)) == QF_ERROR) /* Error? */
exit(1);
break;
case 's':
- sflag++;
+ flags |= FL_SMARTSIZE;
+ break;
+ case 'l':
+ flags |= FL_LOCALONLY;
+ break;
+ case 'Q':
+ flags |= FL_QUIETREFUSE;
break;
case 'V':
version();
@@ -107,15 +121,15 @@ int main(int argc, char **argv)
argc -= optind;
argv += optind;
- if (!uflag && !gflag)
- uflag++;
+ if (!(flags & FL_USER) && !(flags & FL_GROUP))
+ flags |= FL_USER;
init_kernel_interface();
ret = 0;
if (argc == 0) {
- if (uflag)
+ if (flags & FL_USER)
ret |= showquotas(USRQUOTA, getuid());
- if (gflag) {
+ if (flags & FL_GROUP) {
ngroups = getgroups(NGROUPS, gidset);
if (ngroups < 0)
die(1, _("quota: getgroups(): %s\n"), strerror(errno));
@@ -125,13 +139,13 @@ int main(int argc, char **argv)
exit(ret);
}
- if (uflag && gflag)
+ if ((flags & FL_USER) && (flags & FL_GROUP))
usage();
- if (uflag)
+ if (flags & FL_USER)
for (; argc > 0; argc--, argv++)
ret |= showquotas(USRQUOTA, user2uid(*argv));
- else if (gflag)
+ else if (flags & FL_GROUP)
for (; argc > 0; argc--, argv++)
ret |= showquotas(GRPQUOTA, group2gid(*argv));
return ret;
@@ -140,9 +154,9 @@ int main(int argc, char **argv)
void usage(void)
{
errstr( "%s%s%s",
- _("Usage: quota [-guqvs] [-F quotaformat]\n"),
- _("\tquota [-qvs] [-F quotaformat] -u username ...\n"),
- _("\tquota [-qvs] [-F quotaformat] -g groupname ...\n"));
+ _("Usage: quota [-guqvs] [-l | -Q] [-F quotaformat]\n"),
+ _("\tquota [-qvs] [-l | -Q] [-F quotaformat] -u username ...\n"),
+ _("\tquota [-qvs] [-l | -Q] [-F quotaformat] -g groupname ...\n"));
fprintf(stderr, _("Bugs to: %s\n"), MY_EMAIL);
exit(1);
}
@@ -159,12 +173,12 @@ int showquotas(int type, qid_t id)
time(&now);
id2name(id, type, name);
- handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY);
- qlist = getprivs(id, handles);
+ handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY | ((flags & FL_LOCALONLY) ? IOI_LOCALONLY : 0));
+ qlist = getprivs(id, handles, !!(flags & FL_QUIETREFUSE));
over = 0;
for (q = qlist; q; q = q->dq_next) {
bover = iover = 0;
- if (!vflag && !q->dq_dqb.dqb_isoftlimit && !q->dq_dqb.dqb_ihardlimit
+ 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;
@@ -200,7 +214,7 @@ int showquotas(int type, qid_t id)
}
}
over |= bover | iover;
- if (qflag) {
+ if (flags & FL_QUIET) {
if ((msgi || msgb) && !lines++)
heading(type, id, name, "");
if (msgi)
@@ -209,7 +223,7 @@ int showquotas(int type, qid_t id)
printf("\t%s %s\n", msgb, q->dq_h->qh_quotadev);
continue;
}
- if (vflag || q->dq_dqb.dqb_curspace || q->dq_dqb.dqb_curinodes) {
+ if ((flags & FL_VERBOSE) || q->dq_dqb.dqb_curspace || q->dq_dqb.dqb_curinodes) {
char numbuf[3][MAXNUMLEN];
if (!lines++)
@@ -220,22 +234,22 @@ int showquotas(int type, qid_t id)
printf("%15s", q->dq_h->qh_quotadev);
if (bover)
difftime2str(q->dq_dqb.dqb_btime, timebuf);
- space2str(toqb(q->dq_dqb.dqb_curspace), numbuf[0], sflag);
- space2str(q->dq_dqb.dqb_bsoftlimit, numbuf[1], sflag);
- space2str(q->dq_dqb.dqb_bhardlimit, numbuf[2], sflag);
+ 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], bover > 1 ? timebuf : "");
if (iover)
difftime2str(q->dq_dqb.dqb_itime, timebuf);
- number2str(q->dq_dqb.dqb_curinodes, numbuf[0], sflag);
- number2str(q->dq_dqb.dqb_isoftlimit, numbuf[1], sflag);
- number2str(q->dq_dqb.dqb_ihardlimit, numbuf[2], sflag);
+ 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], iover > 1 ? timebuf : "");
continue;
}
}
- if (!qflag && !lines)
+ if (!(flags & FL_QUIET) && !lines)
heading(type, id, name, _("none"));
freeprivs(qlist);
dispose_handle_list(handles);
@@ -246,7 +260,7 @@ void heading(int type, qid_t id, char *name, char *tag)
{
printf(_("Disk quotas for %s %s (%cid %d): %s\n"), type2name(type),
name, *type2name(type), id, tag);
- if (!qflag && !tag[0]) {
+ if (!(flags & FL_QUIET) && !tag[0]) {
printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n", _("Filesystem"),
_("blocks"), _("quota"), _("limit"), _("grace"),
_("files"), _("quota"), _("limit"), _("grace"));
diff --git a/quotacheck.c b/quotacheck.c
index b318f34..351bf99 100644
--- a/quotacheck.c
+++ b/quotacheck.c
@@ -8,7 +8,7 @@
* New quota format implementation - Jan Kara <jack@suse.cz> - Sponsored by SuSE CR
*/
-#ident "$Id: quotacheck.c,v 1.29 2002/05/03 07:05:46 jkar8572 Exp $"
+#ident "$Id: quotacheck.c,v 1.30 2002/07/23 15:59:27 jkar8572 Exp $"
#include <dirent.h>
#include <stdio.h>
@@ -570,7 +570,7 @@ Please turn quotas off or use -f to force checking.\n"),
}
if (!(flags & FL_NEWFILE)) { /* Need to buffer file? */
- if (get_qf_name(mnt, type, (1 << cfmt), NF_EXIST, &qfname) < 0) {
+ if (get_qf_name(mnt, type, (1 << cfmt), 0, &qfname) < 0) {
errstr(_("Cannot get quotafile name for %s\n"), mnt->mnt_fsname);
return -1;
}
diff --git a/quotaops.c b/quotaops.c
index 71628d4..2dc6972 100644
--- a/quotaops.c
+++ b/quotaops.c
@@ -34,7 +34,7 @@
#ident "$Copyright: (c) 1980, 1990 Regents of the University of California. $"
#ident "$Copyright: All rights reserved. $"
-#ident "$Id: quotaops.c,v 1.7 2001/09/27 21:34:58 jkar8572 Exp $"
+#ident "$Id: quotaops.c,v 1.8 2002/07/23 15:59:27 jkar8572 Exp $"
#include <rpc/rpc.h>
#include <sys/types.h>
@@ -109,7 +109,7 @@ void update_grace_times(struct dquot *q)
/*
* Collect the requested quota information.
*/
-struct dquot *getprivs(qid_t id, struct quota_handle **handles)
+struct dquot *getprivs(qid_t id, struct quota_handle **handles, int quiet)
{
struct dquot *q, *qtail = NULL, *qhead = NULL;
int i;
@@ -154,8 +154,10 @@ struct dquot *getprivs(qid_t id, struct quota_handle **handles)
#endif
if (!(q = handles[i]->qh_ops->read_dquot(handles[i], id))) {
- errstr(_("Error while getting quota from %s for %u: %s\n"),
- handles[i]->qh_quotadev, id, strerror(errno));
+ /* If rpc.rquotad is not running filesystem might be just without quotas... */
+ if (errno != ECONNREFUSED || !quiet)
+ errstr(_("Error while getting quota from %s for %u: %s\n"),
+ handles[i]->qh_quotadev, id, strerror(errno));
continue;
}
if (qhead == NULL)
diff --git a/quotaops.h b/quotaops.h
index 4eb0cb1..b680c74 100644
--- a/quotaops.h
+++ b/quotaops.h
@@ -3,7 +3,7 @@
#include "quotaio.h"
-struct dquot *getprivs(qid_t id, struct quota_handle ** handles);
+struct dquot *getprivs(qid_t id, struct quota_handle ** handles, int quiet);
int putprivs(struct dquot * qlist);
int editprivs(char *tmpfile);
int writeprivs(struct dquot * qlist, int outfd, char *name, int quotatype);
diff --git a/quotasys.c b/quotasys.c
index 109838c..3272b70 100644
--- a/quotasys.c
+++ b/quotasys.c
@@ -16,6 +16,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
+#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
@@ -146,6 +147,33 @@ int id2name(int id, int qtype, char *buf)
}
/*
+ * Parse /etc/nsswitch.conf and return type of default passwd handling
+ */
+int passwd_handling(void)
+{
+ FILE *f;
+ char buf[1024], *colpos, *spcpos;
+ int ret = PASSWD_FILES;
+
+ if (!(f = fopen("/etc/nsswitch.conf", "r")))
+ return PASSWD_FILES; /* Can't open nsswitch.conf - fallback on compatible mode */
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strncmp(buf, "passwd:", 7)) /* Not passwd entry? */
+ continue;
+ for (colpos = buf+7; isspace(*colpos); colpos++);
+ if (!*colpos) /* Not found any type of handling? */
+ break;
+ for (spcpos = colpos; !isspace(*spcpos) && *spcpos; spcpos++);
+ *spcpos = 0;
+ if (!strcmp(colpos, "db") || !strcmp(colpos, "nis") || !strcmp(colpos, "nis+"))
+ ret = PASSWD_DB;
+ break;
+ }
+ fclose(f);
+ return ret;
+}
+
+/*
* Convert quota format name to number
*/
int name2fmt(char *str)
diff --git a/quotasys.h b/quotasys.h
index 3a347d0..38f4da6 100644
--- a/quotasys.h
+++ b/quotasys.h
@@ -58,6 +58,12 @@ int gid2group(gid_t, char *);
/* Convert id to user/group name */
int id2name(int id, int qtype, char *buf);
+/* Possible default passwd handling */
+#define PASSWD_FILES 0
+#define PASSWD_DB 1
+/* Parse /etc/nsswitch.conf and return type of default passwd handling */
+int passwd_handling(void);
+
/* Convert quota format name to number */
int name2fmt(char *str);
diff --git a/repquota.8 b/repquota.8
index b7af625..b930bf3 100644
--- a/repquota.8
+++ b/repquota.8
@@ -7,6 +7,10 @@ repquota \- summarize quotas for a filesystem
[
.B \-vsug
] [
+.B \-c
+|
+.B \-C
+] [
.B \-t
|
.B \-n
@@ -20,6 +24,10 @@ repquota \- summarize quotas for a filesystem
[
.B \-avtsug
] [
+.B \-c
+|
+.B \-C
+] [
.B \-t
|
.B \-n
@@ -42,6 +50,22 @@ prints a summary of the disc usage and quotas for the specified file
systems. For each user the current number of files and amount of space
(in kilobytes) is printed, along with any quotas created with
.BR edquota (8).
+As
+.B repquota
+has to translate ids of all users/groups to names (unless option
+.B -n
+was specified) it may take a while to
+print all the information. To make translating as fast as possible
+.B repquota
+tries to detect (by reading
+.BR /etc/nsswitch.conf )
+whether entries are stored in standard plain text file or in database and either
+translates chunks of 1024 names or each name individually. You can override this
+autodetection by
+.B -c
+or
+.B -C
+options.
.SH OPTIONS
.TP
.B \-a
@@ -53,6 +77,13 @@ to be read-write with quotas.
Report all quotas, even if there is no usage. Be also more verbose about quotafile
information.
.TP
+.B \-c
+Cache entries to report and translate uids/gids to names in big chunks by scanning
+all users (default). This is good (fast) behaviour when using /etc/passwd file.
+.TP
+.B \-C
+Translate individual entries. This is faster when you have users stored in database.
+.TP
.B \-t
Truncate user/group names longer than 9 characters. This results in nicer output when
there are such names.
diff --git a/repquota.c b/repquota.c
index 7dcc587..c47308b 100644
--- a/repquota.c
+++ b/repquota.c
@@ -31,6 +31,7 @@
#define FL_TRUNCNAMES 16
#define FL_SHORTNUMS 32
#define FL_NONAME 64
+#define FL_NOCACHE 128 /* Don't cache dquots before resolving */
int flags, fmt = -1;
char **mnt;
@@ -41,7 +42,7 @@ char *progname;
static void usage(void)
{
- errstr(_("Utility for reporting quotas.\nUsage:\n%s [-vugs] [-t|n] [-F quotaformat] (-a | mntpoint)\n"), progname);
+ errstr(_("Utility for reporting quotas.\nUsage:\n%s [-vugs] [-c|C] [-t|n] [-F quotaformat] (-a | mntpoint)\n"), progname);
fprintf(stderr, _("Bugs to %s\n"), MY_EMAIL);
exit(1);
}
@@ -49,8 +50,9 @@ static void usage(void)
static void parse_options(int argcnt, char **argstr)
{
int ret;
+ int cache_specified = 0;
- while ((ret = getopt(argcnt, argstr, "VavughtsnF:")) != -1) {
+ while ((ret = getopt(argcnt, argstr, "VavughtsncCF:")) != -1) {
switch (ret) {
case '?':
case 'h':
@@ -76,6 +78,13 @@ static void parse_options(int argcnt, char **argstr)
case 's':
flags |= FL_SHORTNUMS;
break;
+ case 'C':
+ flags |= FL_NOCACHE;
+ cache_specified = 1;
+ break;
+ case 'c':
+ cache_specified = 1;
+ break;
case 'F':
if ((fmt = name2fmt(optarg)) == QF_ERROR)
exit(1);
@@ -105,6 +114,8 @@ static void parse_options(int argcnt, char **argstr)
mnt = argstr + optind;
mntcnt = argcnt - optind;
}
+ if (!cache_specified && !(flags & FL_NONAME) && passwd_handling() == PASSWD_DB)
+ flags |= FL_NOCACHE;
}
static char overlim(uint usage, uint softlim, uint hardlim)
@@ -198,8 +209,15 @@ static int output(struct dquot *dquot, char *name)
sprintf(namebuf, "#%u", dquot->dq_id);
print(dquot, namebuf);
}
- else if (name)
+ else if (name || flags & FL_NOCACHE) {
+ char namebuf[MAXNAMELEN];
+
+ if (!name) {
+ id2name(dquot->dq_id, dquot->dq_h->qh_type, namebuf);
+ name = namebuf;
+ }
print(dquot, name);
+ }
else {
memcpy(dquot_cache+cached_dquots++, dquot, sizeof(struct dquot));
if (cached_dquots >= MAX_CACHE_DQUOTS)
diff --git a/setquota.c b/setquota.c
index b4171ac..25cd14c 100644
--- a/setquota.c
+++ b/setquota.c
@@ -173,9 +173,9 @@ static void setlimits(struct quota_handle **handles)
{
struct dquot *q, *protoq, *protoprivs = NULL, *curprivs;
- curprivs = getprivs(id, handles);
+ curprivs = getprivs(id, handles, 0);
if (flags & FL_PROTO) {
- protoprivs = getprivs(protoid, handles);
+ protoprivs = getprivs(protoid, handles, 0);
for (q = curprivs, protoq = protoprivs; q; q = q->dq_next, protoq = protoq->dq_next) {
q->dq_dqb.dqb_bsoftlimit = protoq->dq_dqb.dqb_bsoftlimit;
q->dq_dqb.dqb_bhardlimit = protoq->dq_dqb.dqb_bhardlimit;
diff --git a/warnquota.8 b/warnquota.8
index e8548a4..cd39cef 100644
--- a/warnquota.8
+++ b/warnquota.8
@@ -4,6 +4,8 @@ warnquota \- send mail to users over quota
.SH SYNOPSIS
.B warnquota
[
+.B \-ug
+] [
.B \-F
.I quotaformat
] [
@@ -12,6 +14,9 @@ warnquota \- send mail to users over quota
] [
.B \-c
.I configfile
+] [
+.B \-a
+.I adminsfile
]
.SH DESCRIPTION
.B warnquota
@@ -32,19 +37,33 @@ Possible format names are:
.B xfs
(quota on XFS filesystem)
.TP
-.B -q quotatab
+.B -q \f2quotatab\f1
Use
.I quotatab
instead of
.I /etc/quotatab
as file with device description strings (see example file for syntax).
.TP
-.B -c configfile
+.B -c \f2configfile\f1
Use
.I configfile
instead of
.I /etc/warnquota.conf
as configuration file (see example file for syntax).
+.TP
+.B -a \f2adminsfile\f1
+Use
+.I adminsfile
+instead of
+.I /etc/quotagrpadmins
+as a file with administrators of the groups.
+.TP
+.B -u
+check whether users are not exceeding quotas (default).
+.TP
+.B -g
+check whether groups are not exceeding quotas. If group is exceeding quota
+a mail is sent to the user specified in /etc/quotagrpadmins.
.SH FILES
.PD 0
.TP 20
@@ -60,6 +79,9 @@ configuration file
.B /etc/quotatab
device description
.TP
+.B /etc/quotagrpadmins
+administrators of the groups
+.TP
.B /etc/mtab
default filesystems
.TP
diff --git a/warnquota.c b/warnquota.c
index 17638a5..f973fa6 100644
--- a/warnquota.c
+++ b/warnquota.c
@@ -10,7 +10,7 @@
*
* Author: Marco van Wieringen <mvw@planets.elm.net>
*
- * Version: $Id: warnquota.c,v 1.10 2002/03/27 16:21:26 jkar8572 Exp $
+ * Version: $Id: warnquota.c,v 1.11 2002/07/23 15:59:27 jkar8572 Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -26,6 +26,7 @@
#include <errno.h>
#include <ctype.h>
#include <signal.h>
+#include <grp.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -44,21 +45,36 @@
#define SUPPORT "support@localhost"
#define PHONE "(xxx) xxx-xxxx or (yyy) yyy-yyyy"
-#define DEF_MESSAGE _("Hi,\n\nWe noticed that you are in violation with the quotasystem\n" \
+#define DEF_USER_MESSAGE _("Hi,\n\nWe noticed that you are in violation with the quotasystem\n" \
"used on this system. We have found the following violations:\n\n")
-#define DEF_SIGNATURE _("\nWe hope that you will cleanup before your grace period expires.\n" \
+#define DEF_USER_SIGNATURE _("\nWe hope that you will cleanup before your grace period expires.\n" \
"\nBasically, this means that the system thinks you are using more disk space\n" \
"on the above partition(s) than you are allowed. If you do not delete files\n" \
"and get below your quota before the grace period expires, the system will\n" \
"prevent you from creating new files.\n\n" \
"For additional assistance, please contact us at %s\nor via " \
"phone at %s.\n")
+#define DEF_GROUP_MESSAGE _("Hi,\n\nWe noticed that the group %s you are member of violates the quotasystem\n" \
+ "used on this system. We have found the following violations:\n\n")
+#define DEF_GROUP_SIGNATURE _("\nPlease cleanup the group data before the grace period expires.\n" \
+ "\nBasically, this means that the system thinks group is using more disk space\n" \
+ "on the above partition(s) than it is allowed. If you do not delete files\n" \
+ "and get below group quota before the grace period expires, the system will\n" \
+ "prevent you and other members of the group from creating new files owned by\n" \
+ "the group.\n\n" \
+ "For additional assistance, please contact us at %s\nor via " \
+ "phone at %s.\n")
#define SHELL "/bin/sh"
#define QUOTATAB "/etc/quotatab"
#define CNF_BUFFER 2048
#define IOBUF_SIZE 16384 /* Size of buffer for line in config files */
+#define ADMIN_TAB_ALLOC 256 /* How many entries to admins table should we allocate at once? */
#define WARNQUOTA_CONF "/etc/warnquota.conf"
+#define ADMINSFILE "/etc/quotagrpadmins"
+
+#define FL_USER 1
+#define FL_GROUP 2
struct usage {
char *devicename;
@@ -73,11 +89,14 @@ struct configparams {
char cc_to[CNF_BUFFER];
char support[CNF_BUFFER];
char phone[CNF_BUFFER];
- char *message;
- char *signature;
+ char *user_message;
+ char *user_signature;
+ char *group_message;
+ char *group_signature;
};
struct offenderlist {
+ int offender_type;
int offender_id;
char *offender_name;
struct usage *usage;
@@ -89,29 +108,37 @@ typedef struct quotatable {
char *devdesc;
} quotatable_t;
-int qtab_i = 0, fmt = -1;
-char *configfile = WARNQUOTA_CONF, *quotatabfile = QUOTATAB;
+struct adminstable {
+ char *grpname;
+ char *adminname;
+};
+
+int qtab_i = 0, fmt = -1, flags;
+char *configfile = WARNQUOTA_CONF, *quotatabfile = QUOTATAB, *adminsfile = ADMINSFILE;
char *progname;
-quotatable_t *quotatable = (quotatable_t *) NULL;
+quotatable_t *quotatable;
+int adminscnt, adminsalloc;
+struct adminstable *adminstable;
/*
* Global pointers to list.
*/
static struct offenderlist *offenders = (struct offenderlist *)0;
-struct offenderlist *add_offender(int id, char *name)
+struct offenderlist *add_offender(int type, int id, char *name)
{
struct offenderlist *offender;
char namebuf[MAXNAMELEN];
if (!name) {
- if (id2name(id, USRQUOTA, namebuf)) {
- errstr(_("Can't get name for uid %u.\n"), id);
+ if (id2name(id, type, namebuf)) {
+ errstr(_("Can't get name for uid/gid %u.\n"), id);
return NULL;
}
name = namebuf;
}
offender = (struct offenderlist *)smalloc(sizeof(struct offenderlist));
+ offender->offender_type = type;
offender->offender_id = id;
offender->offender_name = sstrdup(name);
offender->usage = (struct usage *)NULL;
@@ -126,11 +153,11 @@ void add_offence(struct dquot *dquot, char *name)
struct usage *usage;
for (lptr = offenders; lptr; lptr = lptr->next)
- if (lptr->offender_id == dquot->dq_id)
+ if (dquot->dq_h->qh_type == lptr->offender_type && lptr->offender_id == dquot->dq_id)
break;
if (!lptr)
- if (!(lptr = add_offender(dquot->dq_id, name)))
+ if (!(lptr = add_offender(dquot->dq_h->qh_type, dquot->dq_id, name)))
return;
usage = (struct usage *)smalloc(sizeof(struct usage));
@@ -183,6 +210,11 @@ FILE *run_mailer(char *command)
}
}
+int admin_name_cmp(const void *key, const void *mem)
+{
+ return strcmp(key, ((struct adminstable *)mem)->grpname);
+}
+
int mail_user(struct offenderlist *offender, struct configparams *config)
{
struct usage *lptr;
@@ -190,19 +222,37 @@ int mail_user(struct offenderlist *offender, struct configparams *config)
int cnt, status;
char timebuf[MAXTIMELEN];
struct util_dqblk *dqb;
+ char *to;
+
+ if (offender->offender_type == USRQUOTA)
+ to = offender->offender_name;
+ else {
+ struct adminstable *admin;
+ if (!(admin = bsearch(offender->offender_name, adminstable, adminscnt, sizeof(struct adminstable), admin_name_cmp))) {
+ errstr(_("Administrator for a group %s not found. Cancelling mail.\n"), offender->offender_name);
+ return -1;
+ }
+ to = admin->adminname;
+ }
if (!(fp = run_mailer(config->mail_cmd)))
return -1;
fprintf(fp, "From: %s\n", config->from);
fprintf(fp, "Reply-To: %s\n", config->support);
fprintf(fp, "Subject: %s\n", config->subject);
- fprintf(fp, "To: %s\n", offender->offender_name);
+ fprintf(fp, "To: %s\n", to);
fprintf(fp, "Cc: %s\n", config->cc_to);
fprintf(fp, "\n");
- if (config->message)
- fputs(config->message, fp);
+ if (offender->offender_type == USRQUOTA)
+ if (config->user_message)
+ fputs(config->user_message, fp);
+ else
+ fputs(DEF_USER_MESSAGE, fp);
else
- fputs(DEF_MESSAGE, fp);
+ if (config->group_message)
+ fprintf(fp, config->group_message, offender->offender_name);
+ else
+ fprintf(fp, DEF_GROUP_MESSAGE, offender->offender_name);
for (lptr = offender->usage; lptr; lptr = lptr->next) {
dqb = &lptr->dq_dqb;
@@ -235,10 +285,16 @@ int mail_user(struct offenderlist *offender, struct configparams *config)
fprintf(fp, " %6Lu%6Lu%6Lu%7s\n\n", (long long)dqb->dqb_curinodes,
(long long)dqb->dqb_isoftlimit, (long long)dqb->dqb_ihardlimit, timebuf);
}
- if (config->signature)
- fputs(config->signature, fp);
+ if (offender->offender_type == USRQUOTA)
+ if (config->user_signature)
+ fputs(config->user_signature, fp);
+ else
+ fprintf(fp, DEF_USER_SIGNATURE, config->support, config->phone);
else
- fprintf(fp, DEF_SIGNATURE, config->support, config->phone);
+ if (config->group_signature)
+ fputs(config->group_signature, fp);
+ else
+ fprintf(fp, DEF_GROUP_SIGNATURE, config->support, config->phone);
fclose(fp);
if (wait(&status) < 0) /* Wait for mailer */
errstr(_("Can't wait for mailer: %s\n"), strerror(errno));
@@ -363,7 +419,7 @@ int readconfigfile(const char *filename, struct configparams *config)
sstrncpy(config->cc_to, CC_TO, CNF_BUFFER);
sstrncpy(config->support, SUPPORT, CNF_BUFFER);
sstrncpy(config->phone, PHONE, CNF_BUFFER);
- config->signature = config->message = NULL;
+ config->user_signature = config->user_message = config->group_signature = config->group_message = NULL;
if (!(fp = fopen(filename, "r"))) {
errstr(_("Can't open %s: %s\n"), filename, strerror(errno));
@@ -420,12 +476,20 @@ int readconfigfile(const char *filename, struct configparams *config)
else if (!strcmp(var, "PHONE"))
sstrncpy(config->phone, value, CNF_BUFFER);
else if (!strcmp(var, "MESSAGE")) {
- config->message = sstrdup(value);
- create_eoln(config->message);
+ config->user_message = sstrdup(value);
+ create_eoln(config->user_message);
}
else if (!strcmp(var, "SIGNATURE")) {
- config->signature = sstrdup(value);
- create_eoln(config->signature);
+ config->user_signature = sstrdup(value);
+ create_eoln(config->user_signature);
+ }
+ else if (!strcmp(var, "GROUP_MESSAGE")) {
+ config->group_message = sstrdup(value);
+ create_eoln(config->group_message);
+ }
+ else if (!strcmp(var, "GROUP_SIGNATURE")) {
+ config->group_signature = sstrdup(value);
+ create_eoln(config->group_signature);
}
else /* not matched at all */
errstr(_("Error in config file (line %d), ignoring\n"), line);
@@ -440,6 +504,68 @@ int readconfigfile(const char *filename, struct configparams *config)
return 0;
}
+int admin_cmp(const void *a1, const void *a2)
+{
+ return strcmp(((struct adminstable *)a1)->grpname, ((struct adminstable *)a2)->grpname);
+}
+
+/* Get administrators of the groups */
+int get_groupadmins(void)
+{
+ FILE *f;
+ int line = 0;
+ char buffer[IOBUF_SIZE], *colpos, *grouppos, *endname, *adminpos;
+
+ if (!(f = fopen(adminsfile, "r"))) {
+ errstr(_("Can't open file with group administrators: %s\n"), strerror(errno));
+ return -1;
+ }
+
+ while (fgets(buffer, IOBUF_SIZE, f)) {
+ line++;
+ if (buffer[0] == ';' || buffer[0] == '#')
+ continue;
+ /* Skip initial spaces */
+ for (colpos = buffer; isspace(*colpos); colpos++);
+ if (!*colpos) /* Empty line? */
+ continue;
+ /* Find splitting colon */
+ for (grouppos = colpos; *colpos && *colpos != ':'; colpos++);
+ if (!*colpos || grouppos == colpos) {
+ errstr(_("Parse error at line %d. Can't find end of group name.\n"), line);
+ continue;
+ }
+ /* Cut trailing spaces */
+ for (endname = colpos-1; isspace(*endname); endname--);
+ *(++endname) = 0;
+ /* Skip initial spaces at admins name */
+ for (colpos++; isspace(*colpos); colpos++);
+ if (!*colpos) {
+ errstr(_("Parse error at line %d. Can't find administrators name.\n"), line);
+ continue;
+ }
+ /* Go through admins name */
+ for (adminpos = colpos; !isspace(*colpos); colpos++);
+ if (*colpos) { /* Some characters after name? */
+ *colpos = 0;
+ /* Skip trailing spaces */
+ for (colpos++; isspace(*colpos); colpos++);
+ if (*colpos) {
+ errstr(_("Parse error at line %d. Trailing characters after administrators name.\n"), line);
+ continue;
+ }
+ }
+ if (adminscnt >= adminsalloc)
+ adminstable = srealloc(adminstable, sizeof(struct adminstable)*(adminsalloc+=ADMIN_TAB_ALLOC));
+ adminstable[adminscnt].grpname = sstrdup(grouppos);
+ adminstable[adminscnt++].adminname = sstrdup(adminpos);
+ }
+
+ fclose(f);
+ qsort(adminstable, adminscnt, sizeof(struct adminstable), admin_cmp);
+ return 0;
+}
+
void warn_quota(void)
{
struct quota_handle **handles;
@@ -451,9 +577,20 @@ void warn_quota(void)
if (get_quotatable() < 0)
exit(1);
- handles = create_handle_list(0, NULL, USRQUOTA, -1, IOI_LOCALONLY | IOI_READONLY | IOI_OPENFILE);
- for (i = 0; handles[i]; i++)
- handles[i]->qh_ops->scan_dquots(handles[i], check_offence);
+ if (flags & FL_USER) {
+ handles = create_handle_list(0, NULL, USRQUOTA, -1, IOI_LOCALONLY | IOI_READONLY | IOI_OPENFILE);
+ for (i = 0; handles[i]; i++)
+ handles[i]->qh_ops->scan_dquots(handles[i], check_offence);
+ dispose_handle_list(handles);
+ }
+ if (flags & FL_GROUP) {
+ if (get_groupadmins() < 0)
+ exit(1);
+ handles = create_handle_list(0, NULL, GRPQUOTA, -1, IOI_LOCALONLY | IOI_READONLY | IOI_OPENFILE);
+ for (i = 0; handles[i]; i++)
+ handles[i]->qh_ops->scan_dquots(handles[i], check_offence);
+ dispose_handle_list(handles);
+ }
if (mail_to_offenders(&config) < 0)
exit(1);
}
@@ -461,14 +598,14 @@ void warn_quota(void)
/* Print usage information */
static void usage(void)
{
- errstr(_("Usage:\n warnquota [-F quotaformat] [-c configfile] [-q quotatabfile]\n"));
+ errstr(_("Usage:\n warnquota [-ug] [-F quotaformat] [-c configfile] [-q quotatabfile]\n"));
}
static void parse_options(int argcnt, char **argstr)
{
int ret;
- while ((ret = getopt(argcnt, argstr, "VF:hc:q:")) != -1) {
+ while ((ret = getopt(argcnt, argstr, "ugVF:hc:q:a:")) != -1) {
switch (ret) {
case '?':
case 'h':
@@ -486,8 +623,19 @@ static void parse_options(int argcnt, char **argstr)
case 'q':
quotatabfile = optarg;
break;
+ case 'a':
+ adminsfile = optarg;
+ break;
+ case 'u':
+ flags |= FL_USER;
+ break;
+ case 'g':
+ flags |= FL_GROUP;
+ break;
}
}
+ if (!(flags & FL_USER) && !(flags & FL_GROUP))
+ flags |= FL_USER;
}
int main(int argc, char **argv)
diff --git a/warnquota.conf b/warnquota.conf
index e406298..b31fef2 100644
--- a/warnquota.conf
+++ b/warnquota.conf
@@ -13,12 +13,19 @@ SUPPORT = "support@myhost.com"
PHONE = "(123) 456-1111 or (222) 333-4444"
# Text in the beginning of the mail (if not specified, default text is used)
# This way text can be split to more lines
-# Line break are done by '|' character
+# Line breaks are done by '|' character
MESSAGE = Hello, I've noticed you use too much space\
on my disk|Delete your files on following filesystems:|
# Text in the end of the mail (if not specified, default text using SUPPORT and PHONE
# is created)
SIGNATURE = See you!| Your admin|
+# Following text is used for mails about group exceeding quotas
+# It should contain string %s exactly once - it will be substituted for a group name
+GROUP_MESSAGE = Hello, a group '%s' you're member of use too much space.|\
+I chose you to do the cleanup.|Delete group files on following filesystems:|
+# Text in the end of the mail to the group (if not specified, default text using SUPPORT
+# and PHONE is created).
+GROUP_SIGNATURE = See you!| Your admin|
#
# end of example warnquota.conf file
#