1 |
|
|
/* $OpenBSD: rquotad.c,v 1.22 2015/01/16 06:39:50 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* by Manuel Bouyer (bouyer@ensta.fr). Public domain. |
5 |
|
|
*/ |
6 |
|
|
|
7 |
|
|
#include <sys/param.h> /* DEV_BSIZE */ |
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 |
54 |
|
|
cleanup(int signo) |
55 |
|
|
{ |
56 |
|
|
(void) pmap_unset(RQUOTAPROG, RQUOTAVERS); /* XXX signal races */ |
57 |
|
|
_exit(0); |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
int |
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 |
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 |
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 |
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 = 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 |
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 |
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 |
|
|
} |