113237Sgraichen/*
213237Sgraichen * by Manuel Bouyer (bouyer@ensta.fr)
3184638Sdes *
413237Sgraichen * There is no copyright, you can use it as you want.
513237Sgraichen */
613237Sgraichen
7184638Sdes#include <sys/cdefs.h>
8184638Sdes__FBSDID("$FreeBSD: stable/11/libexec/rpc.rquotad/rquotad.c 339008 2018-09-29 00:44:23Z sef $");
931420Scharnier
1013237Sgraichen#include <sys/param.h>
1113237Sgraichen#include <sys/mount.h>
1213237Sgraichen#include <sys/file.h>
1313237Sgraichen#include <sys/stat.h>
1413237Sgraichen#include <sys/socket.h>
1513237Sgraichen
16184638Sdes#include <ufs/ufs/quota.h>
17184638Sdes#include <rpc/rpc.h>
18184638Sdes#include <rpcsvc/rquota.h>
19184638Sdes#include <arpa/inet.h>
20184638Sdes#include <netdb.h>
21184638Sdes
2231420Scharnier#include <ctype.h>
2331420Scharnier#include <errno.h>
2431420Scharnier#include <fstab.h>
2531420Scharnier#include <grp.h>
26207736Smckusick#include <libutil.h>
2731420Scharnier#include <pwd.h>
28184638Sdes#include <signal.h>
2913237Sgraichen#include <stdio.h>
3013237Sgraichen#include <stdlib.h>
31339008Ssef#include <err.h>
3231420Scharnier#include <string.h>
33184638Sdes#include <syslog.h>
3413237Sgraichen#include <unistd.h>
3513237Sgraichen
36339008Ssefstatic void rquota_service_1(struct svc_req *request, SVCXPRT *transp);
37339008Ssefstatic void rquota_service_2(struct svc_req *request, SVCXPRT *transp);
38197531Sdesstatic void sendquota(struct svc_req *request, SVCXPRT *transp);
39339008Ssefstatic void sendquota_extended(struct svc_req *request, SVCXPRT *transp);
40339008Ssefstatic int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
4113237Sgraichen
42197531Sdesstatic int from_inetd = 1;
43339008Ssefstatic int debug = 0;
4413237Sgraichen
45184638Sdesstatic void
4690336Simpcleanup(int sig)
4713237Sgraichen{
48184638Sdes
49197506Sdes	(void)sig;
50197506Sdes	(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
5113237Sgraichen	exit(0);
5213237Sgraichen}
5313237Sgraichen
5413237Sgraichenint
55339008Ssefmain(int argc, char **argv)
5613237Sgraichen{
5713237Sgraichen	SVCXPRT *transp;
58100120Salfred	int ok;
59100120Salfred	struct sockaddr_storage from;
60141918Sstefanf	socklen_t fromlen;
61339008Ssef	int vers;
62339008Ssef	int ch;
6313237Sgraichen
64339008Ssef	while ((ch = getopt(argc, argv, "d")) != -1) {
65339008Ssef		switch (ch) {
66339008Ssef		case 'd':
67339008Ssef			debug++;
68339008Ssef			break;
69339008Ssef		default:
70339008Ssef			break;
71339008Ssef		}
72339008Ssef	}
73339008Ssef
7413237Sgraichen	fromlen = sizeof(from);
75197506Sdes	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
7613237Sgraichen		from_inetd = 0;
7713237Sgraichen
7813237Sgraichen	if (!from_inetd) {
79339008Ssef		if (!debug)
80339008Ssef			daemon(0, 0);
81197506Sdes		(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
82197506Sdes		(void)signal(SIGINT, cleanup);
83197506Sdes		(void)signal(SIGTERM, cleanup);
84197506Sdes		(void)signal(SIGHUP, cleanup);
8513237Sgraichen	}
8613237Sgraichen
8713237Sgraichen	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
8813237Sgraichen
8913237Sgraichen	/* create and register the service */
90100120Salfred	if (from_inetd) {
91100120Salfred		transp = svc_tli_create(0, NULL, NULL, 0, 0);
92100120Salfred		if (transp == NULL) {
93100120Salfred			syslog(LOG_ERR, "couldn't create udp service.");
94100120Salfred			exit(1);
95100120Salfred		}
96339008Ssef		vers = RQUOTAVERS;
97100120Salfred		ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
98339008Ssef		    rquota_service_1, NULL);
99339008Ssef		if (ok) {
100339008Ssef			vers = EXT_RQUOTAVERS;
101339008Ssef			ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
102339008Ssef				     rquota_service_2, NULL);
103339008Ssef		}
104197506Sdes	} else {
105339008Ssef		vers = RQUOTAVERS;
106339008Ssef		ok = svc_create(rquota_service_1,
107197506Sdes		    RQUOTAPROG, RQUOTAVERS, "udp");
108339008Ssef		if (ok) {
109339008Ssef			vers = EXT_RQUOTAVERS;
110339008Ssef			ok = svc_create(rquota_service_2,
111339008Ssef					RQUOTAPROG, EXT_RQUOTAVERS, "udp");
112339008Ssef
113339008Ssef		}
114197506Sdes	}
115100120Salfred	if (!ok) {
116184638Sdes		syslog(LOG_ERR,
117339008Ssef		    "unable to register (RQUOTAPROG, %s, %s)",
118339008Ssef		       vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS",
119339008Ssef		       from_inetd ? "(inetd)" : "udp");
12013237Sgraichen		exit(1);
12113237Sgraichen	}
12213237Sgraichen
12313237Sgraichen	svc_run();
12413237Sgraichen	syslog(LOG_ERR, "svc_run returned");
12513237Sgraichen	exit(1);
12613237Sgraichen}
12713237Sgraichen
128197531Sdesstatic void
129339008Ssefrquota_service_2(struct svc_req *request, SVCXPRT *transp)
13013237Sgraichen{
131184638Sdes
13213237Sgraichen	switch (request->rq_proc) {
13313237Sgraichen	case NULLPROC:
13495658Sdes		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
13513237Sgraichen		break;
13613237Sgraichen	case RQUOTAPROC_GETQUOTA:
13713237Sgraichen	case RQUOTAPROC_GETACTIVEQUOTA:
138339008Ssef		sendquota_extended(request, transp);
139339008Ssef		break;
140339008Ssef	default:
141339008Ssef		svcerr_noproc(transp);
142339008Ssef		break;
143339008Ssef	}
144339008Ssef	if (from_inetd)
145339008Ssef		exit(0);
146339008Ssef}
147339008Ssef
148339008Ssefstatic void
149339008Ssefrquota_service_1(struct svc_req *request, SVCXPRT *transp)
150339008Ssef{
151339008Ssef
152339008Ssef	switch (request->rq_proc) {
153339008Ssef	case NULLPROC:
154339008Ssef		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
155339008Ssef		break;
156339008Ssef	case RQUOTAPROC_GETQUOTA:
157339008Ssef	case RQUOTAPROC_GETACTIVEQUOTA:
15813237Sgraichen		sendquota(request, transp);
15913237Sgraichen		break;
16013237Sgraichen	default:
16113237Sgraichen		svcerr_noproc(transp);
16213237Sgraichen		break;
16313237Sgraichen	}
16413237Sgraichen	if (from_inetd)
16513237Sgraichen		exit(0);
16613237Sgraichen}
16713237Sgraichen
16813237Sgraichen/* read quota for the specified id, and send it */
169197531Sdesstatic void
17090336Simpsendquota(struct svc_req *request, SVCXPRT *transp)
17113237Sgraichen{
17213237Sgraichen	struct getquota_args getq_args;
17313237Sgraichen	struct getquota_rslt getq_rslt;
17413237Sgraichen	struct dqblk dqblk;
17513237Sgraichen	struct timeval timev;
176207736Smckusick	int scale;
17713237Sgraichen
178197506Sdes	bzero(&getq_args, sizeof(getq_args));
17995658Sdes	if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
18013237Sgraichen		svcerr_decode(transp);
18113237Sgraichen		return;
18213237Sgraichen	}
18313237Sgraichen	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
18413237Sgraichen		/* bad auth */
18513237Sgraichen		getq_rslt.status = Q_EPERM;
186339008Ssef	} else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
18713237Sgraichen		/* failed, return noquota */
18813237Sgraichen		getq_rslt.status = Q_NOQUOTA;
18913237Sgraichen	} else {
19013237Sgraichen		gettimeofday(&timev, NULL);
19113237Sgraichen		getq_rslt.status = Q_OK;
19213237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
193207736Smckusick		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
194207736Smckusick		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
195207736Smckusick		    DEV_BSIZE * scale;
19613237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
197207736Smckusick		    dqblk.dqb_bhardlimit / scale;
19813237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
199207736Smckusick		    dqblk.dqb_bsoftlimit / scale;
20013237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
201207736Smckusick		    dqblk.dqb_curblocks / scale;
20213237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
20313237Sgraichen		    dqblk.dqb_ihardlimit;
20413237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
20513237Sgraichen		    dqblk.dqb_isoftlimit;
20613237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
20713237Sgraichen		    dqblk.dqb_curinodes;
20813237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
20913237Sgraichen		    dqblk.dqb_btime - timev.tv_sec;
21013237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
21113237Sgraichen		    dqblk.dqb_itime - timev.tv_sec;
21213237Sgraichen	}
213197508Sdes	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
21413237Sgraichen		svcerr_systemerr(transp);
21595658Sdes	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
21613237Sgraichen		syslog(LOG_ERR, "unable to free arguments");
21713237Sgraichen		exit(1);
21813237Sgraichen	}
21913237Sgraichen}
22013237Sgraichen
221197531Sdesstatic void
222339008Ssefsendquota_extended(struct svc_req *request, SVCXPRT *transp)
22313237Sgraichen{
224339008Ssef	struct ext_getquota_args getq_args;
225339008Ssef	struct getquota_rslt getq_rslt;
226339008Ssef	struct dqblk dqblk;
227339008Ssef	struct timeval timev;
228339008Ssef	int scale;
22913237Sgraichen
230339008Ssef	bzero(&getq_args, sizeof(getq_args));
231339008Ssef	if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
232339008Ssef		svcerr_decode(transp);
233339008Ssef		return;
23413237Sgraichen	}
235339008Ssef	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
236339008Ssef		/* bad auth */
237339008Ssef		getq_rslt.status = Q_EPERM;
238339008Ssef	} else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) {
239339008Ssef		/* failed, return noquota */
240339008Ssef		getq_rslt.status = Q_NOQUOTA;
241339008Ssef	} else {
242339008Ssef		gettimeofday(&timev, NULL);
243339008Ssef		getq_rslt.status = Q_OK;
244339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
245339008Ssef		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
246339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
247339008Ssef		    DEV_BSIZE * scale;
248339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
249339008Ssef		    dqblk.dqb_bhardlimit / scale;
250339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
251339008Ssef		    dqblk.dqb_bsoftlimit / scale;
252339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
253339008Ssef		    dqblk.dqb_curblocks / scale;
254339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
255339008Ssef		    dqblk.dqb_ihardlimit;
256339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
257339008Ssef		    dqblk.dqb_isoftlimit;
258339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
259339008Ssef		    dqblk.dqb_curinodes;
260339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
261339008Ssef		    dqblk.dqb_btime - timev.tv_sec;
262339008Ssef		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
263339008Ssef		    dqblk.dqb_itime - timev.tv_sec;
264339008Ssef	}
265339008Ssef	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
266339008Ssef		svcerr_systemerr(transp);
267339008Ssef	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
268339008Ssef		syslog(LOG_ERR, "unable to free arguments");
269339008Ssef		exit(1);
270339008Ssef	}
27113237Sgraichen}
27213237Sgraichen
27313237Sgraichen/*
27413237Sgraichen * gets the quotas for id, filesystem path.
27513237Sgraichen * Return 0 if fail, 1 otherwise
27613237Sgraichen */
277197531Sdesstatic int
278339008Ssefgetfsquota(int type, long id, char *path, struct dqblk *dqblk)
27913237Sgraichen{
280339008Ssef	struct quotafile *qf;
281339008Ssef	/*
282339008Ssef	 * Remote quota checking is limited to mounted filesystems.
283339008Ssef	 * Since UFS and ZFS support the quota system calls, we
284339008Ssef	 * only need to make an fstab object that has the path, and
285339008Ssef	 * a blank name for the filesystem type.
286339008Ssef	 * This allows the quota_open() call to work the way we
287339008Ssef	 * expect it to.
288339008Ssef	 *
289339008Ssef	 * The static char declaration is because compiler warnings
290339008Ssef	 * don't allow passing a const char * to a char *.
291339008Ssef	 */
292339008Ssef	int rv;
293339008Ssef	static char blank[] = "";
294339008Ssef	struct fstab fst;
29513237Sgraichen
296339008Ssef	fst.fs_file = path;
297339008Ssef	fst.fs_mntops = blank;
298339008Ssef	fst.fs_vfstype = blank;
299339008Ssef
300339008Ssef	if (type != USRQUOTA && type != GRPQUOTA)
301339008Ssef		return (0);
302339008Ssef
303339008Ssef	qf = quota_open(&fst, type, O_RDONLY);
304339008Ssef	if (debug)
305339008Ssef		warnx("quota_open(<%s, %s>, %d) returned %p",
306339008Ssef		      fst.fs_file, fst.fs_mntops, type,
307339008Ssef		      qf);
308339008Ssef	if (qf == NULL)
309339008Ssef		return (0);
310339008Ssef
311339008Ssef	rv = quota_read(qf, dqblk, id) == 0;
312339008Ssef	quota_close(qf);
313339008Ssef	if (debug)
314339008Ssef		warnx("getfsquota(%d, %ld, %s, %p) -> %d",
315339008Ssef		      type, id, path, dqblk, rv);
316339008Ssef	return (rv);
31713237Sgraichen}
318