rquotad.c revision 197508
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: head/libexec/rpc.rquotad/rquotad.c 197508 2009-09-25 23:03:24Z des $");
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>
2631420Scharnier#include <pwd.h>
27184638Sdes#include <signal.h>
2813237Sgraichen#include <stdio.h>
2913237Sgraichen#include <stdlib.h>
3031420Scharnier#include <string.h>
31184638Sdes#include <syslog.h>
3213237Sgraichen#include <unistd.h>
3313237Sgraichen
3490336Simpvoid rquota_service(struct svc_req *request, SVCXPRT *transp);
3590336Simpvoid sendquota(struct svc_req *request, SVCXPRT *transp);
3690336Simpvoid printerr_reply(SVCXPRT *transp);
3790336Simpvoid initfs(void);
3890336Simpint getfsquota(long id, char *path, struct dqblk *dqblk);
3990336Simpint hasquota(struct fstab *fs, char **qfnamep);
4013237Sgraichen
4113237Sgraichen/*
4213237Sgraichen * structure containing informations about ufs filesystems
4313237Sgraichen * initialised by initfs()
4413237Sgraichen */
4513237Sgraichenstruct fs_stat {
4613237Sgraichen	struct fs_stat *fs_next;	/* next element */
4713237Sgraichen	char   *fs_file;		/* mount point of the filesystem */
4813237Sgraichen	char   *qfpathname;		/* pathname of the quota file */
4913237Sgraichen	dev_t   st_dev;			/* device of the filesystem */
5013237Sgraichen} fs_stat;
5113237Sgraichenstruct fs_stat *fs_begin = NULL;
5213237Sgraichen
5313237Sgraichenint from_inetd = 1;
5413237Sgraichen
55184638Sdesstatic void
5690336Simpcleanup(int sig)
5713237Sgraichen{
58184638Sdes
59197506Sdes	(void)sig;
60197506Sdes	(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
6113237Sgraichen	exit(0);
6213237Sgraichen}
6313237Sgraichen
6413237Sgraichenint
65184638Sdesmain(void)
6613237Sgraichen{
6713237Sgraichen	SVCXPRT *transp;
68100120Salfred	int ok;
69100120Salfred	struct sockaddr_storage from;
70141918Sstefanf	socklen_t fromlen;
7113237Sgraichen
7213237Sgraichen	fromlen = sizeof(from);
73197506Sdes	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
7413237Sgraichen		from_inetd = 0;
7513237Sgraichen
7613237Sgraichen	if (!from_inetd) {
7713237Sgraichen		daemon(0, 0);
7813237Sgraichen
79197506Sdes		(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
8013237Sgraichen
81197506Sdes		(void)signal(SIGINT, cleanup);
82197506Sdes		(void)signal(SIGTERM, cleanup);
83197506Sdes		(void)signal(SIGHUP, cleanup);
8413237Sgraichen	}
8513237Sgraichen
8613237Sgraichen	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
8713237Sgraichen
8813237Sgraichen	/* create and register the service */
89100120Salfred	if (from_inetd) {
90100120Salfred		transp = svc_tli_create(0, NULL, NULL, 0, 0);
91100120Salfred		if (transp == NULL) {
92100120Salfred			syslog(LOG_ERR, "couldn't create udp service.");
93100120Salfred			exit(1);
94100120Salfred		}
95100120Salfred		ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
96197506Sdes		    rquota_service, NULL);
97197506Sdes	} else {
98100120Salfred		ok = svc_create(rquota_service,
99197506Sdes		    RQUOTAPROG, RQUOTAVERS, "udp");
100197506Sdes	}
101100120Salfred	if (!ok) {
102184638Sdes		syslog(LOG_ERR,
103184638Sdes		    "unable to register (RQUOTAPROG, RQUOTAVERS, %s)",
104184638Sdes		    from_inetd ? "(inetd)" : "udp");
10513237Sgraichen		exit(1);
10613237Sgraichen	}
10713237Sgraichen
108184638Sdes	initfs();
10913237Sgraichen	svc_run();
11013237Sgraichen	syslog(LOG_ERR, "svc_run returned");
11113237Sgraichen	exit(1);
11213237Sgraichen}
11313237Sgraichen
114184638Sdesvoid
11590336Simprquota_service(struct svc_req *request, SVCXPRT *transp)
11613237Sgraichen{
117184638Sdes
11813237Sgraichen	switch (request->rq_proc) {
11913237Sgraichen	case NULLPROC:
12095658Sdes		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
12113237Sgraichen		break;
12213237Sgraichen
12313237Sgraichen	case RQUOTAPROC_GETQUOTA:
12413237Sgraichen	case RQUOTAPROC_GETACTIVEQUOTA:
12513237Sgraichen		sendquota(request, transp);
12613237Sgraichen		break;
12713237Sgraichen
12813237Sgraichen	default:
12913237Sgraichen		svcerr_noproc(transp);
13013237Sgraichen		break;
13113237Sgraichen	}
13213237Sgraichen	if (from_inetd)
13313237Sgraichen		exit(0);
13413237Sgraichen}
13513237Sgraichen
13613237Sgraichen/* read quota for the specified id, and send it */
137184638Sdesvoid
13890336Simpsendquota(struct svc_req *request, SVCXPRT *transp)
13913237Sgraichen{
14013237Sgraichen	struct getquota_args getq_args;
14113237Sgraichen	struct getquota_rslt getq_rslt;
14213237Sgraichen	struct dqblk dqblk;
14313237Sgraichen	struct timeval timev;
14413237Sgraichen
145197506Sdes	bzero(&getq_args, sizeof(getq_args));
14695658Sdes	if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
14713237Sgraichen		svcerr_decode(transp);
14813237Sgraichen		return;
14913237Sgraichen	}
15013237Sgraichen	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
15113237Sgraichen		/* bad auth */
15213237Sgraichen		getq_rslt.status = Q_EPERM;
15313237Sgraichen	} else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
15413237Sgraichen		/* failed, return noquota */
15513237Sgraichen		getq_rslt.status = Q_NOQUOTA;
15613237Sgraichen	} else {
15713237Sgraichen		gettimeofday(&timev, NULL);
15813237Sgraichen		getq_rslt.status = Q_OK;
15913237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
16013237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE;
16113237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
16213237Sgraichen		    dqblk.dqb_bhardlimit;
16313237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
16413237Sgraichen		    dqblk.dqb_bsoftlimit;
16513237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
16613237Sgraichen		    dqblk.dqb_curblocks;
16713237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
16813237Sgraichen		    dqblk.dqb_ihardlimit;
16913237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
17013237Sgraichen		    dqblk.dqb_isoftlimit;
17113237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
17213237Sgraichen		    dqblk.dqb_curinodes;
17313237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
17413237Sgraichen		    dqblk.dqb_btime - timev.tv_sec;
17513237Sgraichen		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
17613237Sgraichen		    dqblk.dqb_itime - timev.tv_sec;
17713237Sgraichen	}
178197508Sdes	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
17913237Sgraichen		svcerr_systemerr(transp);
18095658Sdes	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
18113237Sgraichen		syslog(LOG_ERR, "unable to free arguments");
18213237Sgraichen		exit(1);
18313237Sgraichen	}
18413237Sgraichen}
18513237Sgraichen
186184638Sdesvoid
18790336Simpprinterr_reply(SVCXPRT *transp)	/* when a reply to a request failed */
18813237Sgraichen{
189100120Salfred	char name[INET6_ADDRSTRLEN];
190100120Salfred	struct sockaddr *caller;
191197508Sdes	int save_errno;
19213237Sgraichen
19313237Sgraichen	save_errno = errno;
194100120Salfred	caller = (struct sockaddr *)svc_getrpccaller(transp)->buf;
195100120Salfred	getnameinfo(caller, caller->sa_len, name, sizeof (name),
196197506Sdes	    NULL, 0, NI_NUMERICHOST);
19713237Sgraichen	errno = save_errno;
19813237Sgraichen	if (errno == 0)
19913237Sgraichen		syslog(LOG_ERR, "couldn't send reply to %s", name);
20013237Sgraichen	else
20113237Sgraichen		syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
20213237Sgraichen}
20313237Sgraichen
20413237Sgraichen/* initialise the fs_tab list from entries in /etc/fstab */
205184638Sdesvoid
20690336Simpinitfs(void)
20713237Sgraichen{
20813237Sgraichen	struct fs_stat *fs_current = NULL;
20913237Sgraichen	struct fs_stat *fs_next = NULL;
21013237Sgraichen	char *qfpathname;
21113237Sgraichen	struct fstab *fs;
21213237Sgraichen	struct stat st;
21313237Sgraichen
21413237Sgraichen	setfsent();
21513237Sgraichen	while ((fs = getfsent())) {
21613237Sgraichen		if (strcmp(fs->fs_vfstype, "ufs"))
21713237Sgraichen			continue;
21813237Sgraichen		if (!hasquota(fs, &qfpathname))
21913237Sgraichen			continue;
22013237Sgraichen
221197506Sdes		fs_current = malloc(sizeof(struct fs_stat));
22213237Sgraichen		fs_current->fs_next = fs_next;	/* next element */
22313237Sgraichen
224197506Sdes		fs_current->fs_file = malloc(strlen(fs->fs_file) + 1);
22513237Sgraichen		strcpy(fs_current->fs_file, fs->fs_file);
22613237Sgraichen
227197506Sdes		fs_current->qfpathname = malloc(strlen(qfpathname) + 1);
22813237Sgraichen		strcpy(fs_current->qfpathname, qfpathname);
22913237Sgraichen
23078457Smikeh		stat(fs_current->fs_file, &st);
23113237Sgraichen		fs_current->st_dev = st.st_dev;
23213237Sgraichen
23313237Sgraichen		fs_next = fs_current;
23413237Sgraichen	}
23513237Sgraichen	endfsent();
23613237Sgraichen	fs_begin = fs_current;
23713237Sgraichen}
23813237Sgraichen
23913237Sgraichen/*
24013237Sgraichen * gets the quotas for id, filesystem path.
24113237Sgraichen * Return 0 if fail, 1 otherwise
24213237Sgraichen */
24313237Sgraichenint
244184638Sdesgetfsquota(long id, char *path, struct dqblk *dqblk)
24513237Sgraichen{
24613237Sgraichen	struct stat st_path;
24713237Sgraichen	struct fs_stat *fs;
24813237Sgraichen	int	qcmd, fd, ret = 0;
24913237Sgraichen
25013237Sgraichen	if (stat(path, &st_path) < 0)
25113237Sgraichen		return (0);
25213237Sgraichen
25313237Sgraichen	qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
25413237Sgraichen
25513237Sgraichen	for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
256197508Sdes		/* where the device is the same as path */
25713237Sgraichen		if (fs->st_dev != st_path.st_dev)
25813237Sgraichen			continue;
25913237Sgraichen
26013237Sgraichen		/* find the specified filesystem. get and return quota */
26113237Sgraichen		if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
26213237Sgraichen			return (1);
26313237Sgraichen
26413237Sgraichen		if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
26513237Sgraichen			syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname);
26613237Sgraichen			return (0);
26713237Sgraichen		}
26813237Sgraichen		if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) {
26913237Sgraichen			close(fd);
27013237Sgraichen			return (1);
27113237Sgraichen		}
27213237Sgraichen		switch (read(fd, dqblk, sizeof(struct dqblk))) {
27313237Sgraichen		case 0:
27413237Sgraichen			/*
27513237Sgraichen                         * Convert implicit 0 quota (EOF)
27613237Sgraichen                         * into an explicit one (zero'ed dqblk)
27713237Sgraichen                         */
27895658Sdes			bzero(dqblk, sizeof(struct dqblk));
27913237Sgraichen			ret = 1;
28013237Sgraichen			break;
28113237Sgraichen		case sizeof(struct dqblk):	/* OK */
28213237Sgraichen			ret = 1;
28313237Sgraichen			break;
28413237Sgraichen		default:	/* ERROR */
28513237Sgraichen			syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname);
28613237Sgraichen			close(fd);
28713237Sgraichen			return (0);
28813237Sgraichen		}
28913237Sgraichen		close(fd);
29013237Sgraichen	}
29113237Sgraichen	return (ret);
29213237Sgraichen}
29313237Sgraichen
29413237Sgraichen/*
29513237Sgraichen * Check to see if a particular quota is to be enabled.
29613237Sgraichen * Comes from quota.c, NetBSD 0.9
29713237Sgraichen */
29813237Sgraichenint
29990336Simphasquota(struct fstab *fs, char  **qfnamep)
30013237Sgraichen{
30113237Sgraichen	static char initname, usrname[100];
30213237Sgraichen	static char buf[BUFSIZ];
303184638Sdes	char *opt, *cp;
304184638Sdes	const char *qfextension[] = INITQFNAMES;
30513237Sgraichen
30613237Sgraichen	if (!initname) {
30713237Sgraichen		sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME);
30813237Sgraichen		initname = 1;
30913237Sgraichen	}
31013237Sgraichen	strcpy(buf, fs->fs_mntops);
31113237Sgraichen	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
31213237Sgraichen		if ((cp = index(opt, '=')))
31313237Sgraichen			*cp++ = '\0';
31413237Sgraichen		if (strcmp(opt, usrname) == 0)
31513237Sgraichen			break;
31613237Sgraichen	}
31713237Sgraichen	if (!opt)
31813237Sgraichen		return (0);
31913237Sgraichen	if (cp) {
32013237Sgraichen		*qfnamep = cp;
32113237Sgraichen		return (1);
32213237Sgraichen	}
323184638Sdes	sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME,
324184638Sdes	    qfextension[USRQUOTA]);
32513237Sgraichen	*qfnamep = buf;
32613237Sgraichen	return (1);
32713237Sgraichen}
328