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