2 * QUOTA An implementation of the diskquota system for the LINUX operating
3 * system. QUOTA is implemented using the BSD systemcall interface
4 * as the means of communication with the user level. Should work for
5 * all filesystems because of integration into the VFS layer of the
6 * operating system. This is based on the Melbourne quota system wich
7 * uses both user and group quota files.
9 * Program to mail to users that they are over there quota.
11 * Author: Marco van Wieringen <mvw@planets.elm.net>
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
32 #include <sys/types.h>
34 #include <sys/utsname.h>
35 #ifdef USE_LDAP_MAIL_LOOKUP
46 /* these are just defaults, overridden in the WARNQUOTA_CONF file */
47 #define MAIL_CMD "/usr/lib/sendmail -t"
48 #define FROM "support@localhost"
49 #define SUBJECT "Disk Quota usage on system"
51 #define SUPPORT "support@localhost"
52 #define PHONE "(xxx) xxx-xxxx or (yyy) yyy-yyyy"
54 #define DEF_USER_MESSAGE _("Hi,\n\nWe noticed that you are in violation with the quotasystem\n" \
55 "used on this system. We have found the following violations:\n\n")
56 #define DEF_USER_SIGNATURE _("\nWe hope that you will cleanup before your grace period expires.\n" \
57 "\nBasically, this means that the system thinks you are using more disk space\n" \
58 "on the above partition(s) than you are allowed. If you do not delete files\n" \
59 "and get below your quota before the grace period expires, the system will\n" \
60 "prevent you from creating new files.\n\n" \
61 "For additional assistance, please contact us at %s\nor via " \
63 #define DEF_GROUP_MESSAGE _("Hi,\n\nWe noticed that the group %s you are member of violates the quotasystem\n" \
64 "used on this system. We have found the following violations:\n\n")
65 #define DEF_GROUP_SIGNATURE _("\nPlease cleanup the group data before the grace period expires.\n" \
66 "\nBasically, this means that the system thinks group is using more disk space\n" \
67 "on the above partition(s) than it is allowed. If you do not delete files\n" \
68 "and get below group quota before the grace period expires, the system will\n" \
69 "prevent you and other members of the group from creating new files owned by\n" \
71 "For additional assistance, please contact us at %s\nor via " \
74 #define SHELL "/bin/sh"
75 #define QUOTATAB "/etc/quotatab"
76 #define CNF_BUFFER 2048
77 #define IOBUF_SIZE 16384 /* Size of buffer for line in config files */
78 #define ADMIN_TAB_ALLOC 256 /* How many entries to admins table should we allocate at once? */
79 #define WARNQUOTA_CONF "/etc/warnquota.conf"
80 #define ADMINSFILE "/etc/quotagrpadmins"
85 #define FL_SHORTNUMS 8
86 #define FL_NODETAILS 16
90 struct util_dqblk dq_dqb;
94 #ifdef USE_LDAP_MAIL_LOOKUP
95 static LDAP *ldapconn = NULL;
99 char mail_cmd[CNF_BUFFER];
100 char from[CNF_BUFFER];
101 char subject[CNF_BUFFER];
102 char cc_to[CNF_BUFFER];
103 char support[CNF_BUFFER];
104 char phone[CNF_BUFFER];
105 char charset[CNF_BUFFER];
107 char *user_signature;
109 char *group_signature;
110 int use_ldap_mail; /* 0 */
112 #ifdef USE_LDAP_MAIL_LOOKUP
113 int ldap_is_setup; /* 0 */
114 char ldap_host[CNF_BUFFER];
116 char ldap_uri[CNF_BUFFER];
117 char ldap_binddn[CNF_BUFFER];
118 char ldap_bindpw[CNF_BUFFER];
119 char ldap_basedn[CNF_BUFFER];
120 char ldap_search_attr[CNF_BUFFER];
121 char ldap_mail_attr[CNF_BUFFER];
122 char default_domain[CNF_BUFFER];
123 #endif /* USE_LDAP_MAIL_LOOKUP */
126 struct offenderlist {
131 struct offenderlist *next;
134 typedef struct quotatable {
144 static int qtab_i = 0, fmt = -1, flags;
145 static char maildev[CNF_BUFFER];
146 static struct quota_handle *maildev_handle;
147 static char *configfile = WARNQUOTA_CONF, *quotatabfile = QUOTATAB, *adminsfile = ADMINSFILE;
149 static char *hostname, *domainname;
150 static quotatable_t *quotatable;
151 static int adminscnt, adminsalloc;
152 static struct adminstable *adminstable;
155 * Global pointers to list.
157 static struct offenderlist *offenders = (struct offenderlist *)0;
160 * add any cleanup functions here
162 static void wc_exit(int ex_stat)
164 #ifdef USE_LDAP_MAIL_LOOKUP
167 ldap_unbind_ext(ldapconn, NULL, NULL);
169 ldap_unbind(ldapconn);
175 #ifdef USE_LDAP_MAIL_LOOKUP
176 #ifdef NEED_LDAP_PERROR
177 static void ldap_perror(LDAP *ld, LDAP_CONST char *s)
181 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &err);
182 errstr(_("%s: %s\n"), s, ldap_err2string(err));
186 static int setup_ldap(struct configparams *config)
190 struct berval cred = { .bv_val = config->ldap_bindpw,
191 .bv_len = strlen(config->ldap_bindpw) };
195 ldap_initialize(&ldapconn, config->ldap_uri);
197 ldapconn = ldap_init(config->ldap_host, config->ldap_port);
200 if(ldapconn == NULL) {
201 ldap_perror(ldapconn, "ldap_init");
206 ret = ldap_sasl_bind_s(ldapconn, config->ldap_binddn, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
208 ret = ldap_bind_s(ldapconn, config->ldap_binddn, config->ldap_bindpw, LDAP_AUTH_SIMPLE);
211 ldap_perror(ldapconn, "ldap_bind");
219 static struct offenderlist *add_offender(int type, int id, char *name)
221 struct offenderlist *offender;
222 char namebuf[MAXNAMELEN];
225 if (id2name(id, type, namebuf)) {
226 errstr(_("Cannot get name for uid/gid %u.\n"), id);
231 offender = (struct offenderlist *)smalloc(sizeof(struct offenderlist));
232 offender->offender_type = type;
233 offender->offender_id = id;
234 offender->offender_name = sstrdup(name);
235 offender->usage = (struct usage *)NULL;
236 offender->next = offenders;
237 offenders = offender;
241 static void add_offence(struct dquot *dquot, char *name)
243 struct offenderlist *lptr;
246 for (lptr = offenders; lptr; lptr = lptr->next)
247 if (dquot->dq_h->qh_type == lptr->offender_type && lptr->offender_id == dquot->dq_id)
251 if (!(lptr = add_offender(dquot->dq_h->qh_type, dquot->dq_id, name)))
254 usage = (struct usage *)smalloc(sizeof(struct usage));
255 memcpy(&usage->dq_dqb, &dquot->dq_dqb, sizeof(struct util_dqblk));
257 usage->devicename = sstrdup(dquot->dq_h->qh_quotadev);
261 usage->next = lptr->usage;
265 static int deliverable(struct dquot *dquot)
268 struct dquot *mdquot;
275 if (!strcasecmp(maildev, "any") &&
276 ((dquot->dq_dqb.dqb_bhardlimit && toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit)
277 || ((dquot->dq_dqb.dqb_bsoftlimit && toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bsoftlimit)
278 && (dquot->dq_dqb.dqb_btime && dquot->dq_dqb.dqb_btime <= now))))
282 mdquot = maildev_handle->qh_ops->read_dquot(maildev_handle, dquot->dq_id);
284 ((mdquot->dq_dqb.dqb_bhardlimit && toqb(mdquot->dq_dqb.dqb_curspace) >= mdquot->dq_dqb.dqb_bhardlimit)
285 || ((mdquot->dq_dqb.dqb_bsoftlimit && toqb(mdquot->dq_dqb.dqb_curspace) >= mdquot->dq_dqb.dqb_bsoftlimit)
286 && (mdquot->dq_dqb.dqb_btime && mdquot->dq_dqb.dqb_btime <= now)))) {
294 static int check_offence(struct dquot *dquot, char *name)
296 if ((dquot->dq_dqb.dqb_bsoftlimit && toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bsoftlimit)
297 || (dquot->dq_dqb.dqb_isoftlimit && dquot->dq_dqb.dqb_curinodes >= dquot->dq_dqb.dqb_isoftlimit)) {
298 if(deliverable(dquot))
299 add_offence(dquot, name);
304 static FILE *run_mailer(char *command)
309 if (pipe(pipefd) < 0) {
310 errstr(_("Cannot create pipe: %s\n"), strerror(errno));
313 signal(SIGPIPE, SIG_IGN);
316 errstr(_("Cannot fork: %s\n"), strerror(errno));
320 if (dup2(pipefd[0], 0) < 0) {
321 errstr(_("Cannot duplicate descriptor: %s\n"), strerror(errno));
324 execl(SHELL, SHELL, "-c", command, NULL);
325 errstr(_("Cannot execute '%s': %s\n"), command, strerror(errno));
329 if (!(f = fdopen(pipefd[1], "w")))
330 errstr(_("Cannot open pipe: %s\n"), strerror(errno));
335 static int admin_name_cmp(const void *key, const void *mem)
337 return strcmp(key, ((struct adminstable *)mem)->grpname);
340 static int should_cc(struct offenderlist *offender, struct configparams *config)
343 struct util_dqblk *dqb;
346 if (config->cc_before == -1)
349 for (lptr = offender->usage; lptr; lptr = lptr->next) {
351 if (dqb->dqb_bsoftlimit && dqb->dqb_bsoftlimit <= toqb(dqb->dqb_curspace) && dqb->dqb_btime-config->cc_before <= atime)
353 if (dqb->dqb_isoftlimit && dqb->dqb_isoftlimit <= dqb->dqb_curinodes && dqb->dqb_itime-config->cc_before <= atime)
359 /* Substitute %s and %i for 'name' and %h for hostname */
360 static void format_print(FILE *fp, char *fmt, char *name)
362 char *ch, *lastch = fmt;
364 for (ch = strchr(fmt, '%'); ch; lastch = ch+2, ch = strchr(ch+2, '%')) {
377 fputs(domainname, fp);
387 static int mail_user(struct offenderlist *offender, struct configparams *config)
392 char timebuf[MAXTIMELEN];
393 char numbuf[3][MAXNUMLEN];
394 struct util_dqblk *dqb;
396 #ifdef USE_LDAP_MAIL_LOOKUP
398 LDAPMessage *result, *entry;
399 BerElement *ber = NULL;
400 struct berval **bvals = NULL;
405 if (offender->offender_type == USRQUOTA) {
406 #ifdef USE_LDAP_MAIL_LOOKUP
407 if(config->use_ldap_mail != 0) {
408 if((ldapconn == NULL) && (config->ldap_is_setup == 0)) {
410 if(setup_ldap(config)) {
411 errstr(_("Could not setup ldap connection, returning.\n"));
414 config->ldap_is_setup = 1;
417 if(ldapconn == NULL) {
418 /* ldap was never setup correctly so just use the offender_name */
419 to = sstrdup(offender->offender_name);
421 /* search for the offender_name in ldap */
422 snprintf(searchbuf, 256, "(%s=%s)", config->ldap_search_attr,
423 offender->offender_name);
425 ret = ldap_search_ext_s(ldapconn, config->ldap_basedn,
426 LDAP_SCOPE_SUBTREE, searchbuf,
427 NULL, 0, NULL, NULL, NULL, 0, &result);
429 ret = ldap_search_s(ldapconn, config->ldap_basedn,
430 LDAP_SCOPE_SUBTREE, searchbuf,
434 errstr(_("Error with %s.\n"), offender->offender_name);
435 ldap_perror(ldapconn, "ldap_search");
439 cnt = ldap_count_entries(ldapconn, result);
442 errstr(_("Multiple entries found for client %s (%d). Not sending mail.\n"),
443 offender->offender_name, cnt);
445 } else if(cnt == 0) {
446 errstr(_("Entry not found for client %s. Not sending mail.\n"),
447 offender->offender_name);
451 entry = ldap_first_entry(ldapconn, result);
452 for(a = ldap_first_attribute(ldapconn, entry, &ber); a != NULL;
453 a = ldap_next_attribute( ldapconn, entry, ber)) {
454 if(strcasecmp(a, config->ldap_mail_attr) == 0) {
455 bvals = ldap_get_values_len(ldapconn, entry, a);
457 errstr(_("Could not get values for %s.\n"),
458 offender->offender_name);
461 to = sstrdup(bvals[0]->bv_val);
469 * use just the name and default domain as we didn't find the
470 * attribute we wanted in this entry
472 to = malloc(strlen(offender->offender_name)+
473 strlen(config->default_domain)+1);
474 sprintf(to, "%s@%s", offender->offender_name,
475 config->default_domain);
480 to = sstrdup(offender->offender_name);
483 to = sstrdup(offender->offender_name);
486 struct adminstable *admin;
488 if (!(admin = bsearch(offender->offender_name, adminstable, adminscnt, sizeof(struct adminstable), admin_name_cmp))) {
489 errstr(_("Administrator for a group %s not found. Cancelling mail.\n"), offender->offender_name);
492 to = sstrdup(admin->adminname);
494 if (!(fp = run_mailer(config->mail_cmd))) {
499 fprintf(fp, "From: %s\n", config->from);
500 fprintf(fp, "Reply-To: %s\n", config->support);
501 fprintf(fp, "Subject: %s\n", config->subject);
502 fprintf(fp, "To: %s\n", to);
503 if (should_cc(offender, config))
504 fprintf(fp, "Cc: %s\n", config->cc_to);
505 if ((config->charset)[0] != '\0') { /* are we supposed to set the encoding */
506 fprintf(fp, "Content-Type: text/plain; charset=%s\n", config->charset);
507 fprintf(fp, "Content-Disposition: inline\n");
508 fprintf(fp, "Content-Transfer-Encoding: 8bit\n");
513 if (offender->offender_type == USRQUOTA)
514 if (config->user_message)
515 format_print(fp, config->user_message, offender->offender_name);
517 fputs(DEF_USER_MESSAGE, fp);
519 if (config->group_message)
520 format_print(fp, config->group_message, offender->offender_name);
522 fprintf(fp, DEF_GROUP_MESSAGE, offender->offender_name);
524 if (!(flags & FL_NODETAILS)) {
525 for (lptr = offender->usage; lptr; lptr = lptr->next) {
527 for (cnt = 0; cnt < qtab_i; cnt++)
528 if (!strcmp(quotatable[cnt].devname, lptr->devicename)) {
529 fprintf(fp, "\n%s (%s)\n", quotatable[cnt].devdesc, quotatable[cnt].devname);
532 if (cnt == qtab_i) /* Description not found? */
533 fprintf(fp, "\n%s\n", lptr->devicename);
534 fprintf(fp, _("\n Block limits File limits\n"));
535 fprintf(fp, _("Filesystem used soft hard grace used soft hard grace\n"));
536 if (strlen(lptr->devicename) > 15)
537 fprintf(fp, "%s\n%15s", lptr->devicename, "");
539 fprintf(fp, "%-15s", lptr->devicename);
540 if (dqb->dqb_bsoftlimit && dqb->dqb_bsoftlimit <= toqb(dqb->dqb_curspace))
541 difftime2str(dqb->dqb_btime, timebuf);
544 space2str(toqb(dqb->dqb_curspace), numbuf[0], flags & FL_SHORTNUMS);
545 space2str(dqb->dqb_bsoftlimit, numbuf[1], flags & FL_SHORTNUMS);
546 space2str(dqb->dqb_bhardlimit, numbuf[2], flags & FL_SHORTNUMS);
547 fprintf(fp, "%c%c %7s %7s %7s %6s",
548 dqb->dqb_bsoftlimit && toqb(dqb->dqb_curspace) >= dqb->dqb_bsoftlimit ? '+' : '-',
549 dqb->dqb_isoftlimit && dqb->dqb_curinodes >= dqb->dqb_isoftlimit ? '+' : '-',
550 numbuf[0], numbuf[1], numbuf[2], timebuf);
551 if (dqb->dqb_isoftlimit && dqb->dqb_isoftlimit <= dqb->dqb_curinodes)
552 difftime2str(dqb->dqb_itime, timebuf);
555 number2str(dqb->dqb_curinodes, numbuf[0], flags & FL_SHORTNUMS);
556 number2str(dqb->dqb_isoftlimit, numbuf[1], flags & FL_SHORTNUMS);
557 number2str(dqb->dqb_ihardlimit, numbuf[2], flags & FL_SHORTNUMS);
558 fprintf(fp, " %7s %5s %5s %6s\n\n", numbuf[0], numbuf[1], numbuf[2], timebuf);
563 if (offender->offender_type == USRQUOTA)
564 if (config->user_signature)
565 format_print(fp, config->user_signature, offender->offender_name);
567 fprintf(fp, DEF_USER_SIGNATURE, config->support, config->phone);
569 if (config->group_signature)
570 format_print(fp, config->group_signature, offender->offender_name);
572 fprintf(fp, DEF_GROUP_SIGNATURE, config->support, config->phone);
574 if (wait(&status) < 0) /* Wait for mailer */
575 errstr(_("Cannot wait for mailer: %s\n"), strerror(errno));
576 else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
577 errstr(_("Warning: Mailer exitted abnormally.\n"));
582 static int mail_to_offenders(struct configparams *config)
584 struct offenderlist *lptr;
590 for (lptr = offenders; lptr; lptr = lptr->next)
591 ret |= mail_user(lptr, config);
596 * Wipe spaces, tabs, quotes and newlines from beginning and end of string
598 static void stripstring(char **buff)
602 /* first put a \0 at the tight place to end the string */
603 for (i = strlen(*buff) - 1; i >= 0 && (isspace((*buff)[i]) || (*buff)[i] == '"'
604 || (*buff)[i] == '\''); i--);
607 /* then determine the position to start */
608 for (i = 0; (*buff)[i] && (isspace((*buff)[i]) || (*buff)[i] == '"' || (*buff)[i] == '\''); i++);
613 * Substitute '|' with end of lines
615 static void create_eoln(char *buf)
619 while ((colpos = strchr(colpos, '|')))
624 * Read /etc/quotatab (description of devices for users)
626 static int get_quotatable(void)
629 char buffer[IOBUF_SIZE], *colpos, *devname, *devdesc;
633 if (!(fp = fopen(quotatabfile, "r"))) {
634 errstr(_("Cannot open %s: %s\nWill use device names.\n"), quotatabfile, strerror(errno));
640 for (qtab_i = 0; quotatable = srealloc(quotatable, sizeof(quotatable_t) * (qtab_i + 1)),
641 fgets(buffer, sizeof(buffer), fp); qtab_i++) {
643 quotatable[qtab_i].devname = NULL;
644 quotatable[qtab_i].devdesc = NULL;
645 if (buffer[0] == '#' || buffer[0] == ';') { /* Comment? */
650 for (colpos = buffer; isspace(*colpos); colpos++);
656 if (!(colpos = strchr(buffer, ':'))) {
657 errstr(_("Cannot parse line %d in quotatab (missing ':')\n"), line);
664 stripstring(&devname);
665 stripstring(&devdesc);
666 quotatable[qtab_i].devname = sstrdup(devname);
667 quotatable[qtab_i].devdesc = sstrdup(devdesc);
668 create_eoln(quotatable[qtab_i].devdesc);
670 if (stat(quotatable[qtab_i].devname, &st) < 0)
671 errstr(_("Cannot stat device %s (maybe typo in quotatab)\n"), quotatable[qtab_i].devname);
677 /* Check correctness of the given format */
678 static void verify_format(char *fmt, char *varname)
682 for (ch = strchr(fmt, '%'); ch; ch = strchr(ch+2, '%')) {
691 die(1, _("Incorrect format string for variable %s.\n\
692 Unrecognized expression %%%c.\n"), varname, *(ch+1));
698 * Reads config parameters from configfile
699 * uses default values if errstr occurs
701 static int readconfigfile(const char *filename, struct configparams *config)
704 char buff[IOBUF_SIZE];
708 int line, len, bufpos;
710 /* set default values */
711 sstrncpy(config->mail_cmd, MAIL_CMD, CNF_BUFFER);
712 sstrncpy(config->from, FROM, CNF_BUFFER);
713 sstrncpy(config->subject, SUBJECT, CNF_BUFFER);
714 sstrncpy(config->cc_to, CC_TO, CNF_BUFFER);
715 sstrncpy(config->support, SUPPORT, CNF_BUFFER);
716 sstrncpy(config->phone, PHONE, CNF_BUFFER);
717 (config->charset)[0] = '\0';
719 config->user_signature = config->user_message = config->group_signature = config->group_message = NULL;
720 config->use_ldap_mail = 0;
721 config->cc_before = -1;
723 #ifdef USE_LDAP_MAIL_LOOKUP
724 config->ldap_port = config->ldap_is_setup = 0;
725 config->ldap_host[0] = 0;
726 config->ldap_uri[0] = 0;
729 if (!(fp = fopen(filename, "r"))) {
730 errstr(_("Cannot open %s: %s\n"), filename, strerror(errno));
736 while (fgets(buff + bufpos, sizeof(buff) - bufpos, fp)) { /* start reading lines */
740 /* check for comments or empty lines */
741 if (buff[0] == '#' || buff[0] == ';')
744 for (pos = buff; isspace(*pos); pos++);
745 if (!*pos) /* Nothing else was on the line */
748 len = bufpos + strlen(buff+bufpos);
749 if (buff[len-1] != '\n')
750 errstr(_("Line %d too long. Truncating.\n"), line);
753 if (buff[len-1] == '\\') { /* Should join with next line? */
761 /* check for a '=' char */
762 if ((pos = strchr(buff, '='))) {
763 *pos = 0; /* split buff in two parts: var and value */
770 /* check if var matches anything */
771 if (!strcmp(var, "MAIL_CMD"))
772 sstrncpy(config->mail_cmd, value, CNF_BUFFER);
773 else if (!strcmp(var, "FROM"))
774 sstrncpy(config->from, value, CNF_BUFFER);
775 else if (!strcmp(var, "SUBJECT"))
776 sstrncpy(config->subject, value, CNF_BUFFER);
777 else if (!strcmp(var, "CC_TO"))
778 sstrncpy(config->cc_to, value, CNF_BUFFER);
779 else if (!strcmp(var, "SUPPORT"))
780 sstrncpy(config->support, value, CNF_BUFFER);
781 else if (!strcmp(var, "PHONE"))
782 sstrncpy(config->phone, value, CNF_BUFFER);
783 else if (!strcmp(var, "CHARSET"))
784 sstrncpy(config->charset, value, CNF_BUFFER);
785 else if (!strcmp(var, "MAILDEV"))
787 sstrncpy(maildev, value, CNF_BUFFER);
788 else if (!strcmp(var, "MESSAGE")) {
789 config->user_message = sstrdup(value);
790 create_eoln(config->user_message);
791 verify_format(config->user_message, "MESSAGE");
793 else if (!strcmp(var, "SIGNATURE")) {
794 config->user_signature = sstrdup(value);
795 create_eoln(config->user_signature);
796 verify_format(config->user_signature, "SIGNATURE");
798 else if (!strcmp(var, "GROUP_MESSAGE")) {
799 config->group_message = sstrdup(value);
800 create_eoln(config->group_message);
801 verify_format(config->group_message, "GROUP_MESSAGE");
803 else if (!strcmp(var, "GROUP_SIGNATURE")) {
804 config->group_signature = sstrdup(value);
805 create_eoln(config->group_signature);
806 verify_format(config->group_signature, "GROUP_SIGNATURE");
808 else if (!strcmp(var, "LDAP_MAIL")) {
809 if(strcasecmp(value, "true") == 0)
810 config->use_ldap_mail = 1;
812 config->use_ldap_mail = 0;
814 else if (!strcmp(var, "CC_BEFORE")) {
818 if (sscanf(value, "%d%s", &num, unit) != 2)
820 if (str2timeunits(num, unit, &config->cc_before) < 0) {
822 die(1, _("Cannot parse time at CC_BEFORE variable (line %d).\n"), line);
825 #ifdef USE_LDAP_MAIL_LOOKUP
826 else if (!strcmp(var, "LDAP_HOST"))
827 sstrncpy(config->ldap_host, value, CNF_BUFFER);
828 else if (!strcmp(var, "LDAP_PORT"))
829 config->ldap_port = (int)strtol(value, NULL, 10);
830 else if (!strcmp(var, "LDAP_URI"))
831 sstrncpy(config->ldap_uri, value, CNF_BUFFER);
832 else if(!strcmp(var, "LDAP_BINDDN"))
833 sstrncpy(config->ldap_binddn, value, CNF_BUFFER);
834 else if(!strcmp(var, "LDAP_BINDPW"))
835 sstrncpy(config->ldap_bindpw, value, CNF_BUFFER);
836 else if(!strcmp(var, "LDAP_BASEDN"))
837 sstrncpy(config->ldap_basedn, value, CNF_BUFFER);
838 else if(!strcmp(var, "LDAP_SEARCH_ATTRIBUTE"))
839 sstrncpy(config->ldap_search_attr, value, CNF_BUFFER);
840 else if(!strcmp(var, "LDAP_MAIL_ATTRIBUTE"))
841 sstrncpy(config->ldap_mail_attr, value, CNF_BUFFER);
842 else if(!strcmp(var, "LDAP_DEFAULT_MAIL_DOMAIN"))
843 sstrncpy(config->default_domain, value, CNF_BUFFER);
845 else /* not matched at all */
846 errstr(_("Error in config file (line %d), ignoring\n"), line);
848 else /* no '=' char in this line */
849 errstr(_("Possible error in config file (line %d), ignoring\n"), line);
852 errstr(_("Unterminated last line, ignoring\n"));
853 #ifdef USE_LDAP_MAIL_LOOKUP
854 if (config->use_ldap_mail)
857 if (!config->ldap_uri[0]) {
858 snprintf(config->ldap_uri, CNF_BUFFER, "ldap://%s:%d", config->ldap_host, config->ldap_port);
859 errstr(_("LDAP library version >= 2.3 detected. Please use LDAP_URI instead of hostname and port.\nGenerated URI %s\n"), config->ldap_uri);
862 if (config->ldap_uri[0])
863 die(1, _("LDAP library does not support ldap_initialize() but URI is specified."));
872 static int admin_cmp(const void *a1, const void *a2)
874 return strcmp(((struct adminstable *)a1)->grpname, ((struct adminstable *)a2)->grpname);
877 /* Get administrators of the groups */
878 static int get_groupadmins(void)
882 char buffer[IOBUF_SIZE], *colpos, *grouppos, *endname, *adminpos;
884 if (!(f = fopen(adminsfile, "r"))) {
885 errstr(_("Cannot open file with group administrators: %s\n"), strerror(errno));
889 while (fgets(buffer, IOBUF_SIZE, f)) {
891 if (buffer[0] == ';' || buffer[0] == '#')
893 /* Skip initial spaces */
894 for (colpos = buffer; isspace(*colpos); colpos++);
895 if (!*colpos) /* Empty line? */
897 /* Find splitting colon */
898 for (grouppos = colpos; *colpos && *colpos != ':'; colpos++);
899 if (!*colpos || grouppos == colpos) {
900 errstr(_("Parse error at line %d. Cannot find end of group name.\n"), line);
903 /* Cut trailing spaces */
904 for (endname = colpos-1; isspace(*endname); endname--);
906 /* Skip initial spaces at admins name */
907 for (colpos++; isspace(*colpos); colpos++);
909 errstr(_("Parse error at line %d. Cannot find administrators name.\n"), line);
912 /* Go through admins name */
913 for (adminpos = colpos; !isspace(*colpos); colpos++);
914 if (*colpos) { /* Some characters after name? */
916 /* Skip trailing spaces */
917 for (colpos++; isspace(*colpos); colpos++);
919 errstr(_("Parse error at line %d. Trailing characters after administrators name.\n"), line);
923 if (adminscnt >= adminsalloc)
924 adminstable = srealloc(adminstable, sizeof(struct adminstable)*(adminsalloc+=ADMIN_TAB_ALLOC));
925 adminstable[adminscnt].grpname = sstrdup(grouppos);
926 adminstable[adminscnt++].adminname = sstrdup(adminpos);
930 qsort(adminstable, adminscnt, sizeof(struct adminstable), admin_cmp);
934 static struct quota_handle *find_handle_dev(char *dev, struct quota_handle **handles)
938 for (i = 0; handles[i] && strcmp(dev, handles[i]->qh_quotadev); i++);
942 static void warn_quota(int fs_count, char **fs)
944 struct quota_handle **handles;
945 struct configparams config;
948 if (readconfigfile(configfile, &config) < 0)
950 if (get_quotatable() < 0)
953 if (flags & FL_USER) {
954 handles = create_handle_list(fs_count, fs, USRQUOTA, -1, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
955 if (!maildev[0] || !strcasecmp(maildev, "any"))
956 maildev_handle = NULL;
958 maildev_handle = find_handle_dev(maildev, handles);
959 for (i = 0; handles[i]; i++)
960 handles[i]->qh_ops->scan_dquots(handles[i], check_offence);
961 dispose_handle_list(handles);
963 if (flags & FL_GROUP) {
964 if (get_groupadmins() < 0)
966 handles = create_handle_list(fs_count, fs, GRPQUOTA, -1, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
967 if (!maildev[0] || !strcasecmp(maildev, "any"))
968 maildev_handle = NULL;
970 maildev_handle = find_handle_dev(maildev, handles);
971 for (i = 0; handles[i]; i++)
972 handles[i]->qh_ops->scan_dquots(handles[i], check_offence);
973 dispose_handle_list(handles);
975 if (mail_to_offenders(&config) < 0)
979 /* Print usage information */
980 static void usage(void)
982 errstr(_("Usage:\n warnquota [-ugsid] [-F quotaformat] [-c configfile] [-q quotatabfile] [-a adminsfile] [filesystem...]\n\n\
983 -u, --user warn users\n\
984 -g, --group warn groups\n\
985 -s, --human-readable send information in more human friendly units\n\
986 -i, --no-autofs avoid autofs mountpoints\n\
987 -d, --no-details do not send quota information itself\n\
988 -F, --format=formatname use quotafiles of specific format\n\
989 -c, --config=config-file non-default config file\n\
990 -q, --quota-tab=quotatab-file non-default quotatab\n\
991 -a, --admins-file=admins-file non-default admins file\n\
992 -h, --help display this help message and exit\n\
993 -v, --version display version information and exit\n\n"));
994 fprintf(stderr, _("Bugs to %s\n"), MY_EMAIL);
998 static void parse_options(int argcnt, char **argstr)
1001 struct option long_opts[] = {
1002 { "user", 0, NULL, 'u' },
1003 { "group", 0, NULL, 'g' },
1004 { "version", 0, NULL, 'V' },
1005 { "help", 0, NULL, 'h' },
1006 { "format", 1, NULL, 'F' },
1007 { "config", 1, NULL, 'c' },
1008 { "quota-tab", 1, NULL, 'q' },
1009 { "admins-file", 1, NULL, 'a' },
1010 { "no-autofs", 0, NULL, 'i' },
1011 { "human-readable", 0, NULL, 's' },
1012 { "no-details", 0, NULL, 'd' },
1013 { NULL, 0, NULL, 0 }
1016 while ((ret = getopt_long(argcnt, argstr, "ugVF:hc:q:a:isd", long_opts, NULL)) != -1) {
1025 if ((fmt = name2fmt(optarg)) == QF_ERROR)
1029 configfile = optarg;
1032 quotatabfile = optarg;
1035 adminsfile = optarg;
1044 flags |= FL_NOAUTOFS;
1047 flags |= FL_SHORTNUMS;
1050 flags |= FL_NODETAILS;
1054 if (!(flags & FL_USER) && !(flags & FL_GROUP))
1058 static void get_host_name(void)
1063 die(1, _("Cannot get host name: %s\n"), strerror(errno));
1064 hostname = sstrdup(uts.nodename);
1065 domainname = sstrdup(uts.domainname);
1068 int main(int argc, char **argv)
1071 progname = basename(argv[0]);
1074 parse_options(argc, argv);
1075 init_kernel_interface();
1076 warn_quota(argc - optind, argc > optind ? argv + optind : NULL);