1/*
2 * Copyright (c) 2007-2010 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	$NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $	*/
25
26/*
27 * by Manuel Bouyer (bouyer@ensta.fr). Public domain.
28 */
29
30#include <sys/cdefs.h>
31#ifndef lint
32__RCSID("$NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $");
33#endif
34
35#include <sys/param.h>
36#include <sys/types.h>
37#include <sys/mount.h>
38#include <sys/file.h>
39#include <sys/stat.h>
40#include <sys/socket.h>
41#include <arpa/inet.h>
42#include <sys/event.h>
43#include <sys/time.h>
44#include <signal.h>
45#include <sys/queue.h>
46#include <sys/quota.h>
47
48#include <stdio.h>
49#include <fstab.h>
50#include <ctype.h>
51#include <stdlib.h>
52#include <string.h>
53#include <pwd.h>
54#include <grp.h>
55#include <errno.h>
56#include <unistd.h>
57#include <err.h>
58#include <pthread.h>
59#include <syslog.h>
60#include <libutil.h>
61#include <util.h>
62
63#include <oncrpc/rpc.h>
64#include <oncrpc/rpcb.h>
65#include "rquota.h"
66
67int bindresvport_sa(int sd, struct sockaddr *sa);
68
69void rquota_service(struct svc_req *request, SVCXPRT *transp);
70void ext_rquota_service(struct svc_req *request, SVCXPRT *transp);
71void sendquota(struct svc_req *request, int vers, SVCXPRT *transp);
72int getfsquota(int type, int id, char *path, struct dqblk *dqblk);
73int hasquota(struct statfs *fst, char **uqfnamep, char **gqfnamep);
74void lock_fsq(void);
75void unlock_fsq(void);
76void check_mounts(void);
77void sigmux(int);
78void *rquotad_thread(void *arg);
79
80#if 0
81#define DEBUG(args...)	printf(args)
82#else
83#define DEBUG(args...)
84#endif
85
86#define _PATH_NFS_CONF		"/etc/nfs.conf"
87#define _PATH_RQUOTAD_PID	"/var/run/rquotad.pid"
88
89/*
90 * structure holding NFS server config values
91 */
92struct nfs_conf_server {
93	int rquota_port;
94	int tcp;
95	int udp;
96	int verbose;
97};
98const struct nfs_conf_server config_defaults =
99{
100	0,		/* rquota_port */
101	1,		/* tcp */
102	1,		/* udp */
103	0		/* verbose */
104};
105int config_read(struct nfs_conf_server *conf);
106
107/*
108 * structure containing informations about file systems with quota files
109 */
110struct fsq_stat {
111	TAILQ_ENTRY(fsq_stat) chain;	/* list of file systems */
112	char   *mountdir;		/* mount point of the filesystem */
113	char   *uqfpathname;		/* pathname of the user quota file */
114	char   *gqfpathname;		/* pathname of the group quota file */
115	fsid_t	fsid;			/* fsid for the file system */
116	dev_t   st_dev;			/* device of the filesystem */
117};
118TAILQ_HEAD(fsqhead,fsq_stat) fsqhead;
119pthread_mutex_t fsq_mutex;		/* mutex for file system quota list */
120int gotterm = 0;
121struct nfs_conf_server config;
122
123const char *qfextension[] = INITQFNAMES;
124
125void
126sigmux(__unused int dummy)
127{
128	gotterm = 1;
129}
130
131int
132main(__unused int argc, __unused char *argv[])
133{
134	SVCXPRT *transp;
135	struct sockaddr_storage saddr;
136	struct sockaddr_in *sin = (struct sockaddr_in*)&saddr;
137	struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&saddr;
138	int error, on = 1, svcregcnt;
139	pthread_attr_t pattr;
140	pthread_t thd;
141	int kq, rv;
142	struct kevent ke;
143	struct pidfh *pfh;
144	pid_t pid;
145	int rqudpsock, rqtcpsock;
146	int rqudp6sock, rqtcp6sock;
147
148	/* If we are serving UDP, set up the RQUOTA/UDP sockets. */
149
150	/* set defaults then do config_read() to get config values */
151	config = config_defaults;
152	config_read(&config);
153
154	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
155
156	/* claim PID file */
157	pfh = pidfile_open(_PATH_RQUOTAD_PID, 0644, &pid);
158	if (pfh == NULL) {
159		syslog(LOG_ERR, "can't open rquotad pidfile: %s (%d)", strerror(errno), errno);
160		if ((errno == EACCES) && getuid())
161			syslog(LOG_ERR, "rquotad is expected to be run as root, not as uid %d.", getuid());
162		else if (errno == EEXIST)
163			syslog(LOG_ERR, "rquotad already running, pid: %d", pid);
164		exit(2);
165	}
166	if (pidfile_write(pfh) == -1)
167		syslog(LOG_WARNING, "can't write to rquotad pidfile: %s (%d)", strerror(errno), errno);
168
169	rpcb_unset(NULL, RQUOTAPROG, RQUOTAVERS);
170	rpcb_unset(NULL, RQUOTAPROG, EXT_RQUOTAVERS);
171
172	signal(SIGINT, sigmux);
173	signal(SIGTERM, sigmux);
174	signal(SIGHUP, sigmux);
175
176	/* create and register the service */
177	rqudpsock = rqtcpsock = -1;
178	rqudp6sock = rqtcp6sock = -1;
179
180	/* If we are serving UDP, set up the RQUOTA/UDP sockets. */
181	if (config.udp) {
182
183		/* IPv4 */
184		if ((rqudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
185			syslog(LOG_WARNING, "can't create UDP IPv4 socket: %s (%d)", strerror(errno), errno);
186		if (rqudpsock >= 0) {
187			sin->sin_family = AF_INET;
188			sin->sin_addr.s_addr = INADDR_ANY;
189			sin->sin_port = htons(config.rquota_port);
190			sin->sin_len = sizeof(*sin);
191			if (bindresvport_sa(rqudpsock, (struct sockaddr *)sin) < 0) {
192				syslog(LOG_WARNING, "can't bind UDP IPv4 addr: %s (%d)", strerror(errno), errno);
193				close(rqudpsock);
194				rqudpsock = -1;
195			}
196		}
197		if ((rqudpsock >= 0) && ((transp = svcudp_create(rqudpsock)) == NULL)) {
198			syslog(LOG_WARNING, "cannot create UDP IPv4 service");
199			close(rqudpsock);
200			rqudpsock = -1;
201		}
202		if (rqudpsock >= 0) {
203			svcregcnt = 0;
204			if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_UDP))
205				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, UDP/IPv4)");
206			else
207				svcregcnt++;
208			if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_UDP))
209				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP/IPv4)");
210			else
211				svcregcnt++;
212			if (!svcregcnt) {
213				svc_destroy(transp);
214				close(rqudpsock);
215				rqudpsock = -1;
216			}
217		}
218
219		/* IPv6 */
220		if ((rqudp6sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
221			syslog(LOG_WARNING, "can't create UDP IPv6 socket: %s (%d)", strerror(errno), errno);
222		if (rqudp6sock >= 0) {
223			setsockopt(rqudp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
224			sin6->sin6_family = AF_INET6;
225			sin6->sin6_addr = in6addr_any;
226			sin6->sin6_port = htons(config.rquota_port);
227			sin6->sin6_len = sizeof(*sin6);
228			if (bindresvport_sa(rqudp6sock, (struct sockaddr *)sin6) < 0) {
229				syslog(LOG_WARNING, "can't bind UDP IPv6 addr: %s (%d)", strerror(errno), errno);
230				close(rqudp6sock);
231				rqudp6sock = -1;
232			}
233		}
234		if ((rqudp6sock >= 0) && ((transp = svcudp_create(rqudp6sock)) == NULL)) {
235			syslog(LOG_WARNING, "cannot create UDP IPv6 service");
236			close(rqudp6sock);
237			rqudp6sock = -1;
238		}
239		if (rqudp6sock >= 0) {
240			svcregcnt = 0;
241			if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_UDP))
242				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, UDP/IPv6)");
243			else
244				svcregcnt++;
245			if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_UDP))
246				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP/IPv6)");
247			else
248				svcregcnt++;
249			if (!svcregcnt) {
250				svc_destroy(transp);
251				close(rqudp6sock);
252				rqudp6sock = -1;
253			}
254		}
255	}
256
257	/* If we are serving TCP, set up the RQUOTA/TCP sockets. */
258	if (config.tcp) {
259
260		/* IPv4 */
261		if ((rqtcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
262			syslog(LOG_WARNING, "can't create TCP IPv4 socket: %s (%d)", strerror(errno), errno);
263		if (rqtcpsock >= 0) {
264			if (setsockopt(rqtcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
265				syslog(LOG_WARNING, "setsockopt TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
266			sin->sin_family = AF_INET;
267			sin->sin_addr.s_addr = INADDR_ANY;
268			sin->sin_port = htons(config.rquota_port);
269			sin->sin_len = sizeof(*sin);
270			if (bindresvport_sa(rqtcpsock, (struct sockaddr *)sin) < 0) {
271				syslog(LOG_WARNING, "can't bind TCP IPv4 addr: %s (%d)", strerror(errno), errno);
272				close(rqtcpsock);
273				rqtcpsock = -1;
274			}
275		}
276		if ((rqtcpsock >= 0) && ((transp = svctcp_create(rqtcpsock, 0, 0)) == NULL)) {
277			syslog(LOG_WARNING, "cannot create TCP IPv4 service");
278			close(rqtcpsock);
279			rqtcpsock = -1;
280		}
281		if (rqtcpsock >= 0) {
282			svcregcnt = 0;
283			if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_TCP))
284				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, TCP/IPv4)");
285			else
286				svcregcnt++;
287			if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_TCP))
288				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP/IPv4)");
289			else
290				svcregcnt++;
291			if (!svcregcnt) {
292				svc_destroy(transp);
293				close(rqtcpsock);
294				rqtcpsock = -1;
295			}
296		}
297
298		/* IPv6 */
299		if ((rqtcp6sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
300			syslog(LOG_WARNING, "can't create TCP IPv6 socket: %s (%d)", strerror(errno), errno);
301		if (rqtcp6sock >= 0) {
302			if (setsockopt(rqtcp6sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
303				syslog(LOG_WARNING, "setsockopt TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
304			setsockopt(rqtcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
305			sin6->sin6_family = AF_INET6;
306			sin6->sin6_addr = in6addr_any;
307			sin6->sin6_port = htons(config.rquota_port);
308			sin6->sin6_len = sizeof(*sin6);
309			if (bindresvport_sa(rqtcp6sock, (struct sockaddr *)sin6) < 0) {
310				syslog(LOG_WARNING, "can't bind TCP IPv6 addr: %s (%d)", strerror(errno), errno);
311				close(rqtcp6sock);
312				rqtcp6sock = -1;
313			}
314		}
315		if ((rqtcp6sock >= 0) && ((transp = svctcp_create(rqtcp6sock, 0, 0)) == NULL)) {
316			syslog(LOG_WARNING, "cannot create TCP IPv6 service");
317			close(rqtcp6sock);
318			rqtcp6sock = -1;
319		}
320		if (rqtcp6sock >= 0) {
321			svcregcnt = 0;
322			if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_TCP))
323				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, TCP/IPv6)");
324			else
325				svcregcnt++;
326			if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_TCP))
327				syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP/IPv6)");
328			else
329				svcregcnt++;
330			if (!svcregcnt) {
331				svc_destroy(transp);
332				close(rqtcp6sock);
333				rqtcp6sock = -1;
334			}
335		}
336	}
337
338	if ((rqudp6sock < 0) && (rqtcp6sock < 0))
339		syslog(LOG_WARNING, "Can't create RQUOTA IPv6 sockets");
340	if ((rqudpsock < 0) && (rqtcpsock < 0))
341		syslog(LOG_WARNING, "Can't create RQUOTA IPv4 sockets");
342	if ((rqudp6sock < 0) && (rqtcp6sock < 0) &&
343	    (rqudpsock < 0) && (rqtcpsock < 0)) {
344		syslog(LOG_ERR, "Can't create any RQUOTA sockets!");
345		exit(1);
346	}
347
348	/* init file system quotas list */
349	error = pthread_mutex_init(&fsq_mutex, NULL);
350	if (error) {
351		syslog(LOG_ERR, "file system quota mutex init failed: %s (%d)", strerror(error), error);
352		exit(1);
353	}
354	TAILQ_INIT(&fsqhead);
355	check_mounts();
356
357	/* launch rquotad pthread */
358	pthread_attr_init(&pattr);
359	pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
360	error = pthread_create(&thd, &pattr, rquotad_thread, NULL);
361	if (error) {
362		syslog(LOG_ERR, "rquotad pthread_create: %s (%d)", strerror(error), error);
363		exit(1);
364	}
365
366	/* sit around waiting for mount/unmount events and/or a signal */
367	if ((kq = kqueue()) < 0) {
368		syslog(LOG_ERR, "kqueue: %s (%d)", strerror(errno), errno);
369		exit(1);
370	}
371	EV_SET(&ke, 0, EVFILT_FS, EV_ADD, 0, 0, 0);
372	rv = kevent(kq, &ke, 1, NULL, 0, NULL);
373	if (rv < 0) {
374		syslog(LOG_ERR, "kevent(EVFILT_FS): %s (%d)", strerror(errno), errno);
375		exit(1);
376	}
377
378	while (!gotterm) {
379		rv = kevent(kq, NULL, 0, &ke, 1, NULL);
380		if ((rv > 0) && !(ke.flags & EV_ERROR) && (ke.fflags & (VQ_MOUNT|VQ_UNMOUNT))) {
381			/* syslog(LOG_DEBUG, "mount list changed: 0x%x", ke.fflags); */
382			check_mounts();
383		}
384	}
385
386	alarm(1); /* XXX 5028243 in case rpcb_unset() gets hung up during shutdown */
387	rpcb_unset(NULL, RQUOTAPROG, RQUOTAVERS);
388	rpcb_unset(NULL, RQUOTAPROG, EXT_RQUOTAVERS);
389	pidfile_remove(pfh);
390	exit(0);
391}
392
393/*
394 * The incredibly complex rquotad thread function
395 */
396void *
397rquotad_thread(__unused void *arg)
398{
399	sigset_t sigset;
400
401	sigemptyset(&sigset);
402	sigaddset(&sigset, SIGINT);
403	sigaddset(&sigset, SIGQUIT);
404	sigaddset(&sigset, SIGSYS);
405	sigaddset(&sigset, SIGPIPE);
406	sigaddset(&sigset, SIGTERM);
407	sigaddset(&sigset, SIGHUP);
408	sigaddset(&sigset, SIGUSR1);
409	sigaddset(&sigset, SIGUSR2);
410	sigaddset(&sigset, SIGABRT);
411	pthread_sigmask(SIG_BLOCK, &sigset, NULL);
412
413	svc_run();
414	syslog(LOG_ERR, "rquotad died");
415	exit(1);
416}
417
418void
419rquota_service(struct svc_req *request, SVCXPRT *transp)
420{
421	switch (request->rq_proc) {
422	case NULLPROC:
423		svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
424		break;
425
426	case RQUOTAPROC_GETQUOTA:
427	case RQUOTAPROC_GETACTIVEQUOTA:
428		sendquota(request, RQUOTAVERS, transp);
429		break;
430
431	default:
432		svcerr_noproc(transp);
433		break;
434	}
435}
436
437void
438ext_rquota_service(struct svc_req *request, SVCXPRT *transp)
439{
440	switch (request->rq_proc) {
441	case NULLPROC:
442		svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
443		break;
444
445	case RQUOTAPROC_GETQUOTA:
446	case RQUOTAPROC_GETACTIVEQUOTA:
447		sendquota(request, EXT_RQUOTAVERS, transp);
448		break;
449
450	default:
451		svcerr_noproc(transp);
452		break;
453	}
454}
455
456int
457ismember(struct authunix_parms *aup, int gid)
458{
459	uint g;
460
461	if (aup->aup_gid == (uint32_t)gid)
462		return (1);
463	for (g=0; g < aup->aup_len; g++)
464		if (aup->aup_gids[g] == (uint32_t)gid)
465			return (1);
466	return (0);
467}
468
469/* read quota for the specified id, and send it */
470void
471sendquota(struct svc_req *request, int vers, SVCXPRT *transp)
472{
473	struct getquota_args getq_args;
474	struct ext_getquota_args ext_getq_args;
475	struct getquota_rslt getq_rslt;
476	struct dqblk dqblk;
477	struct timeval timev;
478	struct authunix_parms *aup;
479
480	memset((char *)&getq_args, 0, sizeof(getq_args));
481	memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args));
482	switch (vers) {
483	case RQUOTAVERS:
484		if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args,
485		    (caddr_t)&getq_args)) {
486			svcerr_decode(transp);
487			return;
488		}
489		ext_getq_args.gqa_pathp = getq_args.gqa_pathp;
490		ext_getq_args.gqa_id = getq_args.gqa_uid;
491		ext_getq_args.gqa_type = RQUOTA_USRQUOTA;
492		break;
493	case EXT_RQUOTAVERS:
494		if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args,
495		    (caddr_t)&ext_getq_args)) {
496			svcerr_decode(transp);
497			return;
498		}
499		break;
500	}
501	aup = (struct authunix_parms *)request->rq_clntcred;
502	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
503		/* bad auth */
504		getq_rslt.status = Q_EPERM;
505	} else if ((ext_getq_args.gqa_type == RQUOTA_USRQUOTA) && aup->aup_uid &&
506	    (aup->aup_uid != (uint32_t)ext_getq_args.gqa_id)) {
507		/* only allow user or root to get a user quota */
508		getq_rslt.status = Q_EPERM;
509	} else if ((ext_getq_args.gqa_type == RQUOTA_GRPQUOTA) && aup->aup_uid &&
510	    !ismember(aup, ext_getq_args.gqa_id)) {
511		/* only allow root or group members to get a group quota */
512		getq_rslt.status = Q_EPERM;
513	} else if (!getfsquota(ext_getq_args.gqa_type, ext_getq_args.gqa_id,
514	    ext_getq_args.gqa_pathp, &dqblk)) {
515		/* failed, return noquota */
516		getq_rslt.status = Q_NOQUOTA;
517	} else {
518		uint32_t bsize = DEV_BSIZE;
519#define CLAMP_MAX_32(V)	(((V) > UINT32_MAX) ? UINT32_MAX : (V))
520
521		/* scale the block size up to fit values into 32 bits */
522		while ((bsize < INT32_MAX) &&
523			(((dqblk.dqb_bhardlimit / bsize) > UINT32_MAX) ||
524			 ((dqblk.dqb_bsoftlimit / bsize) > UINT32_MAX) ||
525			 ((dqblk.dqb_curbytes / bsize) > UINT32_MAX)))
526			bsize <<= 1;
527
528		gettimeofday(&timev, NULL);
529		getq_rslt.status = Q_OK;
530		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
531		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = bsize;
532		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
533			CLAMP_MAX_32(dqblk.dqb_bhardlimit / bsize);
534		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
535			CLAMP_MAX_32(dqblk.dqb_bsoftlimit / bsize);
536		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
537			CLAMP_MAX_32(dqblk.dqb_curbytes / bsize);
538		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
539			CLAMP_MAX_32(dqblk.dqb_ihardlimit);
540		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
541			CLAMP_MAX_32(dqblk.dqb_isoftlimit);
542		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
543			CLAMP_MAX_32(dqblk.dqb_curinodes);
544		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
545		    dqblk.dqb_btime - timev.tv_sec;
546		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
547		    dqblk.dqb_itime - timev.tv_sec;
548	}
549	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
550		svcerr_systemerr(transp);
551	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
552		syslog(LOG_ERR, "unable to free arguments");
553		exit(1);
554	}
555}
556
557/*
558 * gets the quotas for id, filesystem path.
559 * Return 0 if fail, 1 otherwise
560 */
561int
562getfsquota(int type, int id, char *path, struct dqblk *dqblk)
563{
564	struct stat st_path;
565	struct fsq_stat *fs;
566	int	qcmd, fd, ret = 0;
567	char *filename;
568
569	qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA);
570
571	lock_fsq();
572
573	/* first, ask for quota directly */
574	if (quotactl(path, qcmd, id, (char*)dqblk) == 0) {
575		ret = 1;
576		goto out;
577	}
578
579	/* otherwise, search/check manually */
580	if (stat(path, &st_path) < 0) {
581		ret = 0;
582		goto out;
583	}
584
585	TAILQ_FOREACH(fs, &fsqhead, chain) {
586		/* where the device is the same as path */
587		if (st_path.st_dev < fs->st_dev) {
588			ret = 0;
589			goto out;
590		}
591		if (fs->st_dev != st_path.st_dev)
592			continue;
593
594		filename = (type == RQUOTA_USRQUOTA) ?
595		    fs->uqfpathname : fs->gqfpathname;
596		if (filename == NULL) {
597			ret = 0;
598			goto out;
599		}
600		if ((fd = open(filename, O_RDONLY)) < 0) {
601			syslog(LOG_WARNING, "open error: %s: %m", filename);
602			ret = 0;
603			goto out;
604		}
605		if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET)
606		    == (off_t)-1) {
607			close(fd);
608			ret = 0;
609			goto out;
610		}
611		switch (read(fd, dqblk, sizeof(struct dqblk))) {
612		case 0:
613			/*
614                         * Convert implicit 0 quota (EOF)
615                         * into an explicit one (zero'ed dqblk)
616                         */
617			memset((caddr_t) dqblk, 0, sizeof(struct dqblk));
618			ret = 1;
619			break;
620		case sizeof(struct dqblk):	/* OK */
621			ret = 1;
622			break;
623		default:	/* ERROR */
624			syslog(LOG_WARNING, "read error: %s: %m", filename);
625			close(fd);
626			ret = 0;
627			goto out;
628		}
629		close(fd);
630	}
631out:
632	unlock_fsq();
633	return (ret);
634}
635
636/*
637 * Check to see if a particular quota is to be enabled.
638 * Comes from quota.c, NetBSD 0.9
639 */
640int
641hasquota(struct statfs *fst, char **uqfnamep, char **gqfnamep)
642{
643	static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN];
644	struct stat sb;
645	int qcnt = 0;
646
647	/*
648	  From quota.c:
649	  We only support the default path to the
650	  on disk quota files.
651	*/
652
653	snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, QUOTAOPSNAME, qfextension[USRQUOTA] );
654	if (stat(buf, &sb) == 0) {
655		snprintf(ubuf, sizeof(ubuf), "%s/%s.%s", fst->f_mntonname, QUOTAFILENAME, qfextension[USRQUOTA]);
656		*uqfnamep = ubuf;
657		qcnt++;
658	}
659	snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, QUOTAOPSNAME, qfextension[GRPQUOTA] );
660	if (stat(buf, &sb) == 0) {
661		snprintf(gbuf, sizeof(gbuf), "%s/%s.%s", fst->f_mntonname, QUOTAFILENAME, qfextension[GRPQUOTA]);
662		*gqfnamep = gbuf;
663		qcnt++;
664	}
665	return (qcnt);
666}
667
668/* functions for locking/unlocking the file system quota list */
669void
670lock_fsq(void)
671{
672	int error = pthread_mutex_lock(&fsq_mutex);
673	if (error)
674		syslog(LOG_ERR, "mutex lock failed: %s (%d)", strerror(error), error);
675}
676void
677unlock_fsq(void)
678{
679	int error = pthread_mutex_unlock(&fsq_mutex);
680	if (error)
681		syslog(LOG_ERR, "mutex unlock failed: %s (%d)", strerror(error), error);
682}
683
684/* functions for adding/deleting entries from the file system quota list */
685static void
686fsadd(struct statfs *fst)
687{
688	struct fsq_stat *fs = NULL, *fs2;
689	char *uqfpathname = NULL, *gqfpathname = NULL;
690	struct stat st;
691
692	if (strcmp(fst->f_fstypename, "hfs") &&
693	    strcmp(fst->f_fstypename, "ufs"))
694		return;
695	if (!hasquota(fst, &uqfpathname, &gqfpathname))
696		return;
697
698	fs = (struct fsq_stat *) malloc(sizeof(*fs));
699	if (fs == NULL) {
700		syslog(LOG_ERR, "can't malloc: %m");
701		return;
702	}
703	bzero(fs, sizeof(*fs));
704
705	fs->mountdir = strdup(fst->f_mntonname);
706	if (fs->mountdir == NULL) {
707		syslog(LOG_ERR, "can't strdup: %m");
708		goto failed;
709	}
710
711	if (uqfpathname) {
712		fs->uqfpathname = strdup(uqfpathname);
713		if (fs->uqfpathname == NULL) {
714			syslog(LOG_ERR, "can't strdup: %m");
715			goto failed;
716		}
717	}
718	if (gqfpathname) {
719		fs->gqfpathname = strdup(gqfpathname);
720		if (fs->gqfpathname == NULL) {
721			syslog(LOG_ERR, "can't strdup: %m");
722			goto failed;
723		}
724	}
725	if (stat(fst->f_mntonname, &st))
726		goto failed;
727	fs->st_dev = st.st_dev;
728	fs->fsid.val[0] = fst->f_fsid.val[0];
729	fs->fsid.val[1] = fst->f_fsid.val[1];
730
731	/* insert it into the list by st_dev order */
732	TAILQ_FOREACH(fs2, &fsqhead, chain) {
733		if (fs->st_dev < fs2->st_dev)
734			break;
735	}
736	if (fs2)
737		TAILQ_INSERT_BEFORE(fs2, fs, chain);
738	else
739		TAILQ_INSERT_TAIL(&fsqhead, fs, chain);
740
741	return;
742failed:
743	if (fs->gqfpathname)
744		free(fs->gqfpathname);
745	if (fs->uqfpathname)
746		free(fs->uqfpathname);
747	if (fs->mountdir)
748		free(fs->mountdir);
749	free(fs);
750	return;
751}
752static void
753fsdel(struct statfs *fst)
754{
755	struct fsq_stat *fs;
756
757	TAILQ_FOREACH(fs, &fsqhead, chain) {
758		if ((fs->fsid.val[0] != fst->f_fsid.val[0]) ||
759		    (fs->fsid.val[1] != fst->f_fsid.val[1]))
760		    	continue;
761		if (strcmp(fs->mountdir, fst->f_mntonname))
762			continue;
763		break;
764	}
765	if (!fs)
766		return;
767	TAILQ_REMOVE(&fsqhead, fs, chain);
768	if (fs->gqfpathname)
769		free(fs->gqfpathname);
770	if (fs->uqfpathname)
771		free(fs->uqfpathname);
772	if (fs->mountdir)
773		free(fs->mountdir);
774	free(fs);
775}
776
777/*
778 * code to monitor list of mounts
779 */
780static struct statfs *sfs[2];
781static int size[2], cnt[2], cur, lastfscnt;
782#define PREV	((cur + 1) & 1)
783
784static int
785sfscmp(const void *arg1, const void *arg2)
786{
787	const struct statfs *sfs1 = arg1;
788	const struct statfs *sfs2 = arg2;
789	return strcmp(sfs1->f_mntonname, sfs2->f_mntonname);
790}
791
792static void
793get_mounts(void)
794{
795	cur = (cur + 1) % 2;
796	while (size[cur] < (lastfscnt = getfsstat(sfs[cur], size[cur] * sizeof(struct statfs), MNT_NOWAIT))) {
797		free(sfs[cur]);
798		size[cur] = lastfscnt + 32;
799		sfs[cur] = malloc(size[cur] * sizeof(struct statfs));
800		if (!sfs[cur])
801			err(1, "no memory");
802	}
803	cnt[cur] = lastfscnt;
804	qsort(sfs[cur], cnt[cur], sizeof(struct statfs), sfscmp);
805}
806
807void
808check_mounts(void)
809{
810	int i, j, cmp;
811
812	lock_fsq();
813	get_mounts();
814
815	for (i=j=0; (i < cnt[PREV]) && (j < cnt[cur]); ) {
816		cmp = sfscmp(&sfs[PREV][i], &sfs[cur][j]);
817		if (!cmp) {
818			i++;
819			j++;
820			continue;
821		}
822		if (cmp < 0) {
823			/* file system no longer mounted */
824			DEBUG("- %s\n", sfs[PREV][i].f_mntonname);
825			fsdel(&sfs[PREV][i]);
826			i++;
827		}
828		if (cmp > 0) {
829			/* file system mounted */
830			DEBUG("+ %s\n", sfs[cur][j].f_mntonname);
831			fsadd(&sfs[cur][j]);
832			j++;
833		}
834	}
835	while (i < cnt[PREV]) {
836		/* file system no longer mounted */
837		DEBUG("- %s\n", sfs[PREV][i].f_mntonname);
838		fsdel(&sfs[PREV][i]);
839		i++;
840	}
841	while (j < cnt[cur]) {
842		/* file system mounted */
843		DEBUG("+ %s\n", sfs[cur][j].f_mntonname);
844		fsadd(&sfs[cur][j]);
845		j++;
846	}
847
848	unlock_fsq();
849}
850
851/*
852 * read the NFS server values from nfs.conf
853 */
854int
855config_read(struct nfs_conf_server *conf)
856{
857	FILE *f;
858	size_t len, linenum = 0;
859	char *line, *p, *key, *value;
860	long val;
861
862	if (!(f = fopen(_PATH_NFS_CONF, "r"))) {
863		if (errno != ENOENT)
864			syslog(LOG_WARNING, "%s", _PATH_NFS_CONF);
865		return (1);
866	}
867
868	for (;(line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) {
869		if (len <= 0)
870			continue;
871		/* trim trailing whitespace */
872		p = line + len - 1;
873		while ((p > line) && isspace(*p))
874			*p-- = '\0';
875		/* find key start */
876		key = line;
877		while (isspace(*key))
878			key++;
879		/* find equals/value */
880		value = p = strchr(line, '=');
881		if (p) /* trim trailing whitespace on key */
882			do { *p-- = '\0'; } while ((p > line) && isspace(*p));
883		/* find value start */
884		if (value)
885			do { value++; } while (isspace(*value));
886
887		/* all server keys start with "nfs.server." */
888		if (strncmp(key, "nfs.server.", 11)) {
889			DEBUG("%4ld %s=%s\n", linenum, key, value ? value : "");
890			continue;
891		}
892
893		val = !value ? 1 : strtol(value, NULL, 0);
894		DEBUG("%4ld %s=%s (%d)\n", linenum, key, value ? value : "", val);
895
896		if (!strcmp(key, "nfs.server.rquota.port")) {
897			if (value && val)
898				conf->rquota_port = val;
899		} else if (!strcmp(key, "nfs.server.rquota.tcp")) {
900			conf->tcp = val;
901		} else if (!strcmp(key, "nfs.server.rquota.udp")) {
902			conf->udp = val;
903		} else if (!strcmp(key, "nfs.server.verbose")) {
904			conf->verbose = val;
905		} else {
906			DEBUG("ignoring unknown config value: %4ld %s=%s\n", linenum, key, value ? value : "");
907		}
908
909	}
910
911	fclose(f);
912	return (0);
913}
914
915