xref: /dragonfly/libexec/rpc.rquotad/rquotad.c (revision ce0e08e2)
1 /*
2  * by Manuel Bouyer (bouyer@ensta.fr)
3  *
4  * There is no copyright, you can use it as you want.
5  *
6  * $FreeBSD: src/libexec/rpc.rquotad/rquotad.c,v 1.3.2.1 2001/07/02 23:46:27 mikeh Exp $
7  * $DragonFly: src/libexec/rpc.rquotad/rquotad.c,v 1.6 2008/11/19 18:41:30 swildner Exp $
8  */
9 
10 #include <sys/param.h>
11 #include <sys/types.h>
12 #include <sys/mount.h>
13 #include <sys/file.h>
14 #include <sys/stat.h>
15 #include <sys/socket.h>
16 #include <signal.h>
17 
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fstab.h>
21 #include <grp.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include <syslog.h>
29 #include <varargs.h>
30 
31 #include <vfs/ufs/quota.h>
32 #include <rpc/rpc.h>
33 #include <rpc/pmap_clnt.h>
34 #include <rpcsvc/rquota.h>
35 #include <arpa/inet.h>
36 
37 static void cleanup(int);
38 static void rquota_service(struct svc_req *request, SVCXPRT *transp);
39 static void sendquota(struct svc_req *request, SVCXPRT *transp);
40 static void initfs(void);
41 static int getfsquota(long id, char *path, struct ufs_dqblk *dqblk);
42 static int hasquota(struct fstab *fs, char **qfnamep);
43 
44 /*
45  * structure containing informations about ufs filesystems
46  * initialised by initfs()
47  */
48 struct fs_stat {
49 	struct fs_stat *fs_next;	/* next element */
50 	char   *fs_file;		/* mount point of the filesystem */
51 	char   *qfpathname;		/* pathname of the quota file */
52 	dev_t   st_dev;			/* device of the filesystem */
53 } fs_stat;
54 struct fs_stat *fs_begin = NULL;
55 
56 int from_inetd = 1;
57 
58 void
59 cleanup(int signo __unused)
60 {
61 	(void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
62 	exit(0);
63 }
64 
65 int
66 main(void)
67 {
68 	SVCXPRT *transp;
69 	int sock = 0;
70 	int proto = 0;
71 	struct sockaddr_in from;
72 	int fromlen;
73 
74 	fromlen = sizeof(from);
75 	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
76 		from_inetd = 0;
77 		sock = RPC_ANYSOCK;
78 		proto = IPPROTO_UDP;
79 	}
80 
81 	if (!from_inetd) {
82 		daemon(0, 0);
83 
84 		(void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
85 
86 		(void) signal(SIGINT, cleanup);
87 		(void) signal(SIGTERM, cleanup);
88 		(void) signal(SIGHUP, cleanup);
89 	}
90 
91 	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
92 
93 	/* create and register the service */
94 	transp = svcudp_create(sock);
95 	if (transp == NULL) {
96 		syslog(LOG_ERR, "couldn't create udp service");
97 		exit(1);
98 	}
99 	if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) {
100 		syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s)", proto?"udp":"(inetd)");
101 		exit(1);
102 	}
103 
104 	initfs();		/* init the fs_stat list */
105 	svc_run();
106 	syslog(LOG_ERR, "svc_run returned");
107 	exit(1);
108 }
109 
110 void
111 rquota_service(struct svc_req *request, SVCXPRT *transp)
112 {
113 	switch (request->rq_proc) {
114 	case NULLPROC:
115 		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
116 		break;
117 
118 	case RQUOTAPROC_GETQUOTA:
119 	case RQUOTAPROC_GETACTIVEQUOTA:
120 		sendquota(request, transp);
121 		break;
122 
123 	default:
124 		svcerr_noproc(transp);
125 		break;
126 	}
127 	if (from_inetd)
128 		exit(0);
129 }
130 
131 /* read quota for the specified id, and send it */
132 void
133 sendquota(struct svc_req *request, SVCXPRT *transp)
134 {
135 	struct getquota_args getq_args;
136 	struct getquota_rslt getq_rslt;
137 	struct ufs_dqblk dqblk;
138 	struct timeval timev;
139 
140 	bzero((char *)&getq_args, sizeof(getq_args));
141 	if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
142 		svcerr_decode(transp);
143 		return;
144 	}
145 	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
146 		/* bad auth */
147 		getq_rslt.status = Q_EPERM;
148 	} else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
149 		/* failed, return noquota */
150 		getq_rslt.status = Q_NOQUOTA;
151 	} else {
152 		gettimeofday(&timev, NULL);
153 		getq_rslt.status = Q_OK;
154 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
155 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE;
156 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
157 		    dqblk.dqb_bhardlimit;
158 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
159 		    dqblk.dqb_bsoftlimit;
160 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
161 		    dqblk.dqb_curblocks;
162 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
163 		    dqblk.dqb_ihardlimit;
164 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
165 		    dqblk.dqb_isoftlimit;
166 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
167 		    dqblk.dqb_curinodes;
168 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
169 		    dqblk.dqb_btime - timev.tv_sec;
170 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
171 		    dqblk.dqb_itime - timev.tv_sec;
172 	}
173 	if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) {
174 		svcerr_systemerr(transp);
175 	}
176 	if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
177 		syslog(LOG_ERR, "unable to free arguments");
178 		exit(1);
179 	}
180 }
181 
182 /* initialise the fs_tab list from entries in /etc/fstab */
183 void
184 initfs(void)
185 {
186 	struct fs_stat *fs_current = NULL;
187 	struct fs_stat *fs_next = NULL;
188 	char *qfpathname;
189 	struct fstab *fs;
190 	struct stat st;
191 
192 	setfsent();
193 	while ((fs = getfsent())) {
194 		if (strcmp(fs->fs_vfstype, "ufs"))
195 			continue;
196 		if (!hasquota(fs, &qfpathname))
197 			continue;
198 
199 		fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat));
200 		fs_current->fs_next = fs_next;	/* next element */
201 
202 		fs_current->fs_file = malloc(sizeof(char) * (strlen(fs->fs_file) + 1));
203 		strcpy(fs_current->fs_file, fs->fs_file);
204 
205 		fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1));
206 		strcpy(fs_current->qfpathname, qfpathname);
207 
208 		stat(fs_current->fs_file, &st);
209 		fs_current->st_dev = st.st_dev;
210 
211 		fs_next = fs_current;
212 	}
213 	endfsent();
214 	fs_begin = fs_current;
215 }
216 
217 /*
218  * gets the quotas for id, filesystem path.
219  * Return 0 if fail, 1 otherwise
220  */
221 int
222 getfsquota(long id, char *path, struct ufs_dqblk *dqblk)
223 {
224 	struct stat st_path;
225 	struct fs_stat *fs;
226 	int	qcmd, fd, ret = 0;
227 
228 	if (stat(path, &st_path) < 0)
229 		return (0);
230 
231 	qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
232 
233 	for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
234 		/* where the devise is the same as path */
235 		if (fs->st_dev != st_path.st_dev)
236 			continue;
237 
238 		/* find the specified filesystem. get and return quota */
239 		if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
240 			return (1);
241 
242 		if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
243 			syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname);
244 			return (0);
245 		}
246 		if (lseek(fd, (off_t)(id * sizeof(struct ufs_dqblk)), L_SET) == (off_t)-1) {
247 			close(fd);
248 			return (1);
249 		}
250 		switch (read(fd, dqblk, sizeof(struct ufs_dqblk))) {
251 		case 0:
252 			/*
253                          * Convert implicit 0 quota (EOF)
254                          * into an explicit one (zero'ed dqblk)
255                          */
256 			bzero((caddr_t) dqblk, sizeof(struct ufs_dqblk));
257 			ret = 1;
258 			break;
259 		case sizeof(struct ufs_dqblk):	/* OK */
260 			ret = 1;
261 			break;
262 		default:	/* ERROR */
263 			syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname);
264 			close(fd);
265 			return (0);
266 		}
267 		close(fd);
268 	}
269 	return (ret);
270 }
271 
272 /*
273  * Check to see if a particular quota is to be enabled.
274  * Comes from quota.c, NetBSD 0.9
275  */
276 int
277 hasquota(struct fstab *fs, char **qfnamep)
278 {
279 	static char initname, usrname[100];
280 	static char buf[BUFSIZ];
281 	char	*opt, *cp;
282 	const char *qfextension[] = INITQFNAMES;
283 
284 	if (!initname) {
285 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME);
286 		initname = 1;
287 	}
288 	strcpy(buf, fs->fs_mntops);
289 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
290 		if ((cp = index(opt, '=')))
291 			*cp++ = '\0';
292 		if (strcmp(opt, usrname) == 0)
293 			break;
294 	}
295 	if (!opt)
296 		return (0);
297 	if (cp) {
298 		*qfnamep = cp;
299 		return (1);
300 	}
301 	sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[USRQUOTA]);
302 	*qfnamep = buf;
303 	return (1);
304 }
305