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