1 /* $NetBSD: rquotad.c,v 1.20 2003/02/14 14:55:59 bouyer 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.20 2003/02/14 14:55:59 bouyer 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 switch (vers) { 187 case RQUOTAVERS: 188 if (!svc_getargs(transp, xdr_getquota_args, 189 (caddr_t)&getq_args)) { 190 svcerr_decode(transp); 191 return; 192 } 193 ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 194 ext_getq_args.gqa_id = getq_args.gqa_uid; 195 ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 196 break; 197 case EXT_RQUOTAVERS: 198 if (!svc_getargs(transp, xdr_ext_getquota_args, 199 (caddr_t)&ext_getq_args)) { 200 svcerr_decode(transp); 201 return; 202 } 203 break; 204 } 205 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 206 /* bad auth */ 207 getq_rslt.status = Q_EPERM; 208 } else if (!getfsquota(ext_getq_args.gqa_type, ext_getq_args.gqa_id, 209 ext_getq_args.gqa_pathp, &dqblk)) { 210 /* failed, return noquota */ 211 getq_rslt.status = Q_NOQUOTA; 212 } else { 213 gettimeofday(&timev, NULL); 214 getq_rslt.status = Q_OK; 215 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 216 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; 217 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 218 dqblk.dqb_bhardlimit; 219 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 220 dqblk.dqb_bsoftlimit; 221 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 222 dqblk.dqb_curblocks; 223 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 224 dqblk.dqb_ihardlimit; 225 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 226 dqblk.dqb_isoftlimit; 227 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 228 dqblk.dqb_curinodes; 229 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 230 dqblk.dqb_btime - timev.tv_sec; 231 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 232 dqblk.dqb_itime - timev.tv_sec; 233 } 234 if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) 235 svcerr_systemerr(transp); 236 if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 237 syslog(LOG_ERR, "unable to free arguments"); 238 exit(1); 239 } 240 } 241 242 /* initialise the fs_tab list from entries in /etc/fstab */ 243 void 244 initfs() 245 { 246 struct fs_stat *fs_current = NULL; 247 struct fs_stat *fs_next = NULL; 248 char *uqfpathname, *gqfpathname; 249 struct fstab *fs; 250 struct stat st; 251 252 setfsent(); 253 while ((fs = getfsent())) { 254 if (strcmp(fs->fs_vfstype, MOUNT_FFS)) 255 continue; 256 if (!hasquota(fs, &uqfpathname, &gqfpathname)) 257 continue; 258 259 fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); 260 if (fs_current == NULL) { 261 syslog(LOG_ERR, "can't malloc: %m"); 262 exit(1); 263 } 264 fs_current->fs_next = fs_next; /* next element */ 265 266 fs_current->fs_file = strdup(fs->fs_file); 267 if (fs_current->fs_file == NULL) { 268 syslog(LOG_ERR, "can't strdup: %m"); 269 exit(1); 270 } 271 272 if (uqfpathname) { 273 fs_current->uqfpathname = strdup(uqfpathname); 274 if (fs_current->uqfpathname == NULL) { 275 syslog(LOG_ERR, "can't strdup: %m"); 276 exit(1); 277 } 278 } else 279 fs_current->uqfpathname = NULL; 280 if (gqfpathname) { 281 fs_current->gqfpathname = strdup(gqfpathname); 282 if (fs_current->gqfpathname == NULL) { 283 syslog(LOG_ERR, "can't strdup: %m"); 284 exit(1); 285 } 286 } else 287 fs_current->gqfpathname = NULL; 288 stat(fs->fs_file, &st); 289 fs_current->st_dev = st.st_dev; 290 291 fs_next = fs_current; 292 } 293 endfsent(); 294 fs_begin = fs_current; 295 } 296 297 /* 298 * gets the quotas for id, filesystem path. 299 * Return 0 if fail, 1 otherwise 300 */ 301 int 302 getfsquota(int type, long id, char *path, struct dqblk *dqblk) 303 { 304 struct stat st_path; 305 struct fs_stat *fs; 306 int qcmd, fd, ret = 0; 307 char *filename; 308 309 if (stat(path, &st_path) < 0) 310 return (0); 311 312 qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA); 313 314 for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { 315 /* where the device is the same as path */ 316 if (fs->st_dev != st_path.st_dev) 317 continue; 318 319 /* find the specified filesystem. get and return quota */ 320 if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) 321 return (1); 322 filename = (type == RQUOTA_USRQUOTA) ? 323 fs->uqfpathname : fs->gqfpathname; 324 if (filename == NULL) 325 return 0; 326 if ((fd = open(filename, O_RDONLY)) < 0) { 327 syslog(LOG_WARNING, "open error: %s: %m", filename); 328 return (0); 329 } 330 if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET) 331 == (off_t)-1) { 332 close(fd); 333 return (0); 334 } 335 switch (read(fd, dqblk, sizeof(struct dqblk))) { 336 case 0: 337 /* 338 * Convert implicit 0 quota (EOF) 339 * into an explicit one (zero'ed dqblk) 340 */ 341 memset((caddr_t) dqblk, 0, sizeof(struct dqblk)); 342 ret = 1; 343 break; 344 case sizeof(struct dqblk): /* OK */ 345 ret = 1; 346 break; 347 default: /* ERROR */ 348 syslog(LOG_WARNING, "read error: %s: %m", filename); 349 close(fd); 350 return (0); 351 } 352 close(fd); 353 } 354 return (ret); 355 } 356 357 /* 358 * Check to see if a particular quota is to be enabled. 359 * Comes from quota.c, NetBSD 0.9 360 */ 361 int 362 hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep) 363 { 364 static char initname=0, usrname[100], grpname[100]; 365 static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN]; 366 char *opt, *cp = NULL; 367 int ret = 0; 368 369 if (!initname) { 370 (void)snprintf(usrname, sizeof usrname, "%s%s", 371 qfextension[USRQUOTA], QUOTAFILENAME); 372 (void)snprintf(grpname, sizeof grpname, "%s%s", 373 qfextension[GRPQUOTA], QUOTAFILENAME); 374 } 375 376 *uqfnamep = NULL; 377 *gqfnamep = NULL; 378 strncpy(buf, fs->fs_mntops, sizeof(buf) - 1); 379 buf[sizeof(buf) - 1] = '\0'; 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