11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1990, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * Robert Elz at The University of Melbourne. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 4. Neither the name of the University nor the names of its contributors 171590Srgrimes * may be used to endorse or promote products derived from this software 181590Srgrimes * without specific prior written permission. 191590Srgrimes * 201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301590Srgrimes * SUCH DAMAGE. 311590Srgrimes */ 321590Srgrimes 331590Srgrimes#ifndef lint 3427888Scharnierstatic const char copyright[] = 351590Srgrimes"@(#) Copyright (c) 1980, 1990, 1993\n\ 361590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3795624Smarkm#endif 381590Srgrimes 391590Srgrimes#ifndef lint 4095624Smarkmstatic const char sccsid[] = "from: @(#)quota.c 8.1 (Berkeley) 6/6/93"; 411590Srgrimes#endif /* not lint */ 421590Srgrimes 431590Srgrimes/* 441590Srgrimes * Disk quota reporting program. 451590Srgrimes */ 4695624Smarkm#include <sys/cdefs.h> 4795624Smarkm__FBSDID("$FreeBSD: stable/11/usr.bin/quota/quota.c 352576 2019-09-21 14:06:16Z hrs $"); 4895624Smarkm 491590Srgrimes#include <sys/param.h> 5013236Sgraichen#include <sys/types.h> 511590Srgrimes#include <sys/file.h> 521590Srgrimes#include <sys/stat.h> 5313236Sgraichen#include <sys/mount.h> 5413236Sgraichen#include <sys/socket.h> 5595624Smarkm 5695624Smarkm#include <rpc/rpc.h> 5795624Smarkm#include <rpc/pmap_prot.h> 5895624Smarkm#include <rpcsvc/rquota.h> 5995624Smarkm 601590Srgrimes#include <ufs/ufs/quota.h> 6195624Smarkm 6227888Scharnier#include <ctype.h> 6327888Scharnier#include <err.h> 6427888Scharnier#include <fstab.h> 6527888Scharnier#include <grp.h> 66163599Sru#include <libutil.h> 6795624Smarkm#include <netdb.h> 6827888Scharnier#include <pwd.h> 691590Srgrimes#include <stdio.h> 70169345Sdwmalone#include <stdint.h> 7113236Sgraichen#include <stdlib.h> 7213236Sgraichen#include <string.h> 73166646Smpp#include <time.h> 7416379Salex#include <unistd.h> 751590Srgrimes 76227176Sedstatic const char *qfextension[] = INITQFNAMES; 7713236Sgraichen 781590Srgrimesstruct quotause { 791590Srgrimes struct quotause *next; 801590Srgrimes long flags; 811590Srgrimes struct dqblk dqblk; 821590Srgrimes char fsname[MAXPATHLEN + 1]; 8313236Sgraichen}; 841590Srgrimes 85207736Smckusickstatic char *timeprt(int64_t seconds); 8692921Simpstatic struct quotause *getprivs(long id, int quotatype); 8795624Smarkmstatic void usage(void); 88166388Smppstatic int showuid(u_long uid); 89166388Smppstatic int showgid(u_long gid); 90166388Smppstatic int showusrname(char *name); 91166388Smppstatic int showgrpname(char *name); 92166388Smppstatic int showquotas(int type, u_long id, const char *name); 93166646Smppstatic void showrawquotas(int type, u_long id, struct quotause *qup); 9495624Smarkmstatic void heading(int type, u_long id, const char *name, const char *tag); 9595624Smarkmstatic int getufsquota(struct fstab *fs, struct quotause *qup, long id, 9695624Smarkm int quotatype); 9795624Smarkmstatic int getnfsquota(struct statfs *fst, struct quotause *qup, long id, 9895624Smarkm int quotatype); 99339008Ssefstatic enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, 10095624Smarkm xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); 10116379Salexstatic int alldigits(char *s); 10213236Sgraichen 103227176Sedstatic int hflag; 104227176Sedstatic int lflag; 105227176Sedstatic int rflag; 106227176Sedstatic int qflag; 107227176Sedstatic int vflag; 108227176Sedstatic char *filename = NULL; 1091590Srgrimes 11016379Salexint 11195624Smarkmmain(int argc, char *argv[]) 1121590Srgrimes{ 11313236Sgraichen int ngroups; 114207736Smckusick gid_t mygid, gidset[NGROUPS]; 115166388Smpp int i, ch, gflag = 0, uflag = 0, errflag = 0; 1161590Srgrimes 117166646Smpp while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) { 1181590Srgrimes switch(ch) { 119166646Smpp case 'f': 120166646Smpp filename = optarg; 121166646Smpp break; 1221590Srgrimes case 'g': 1231590Srgrimes gflag++; 1241590Srgrimes break; 125163599Sru case 'h': 126163599Sru hflag++; 127163599Sru break; 128101545Siedowse case 'l': 129101545Siedowse lflag++; 130101545Siedowse break; 131101544Siedowse case 'q': 132101544Siedowse qflag++; 133101544Siedowse break; 134166646Smpp case 'r': 135166646Smpp rflag++; 136166646Smpp break; 1371590Srgrimes case 'u': 1381590Srgrimes uflag++; 1391590Srgrimes break; 1401590Srgrimes case 'v': 1411590Srgrimes vflag++; 1421590Srgrimes break; 1431590Srgrimes default: 1441590Srgrimes usage(); 1451590Srgrimes } 1461590Srgrimes } 1471590Srgrimes argc -= optind; 1481590Srgrimes argv += optind; 1491590Srgrimes if (!uflag && !gflag) 1501590Srgrimes uflag++; 1511590Srgrimes if (argc == 0) { 1521590Srgrimes if (uflag) 153166388Smpp errflag += showuid(getuid()); 1541590Srgrimes if (gflag) { 15513236Sgraichen mygid = getgid(); 156207736Smckusick ngroups = getgroups(NGROUPS, gidset); 15727888Scharnier if (ngroups < 0) 15827888Scharnier err(1, "getgroups"); 159166388Smpp errflag += showgid(mygid); 16013236Sgraichen for (i = 0; i < ngroups; i++) 16113236Sgraichen if (gidset[i] != mygid) 162166388Smpp errflag += showgid(gidset[i]); 1631590Srgrimes } 164166388Smpp return(errflag); 1651590Srgrimes } 1661590Srgrimes if (uflag && gflag) 1671590Srgrimes usage(); 1681590Srgrimes if (uflag) { 1691590Srgrimes for (; argc > 0; argc--, argv++) { 1701590Srgrimes if (alldigits(*argv)) 171166388Smpp errflag += showuid(atoi(*argv)); 1721590Srgrimes else 173166388Smpp errflag += showusrname(*argv); 1741590Srgrimes } 175166388Smpp return(errflag); 1761590Srgrimes } 1771590Srgrimes if (gflag) { 1781590Srgrimes for (; argc > 0; argc--, argv++) { 1791590Srgrimes if (alldigits(*argv)) 180166388Smpp errflag += showgid(atoi(*argv)); 1811590Srgrimes else 182166388Smpp errflag += showgrpname(*argv); 1831590Srgrimes } 1841590Srgrimes } 185166388Smpp return(errflag); 1861590Srgrimes} 1871590Srgrimes 18816379Salexstatic void 18995624Smarkmusage(void) 1901590Srgrimes{ 1911590Srgrimes 1921590Srgrimes fprintf(stderr, "%s\n%s\n%s\n", 193166646Smpp "usage: quota [-ghlu] [-f path] [-v | -q | -r]", 194166646Smpp " quota [-hlu] [-f path] [-v | -q | -r] user ...", 195166646Smpp " quota -g [-hl] [-f path] [-v | -q | -r] group ..."); 1961590Srgrimes exit(1); 1971590Srgrimes} 1981590Srgrimes 1991590Srgrimes/* 2001590Srgrimes * Print out quotas for a specified user identifier. 2011590Srgrimes */ 202166388Smppstatic int 20395624Smarkmshowuid(u_long uid) 2041590Srgrimes{ 2051590Srgrimes struct passwd *pwd = getpwuid(uid); 20695624Smarkm const char *name; 2071590Srgrimes 2081590Srgrimes if (pwd == NULL) 2091590Srgrimes name = "(no account)"; 2101590Srgrimes else 2111590Srgrimes name = pwd->pw_name; 212166388Smpp return(showquotas(USRQUOTA, uid, name)); 2131590Srgrimes} 2141590Srgrimes 2151590Srgrimes/* 2161590Srgrimes * Print out quotas for a specifed user name. 2171590Srgrimes */ 218166388Smppstatic int 21995624Smarkmshowusrname(char *name) 2201590Srgrimes{ 2211590Srgrimes struct passwd *pwd = getpwnam(name); 2221590Srgrimes 2231590Srgrimes if (pwd == NULL) { 22427888Scharnier warnx("%s: unknown user", name); 225166388Smpp return(1); 2261590Srgrimes } 227166388Smpp return(showquotas(USRQUOTA, pwd->pw_uid, name)); 2281590Srgrimes} 2291590Srgrimes 2301590Srgrimes/* 2311590Srgrimes * Print out quotas for a specified group identifier. 2321590Srgrimes */ 233166388Smppstatic int 23495624Smarkmshowgid(u_long gid) 2351590Srgrimes{ 2361590Srgrimes struct group *grp = getgrgid(gid); 23795624Smarkm const char *name; 2381590Srgrimes 2391590Srgrimes if (grp == NULL) 2401590Srgrimes name = "(no entry)"; 2411590Srgrimes else 2421590Srgrimes name = grp->gr_name; 243166388Smpp return(showquotas(GRPQUOTA, gid, name)); 2441590Srgrimes} 2451590Srgrimes 2461590Srgrimes/* 2471590Srgrimes * Print out quotas for a specifed group name. 2481590Srgrimes */ 249166388Smppstatic int 25095624Smarkmshowgrpname(char *name) 2511590Srgrimes{ 2521590Srgrimes struct group *grp = getgrnam(name); 2531590Srgrimes 2541590Srgrimes if (grp == NULL) { 25527888Scharnier warnx("%s: unknown group", name); 256166388Smpp return(1); 2571590Srgrimes } 258166388Smpp return(showquotas(GRPQUOTA, grp->gr_gid, name)); 2591590Srgrimes} 2601590Srgrimes 26116379Salexstatic void 262207736Smckusickprthumanval(int len, u_int64_t bytes) 263163599Sru{ 264163599Sru char buf[len + 1]; 265163599Sru 266223690Spluknet /* 267223690Spluknet * Limit the width to 5 bytes as that is what users expect. 268223690Spluknet */ 269298682Saraujo humanize_number(buf, MIN(sizeof(buf), 5), bytes, "", 270298682Saraujo HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 271163599Sru 272163599Sru (void)printf(" %*s", len, buf); 273163599Sru} 274163599Sru 275166388Smppstatic int 27695624Smarkmshowquotas(int type, u_long id, const char *name) 2771590Srgrimes{ 27895624Smarkm struct quotause *qup; 27913236Sgraichen struct quotause *quplist; 28095624Smarkm const char *msgi, *msgb; 28195624Smarkm const char *nam; 282181267Sdelphij char *bgrace = NULL, *igrace = NULL; 283166388Smpp int lines = 0, overquota = 0; 2841590Srgrimes static time_t now; 2851590Srgrimes 2861590Srgrimes if (now == 0) 2871590Srgrimes time(&now); 2881590Srgrimes quplist = getprivs(id, type); 2891590Srgrimes for (qup = quplist; qup; qup = qup->next) { 290207736Smckusick msgi = NULL; 2911590Srgrimes if (qup->dqblk.dqb_ihardlimit && 292166388Smpp qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) { 293166388Smpp overquota++; 2941590Srgrimes msgi = "File limit reached on"; 295166388Smpp } 2961590Srgrimes else if (qup->dqblk.dqb_isoftlimit && 29795624Smarkm qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) { 298166388Smpp overquota++; 2991590Srgrimes if (qup->dqblk.dqb_itime > now) 3001590Srgrimes msgi = "In file grace period on"; 3011590Srgrimes else 3021590Srgrimes msgi = "Over file quota on"; 30395624Smarkm } 304207736Smckusick msgb = NULL; 3051590Srgrimes if (qup->dqblk.dqb_bhardlimit && 306166388Smpp qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) { 307166388Smpp overquota++; 3081590Srgrimes msgb = "Block limit reached on"; 309166388Smpp } 3101590Srgrimes else if (qup->dqblk.dqb_bsoftlimit && 31195624Smarkm qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) { 312166388Smpp overquota++; 3131590Srgrimes if (qup->dqblk.dqb_btime > now) 3141590Srgrimes msgb = "In block grace period on"; 3151590Srgrimes else 3161590Srgrimes msgb = "Over block quota on"; 31795624Smarkm } 318166646Smpp if (rflag) { 319166646Smpp showrawquotas(type, id, qup); 320166646Smpp continue; 321166646Smpp } 322166646Smpp if (!vflag && 323166646Smpp qup->dqblk.dqb_isoftlimit == 0 && 324166646Smpp qup->dqblk.dqb_ihardlimit == 0 && 325166646Smpp qup->dqblk.dqb_bsoftlimit == 0 && 326166646Smpp qup->dqblk.dqb_bhardlimit == 0) 327166646Smpp continue; 3281590Srgrimes if (qflag) { 329207736Smckusick if ((msgi != NULL || msgb != NULL) && 3301590Srgrimes lines++ == 0) 3311590Srgrimes heading(type, id, name, ""); 332207736Smckusick if (msgi != NULL) 3331590Srgrimes printf("\t%s %s\n", msgi, qup->fsname); 334207736Smckusick if (msgb != NULL) 3351590Srgrimes printf("\t%s %s\n", msgb, qup->fsname); 3361590Srgrimes continue; 3371590Srgrimes } 338207736Smckusick if (!vflag && 339207736Smckusick qup->dqblk.dqb_curblocks == 0 && 340207736Smckusick qup->dqblk.dqb_curinodes == 0) 3411590Srgrimes continue; 342207736Smckusick if (lines++ == 0) 343207736Smckusick heading(type, id, name, ""); 344207736Smckusick nam = qup->fsname; 345207736Smckusick if (strlen(qup->fsname) > 15) { 346207736Smckusick printf("%s\n", qup->fsname); 347207736Smckusick nam = ""; 348207736Smckusick } 349207736Smckusick printf("%-15s", nam); 350207736Smckusick if (hflag) { 351207736Smckusick prthumanval(7, dbtob(qup->dqblk.dqb_curblocks)); 352207736Smckusick printf("%c", (msgb == NULL) ? ' ' : '*'); 353207736Smckusick prthumanval(7, dbtob(qup->dqblk.dqb_bsoftlimit)); 354207736Smckusick prthumanval(7, dbtob(qup->dqblk.dqb_bhardlimit)); 355207736Smckusick } else { 356207736Smckusick printf(" %7ju%c %7ju %7ju", 357223690Spluknet (uintmax_t)dbtob(qup->dqblk.dqb_curblocks) 358223690Spluknet / 1024, 359207736Smckusick (msgb == NULL) ? ' ' : '*', 360223690Spluknet (uintmax_t)dbtob(qup->dqblk.dqb_bsoftlimit) 361223690Spluknet / 1024, 362223690Spluknet (uintmax_t)dbtob(qup->dqblk.dqb_bhardlimit) 363223690Spluknet / 1024); 3641590Srgrimes } 365207736Smckusick if (msgb != NULL) 366207736Smckusick bgrace = timeprt(qup->dqblk.dqb_btime); 367207736Smckusick if (msgi != NULL) 368207736Smckusick igrace = timeprt(qup->dqblk.dqb_itime); 369207736Smckusick printf("%8s %6ju%c %6ju %6ju%8s\n" 370207736Smckusick , (msgb == NULL) ? "" : bgrace 371207736Smckusick , (uintmax_t)qup->dqblk.dqb_curinodes 372207736Smckusick , (msgi == NULL) ? ' ' : '*' 373207736Smckusick , (uintmax_t)qup->dqblk.dqb_isoftlimit 374207736Smckusick , (uintmax_t)qup->dqblk.dqb_ihardlimit 375207736Smckusick , (msgi == NULL) ? "" : igrace 376207736Smckusick ); 377207736Smckusick if (msgb != NULL) 378207736Smckusick free(bgrace); 379207736Smckusick if (msgi != NULL) 380207736Smckusick free(igrace); 3811590Srgrimes } 382166646Smpp if (!qflag && !rflag && lines == 0) 3831590Srgrimes heading(type, id, name, "none"); 384207736Smckusick return (overquota); 3851590Srgrimes} 3861590Srgrimes 38716379Salexstatic void 388181267Sdelphijshowrawquotas(int type, u_long id, struct quotause *qup) 389166646Smpp{ 390207736Smckusick time_t t; 391207736Smckusick 392166646Smpp printf("Raw %s quota information for id %lu on %s\n", 393166646Smpp type == USRQUOTA ? "user" : "group", id, qup->fsname); 394207736Smckusick printf("block hard limit: %ju\n", 395207736Smckusick (uintmax_t)qup->dqblk.dqb_bhardlimit); 396207736Smckusick printf("block soft limit: %ju\n", 397207736Smckusick (uintmax_t)qup->dqblk.dqb_bsoftlimit); 398207736Smckusick printf("current block count: %ju\n", 399207736Smckusick (uintmax_t)qup->dqblk.dqb_curblocks); 400207736Smckusick printf("i-node hard limit: %ju\n", 401207736Smckusick (uintmax_t)qup->dqblk.dqb_ihardlimit); 402207736Smckusick printf("i-node soft limit: %ju\n", 403207736Smckusick (uintmax_t)qup->dqblk.dqb_isoftlimit); 404207736Smckusick printf("current i-node count: %ju\n", 405207736Smckusick (uintmax_t)qup->dqblk.dqb_curinodes); 406207736Smckusick printf("block grace time: %jd", 407207736Smckusick (intmax_t)qup->dqblk.dqb_btime); 408181262Scognet if (qup->dqblk.dqb_btime != 0) { 409207736Smckusick t = qup->dqblk.dqb_btime; 410207736Smckusick printf(" %s", ctime(&t)); 411207736Smckusick } else { 412166646Smpp printf("\n"); 413207736Smckusick } 414169345Sdwmalone printf("i-node grace time: %jd", (intmax_t)qup->dqblk.dqb_itime); 415181262Scognet if (qup->dqblk.dqb_itime != 0) { 416207736Smckusick t = qup->dqblk.dqb_itime; 417207736Smckusick printf(" %s", ctime(&t)); 418207736Smckusick } else { 419166646Smpp printf("\n"); 420207736Smckusick } 421166646Smpp} 422166646Smpp 423166646Smpp 424166646Smppstatic void 42595624Smarkmheading(int type, u_long id, const char *name, const char *tag) 4261590Srgrimes{ 4271590Srgrimes 42813365Sgraichen printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type], 4291590Srgrimes name, *qfextension[type], id, tag); 4301590Srgrimes if (!qflag && tag[0] == '\0') { 431207736Smckusick printf("%-15s %7s %8s %7s %7s %6s %7s %6s%8s\n" 4321590Srgrimes , "Filesystem" 43377047Spirzyk , "usage" 4341590Srgrimes , "quota" 4351590Srgrimes , "limit" 4361590Srgrimes , "grace" 4371590Srgrimes , "files" 4381590Srgrimes , "quota" 4391590Srgrimes , "limit" 4401590Srgrimes , "grace" 4411590Srgrimes ); 4421590Srgrimes } 4431590Srgrimes} 4441590Srgrimes 4451590Srgrimes/* 4461590Srgrimes * Calculate the grace period and return a printable string for it. 4471590Srgrimes */ 448166495Smppstatic char * 449207736Smckusicktimeprt(int64_t seconds) 4501590Srgrimes{ 4511590Srgrimes time_t hours, minutes; 452207736Smckusick char *buf; 4531590Srgrimes static time_t now; 4541590Srgrimes 4551590Srgrimes if (now == 0) 4561590Srgrimes time(&now); 457166495Smpp if (now > seconds) { 458207736Smckusick if ((buf = strdup("none")) == NULL) 459207736Smckusick errx(1, "strdup() failed in timeprt()"); 460207736Smckusick return (buf); 461166495Smpp } 4621590Srgrimes seconds -= now; 4631590Srgrimes minutes = (seconds + 30) / 60; 4641590Srgrimes hours = (minutes + 30) / 60; 4651590Srgrimes if (hours >= 36) { 466166495Smpp if (asprintf(&buf, "%lddays", ((long)hours + 12) / 24) < 0) 467207736Smckusick errx(1, "asprintf() failed in timeprt(1)"); 4681590Srgrimes return (buf); 4691590Srgrimes } 4701590Srgrimes if (minutes >= 60) { 471166495Smpp if (asprintf(&buf, "%2ld:%ld", (long)minutes / 60, 472166495Smpp (long)minutes % 60) < 0) 473207736Smckusick errx(1, "asprintf() failed in timeprt(2)"); 4741590Srgrimes return (buf); 4751590Srgrimes } 476166495Smpp if (asprintf(&buf, "%2ld", (long)minutes) < 0) 477207736Smckusick errx(1, "asprintf() failed in timeprt(3)"); 4781590Srgrimes return (buf); 4791590Srgrimes} 4801590Srgrimes 4811590Srgrimes/* 4821590Srgrimes * Collect the requested quota information. 4831590Srgrimes */ 48416379Salexstatic struct quotause * 48595624Smarkmgetprivs(long id, int quotatype) 4861590Srgrimes{ 487101544Siedowse struct quotause *qup, *quptail = NULL; 48895624Smarkm struct fstab *fs; 4891590Srgrimes struct quotause *quphead; 49013236Sgraichen struct statfs *fst; 49113236Sgraichen int nfst, i; 492166646Smpp struct statfs sfb; 4931590Srgrimes 49413236Sgraichen qup = quphead = (struct quotause *)0; 49513236Sgraichen 496166646Smpp if (filename != NULL && statfs(filename, &sfb) != 0) 497166646Smpp err(1, "cannot statfs %s", filename); 49897764Siedowse nfst = getmntinfo(&fst, MNT_NOWAIT); 49927888Scharnier if (nfst == 0) 50027888Scharnier errx(2, "no filesystems mounted!"); 5011590Srgrimes setfsent(); 502207736Smckusick for (i = 0; i < nfst; i++) { 50313236Sgraichen if (qup == NULL) { 50413365Sgraichen if ((qup = (struct quotause *)malloc(sizeof *qup)) 50527888Scharnier == NULL) 50627888Scharnier errx(2, "out of memory"); 5071590Srgrimes } 508166646Smpp /* 509166646Smpp * See if the user requested a specific file system 510166646Smpp * or specified a file inside a mounted file system. 511166646Smpp */ 512166646Smpp if (filename != NULL && 513166646Smpp strcmp(sfb.f_mntonname, fst[i].f_mntonname) != 0) 514166646Smpp continue; 51532651Sbde if (strcmp(fst[i].f_fstypename, "nfs") == 0) { 516101545Siedowse if (lflag) 517101545Siedowse continue; 518166646Smpp if (getnfsquota(&fst[i], qup, id, quotatype) == 0) 5191590Srgrimes continue; 52036880Sache } else if (strcmp(fst[i].f_fstypename, "ufs") == 0) { 52113236Sgraichen /* 52213236Sgraichen * XXX 52313236Sgraichen * UFS filesystems must be in /etc/fstab, and must 52413236Sgraichen * indicate that they have quotas on (?!) This is quite 52513236Sgraichen * unlike SunOS where quotas can be enabled/disabled 52613236Sgraichen * on a filesystem independent of /etc/fstab, and it 52713236Sgraichen * will still print quotas for them. 52813236Sgraichen */ 52913236Sgraichen if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL) 5301590Srgrimes continue; 53195624Smarkm if (getufsquota(fs, qup, id, quotatype) == 0) 53213236Sgraichen continue; 53313236Sgraichen } else 53413236Sgraichen continue; 53513236Sgraichen strcpy(qup->fsname, fst[i].f_mntonname); 5361590Srgrimes if (quphead == NULL) 5371590Srgrimes quphead = qup; 5381590Srgrimes else 5391590Srgrimes quptail->next = qup; 5401590Srgrimes quptail = qup; 54113236Sgraichen quptail->next = 0; 54213236Sgraichen qup = NULL; 5431590Srgrimes } 54413236Sgraichen if (qup) 54513236Sgraichen free(qup); 5461590Srgrimes endfsent(); 5471590Srgrimes return (quphead); 5481590Srgrimes} 5491590Srgrimes 5501590Srgrimes/* 551207736Smckusick * Check to see if a particular quota is available. 5521590Srgrimes */ 55316379Salexstatic int 554207736Smckusickgetufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype) 5551590Srgrimes{ 556207736Smckusick struct quotafile *qf; 5571590Srgrimes 558207736Smckusick if ((qf = quota_open(fs, quotatype, O_RDONLY)) == NULL) 5591590Srgrimes return (0); 560207736Smckusick if (quota_read(qf, &qup->dqblk, id) != 0) 561166485Smpp return (0); 562207736Smckusick quota_close(qf); 5631590Srgrimes return (1); 5641590Srgrimes} 5651590Srgrimes 56616379Salexstatic int 56795624Smarkmgetnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) 56813236Sgraichen{ 569339008Ssef struct ext_getquota_args gq_args; 570339008Ssef struct getquota_args old_gq_args; 57113236Sgraichen struct getquota_rslt gq_rslt; 57213236Sgraichen struct dqblk *dqp = &qup->dqblk; 57313236Sgraichen struct timeval tv; 574285253Shrs char *cp, host[NI_MAXHOST]; 575339008Ssef enum clnt_stat call_stat; 57613236Sgraichen 57713236Sgraichen if (fst->f_flags & MNT_LOCAL) 57813236Sgraichen return (0); 57913236Sgraichen 58013236Sgraichen /* 58113236Sgraichen * must be some form of "hostname:/path" 58213236Sgraichen */ 583285253Shrs cp = fst->f_mntfromname; 584285253Shrs do { 585285253Shrs cp = strrchr(cp, ':'); 586285253Shrs } while (cp != NULL && *(cp + 1) != '/'); 58713236Sgraichen if (cp == NULL) { 58858618Scharnier warnx("cannot find hostname for %s", fst->f_mntfromname); 58913236Sgraichen return (0); 59013236Sgraichen } 591285253Shrs memset(host, 0, sizeof(host)); 592285253Shrs memcpy(host, fst->f_mntfromname, cp - fst->f_mntfromname); 593285253Shrs host[sizeof(host) - 1] = '\0'; 59413236Sgraichen 59597764Siedowse /* Avoid attempting the RPC for special amd(8) filesystems. */ 59697764Siedowse if (strncmp(fst->f_mntfromname, "pid", 3) == 0 && 597285253Shrs strchr(fst->f_mntfromname, '@') != NULL) 59897764Siedowse return (0); 59997764Siedowse 60013236Sgraichen gq_args.gqa_pathp = cp + 1; 601339008Ssef gq_args.gqa_id = id; 602339008Ssef gq_args.gqa_type = quotatype; 60313236Sgraichen 604339008Ssef call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS, 605339008Ssef RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args, 606339008Ssef (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); 607352576Shrs if (call_stat == RPC_PROGVERSMISMATCH || call_stat == RPC_PROGNOTREGISTERED) { 608339008Ssef if (quotatype == USRQUOTA) { 609339008Ssef old_gq_args.gqa_pathp = cp + 1; 610339008Ssef old_gq_args.gqa_uid = id; 611339008Ssef call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS, 612339008Ssef RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args, 613339008Ssef (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); 614339008Ssef } else { 615339008Ssef /* Old rpc quota does not support group type */ 616339008Ssef return (0); 617339008Ssef } 618339008Ssef } 619339008Ssef if (call_stat != 0) 620339008Ssef return (call_stat); 621339008Ssef 62213236Sgraichen switch (gq_rslt.status) { 62313236Sgraichen case Q_NOQUOTA: 62413236Sgraichen break; 62513236Sgraichen case Q_EPERM: 62627888Scharnier warnx("quota permission error, host: %s", 62713236Sgraichen fst->f_mntfromname); 62813236Sgraichen break; 62913236Sgraichen case Q_OK: 63013236Sgraichen gettimeofday(&tv, NULL); 63113236Sgraichen /* blocks*/ 63213236Sgraichen dqp->dqb_bhardlimit = 63313236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * 634118464Sdas (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 63513236Sgraichen dqp->dqb_bsoftlimit = 63613236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * 637118464Sdas (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 63813236Sgraichen dqp->dqb_curblocks = 63913236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * 640118464Sdas (gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); 64113236Sgraichen /* inodes */ 64213236Sgraichen dqp->dqb_ihardlimit = 64313236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; 64413236Sgraichen dqp->dqb_isoftlimit = 64513236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; 64613236Sgraichen dqp->dqb_curinodes = 64713236Sgraichen gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; 64813236Sgraichen /* grace times */ 64913236Sgraichen dqp->dqb_btime = 65013236Sgraichen tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft; 65113236Sgraichen dqp->dqb_itime = 65213236Sgraichen tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft; 65313236Sgraichen return (1); 65413236Sgraichen default: 65558618Scharnier warnx("bad rpc result, host: %s", fst->f_mntfromname); 65613236Sgraichen break; 65713236Sgraichen } 658285253Shrs 65913236Sgraichen return (0); 66013236Sgraichen} 66113236Sgraichen 662339008Ssefstatic enum clnt_stat 66395624Smarkmcallaurpc(char *host, int prognum, int versnum, int procnum, 66495624Smarkm xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) 66513236Sgraichen{ 66613236Sgraichen enum clnt_stat clnt_stat; 66713236Sgraichen struct timeval timeout, tottimeout; 66813236Sgraichen 66913236Sgraichen CLIENT *client = NULL; 670285253Shrs 671285253Shrs client = clnt_create(host, prognum, versnum, "udp"); 672285253Shrs if (client == NULL) 673285253Shrs return ((int)rpc_createerr.cf_stat); 67413236Sgraichen timeout.tv_usec = 0; 67513236Sgraichen timeout.tv_sec = 6; 676285253Shrs CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout); 67713236Sgraichen 67813236Sgraichen client->cl_auth = authunix_create_default(); 67913236Sgraichen tottimeout.tv_sec = 25; 68013236Sgraichen tottimeout.tv_usec = 0; 68113236Sgraichen clnt_stat = clnt_call(client, procnum, inproc, in, 68213236Sgraichen outproc, out, tottimeout); 683339008Ssef return (clnt_stat); 68413236Sgraichen} 68513236Sgraichen 68616379Salexstatic int 68795624Smarkmalldigits(char *s) 6881590Srgrimes{ 68995624Smarkm int c; 6901590Srgrimes 6911590Srgrimes c = *s++; 6921590Srgrimes do { 6931590Srgrimes if (!isdigit(c)) 6941590Srgrimes return (0); 69516379Salex } while ((c = *s++)); 6961590Srgrimes return (1); 6971590Srgrimes} 698