rquotad.c revision 13238
1193323Sed/* 2193323Sed * by Manuel Bouyer (bouyer@ensta.fr) 3193323Sed * 4193323Sed * There is no copyright, you can use it as you want. 5193323Sed */ 6193323Sed 7193323Sed#include <sys/param.h> 8193323Sed#include <sys/types.h> 9193323Sed#include <sys/mount.h> 10193323Sed#include <sys/file.h> 11193323Sed#include <sys/stat.h> 12193323Sed#include <sys/socket.h> 13193323Sed#include <signal.h> 14193323Sed 15193323Sed#include <stdio.h> 16193323Sed#include <fstab.h> 17198090Srdivacky#include <ctype.h> 18193323Sed#include <stdlib.h> 19193323Sed#include <unistd.h> 20199481Srdivacky#include <string.h> 21193323Sed#include <pwd.h> 22193323Sed#include <grp.h> 23193323Sed#include <errno.h> 24199481Srdivacky 25193323Sed#include <syslog.h> 26193323Sed#include <varargs.h> 27193323Sed 28193323Sed#include <ufs/ufs/quota.h> 29199481Srdivacky#include <rpc/rpc.h> 30193323Sed#include <rpc/pmap_clnt.h> 31199481Srdivacky#include <rpcsvc/rquota.h> 32193323Sed#include <arpa/inet.h> 33193323Sed 34193323Sedvoid rquota_service __P((struct svc_req *request, SVCXPRT *transp)); 35193323Sedvoid sendquota __P((struct svc_req *request, SVCXPRT *transp)); 36193323Sedvoid printerr_reply __P((SVCXPRT *transp)); 37199481Srdivackyvoid initfs __P((void)); 38201360Srdivackyint getfsquota __P((long id, char *path, struct dqblk *dqblk)); 39193323Sedint hasquota __P((struct fstab *fs, char **qfnamep)); 40198090Srdivacky 41198090Srdivacky/* 42198090Srdivacky * structure containing informations about ufs filesystems 43193323Sed * initialised by initfs() 44198090Srdivacky */ 45193323Sedstruct fs_stat { 46198090Srdivacky struct fs_stat *fs_next; /* next element */ 47198090Srdivacky char *fs_file; /* mount point of the filesystem */ 48199481Srdivacky char *qfpathname; /* pathname of the quota file */ 49199511Srdivacky dev_t st_dev; /* device of the filesystem */ 50199511Srdivacky} fs_stat; 51198090Srdivackystruct fs_stat *fs_begin = NULL; 52193323Sed 53193323Sedint from_inetd = 1; 54198090Srdivacky 55193323Sedvoid 56193323Sedcleanup() 57198090Srdivacky{ 58198090Srdivacky (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); 59199481Srdivacky exit(0); 60199481Srdivacky} 61199481Srdivacky 62199511Srdivackyint 63199481Srdivackymain(argc, argv) 64199511Srdivacky int argc; 65199481Srdivacky char *argv[]; 66199481Srdivacky{ 67193323Sed SVCXPRT *transp; 68193323Sed int sock = 0; 69193323Sed int proto = 0; 70193323Sed struct sockaddr_in from; 71193323Sed int fromlen; 72193323Sed 73193323Sed fromlen = sizeof(from); 74201360Srdivacky if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { 75193323Sed from_inetd = 0; 76193323Sed sock = RPC_ANYSOCK; 77193323Sed proto = IPPROTO_UDP; 78193323Sed } 79193323Sed 80193323Sed if (!from_inetd) { 81193323Sed daemon(0, 0); 82201360Srdivacky 83201360Srdivacky (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); 84201360Srdivacky 85201360Srdivacky (void) signal(SIGINT, cleanup); 86201360Srdivacky (void) signal(SIGTERM, cleanup); 87201360Srdivacky (void) signal(SIGHUP, cleanup); 88193323Sed } 89193323Sed 90193323Sed openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 91193323Sed 92193323Sed /* create and register the service */ 93193323Sed transp = svcudp_create(sock); 94193323Sed if (transp == NULL) { 95193323Sed syslog(LOG_ERR, "couldn't create udp service."); 96198090Srdivacky exit(1); 97198090Srdivacky } 98193323Sed if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) { 99193323Sed syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s).", proto?"udp":"(inetd)"); 100193323Sed exit(1); 101193323Sed } 102193323Sed 103193323Sed initfs(); /* init the fs_stat list */ 104193323Sed svc_run(); 105193323Sed syslog(LOG_ERR, "svc_run returned"); 106193323Sed exit(1); 107193323Sed} 108193323Sed 109193323Sedvoid 110193323Sedrquota_service(request, transp) 111193323Sed struct svc_req *request; 112193323Sed SVCXPRT *transp; 113193323Sed{ 114193323Sed switch (request->rq_proc) { 115193323Sed case NULLPROC: 116193323Sed (void)svc_sendreply(transp, xdr_void, (char *)NULL); 117193323Sed break; 118193323Sed 119193323Sed case RQUOTAPROC_GETQUOTA: 120193323Sed case RQUOTAPROC_GETACTIVEQUOTA: 121193323Sed sendquota(request, transp); 122193323Sed break; 123193323Sed 124199481Srdivacky default: 125199481Srdivacky svcerr_noproc(transp); 126199481Srdivacky break; 127199481Srdivacky } 128198090Srdivacky if (from_inetd) 129198090Srdivacky exit(0); 130199481Srdivacky} 131198090Srdivacky 132193323Sed/* read quota for the specified id, and send it */ 133193323Sedvoid 134193323Sedsendquota(request, transp) 135193323Sed struct svc_req *request; 136199481Srdivacky SVCXPRT *transp; 137199481Srdivacky{ 138199481Srdivacky struct getquota_args getq_args; 139199481Srdivacky struct getquota_rslt getq_rslt; 140193323Sed struct dqblk dqblk; 141193323Sed struct timeval timev; 142199481Srdivacky 143193323Sed bzero((char *)&getq_args, sizeof(getq_args)); 144193323Sed if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 145199481Srdivacky svcerr_decode(transp); 146193323Sed return; 147193323Sed } 148193323Sed if (request->rq_cred.oa_flavor != AUTH_UNIX) { 149193323Sed /* bad auth */ 150193323Sed getq_rslt.status = Q_EPERM; 151193323Sed } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { 152193323Sed /* failed, return noquota */ 153199481Srdivacky getq_rslt.status = Q_NOQUOTA; 154193323Sed } else { 155193323Sed gettimeofday(&timev, NULL); 156199481Srdivacky getq_rslt.status = Q_OK; 157193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 158193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; 159193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 160199481Srdivacky dqblk.dqb_bhardlimit; 161193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 162193323Sed dqblk.dqb_bsoftlimit; 163193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 164193323Sed dqblk.dqb_curblocks; 165193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 166193323Sed dqblk.dqb_ihardlimit; 167193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 168193323Sed dqblk.dqb_isoftlimit; 169193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 170193323Sed dqblk.dqb_curinodes; 171193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 172193323Sed dqblk.dqb_btime - timev.tv_sec; 173193323Sed getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 174199481Srdivacky dqblk.dqb_itime - timev.tv_sec; 175198090Srdivacky } 176198090Srdivacky if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) { 177198090Srdivacky svcerr_systemerr(transp); 178201360Srdivacky } 179193323Sed if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 180193323Sed syslog(LOG_ERR, "unable to free arguments"); 181193323Sed exit(1); 182193323Sed } 183193323Sed} 184193323Sed 185193323Sedvoid 186193323Sedprinterr_reply(transp) /* when a reply to a request failed */ 187193323Sed SVCXPRT *transp; 188193323Sed{ 189193323Sed char *name; 190201360Srdivacky struct sockaddr_in *caller; 191193323Sed int save_errno; 192193323Sed 193193323Sed save_errno = errno; 194193323Sed 195193323Sed caller = svc_getcaller(transp); 196193323Sed name = (char *)inet_ntoa(caller->sin_addr); 197193323Sed errno = save_errno; 198193323Sed if (errno == 0) 199193323Sed syslog(LOG_ERR, "couldn't send reply to %s", name); 200193323Sed else 201193323Sed syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 202201360Srdivacky} 203201360Srdivacky 204201360Srdivacky/* initialise the fs_tab list from entries in /etc/fstab */ 205201360Srdivackyvoid 206201360Srdivackyinitfs() 207201360Srdivacky{ 208201360Srdivacky struct fs_stat *fs_current = NULL; 209201360Srdivacky struct fs_stat *fs_next = NULL; 210202375Srdivacky char *qfpathname; 211201360Srdivacky struct fstab *fs; 212201360Srdivacky struct stat st; 213201360Srdivacky 214193323Sed setfsent(); 215193323Sed while ((fs = getfsent())) { 216193323Sed if (strcmp(fs->fs_vfstype, "ufs")) 217198090Srdivacky continue; 218199481Srdivacky if (!hasquota(fs, &qfpathname)) 219198090Srdivacky continue; 220198090Srdivacky 221193323Sed fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); 222193323Sed fs_current->fs_next = fs_next; /* next element */ 223193323Sed 224193323Sed fs_current->fs_file = malloc(sizeof(char) * (strlen(fs->fs_file) + 1)); 225193323Sed strcpy(fs_current->fs_file, fs->fs_file); 226193323Sed 227201360Srdivacky fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1)); 228201360Srdivacky strcpy(fs_current->qfpathname, qfpathname); 229193323Sed 230201360Srdivacky stat(qfpathname, &st); 231193323Sed fs_current->st_dev = st.st_dev; 232201360Srdivacky 233201360Srdivacky fs_next = fs_current; 234201360Srdivacky } 235201360Srdivacky endfsent(); 236201360Srdivacky fs_begin = fs_current; 237202375Srdivacky} 238201360Srdivacky 239201360Srdivacky/* 240201360Srdivacky * gets the quotas for id, filesystem path. 241201360Srdivacky * Return 0 if fail, 1 otherwise 242193323Sed */ 243193323Sedint 244193323Sedgetfsquota(id, path, dqblk) 245193323Sed long id; 246193323Sed char *path; 247193323Sed struct dqblk *dqblk; 248193323Sed{ 249193323Sed struct stat st_path; 250193323Sed struct fs_stat *fs; 251193323Sed int qcmd, fd, ret = 0; 252193323Sed 253193323Sed if (stat(path, &st_path) < 0) 254193323Sed return (0); 255193323Sed 256193323Sed qcmd = QCMD(Q_GETQUOTA, USRQUOTA); 257193323Sed 258193323Sed for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { 259193323Sed /* where the devise is the same as path */ 260193323Sed if (fs->st_dev != st_path.st_dev) 261193323Sed continue; 262193323Sed 263201360Srdivacky /* find the specified filesystem. get and return quota */ 264193323Sed if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) 265193323Sed return (1); 266193323Sed 267193323Sed if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) { 268193323Sed syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname); 269193323Sed return (0); 270193323Sed } 271193323Sed if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) { 272193323Sed close(fd); 273193323Sed return (1); 274198090Srdivacky } 275198090Srdivacky switch (read(fd, dqblk, sizeof(struct dqblk))) { 276198090Srdivacky case 0: 277198090Srdivacky /* 278198090Srdivacky * Convert implicit 0 quota (EOF) 279198090Srdivacky * into an explicit one (zero'ed dqblk) 280198090Srdivacky */ 281193323Sed bzero((caddr_t) dqblk, sizeof(struct dqblk)); 282193323Sed ret = 1; 283193323Sed break; 284193323Sed case sizeof(struct dqblk): /* OK */ 285193323Sed ret = 1; 286193323Sed break; 287193323Sed default: /* ERROR */ 288193323Sed syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname); 289193323Sed close(fd); 290193323Sed return (0); 291193323Sed } 292193323Sed close(fd); 293193323Sed } 294199481Srdivacky return (ret); 295193323Sed} 296193323Sed 297199481Srdivacky/* 298199481Srdivacky * Check to see if a particular quota is to be enabled. 299193323Sed * Comes from quota.c, NetBSD 0.9 300193323Sed */ 301201360Srdivackyint 302201360Srdivackyhasquota(fs, qfnamep) 303193323Sed struct fstab *fs; 304193323Sed char **qfnamep; 305193323Sed{ 306199481Srdivacky static char initname, usrname[100]; 307193323Sed static char buf[BUFSIZ]; 308193323Sed char *opt, *cp; 309193323Sed char *qfextension[] = INITQFNAMES; 310193323Sed 311193323Sed if (!initname) { 312193323Sed sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME); 313193323Sed initname = 1; 314193323Sed } 315193323Sed strcpy(buf, fs->fs_mntops); 316193323Sed for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 317193323Sed if ((cp = index(opt, '='))) 318193323Sed *cp++ = '\0'; 319201360Srdivacky if (strcmp(opt, usrname) == 0) 320193323Sed break; 321193323Sed } 322193323Sed if (!opt) 323200581Srdivacky return (0); 324193323Sed if (cp) { 325193323Sed *qfnamep = cp; 326193323Sed return (1); 327193323Sed } 328193323Sed sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[USRQUOTA]); 329201360Srdivacky *qfnamep = buf; 330193323Sed return (1); 331201360Srdivacky} 332201360Srdivacky