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