1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <signal.h> 35 #include <syslog.h> 36 #include <string.h> 37 #include <stropts.h> 38 #include <errno.h> 39 #include <sys/netconfig.h> 40 #include <sys/mntent.h> 41 #include <sys/mnttab.h> 42 #include <sys/param.h> 43 #include <sys/time.h> 44 #ifdef notdef 45 #include <netconfig.h> 46 #endif 47 #include <sys/stat.h> 48 #include <sys/file.h> 49 #include <sys/fs/ufs_quota.h> 50 #include <netdir.h> 51 #include <rpc/rpc.h> 52 #include <rpcsvc/rquota.h> 53 #include <tiuser.h> 54 #include <unistd.h> 55 56 #define QFNAME "quotas" /* name of quota file */ 57 #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */ 58 59 struct fsquot { 60 struct fsquot *fsq_next; 61 char *fsq_dir; 62 char *fsq_devname; 63 dev_t fsq_dev; 64 }; 65 66 struct fsquot *fsqlist = NULL; 67 68 typedef struct authunix_parms *authp; 69 70 static int request_pending; /* Request in progress ? */ 71 72 void closedown(); 73 void dispatch(); 74 struct fsquot *findfsq(); 75 void freefs(); 76 int getdiskquota(); 77 void getquota(); 78 int hasquota(); 79 void log_cant_reply(); 80 void setupfs(); 81 82 /*ARGSUSED*/ 83 main(argc, argv) 84 int argc; 85 char **argv; 86 { 87 register SVCXPRT *transp; 88 89 /* 90 * If stdin looks like a TLI endpoint, we assume 91 * that we were started by a port monitor. If 92 * t_getstate fails with TBADF, this is not a 93 * TLI endpoint. 94 */ 95 if (t_getstate(0) != -1 || t_errno != TBADF) { 96 char *netid; 97 struct netconfig *nconf = NULL; 98 99 openlog("rquotad", LOG_PID, LOG_DAEMON); 100 101 if ((netid = getenv("NLSPROVIDER")) == NULL) { 102 struct t_info tinfo; 103 104 if (t_sync(0) == -1) { 105 syslog(LOG_ERR, "could not do t_sync"); 106 exit(1); 107 } 108 if (t_getinfo(0, &tinfo) == -1) { 109 syslog(LOG_ERR, "t_getinfo failed"); 110 exit(1); 111 } 112 if (tinfo.servtype == T_CLTS) { 113 if (tinfo.addr == INET_ADDRSTRLEN) 114 netid = "udp"; 115 else 116 netid = "udp6"; 117 } else { 118 syslog(LOG_ERR, "wrong transport"); 119 exit(1); 120 } 121 } 122 if ((nconf = getnetconfigent(netid)) == NULL) { 123 syslog(LOG_ERR, "cannot get transport info"); 124 } 125 126 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { 127 syslog(LOG_ERR, "cannot create server handle"); 128 exit(1); 129 } 130 if (nconf) 131 freenetconfigent(nconf); 132 133 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) { 134 syslog(LOG_ERR, 135 "unable to register (RQUOTAPROG, RQUOTAVERS)."); 136 exit(1); 137 } 138 139 (void) sigset(SIGALRM, (void(*)(int)) closedown); 140 (void) alarm(RPCSVC_CLOSEDOWN); 141 142 svc_run(); 143 exit(1); 144 /* NOTREACHED */ 145 } 146 147 /* 148 * Started from a shell - fork the daemon. 149 */ 150 151 switch (fork()) { 152 case 0: /* child */ 153 break; 154 case -1: 155 perror("rquotad: can't fork"); 156 exit(1); 157 default: /* parent */ 158 exit(0); 159 } 160 161 /* 162 * Close existing file descriptors, open "/dev/null" as 163 * standard input, output, and error, and detach from 164 * controlling terminal. 165 */ 166 closefrom(0); 167 (void) open("/dev/null", O_RDONLY); 168 (void) open("/dev/null", O_WRONLY); 169 (void) dup(1); 170 (void) setsid(); 171 172 openlog("rquotad", LOG_PID, LOG_DAEMON); 173 174 /* 175 * Create datagram service 176 */ 177 if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) { 178 syslog(LOG_ERR, "couldn't register datagram_v service"); 179 exit(1); 180 } 181 182 /* 183 * Start serving 184 */ 185 svc_run(); 186 syslog(LOG_ERR, "Error: svc_run shouldn't have returned"); 187 exit(1); 188 /* NOTREACHED */ 189 } 190 191 void 192 dispatch(rqstp, transp) 193 register struct svc_req *rqstp; 194 register SVCXPRT *transp; 195 { 196 197 request_pending = 1; 198 199 switch (rqstp->rq_proc) { 200 case NULLPROC: 201 errno = 0; 202 if (!svc_sendreply(transp, xdr_void, 0)) 203 log_cant_reply(transp); 204 break; 205 206 case RQUOTAPROC_GETQUOTA: 207 case RQUOTAPROC_GETACTIVEQUOTA: 208 getquota(rqstp, transp); 209 break; 210 211 default: 212 svcerr_noproc(transp); 213 break; 214 } 215 216 request_pending = 0; 217 } 218 219 void 220 closedown() 221 { 222 if (!request_pending) { 223 int i, openfd; 224 struct t_info tinfo; 225 226 if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS)) 227 exit(0); 228 229 for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) { 230 if (svc_pollfd[i].fd >= 0) 231 openfd++; 232 } 233 234 if (openfd <= 1) 235 exit(0); 236 } 237 (void) alarm(RPCSVC_CLOSEDOWN); 238 } 239 240 void 241 getquota(rqstp, transp) 242 register struct svc_req *rqstp; 243 register SVCXPRT *transp; 244 { 245 struct getquota_args gqa; 246 struct getquota_rslt gqr; 247 struct dqblk dqblk; 248 struct fsquot *fsqp; 249 struct timeval tv; 250 bool_t qactive; 251 252 gqa.gqa_pathp = NULL; /* let xdr allocate the storage */ 253 if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) { 254 svcerr_decode(transp); 255 return; 256 } 257 /* 258 * This authentication is really bogus with the current rpc 259 * authentication scheme. One day we will have something for real. 260 */ 261 if (rqstp->rq_cred.oa_flavor != AUTH_UNIX || 262 (((authp) rqstp->rq_clntcred)->aup_uid != 0 && 263 ((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) { 264 gqr.status = Q_EPERM; 265 goto sendreply; 266 } 267 fsqp = findfsq(gqa.gqa_pathp); 268 if (fsqp == NULL) { 269 gqr.status = Q_NOQUOTA; 270 goto sendreply; 271 } 272 273 if (quotactl(Q_GETQUOTA, fsqp->fsq_dir, (uid_t)gqa.gqa_uid, &dqblk) != 274 0) { 275 qactive = FALSE; 276 if ((errno == ENOENT) || 277 (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) { 278 gqr.status = Q_NOQUOTA; 279 goto sendreply; 280 } 281 282 /* 283 * If there is no quotas file, don't bother to sync it. 284 */ 285 if (errno != ENOENT) { 286 if (quotactl(Q_ALLSYNC, fsqp->fsq_dir, 287 (uid_t)gqa.gqa_uid, &dqblk) < 0 && 288 errno == EINVAL) 289 syslog(LOG_WARNING, 290 "Quotas are not compiled into this kernel"); 291 if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid, &dqblk) == 292 0) { 293 gqr.status = Q_NOQUOTA; 294 goto sendreply; 295 } 296 } 297 } else { 298 qactive = TRUE; 299 } 300 /* 301 * We send the remaining time instead of the absolute time 302 * because clock skew between machines should be much greater 303 * than rpc delay. 304 */ 305 #define gqrslt getquota_rslt_u.gqr_rquota 306 307 gettimeofday(&tv, NULL); 308 gqr.status = Q_OK; 309 gqr.gqrslt.rq_active = qactive; 310 gqr.gqrslt.rq_bsize = DEV_BSIZE; 311 gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit; 312 gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit; 313 gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks; 314 gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit; 315 gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit; 316 gqr.gqrslt.rq_curfiles = dqblk.dqb_curfiles; 317 gqr.gqrslt.rq_btimeleft = dqblk.dqb_btimelimit - tv.tv_sec; 318 gqr.gqrslt.rq_ftimeleft = dqblk.dqb_ftimelimit - tv.tv_sec; 319 sendreply: 320 errno = 0; 321 if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr)) 322 log_cant_reply(transp); 323 } 324 325 quotactl(cmd, mountp, uid, dqp) 326 int cmd; 327 char *mountp; 328 uid_t uid; 329 struct dqblk *dqp; 330 { 331 int fd; 332 int status; 333 struct quotctl quota; 334 char mountpoint[256]; 335 FILE *fstab; 336 struct mnttab mntp; 337 338 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) { 339 /* 340 * Find the mount point of any ufs file system. this is 341 * because the ioctl that implements the quotactl call has 342 * to go to a real file, and not to the block device. 343 */ 344 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 345 syslog(LOG_ERR, "can not open %s: %m ", MNTTAB); 346 return (-1); 347 } 348 fd = -1; 349 while ((status = getmntent(fstab, &mntp)) == NULL) { 350 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 || 351 !(hasmntopt(&mntp, MNTOPT_RQ) || 352 hasmntopt(&mntp, MNTOPT_QUOTA))) 353 continue; 354 (void) strcpy(mountpoint, mntp.mnt_mountp); 355 strcat(mountpoint, "/quotas"); 356 if ((fd = open64(mountpoint, O_RDWR)) >= 0) 357 break; 358 } 359 fclose(fstab); 360 if (fd == -1) { 361 errno = ENOENT; 362 return (-1); 363 } 364 } else { 365 if (mountp == NULL || mountp[0] == '\0') { 366 errno = ENOENT; 367 return (-1); 368 } 369 (void) strcpy(mountpoint, mountp); 370 strcat(mountpoint, "/quotas"); 371 372 if ((fd = open64(mountpoint, O_RDONLY)) < 0) { 373 errno = ENOENT; 374 syslog(LOG_ERR, "can not open %s: %m ", mountpoint); 375 return (-1); 376 } 377 } 378 quota.op = cmd; 379 quota.uid = uid; 380 quota.addr = (caddr_t)dqp; 381 382 status = ioctl(fd, Q_QUOTACTL, "a); 383 384 close(fd); 385 return (status); 386 } 387 388 /* 389 * Return the quota information for the given path. Returns NULL if none 390 * was found. 391 */ 392 393 struct fsquot * 394 findfsq(dir) 395 char *dir; 396 { 397 struct stat sb; 398 register struct fsquot *fsqp; 399 static time_t lastmtime = 0; /* mount table's previous mtime */ 400 401 /* 402 * If we've never looked at the mount table, or it has changed 403 * since the last time, rebuild the list of quota'd file systems 404 * and remember the current mod time for the mount table. 405 */ 406 407 if (stat(MNTTAB, &sb) < 0) { 408 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB); 409 return (NULL); 410 } 411 if (lastmtime == 0 || sb.st_mtime != lastmtime) { 412 freefs(); 413 setupfs(); 414 lastmtime = sb.st_mtime; 415 } 416 417 /* 418 * Try to find the given path in the list of file systems with 419 * quotas. 420 */ 421 422 if (fsqlist == NULL) 423 return (NULL); 424 if (stat(dir, &sb) < 0) 425 return (NULL); 426 for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) { 427 if (sb.st_dev == fsqp->fsq_dev) 428 return (fsqp); 429 } 430 return (NULL); 431 } 432 433 void 434 setupfs() 435 { 436 register struct fsquot *fsqp; 437 FILE *mt; 438 struct mnttab m; 439 struct stat sb; 440 char qfilename[MAXPATHLEN]; 441 442 mt = fopen(MNTTAB, "r"); 443 if (mt == NULL) { 444 syslog(LOG_ERR, "can't read %s: %m", MNTTAB); 445 return; 446 } 447 448 while (getmntent(mt, &m) == 0) { 449 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0) 450 continue; 451 if (!hasquota(m.mnt_mntopts)) { 452 sprintf(qfilename, "%s/%s", m.mnt_mountp, QFNAME); 453 if (access(qfilename, F_OK) < 0) 454 continue; 455 } 456 if (stat(m.mnt_special, &sb) < 0 || 457 (sb.st_mode & S_IFMT) != S_IFBLK) 458 continue; 459 fsqp = (struct fsquot *)malloc(sizeof (struct fsquot)); 460 if (fsqp == NULL) { 461 syslog(LOG_ERR, "out of memory"); 462 exit(1); 463 } 464 fsqp->fsq_next = fsqlist; 465 fsqp->fsq_dir = (char *)malloc(strlen(m.mnt_mountp) + 1); 466 fsqp->fsq_devname = (char *)malloc(strlen(m.mnt_special) + 1); 467 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) { 468 syslog(LOG_ERR, "out of memory"); 469 exit(1); 470 } 471 strcpy(fsqp->fsq_dir, m.mnt_mountp); 472 strcpy(fsqp->fsq_devname, m.mnt_special); 473 fsqp->fsq_dev = sb.st_rdev; 474 fsqlist = fsqp; 475 } 476 (void) fclose(mt); 477 } 478 479 /* 480 * Free the memory used by the current list of quota'd file systems. Nulls 481 * out the list. 482 */ 483 484 void 485 freefs() 486 { 487 register struct fsquot *fsqp; 488 489 while ((fsqp = fsqlist) != NULL) { 490 fsqlist = fsqp->fsq_next; 491 free(fsqp->fsq_dir); 492 free(fsqp->fsq_devname); 493 free(fsqp); 494 } 495 } 496 497 int 498 getdiskquota(fsqp, uid, dqp) 499 struct fsquot *fsqp; 500 uid_t uid; 501 struct dqblk *dqp; 502 { 503 int fd; 504 char qfilename[MAXPATHLEN]; 505 506 sprintf(qfilename, "%s/%s", fsqp->fsq_dir, QFNAME); 507 if ((fd = open64(qfilename, O_RDONLY)) < 0) 508 return (0); 509 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 510 if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) { 511 close(fd); 512 return (0); 513 } 514 close(fd); 515 if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 && 516 dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) { 517 return (0); 518 } 519 return (1); 520 } 521 522 /* 523 * Get the client's hostname from the transport handle 524 * If the name is not available then return "(anon)". 525 */ 526 struct nd_hostservlist * 527 getclientsnames(transp) 528 SVCXPRT *transp; 529 { 530 struct netbuf *nbuf; 531 struct netconfig *nconf; 532 static struct nd_hostservlist *serv; 533 static struct nd_hostservlist anon_hsl; 534 static struct nd_hostserv anon_hs; 535 static char anon_hname[] = "(anon)"; 536 static char anon_sname[] = ""; 537 538 /* Set up anonymous client */ 539 anon_hs.h_host = anon_hname; 540 anon_hs.h_serv = anon_sname; 541 anon_hsl.h_cnt = 1; 542 anon_hsl.h_hostservs = &anon_hs; 543 544 if (serv) { 545 netdir_free((char *)serv, ND_HOSTSERVLIST); 546 serv = NULL; 547 } 548 nconf = getnetconfigent(transp->xp_netid); 549 if (nconf == NULL) { 550 syslog(LOG_ERR, "%s: getnetconfigent failed", 551 transp->xp_netid); 552 return (&anon_hsl); 553 } 554 555 nbuf = svc_getrpccaller(transp); 556 if (nbuf == NULL) { 557 freenetconfigent(nconf); 558 return (&anon_hsl); 559 } 560 if (netdir_getbyaddr(nconf, &serv, nbuf)) { 561 freenetconfigent(nconf); 562 return (&anon_hsl); 563 } 564 freenetconfigent(nconf); 565 return (serv); 566 } 567 568 void 569 log_cant_reply(transp) 570 SVCXPRT *transp; 571 { 572 int saverrno; 573 struct nd_hostservlist *clnames; 574 register char *name; 575 576 saverrno = errno; /* save error code */ 577 clnames = getclientsnames(transp); 578 if (clnames == NULL) 579 return; 580 name = clnames->h_hostservs->h_host; 581 582 errno = saverrno; 583 if (errno == 0) 584 syslog(LOG_ERR, "couldn't send reply to %s", name); 585 else 586 syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 587 } 588 589 char *mntopts[] = { MNTOPT_QUOTA, NULL }; 590 #define QUOTA 0 591 592 /* 593 * Return 1 if "quota" appears in the options string 594 */ 595 int 596 hasquota(opts) 597 char *opts; 598 { 599 char *value; 600 601 if (opts == NULL) 602 return (0); 603 while (*opts != '\0') { 604 if (getsubopt(&opts, mntopts, &value) == QUOTA) 605 return (1); 606 } 607 608 return (0); 609 } 610