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