113237Sgraichen/* 213237Sgraichen * by Manuel Bouyer (bouyer@ensta.fr) 3184638Sdes * 413237Sgraichen * There is no copyright, you can use it as you want. 513237Sgraichen */ 613237Sgraichen 7184638Sdes#include <sys/cdefs.h> 8184638Sdes__FBSDID("$FreeBSD: stable/11/libexec/rpc.rquotad/rquotad.c 339008 2018-09-29 00:44:23Z sef $"); 931420Scharnier 1013237Sgraichen#include <sys/param.h> 1113237Sgraichen#include <sys/mount.h> 1213237Sgraichen#include <sys/file.h> 1313237Sgraichen#include <sys/stat.h> 1413237Sgraichen#include <sys/socket.h> 1513237Sgraichen 16184638Sdes#include <ufs/ufs/quota.h> 17184638Sdes#include <rpc/rpc.h> 18184638Sdes#include <rpcsvc/rquota.h> 19184638Sdes#include <arpa/inet.h> 20184638Sdes#include <netdb.h> 21184638Sdes 2231420Scharnier#include <ctype.h> 2331420Scharnier#include <errno.h> 2431420Scharnier#include <fstab.h> 2531420Scharnier#include <grp.h> 26207736Smckusick#include <libutil.h> 2731420Scharnier#include <pwd.h> 28184638Sdes#include <signal.h> 2913237Sgraichen#include <stdio.h> 3013237Sgraichen#include <stdlib.h> 31339008Ssef#include <err.h> 3231420Scharnier#include <string.h> 33184638Sdes#include <syslog.h> 3413237Sgraichen#include <unistd.h> 3513237Sgraichen 36339008Ssefstatic void rquota_service_1(struct svc_req *request, SVCXPRT *transp); 37339008Ssefstatic void rquota_service_2(struct svc_req *request, SVCXPRT *transp); 38197531Sdesstatic void sendquota(struct svc_req *request, SVCXPRT *transp); 39339008Ssefstatic void sendquota_extended(struct svc_req *request, SVCXPRT *transp); 40339008Ssefstatic int getfsquota(int type, long id, char *path, struct dqblk *dqblk); 4113237Sgraichen 42197531Sdesstatic int from_inetd = 1; 43339008Ssefstatic int debug = 0; 4413237Sgraichen 45184638Sdesstatic void 4690336Simpcleanup(int sig) 4713237Sgraichen{ 48184638Sdes 49197506Sdes (void)sig; 50197506Sdes (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 5113237Sgraichen exit(0); 5213237Sgraichen} 5313237Sgraichen 5413237Sgraichenint 55339008Ssefmain(int argc, char **argv) 5613237Sgraichen{ 5713237Sgraichen SVCXPRT *transp; 58100120Salfred int ok; 59100120Salfred struct sockaddr_storage from; 60141918Sstefanf socklen_t fromlen; 61339008Ssef int vers; 62339008Ssef int ch; 6313237Sgraichen 64339008Ssef while ((ch = getopt(argc, argv, "d")) != -1) { 65339008Ssef switch (ch) { 66339008Ssef case 'd': 67339008Ssef debug++; 68339008Ssef break; 69339008Ssef default: 70339008Ssef break; 71339008Ssef } 72339008Ssef } 73339008Ssef 7413237Sgraichen fromlen = sizeof(from); 75197506Sdes if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 7613237Sgraichen from_inetd = 0; 7713237Sgraichen 7813237Sgraichen if (!from_inetd) { 79339008Ssef if (!debug) 80339008Ssef daemon(0, 0); 81197506Sdes (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 82197506Sdes (void)signal(SIGINT, cleanup); 83197506Sdes (void)signal(SIGTERM, cleanup); 84197506Sdes (void)signal(SIGHUP, cleanup); 8513237Sgraichen } 8613237Sgraichen 8713237Sgraichen openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 8813237Sgraichen 8913237Sgraichen /* create and register the service */ 90100120Salfred if (from_inetd) { 91100120Salfred transp = svc_tli_create(0, NULL, NULL, 0, 0); 92100120Salfred if (transp == NULL) { 93100120Salfred syslog(LOG_ERR, "couldn't create udp service."); 94100120Salfred exit(1); 95100120Salfred } 96339008Ssef vers = RQUOTAVERS; 97100120Salfred ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS, 98339008Ssef rquota_service_1, NULL); 99339008Ssef if (ok) { 100339008Ssef vers = EXT_RQUOTAVERS; 101339008Ssef ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 102339008Ssef rquota_service_2, NULL); 103339008Ssef } 104197506Sdes } else { 105339008Ssef vers = RQUOTAVERS; 106339008Ssef ok = svc_create(rquota_service_1, 107197506Sdes RQUOTAPROG, RQUOTAVERS, "udp"); 108339008Ssef if (ok) { 109339008Ssef vers = EXT_RQUOTAVERS; 110339008Ssef ok = svc_create(rquota_service_2, 111339008Ssef RQUOTAPROG, EXT_RQUOTAVERS, "udp"); 112339008Ssef 113339008Ssef } 114197506Sdes } 115100120Salfred if (!ok) { 116184638Sdes syslog(LOG_ERR, 117339008Ssef "unable to register (RQUOTAPROG, %s, %s)", 118339008Ssef vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS", 119339008Ssef from_inetd ? "(inetd)" : "udp"); 12013237Sgraichen exit(1); 12113237Sgraichen } 12213237Sgraichen 12313237Sgraichen svc_run(); 12413237Sgraichen syslog(LOG_ERR, "svc_run returned"); 12513237Sgraichen exit(1); 12613237Sgraichen} 12713237Sgraichen 128197531Sdesstatic void 129339008Ssefrquota_service_2(struct svc_req *request, SVCXPRT *transp) 13013237Sgraichen{ 131184638Sdes 13213237Sgraichen switch (request->rq_proc) { 13313237Sgraichen case NULLPROC: 13495658Sdes (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 13513237Sgraichen break; 13613237Sgraichen case RQUOTAPROC_GETQUOTA: 13713237Sgraichen case RQUOTAPROC_GETACTIVEQUOTA: 138339008Ssef sendquota_extended(request, transp); 139339008Ssef break; 140339008Ssef default: 141339008Ssef svcerr_noproc(transp); 142339008Ssef break; 143339008Ssef } 144339008Ssef if (from_inetd) 145339008Ssef exit(0); 146339008Ssef} 147339008Ssef 148339008Ssefstatic void 149339008Ssefrquota_service_1(struct svc_req *request, SVCXPRT *transp) 150339008Ssef{ 151339008Ssef 152339008Ssef switch (request->rq_proc) { 153339008Ssef case NULLPROC: 154339008Ssef (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 155339008Ssef break; 156339008Ssef case RQUOTAPROC_GETQUOTA: 157339008Ssef case RQUOTAPROC_GETACTIVEQUOTA: 15813237Sgraichen sendquota(request, transp); 15913237Sgraichen break; 16013237Sgraichen default: 16113237Sgraichen svcerr_noproc(transp); 16213237Sgraichen break; 16313237Sgraichen } 16413237Sgraichen if (from_inetd) 16513237Sgraichen exit(0); 16613237Sgraichen} 16713237Sgraichen 16813237Sgraichen/* read quota for the specified id, and send it */ 169197531Sdesstatic void 17090336Simpsendquota(struct svc_req *request, SVCXPRT *transp) 17113237Sgraichen{ 17213237Sgraichen struct getquota_args getq_args; 17313237Sgraichen struct getquota_rslt getq_rslt; 17413237Sgraichen struct dqblk dqblk; 17513237Sgraichen struct timeval timev; 176207736Smckusick int scale; 17713237Sgraichen 178197506Sdes bzero(&getq_args, sizeof(getq_args)); 17995658Sdes if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 18013237Sgraichen svcerr_decode(transp); 18113237Sgraichen return; 18213237Sgraichen } 18313237Sgraichen if (request->rq_cred.oa_flavor != AUTH_UNIX) { 18413237Sgraichen /* bad auth */ 18513237Sgraichen getq_rslt.status = Q_EPERM; 186339008Ssef } else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { 18713237Sgraichen /* failed, return noquota */ 18813237Sgraichen getq_rslt.status = Q_NOQUOTA; 18913237Sgraichen } else { 19013237Sgraichen gettimeofday(&timev, NULL); 19113237Sgraichen getq_rslt.status = Q_OK; 19213237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 193207736Smckusick scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); 194207736Smckusick getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = 195207736Smckusick DEV_BSIZE * scale; 19613237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 197207736Smckusick dqblk.dqb_bhardlimit / scale; 19813237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 199207736Smckusick dqblk.dqb_bsoftlimit / scale; 20013237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 201207736Smckusick dqblk.dqb_curblocks / scale; 20213237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 20313237Sgraichen dqblk.dqb_ihardlimit; 20413237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 20513237Sgraichen dqblk.dqb_isoftlimit; 20613237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 20713237Sgraichen dqblk.dqb_curinodes; 20813237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 20913237Sgraichen dqblk.dqb_btime - timev.tv_sec; 21013237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 21113237Sgraichen dqblk.dqb_itime - timev.tv_sec; 21213237Sgraichen } 213197508Sdes if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 21413237Sgraichen svcerr_systemerr(transp); 21595658Sdes if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 21613237Sgraichen syslog(LOG_ERR, "unable to free arguments"); 21713237Sgraichen exit(1); 21813237Sgraichen } 21913237Sgraichen} 22013237Sgraichen 221197531Sdesstatic void 222339008Ssefsendquota_extended(struct svc_req *request, SVCXPRT *transp) 22313237Sgraichen{ 224339008Ssef struct ext_getquota_args getq_args; 225339008Ssef struct getquota_rslt getq_rslt; 226339008Ssef struct dqblk dqblk; 227339008Ssef struct timeval timev; 228339008Ssef int scale; 22913237Sgraichen 230339008Ssef bzero(&getq_args, sizeof(getq_args)); 231339008Ssef if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) { 232339008Ssef svcerr_decode(transp); 233339008Ssef return; 23413237Sgraichen } 235339008Ssef if (request->rq_cred.oa_flavor != AUTH_UNIX) { 236339008Ssef /* bad auth */ 237339008Ssef getq_rslt.status = Q_EPERM; 238339008Ssef } else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) { 239339008Ssef /* failed, return noquota */ 240339008Ssef getq_rslt.status = Q_NOQUOTA; 241339008Ssef } else { 242339008Ssef gettimeofday(&timev, NULL); 243339008Ssef getq_rslt.status = Q_OK; 244339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 245339008Ssef scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); 246339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = 247339008Ssef DEV_BSIZE * scale; 248339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 249339008Ssef dqblk.dqb_bhardlimit / scale; 250339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 251339008Ssef dqblk.dqb_bsoftlimit / scale; 252339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 253339008Ssef dqblk.dqb_curblocks / scale; 254339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 255339008Ssef dqblk.dqb_ihardlimit; 256339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 257339008Ssef dqblk.dqb_isoftlimit; 258339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 259339008Ssef dqblk.dqb_curinodes; 260339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 261339008Ssef dqblk.dqb_btime - timev.tv_sec; 262339008Ssef getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 263339008Ssef dqblk.dqb_itime - timev.tv_sec; 264339008Ssef } 265339008Ssef if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 266339008Ssef svcerr_systemerr(transp); 267339008Ssef if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 268339008Ssef syslog(LOG_ERR, "unable to free arguments"); 269339008Ssef exit(1); 270339008Ssef } 27113237Sgraichen} 27213237Sgraichen 27313237Sgraichen/* 27413237Sgraichen * gets the quotas for id, filesystem path. 27513237Sgraichen * Return 0 if fail, 1 otherwise 27613237Sgraichen */ 277197531Sdesstatic int 278339008Ssefgetfsquota(int type, long id, char *path, struct dqblk *dqblk) 27913237Sgraichen{ 280339008Ssef struct quotafile *qf; 281339008Ssef /* 282339008Ssef * Remote quota checking is limited to mounted filesystems. 283339008Ssef * Since UFS and ZFS support the quota system calls, we 284339008Ssef * only need to make an fstab object that has the path, and 285339008Ssef * a blank name for the filesystem type. 286339008Ssef * This allows the quota_open() call to work the way we 287339008Ssef * expect it to. 288339008Ssef * 289339008Ssef * The static char declaration is because compiler warnings 290339008Ssef * don't allow passing a const char * to a char *. 291339008Ssef */ 292339008Ssef int rv; 293339008Ssef static char blank[] = ""; 294339008Ssef struct fstab fst; 29513237Sgraichen 296339008Ssef fst.fs_file = path; 297339008Ssef fst.fs_mntops = blank; 298339008Ssef fst.fs_vfstype = blank; 299339008Ssef 300339008Ssef if (type != USRQUOTA && type != GRPQUOTA) 301339008Ssef return (0); 302339008Ssef 303339008Ssef qf = quota_open(&fst, type, O_RDONLY); 304339008Ssef if (debug) 305339008Ssef warnx("quota_open(<%s, %s>, %d) returned %p", 306339008Ssef fst.fs_file, fst.fs_mntops, type, 307339008Ssef qf); 308339008Ssef if (qf == NULL) 309339008Ssef return (0); 310339008Ssef 311339008Ssef rv = quota_read(qf, dqblk, id) == 0; 312339008Ssef quota_close(qf); 313339008Ssef if (debug) 314339008Ssef warnx("getfsquota(%d, %ld, %s, %p) -> %d", 315339008Ssef type, id, path, dqblk, rv); 316339008Ssef return (rv); 31713237Sgraichen} 318