xref: /titanic_41/usr/src/cmd/fs.d/nfs/rquotad/rpc.rquotad.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <syslog.h>
36 #include <string.h>
37 #include <stropts.h>
38 #include <errno.h>
39 #include <sys/netconfig.h>
40 #include <sys/mntent.h>
41 #include <sys/mnttab.h>
42 #include <sys/param.h>
43 #include <sys/time.h>
44 #ifdef notdef
45 #include <netconfig.h>
46 #endif
47 #include <sys/stat.h>
48 #include <sys/file.h>
49 #include <sys/fs/ufs_quota.h>
50 #include <netdir.h>
51 #include <rpc/rpc.h>
52 #include <rpcsvc/rquota.h>
53 #include <tiuser.h>
54 #include <unistd.h>
55 
56 #define	QFNAME		"quotas"	/* name of quota file */
57 #define	RPCSVC_CLOSEDOWN 120		/* 2 minutes */
58 
59 struct fsquot {
60 	struct fsquot *fsq_next;
61 	char *fsq_dir;
62 	char *fsq_devname;
63 	dev_t fsq_dev;
64 };
65 
66 struct fsquot *fsqlist = NULL;
67 
68 typedef struct authunix_parms *authp;
69 
70 static int request_pending;		/* Request in progress ? */
71 
72 void closedown();
73 void dispatch();
74 struct fsquot *findfsq();
75 void freefs();
76 int  getdiskquota();
77 void getquota();
78 int  hasquota();
79 void log_cant_reply();
80 void setupfs();
81 
82 /*ARGSUSED*/
83 main(argc, argv)
84 	int argc;
85 	char **argv;
86 {
87 	register SVCXPRT *transp;
88 
89 	/*
90 	 * If stdin looks like a TLI endpoint, we assume
91 	 * that we were started by a port monitor. If
92 	 * t_getstate fails with TBADF, this is not a
93 	 * TLI endpoint.
94 	 */
95 	if (t_getstate(0) != -1 || t_errno != TBADF) {
96 		char *netid;
97 		struct netconfig *nconf = NULL;
98 
99 		openlog("rquotad", LOG_PID, LOG_DAEMON);
100 
101 		if ((netid = getenv("NLSPROVIDER")) == NULL) {
102 			struct t_info tinfo;
103 
104 			if (t_sync(0) == -1) {
105 				syslog(LOG_ERR, "could not do t_sync");
106 				exit(1);
107 			}
108 			if (t_getinfo(0, &tinfo) == -1) {
109 				syslog(LOG_ERR, "t_getinfo failed");
110 				exit(1);
111 			}
112 			if (tinfo.servtype == T_CLTS) {
113 				if (tinfo.addr == INET_ADDRSTRLEN)
114 					netid = "udp";
115 				else
116 					netid = "udp6";
117 			} else {
118 				syslog(LOG_ERR, "wrong transport");
119 				exit(1);
120 			}
121 		}
122 		if ((nconf = getnetconfigent(netid)) == NULL) {
123 			syslog(LOG_ERR, "cannot get transport info");
124 		}
125 
126 		if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
127 			syslog(LOG_ERR, "cannot create server handle");
128 			exit(1);
129 		}
130 		if (nconf)
131 			freenetconfigent(nconf);
132 
133 		if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) {
134 			syslog(LOG_ERR,
135 				"unable to register (RQUOTAPROG, RQUOTAVERS).");
136 			exit(1);
137 		}
138 
139 		(void) sigset(SIGALRM, (void(*)(int)) closedown);
140 		(void) alarm(RPCSVC_CLOSEDOWN);
141 
142 		svc_run();
143 		exit(1);
144 		/* NOTREACHED */
145 	}
146 
147 	/*
148 	 * Started from a shell - fork the daemon.
149 	 */
150 
151 	switch (fork()) {
152 	case 0:		/* child */
153 		break;
154 	case -1:
155 		perror("rquotad: can't fork");
156 		exit(1);
157 	default:	/* parent */
158 		exit(0);
159 	}
160 
161 	/*
162 	 * Close existing file descriptors, open "/dev/null" as
163 	 * standard input, output, and error, and detach from
164 	 * controlling terminal.
165 	 */
166 	closefrom(0);
167 	(void) open("/dev/null", O_RDONLY);
168 	(void) open("/dev/null", O_WRONLY);
169 	(void) dup(1);
170 	(void) setsid();
171 
172 	openlog("rquotad", LOG_PID, LOG_DAEMON);
173 
174 	/*
175 	 * Create datagram service
176 	 */
177 	if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) {
178 		syslog(LOG_ERR, "couldn't register datagram_v service");
179 		exit(1);
180 	}
181 
182 	/*
183 	 * Start serving
184 	 */
185 	svc_run();
186 	syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
187 	exit(1);
188 	/* NOTREACHED */
189 }
190 
191 void
192 dispatch(rqstp, transp)
193 	register struct svc_req *rqstp;
194 	register SVCXPRT *transp;
195 {
196 
197 	request_pending = 1;
198 
199 	switch (rqstp->rq_proc) {
200 	case NULLPROC:
201 		errno = 0;
202 		if (!svc_sendreply(transp, xdr_void, 0))
203 			log_cant_reply(transp);
204 		break;
205 
206 	case RQUOTAPROC_GETQUOTA:
207 	case RQUOTAPROC_GETACTIVEQUOTA:
208 		getquota(rqstp, transp);
209 		break;
210 
211 	default:
212 		svcerr_noproc(transp);
213 		break;
214 	}
215 
216 	request_pending = 0;
217 }
218 
219 void
220 closedown()
221 {
222 	if (!request_pending) {
223 		int i, openfd;
224 		struct t_info tinfo;
225 
226 		if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))
227 			exit(0);
228 
229 		for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) {
230 			if (svc_pollfd[i].fd >= 0)
231 				openfd++;
232 		}
233 
234 		if (openfd <= 1)
235 			exit(0);
236 	}
237 	(void) alarm(RPCSVC_CLOSEDOWN);
238 }
239 
240 void
241 getquota(rqstp, transp)
242 	register struct svc_req *rqstp;
243 	register SVCXPRT *transp;
244 {
245 	struct getquota_args gqa;
246 	struct getquota_rslt gqr;
247 	struct dqblk dqblk;
248 	struct fsquot *fsqp;
249 	struct timeval tv;
250 	bool_t qactive;
251 
252 	gqa.gqa_pathp = NULL;		/* let xdr allocate the storage */
253 	if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) {
254 		svcerr_decode(transp);
255 		return;
256 	}
257 	/*
258 	 * This authentication is really bogus with the current rpc
259 	 * authentication scheme. One day we will have something for real.
260 	 */
261 	if (rqstp->rq_cred.oa_flavor != AUTH_UNIX ||
262 	    (((authp) rqstp->rq_clntcred)->aup_uid != 0 &&
263 		((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) {
264 		gqr.status = Q_EPERM;
265 		goto sendreply;
266 	}
267 	fsqp = findfsq(gqa.gqa_pathp);
268 	if (fsqp == NULL) {
269 		gqr.status = Q_NOQUOTA;
270 		goto sendreply;
271 	}
272 
273 	if (quotactl(Q_GETQUOTA, fsqp->fsq_dir, (uid_t)gqa.gqa_uid, &dqblk) !=
274 	    0) {
275 		qactive = FALSE;
276 		if ((errno == ENOENT) ||
277 			(rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) {
278 			gqr.status = Q_NOQUOTA;
279 			goto sendreply;
280 		}
281 
282 		/*
283 		 * If there is no quotas file, don't bother to sync it.
284 		 */
285 		if (errno != ENOENT) {
286 			if (quotactl(Q_ALLSYNC, fsqp->fsq_dir,
287 			    (uid_t)gqa.gqa_uid, &dqblk) < 0 &&
288 				errno == EINVAL)
289 				syslog(LOG_WARNING,
290 				    "Quotas are not compiled into this kernel");
291 			if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid, &dqblk) ==
292 			    0) {
293 				gqr.status = Q_NOQUOTA;
294 				goto sendreply;
295 			}
296 		}
297 	} else {
298 		qactive = TRUE;
299 	}
300 	/*
301 	 * We send the remaining time instead of the absolute time
302 	 * because clock skew between machines should be much greater
303 	 * than rpc delay.
304 	 */
305 #define	gqrslt getquota_rslt_u.gqr_rquota
306 
307 	gettimeofday(&tv, NULL);
308 	gqr.status = Q_OK;
309 	gqr.gqrslt.rq_active	= qactive;
310 	gqr.gqrslt.rq_bsize	= DEV_BSIZE;
311 	gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit;
312 	gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit;
313 	gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks;
314 	gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit;
315 	gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit;
316 	gqr.gqrslt.rq_curfiles	= dqblk.dqb_curfiles;
317 	gqr.gqrslt.rq_btimeleft	= dqblk.dqb_btimelimit - tv.tv_sec;
318 	gqr.gqrslt.rq_ftimeleft	= dqblk.dqb_ftimelimit - tv.tv_sec;
319 sendreply:
320 	errno = 0;
321 	if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr))
322 		log_cant_reply(transp);
323 }
324 
325 quotactl(cmd, mountp, uid, dqp)
326 	int	cmd;
327 	char	*mountp;
328 	uid_t	uid;
329 	struct dqblk *dqp;
330 {
331 	int 		fd;
332 	int 		status;
333 	struct quotctl 	quota;
334 	char		mountpoint[256];
335 	FILE		*fstab;
336 	struct mnttab	mntp;
337 
338 	if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
339 		/*
340 		 * Find the mount point of any ufs file system. this is
341 		 * because the ioctl that implements the quotactl call has
342 		 * to go to a real file, and not to the block device.
343 		 */
344 		if ((fstab = fopen(MNTTAB, "r")) == NULL) {
345 			syslog(LOG_ERR, "can not open %s: %m ", MNTTAB);
346 			return (-1);
347 		}
348 		fd = -1;
349 		while ((status = getmntent(fstab, &mntp)) == NULL) {
350 			if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
351 				!(hasmntopt(&mntp, MNTOPT_RQ) ||
352 				hasmntopt(&mntp, MNTOPT_QUOTA)))
353 				continue;
354 			(void) strcpy(mountpoint, mntp.mnt_mountp);
355 			strcat(mountpoint, "/quotas");
356 			if ((fd = open64(mountpoint, O_RDWR)) >= 0)
357 				break;
358 		}
359 		fclose(fstab);
360 		if (fd == -1) {
361 			errno = ENOENT;
362 			return (-1);
363 		}
364 	} else {
365 		if (mountp == NULL || mountp[0] == '\0') {
366 			errno = ENOENT;
367 			return (-1);
368 		}
369 		(void) strcpy(mountpoint, mountp);
370 		strcat(mountpoint, "/quotas");
371 
372 		if ((fd = open64(mountpoint, O_RDONLY)) < 0) {
373 			errno = ENOENT;
374 			syslog(LOG_ERR, "can not open %s: %m ", mountpoint);
375 			return (-1);
376 		}
377 	}
378 	quota.op = cmd;
379 	quota.uid = uid;
380 	quota.addr = (caddr_t)dqp;
381 
382 	status = ioctl(fd, Q_QUOTACTL, &quota);
383 
384 	close(fd);
385 	return (status);
386 }
387 
388 /*
389  * Return the quota information for the given path.  Returns NULL if none
390  * was found.
391  */
392 
393 struct fsquot *
394 findfsq(dir)
395 	char *dir;
396 {
397 	struct stat sb;
398 	register struct fsquot *fsqp;
399 	static time_t lastmtime = 0; 	/* mount table's previous mtime */
400 
401 	/*
402 	 * If we've never looked at the mount table, or it has changed
403 	 * since the last time, rebuild the list of quota'd file systems
404 	 * and remember the current mod time for the mount table.
405 	 */
406 
407 	if (stat(MNTTAB, &sb) < 0) {
408 		syslog(LOG_ERR, "can't stat %s: %m", MNTTAB);
409 		return (NULL);
410 	}
411 	if (lastmtime == 0 || sb.st_mtime != lastmtime) {
412 		freefs();
413 		setupfs();
414 		lastmtime = sb.st_mtime;
415 	}
416 
417 	/*
418 	 * Try to find the given path in the list of file systems with
419 	 * quotas.
420 	 */
421 
422 	if (fsqlist == NULL)
423 		return (NULL);
424 	if (stat(dir, &sb) < 0)
425 		return (NULL);
426 	for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) {
427 		if (sb.st_dev == fsqp->fsq_dev)
428 			return (fsqp);
429 	}
430 	return (NULL);
431 }
432 
433 void
434 setupfs()
435 {
436 	register struct fsquot *fsqp;
437 	FILE *mt;
438 	struct mnttab m;
439 	struct stat sb;
440 	char qfilename[MAXPATHLEN];
441 
442 	mt = fopen(MNTTAB, "r");
443 	if (mt == NULL) {
444 		syslog(LOG_ERR, "can't read %s: %m", MNTTAB);
445 		return;
446 	}
447 
448 	while (getmntent(mt, &m) == 0) {
449 		if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0)
450 			continue;
451 		if (!hasquota(m.mnt_mntopts)) {
452 			sprintf(qfilename, "%s/%s", m.mnt_mountp, QFNAME);
453 			if (access(qfilename, F_OK) < 0)
454 				continue;
455 		}
456 		if (stat(m.mnt_special, &sb) < 0 ||
457 		    (sb.st_mode & S_IFMT) != S_IFBLK)
458 			continue;
459 		fsqp = (struct fsquot *)malloc(sizeof (struct fsquot));
460 		if (fsqp == NULL) {
461 			syslog(LOG_ERR, "out of memory");
462 			exit(1);
463 		}
464 		fsqp->fsq_next = fsqlist;
465 		fsqp->fsq_dir = (char *)malloc(strlen(m.mnt_mountp) + 1);
466 		fsqp->fsq_devname = (char *)malloc(strlen(m.mnt_special) + 1);
467 		if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
468 			syslog(LOG_ERR, "out of memory");
469 			exit(1);
470 		}
471 		strcpy(fsqp->fsq_dir, m.mnt_mountp);
472 		strcpy(fsqp->fsq_devname, m.mnt_special);
473 		fsqp->fsq_dev = sb.st_rdev;
474 		fsqlist = fsqp;
475 	}
476 	(void) fclose(mt);
477 }
478 
479 /*
480  * Free the memory used by the current list of quota'd file systems.  Nulls
481  * out the list.
482  */
483 
484 void
485 freefs()
486 {
487 	register struct fsquot *fsqp;
488 
489 	while ((fsqp = fsqlist) != NULL) {
490 		fsqlist = fsqp->fsq_next;
491 		free(fsqp->fsq_dir);
492 		free(fsqp->fsq_devname);
493 		free(fsqp);
494 	}
495 }
496 
497 int
498 getdiskquota(fsqp, uid, dqp)
499 	struct fsquot *fsqp;
500 	uid_t uid;
501 	struct dqblk *dqp;
502 {
503 	int fd;
504 	char qfilename[MAXPATHLEN];
505 
506 	sprintf(qfilename, "%s/%s", fsqp->fsq_dir, QFNAME);
507 	if ((fd = open64(qfilename, O_RDONLY)) < 0)
508 		return (0);
509 	(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
510 	if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) {
511 		close(fd);
512 		return (0);
513 	}
514 	close(fd);
515 	if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 &&
516 	    dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) {
517 		return (0);
518 	}
519 	return (1);
520 }
521 
522 /*
523  * Get the client's hostname from the transport handle
524  * If the name is not available then return "(anon)".
525  */
526 struct nd_hostservlist *
527 getclientsnames(transp)
528 	SVCXPRT *transp;
529 {
530 	struct netbuf *nbuf;
531 	struct netconfig *nconf;
532 	static struct nd_hostservlist	*serv;
533 	static struct nd_hostservlist	anon_hsl;
534 	static struct nd_hostserv	anon_hs;
535 	static char anon_hname[] = "(anon)";
536 	static char anon_sname[] = "";
537 
538 	/* Set up anonymous client */
539 	anon_hs.h_host = anon_hname;
540 	anon_hs.h_serv = anon_sname;
541 	anon_hsl.h_cnt = 1;
542 	anon_hsl.h_hostservs = &anon_hs;
543 
544 	if (serv) {
545 		netdir_free((char *)serv, ND_HOSTSERVLIST);
546 		serv = NULL;
547 	}
548 	nconf = getnetconfigent(transp->xp_netid);
549 	if (nconf == NULL) {
550 		syslog(LOG_ERR, "%s: getnetconfigent failed",
551 			transp->xp_netid);
552 		return (&anon_hsl);
553 	}
554 
555 	nbuf = svc_getrpccaller(transp);
556 	if (nbuf == NULL) {
557 		freenetconfigent(nconf);
558 		return (&anon_hsl);
559 	}
560 	if (netdir_getbyaddr(nconf, &serv, nbuf)) {
561 		freenetconfigent(nconf);
562 		return (&anon_hsl);
563 	}
564 	freenetconfigent(nconf);
565 	return (serv);
566 }
567 
568 void
569 log_cant_reply(transp)
570 	SVCXPRT *transp;
571 {
572 	int saverrno;
573 	struct nd_hostservlist *clnames;
574 	register char *name;
575 
576 	saverrno = errno;	/* save error code */
577 	clnames = getclientsnames(transp);
578 	if (clnames == NULL)
579 		return;
580 	name = clnames->h_hostservs->h_host;
581 
582 	errno = saverrno;
583 	if (errno == 0)
584 		syslog(LOG_ERR, "couldn't send reply to %s", name);
585 	else
586 		syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
587 }
588 
589 char *mntopts[] = { MNTOPT_QUOTA, NULL };
590 #define	QUOTA    0
591 
592 /*
593  * Return 1 if "quota" appears in the options string
594  */
595 int
596 hasquota(opts)
597 	char *opts;
598 {
599 	char *value;
600 
601 	if (opts == NULL)
602 		return (0);
603 	while (*opts != '\0') {
604 		if (getsubopt(&opts, mntopts, &value) == QUOTA)
605 			return (1);
606 	}
607 
608 	return (0);
609 }
610