1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright 2016 Nexenta Systems, Inc. All rights reserved. */
27
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30
31 /*
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
35 *
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
39 */
40
41 /*
42 * Disk quota reporting program.
43 */
44 #include <stdio.h>
45 #include <sys/mnttab.h>
46 #include <ctype.h>
47 #include <pwd.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <memory.h>
51 #include <sys/time.h>
52 #include <sys/param.h>
53 #include <sys/types.h>
54 #include <sys/sysmacros.h>
55 #include <sys/mntent.h>
56 #include <sys/file.h>
57 #include <sys/stat.h>
58 #include <sys/fs/ufs_quota.h>
59 #include <priv_utils.h>
60 #include <locale.h>
61 #include <rpc/rpc.h>
62 #include <netdb.h>
63 #include <rpcsvc/rquota.h>
64 #include <zone.h>
65 #include "../../nfs/lib/replica.h"
66 #include <dlfcn.h>
67 #include <libzfs.h>
68
69 int vflag;
70 int nolocalquota;
71
72 /*
73 * struct dqblk is a 32 bit quantity and is common across NFS and UFS.
74 * UFS has a 2TB limit and an uint32_t can hold this value.
75 * NFS translates this to rquota where all members are unit32_t in size.
76 * A private dqblk, dqblk_zfs is defined here.
77 */
78 struct dqblk_zfs {
79 uint64_t dqbz_bhardlimit; /* absolute limit on disk blks alloc */
80 uint64_t dqbz_bsoftlimit; /* preferred limit on disk blks */
81 uint64_t dqbz_curblocks; /* current block count */
82 uint64_t dqbz_fhardlimit; /* maximum # allocated files + 1 */
83 uint64_t dqbz_fsoftlimit; /* preferred file limit */
84 uint64_t dqbz_curfiles; /* current # allocated files */
85 uint64_t dqbz_btimelimit; /* time limit for excessive disk use */
86 uint64_t dqbz_ftimelimit; /* time limit for excessive files */
87 };
88 extern int optind;
89 extern char *optarg;
90
91 #define QFNAME "quotas"
92
93 #if DEV_BSIZE < 1024
94 #define kb(x) ((x) / (1024 / DEV_BSIZE))
95 #else
96 #define kb(x) ((x) * (DEV_BSIZE / 1024))
97 #endif
98
99 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
100 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
101 #endif
102
103 static void zexit(int);
104 static boolean_t blklimits_is_zero(struct dqblk *, struct dqblk_zfs *);
105 static int getzfsquota(char *, char *, struct dqblk_zfs *);
106 static int getnfsquota(char *, char *, uid_t, struct dqblk *);
107 static void showuid(uid_t);
108 static void showquotas(uid_t, char *);
109 static void warn(struct mnttab *, struct dqblk *, struct dqblk_zfs *);
110 static void warn_dqblk_impl(struct mnttab *, struct dqblk *);
111 static void warn_dqblk_zfs_impl(struct mnttab *, struct dqblk_zfs *);
112 static void heading(uid_t, char *);
113 static void prquota(struct mnttab *, struct dqblk *, struct dqblk_zfs *);
114 static void prquota_dqblk_impl(struct mnttab *, struct dqblk *);
115 static void prquota_dqblk_zfs_impl(struct mnttab *, struct dqblk_zfs *);
116 static void fmttime(char *, long);
117
118 static libzfs_handle_t *g_zfs = NULL;
119
120 int
121 main(int argc, char *argv[])
122 {
123 int opt;
124 int i;
125 int status = 0;
126
127 (void) setlocale(LC_ALL, "");
128 (void) textdomain(TEXT_DOMAIN);
129
130 /*
131 * PRIV_FILE_DAC_READ is needed to read the QFNAME file
132 * Clear all other privleges from the limit set, and add
133 * the required privilege to the bracketed set.
134 */
135
136 if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_FILE_DAC_READ,
137 NULL) == -1) {
138 (void) fprintf(stderr,
139 gettext("Insufficient privileges, "
140 "quota must be set-uid root or have "
141 "file_dac_read privileges\n"));
142
143 exit(1);
144 }
145
146
147 while ((opt = getopt(argc, argv, "vV")) != EOF) {
148 switch (opt) {
149
150 case 'v':
151 vflag++;
152 break;
153
154 case 'V': /* Print command line */
155 {
156 char *opt_text;
157 int opt_count;
158
159 (void) fprintf(stdout, "quota -F UFS ");
160 for (opt_count = 1; opt_count < argc; opt_count++) {
161 opt_text = argv[opt_count];
162 if (opt_text)
163 (void) fprintf(stdout, " %s ",
164 opt_text);
165 }
166 (void) fprintf(stdout, "\n");
167 }
168 break;
169
170 case '?':
171 fprintf(stderr, "usage: quota [-v] [username]\n");
172 zexit(32);
173 }
174 }
175 if (quotactl(Q_ALLSYNC, NULL, (uid_t)0, NULL) < 0 && errno == EINVAL) {
176 if (vflag)
177 fprintf(stderr, "There are no quotas on this system\n");
178 nolocalquota++;
179 }
180 if (argc == optind) {
181 showuid(getuid());
182 zexit(0);
183 }
184 for (i = optind; i < argc; i++) {
185 if (alldigits(argv[i])) {
186 showuid((uid_t)atoi(argv[i]));
187 } else
188 status |= showname(argv[i]);
189 }
190 __priv_relinquish();
191 return (status);
192 }
193
194 static void
195 showuid(uid_t uid)
196 {
197 struct passwd *pwd = getpwuid(uid);
198
199 if (uid == 0) {
200 if (vflag)
201 printf("no disk quota for uid 0\n");
202 return;
203 }
204 if (pwd == NULL)
205 showquotas(uid, "(no account)");
206 else
207 showquotas(uid, pwd->pw_name);
208 }
209
210 int
211 showname(char *name)
212 {
213 struct passwd *pwd = getpwnam(name);
214
215 if (pwd == NULL) {
216 fprintf(stderr, "quota: %s: unknown user\n", name);
217 return (32);
218 }
219 if (pwd->pw_uid == 0) {
220 if (vflag)
221 printf("no disk quota for %s (uid 0)\n", name);
222 return (0);
223 }
224 showquotas(pwd->pw_uid, name);
225 return (0);
226 }
227
228 static void
229 showquotas(uid_t uid, char *name)
230 {
231 struct mnttab mnt;
232 FILE *mtab;
233 struct dqblk dqblk;
234 struct dqblk_zfs dqblkz;
235 uid_t myuid;
236 struct failed_srv {
237 char *serv_name;
238 struct failed_srv *next;
239 };
240 struct failed_srv *failed_srv_list = NULL;
241 int rc;
242 char my_zonename[ZONENAME_MAX];
243 zoneid_t my_zoneid = getzoneid();
244
245 myuid = getuid();
246 if (uid != myuid && myuid != 0) {
247 printf("quota: %s (uid %d): permission denied\n", name, uid);
248 zexit(32);
249 }
250
251 memset(my_zonename, '\0', ZONENAME_MAX);
252 getzonenamebyid(my_zoneid, my_zonename, ZONENAME_MAX);
253
254 if (vflag)
255 heading(uid, name);
256 mtab = fopen(MNTTAB, "r");
257 while (getmntent(mtab, &mnt) == NULL) {
258 boolean_t is_zfs = B_FALSE;
259
260 if (strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) == 0) {
261 is_zfs = B_TRUE;
262 bzero(&dqblkz, sizeof (dqblkz));
263 if (getzfsquota(name, mnt.mnt_special, &dqblkz))
264 continue;
265 } else if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) {
266 if (nolocalquota ||
267 (quotactl(Q_GETQUOTA,
268 mnt.mnt_mountp, uid, &dqblk) != 0 &&
269 !(vflag && getdiskquota(&mnt, uid, &dqblk))))
270 continue;
271 } else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) {
272
273 struct replica *rl;
274 int count;
275 char *mntopt = NULL;
276
277 /*
278 * Skip checking quotas for file systems mounted
279 * in other zones. Zone names will be passed in
280 * following format from hasmntopt():
281 * "zone=<zone-name>,<mnt options...>"
282 */
283 if ((mntopt = hasmntopt(&mnt, MNTOPT_ZONE)) &&
284 (my_zonename[0] != '\0')) {
285 mntopt += strcspn(mntopt, "=") + 1;
286 if (strncmp(mntopt, my_zonename,
287 strcspn(mntopt, ",")) != 0)
288 continue;
289 }
290
291 if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts))
292 continue;
293
294 /*
295 * Skip quota processing if mounted with public
296 * option. We are not likely to be able to pierce
297 * a fire wall to contact the quota server.
298 */
299 if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts))
300 continue;
301
302 rl = parse_replica(mnt.mnt_special, &count);
303
304 if (rl == NULL) {
305
306 if (count < 0)
307 fprintf(stderr, "cannot find hostname "
308 "and/or pathname for %s\n",
309 mnt.mnt_mountp);
310 else
311 fprintf(stderr, "no memory to parse "
312 "mnttab entry for %s\n",
313 mnt.mnt_mountp);
314 continue;
315 }
316
317 /*
318 * We skip quota reporting on mounts with replicas
319 * for the following reasons:
320 *
321 * (1) Very little point in reporting quotas on
322 * a set of read-only replicas ... how will the
323 * user correct the problem?
324 *
325 * (2) Which replica would we report the quota
326 * for? If we pick the current replica, what
327 * happens when a fail over event occurs? The
328 * next time quota is run, the quota will look
329 * all different, or there won't even be one.
330 * This has the potential to break scripts.
331 *
332 * If we prnt quouta for all replicas, how do
333 * we present the output without breaking scripts?
334 */
335
336 if (count > 1) {
337 free_replica(rl, count);
338 continue;
339 }
340
341 /*
342 * Skip file systems mounted using public fh.
343 * We are not likely to be able to pierce
344 * a fire wall to contact the quota server.
345 */
346 if (strcmp(rl[0].host, "nfs") == 0 &&
347 strncmp(rl[0].path, "//", 2) == 0) {
348 free_replica(rl, count);
349 continue;
350 }
351
352 /*
353 * Skip getting quotas from failing servers
354 */
355 if (failed_srv_list != NULL) {
356 struct failed_srv *tmp_list;
357 int found_failed = 0;
358 size_t len = strlen(rl[0].host);
359
360 tmp_list = failed_srv_list;
361 do {
362 if (strncasecmp(rl[0].host,
363 tmp_list->serv_name, len) == 0) {
364 found_failed = 1;
365 break;
366 }
367 } while ((tmp_list = tmp_list->next) != NULL);
368 if (found_failed) {
369 free_replica(rl, count);
370 continue;
371 }
372 }
373
374 rc = getnfsquota(rl[0].host, rl[0].path, uid, &dqblk);
375 if (rc != RPC_SUCCESS) {
376 size_t len;
377 struct failed_srv *tmp_srv;
378
379 /*
380 * Failed to get quota from this server. Add
381 * this server to failed_srv_list and skip
382 * getting quotas for other mounted filesystems
383 * from this server.
384 */
385 if (rc == RPC_TIMEDOUT || rc == RPC_CANTSEND) {
386 len = strlen(rl[0].host);
387 tmp_srv = (struct failed_srv *)malloc(
388 sizeof (struct failed_srv));
389 tmp_srv->serv_name = (char *)malloc(
390 len * sizeof (char) + 1);
391 strncpy(tmp_srv->serv_name, rl[0].host,
392 len);
393 tmp_srv->serv_name[len] = '\0';
394
395 tmp_srv->next = failed_srv_list;
396 failed_srv_list = tmp_srv;
397 }
398
399 free_replica(rl, count);
400 continue;
401 }
402
403 free_replica(rl, count);
404 } else {
405 continue;
406 }
407
408 if (blklimits_is_zero(&dqblk, is_zfs ? &dqblkz : NULL))
409 continue;
410
411 if (vflag)
412 prquota(&mnt, &dqblk, is_zfs ? &dqblkz : NULL);
413 else
414 warn(&mnt, &dqblk, is_zfs ? &dqblkz : NULL);
415
416 }
417
418 /*
419 * Free list of failed servers
420 */
421 while (failed_srv_list != NULL) {
422 struct failed_srv *tmp_srv = failed_srv_list;
423
424 failed_srv_list = failed_srv_list->next;
425 free(tmp_srv->serv_name);
426 free(tmp_srv);
427 }
428
429 fclose(mtab);
430 }
431
432 static void
433 warn(struct mnttab *mntp, struct dqblk *dqp, struct dqblk_zfs *dqzp)
434 {
435 if (dqzp != NULL)
436 warn_dqblk_zfs_impl(mntp, dqzp);
437 else
438 warn_dqblk_impl(mntp, dqp);
439 }
440
441 static void
442 heading(uid_t uid, char *name)
443 {
444 printf("Disk quotas for %s (uid %ld):\n", name, (long)uid);
445 printf("%-12s %7s%7s%7s%12s%7s%7s%7s%12s\n",
446 "Filesystem",
447 "usage",
448 "quota",
449 "limit",
450 "timeleft",
451 "files",
452 "quota",
453 "limit",
454 "timeleft");
455 }
456
457 static void
458 prquota(struct mnttab *mntp, struct dqblk *dqp, struct dqblk_zfs *dqzp)
459 {
460 if (dqzp != NULL)
461 prquota_dqblk_zfs_impl(mntp, dqzp);
462 else
463 prquota_dqblk_impl(mntp, dqp);
464 }
465
466 static void
467 fmttime(char *buf, long time)
468 {
469 int i;
470 static struct {
471 int c_secs; /* conversion units in secs */
472 char *c_str; /* unit string */
473 } cunits [] = {
474 {60*60*24*28, "months"},
475 {60*60*24*7, "weeks"},
476 {60*60*24, "days"},
477 {60*60, "hours"},
478 {60, "mins"},
479 {1, "secs"}
480 };
481
482 if (time <= 0) {
483 strlcpy(buf, "EXPIRED", sizeof (*buf));
484 return;
485 }
486 for (i = 0; i < sizeof (cunits)/sizeof (cunits[0]); i++) {
487 if (time >= cunits[i].c_secs)
488 break;
489 }
490 snprintf(buf, sizeof (*buf), "%.1f %s",
491 (double)time/cunits[i].c_secs, cunits[i].c_str);
492 }
493
494 int
495 alldigits(char *s)
496 {
497 int c;
498
499 c = *s++;
500 do {
501 if (!isdigit(c))
502 return (0);
503 } while (c = *s++);
504 return (1);
505 }
506
507 int
508 getdiskquota(struct mnttab *mntp, uid_t uid, struct dqblk *dqp)
509 {
510 int fd;
511 dev_t fsdev;
512 struct stat64 statb;
513 char qfilename[MAXPATHLEN];
514
515 if (stat64(mntp->mnt_special, &statb) < 0 ||
516 (statb.st_mode & S_IFMT) != S_IFBLK)
517 return (0);
518 fsdev = statb.st_rdev;
519 (void) snprintf(qfilename, sizeof (qfilename), "%s/%s",
520 mntp->mnt_mountp, QFNAME);
521 if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev)
522 return (0);
523 (void) __priv_bracket(PRIV_ON);
524 fd = open64(qfilename, O_RDONLY);
525 (void) __priv_bracket(PRIV_OFF);
526 if (fd < 0)
527 return (0);
528 (void) llseek(fd, (offset_t)dqoff(uid), L_SET);
529 switch (read(fd, dqp, sizeof (struct dqblk))) {
530 case 0: /* EOF */
531 /*
532 * Convert implicit 0 quota (EOF)
533 * into an explicit one (zero'ed dqblk).
534 */
535 memset((caddr_t)dqp, 0, sizeof (struct dqblk));
536 break;
537
538 case sizeof (struct dqblk): /* OK */
539 break;
540
541 default: /* ERROR */
542 close(fd);
543 return (0);
544 }
545 close(fd);
546 return (1);
547 }
548
549 int
550 quotactl(int cmd, char *mountp, uid_t uid, caddr_t addr)
551 {
552 int fd;
553 int status;
554 struct quotctl quota;
555 char qfile[MAXPATHLEN];
556
557 FILE *fstab;
558 struct mnttab mnt;
559
560
561 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
562 /*
563 * Find the mount point of any mounted file system. This is
564 * because the ioctl that implements the quotactl call has
565 * to go to a real file, and not to the block device.
566 */
567 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
568 fprintf(stderr, "%s: ", MNTTAB);
569 perror("open");
570 zexit(32);
571 }
572 fd = -1;
573 while ((status = getmntent(fstab, &mnt)) == NULL) {
574 if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0 ||
575 hasopt(MNTOPT_RO, mnt.mnt_mntopts))
576 continue;
577 if ((strlcpy(qfile, mnt.mnt_mountp,
578 sizeof (qfile)) >= sizeof (qfile)) ||
579 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
580 sizeof (qfile))) {
581 continue;
582 }
583 (void) __priv_bracket(PRIV_ON);
584 fd = open64(qfile, O_RDONLY);
585 (void) __priv_bracket(PRIV_OFF);
586 if (fd != -1)
587 break;
588 }
589 fclose(fstab);
590 if (fd == -1) {
591 errno = ENOENT;
592 return (-1);
593 }
594 } else {
595 if (mountp == NULL || mountp[0] == '\0') {
596 errno = ENOENT;
597 return (-1);
598 }
599 if ((strlcpy(qfile, mountp, sizeof (qfile)) >= sizeof
600 (qfile)) ||
601 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= sizeof
602 (qfile))) {
603 errno = ENOENT;
604 return (-1);
605 }
606 (void) __priv_bracket(PRIV_ON);
607 fd = open64(qfile, O_RDONLY);
608 (void) __priv_bracket(PRIV_OFF);
609 if (fd < 0)
610 return (-1);
611 } /* else */
612 quota.op = cmd;
613 quota.uid = uid;
614 quota.addr = addr;
615 status = ioctl(fd, Q_QUOTACTL, "a);
616 if (fd != 0)
617 close(fd);
618 return (status);
619 }
620
621
622 /*
623 * Return 1 if opt appears in optlist
624 */
625 int
626 hasopt(char *opt, char *optlist)
627 {
628 char *value;
629 char *opts[2];
630
631 opts[0] = opt;
632 opts[1] = NULL;
633
634 if (optlist == NULL)
635 return (0);
636 while (*optlist != '\0') {
637 if (getsubopt(&optlist, opts, &value) == 0)
638 return (1);
639 }
640 return (0);
641 }
642
643 /*
644 * If there are no quotas available, then getnfsquota() returns
645 * RPC_SYSTEMERROR to caller.
646 */
647 static int
648 getnfsquota(char *hostp, char *path, uid_t uid, struct dqblk *dqp)
649 {
650 struct getquota_args gq_args;
651 struct getquota_rslt gq_rslt;
652 struct rquota *rquota;
653 extern char *strchr();
654 int rpc_err;
655
656 gq_args.gqa_pathp = path;
657 gq_args.gqa_uid = uid;
658 rpc_err = callaurpc(hostp, RQUOTAPROG, RQUOTAVERS,
659 (vflag? RQUOTAPROC_GETQUOTA: RQUOTAPROC_GETACTIVEQUOTA),
660 xdr_getquota_args, &gq_args, xdr_getquota_rslt, &gq_rslt);
661 if (rpc_err != RPC_SUCCESS) {
662 return (rpc_err);
663 }
664 switch (gq_rslt.status) {
665 case Q_OK:
666 {
667 struct timeval tv;
668 u_longlong_t limit;
669
670 rquota = &gq_rslt.getquota_rslt_u.gqr_rquota;
671
672 if (!vflag && rquota->rq_active == FALSE) {
673 return (RPC_SYSTEMERROR);
674 }
675 gettimeofday(&tv, NULL);
676 limit = (u_longlong_t)(rquota->rq_bhardlimit) *
677 rquota->rq_bsize / DEV_BSIZE;
678 dqp->dqb_bhardlimit = limit;
679 limit = (u_longlong_t)(rquota->rq_bsoftlimit) *
680 rquota->rq_bsize / DEV_BSIZE;
681 dqp->dqb_bsoftlimit = limit;
682 limit = (u_longlong_t)(rquota->rq_curblocks) *
683 rquota->rq_bsize / DEV_BSIZE;
684 dqp->dqb_curblocks = limit;
685 dqp->dqb_fhardlimit = rquota->rq_fhardlimit;
686 dqp->dqb_fsoftlimit = rquota->rq_fsoftlimit;
687 dqp->dqb_curfiles = rquota->rq_curfiles;
688 dqp->dqb_btimelimit =
689 tv.tv_sec + rquota->rq_btimeleft;
690 dqp->dqb_ftimelimit =
691 tv.tv_sec + rquota->rq_ftimeleft;
692 return (RPC_SUCCESS);
693 }
694
695 case Q_NOQUOTA:
696 return (RPC_SYSTEMERROR);
697
698 case Q_EPERM:
699 fprintf(stderr, "quota permission error, host: %s\n", hostp);
700 return (RPC_AUTHERROR);
701
702 default:
703 fprintf(stderr, "bad rpc result, host: %s\n", hostp);
704 return (RPC_CANTDECODEARGS);
705 }
706
707 /* NOTREACHED */
708 }
709
710 int
711 callaurpc(char *host, int prognum, int versnum, int procnum,
712 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
713 {
714 static enum clnt_stat clnt_stat;
715 struct timeval tottimeout = {20, 0};
716
717 static CLIENT *cl = NULL;
718 static int oldprognum, oldversnum;
719 static char oldhost[MAXHOSTNAMELEN+1];
720
721 /*
722 * Cache the client handle in case there are lots
723 * of entries in the /etc/mnttab for the same
724 * server. If the server returns an error, don't
725 * make further calls.
726 */
727 if (cl == NULL || oldprognum != prognum || oldversnum != versnum ||
728 strcmp(oldhost, host) != 0) {
729 if (cl) {
730 clnt_destroy(cl);
731 cl = NULL;
732 }
733 cl = clnt_create_timed(host, prognum, versnum, "udp",
734 &tottimeout);
735 if (cl == NULL)
736 return ((int)RPC_TIMEDOUT);
737 if ((cl->cl_auth = authunix_create_default()) == NULL) {
738 clnt_destroy(cl);
739 return (RPC_CANTSEND);
740 }
741 oldprognum = prognum;
742 oldversnum = versnum;
743 (void) strlcpy(oldhost, host, sizeof (oldhost));
744 clnt_stat = RPC_SUCCESS;
745 }
746
747 if (clnt_stat != RPC_SUCCESS)
748 return ((int)clnt_stat); /* don't bother retrying */
749
750 clnt_stat = clnt_call(cl, procnum, inproc, in,
751 outproc, out, tottimeout);
752
753 return ((int)clnt_stat);
754 }
755
756 static int
757 getzfsquota(char *user, char *dataset, struct dqblk_zfs *zq)
758 {
759 zfs_handle_t *zhp = NULL;
760 char propname[ZFS_MAXPROPLEN];
761 uint64_t userquota, userused;
762
763 if (g_zfs == NULL) {
764 g_zfs = libzfs_init();
765 }
766
767 if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
768 return (1);
769
770 (void) snprintf(propname, sizeof (propname), "userquota@%s", user);
771 if (zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) {
772 zfs_close(zhp);
773 return (1);
774 }
775
776 (void) snprintf(propname, sizeof (propname), "userused@%s", user);
777 if (zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) {
778 zfs_close(zhp);
779 return (1);
780 }
781
782 zq->dqbz_bhardlimit = userquota / DEV_BSIZE;
783 zq->dqbz_bsoftlimit = userquota / DEV_BSIZE;
784 zq->dqbz_curblocks = userused / DEV_BSIZE;
785 zfs_close(zhp);
786 return (0);
787 }
788
789 static boolean_t
790 blklimits_is_zero(struct dqblk *dqp, struct dqblk_zfs *dqzp)
791 {
792 if (dqzp == NULL) {
793 if (dqp->dqb_bsoftlimit == 0 && dqp->dqb_bhardlimit == 0 &&
794 dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0) {
795 return (B_TRUE);
796 } else {
797 return (B_FALSE);
798 }
799 } else {
800 if (dqzp->dqbz_bsoftlimit == 0 && dqzp->dqbz_bhardlimit == 0 &&
801 dqzp->dqbz_fsoftlimit == 0 && dqzp->dqbz_fhardlimit == 0) {
802 return (B_TRUE);
803 } else {
804 return (B_FALSE);
805 }
806 }
807 }
808
809 static void
810 warn_dqblk_impl(struct mnttab *mntp, struct dqblk *dqp)
811 {
812 struct timeval tv;
813
814 time(&(tv.tv_sec));
815 tv.tv_usec = 0;
816 if (dqp->dqb_bhardlimit &&
817 dqp->dqb_curblocks >= dqp->dqb_bhardlimit) {
818 printf("Block limit reached on %s\n", mntp->mnt_mountp);
819 } else if (dqp->dqb_bsoftlimit &&
820 dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) {
821 if (dqp->dqb_btimelimit == 0) {
822 printf("Over disk quota on %s, remove %luK\n",
823 mntp->mnt_mountp,
824 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1));
825 } else if (dqp->dqb_btimelimit > tv.tv_sec) {
826 char btimeleft[80];
827
828 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec);
829 printf("Over disk quota on %s, remove %luK within %s\n",
830 mntp->mnt_mountp,
831 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1),
832 btimeleft);
833 } else {
834 printf("Over disk quota on %s, time limit has expired,"
835 " remove %luK\n", mntp->mnt_mountp,
836 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1));
837 }
838 }
839 if (dqp->dqb_fhardlimit &&
840 dqp->dqb_curfiles >= dqp->dqb_fhardlimit) {
841 printf("File count limit reached on %s\n", mntp->mnt_mountp);
842 } else if (dqp->dqb_fsoftlimit &&
843 dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) {
844 if (dqp->dqb_ftimelimit == 0) {
845 printf("Over file quota on %s, remove %lu file%s\n",
846 mntp->mnt_mountp,
847 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
848 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
849 "s" : ""));
850 } else if (dqp->dqb_ftimelimit > tv.tv_sec) {
851 char ftimeleft[80];
852
853 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec);
854 printf("Over file quota on %s, remove %lu file%s"
855 " within %s\n", mntp->mnt_mountp,
856 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
857 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
858 "s" : ""), ftimeleft);
859 } else {
860 printf("Over file quota on %s, time limit has expired,"
861 " remove %lu file%s\n", mntp->mnt_mountp,
862 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
863 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
864 "s" : ""));
865 }
866 }
867 }
868
869 static void
870 warn_dqblk_zfs_impl(struct mnttab *mntp, struct dqblk_zfs *dqzp)
871 {
872 struct timeval tv;
873
874 time(&(tv.tv_sec));
875 tv.tv_usec = 0;
876 if (dqzp->dqbz_bhardlimit &&
877 dqzp->dqbz_curblocks >= dqzp->dqbz_bhardlimit) {
878 printf("Block limit reached on %s\n", mntp->mnt_mountp);
879 } else if (dqzp->dqbz_bsoftlimit &&
880 dqzp->dqbz_curblocks >= dqzp->dqbz_bsoftlimit) {
881 if (dqzp->dqbz_btimelimit == 0) {
882 printf("Over disk quota on %s, remove %luK\n",
883 mntp->mnt_mountp,
884 kb(dqzp->dqbz_curblocks -
885 dqzp->dqbz_bsoftlimit + 1));
886 } else if (dqzp->dqbz_btimelimit > tv.tv_sec) {
887 char btimeleft[80];
888
889 fmttime(btimeleft, dqzp->dqbz_btimelimit - tv.tv_sec);
890 printf("Over disk quota on %s, remove %luK within %s\n",
891 mntp->mnt_mountp,
892 kb(dqzp->dqbz_curblocks -
893 dqzp->dqbz_bsoftlimit + 1),
894 btimeleft);
895 } else {
896 printf("Over disk quota on %s, time limit has expired,"
897 " remove %luK\n", mntp->mnt_mountp,
898 kb(dqzp->dqbz_curblocks -
899 dqzp->dqbz_bsoftlimit + 1));
900 }
901 }
902 if (dqzp->dqbz_fhardlimit &&
903 dqzp->dqbz_curfiles >= dqzp->dqbz_fhardlimit) {
904 printf("File count limit reached on %s\n", mntp->mnt_mountp);
905 } else if (dqzp->dqbz_fsoftlimit &&
906 dqzp->dqbz_curfiles >= dqzp->dqbz_fsoftlimit) {
907 if (dqzp->dqbz_ftimelimit == 0) {
908 printf("Over file quota on %s, remove %lu file%s\n",
909 mntp->mnt_mountp,
910 dqzp->dqbz_curfiles - dqzp->dqbz_fsoftlimit + 1,
911 ((dqzp->dqbz_curfiles -
912 dqzp->dqbz_fsoftlimit + 1) > 1 ?
913 "s" : ""));
914 } else if (dqzp->dqbz_ftimelimit > tv.tv_sec) {
915 char ftimeleft[80];
916
917 fmttime(ftimeleft, dqzp->dqbz_ftimelimit - tv.tv_sec);
918 printf("Over file quota on %s, remove %lu file%s "
919 " within %s\n", mntp->mnt_mountp,
920 dqzp->dqbz_curfiles - dqzp->dqbz_fsoftlimit + 1,
921 ((dqzp->dqbz_curfiles -
922 dqzp->dqbz_fsoftlimit + 1) > 1 ?
923 "s" : ""), ftimeleft);
924 } else {
925 printf("Over file quota on %s, time limit has expired,"
926 " remove %lu file%s\n", mntp->mnt_mountp,
927 dqzp->dqbz_curfiles - dqzp->dqbz_fsoftlimit + 1,
928 ((dqzp->dqbz_curfiles -
929 dqzp->dqbz_fsoftlimit + 1) > 1 ?
930 "s" : ""));
931 }
932 }
933 }
934
935 static void
936 prquota_dqblk_impl(struct mnttab *mntp, struct dqblk *dqp)
937 {
938 struct timeval tv;
939 char ftimeleft[80], btimeleft[80];
940 char *cp;
941
942 time(&(tv.tv_sec));
943 tv.tv_usec = 0;
944 if (dqp->dqb_bsoftlimit && dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) {
945 if (dqp->dqb_btimelimit == 0) {
946 strlcpy(btimeleft, "NOT STARTED", sizeof (btimeleft));
947 } else if (dqp->dqb_btimelimit > tv.tv_sec) {
948 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec);
949 } else {
950 strlcpy(btimeleft, "EXPIRED", sizeof (btimeleft));
951 }
952 } else {
953 btimeleft[0] = '\0';
954 }
955 if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) {
956 if (dqp->dqb_ftimelimit == 0) {
957 strlcpy(ftimeleft, "NOT STARTED", sizeof (ftimeleft));
958 } else if (dqp->dqb_ftimelimit > tv.tv_sec) {
959 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec);
960 } else {
961 strlcpy(ftimeleft, "EXPIRED", sizeof (ftimeleft));
962 }
963 } else {
964 ftimeleft[0] = '\0';
965 }
966 if (strlen(mntp->mnt_mountp) > 12) {
967 printf("%s\n", mntp->mnt_mountp);
968 cp = "";
969 } else {
970 cp = mntp->mnt_mountp;
971 }
972
973 if (dqp->dqb_curfiles == 0 &&
974 dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0) {
975 printf("%-12.12s %7d %6d %6d %11s %6s %6s %6s %11s\n",
976 cp,
977 kb(dqp->dqb_curblocks),
978 kb(dqp->dqb_bsoftlimit),
979 kb(dqp->dqb_bhardlimit),
980 "-",
981 "-",
982 "-",
983 "-",
984 "-");
985 } else {
986 printf("%-12.12s %7d %6d %6d %11s %6d %6d %6d %11s\n",
987 cp,
988 kb(dqp->dqb_curblocks),
989 kb(dqp->dqb_bsoftlimit),
990 kb(dqp->dqb_bhardlimit),
991 btimeleft,
992 dqp->dqb_curfiles,
993 dqp->dqb_fsoftlimit,
994 dqp->dqb_fhardlimit,
995 ftimeleft);
996 }
997 }
998
999 static void
1000 prquota_dqblk_zfs_impl(struct mnttab *mntp, struct dqblk_zfs *dqzp)
1001 {
1002 struct timeval tv;
1003 char ftimeleft[80], btimeleft[80];
1004 char *cp;
1005
1006 time(&(tv.tv_sec));
1007 tv.tv_usec = 0;
1008 if (dqzp->dqbz_bsoftlimit &&
1009 dqzp->dqbz_curblocks >= dqzp->dqbz_bsoftlimit) {
1010 if (dqzp->dqbz_btimelimit == 0) {
1011 strlcpy(btimeleft, "NOT STARTED", sizeof (btimeleft));
1012 } else if (dqzp->dqbz_btimelimit > tv.tv_sec) {
1013 fmttime(btimeleft, dqzp->dqbz_btimelimit - tv.tv_sec);
1014 } else {
1015 strlcpy(btimeleft, "EXPIRED", sizeof (btimeleft));
1016 }
1017 } else {
1018 btimeleft[0] = '\0';
1019 }
1020 if (dqzp->dqbz_fsoftlimit &&
1021 dqzp->dqbz_curfiles >= dqzp->dqbz_fsoftlimit) {
1022 if (dqzp->dqbz_ftimelimit == 0) {
1023 strlcpy(ftimeleft, "NOT STARTED", sizeof (ftimeleft));
1024 } else if (dqzp->dqbz_ftimelimit > tv.tv_sec) {
1025 fmttime(ftimeleft, dqzp->dqbz_ftimelimit - tv.tv_sec);
1026 } else {
1027 strlcpy(ftimeleft, "EXPIRED", sizeof (ftimeleft));
1028 }
1029 } else {
1030 ftimeleft[0] = '\0';
1031 }
1032 if (strlen(mntp->mnt_mountp) > 12) {
1033 printf("%s\n", mntp->mnt_mountp);
1034 cp = "";
1035 } else {
1036 cp = mntp->mnt_mountp;
1037 }
1038
1039 if (dqzp->dqbz_curfiles == 0 &&
1040 dqzp->dqbz_fsoftlimit == 0 && dqzp->dqbz_fhardlimit == 0) {
1041 printf("%-12.12s %7llu %6llu %6llu %11s %6s %6s %6s %11s\n",
1042 cp,
1043 kb(dqzp->dqbz_curblocks),
1044 kb(dqzp->dqbz_bsoftlimit),
1045 kb(dqzp->dqbz_bhardlimit),
1046 "-",
1047 "-",
1048 "-",
1049 "-",
1050 "-");
1051 } else {
1052 printf("%-12.12s %7llu %6llu %6llu %11s %6d %6d %6d %11s\n",
1053 cp,
1054 kb(dqzp->dqbz_curblocks),
1055 kb(dqzp->dqbz_bsoftlimit),
1056 kb(dqzp->dqbz_bhardlimit),
1057 btimeleft,
1058 dqzp->dqbz_curfiles,
1059 dqzp->dqbz_fsoftlimit,
1060 dqzp->dqbz_fhardlimit,
1061 ftimeleft);
1062 }
1063 }
1064
1065 static void
1066 zexit(int n)
1067 {
1068 if (g_zfs != NULL)
1069 libzfs_fini(g_zfs);
1070 exit(n);
1071 }