xref: /freebsd/libexec/rpc.rquotad/rquotad.c (revision 63f537551380d2dab29fa402ad1269feae17e594)
1 /*
2  * by Manuel Bouyer (bouyer@ensta.fr)
3  *
4  * There is no copyright, you can use it as you want.
5  */
6 
7 #include <sys/cdefs.h>
8 #include <sys/param.h>
9 #include <sys/mount.h>
10 #include <sys/file.h>
11 #include <sys/stat.h>
12 #include <sys/socket.h>
13 
14 #include <ufs/ufs/quota.h>
15 #include <rpc/rpc.h>
16 #include <rpcsvc/rquota.h>
17 #include <arpa/inet.h>
18 #include <netdb.h>
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fstab.h>
23 #include <grp.h>
24 #include <libutil.h>
25 #include <pwd.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <err.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 
34 static void rquota_service_1(struct svc_req *request, SVCXPRT *transp);
35 static void rquota_service_2(struct svc_req *request, SVCXPRT *transp);
36 static void sendquota(struct svc_req *request, SVCXPRT *transp);
37 static void sendquota_extended(struct svc_req *request, SVCXPRT *transp);
38 static int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
39 
40 static int from_inetd = 1;
41 static int debug = 0;
42 
43 static void
44 cleanup(int sig)
45 {
46 
47 	(void)sig;
48 	(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
49 	exit(0);
50 }
51 
52 int
53 main(int argc, char **argv)
54 {
55 	SVCXPRT *transp;
56 	int ok;
57 	struct sockaddr_storage from;
58 	socklen_t fromlen;
59 	int vers;
60 	int ch;
61 
62 	while ((ch = getopt(argc, argv, "d")) != -1) {
63 		switch (ch) {
64 		case 'd':
65 			debug++;
66 			break;
67 		default:
68 			break;
69 		}
70 	}
71 
72 	fromlen = sizeof(from);
73 	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
74 		from_inetd = 0;
75 
76 	if (!from_inetd) {
77 		if (!debug)
78 			daemon(0, 0);
79 		(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
80 		(void)signal(SIGINT, cleanup);
81 		(void)signal(SIGTERM, cleanup);
82 		(void)signal(SIGHUP, cleanup);
83 	}
84 
85 	openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
86 
87 	/* create and register the service */
88 	if (from_inetd) {
89 		transp = svc_tli_create(0, NULL, NULL, 0, 0);
90 		if (transp == NULL) {
91 			syslog(LOG_ERR, "couldn't create udp service.");
92 			exit(1);
93 		}
94 		vers = RQUOTAVERS;
95 		ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
96 		    rquota_service_1, NULL);
97 		if (ok) {
98 			vers = EXT_RQUOTAVERS;
99 			ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
100 				     rquota_service_2, NULL);
101 		}
102 	} else {
103 		vers = RQUOTAVERS;
104 		ok = svc_create(rquota_service_1,
105 		    RQUOTAPROG, RQUOTAVERS, "udp");
106 		if (ok) {
107 			vers = EXT_RQUOTAVERS;
108 			ok = svc_create(rquota_service_2,
109 					RQUOTAPROG, EXT_RQUOTAVERS, "udp");
110 
111 		}
112 	}
113 	if (!ok) {
114 		syslog(LOG_ERR,
115 		    "unable to register (RQUOTAPROG, %s, %s)",
116 		       vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS",
117 		       from_inetd ? "(inetd)" : "udp");
118 		exit(1);
119 	}
120 
121 	svc_run();
122 	syslog(LOG_ERR, "svc_run returned");
123 	exit(1);
124 }
125 
126 static void
127 rquota_service_2(struct svc_req *request, SVCXPRT *transp)
128 {
129 
130 	switch (request->rq_proc) {
131 	case NULLPROC:
132 		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
133 		break;
134 	case RQUOTAPROC_GETQUOTA:
135 	case RQUOTAPROC_GETACTIVEQUOTA:
136 		sendquota_extended(request, transp);
137 		break;
138 	default:
139 		svcerr_noproc(transp);
140 		break;
141 	}
142 	if (from_inetd)
143 		exit(0);
144 }
145 
146 static void
147 rquota_service_1(struct svc_req *request, SVCXPRT *transp)
148 {
149 
150 	switch (request->rq_proc) {
151 	case NULLPROC:
152 		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
153 		break;
154 	case RQUOTAPROC_GETQUOTA:
155 	case RQUOTAPROC_GETACTIVEQUOTA:
156 		sendquota(request, transp);
157 		break;
158 	default:
159 		svcerr_noproc(transp);
160 		break;
161 	}
162 	if (from_inetd)
163 		exit(0);
164 }
165 
166 /* read quota for the specified id, and send it */
167 static void
168 sendquota(struct svc_req *request, SVCXPRT *transp)
169 {
170 	struct getquota_args getq_args;
171 	struct getquota_rslt getq_rslt;
172 	struct dqblk dqblk;
173 	struct timeval timev;
174 	int scale;
175 
176 	bzero(&getq_args, sizeof(getq_args));
177 	if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
178 		svcerr_decode(transp);
179 		return;
180 	}
181 	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
182 		/* bad auth */
183 		getq_rslt.status = Q_EPERM;
184 	} else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
185 		/* failed, return noquota */
186 		getq_rslt.status = Q_NOQUOTA;
187 	} else {
188 		gettimeofday(&timev, NULL);
189 		getq_rslt.status = Q_OK;
190 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
191 		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
192 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
193 		    DEV_BSIZE * scale;
194 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
195 		    dqblk.dqb_bhardlimit / scale;
196 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
197 		    dqblk.dqb_bsoftlimit / scale;
198 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
199 		    dqblk.dqb_curblocks / scale;
200 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
201 		    dqblk.dqb_ihardlimit;
202 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
203 		    dqblk.dqb_isoftlimit;
204 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
205 		    dqblk.dqb_curinodes;
206 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
207 		    dqblk.dqb_btime - timev.tv_sec;
208 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
209 		    dqblk.dqb_itime - timev.tv_sec;
210 	}
211 	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
212 		svcerr_systemerr(transp);
213 	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
214 		syslog(LOG_ERR, "unable to free arguments");
215 		exit(1);
216 	}
217 }
218 
219 static void
220 sendquota_extended(struct svc_req *request, SVCXPRT *transp)
221 {
222 	struct ext_getquota_args getq_args;
223 	struct getquota_rslt getq_rslt;
224 	struct dqblk dqblk;
225 	struct timeval timev;
226 	int scale;
227 
228 	bzero(&getq_args, sizeof(getq_args));
229 	if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
230 		svcerr_decode(transp);
231 		return;
232 	}
233 	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
234 		/* bad auth */
235 		getq_rslt.status = Q_EPERM;
236 	} else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) {
237 		/* failed, return noquota */
238 		getq_rslt.status = Q_NOQUOTA;
239 	} else {
240 		gettimeofday(&timev, NULL);
241 		getq_rslt.status = Q_OK;
242 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
243 		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
244 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
245 		    DEV_BSIZE * scale;
246 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
247 		    dqblk.dqb_bhardlimit / scale;
248 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
249 		    dqblk.dqb_bsoftlimit / scale;
250 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
251 		    dqblk.dqb_curblocks / scale;
252 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
253 		    dqblk.dqb_ihardlimit;
254 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
255 		    dqblk.dqb_isoftlimit;
256 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
257 		    dqblk.dqb_curinodes;
258 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
259 		    dqblk.dqb_btime - timev.tv_sec;
260 		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
261 		    dqblk.dqb_itime - timev.tv_sec;
262 	}
263 	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
264 		svcerr_systemerr(transp);
265 	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
266 		syslog(LOG_ERR, "unable to free arguments");
267 		exit(1);
268 	}
269 }
270 
271 /*
272  * gets the quotas for id, filesystem path.
273  * Return 0 if fail, 1 otherwise
274  */
275 static int
276 getfsquota(int type, long id, char *path, struct dqblk *dqblk)
277 {
278 	struct quotafile *qf;
279 	/*
280 	 * Remote quota checking is limited to mounted filesystems.
281 	 * Since UFS and ZFS support the quota system calls, we
282 	 * only need to make an fstab object that has the path, and
283 	 * a blank name for the filesystem type.
284 	 * This allows the quota_open() call to work the way we
285 	 * expect it to.
286 	 *
287 	 * The static char declaration is because compiler warnings
288 	 * don't allow passing a const char * to a char *.
289 	 */
290 	int rv;
291 	static char blank[] = "";
292 	struct fstab fst;
293 
294 	fst.fs_file = path;
295 	fst.fs_mntops = blank;
296 	fst.fs_vfstype = blank;
297 
298 	if (type != USRQUOTA && type != GRPQUOTA)
299 		return (0);
300 
301 	qf = quota_open(&fst, type, O_RDONLY);
302 	if (debug)
303 		warnx("quota_open(<%s, %s>, %d) returned %p",
304 		      fst.fs_file, fst.fs_mntops, type,
305 		      qf);
306 	if (qf == NULL)
307 		return (0);
308 
309 	rv = quota_read(qf, dqblk, id) == 0;
310 	quota_close(qf);
311 	if (debug)
312 		warnx("getfsquota(%d, %ld, %s, %p) -> %d",
313 		      type, id, path, dqblk, rv);
314 	return (rv);
315 }
316