rquotad.c revision 13238
1193323Sed/*
2193323Sed * by Manuel Bouyer (bouyer@ensta.fr)
3193323Sed *
4193323Sed * There is no copyright, you can use it as you want.
5193323Sed */
6193323Sed
7193323Sed#include <sys/param.h>
8193323Sed#include <sys/types.h>
9193323Sed#include <sys/mount.h>
10193323Sed#include <sys/file.h>
11193323Sed#include <sys/stat.h>
12193323Sed#include <sys/socket.h>
13193323Sed#include <signal.h>
14193323Sed
15193323Sed#include <stdio.h>
16193323Sed#include <fstab.h>
17198090Srdivacky#include <ctype.h>
18193323Sed#include <stdlib.h>
19193323Sed#include <unistd.h>
20199481Srdivacky#include <string.h>
21193323Sed#include <pwd.h>
22193323Sed#include <grp.h>
23193323Sed#include <errno.h>
24199481Srdivacky
25193323Sed#include <syslog.h>
26193323Sed#include <varargs.h>
27193323Sed
28193323Sed#include <ufs/ufs/quota.h>
29199481Srdivacky#include <rpc/rpc.h>
30193323Sed#include <rpc/pmap_clnt.h>
31199481Srdivacky#include <rpcsvc/rquota.h>
32193323Sed#include <arpa/inet.h>
33193323Sed
34193323Sedvoid rquota_service __P((struct svc_req *request, SVCXPRT *transp));
35193323Sedvoid sendquota __P((struct svc_req *request, SVCXPRT *transp));
36193323Sedvoid printerr_reply __P((SVCXPRT *transp));
37199481Srdivackyvoid initfs __P((void));
38201360Srdivackyint getfsquota __P((long id, char *path, struct dqblk *dqblk));
39193323Sedint hasquota __P((struct fstab *fs, char **qfnamep));
40198090Srdivacky
41198090Srdivacky/*
42198090Srdivacky * structure containing informations about ufs filesystems
43193323Sed * initialised by initfs()
44198090Srdivacky */
45193323Sedstruct fs_stat {
46198090Srdivacky	struct fs_stat *fs_next;	/* next element */
47198090Srdivacky	char   *fs_file;		/* mount point of the filesystem */
48199481Srdivacky	char   *qfpathname;		/* pathname of the quota file */
49199511Srdivacky	dev_t   st_dev;			/* device of the filesystem */
50199511Srdivacky} fs_stat;
51198090Srdivackystruct fs_stat *fs_begin = NULL;
52193323Sed
53193323Sedint from_inetd = 1;
54198090Srdivacky
55193323Sedvoid
56193323Sedcleanup()
57198090Srdivacky{
58198090Srdivacky	(void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
59199481Srdivacky	exit(0);
60199481Srdivacky}
61199481Srdivacky
62199511Srdivackyint
63199481Srdivackymain(argc, argv)
64199511Srdivacky	int     argc;
65199481Srdivacky	char   *argv[];
66199481Srdivacky{
67193323Sed	SVCXPRT *transp;
68193323Sed	int sock = 0;
69193323Sed	int proto = 0;
70193323Sed	struct sockaddr_in from;
71193323Sed	int fromlen;
72193323Sed
73193323Sed	fromlen = sizeof(from);
74201360Srdivacky	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
75193323Sed		from_inetd = 0;
76193323Sed		sock = RPC_ANYSOCK;
77193323Sed		proto = IPPROTO_UDP;
78193323Sed	}
79193323Sed
80193323Sed	if (!from_inetd) {
81193323Sed		daemon(0, 0);
82201360Srdivacky
83201360Srdivacky		(void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
84201360Srdivacky
85201360Srdivacky		(void) signal(SIGINT, cleanup);
86201360Srdivacky		(void) signal(SIGTERM, cleanup);
87201360Srdivacky		(void) signal(SIGHUP, cleanup);
88193323Sed	}
89193323Sed
90193323Sed	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
91193323Sed
92193323Sed	/* create and register the service */
93193323Sed	transp = svcudp_create(sock);
94193323Sed	if (transp == NULL) {
95193323Sed		syslog(LOG_ERR, "couldn't create udp service.");
96198090Srdivacky		exit(1);
97198090Srdivacky	}
98193323Sed	if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) {
99193323Sed		syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s).", proto?"udp":"(inetd)");
100193323Sed		exit(1);
101193323Sed	}
102193323Sed
103193323Sed	initfs();		/* init the fs_stat list */
104193323Sed	svc_run();
105193323Sed	syslog(LOG_ERR, "svc_run returned");
106193323Sed	exit(1);
107193323Sed}
108193323Sed
109193323Sedvoid
110193323Sedrquota_service(request, transp)
111193323Sed	struct svc_req *request;
112193323Sed	SVCXPRT *transp;
113193323Sed{
114193323Sed	switch (request->rq_proc) {
115193323Sed	case NULLPROC:
116193323Sed		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
117193323Sed		break;
118193323Sed
119193323Sed	case RQUOTAPROC_GETQUOTA:
120193323Sed	case RQUOTAPROC_GETACTIVEQUOTA:
121193323Sed		sendquota(request, transp);
122193323Sed		break;
123193323Sed
124199481Srdivacky	default:
125199481Srdivacky		svcerr_noproc(transp);
126199481Srdivacky		break;
127199481Srdivacky	}
128198090Srdivacky	if (from_inetd)
129198090Srdivacky		exit(0);
130199481Srdivacky}
131198090Srdivacky
132193323Sed/* read quota for the specified id, and send it */
133193323Sedvoid
134193323Sedsendquota(request, transp)
135193323Sed	struct svc_req *request;
136199481Srdivacky	SVCXPRT *transp;
137199481Srdivacky{
138199481Srdivacky	struct getquota_args getq_args;
139199481Srdivacky	struct getquota_rslt getq_rslt;
140193323Sed	struct dqblk dqblk;
141193323Sed	struct timeval timev;
142199481Srdivacky
143193323Sed	bzero((char *)&getq_args, sizeof(getq_args));
144193323Sed	if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
145199481Srdivacky		svcerr_decode(transp);
146193323Sed		return;
147193323Sed	}
148193323Sed	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
149193323Sed		/* bad auth */
150193323Sed		getq_rslt.status = Q_EPERM;
151193323Sed	} else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
152193323Sed		/* failed, return noquota */
153199481Srdivacky		getq_rslt.status = Q_NOQUOTA;
154193323Sed	} else {
155193323Sed		gettimeofday(&timev, NULL);
156199481Srdivacky		getq_rslt.status = Q_OK;
157193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
158193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE;
159193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
160199481Srdivacky		    dqblk.dqb_bhardlimit;
161193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
162193323Sed		    dqblk.dqb_bsoftlimit;
163193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
164193323Sed		    dqblk.dqb_curblocks;
165193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
166193323Sed		    dqblk.dqb_ihardlimit;
167193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
168193323Sed		    dqblk.dqb_isoftlimit;
169193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
170193323Sed		    dqblk.dqb_curinodes;
171193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
172193323Sed		    dqblk.dqb_btime - timev.tv_sec;
173193323Sed		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
174199481Srdivacky		    dqblk.dqb_itime - timev.tv_sec;
175198090Srdivacky	}
176198090Srdivacky	if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) {
177198090Srdivacky		svcerr_systemerr(transp);
178201360Srdivacky	}
179193323Sed	if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
180193323Sed		syslog(LOG_ERR, "unable to free arguments");
181193323Sed		exit(1);
182193323Sed	}
183193323Sed}
184193323Sed
185193323Sedvoid
186193323Sedprinterr_reply(transp)	/* when a reply to a request failed */
187193323Sed	SVCXPRT *transp;
188193323Sed{
189193323Sed	char   *name;
190201360Srdivacky	struct sockaddr_in *caller;
191193323Sed	int     save_errno;
192193323Sed
193193323Sed	save_errno = errno;
194193323Sed
195193323Sed	caller = svc_getcaller(transp);
196193323Sed	name = (char *)inet_ntoa(caller->sin_addr);
197193323Sed	errno = save_errno;
198193323Sed	if (errno == 0)
199193323Sed		syslog(LOG_ERR, "couldn't send reply to %s", name);
200193323Sed	else
201193323Sed		syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
202201360Srdivacky}
203201360Srdivacky
204201360Srdivacky/* initialise the fs_tab list from entries in /etc/fstab */
205201360Srdivackyvoid
206201360Srdivackyinitfs()
207201360Srdivacky{
208201360Srdivacky	struct fs_stat *fs_current = NULL;
209201360Srdivacky	struct fs_stat *fs_next = NULL;
210202375Srdivacky	char *qfpathname;
211201360Srdivacky	struct fstab *fs;
212201360Srdivacky	struct stat st;
213201360Srdivacky
214193323Sed	setfsent();
215193323Sed	while ((fs = getfsent())) {
216193323Sed		if (strcmp(fs->fs_vfstype, "ufs"))
217198090Srdivacky			continue;
218199481Srdivacky		if (!hasquota(fs, &qfpathname))
219198090Srdivacky			continue;
220198090Srdivacky
221193323Sed		fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat));
222193323Sed		fs_current->fs_next = fs_next;	/* next element */
223193323Sed
224193323Sed		fs_current->fs_file = malloc(sizeof(char) * (strlen(fs->fs_file) + 1));
225193323Sed		strcpy(fs_current->fs_file, fs->fs_file);
226193323Sed
227201360Srdivacky		fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1));
228201360Srdivacky		strcpy(fs_current->qfpathname, qfpathname);
229193323Sed
230201360Srdivacky		stat(qfpathname, &st);
231193323Sed		fs_current->st_dev = st.st_dev;
232201360Srdivacky
233201360Srdivacky		fs_next = fs_current;
234201360Srdivacky	}
235201360Srdivacky	endfsent();
236201360Srdivacky	fs_begin = fs_current;
237202375Srdivacky}
238201360Srdivacky
239201360Srdivacky/*
240201360Srdivacky * gets the quotas for id, filesystem path.
241201360Srdivacky * Return 0 if fail, 1 otherwise
242193323Sed */
243193323Sedint
244193323Sedgetfsquota(id, path, dqblk)
245193323Sed	long id;
246193323Sed	char   *path;
247193323Sed	struct dqblk *dqblk;
248193323Sed{
249193323Sed	struct stat st_path;
250193323Sed	struct fs_stat *fs;
251193323Sed	int	qcmd, fd, ret = 0;
252193323Sed
253193323Sed	if (stat(path, &st_path) < 0)
254193323Sed		return (0);
255193323Sed
256193323Sed	qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
257193323Sed
258193323Sed	for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
259193323Sed		/* where the devise is the same as path */
260193323Sed		if (fs->st_dev != st_path.st_dev)
261193323Sed			continue;
262193323Sed
263201360Srdivacky		/* find the specified filesystem. get and return quota */
264193323Sed		if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
265193323Sed			return (1);
266193323Sed
267193323Sed		if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
268193323Sed			syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname);
269193323Sed			return (0);
270193323Sed		}
271193323Sed		if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) {
272193323Sed			close(fd);
273193323Sed			return (1);
274198090Srdivacky		}
275198090Srdivacky		switch (read(fd, dqblk, sizeof(struct dqblk))) {
276198090Srdivacky		case 0:
277198090Srdivacky			/*
278198090Srdivacky                         * Convert implicit 0 quota (EOF)
279198090Srdivacky                         * into an explicit one (zero'ed dqblk)
280198090Srdivacky                         */
281193323Sed			bzero((caddr_t) dqblk, sizeof(struct dqblk));
282193323Sed			ret = 1;
283193323Sed			break;
284193323Sed		case sizeof(struct dqblk):	/* OK */
285193323Sed			ret = 1;
286193323Sed			break;
287193323Sed		default:	/* ERROR */
288193323Sed			syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname);
289193323Sed			close(fd);
290193323Sed			return (0);
291193323Sed		}
292193323Sed		close(fd);
293193323Sed	}
294199481Srdivacky	return (ret);
295193323Sed}
296193323Sed
297199481Srdivacky/*
298199481Srdivacky * Check to see if a particular quota is to be enabled.
299193323Sed * Comes from quota.c, NetBSD 0.9
300193323Sed */
301201360Srdivackyint
302201360Srdivackyhasquota(fs, qfnamep)
303193323Sed	struct fstab *fs;
304193323Sed	char  **qfnamep;
305193323Sed{
306199481Srdivacky	static char initname, usrname[100];
307193323Sed	static char buf[BUFSIZ];
308193323Sed	char	*opt, *cp;
309193323Sed	char	*qfextension[] = INITQFNAMES;
310193323Sed
311193323Sed	if (!initname) {
312193323Sed		sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME);
313193323Sed		initname = 1;
314193323Sed	}
315193323Sed	strcpy(buf, fs->fs_mntops);
316193323Sed	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
317193323Sed		if ((cp = index(opt, '=')))
318193323Sed			*cp++ = '\0';
319201360Srdivacky		if (strcmp(opt, usrname) == 0)
320193323Sed			break;
321193323Sed	}
322193323Sed	if (!opt)
323200581Srdivacky		return (0);
324193323Sed	if (cp) {
325193323Sed		*qfnamep = cp;
326193323Sed		return (1);
327193323Sed	}
328193323Sed	sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[USRQUOTA]);
329201360Srdivacky	*qfnamep = buf;
330193323Sed	return (1);
331201360Srdivacky}
332201360Srdivacky