1 /* $NetBSD: rquotad.c,v 1.22 2004/09/07 13:20:40 jrf Exp $ */ 2 3 /* 4 * by Manuel Bouyer (bouyer@ensta.fr). Public domain. 5 */ 6 7 #include <sys/cdefs.h> 8 #ifndef lint 9 __RCSID("$NetBSD: rquotad.c,v 1.22 2004/09/07 13:20:40 jrf Exp $"); 10 #endif 11 12 #include <sys/param.h> 13 #include <sys/types.h> 14 #include <sys/mount.h> 15 #include <sys/file.h> 16 #include <sys/stat.h> 17 #include <sys/socket.h> 18 #include <signal.h> 19 20 #include <stdio.h> 21 #include <fstab.h> 22 #include <ctype.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <pwd.h> 26 #include <grp.h> 27 #include <errno.h> 28 #include <unistd.h> 29 30 #include <syslog.h> 31 32 #include <ufs/ufs/quota.h> 33 #include <rpc/rpc.h> 34 #include <rpcsvc/rquota.h> 35 #include <arpa/inet.h> 36 37 void rquota_service(struct svc_req *request, SVCXPRT *transp); 38 void ext_rquota_service(struct svc_req *request, SVCXPRT *transp); 39 void sendquota(struct svc_req *request, int vers, SVCXPRT *transp); 40 void initfs(void); 41 int getfsquota(int type, long id, char *path, struct dqblk *dqblk); 42 int hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep); 43 void cleanup(int); 44 int main(int, char *[]); 45 46 /* 47 * structure containing informations about ufs filesystems 48 * initialised by initfs() 49 */ 50 struct fs_stat { 51 struct fs_stat *fs_next; /* next element */ 52 char *fs_file; /* mount point of the filesystem */ 53 char *uqfpathname; /* pathname of the user quota file */ 54 char *gqfpathname; /* pathname of the group quota file */ 55 dev_t st_dev; /* device of the filesystem */ 56 } fs_stat; 57 struct fs_stat *fs_begin = NULL; 58 59 char *qfextension[] = INITQFNAMES; 60 int from_inetd = 1; 61 62 void 63 cleanup(int dummy) 64 { 65 66 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 67 (void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 68 exit(0); 69 } 70 71 int 72 main(int argc, char *argv[]) 73 { 74 SVCXPRT *transp; 75 struct sockaddr_storage from; 76 int fromlen; 77 78 fromlen = sizeof(from); 79 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 80 from_inetd = 0; 81 82 if (!from_inetd) { 83 daemon(0, 0); 84 85 (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 86 (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 87 (void) signal(SIGINT, cleanup); 88 (void) signal(SIGTERM, cleanup); 89 (void) signal(SIGHUP, cleanup); 90 } 91 92 openlog("rpc.rquotad", LOG_PID, LOG_DAEMON); 93 94 /* create and register the service */ 95 if (from_inetd) { 96 transp = svc_dg_create(0, 0, 0); 97 if (transp == NULL) { 98 syslog(LOG_ERR, "couldn't create udp service."); 99 exit(1); 100 } 101 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, 102 NULL)) { 103 syslog(LOG_ERR, 104 "unable to register (RQUOTAPROG, RQUOTAVERS)."); 105 exit(1); 106 } 107 if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 108 ext_rquota_service, NULL)) { 109 syslog(LOG_ERR, 110 "unable to register (RQUOTAPROG, EXT_RQUOTAVERS)."); 111 exit(1); 112 } 113 } else { 114 if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){ 115 syslog(LOG_ERR, 116 "unable to create (RQUOTAPROG, RQUOTAVERS)."); 117 exit(1); 118 } 119 if (!svc_create(ext_rquota_service, RQUOTAPROG, 120 EXT_RQUOTAVERS, "udp")){ 121 syslog(LOG_ERR, 122 "unable to create (RQUOTAPROG, EXT_RQUOTAVERS)."); 123 exit(1); 124 } 125 } 126 127 initfs(); /* init the fs_stat list */ 128 svc_run(); 129 syslog(LOG_ERR, "svc_run returned"); 130 exit(1); 131 } 132 133 void 134 rquota_service(struct svc_req *request, SVCXPRT *transp) 135 { 136 switch (request->rq_proc) { 137 case NULLPROC: 138 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 139 break; 140 141 case RQUOTAPROC_GETQUOTA: 142 case RQUOTAPROC_GETACTIVEQUOTA: 143 sendquota(request, RQUOTAVERS, transp); 144 break; 145 146 default: 147 svcerr_noproc(transp); 148 break; 149 } 150 if (from_inetd) 151 exit(0); 152 } 153 154 void 155 ext_rquota_service(struct svc_req *request, SVCXPRT *transp) 156 { 157 switch (request->rq_proc) { 158 case NULLPROC: 159 (void)svc_sendreply(transp, xdr_void, (char *)NULL); 160 break; 161 162 case RQUOTAPROC_GETQUOTA: 163 case RQUOTAPROC_GETACTIVEQUOTA: 164 sendquota(request, EXT_RQUOTAVERS, transp); 165 break; 166 167 default: 168 svcerr_noproc(transp); 169 break; 170 } 171 if (from_inetd) 172 exit(0); 173 } 174 175 /* read quota for the specified id, and send it */ 176 void 177 sendquota(struct svc_req *request, int vers, SVCXPRT *transp) 178 { 179 struct getquota_args getq_args; 180 struct ext_getquota_args ext_getq_args; 181 struct getquota_rslt getq_rslt; 182 struct dqblk dqblk; 183 struct timeval timev; 184 185 memset((char *)&getq_args, 0, sizeof(getq_args)); 186 memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args)); 187 switch (vers) { 188 case RQUOTAVERS: 189 if (!svc_getargs(transp, xdr_getquota_args, 190 (caddr_t)&getq_args)) { 191 svcerr_decode(transp); 192 return; 193 } 194 ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 195 ext_getq_args.gqa_id = getq_args.gqa_uid; 196 ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 197 break; 198 case EXT_RQUOTAVERS: 199 if (!svc_getargs(transp, xdr_ext_getquota_args, 200 (caddr_t)&ext_getq_args)) { 201 svcerr_decode(transp); 202 return; 203 } 204 break; 205 } 206 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 207 /* bad auth */ 208 getq_rslt.status = Q_EPERM; 209 } else if (!getfsquota(ext_getq_args.gqa_type, ext_getq_args.gqa_id, 210 ext_getq_args.gqa_pathp, &dqblk)) { 211 /* failed, return noquota */ 212 getq_rslt.status = Q_NOQUOTA; 213 } else { 214 gettimeofday(&timev, NULL); 215 getq_rslt.status = Q_OK; 216 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 217 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; 218 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 219 dqblk.dqb_bhardlimit; 220 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 221 dqblk.dqb_bsoftlimit; 222 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 223 dqblk.dqb_curblocks; 224 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 225 dqblk.dqb_ihardlimit; 226 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 227 dqblk.dqb_isoftlimit; 228 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 229 dqblk.dqb_curinodes; 230 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 231 dqblk.dqb_btime - timev.tv_sec; 232 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 233 dqblk.dqb_itime - timev.tv_sec; 234 } 235 if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) 236 svcerr_systemerr(transp); 237 if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 238 syslog(LOG_ERR, "unable to free arguments"); 239 exit(1); 240 } 241 } 242 243 /* initialise the fs_tab list from entries in /etc/fstab */ 244 void 245 initfs() 246 { 247 struct fs_stat *fs_current = NULL; 248 struct fs_stat *fs_next = NULL; 249 char *uqfpathname, *gqfpathname; 250 struct fstab *fs; 251 struct stat st; 252 253 setfsent(); 254 while ((fs = getfsent())) { 255 if (strcmp(fs->fs_vfstype, MOUNT_FFS)) 256 continue; 257 if (!hasquota(fs, &uqfpathname, &gqfpathname)) 258 continue; 259 260 fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); 261 if (fs_current == NULL) { 262 syslog(LOG_ERR, "can't malloc: %m"); 263 exit(1); 264 } 265 fs_current->fs_next = fs_next; /* next element */ 266 267 fs_current->fs_file = strdup(fs->fs_file); 268 if (fs_current->fs_file == NULL) { 269 syslog(LOG_ERR, "can't strdup: %m"); 270 exit(1); 271 } 272 273 if (uqfpathname) { 274 fs_current->uqfpathname = strdup(uqfpathname); 275 if (fs_current->uqfpathname == NULL) { 276 syslog(LOG_ERR, "can't strdup: %m"); 277 exit(1); 278 } 279 } else 280 fs_current->uqfpathname = NULL; 281 if (gqfpathname) { 282 fs_current->gqfpathname = strdup(gqfpathname); 283 if (fs_current->gqfpathname == NULL) { 284 syslog(LOG_ERR, "can't strdup: %m"); 285 exit(1); 286 } 287 } else 288 fs_current->gqfpathname = NULL; 289 stat(fs->fs_file, &st); 290 fs_current->st_dev = st.st_dev; 291 292 fs_next = fs_current; 293 } 294 endfsent(); 295 fs_begin = fs_current; 296 } 297 298 /* 299 * gets the quotas for id, filesystem path. 300 * Return 0 if fail, 1 otherwise 301 */ 302 int 303 getfsquota(int type, long id, char *path, struct dqblk *dqblk) 304 { 305 struct stat st_path; 306 struct fs_stat *fs; 307 int qcmd, fd, ret = 0; 308 char *filename; 309 310 if (stat(path, &st_path) < 0) 311 return (0); 312 313 qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA); 314 315 for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { 316 /* where the device is the same as path */ 317 if (fs->st_dev != st_path.st_dev) 318 continue; 319 320 /* find the specified filesystem. get and return quota */ 321 if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) 322 return (1); 323 filename = (type == RQUOTA_USRQUOTA) ? 324 fs->uqfpathname : fs->gqfpathname; 325 if (filename == NULL) 326 return 0; 327 if ((fd = open(filename, O_RDONLY)) < 0) { 328 syslog(LOG_WARNING, "open error: %s: %m", filename); 329 return (0); 330 } 331 if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET) 332 == (off_t)-1) { 333 close(fd); 334 return (0); 335 } 336 switch (read(fd, dqblk, sizeof(struct dqblk))) { 337 case 0: 338 /* 339 * Convert implicit 0 quota (EOF) 340 * into an explicit one (zero'ed dqblk) 341 */ 342 memset((caddr_t) dqblk, 0, sizeof(struct dqblk)); 343 ret = 1; 344 break; 345 case sizeof(struct dqblk): /* OK */ 346 ret = 1; 347 break; 348 default: /* ERROR */ 349 syslog(LOG_WARNING, "read error: %s: %m", filename); 350 close(fd); 351 return (0); 352 } 353 close(fd); 354 } 355 return (ret); 356 } 357 358 /* 359 * Check to see if a particular quota is to be enabled. 360 * Comes from quota.c, NetBSD 0.9 361 */ 362 int 363 hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep) 364 { 365 static char initname=0, usrname[100], grpname[100]; 366 static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN]; 367 char *opt, *cp = NULL; 368 int ret = 0; 369 370 if (!initname) { 371 (void)snprintf(usrname, sizeof usrname, "%s%s", 372 qfextension[USRQUOTA], QUOTAFILENAME); 373 (void)snprintf(grpname, sizeof grpname, "%s%s", 374 qfextension[GRPQUOTA], QUOTAFILENAME); 375 } 376 377 *uqfnamep = NULL; 378 *gqfnamep = NULL; 379 (void)strlcpy(buf, fs->fs_mntops, sizeof(buf)); 380 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 381 if ((cp = strchr(opt, '='))) 382 *cp++ = '\0'; 383 if (strcmp(opt, usrname) == 0) { 384 ret = 1; 385 if (cp) 386 *uqfnamep = cp; 387 else { 388 (void)snprintf(ubuf, sizeof ubuf, "%s/%s.%s", 389 fs->fs_file, QUOTAFILENAME, 390 qfextension[USRQUOTA]); 391 *uqfnamep = ubuf; 392 } 393 } else if (strcmp(opt, grpname) == 0) { 394 ret = 1; 395 if (cp) 396 *gqfnamep = cp; 397 else { 398 (void)snprintf(gbuf, sizeof gbuf, "%s/%s.%s", 399 fs->fs_file, QUOTAFILENAME, 400 qfextension[GRPQUOTA]); 401 *gqfnamep = gbuf; 402 } 403 } 404 } 405 return (ret); 406 } 407