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$");
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>
3131420Scharnier#include <string.h>
32184638Sdes#include <syslog.h>
3313237Sgraichen#include <unistd.h>
3413237Sgraichen
35197531Sdesstatic void rquota_service(struct svc_req *request, SVCXPRT *transp);
36197531Sdesstatic void sendquota(struct svc_req *request, SVCXPRT *transp);
37197531Sdesstatic void initfs(void);
38197531Sdesstatic int getfsquota(long id, char *path, struct dqblk *dqblk);
3913237Sgraichen
40207736Smckusickstatic struct quotafile **qfa;	/* array of qfs */
41207736Smckusickstatic int nqf, szqf;		/* number of qfs and size of array */
42197531Sdesstatic int from_inetd = 1;
4313237Sgraichen
44184638Sdesstatic void
4590336Simpcleanup(int sig)
4613237Sgraichen{
47184638Sdes
48197506Sdes	(void)sig;
49197506Sdes	(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
5013237Sgraichen	exit(0);
5113237Sgraichen}
5213237Sgraichen
5313237Sgraichenint
54184638Sdesmain(void)
5513237Sgraichen{
5613237Sgraichen	SVCXPRT *transp;
57100120Salfred	int ok;
58100120Salfred	struct sockaddr_storage from;
59141918Sstefanf	socklen_t fromlen;
6013237Sgraichen
6113237Sgraichen	fromlen = sizeof(from);
62197506Sdes	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
6313237Sgraichen		from_inetd = 0;
6413237Sgraichen
6513237Sgraichen	if (!from_inetd) {
6613237Sgraichen		daemon(0, 0);
67197506Sdes		(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
68197506Sdes		(void)signal(SIGINT, cleanup);
69197506Sdes		(void)signal(SIGTERM, cleanup);
70197506Sdes		(void)signal(SIGHUP, cleanup);
7113237Sgraichen	}
7213237Sgraichen
7313237Sgraichen	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
7413237Sgraichen
7513237Sgraichen	/* create and register the service */
76100120Salfred	if (from_inetd) {
77100120Salfred		transp = svc_tli_create(0, NULL, NULL, 0, 0);
78100120Salfred		if (transp == NULL) {
79100120Salfred			syslog(LOG_ERR, "couldn't create udp service.");
80100120Salfred			exit(1);
81100120Salfred		}
82100120Salfred		ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
83197506Sdes		    rquota_service, NULL);
84197506Sdes	} else {
85100120Salfred		ok = svc_create(rquota_service,
86197506Sdes		    RQUOTAPROG, RQUOTAVERS, "udp");
87197506Sdes	}
88100120Salfred	if (!ok) {
89184638Sdes		syslog(LOG_ERR,
90184638Sdes		    "unable to register (RQUOTAPROG, RQUOTAVERS, %s)",
91184638Sdes		    from_inetd ? "(inetd)" : "udp");
9213237Sgraichen		exit(1);
9313237Sgraichen	}
9413237Sgraichen
95184638Sdes	initfs();
9613237Sgraichen	svc_run();
9713237Sgraichen	syslog(LOG_ERR, "svc_run returned");
9813237Sgraichen	exit(1);
9913237Sgraichen}
10013237Sgraichen
101197531Sdesstatic void
10290336Simprquota_service(struct svc_req *request, SVCXPRT *transp)
10313237Sgraichen{
104184638Sdes
10513237Sgraichen	switch (request->rq_proc) {
10613237Sgraichen	case NULLPROC:
10795658Sdes		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
10813237Sgraichen		break;
10913237Sgraichen	case RQUOTAPROC_GETQUOTA:
11013237Sgraichen	case RQUOTAPROC_GETACTIVEQUOTA:
11113237Sgraichen		sendquota(request, transp);
11213237Sgraichen		break;
11313237Sgraichen	default:
11413237Sgraichen		svcerr_noproc(transp);
11513237Sgraichen		break;
11613237Sgraichen	}
11713237Sgraichen	if (from_inetd)
11813237Sgraichen		exit(0);
11913237Sgraichen}
12013237Sgraichen
12113237Sgraichen/* read quota for the specified id, and send it */
122197531Sdesstatic void
12390336Simpsendquota(struct svc_req *request, SVCXPRT *transp)
12413237Sgraichen{
12513237Sgraichen	struct getquota_args getq_args;
12613237Sgraichen	struct getquota_rslt getq_rslt;
12713237Sgraichen	struct dqblk dqblk;
12813237Sgraichen	struct timeval timev;
129207736Smckusick	int scale;
13013237Sgraichen
131197506Sdes	bzero(&getq_args, sizeof(getq_args));
13295658Sdes	if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
13313237Sgraichen		svcerr_decode(transp);
13413237Sgraichen		return;
13513237Sgraichen	}
13613237Sgraichen	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
13713237Sgraichen		/* bad auth */
13813237Sgraichen		getq_rslt.status = Q_EPERM;
13913237Sgraichen	} else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
14013237Sgraichen		/* failed, return noquota */
14113237Sgraichen		getq_rslt.status = Q_NOQUOTA;
14213237Sgraichen	} else {
14313237Sgraichen		gettimeofday(&timev, NULL);
14413237Sgraichen		getq_rslt.status = Q_OK;
14513237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
146207736Smckusick		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
147207736Smckusick		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
148207736Smckusick		    DEV_BSIZE * scale;
14913237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
150207736Smckusick		    dqblk.dqb_bhardlimit / scale;
15113237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
152207736Smckusick		    dqblk.dqb_bsoftlimit / scale;
15313237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
154207736Smckusick		    dqblk.dqb_curblocks / scale;
15513237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
15613237Sgraichen		    dqblk.dqb_ihardlimit;
15713237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
15813237Sgraichen		    dqblk.dqb_isoftlimit;
15913237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
16013237Sgraichen		    dqblk.dqb_curinodes;
16113237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
16213237Sgraichen		    dqblk.dqb_btime - timev.tv_sec;
16313237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
16413237Sgraichen		    dqblk.dqb_itime - timev.tv_sec;
16513237Sgraichen	}
166197508Sdes	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
16713237Sgraichen		svcerr_systemerr(transp);
16895658Sdes	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
16913237Sgraichen		syslog(LOG_ERR, "unable to free arguments");
17013237Sgraichen		exit(1);
17113237Sgraichen	}
17213237Sgraichen}
17313237Sgraichen
174197531Sdesstatic void
17590336Simpinitfs(void)
17613237Sgraichen{
17713237Sgraichen	struct fstab *fs;
17813237Sgraichen
17913237Sgraichen	setfsent();
180207736Smckusick	szqf = 8;
181207736Smckusick	if ((qfa = malloc(szqf * sizeof *qfa)) == NULL)
182207736Smckusick		goto enomem;
18313237Sgraichen	while ((fs = getfsent())) {
18413237Sgraichen		if (strcmp(fs->fs_vfstype, "ufs"))
18513237Sgraichen			continue;
186207736Smckusick		if (nqf >= szqf) {
187207736Smckusick			szqf *= 2;
188207736Smckusick			if ((qfa = reallocf(qfa, szqf * sizeof *qfa)) == NULL)
189207736Smckusick				goto enomem;
190207736Smckusick		}
191207736Smckusick		if ((qfa[nqf] = quota_open(fs, USRQUOTA, O_RDONLY)) == NULL) {
192207736Smckusick			if (errno != EOPNOTSUPP)
193207736Smckusick				goto fserr;
19413237Sgraichen			continue;
195207736Smckusick		}
196207736Smckusick		++nqf;
197207736Smckusick		/* XXX */
19813237Sgraichen	}
19913237Sgraichen	endfsent();
200207736Smckusick	return;
201207736Smckusickenomem:
202207736Smckusick	syslog(LOG_ERR, "out of memory");
203207736Smckusick	exit(1);
204207736Smckusickfserr:
205207736Smckusick	syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno));
206207736Smckusick	exit(1);
20713237Sgraichen}
20813237Sgraichen
20913237Sgraichen/*
21013237Sgraichen * gets the quotas for id, filesystem path.
21113237Sgraichen * Return 0 if fail, 1 otherwise
21213237Sgraichen */
213197531Sdesstatic int
214184638Sdesgetfsquota(long id, char *path, struct dqblk *dqblk)
21513237Sgraichen{
216207736Smckusick	int i;
21713237Sgraichen
218207736Smckusick	for (i = 0; i < nqf; ++i)
219207736Smckusick		if (quota_check_path(qfa[i], path) == 1)
220207736Smckusick			return (quota_read(qfa[i], dqblk, id) == 0);
221207736Smckusick	return (0);
22213237Sgraichen}
223