/* $NetBSD: repquota.c,v 1.45 2015/06/16 23:04:14 christos Exp $ */ /* * Copyright (c) 1980, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)repquota.c 8.2 (Berkeley) 11/22/94"; #else __RCSID("$NetBSD: repquota.c,v 1.45 2015/06/16 23:04:14 christos Exp $"); #endif #endif /* not lint */ /* * Quota report */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "printquota.h" /* * XXX. Ideally we shouldn't compile either of these in, but it's a * nontrivial rework to avoid it and it'll work ok for now. */ #define REPQUOTA_NUMIDTYPES 2 #define REPQUOTA_NUMOBJTYPES 2 struct fileusage { struct fileusage *fu_next; struct quotaval fu_qv[REPQUOTA_NUMOBJTYPES]; uint32_t fu_id; char fu_name[1]; /* actually bigger */ }; #define FUHASH 1024 /* must be power of two */ static struct fileusage *fuhead[REPQUOTA_NUMIDTYPES][FUHASH]; /* highest addid()'ed identifier per idtype */ static uint32_t highid[REPQUOTA_NUMIDTYPES]; int valid[REPQUOTA_NUMIDTYPES]; static struct quotaval defaultqv[REPQUOTA_NUMIDTYPES][REPQUOTA_NUMOBJTYPES]; static int vflag = 0; /* verbose */ static int aflag = 0; /* all file systems */ static int hflag = 0; /* humanize */ static int xflag = 0; /* export */ /* * XXX this should go away and be replaced with a call to * quota_idtype_getname(), but that needs a quotahandle and requires * the same nontrivial rework as getting rid of REPQUOTA_NUMIDTYPES. */ static const char *const repquota_idtype_names[REPQUOTA_NUMIDTYPES] = { "user", "group", }; static struct fileusage *addid(uint32_t, int, const char *); static struct fileusage *lookup(uint32_t, int); static struct fileusage *qremove(uint32_t, int); static int repquota(struct quotahandle *, int); static void usage(void) __attribute__((__noreturn__)); static void printquotas(int, struct quotahandle *); static void exportquotas(void); static int oneof(const char *, char *[], int cnt); static int isover(struct quotaval *qv, time_t now); int main(int argc, char **argv) { int gflag = 0, uflag = 0, errs = 0; long i, argnum, done = 0; int ch; struct statvfs *fst; int nfst; struct quotahandle *qh; if (!strcmp(getprogname(), "quotadump")) { xflag = 1; } while ((ch = getopt(argc, argv, "aguhvx")) != -1) { switch(ch) { case 'a': aflag++; break; case 'g': gflag++; break; case 'u': uflag++; break; case 'h': hflag++; break; case 'v': vflag++; break; case 'x': xflag++; break; default: usage(); } } argc -= optind; argv += optind; if (xflag && (argc != 1 || aflag)) usage(); if (argc == 0 && !aflag) usage(); if (!gflag && !uflag) { if (aflag) gflag++; uflag++; } nfst = getmntinfo(&fst, MNT_WAIT); if (nfst == 0) errx(1, "no filesystems mounted!"); for (i = 0; i < nfst; i++) { if ((fst[i].f_flag & ST_QUOTA) == 0) continue; /* check if we want this volume */ if (!aflag) { argnum = oneof(fst[i].f_mntonname, argv, argc); if (argnum < 0) { argnum = oneof(fst[i].f_mntfromname, argv, argc); } if (argnum < 0) { continue; } done |= 1U << argnum; } qh = quota_open(fst[i].f_mntonname); if (qh == NULL) { /* XXX: check this errno */ if (errno == EOPNOTSUPP || errno == ENXIO) { continue; } warn("%s: quota_open", fst[i].f_mntonname); continue; } if (gflag) errs += repquota(qh, QUOTA_IDTYPE_GROUP); if (uflag) errs += repquota(qh, QUOTA_IDTYPE_USER); quota_close(qh); } if (xflag) exportquotas(); for (i = 0; i < argc; i++) if ((done & (1U << i)) == 0) warnx("%s not mounted", argv[i]); return errs; } static void usage(void) { const char *p = getprogname(); fprintf(stderr, "usage: %s [-D] [-v] [-g] [-u] -a\n" "\t%s [-D] [-v] [-g] [-u] filesys ...\n" "\t%s -x [-D] [-g] [-u] filesys\n", p, p, p); exit(1); } static int repquota(struct quotahandle *qh, int idtype) { struct quotacursor *qc; struct quotakey qk; struct quotaval qv; struct quotaval *qvp; struct fileusage *fup; qc = quota_opencursor(qh); if (qc == NULL) { return 1; } if (idtype == QUOTA_IDTYPE_USER) { quotacursor_skipidtype(qc, QUOTA_IDTYPE_GROUP); } if (idtype == QUOTA_IDTYPE_GROUP) { quotacursor_skipidtype(qc, QUOTA_IDTYPE_USER); } valid[idtype] = 0; while (!quotacursor_atend(qc)) { if (quotacursor_get(qc, &qk, &qv)) { err(1, "%s: quotacursor_get", quota_getmountpoint(qh)); } if (qk.qk_idtype != idtype) { continue; } valid[idtype] = 1; if (qk.qk_id == QUOTA_DEFAULTID) { qvp = defaultqv[idtype]; } else { if ((fup = lookup(qk.qk_id, idtype)) == 0) fup = addid(qk.qk_id, idtype, (char *)0); qvp = fup->fu_qv; } if (qk.qk_objtype == QUOTA_OBJTYPE_BLOCKS) { qvp[QUOTA_OBJTYPE_BLOCKS] = qv; } else if (qk.qk_objtype == QUOTA_OBJTYPE_FILES) { qvp[QUOTA_OBJTYPE_FILES] = qv; } } if (xflag == 0 && valid[idtype]) printquotas(idtype, qh); return 0; } static void printquotas(int idtype, struct quotahandle *qh) { static int multiple = 0; uint32_t id; int i; struct fileusage *fup; struct quotaval *q; const char *timemsg[REPQUOTA_NUMOBJTYPES]; char overchar[REPQUOTA_NUMOBJTYPES]; time_t now; char b0[2][20], b1[20], b2[20], b3[20]; int ok, objtype; int isbytes, width; switch (idtype) { case QUOTA_IDTYPE_GROUP: { struct group *gr; setgrent(); while ((gr = getgrent()) != 0) (void)addid(gr->gr_gid, idtype, gr->gr_name); endgrent(); break; } case QUOTA_IDTYPE_USER: { struct passwd *pw; setpwent(); while ((pw = getpwent()) != 0) (void)addid(pw->pw_uid, idtype, pw->pw_name); endpwent(); break; } default: errx(1, "Unknown quota ID type %d", idtype); } time(&now); if (multiple++) printf("\n"); if (vflag) printf("*** Report for %s quotas on %s (%s: %s)\n", repquota_idtype_names[idtype], quota_getmountpoint(qh), quota_getmountdevice(qh), quota_getimplname(qh)); printf(" Block limits " "File limits\n"); printf(idtype == QUOTA_IDTYPE_USER ? "User " : "Group"); printf(" used soft hard grace used" " soft hard grace\n"); for (id = 0; id <= highid[idtype]; id++) { fup = qremove(id, idtype); q = fup->fu_qv; if (fup == 0) continue; for (i = 0; i < REPQUOTA_NUMOBJTYPES; i++) { if (isover(&q[i], now)) { timemsg[i] = timeprt(b0[i], 8, now, q[i].qv_expiretime); overchar[i] = '+'; } else { if (vflag && q[i].qv_grace != QUOTA_NOTIME) { timemsg[i] = timeprt(b0[i], 8, 0, q[i].qv_grace); } else { timemsg[i] = ""; } overchar[i] = '-'; } } ok = 1; for (objtype = 0; objtype < REPQUOTA_NUMOBJTYPES; objtype++) { if (q[objtype].qv_usage != 0 || overchar[objtype] != '-') { ok = 0; } } if (ok && vflag == 0) continue; if (strlen(fup->fu_name) > 9) printf("%s ", fup->fu_name); else printf("%-10s", fup->fu_name); for (objtype = 0; objtype < REPQUOTA_NUMOBJTYPES; objtype++) { printf("%c", overchar[objtype]); } for (objtype = 0; objtype < REPQUOTA_NUMOBJTYPES; objtype++) { isbytes = quota_objtype_isbytes(qh, objtype); width = isbytes ? 9 : 8; printf("%*s%*s%*s%7s", width, intprt(b1, width+1, q[objtype].qv_usage, isbytes ? HN_B : 0, hflag), width, intprt(b2, width+1, q[objtype].qv_softlimit, isbytes ? HN_B : 0, hflag), width, intprt(b3, width+1, q[objtype].qv_hardlimit, isbytes ? HN_B : 0, hflag), timemsg[objtype]); if (objtype + 1 < REPQUOTA_NUMOBJTYPES) { printf(" "); } else { printf("\n"); } } free(fup); } } static void exportquotaval(const struct quotaval *qv) { if (qv->qv_hardlimit == QUOTA_NOLIMIT) { printf(" -"); } else { printf(" %llu", (unsigned long long)qv->qv_hardlimit); } if (qv->qv_softlimit == QUOTA_NOLIMIT) { printf(" -"); } else { printf(" %llu", (unsigned long long)qv->qv_softlimit); } printf(" %llu", (unsigned long long)qv->qv_usage); if (qv->qv_expiretime == QUOTA_NOTIME) { printf(" -"); } else { printf(" %lld", (long long)qv->qv_expiretime); } if (qv->qv_grace == QUOTA_NOTIME) { printf(" -"); } else { printf(" %lld", (long long)qv->qv_grace); } } static void exportquotas(void) { int idtype; id_t id; struct fileusage *fup; /* header */ printf("@format netbsd-quota-dump v1\n"); printf("# idtype id objtype hard soft usage expire grace\n"); for (idtype = 0; idtype < REPQUOTA_NUMIDTYPES; idtype++) { if (valid[idtype] == 0) continue; printf("%s default block ", repquota_idtype_names[idtype]); exportquotaval(&defaultqv[idtype][QUOTA_OBJTYPE_BLOCKS]); printf("\n"); printf("%s default file ", repquota_idtype_names[idtype]); exportquotaval(&defaultqv[idtype][QUOTA_OBJTYPE_FILES]); printf("\n"); for (id = 0; id <= highid[idtype]; id++) { fup = qremove(id, idtype); if (fup == 0) continue; printf("%s %u block ", repquota_idtype_names[idtype], id); exportquotaval(&fup->fu_qv[QUOTA_OBJTYPE_BLOCKS]); printf("\n"); printf("%s %u file ", repquota_idtype_names[idtype], id); exportquotaval(&fup->fu_qv[QUOTA_OBJTYPE_FILES]); printf("\n"); free(fup); } } printf("@end\n"); } /* * Routines to manage the file usage table. * * Lookup an id of a specific id type. */ struct fileusage * lookup(uint32_t id, int idtype) { struct fileusage *fup; for (fup = fuhead[idtype][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) if (fup->fu_id == id) return fup; return NULL; } /* * Lookup and remove an id of a specific id type. */ static struct fileusage * qremove(uint32_t id, int idtype) { struct fileusage *fup, **fupp; for (fupp = &fuhead[idtype][id & (FUHASH-1)]; *fupp != 0;) { fup = *fupp; if (fup->fu_id == id) { *fupp = fup->fu_next; return fup; } fupp = &fup->fu_next; } return NULL; } /* * Add a new file usage id if it does not already exist. */ static struct fileusage * addid(uint32_t id, int idtype, const char *name) { struct fileusage *fup, **fhp; struct group *gr = NULL; struct passwd *pw = NULL; size_t len; if ((fup = lookup(id, idtype)) != NULL) { return fup; } if (name == NULL) { switch(idtype) { case QUOTA_IDTYPE_GROUP: gr = getgrgid(id); if (gr != NULL) name = gr->gr_name; break; case QUOTA_IDTYPE_USER: pw = getpwuid(id); if (pw) name = pw->pw_name; break; default: errx(1, "Unknown quota ID type %d", idtype); } } if (name) len = strlen(name); else len = 10; if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) err(1, "out of memory for fileusage structures"); fhp = &fuhead[idtype][id & (FUHASH - 1)]; fup->fu_next = *fhp; *fhp = fup; fup->fu_id = id; if (id > highid[idtype]) highid[idtype] = id; if (name) { memmove(fup->fu_name, name, len + 1); } else { snprintf(fup->fu_name, len + 1, "%u", id); } /* * XXX nothing guarantees the default limits have been loaded yet */ fup->fu_qv[QUOTA_OBJTYPE_BLOCKS] = defaultqv[idtype][QUOTA_OBJTYPE_BLOCKS]; fup->fu_qv[QUOTA_OBJTYPE_FILES] = defaultqv[idtype][QUOTA_OBJTYPE_FILES]; return fup; } /* * Check to see if target appears in list of size cnt. */ static int oneof(const char *target, char *list[], int cnt) { int i; for (i = 0; i < cnt; i++) if (strcmp(target, list[i]) == 0) return i; return -1; } static int isover(struct quotaval *qv, time_t now) { return (qv->qv_usage >= qv->qv_hardlimit || qv->qv_usage >= qv->qv_softlimit); }