rquotad.c revision 197508
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: head/libexec/rpc.rquotad/rquotad.c 197508 2009-09-25 23:03:24Z des $"); 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> 2631420Scharnier#include <pwd.h> 27184638Sdes#include <signal.h> 2813237Sgraichen#include <stdio.h> 2913237Sgraichen#include <stdlib.h> 3031420Scharnier#include <string.h> 31184638Sdes#include <syslog.h> 3213237Sgraichen#include <unistd.h> 3313237Sgraichen 3490336Simpvoid rquota_service(struct svc_req *request, SVCXPRT *transp); 3590336Simpvoid sendquota(struct svc_req *request, SVCXPRT *transp); 3690336Simpvoid printerr_reply(SVCXPRT *transp); 3790336Simpvoid initfs(void); 3890336Simpint getfsquota(long id, char *path, struct dqblk *dqblk); 3990336Simpint hasquota(struct fstab *fs, char **qfnamep); 4013237Sgraichen 4113237Sgraichen/* 4213237Sgraichen * structure containing informations about ufs filesystems 4313237Sgraichen * initialised by initfs() 4413237Sgraichen */ 4513237Sgraichenstruct fs_stat { 4613237Sgraichen struct fs_stat *fs_next; /* next element */ 4713237Sgraichen char *fs_file; /* mount point of the filesystem */ 4813237Sgraichen char *qfpathname; /* pathname of the quota file */ 4913237Sgraichen dev_t st_dev; /* device of the filesystem */ 5013237Sgraichen} fs_stat; 5113237Sgraichenstruct fs_stat *fs_begin = NULL; 5213237Sgraichen 5313237Sgraichenint from_inetd = 1; 5413237Sgraichen 55184638Sdesstatic void 5690336Simpcleanup(int sig) 5713237Sgraichen{ 58184638Sdes 59197506Sdes (void)sig; 60197506Sdes (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 6113237Sgraichen exit(0); 6213237Sgraichen} 6313237Sgraichen 6413237Sgraichenint 65184638Sdesmain(void) 6613237Sgraichen{ 6713237Sgraichen SVCXPRT *transp; 68100120Salfred int ok; 69100120Salfred struct sockaddr_storage from; 70141918Sstefanf socklen_t fromlen; 7113237Sgraichen 7213237Sgraichen fromlen = sizeof(from); 73197506Sdes if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 7413237Sgraichen from_inetd = 0; 7513237Sgraichen 7613237Sgraichen if (!from_inetd) { 7713237Sgraichen daemon(0, 0); 7813237Sgraichen 79197506Sdes (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 8013237Sgraichen 81197506Sdes (void)signal(SIGINT, cleanup); 82197506Sdes (void)signal(SIGTERM, cleanup); 83197506Sdes (void)signal(SIGHUP, cleanup); 8413237Sgraichen } 8513237Sgraichen 8613237Sgraichen openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 8713237Sgraichen 8813237Sgraichen /* create and register the service */ 89100120Salfred if (from_inetd) { 90100120Salfred transp = svc_tli_create(0, NULL, NULL, 0, 0); 91100120Salfred if (transp == NULL) { 92100120Salfred syslog(LOG_ERR, "couldn't create udp service."); 93100120Salfred exit(1); 94100120Salfred } 95100120Salfred ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS, 96197506Sdes rquota_service, NULL); 97197506Sdes } else { 98100120Salfred ok = svc_create(rquota_service, 99197506Sdes RQUOTAPROG, RQUOTAVERS, "udp"); 100197506Sdes } 101100120Salfred if (!ok) { 102184638Sdes syslog(LOG_ERR, 103184638Sdes "unable to register (RQUOTAPROG, RQUOTAVERS, %s)", 104184638Sdes from_inetd ? "(inetd)" : "udp"); 10513237Sgraichen exit(1); 10613237Sgraichen } 10713237Sgraichen 108184638Sdes initfs(); 10913237Sgraichen svc_run(); 11013237Sgraichen syslog(LOG_ERR, "svc_run returned"); 11113237Sgraichen exit(1); 11213237Sgraichen} 11313237Sgraichen 114184638Sdesvoid 11590336Simprquota_service(struct svc_req *request, SVCXPRT *transp) 11613237Sgraichen{ 117184638Sdes 11813237Sgraichen switch (request->rq_proc) { 11913237Sgraichen case NULLPROC: 12095658Sdes (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 12113237Sgraichen break; 12213237Sgraichen 12313237Sgraichen case RQUOTAPROC_GETQUOTA: 12413237Sgraichen case RQUOTAPROC_GETACTIVEQUOTA: 12513237Sgraichen sendquota(request, transp); 12613237Sgraichen break; 12713237Sgraichen 12813237Sgraichen default: 12913237Sgraichen svcerr_noproc(transp); 13013237Sgraichen break; 13113237Sgraichen } 13213237Sgraichen if (from_inetd) 13313237Sgraichen exit(0); 13413237Sgraichen} 13513237Sgraichen 13613237Sgraichen/* read quota for the specified id, and send it */ 137184638Sdesvoid 13890336Simpsendquota(struct svc_req *request, SVCXPRT *transp) 13913237Sgraichen{ 14013237Sgraichen struct getquota_args getq_args; 14113237Sgraichen struct getquota_rslt getq_rslt; 14213237Sgraichen struct dqblk dqblk; 14313237Sgraichen struct timeval timev; 14413237Sgraichen 145197506Sdes bzero(&getq_args, sizeof(getq_args)); 14695658Sdes if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 14713237Sgraichen svcerr_decode(transp); 14813237Sgraichen return; 14913237Sgraichen } 15013237Sgraichen if (request->rq_cred.oa_flavor != AUTH_UNIX) { 15113237Sgraichen /* bad auth */ 15213237Sgraichen getq_rslt.status = Q_EPERM; 15313237Sgraichen } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { 15413237Sgraichen /* failed, return noquota */ 15513237Sgraichen getq_rslt.status = Q_NOQUOTA; 15613237Sgraichen } else { 15713237Sgraichen gettimeofday(&timev, NULL); 15813237Sgraichen getq_rslt.status = Q_OK; 15913237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 16013237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; 16113237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 16213237Sgraichen dqblk.dqb_bhardlimit; 16313237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 16413237Sgraichen dqblk.dqb_bsoftlimit; 16513237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 16613237Sgraichen dqblk.dqb_curblocks; 16713237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 16813237Sgraichen dqblk.dqb_ihardlimit; 16913237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 17013237Sgraichen dqblk.dqb_isoftlimit; 17113237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 17213237Sgraichen dqblk.dqb_curinodes; 17313237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 17413237Sgraichen dqblk.dqb_btime - timev.tv_sec; 17513237Sgraichen getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 17613237Sgraichen dqblk.dqb_itime - timev.tv_sec; 17713237Sgraichen } 178197508Sdes if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 17913237Sgraichen svcerr_systemerr(transp); 18095658Sdes if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 18113237Sgraichen syslog(LOG_ERR, "unable to free arguments"); 18213237Sgraichen exit(1); 18313237Sgraichen } 18413237Sgraichen} 18513237Sgraichen 186184638Sdesvoid 18790336Simpprinterr_reply(SVCXPRT *transp) /* when a reply to a request failed */ 18813237Sgraichen{ 189100120Salfred char name[INET6_ADDRSTRLEN]; 190100120Salfred struct sockaddr *caller; 191197508Sdes int save_errno; 19213237Sgraichen 19313237Sgraichen save_errno = errno; 194100120Salfred caller = (struct sockaddr *)svc_getrpccaller(transp)->buf; 195100120Salfred getnameinfo(caller, caller->sa_len, name, sizeof (name), 196197506Sdes NULL, 0, NI_NUMERICHOST); 19713237Sgraichen errno = save_errno; 19813237Sgraichen if (errno == 0) 19913237Sgraichen syslog(LOG_ERR, "couldn't send reply to %s", name); 20013237Sgraichen else 20113237Sgraichen syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 20213237Sgraichen} 20313237Sgraichen 20413237Sgraichen/* initialise the fs_tab list from entries in /etc/fstab */ 205184638Sdesvoid 20690336Simpinitfs(void) 20713237Sgraichen{ 20813237Sgraichen struct fs_stat *fs_current = NULL; 20913237Sgraichen struct fs_stat *fs_next = NULL; 21013237Sgraichen char *qfpathname; 21113237Sgraichen struct fstab *fs; 21213237Sgraichen struct stat st; 21313237Sgraichen 21413237Sgraichen setfsent(); 21513237Sgraichen while ((fs = getfsent())) { 21613237Sgraichen if (strcmp(fs->fs_vfstype, "ufs")) 21713237Sgraichen continue; 21813237Sgraichen if (!hasquota(fs, &qfpathname)) 21913237Sgraichen continue; 22013237Sgraichen 221197506Sdes fs_current = malloc(sizeof(struct fs_stat)); 22213237Sgraichen fs_current->fs_next = fs_next; /* next element */ 22313237Sgraichen 224197506Sdes fs_current->fs_file = malloc(strlen(fs->fs_file) + 1); 22513237Sgraichen strcpy(fs_current->fs_file, fs->fs_file); 22613237Sgraichen 227197506Sdes fs_current->qfpathname = malloc(strlen(qfpathname) + 1); 22813237Sgraichen strcpy(fs_current->qfpathname, qfpathname); 22913237Sgraichen 23078457Smikeh stat(fs_current->fs_file, &st); 23113237Sgraichen fs_current->st_dev = st.st_dev; 23213237Sgraichen 23313237Sgraichen fs_next = fs_current; 23413237Sgraichen } 23513237Sgraichen endfsent(); 23613237Sgraichen fs_begin = fs_current; 23713237Sgraichen} 23813237Sgraichen 23913237Sgraichen/* 24013237Sgraichen * gets the quotas for id, filesystem path. 24113237Sgraichen * Return 0 if fail, 1 otherwise 24213237Sgraichen */ 24313237Sgraichenint 244184638Sdesgetfsquota(long id, char *path, struct dqblk *dqblk) 24513237Sgraichen{ 24613237Sgraichen struct stat st_path; 24713237Sgraichen struct fs_stat *fs; 24813237Sgraichen int qcmd, fd, ret = 0; 24913237Sgraichen 25013237Sgraichen if (stat(path, &st_path) < 0) 25113237Sgraichen return (0); 25213237Sgraichen 25313237Sgraichen qcmd = QCMD(Q_GETQUOTA, USRQUOTA); 25413237Sgraichen 25513237Sgraichen for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { 256197508Sdes /* where the device is the same as path */ 25713237Sgraichen if (fs->st_dev != st_path.st_dev) 25813237Sgraichen continue; 25913237Sgraichen 26013237Sgraichen /* find the specified filesystem. get and return quota */ 26113237Sgraichen if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) 26213237Sgraichen return (1); 26313237Sgraichen 26413237Sgraichen if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) { 26513237Sgraichen syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname); 26613237Sgraichen return (0); 26713237Sgraichen } 26813237Sgraichen if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) { 26913237Sgraichen close(fd); 27013237Sgraichen return (1); 27113237Sgraichen } 27213237Sgraichen switch (read(fd, dqblk, sizeof(struct dqblk))) { 27313237Sgraichen case 0: 27413237Sgraichen /* 27513237Sgraichen * Convert implicit 0 quota (EOF) 27613237Sgraichen * into an explicit one (zero'ed dqblk) 27713237Sgraichen */ 27895658Sdes bzero(dqblk, sizeof(struct dqblk)); 27913237Sgraichen ret = 1; 28013237Sgraichen break; 28113237Sgraichen case sizeof(struct dqblk): /* OK */ 28213237Sgraichen ret = 1; 28313237Sgraichen break; 28413237Sgraichen default: /* ERROR */ 28513237Sgraichen syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname); 28613237Sgraichen close(fd); 28713237Sgraichen return (0); 28813237Sgraichen } 28913237Sgraichen close(fd); 29013237Sgraichen } 29113237Sgraichen return (ret); 29213237Sgraichen} 29313237Sgraichen 29413237Sgraichen/* 29513237Sgraichen * Check to see if a particular quota is to be enabled. 29613237Sgraichen * Comes from quota.c, NetBSD 0.9 29713237Sgraichen */ 29813237Sgraichenint 29990336Simphasquota(struct fstab *fs, char **qfnamep) 30013237Sgraichen{ 30113237Sgraichen static char initname, usrname[100]; 30213237Sgraichen static char buf[BUFSIZ]; 303184638Sdes char *opt, *cp; 304184638Sdes const char *qfextension[] = INITQFNAMES; 30513237Sgraichen 30613237Sgraichen if (!initname) { 30713237Sgraichen sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME); 30813237Sgraichen initname = 1; 30913237Sgraichen } 31013237Sgraichen strcpy(buf, fs->fs_mntops); 31113237Sgraichen for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 31213237Sgraichen if ((cp = index(opt, '='))) 31313237Sgraichen *cp++ = '\0'; 31413237Sgraichen if (strcmp(opt, usrname) == 0) 31513237Sgraichen break; 31613237Sgraichen } 31713237Sgraichen if (!opt) 31813237Sgraichen return (0); 31913237Sgraichen if (cp) { 32013237Sgraichen *qfnamep = cp; 32113237Sgraichen return (1); 32213237Sgraichen } 323184638Sdes sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, 324184638Sdes qfextension[USRQUOTA]); 32513237Sgraichen *qfnamep = buf; 32613237Sgraichen return (1); 32713237Sgraichen} 328