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