1/* 2 * Copyright (c) 2007-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* $NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $ */ 25 26/* 27 * by Manuel Bouyer (bouyer@ensta.fr). Public domain. 28 */ 29 30#include <sys/cdefs.h> 31#ifndef lint 32__RCSID("$NetBSD: rquotad.c,v 1.23 2006/05/09 20:18:07 mrg Exp $"); 33#endif 34 35#include <sys/param.h> 36#include <sys/types.h> 37#include <sys/mount.h> 38#include <sys/file.h> 39#include <sys/stat.h> 40#include <sys/socket.h> 41#include <arpa/inet.h> 42#include <sys/event.h> 43#include <sys/time.h> 44#include <signal.h> 45#include <sys/queue.h> 46#include <sys/quota.h> 47 48#include <stdio.h> 49#include <fstab.h> 50#include <ctype.h> 51#include <stdlib.h> 52#include <string.h> 53#include <pwd.h> 54#include <grp.h> 55#include <errno.h> 56#include <unistd.h> 57#include <err.h> 58#include <pthread.h> 59#include <syslog.h> 60#include <libutil.h> 61#include <util.h> 62 63#include <oncrpc/rpc.h> 64#include <oncrpc/rpcb.h> 65#include "rquota.h" 66 67int bindresvport_sa(int sd, struct sockaddr *sa); 68 69void rquota_service(struct svc_req *request, SVCXPRT *transp); 70void ext_rquota_service(struct svc_req *request, SVCXPRT *transp); 71void sendquota(struct svc_req *request, int vers, SVCXPRT *transp); 72int getfsquota(int type, int id, char *path, struct dqblk *dqblk); 73int hasquota(struct statfs *fst, char **uqfnamep, char **gqfnamep); 74void lock_fsq(void); 75void unlock_fsq(void); 76void check_mounts(void); 77void sigmux(int); 78void *rquotad_thread(void *arg); 79 80#if 0 81#define DEBUG(args...) printf(args) 82#else 83#define DEBUG(args...) 84#endif 85 86#define _PATH_NFS_CONF "/etc/nfs.conf" 87#define _PATH_RQUOTAD_PID "/var/run/rquotad.pid" 88 89/* 90 * structure holding NFS server config values 91 */ 92struct nfs_conf_server { 93 int rquota_port; 94 int tcp; 95 int udp; 96 int verbose; 97}; 98const struct nfs_conf_server config_defaults = 99{ 100 0, /* rquota_port */ 101 1, /* tcp */ 102 1, /* udp */ 103 0 /* verbose */ 104}; 105int config_read(struct nfs_conf_server *conf); 106 107/* 108 * structure containing informations about file systems with quota files 109 */ 110struct fsq_stat { 111 TAILQ_ENTRY(fsq_stat) chain; /* list of file systems */ 112 char *mountdir; /* mount point of the filesystem */ 113 char *uqfpathname; /* pathname of the user quota file */ 114 char *gqfpathname; /* pathname of the group quota file */ 115 fsid_t fsid; /* fsid for the file system */ 116 dev_t st_dev; /* device of the filesystem */ 117}; 118TAILQ_HEAD(fsqhead,fsq_stat) fsqhead; 119pthread_mutex_t fsq_mutex; /* mutex for file system quota list */ 120int gotterm = 0; 121struct nfs_conf_server config; 122 123const char *qfextension[] = INITQFNAMES; 124 125void 126sigmux(__unused int dummy) 127{ 128 gotterm = 1; 129} 130 131int 132main(__unused int argc, __unused char *argv[]) 133{ 134 SVCXPRT *transp; 135 struct sockaddr_storage saddr; 136 struct sockaddr_in *sin = (struct sockaddr_in*)&saddr; 137 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&saddr; 138 int error, on = 1, svcregcnt; 139 pthread_attr_t pattr; 140 pthread_t thd; 141 int kq, rv; 142 struct kevent ke; 143 struct pidfh *pfh; 144 pid_t pid; 145 int rqudpsock, rqtcpsock; 146 int rqudp6sock, rqtcp6sock; 147 148 /* If we are serving UDP, set up the RQUOTA/UDP sockets. */ 149 150 /* set defaults then do config_read() to get config values */ 151 config = config_defaults; 152 config_read(&config); 153 154 openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 155 156 /* claim PID file */ 157 pfh = pidfile_open(_PATH_RQUOTAD_PID, 0644, &pid); 158 if (pfh == NULL) { 159 syslog(LOG_ERR, "can't open rquotad pidfile: %s (%d)", strerror(errno), errno); 160 if ((errno == EACCES) && getuid()) 161 syslog(LOG_ERR, "rquotad is expected to be run as root, not as uid %d.", getuid()); 162 else if (errno == EEXIST) 163 syslog(LOG_ERR, "rquotad already running, pid: %d", pid); 164 exit(2); 165 } 166 if (pidfile_write(pfh) == -1) 167 syslog(LOG_WARNING, "can't write to rquotad pidfile: %s (%d)", strerror(errno), errno); 168 169 rpcb_unset(NULL, RQUOTAPROG, RQUOTAVERS); 170 rpcb_unset(NULL, RQUOTAPROG, EXT_RQUOTAVERS); 171 172 signal(SIGINT, sigmux); 173 signal(SIGTERM, sigmux); 174 signal(SIGHUP, sigmux); 175 176 /* create and register the service */ 177 rqudpsock = rqtcpsock = -1; 178 rqudp6sock = rqtcp6sock = -1; 179 180 /* If we are serving UDP, set up the RQUOTA/UDP sockets. */ 181 if (config.udp) { 182 183 /* IPv4 */ 184 if ((rqudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 185 syslog(LOG_WARNING, "can't create UDP IPv4 socket: %s (%d)", strerror(errno), errno); 186 if (rqudpsock >= 0) { 187 sin->sin_family = AF_INET; 188 sin->sin_addr.s_addr = INADDR_ANY; 189 sin->sin_port = htons(config.rquota_port); 190 sin->sin_len = sizeof(*sin); 191 if (bindresvport_sa(rqudpsock, (struct sockaddr *)sin) < 0) { 192 syslog(LOG_WARNING, "can't bind UDP IPv4 addr: %s (%d)", strerror(errno), errno); 193 close(rqudpsock); 194 rqudpsock = -1; 195 } 196 } 197 if ((rqudpsock >= 0) && ((transp = svcudp_create(rqudpsock)) == NULL)) { 198 syslog(LOG_WARNING, "cannot create UDP IPv4 service"); 199 close(rqudpsock); 200 rqudpsock = -1; 201 } 202 if (rqudpsock >= 0) { 203 svcregcnt = 0; 204 if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_UDP)) 205 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, UDP/IPv4)"); 206 else 207 svcregcnt++; 208 if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_UDP)) 209 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP/IPv4)"); 210 else 211 svcregcnt++; 212 if (!svcregcnt) { 213 svc_destroy(transp); 214 close(rqudpsock); 215 rqudpsock = -1; 216 } 217 } 218 219 /* IPv6 */ 220 if ((rqudp6sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 221 syslog(LOG_WARNING, "can't create UDP IPv6 socket: %s (%d)", strerror(errno), errno); 222 if (rqudp6sock >= 0) { 223 setsockopt(rqudp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); 224 sin6->sin6_family = AF_INET6; 225 sin6->sin6_addr = in6addr_any; 226 sin6->sin6_port = htons(config.rquota_port); 227 sin6->sin6_len = sizeof(*sin6); 228 if (bindresvport_sa(rqudp6sock, (struct sockaddr *)sin6) < 0) { 229 syslog(LOG_WARNING, "can't bind UDP IPv6 addr: %s (%d)", strerror(errno), errno); 230 close(rqudp6sock); 231 rqudp6sock = -1; 232 } 233 } 234 if ((rqudp6sock >= 0) && ((transp = svcudp_create(rqudp6sock)) == NULL)) { 235 syslog(LOG_WARNING, "cannot create UDP IPv6 service"); 236 close(rqudp6sock); 237 rqudp6sock = -1; 238 } 239 if (rqudp6sock >= 0) { 240 svcregcnt = 0; 241 if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_UDP)) 242 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, UDP/IPv6)"); 243 else 244 svcregcnt++; 245 if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_UDP)) 246 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP/IPv6)"); 247 else 248 svcregcnt++; 249 if (!svcregcnt) { 250 svc_destroy(transp); 251 close(rqudp6sock); 252 rqudp6sock = -1; 253 } 254 } 255 } 256 257 /* If we are serving TCP, set up the RQUOTA/TCP sockets. */ 258 if (config.tcp) { 259 260 /* IPv4 */ 261 if ((rqtcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 262 syslog(LOG_WARNING, "can't create TCP IPv4 socket: %s (%d)", strerror(errno), errno); 263 if (rqtcpsock >= 0) { 264 if (setsockopt(rqtcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 265 syslog(LOG_WARNING, "setsockopt TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno); 266 sin->sin_family = AF_INET; 267 sin->sin_addr.s_addr = INADDR_ANY; 268 sin->sin_port = htons(config.rquota_port); 269 sin->sin_len = sizeof(*sin); 270 if (bindresvport_sa(rqtcpsock, (struct sockaddr *)sin) < 0) { 271 syslog(LOG_WARNING, "can't bind TCP IPv4 addr: %s (%d)", strerror(errno), errno); 272 close(rqtcpsock); 273 rqtcpsock = -1; 274 } 275 } 276 if ((rqtcpsock >= 0) && ((transp = svctcp_create(rqtcpsock, 0, 0)) == NULL)) { 277 syslog(LOG_WARNING, "cannot create TCP IPv4 service"); 278 close(rqtcpsock); 279 rqtcpsock = -1; 280 } 281 if (rqtcpsock >= 0) { 282 svcregcnt = 0; 283 if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_TCP)) 284 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, TCP/IPv4)"); 285 else 286 svcregcnt++; 287 if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_TCP)) 288 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP/IPv4)"); 289 else 290 svcregcnt++; 291 if (!svcregcnt) { 292 svc_destroy(transp); 293 close(rqtcpsock); 294 rqtcpsock = -1; 295 } 296 } 297 298 /* IPv6 */ 299 if ((rqtcp6sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) 300 syslog(LOG_WARNING, "can't create TCP IPv6 socket: %s (%d)", strerror(errno), errno); 301 if (rqtcp6sock >= 0) { 302 if (setsockopt(rqtcp6sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) 303 syslog(LOG_WARNING, "setsockopt TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno); 304 setsockopt(rqtcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); 305 sin6->sin6_family = AF_INET6; 306 sin6->sin6_addr = in6addr_any; 307 sin6->sin6_port = htons(config.rquota_port); 308 sin6->sin6_len = sizeof(*sin6); 309 if (bindresvport_sa(rqtcp6sock, (struct sockaddr *)sin6) < 0) { 310 syslog(LOG_WARNING, "can't bind TCP IPv6 addr: %s (%d)", strerror(errno), errno); 311 close(rqtcp6sock); 312 rqtcp6sock = -1; 313 } 314 } 315 if ((rqtcp6sock >= 0) && ((transp = svctcp_create(rqtcp6sock, 0, 0)) == NULL)) { 316 syslog(LOG_WARNING, "cannot create TCP IPv6 service"); 317 close(rqtcp6sock); 318 rqtcp6sock = -1; 319 } 320 if (rqtcp6sock >= 0) { 321 svcregcnt = 0; 322 if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, IPPROTO_TCP)) 323 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, RQUOTAVERS, TCP/IPv6)"); 324 else 325 svcregcnt++; 326 if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, ext_rquota_service, IPPROTO_TCP)) 327 syslog(LOG_WARNING, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP/IPv6)"); 328 else 329 svcregcnt++; 330 if (!svcregcnt) { 331 svc_destroy(transp); 332 close(rqtcp6sock); 333 rqtcp6sock = -1; 334 } 335 } 336 } 337 338 if ((rqudp6sock < 0) && (rqtcp6sock < 0)) 339 syslog(LOG_WARNING, "Can't create RQUOTA IPv6 sockets"); 340 if ((rqudpsock < 0) && (rqtcpsock < 0)) 341 syslog(LOG_WARNING, "Can't create RQUOTA IPv4 sockets"); 342 if ((rqudp6sock < 0) && (rqtcp6sock < 0) && 343 (rqudpsock < 0) && (rqtcpsock < 0)) { 344 syslog(LOG_ERR, "Can't create any RQUOTA sockets!"); 345 exit(1); 346 } 347 348 /* init file system quotas list */ 349 error = pthread_mutex_init(&fsq_mutex, NULL); 350 if (error) { 351 syslog(LOG_ERR, "file system quota mutex init failed: %s (%d)", strerror(error), error); 352 exit(1); 353 } 354 TAILQ_INIT(&fsqhead); 355 check_mounts(); 356 357 /* launch rquotad pthread */ 358 pthread_attr_init(&pattr); 359 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED); 360 error = pthread_create(&thd, &pattr, rquotad_thread, NULL); 361 if (error) { 362 syslog(LOG_ERR, "rquotad pthread_create: %s (%d)", strerror(error), error); 363 exit(1); 364 } 365 366 /* sit around waiting for mount/unmount events and/or a signal */ 367 if ((kq = kqueue()) < 0) { 368 syslog(LOG_ERR, "kqueue: %s (%d)", strerror(errno), errno); 369 exit(1); 370 } 371 EV_SET(&ke, 0, EVFILT_FS, EV_ADD, 0, 0, 0); 372 rv = kevent(kq, &ke, 1, NULL, 0, NULL); 373 if (rv < 0) { 374 syslog(LOG_ERR, "kevent(EVFILT_FS): %s (%d)", strerror(errno), errno); 375 exit(1); 376 } 377 378 while (!gotterm) { 379 rv = kevent(kq, NULL, 0, &ke, 1, NULL); 380 if ((rv > 0) && !(ke.flags & EV_ERROR) && (ke.fflags & (VQ_MOUNT|VQ_UNMOUNT))) { 381 /* syslog(LOG_DEBUG, "mount list changed: 0x%x", ke.fflags); */ 382 check_mounts(); 383 } 384 } 385 386 alarm(1); /* XXX 5028243 in case rpcb_unset() gets hung up during shutdown */ 387 rpcb_unset(NULL, RQUOTAPROG, RQUOTAVERS); 388 rpcb_unset(NULL, RQUOTAPROG, EXT_RQUOTAVERS); 389 pidfile_remove(pfh); 390 exit(0); 391} 392 393/* 394 * The incredibly complex rquotad thread function 395 */ 396void * 397rquotad_thread(__unused void *arg) 398{ 399 sigset_t sigset; 400 401 sigemptyset(&sigset); 402 sigaddset(&sigset, SIGINT); 403 sigaddset(&sigset, SIGQUIT); 404 sigaddset(&sigset, SIGSYS); 405 sigaddset(&sigset, SIGPIPE); 406 sigaddset(&sigset, SIGTERM); 407 sigaddset(&sigset, SIGHUP); 408 sigaddset(&sigset, SIGUSR1); 409 sigaddset(&sigset, SIGUSR2); 410 sigaddset(&sigset, SIGABRT); 411 pthread_sigmask(SIG_BLOCK, &sigset, NULL); 412 413 svc_run(); 414 syslog(LOG_ERR, "rquotad died"); 415 exit(1); 416} 417 418void 419rquota_service(struct svc_req *request, SVCXPRT *transp) 420{ 421 switch (request->rq_proc) { 422 case NULLPROC: 423 svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 424 break; 425 426 case RQUOTAPROC_GETQUOTA: 427 case RQUOTAPROC_GETACTIVEQUOTA: 428 sendquota(request, RQUOTAVERS, transp); 429 break; 430 431 default: 432 svcerr_noproc(transp); 433 break; 434 } 435} 436 437void 438ext_rquota_service(struct svc_req *request, SVCXPRT *transp) 439{ 440 switch (request->rq_proc) { 441 case NULLPROC: 442 svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 443 break; 444 445 case RQUOTAPROC_GETQUOTA: 446 case RQUOTAPROC_GETACTIVEQUOTA: 447 sendquota(request, EXT_RQUOTAVERS, transp); 448 break; 449 450 default: 451 svcerr_noproc(transp); 452 break; 453 } 454} 455 456int 457ismember(struct authunix_parms *aup, int gid) 458{ 459 uint g; 460 461 if (aup->aup_gid == (uint32_t)gid) 462 return (1); 463 for (g=0; g < aup->aup_len; g++) 464 if (aup->aup_gids[g] == (uint32_t)gid) 465 return (1); 466 return (0); 467} 468 469/* read quota for the specified id, and send it */ 470void 471sendquota(struct svc_req *request, int vers, SVCXPRT *transp) 472{ 473 struct getquota_args getq_args; 474 struct ext_getquota_args ext_getq_args; 475 struct getquota_rslt getq_rslt; 476 struct dqblk dqblk; 477 struct timeval timev; 478 struct authunix_parms *aup; 479 480 memset((char *)&getq_args, 0, sizeof(getq_args)); 481 memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args)); 482 switch (vers) { 483 case RQUOTAVERS: 484 if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, 485 (caddr_t)&getq_args)) { 486 svcerr_decode(transp); 487 return; 488 } 489 ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 490 ext_getq_args.gqa_id = getq_args.gqa_uid; 491 ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 492 break; 493 case EXT_RQUOTAVERS: 494 if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, 495 (caddr_t)&ext_getq_args)) { 496 svcerr_decode(transp); 497 return; 498 } 499 break; 500 } 501 aup = (struct authunix_parms *)request->rq_clntcred; 502 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 503 /* bad auth */ 504 getq_rslt.status = Q_EPERM; 505 } else if ((ext_getq_args.gqa_type == RQUOTA_USRQUOTA) && aup->aup_uid && 506 (aup->aup_uid != (uint32_t)ext_getq_args.gqa_id)) { 507 /* only allow user or root to get a user quota */ 508 getq_rslt.status = Q_EPERM; 509 } else if ((ext_getq_args.gqa_type == RQUOTA_GRPQUOTA) && aup->aup_uid && 510 !ismember(aup, ext_getq_args.gqa_id)) { 511 /* only allow root or group members to get a group quota */ 512 getq_rslt.status = Q_EPERM; 513 } else if (!getfsquota(ext_getq_args.gqa_type, ext_getq_args.gqa_id, 514 ext_getq_args.gqa_pathp, &dqblk)) { 515 /* failed, return noquota */ 516 getq_rslt.status = Q_NOQUOTA; 517 } else { 518 uint32_t bsize = DEV_BSIZE; 519#define CLAMP_MAX_32(V) (((V) > UINT32_MAX) ? UINT32_MAX : (V)) 520 521 /* scale the block size up to fit values into 32 bits */ 522 while ((bsize < INT32_MAX) && 523 (((dqblk.dqb_bhardlimit / bsize) > UINT32_MAX) || 524 ((dqblk.dqb_bsoftlimit / bsize) > UINT32_MAX) || 525 ((dqblk.dqb_curbytes / bsize) > UINT32_MAX))) 526 bsize <<= 1; 527 528 gettimeofday(&timev, NULL); 529 getq_rslt.status = Q_OK; 530 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 531 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = bsize; 532 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 533 CLAMP_MAX_32(dqblk.dqb_bhardlimit / bsize); 534 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 535 CLAMP_MAX_32(dqblk.dqb_bsoftlimit / bsize); 536 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 537 CLAMP_MAX_32(dqblk.dqb_curbytes / bsize); 538 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 539 CLAMP_MAX_32(dqblk.dqb_ihardlimit); 540 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 541 CLAMP_MAX_32(dqblk.dqb_isoftlimit); 542 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 543 CLAMP_MAX_32(dqblk.dqb_curinodes); 544 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 545 dqblk.dqb_btime - timev.tv_sec; 546 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 547 dqblk.dqb_itime - timev.tv_sec; 548 } 549 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 550 svcerr_systemerr(transp); 551 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 552 syslog(LOG_ERR, "unable to free arguments"); 553 exit(1); 554 } 555} 556 557/* 558 * gets the quotas for id, filesystem path. 559 * Return 0 if fail, 1 otherwise 560 */ 561int 562getfsquota(int type, int id, char *path, struct dqblk *dqblk) 563{ 564 struct stat st_path; 565 struct fsq_stat *fs; 566 int qcmd, fd, ret = 0; 567 char *filename; 568 569 qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA); 570 571 lock_fsq(); 572 573 /* first, ask for quota directly */ 574 if (quotactl(path, qcmd, id, (char*)dqblk) == 0) { 575 ret = 1; 576 goto out; 577 } 578 579 /* otherwise, search/check manually */ 580 if (stat(path, &st_path) < 0) { 581 ret = 0; 582 goto out; 583 } 584 585 TAILQ_FOREACH(fs, &fsqhead, chain) { 586 /* where the device is the same as path */ 587 if (st_path.st_dev < fs->st_dev) { 588 ret = 0; 589 goto out; 590 } 591 if (fs->st_dev != st_path.st_dev) 592 continue; 593 594 filename = (type == RQUOTA_USRQUOTA) ? 595 fs->uqfpathname : fs->gqfpathname; 596 if (filename == NULL) { 597 ret = 0; 598 goto out; 599 } 600 if ((fd = open(filename, O_RDONLY)) < 0) { 601 syslog(LOG_WARNING, "open error: %s: %m", filename); 602 ret = 0; 603 goto out; 604 } 605 if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET) 606 == (off_t)-1) { 607 close(fd); 608 ret = 0; 609 goto out; 610 } 611 switch (read(fd, dqblk, sizeof(struct dqblk))) { 612 case 0: 613 /* 614 * Convert implicit 0 quota (EOF) 615 * into an explicit one (zero'ed dqblk) 616 */ 617 memset((caddr_t) dqblk, 0, sizeof(struct dqblk)); 618 ret = 1; 619 break; 620 case sizeof(struct dqblk): /* OK */ 621 ret = 1; 622 break; 623 default: /* ERROR */ 624 syslog(LOG_WARNING, "read error: %s: %m", filename); 625 close(fd); 626 ret = 0; 627 goto out; 628 } 629 close(fd); 630 } 631out: 632 unlock_fsq(); 633 return (ret); 634} 635 636/* 637 * Check to see if a particular quota is to be enabled. 638 * Comes from quota.c, NetBSD 0.9 639 */ 640int 641hasquota(struct statfs *fst, char **uqfnamep, char **gqfnamep) 642{ 643 static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN]; 644 struct stat sb; 645 int qcnt = 0; 646 647 /* 648 From quota.c: 649 We only support the default path to the 650 on disk quota files. 651 */ 652 653 snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, QUOTAOPSNAME, qfextension[USRQUOTA] ); 654 if (stat(buf, &sb) == 0) { 655 snprintf(ubuf, sizeof(ubuf), "%s/%s.%s", fst->f_mntonname, QUOTAFILENAME, qfextension[USRQUOTA]); 656 *uqfnamep = ubuf; 657 qcnt++; 658 } 659 snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, QUOTAOPSNAME, qfextension[GRPQUOTA] ); 660 if (stat(buf, &sb) == 0) { 661 snprintf(gbuf, sizeof(gbuf), "%s/%s.%s", fst->f_mntonname, QUOTAFILENAME, qfextension[GRPQUOTA]); 662 *gqfnamep = gbuf; 663 qcnt++; 664 } 665 return (qcnt); 666} 667 668/* functions for locking/unlocking the file system quota list */ 669void 670lock_fsq(void) 671{ 672 int error = pthread_mutex_lock(&fsq_mutex); 673 if (error) 674 syslog(LOG_ERR, "mutex lock failed: %s (%d)", strerror(error), error); 675} 676void 677unlock_fsq(void) 678{ 679 int error = pthread_mutex_unlock(&fsq_mutex); 680 if (error) 681 syslog(LOG_ERR, "mutex unlock failed: %s (%d)", strerror(error), error); 682} 683 684/* functions for adding/deleting entries from the file system quota list */ 685static void 686fsadd(struct statfs *fst) 687{ 688 struct fsq_stat *fs = NULL, *fs2; 689 char *uqfpathname = NULL, *gqfpathname = NULL; 690 struct stat st; 691 692 if (strcmp(fst->f_fstypename, "hfs") && 693 strcmp(fst->f_fstypename, "ufs")) 694 return; 695 if (!hasquota(fst, &uqfpathname, &gqfpathname)) 696 return; 697 698 fs = (struct fsq_stat *) malloc(sizeof(*fs)); 699 if (fs == NULL) { 700 syslog(LOG_ERR, "can't malloc: %m"); 701 return; 702 } 703 bzero(fs, sizeof(*fs)); 704 705 fs->mountdir = strdup(fst->f_mntonname); 706 if (fs->mountdir == NULL) { 707 syslog(LOG_ERR, "can't strdup: %m"); 708 goto failed; 709 } 710 711 if (uqfpathname) { 712 fs->uqfpathname = strdup(uqfpathname); 713 if (fs->uqfpathname == NULL) { 714 syslog(LOG_ERR, "can't strdup: %m"); 715 goto failed; 716 } 717 } 718 if (gqfpathname) { 719 fs->gqfpathname = strdup(gqfpathname); 720 if (fs->gqfpathname == NULL) { 721 syslog(LOG_ERR, "can't strdup: %m"); 722 goto failed; 723 } 724 } 725 if (stat(fst->f_mntonname, &st)) 726 goto failed; 727 fs->st_dev = st.st_dev; 728 fs->fsid.val[0] = fst->f_fsid.val[0]; 729 fs->fsid.val[1] = fst->f_fsid.val[1]; 730 731 /* insert it into the list by st_dev order */ 732 TAILQ_FOREACH(fs2, &fsqhead, chain) { 733 if (fs->st_dev < fs2->st_dev) 734 break; 735 } 736 if (fs2) 737 TAILQ_INSERT_BEFORE(fs2, fs, chain); 738 else 739 TAILQ_INSERT_TAIL(&fsqhead, fs, chain); 740 741 return; 742failed: 743 if (fs->gqfpathname) 744 free(fs->gqfpathname); 745 if (fs->uqfpathname) 746 free(fs->uqfpathname); 747 if (fs->mountdir) 748 free(fs->mountdir); 749 free(fs); 750 return; 751} 752static void 753fsdel(struct statfs *fst) 754{ 755 struct fsq_stat *fs; 756 757 TAILQ_FOREACH(fs, &fsqhead, chain) { 758 if ((fs->fsid.val[0] != fst->f_fsid.val[0]) || 759 (fs->fsid.val[1] != fst->f_fsid.val[1])) 760 continue; 761 if (strcmp(fs->mountdir, fst->f_mntonname)) 762 continue; 763 break; 764 } 765 if (!fs) 766 return; 767 TAILQ_REMOVE(&fsqhead, fs, chain); 768 if (fs->gqfpathname) 769 free(fs->gqfpathname); 770 if (fs->uqfpathname) 771 free(fs->uqfpathname); 772 if (fs->mountdir) 773 free(fs->mountdir); 774 free(fs); 775} 776 777/* 778 * code to monitor list of mounts 779 */ 780static struct statfs *sfs[2]; 781static int size[2], cnt[2], cur, lastfscnt; 782#define PREV ((cur + 1) & 1) 783 784static int 785sfscmp(const void *arg1, const void *arg2) 786{ 787 const struct statfs *sfs1 = arg1; 788 const struct statfs *sfs2 = arg2; 789 return strcmp(sfs1->f_mntonname, sfs2->f_mntonname); 790} 791 792static void 793get_mounts(void) 794{ 795 cur = (cur + 1) % 2; 796 while (size[cur] < (lastfscnt = getfsstat(sfs[cur], size[cur] * sizeof(struct statfs), MNT_NOWAIT))) { 797 free(sfs[cur]); 798 size[cur] = lastfscnt + 32; 799 sfs[cur] = malloc(size[cur] * sizeof(struct statfs)); 800 if (!sfs[cur]) 801 err(1, "no memory"); 802 } 803 cnt[cur] = lastfscnt; 804 qsort(sfs[cur], cnt[cur], sizeof(struct statfs), sfscmp); 805} 806 807void 808check_mounts(void) 809{ 810 int i, j, cmp; 811 812 lock_fsq(); 813 get_mounts(); 814 815 for (i=j=0; (i < cnt[PREV]) && (j < cnt[cur]); ) { 816 cmp = sfscmp(&sfs[PREV][i], &sfs[cur][j]); 817 if (!cmp) { 818 i++; 819 j++; 820 continue; 821 } 822 if (cmp < 0) { 823 /* file system no longer mounted */ 824 DEBUG("- %s\n", sfs[PREV][i].f_mntonname); 825 fsdel(&sfs[PREV][i]); 826 i++; 827 } 828 if (cmp > 0) { 829 /* file system mounted */ 830 DEBUG("+ %s\n", sfs[cur][j].f_mntonname); 831 fsadd(&sfs[cur][j]); 832 j++; 833 } 834 } 835 while (i < cnt[PREV]) { 836 /* file system no longer mounted */ 837 DEBUG("- %s\n", sfs[PREV][i].f_mntonname); 838 fsdel(&sfs[PREV][i]); 839 i++; 840 } 841 while (j < cnt[cur]) { 842 /* file system mounted */ 843 DEBUG("+ %s\n", sfs[cur][j].f_mntonname); 844 fsadd(&sfs[cur][j]); 845 j++; 846 } 847 848 unlock_fsq(); 849} 850 851/* 852 * read the NFS server values from nfs.conf 853 */ 854int 855config_read(struct nfs_conf_server *conf) 856{ 857 FILE *f; 858 size_t len, linenum = 0; 859 char *line, *p, *key, *value; 860 long val; 861 862 if (!(f = fopen(_PATH_NFS_CONF, "r"))) { 863 if (errno != ENOENT) 864 syslog(LOG_WARNING, "%s", _PATH_NFS_CONF); 865 return (1); 866 } 867 868 for (;(line = fparseln(f, &len, &linenum, NULL, 0)); free(line)) { 869 if (len <= 0) 870 continue; 871 /* trim trailing whitespace */ 872 p = line + len - 1; 873 while ((p > line) && isspace(*p)) 874 *p-- = '\0'; 875 /* find key start */ 876 key = line; 877 while (isspace(*key)) 878 key++; 879 /* find equals/value */ 880 value = p = strchr(line, '='); 881 if (p) /* trim trailing whitespace on key */ 882 do { *p-- = '\0'; } while ((p > line) && isspace(*p)); 883 /* find value start */ 884 if (value) 885 do { value++; } while (isspace(*value)); 886 887 /* all server keys start with "nfs.server." */ 888 if (strncmp(key, "nfs.server.", 11)) { 889 DEBUG("%4ld %s=%s\n", linenum, key, value ? value : ""); 890 continue; 891 } 892 893 val = !value ? 1 : strtol(value, NULL, 0); 894 DEBUG("%4ld %s=%s (%d)\n", linenum, key, value ? value : "", val); 895 896 if (!strcmp(key, "nfs.server.rquota.port")) { 897 if (value && val) 898 conf->rquota_port = val; 899 } else if (!strcmp(key, "nfs.server.rquota.tcp")) { 900 conf->tcp = val; 901 } else if (!strcmp(key, "nfs.server.rquota.udp")) { 902 conf->udp = val; 903 } else if (!strcmp(key, "nfs.server.verbose")) { 904 conf->verbose = val; 905 } else { 906 DEBUG("ignoring unknown config value: %4ld %s=%s\n", linenum, key, value ? value : ""); 907 } 908 909 } 910 911 fclose(f); 912 return (0); 913} 914 915