summaryrefslogtreecommitdiff
path: root/warnquota.c
diff options
context:
space:
mode:
Diffstat (limited to 'warnquota.c')
-rw-r--r--warnquota.c208
1 files changed, 178 insertions, 30 deletions
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)