6198 Let's EOL cachefs
[illumos-gate.git] / usr / src / cmd / fs.d / nfs / rquotad / rpc.rquotad.c
blob4eacff6270e82085f4141c22730c224bec76391b
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <syslog.h>
32 #include <string.h>
33 #include <stropts.h>
34 #include <errno.h>
35 #include <sys/netconfig.h>
36 #include <sys/mntent.h>
37 #include <sys/mnttab.h>
38 #include <sys/param.h>
39 #include <sys/time.h>
40 #ifdef notdef
41 #include <netconfig.h>
42 #endif
43 #include <sys/stat.h>
44 #include <sys/file.h>
45 #include <sys/fs/ufs_quota.h>
46 #include <netdir.h>
47 #include <rpc/rpc.h>
48 #include <rpcsvc/rquota.h>
49 #include <tiuser.h>
50 #include <unistd.h>
51 #include <dlfcn.h>
52 #include <libzfs.h>
54 #define QFNAME "quotas" /* name of quota file */
55 #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */
57 struct fsquot {
58 char *fsq_fstype;
59 struct fsquot *fsq_next;
60 char *fsq_dir;
61 char *fsq_devname;
62 dev_t fsq_dev;
65 struct fsquot *fsqlist = NULL;
67 typedef struct authunix_parms *authp;
69 static int request_pending; /* Request in progress ? */
71 void closedown();
72 void dispatch();
73 struct fsquot *findfsq();
74 void freefs();
75 int getdiskquota();
76 void getquota();
77 int hasquota();
78 void log_cant_reply();
79 void setupfs();
80 static void zexit();
82 static libzfs_handle_t *(*_libzfs_init)(void);
83 static void (*_libzfs_fini)(libzfs_handle_t *);
84 static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int);
85 static void (*_zfs_close)(zfs_handle_t *);
86 static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *,
87 uint64_t *);
88 static libzfs_handle_t *g_zfs = NULL;
91 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
92 * packages. 'rquotad' supports zfs as an option.
94 static void
95 load_libzfs(void)
97 void *hdl;
99 if (g_zfs != NULL)
100 return;
102 if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) {
103 _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl,
104 "libzfs_init");
105 _libzfs_fini = (void (*)())dlsym(hdl, "libzfs_fini");
106 _zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open");
107 _zfs_close = (void (*)())dlsym(hdl, "zfs_close");
108 _zfs_prop_get_userquota_int = (int (*)())
109 dlsym(hdl, "zfs_prop_get_userquota_int");
111 if (_libzfs_init && _libzfs_fini && _zfs_open &&
112 _zfs_close && _zfs_prop_get_userquota_int)
113 g_zfs = _libzfs_init();
117 /*ARGSUSED*/
119 main(int argc, char *argv[])
121 register SVCXPRT *transp;
123 load_libzfs();
126 * If stdin looks like a TLI endpoint, we assume
127 * that we were started by a port monitor. If
128 * t_getstate fails with TBADF, this is not a
129 * TLI endpoint.
131 if (t_getstate(0) != -1 || t_errno != TBADF) {
132 char *netid;
133 struct netconfig *nconf = NULL;
135 openlog("rquotad", LOG_PID, LOG_DAEMON);
137 if ((netid = getenv("NLSPROVIDER")) == NULL) {
138 struct t_info tinfo;
140 if (t_sync(0) == -1) {
141 syslog(LOG_ERR, "could not do t_sync");
142 zexit(1);
144 if (t_getinfo(0, &tinfo) == -1) {
145 syslog(LOG_ERR, "t_getinfo failed");
146 zexit(1);
148 if (tinfo.servtype == T_CLTS) {
149 if (tinfo.addr == INET_ADDRSTRLEN)
150 netid = "udp";
151 else
152 netid = "udp6";
153 } else {
154 syslog(LOG_ERR, "wrong transport");
155 zexit(1);
158 if ((nconf = getnetconfigent(netid)) == NULL) {
159 syslog(LOG_ERR, "cannot get transport info");
162 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
163 syslog(LOG_ERR, "cannot create server handle");
164 zexit(1);
166 if (nconf)
167 freenetconfigent(nconf);
169 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) {
170 syslog(LOG_ERR,
171 "unable to register (RQUOTAPROG, RQUOTAVERS).");
172 zexit(1);
175 (void) sigset(SIGALRM, (void(*)(int)) closedown);
176 (void) alarm(RPCSVC_CLOSEDOWN);
178 svc_run();
179 zexit(1);
180 /* NOTREACHED */
184 * Started from a shell - fork the daemon.
187 switch (fork()) {
188 case 0: /* child */
189 break;
190 case -1:
191 perror("rquotad: can't fork");
192 zexit(1);
193 default: /* parent */
194 zexit(0);
198 * Close existing file descriptors, open "/dev/null" as
199 * standard input, output, and error, and detach from
200 * controlling terminal.
202 closefrom(0);
203 (void) open("/dev/null", O_RDONLY);
204 (void) open("/dev/null", O_WRONLY);
205 (void) dup(1);
206 (void) setsid();
208 openlog("rquotad", LOG_PID, LOG_DAEMON);
211 * Create datagram service
213 if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) {
214 syslog(LOG_ERR, "couldn't register datagram_v service");
215 zexit(1);
219 * Start serving
221 svc_run();
222 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
223 return (1);
226 void
227 dispatch(rqstp, transp)
228 register struct svc_req *rqstp;
229 register SVCXPRT *transp;
232 request_pending = 1;
234 switch (rqstp->rq_proc) {
235 case NULLPROC:
236 errno = 0;
237 if (!svc_sendreply(transp, xdr_void, 0))
238 log_cant_reply(transp);
239 break;
241 case RQUOTAPROC_GETQUOTA:
242 case RQUOTAPROC_GETACTIVEQUOTA:
243 getquota(rqstp, transp);
244 break;
246 default:
247 svcerr_noproc(transp);
248 break;
251 request_pending = 0;
254 void
255 closedown()
257 if (!request_pending) {
258 int i, openfd;
259 struct t_info tinfo;
261 if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))
262 zexit(0);
264 for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) {
265 if (svc_pollfd[i].fd >= 0)
266 openfd++;
269 if (openfd <= 1)
270 zexit(0);
272 (void) alarm(RPCSVC_CLOSEDOWN);
275 static int
276 getzfsquota(uid_t user, char *dataset, struct dqblk *zq)
278 zfs_handle_t *zhp = NULL;
279 char propname[ZFS_MAXPROPLEN];
280 uint64_t userquota, userused;
282 if (g_zfs == NULL)
283 return (1);
285 if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) {
286 syslog(LOG_ERR, "can not open zfs dataset %s", dataset);
287 return (1);
290 (void) snprintf(propname, sizeof (propname), "userquota@%u", user);
291 if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) {
292 _zfs_close(zhp);
293 return (1);
296 (void) snprintf(propname, sizeof (propname), "userused@%u", user);
297 if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) {
298 _zfs_close(zhp);
299 return (1);
302 zq->dqb_bhardlimit = userquota / DEV_BSIZE;
303 zq->dqb_bsoftlimit = userquota / DEV_BSIZE;
304 zq->dqb_curblocks = userused / DEV_BSIZE;
305 _zfs_close(zhp);
306 return (0);
309 void
310 getquota(rqstp, transp)
311 register struct svc_req *rqstp;
312 register SVCXPRT *transp;
314 struct getquota_args gqa;
315 struct getquota_rslt gqr;
316 struct dqblk dqblk;
317 struct fsquot *fsqp;
318 struct timeval tv;
319 bool_t qactive;
321 gqa.gqa_pathp = NULL; /* let xdr allocate the storage */
322 if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) {
323 svcerr_decode(transp);
324 return;
327 * This authentication is really bogus with the current rpc
328 * authentication scheme. One day we will have something for real.
330 if (rqstp->rq_cred.oa_flavor != AUTH_UNIX ||
331 (((authp) rqstp->rq_clntcred)->aup_uid != 0 &&
332 ((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) {
333 gqr.status = Q_EPERM;
334 goto sendreply;
336 fsqp = findfsq(gqa.gqa_pathp);
337 if (fsqp == NULL) {
338 gqr.status = Q_NOQUOTA;
339 goto sendreply;
342 bzero(&dqblk, sizeof (dqblk));
343 if (strcmp(fsqp->fsq_fstype, MNTTYPE_ZFS) == 0) {
344 if (getzfsquota(gqa.gqa_uid, fsqp->fsq_devname, &dqblk)) {
345 gqr.status = Q_NOQUOTA;
346 goto sendreply;
348 qactive = TRUE;
349 } else {
350 if (quotactl(Q_GETQUOTA, fsqp->fsq_dir,
351 (uid_t)gqa.gqa_uid, &dqblk) != 0) {
352 qactive = FALSE;
353 if ((errno == ENOENT) ||
354 (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) {
355 gqr.status = Q_NOQUOTA;
356 goto sendreply;
360 * If there is no quotas file, don't bother to sync it.
362 if (errno != ENOENT) {
363 if (quotactl(Q_ALLSYNC, fsqp->fsq_dir,
364 (uid_t)gqa.gqa_uid, &dqblk) < 0 &&
365 errno == EINVAL)
366 syslog(LOG_WARNING,
367 "Quotas are not compiled "
368 "into this kernel");
369 if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid,
370 &dqblk) == 0) {
371 gqr.status = Q_NOQUOTA;
372 goto sendreply;
375 } else {
376 qactive = TRUE;
379 * We send the remaining time instead of the absolute time
380 * because clock skew between machines should be much greater
381 * than rpc delay.
383 #define gqrslt getquota_rslt_u.gqr_rquota
385 gettimeofday(&tv, NULL);
386 gqr.gqrslt.rq_btimeleft = dqblk.dqb_btimelimit - tv.tv_sec;
387 gqr.gqrslt.rq_ftimeleft = dqblk.dqb_ftimelimit - tv.tv_sec;
390 gqr.status = Q_OK;
391 gqr.gqrslt.rq_active = qactive;
392 gqr.gqrslt.rq_bsize = DEV_BSIZE;
393 gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit;
394 gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit;
395 gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks;
396 gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit;
397 gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit;
398 gqr.gqrslt.rq_curfiles = dqblk.dqb_curfiles;
399 sendreply:
400 errno = 0;
401 if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr))
402 log_cant_reply(transp);
406 quotactl(cmd, mountp, uid, dqp)
407 int cmd;
408 char *mountp;
409 uid_t uid;
410 struct dqblk *dqp;
412 int fd;
413 int status;
414 struct quotctl quota;
415 char mountpoint[256];
416 FILE *fstab;
417 struct mnttab mntp;
419 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
421 * Find the mount point of any ufs file system. this is
422 * because the ioctl that implements the quotactl call has
423 * to go to a real file, and not to the block device.
425 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
426 syslog(LOG_ERR, "can not open %s: %m ", MNTTAB);
427 return (-1);
429 fd = -1;
430 while ((status = getmntent(fstab, &mntp)) == NULL) {
431 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
432 !(hasmntopt(&mntp, MNTOPT_RQ) ||
433 hasmntopt(&mntp, MNTOPT_QUOTA)))
434 continue;
435 (void) strlcpy(mountpoint, mntp.mnt_mountp,
436 sizeof (mountpoint));
437 strcat(mountpoint, "/quotas");
438 if ((fd = open64(mountpoint, O_RDWR)) >= 0)
439 break;
441 fclose(fstab);
442 if (fd == -1) {
443 errno = ENOENT;
444 return (-1);
446 } else {
447 if (mountp == NULL || mountp[0] == '\0') {
448 errno = ENOENT;
449 return (-1);
451 (void) strlcpy(mountpoint, mountp, sizeof (mountpoint));
452 strcat(mountpoint, "/quotas");
454 if ((fd = open64(mountpoint, O_RDONLY)) < 0) {
455 errno = ENOENT;
456 syslog(LOG_ERR, "can not open %s: %m ", mountpoint);
457 return (-1);
460 quota.op = cmd;
461 quota.uid = uid;
462 quota.addr = (caddr_t)dqp;
464 status = ioctl(fd, Q_QUOTACTL, &quota);
466 close(fd);
467 return (status);
471 * Return the quota information for the given path. Returns NULL if none
472 * was found.
475 struct fsquot *
476 findfsq(char *dir)
478 struct stat sb;
479 struct fsquot *fsqp;
480 static time_t lastmtime = 0; /* mount table's previous mtime */
483 * If we've never looked at the mount table, or it has changed
484 * since the last time, rebuild the list of quota'd file systems
485 * and remember the current mod time for the mount table.
488 if (stat(MNTTAB, &sb) < 0) {
489 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB);
490 return (NULL);
492 if (lastmtime == 0 || sb.st_mtime != lastmtime) {
493 freefs();
494 setupfs();
495 lastmtime = sb.st_mtime;
499 * Try to find the given path in the list of file systems with
500 * quotas.
503 if (fsqlist == NULL)
504 return (NULL);
505 if (stat(dir, &sb) < 0)
506 return (NULL);
508 for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) {
509 if (sb.st_dev == fsqp->fsq_dev)
510 return (fsqp);
513 return (NULL);
516 static void
517 setup_zfs(struct mnttab *mp)
519 struct fsquot *fsqp;
520 struct stat sb;
522 if (stat(mp->mnt_mountp, &sb) < 0)
523 return;
525 fsqp = malloc(sizeof (struct fsquot));
526 if (fsqp == NULL) {
527 syslog(LOG_ERR, "out of memory");
528 zexit(1);
530 fsqp->fsq_dir = strdup(mp->mnt_mountp);
531 fsqp->fsq_devname = strdup(mp->mnt_special);
532 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
533 syslog(LOG_ERR, "out of memory");
534 zexit(1);
537 fsqp->fsq_fstype = MNTTYPE_ZFS;
538 fsqp->fsq_dev = sb.st_dev;
539 fsqp->fsq_next = fsqlist;
540 fsqlist = fsqp;
543 void
544 setupfs()
546 struct fsquot *fsqp;
547 FILE *mt;
548 struct mnttab m;
549 struct stat sb;
550 char qfilename[MAXPATHLEN];
552 mt = fopen(MNTTAB, "r");
553 if (mt == NULL) {
554 syslog(LOG_ERR, "can't read %s: %m", MNTTAB);
555 return;
558 while (getmntent(mt, &m) == 0) {
559 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) {
560 setup_zfs(&m);
561 continue;
564 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0)
565 continue;
566 if (!hasquota(m.mnt_mntopts)) {
567 snprintf(qfilename, sizeof (qfilename), "%s/%s",
568 m.mnt_mountp, QFNAME);
569 if (access(qfilename, F_OK) < 0)
570 continue;
572 if (stat(m.mnt_special, &sb) < 0 ||
573 (sb.st_mode & S_IFMT) != S_IFBLK)
574 continue;
575 fsqp = malloc(sizeof (struct fsquot));
576 if (fsqp == NULL) {
577 syslog(LOG_ERR, "out of memory");
578 zexit(1);
580 fsqp->fsq_dir = strdup(m.mnt_mountp);
581 fsqp->fsq_devname = strdup(m.mnt_special);
582 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
583 syslog(LOG_ERR, "out of memory");
584 zexit(1);
586 fsqp->fsq_fstype = MNTTYPE_UFS;
587 fsqp->fsq_dev = sb.st_rdev;
588 fsqp->fsq_next = fsqlist;
589 fsqlist = fsqp;
591 (void) fclose(mt);
595 * Free the memory used by the current list of quota'd file systems. Nulls
596 * out the list.
599 void
600 freefs()
602 register struct fsquot *fsqp;
604 while ((fsqp = fsqlist) != NULL) {
605 fsqlist = fsqp->fsq_next;
606 free(fsqp->fsq_dir);
607 free(fsqp->fsq_devname);
608 free(fsqp);
613 getdiskquota(fsqp, uid, dqp)
614 struct fsquot *fsqp;
615 uid_t uid;
616 struct dqblk *dqp;
618 int fd;
619 char qfilename[MAXPATHLEN];
621 snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME);
622 if ((fd = open64(qfilename, O_RDONLY)) < 0)
623 return (0);
624 (void) llseek(fd, (offset_t)dqoff(uid), L_SET);
625 if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) {
626 close(fd);
627 return (0);
629 close(fd);
630 if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 &&
631 dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) {
632 return (0);
634 return (1);
638 * Get the client's hostname from the transport handle
639 * If the name is not available then return "(anon)".
641 struct nd_hostservlist *
642 getclientsnames(transp)
643 SVCXPRT *transp;
645 struct netbuf *nbuf;
646 struct netconfig *nconf;
647 static struct nd_hostservlist *serv;
648 static struct nd_hostservlist anon_hsl;
649 static struct nd_hostserv anon_hs;
650 static char anon_hname[] = "(anon)";
651 static char anon_sname[] = "";
653 /* Set up anonymous client */
654 anon_hs.h_host = anon_hname;
655 anon_hs.h_serv = anon_sname;
656 anon_hsl.h_cnt = 1;
657 anon_hsl.h_hostservs = &anon_hs;
659 if (serv) {
660 netdir_free((char *)serv, ND_HOSTSERVLIST);
661 serv = NULL;
663 nconf = getnetconfigent(transp->xp_netid);
664 if (nconf == NULL) {
665 syslog(LOG_ERR, "%s: getnetconfigent failed",
666 transp->xp_netid);
667 return (&anon_hsl);
670 nbuf = svc_getrpccaller(transp);
671 if (nbuf == NULL) {
672 freenetconfigent(nconf);
673 return (&anon_hsl);
675 if (netdir_getbyaddr(nconf, &serv, nbuf)) {
676 freenetconfigent(nconf);
677 return (&anon_hsl);
679 freenetconfigent(nconf);
680 return (serv);
683 void
684 log_cant_reply(transp)
685 SVCXPRT *transp;
687 int saverrno;
688 struct nd_hostservlist *clnames;
689 register char *name;
691 saverrno = errno; /* save error code */
692 clnames = getclientsnames(transp);
693 if (clnames == NULL)
694 return;
695 name = clnames->h_hostservs->h_host;
697 errno = saverrno;
698 if (errno == 0)
699 syslog(LOG_ERR, "couldn't send reply to %s", name);
700 else
701 syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
704 char *mntopts[] = { MNTOPT_QUOTA, NULL };
705 #define QUOTA 0
708 * Return 1 if "quota" appears in the options string
711 hasquota(opts)
712 char *opts;
714 char *value;
716 if (opts == NULL)
717 return (0);
718 while (*opts != '\0') {
719 if (getsubopt(&opts, mntopts, &value) == QUOTA)
720 return (1);
723 return (0);
726 static void
727 zexit(int n)
729 if (g_zfs != NULL)
730 _libzfs_fini(g_zfs);
731 exit(n);