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