rquotad.c revision 1.23 1 1.23 mrg /* $NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $ */
2 1.7 thorpej
3 1.1 deraadt /*
4 1.19 bouyer * by Manuel Bouyer (bouyer (at) ensta.fr). Public domain.
5 1.1 deraadt */
6 1.1 deraadt
7 1.9 mrg #include <sys/cdefs.h>
8 1.9 mrg #ifndef lint
9 1.23 mrg __RCSID("$NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $");
10 1.9 mrg #endif
11 1.9 mrg
12 1.1 deraadt #include <sys/param.h>
13 1.1 deraadt #include <sys/types.h>
14 1.1 deraadt #include <sys/mount.h>
15 1.1 deraadt #include <sys/file.h>
16 1.1 deraadt #include <sys/stat.h>
17 1.9 mrg #include <sys/socket.h>
18 1.5 jtc #include <signal.h>
19 1.1 deraadt
20 1.1 deraadt #include <stdio.h>
21 1.1 deraadt #include <fstab.h>
22 1.1 deraadt #include <ctype.h>
23 1.1 deraadt #include <stdlib.h>
24 1.1 deraadt #include <string.h>
25 1.1 deraadt #include <pwd.h>
26 1.1 deraadt #include <grp.h>
27 1.1 deraadt #include <errno.h>
28 1.9 mrg #include <unistd.h>
29 1.1 deraadt
30 1.1 deraadt #include <syslog.h>
31 1.1 deraadt
32 1.1 deraadt #include <ufs/ufs/quota.h>
33 1.1 deraadt #include <rpc/rpc.h>
34 1.1 deraadt #include <rpcsvc/rquota.h>
35 1.2 cgd #include <arpa/inet.h>
36 1.1 deraadt
37 1.15 fvdl void rquota_service(struct svc_req *request, SVCXPRT *transp);
38 1.20 bouyer void ext_rquota_service(struct svc_req *request, SVCXPRT *transp);
39 1.20 bouyer void sendquota(struct svc_req *request, int vers, SVCXPRT *transp);
40 1.15 fvdl void initfs(void);
41 1.20 bouyer int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
42 1.20 bouyer int hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep);
43 1.15 fvdl void cleanup(int);
44 1.15 fvdl int main(int, char *[]);
45 1.1 deraadt
46 1.1 deraadt /*
47 1.1 deraadt * structure containing informations about ufs filesystems
48 1.1 deraadt * initialised by initfs()
49 1.1 deraadt */
50 1.1 deraadt struct fs_stat {
51 1.1 deraadt struct fs_stat *fs_next; /* next element */
52 1.1 deraadt char *fs_file; /* mount point of the filesystem */
53 1.20 bouyer char *uqfpathname; /* pathname of the user quota file */
54 1.20 bouyer char *gqfpathname; /* pathname of the group quota file */
55 1.1 deraadt dev_t st_dev; /* device of the filesystem */
56 1.1 deraadt } fs_stat;
57 1.1 deraadt struct fs_stat *fs_begin = NULL;
58 1.1 deraadt
59 1.9 mrg char *qfextension[] = INITQFNAMES;
60 1.4 mycroft int from_inetd = 1;
61 1.4 mycroft
62 1.4 mycroft void
63 1.15 fvdl cleanup(int dummy)
64 1.4 mycroft {
65 1.11 mrg
66 1.15 fvdl (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
67 1.20 bouyer (void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
68 1.4 mycroft exit(0);
69 1.4 mycroft }
70 1.4 mycroft
71 1.1 deraadt int
72 1.15 fvdl main(int argc, char *argv[])
73 1.1 deraadt {
74 1.4 mycroft SVCXPRT *transp;
75 1.15 fvdl struct sockaddr_storage from;
76 1.23 mrg socklen_t fromlen;
77 1.1 deraadt
78 1.3 mycroft fromlen = sizeof(from);
79 1.15 fvdl if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
80 1.1 deraadt from_inetd = 0;
81 1.3 mycroft
82 1.1 deraadt if (!from_inetd) {
83 1.1 deraadt daemon(0, 0);
84 1.3 mycroft
85 1.16 fvdl (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
86 1.20 bouyer (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
87 1.4 mycroft (void) signal(SIGINT, cleanup);
88 1.4 mycroft (void) signal(SIGTERM, cleanup);
89 1.4 mycroft (void) signal(SIGHUP, cleanup);
90 1.1 deraadt }
91 1.3 mycroft
92 1.13 mrg openlog("rpc.rquotad", LOG_PID, LOG_DAEMON);
93 1.1 deraadt
94 1.1 deraadt /* create and register the service */
95 1.15 fvdl if (from_inetd) {
96 1.15 fvdl transp = svc_dg_create(0, 0, 0);
97 1.15 fvdl if (transp == NULL) {
98 1.15 fvdl syslog(LOG_ERR, "couldn't create udp service.");
99 1.15 fvdl exit(1);
100 1.15 fvdl }
101 1.15 fvdl if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service,
102 1.15 fvdl NULL)) {
103 1.15 fvdl syslog(LOG_ERR,
104 1.15 fvdl "unable to register (RQUOTAPROG, RQUOTAVERS).");
105 1.15 fvdl exit(1);
106 1.15 fvdl }
107 1.20 bouyer if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
108 1.20 bouyer ext_rquota_service, NULL)) {
109 1.20 bouyer syslog(LOG_ERR,
110 1.20 bouyer "unable to register (RQUOTAPROG, EXT_RQUOTAVERS).");
111 1.20 bouyer exit(1);
112 1.20 bouyer }
113 1.15 fvdl } else {
114 1.15 fvdl if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){
115 1.15 fvdl syslog(LOG_ERR,
116 1.15 fvdl "unable to create (RQUOTAPROG, RQUOTAVERS).");
117 1.15 fvdl exit(1);
118 1.15 fvdl }
119 1.20 bouyer if (!svc_create(ext_rquota_service, RQUOTAPROG,
120 1.20 bouyer EXT_RQUOTAVERS, "udp")){
121 1.20 bouyer syslog(LOG_ERR,
122 1.20 bouyer "unable to create (RQUOTAPROG, EXT_RQUOTAVERS).");
123 1.20 bouyer exit(1);
124 1.20 bouyer }
125 1.1 deraadt }
126 1.4 mycroft
127 1.1 deraadt initfs(); /* init the fs_stat list */
128 1.1 deraadt svc_run();
129 1.4 mycroft syslog(LOG_ERR, "svc_run returned");
130 1.4 mycroft exit(1);
131 1.1 deraadt }
132 1.1 deraadt
133 1.1 deraadt void
134 1.15 fvdl rquota_service(struct svc_req *request, SVCXPRT *transp)
135 1.1 deraadt {
136 1.1 deraadt switch (request->rq_proc) {
137 1.1 deraadt case NULLPROC:
138 1.4 mycroft (void)svc_sendreply(transp, xdr_void, (char *)NULL);
139 1.1 deraadt break;
140 1.4 mycroft
141 1.1 deraadt case RQUOTAPROC_GETQUOTA:
142 1.1 deraadt case RQUOTAPROC_GETACTIVEQUOTA:
143 1.20 bouyer sendquota(request, RQUOTAVERS, transp);
144 1.20 bouyer break;
145 1.20 bouyer
146 1.20 bouyer default:
147 1.20 bouyer svcerr_noproc(transp);
148 1.20 bouyer break;
149 1.20 bouyer }
150 1.20 bouyer if (from_inetd)
151 1.20 bouyer exit(0);
152 1.20 bouyer }
153 1.20 bouyer
154 1.20 bouyer void
155 1.20 bouyer ext_rquota_service(struct svc_req *request, SVCXPRT *transp)
156 1.20 bouyer {
157 1.20 bouyer switch (request->rq_proc) {
158 1.20 bouyer case NULLPROC:
159 1.20 bouyer (void)svc_sendreply(transp, xdr_void, (char *)NULL);
160 1.20 bouyer break;
161 1.20 bouyer
162 1.20 bouyer case RQUOTAPROC_GETQUOTA:
163 1.20 bouyer case RQUOTAPROC_GETACTIVEQUOTA:
164 1.20 bouyer sendquota(request, EXT_RQUOTAVERS, transp);
165 1.1 deraadt break;
166 1.4 mycroft
167 1.1 deraadt default:
168 1.4 mycroft svcerr_noproc(transp);
169 1.1 deraadt break;
170 1.1 deraadt }
171 1.4 mycroft if (from_inetd)
172 1.4 mycroft exit(0);
173 1.1 deraadt }
174 1.1 deraadt
175 1.1 deraadt /* read quota for the specified id, and send it */
176 1.1 deraadt void
177 1.20 bouyer sendquota(struct svc_req *request, int vers, SVCXPRT *transp)
178 1.1 deraadt {
179 1.1 deraadt struct getquota_args getq_args;
180 1.20 bouyer struct ext_getquota_args ext_getq_args;
181 1.1 deraadt struct getquota_rslt getq_rslt;
182 1.1 deraadt struct dqblk dqblk;
183 1.1 deraadt struct timeval timev;
184 1.1 deraadt
185 1.12 perry memset((char *)&getq_args, 0, sizeof(getq_args));
186 1.21 bouyer memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args));
187 1.20 bouyer switch (vers) {
188 1.20 bouyer case RQUOTAVERS:
189 1.20 bouyer if (!svc_getargs(transp, xdr_getquota_args,
190 1.20 bouyer (caddr_t)&getq_args)) {
191 1.20 bouyer svcerr_decode(transp);
192 1.20 bouyer return;
193 1.20 bouyer }
194 1.20 bouyer ext_getq_args.gqa_pathp = getq_args.gqa_pathp;
195 1.20 bouyer ext_getq_args.gqa_id = getq_args.gqa_uid;
196 1.20 bouyer ext_getq_args.gqa_type = RQUOTA_USRQUOTA;
197 1.20 bouyer break;
198 1.20 bouyer case EXT_RQUOTAVERS:
199 1.20 bouyer if (!svc_getargs(transp, xdr_ext_getquota_args,
200 1.20 bouyer (caddr_t)&ext_getq_args)) {
201 1.20 bouyer svcerr_decode(transp);
202 1.20 bouyer return;
203 1.20 bouyer }
204 1.20 bouyer break;
205 1.1 deraadt }
206 1.1 deraadt if (request->rq_cred.oa_flavor != AUTH_UNIX) {
207 1.1 deraadt /* bad auth */
208 1.1 deraadt getq_rslt.status = Q_EPERM;
209 1.20 bouyer } else if (!getfsquota(ext_getq_args.gqa_type, ext_getq_args.gqa_id,
210 1.20 bouyer ext_getq_args.gqa_pathp, &dqblk)) {
211 1.4 mycroft /* failed, return noquota */
212 1.4 mycroft getq_rslt.status = Q_NOQUOTA;
213 1.1 deraadt } else {
214 1.1 deraadt gettimeofday(&timev, NULL);
215 1.1 deraadt getq_rslt.status = Q_OK;
216 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
217 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE;
218 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
219 1.1 deraadt dqblk.dqb_bhardlimit;
220 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
221 1.1 deraadt dqblk.dqb_bsoftlimit;
222 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
223 1.1 deraadt dqblk.dqb_curblocks;
224 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
225 1.1 deraadt dqblk.dqb_ihardlimit;
226 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
227 1.1 deraadt dqblk.dqb_isoftlimit;
228 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
229 1.1 deraadt dqblk.dqb_curinodes;
230 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
231 1.1 deraadt dqblk.dqb_btime - timev.tv_sec;
232 1.1 deraadt getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
233 1.1 deraadt dqblk.dqb_itime - timev.tv_sec;
234 1.1 deraadt }
235 1.10 lukem if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt))
236 1.4 mycroft svcerr_systemerr(transp);
237 1.4 mycroft if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
238 1.4 mycroft syslog(LOG_ERR, "unable to free arguments");
239 1.4 mycroft exit(1);
240 1.4 mycroft }
241 1.1 deraadt }
242 1.1 deraadt
243 1.1 deraadt /* initialise the fs_tab list from entries in /etc/fstab */
244 1.1 deraadt void
245 1.1 deraadt initfs()
246 1.1 deraadt {
247 1.1 deraadt struct fs_stat *fs_current = NULL;
248 1.1 deraadt struct fs_stat *fs_next = NULL;
249 1.20 bouyer char *uqfpathname, *gqfpathname;
250 1.1 deraadt struct fstab *fs;
251 1.1 deraadt struct stat st;
252 1.1 deraadt
253 1.1 deraadt setfsent();
254 1.9 mrg while ((fs = getfsent())) {
255 1.11 mrg if (strcmp(fs->fs_vfstype, MOUNT_FFS))
256 1.1 deraadt continue;
257 1.20 bouyer if (!hasquota(fs, &uqfpathname, &gqfpathname))
258 1.1 deraadt continue;
259 1.1 deraadt
260 1.1 deraadt fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat));
261 1.10 lukem if (fs_current == NULL) {
262 1.10 lukem syslog(LOG_ERR, "can't malloc: %m");
263 1.10 lukem exit(1);
264 1.10 lukem }
265 1.1 deraadt fs_current->fs_next = fs_next; /* next element */
266 1.1 deraadt
267 1.10 lukem fs_current->fs_file = strdup(fs->fs_file);
268 1.10 lukem if (fs_current->fs_file == NULL) {
269 1.10 lukem syslog(LOG_ERR, "can't strdup: %m");
270 1.10 lukem exit(1);
271 1.10 lukem }
272 1.1 deraadt
273 1.20 bouyer if (uqfpathname) {
274 1.20 bouyer fs_current->uqfpathname = strdup(uqfpathname);
275 1.20 bouyer if (fs_current->uqfpathname == NULL) {
276 1.20 bouyer syslog(LOG_ERR, "can't strdup: %m");
277 1.20 bouyer exit(1);
278 1.20 bouyer }
279 1.20 bouyer } else
280 1.20 bouyer fs_current->uqfpathname = NULL;
281 1.20 bouyer if (gqfpathname) {
282 1.20 bouyer fs_current->gqfpathname = strdup(gqfpathname);
283 1.20 bouyer if (fs_current->gqfpathname == NULL) {
284 1.20 bouyer syslog(LOG_ERR, "can't strdup: %m");
285 1.20 bouyer exit(1);
286 1.20 bouyer }
287 1.20 bouyer } else
288 1.20 bouyer fs_current->gqfpathname = NULL;
289 1.10 lukem stat(fs->fs_file, &st);
290 1.1 deraadt fs_current->st_dev = st.st_dev;
291 1.1 deraadt
292 1.1 deraadt fs_next = fs_current;
293 1.1 deraadt }
294 1.1 deraadt endfsent();
295 1.1 deraadt fs_begin = fs_current;
296 1.1 deraadt }
297 1.1 deraadt
298 1.1 deraadt /*
299 1.1 deraadt * gets the quotas for id, filesystem path.
300 1.1 deraadt * Return 0 if fail, 1 otherwise
301 1.1 deraadt */
302 1.1 deraadt int
303 1.20 bouyer getfsquota(int type, long id, char *path, struct dqblk *dqblk)
304 1.1 deraadt {
305 1.1 deraadt struct stat st_path;
306 1.1 deraadt struct fs_stat *fs;
307 1.1 deraadt int qcmd, fd, ret = 0;
308 1.20 bouyer char *filename;
309 1.1 deraadt
310 1.1 deraadt if (stat(path, &st_path) < 0)
311 1.1 deraadt return (0);
312 1.1 deraadt
313 1.20 bouyer qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA);
314 1.1 deraadt
315 1.1 deraadt for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
316 1.10 lukem /* where the device is the same as path */
317 1.1 deraadt if (fs->st_dev != st_path.st_dev)
318 1.1 deraadt continue;
319 1.1 deraadt
320 1.1 deraadt /* find the specified filesystem. get and return quota */
321 1.1 deraadt if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
322 1.1 deraadt return (1);
323 1.20 bouyer filename = (type == RQUOTA_USRQUOTA) ?
324 1.20 bouyer fs->uqfpathname : fs->gqfpathname;
325 1.20 bouyer if (filename == NULL)
326 1.20 bouyer return 0;
327 1.20 bouyer if ((fd = open(filename, O_RDONLY)) < 0) {
328 1.20 bouyer syslog(LOG_WARNING, "open error: %s: %m", filename);
329 1.1 deraadt return (0);
330 1.1 deraadt }
331 1.8 kleink if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET)
332 1.8 kleink == (off_t)-1) {
333 1.1 deraadt close(fd);
334 1.20 bouyer return (0);
335 1.1 deraadt }
336 1.1 deraadt switch (read(fd, dqblk, sizeof(struct dqblk))) {
337 1.1 deraadt case 0:
338 1.1 deraadt /*
339 1.1 deraadt * Convert implicit 0 quota (EOF)
340 1.1 deraadt * into an explicit one (zero'ed dqblk)
341 1.1 deraadt */
342 1.12 perry memset((caddr_t) dqblk, 0, sizeof(struct dqblk));
343 1.1 deraadt ret = 1;
344 1.1 deraadt break;
345 1.1 deraadt case sizeof(struct dqblk): /* OK */
346 1.1 deraadt ret = 1;
347 1.1 deraadt break;
348 1.1 deraadt default: /* ERROR */
349 1.20 bouyer syslog(LOG_WARNING, "read error: %s: %m", filename);
350 1.1 deraadt close(fd);
351 1.1 deraadt return (0);
352 1.1 deraadt }
353 1.1 deraadt close(fd);
354 1.1 deraadt }
355 1.1 deraadt return (ret);
356 1.1 deraadt }
357 1.1 deraadt
358 1.1 deraadt /*
359 1.1 deraadt * Check to see if a particular quota is to be enabled.
360 1.1 deraadt * Comes from quota.c, NetBSD 0.9
361 1.1 deraadt */
362 1.1 deraadt int
363 1.20 bouyer hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep)
364 1.1 deraadt {
365 1.20 bouyer static char initname=0, usrname[100], grpname[100];
366 1.20 bouyer static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN];
367 1.9 mrg char *opt, *cp = NULL;
368 1.20 bouyer int ret = 0;
369 1.1 deraadt
370 1.1 deraadt if (!initname) {
371 1.9 mrg (void)snprintf(usrname, sizeof usrname, "%s%s",
372 1.9 mrg qfextension[USRQUOTA], QUOTAFILENAME);
373 1.20 bouyer (void)snprintf(grpname, sizeof grpname, "%s%s",
374 1.20 bouyer qfextension[GRPQUOTA], QUOTAFILENAME);
375 1.1 deraadt }
376 1.20 bouyer
377 1.20 bouyer *uqfnamep = NULL;
378 1.20 bouyer *gqfnamep = NULL;
379 1.22 jrf (void)strlcpy(buf, fs->fs_mntops, sizeof(buf));
380 1.1 deraadt for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
381 1.10 lukem if ((cp = strchr(opt, '=')))
382 1.1 deraadt *cp++ = '\0';
383 1.20 bouyer if (strcmp(opt, usrname) == 0) {
384 1.20 bouyer ret = 1;
385 1.20 bouyer if (cp)
386 1.20 bouyer *uqfnamep = cp;
387 1.20 bouyer else {
388 1.20 bouyer (void)snprintf(ubuf, sizeof ubuf, "%s/%s.%s",
389 1.20 bouyer fs->fs_file, QUOTAFILENAME,
390 1.20 bouyer qfextension[USRQUOTA]);
391 1.20 bouyer *uqfnamep = ubuf;
392 1.20 bouyer }
393 1.20 bouyer } else if (strcmp(opt, grpname) == 0) {
394 1.20 bouyer ret = 1;
395 1.20 bouyer if (cp)
396 1.20 bouyer *gqfnamep = cp;
397 1.20 bouyer else {
398 1.20 bouyer (void)snprintf(gbuf, sizeof gbuf, "%s/%s.%s",
399 1.20 bouyer fs->fs_file, QUOTAFILENAME,
400 1.20 bouyer qfextension[GRPQUOTA]);
401 1.20 bouyer *gqfnamep = gbuf;
402 1.20 bouyer }
403 1.20 bouyer }
404 1.1 deraadt }
405 1.20 bouyer return (ret);
406 1.1 deraadt }
407