1/* $NetBSD: rquotad.c,v 1.31 2011/11/25 16:55:05 dholland Exp $ */ 2 3/* 4 * by Manuel Bouyer (bouyer@ensta.fr). Public domain. 5 */ 6 7#include <sys/cdefs.h> 8#ifndef lint 9__RCSID("$NetBSD: rquotad.c,v 1.31 2011/11/25 16:55:05 dholland Exp $"); 10#endif 11 12#include <sys/param.h> 13#include <sys/types.h> 14#include <sys/mount.h> 15#include <sys/file.h> 16#include <sys/stat.h> 17#include <sys/socket.h> 18#include <signal.h> 19 20#include <stdio.h> 21#include <fstab.h> 22#include <ctype.h> 23#include <stdlib.h> 24#include <string.h> 25#include <pwd.h> 26#include <grp.h> 27#include <errno.h> 28#include <unistd.h> 29#include <syslog.h> 30 31#include <rpc/rpc.h> 32#include <rpcsvc/rquota.h> 33#include <arpa/inet.h> 34 35#include <quota.h> 36 37static void rquota_service(struct svc_req *request, SVCXPRT *transp); 38static void ext_rquota_service(struct svc_req *request, SVCXPRT *transp); 39static void sendquota(struct svc_req *request, int vers, SVCXPRT *transp); 40__dead static void cleanup(int); 41 42static int from_inetd = 1; 43 44static void 45cleanup(int dummy) 46{ 47 48 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 49 (void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 50 exit(0); 51} 52 53int 54main(int argc, char *argv[]) 55{ 56 SVCXPRT *transp; 57 struct sockaddr_storage from; 58 socklen_t fromlen; 59 60 fromlen = sizeof(from); 61 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 62 from_inetd = 0; 63 64 if (!from_inetd) { 65 daemon(0, 0); 66 67 (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 68 (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 69 (void) signal(SIGINT, cleanup); 70 (void) signal(SIGTERM, cleanup); 71 (void) signal(SIGHUP, cleanup); 72 } 73 74 openlog("rpc.rquotad", LOG_PID, LOG_DAEMON); 75 76 /* create and register the service */ 77 if (from_inetd) { 78 transp = svc_dg_create(0, 0, 0); 79 if (transp == NULL) { 80 syslog(LOG_ERR, "couldn't create udp service."); 81 exit(1); 82 } 83 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, 84 NULL)) { 85 syslog(LOG_ERR, 86 "unable to register (RQUOTAPROG, RQUOTAVERS)."); 87 exit(1); 88 } 89 if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 90 ext_rquota_service, NULL)) { 91 syslog(LOG_ERR, 92 "unable to register (RQUOTAPROG, EXT_RQUOTAVERS)."); 93 exit(1); 94 } 95 } else { 96 if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){ 97 syslog(LOG_ERR, 98 "unable to create (RQUOTAPROG, RQUOTAVERS)."); 99 exit(1); 100 } 101 if (!svc_create(ext_rquota_service, RQUOTAPROG, 102 EXT_RQUOTAVERS, "udp")){ 103 syslog(LOG_ERR, 104 "unable to create (RQUOTAPROG, EXT_RQUOTAVERS)."); 105 exit(1); 106 } 107 } 108 109 svc_run(); 110 syslog(LOG_ERR, "svc_run returned"); 111 exit(1); 112} 113 114static void 115rquota_service(struct svc_req *request, SVCXPRT *transp) 116{ 117 switch (request->rq_proc) { 118 case NULLPROC: 119 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 120 break; 121 122 case RQUOTAPROC_GETQUOTA: 123 case RQUOTAPROC_GETACTIVEQUOTA: 124 sendquota(request, RQUOTAVERS, transp); 125 break; 126 127 default: 128 svcerr_noproc(transp); 129 break; 130 } 131 if (from_inetd) 132 exit(0); 133} 134 135static void 136ext_rquota_service(struct svc_req *request, SVCXPRT *transp) 137{ 138 switch (request->rq_proc) { 139 case NULLPROC: 140 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 141 break; 142 143 case RQUOTAPROC_GETQUOTA: 144 case RQUOTAPROC_GETACTIVEQUOTA: 145 sendquota(request, EXT_RQUOTAVERS, transp); 146 break; 147 148 default: 149 svcerr_noproc(transp); 150 break; 151 } 152 if (from_inetd) 153 exit(0); 154} 155 156/* 157 * Convert a limit to rquota representation (where 0 == unlimited). 158 * Clamp the result into a uint32_t. 159 */ 160static uint32_t 161limit_to_rquota(uint64_t lim) 162{ 163 if (lim == QUOTA_NOLIMIT || lim > 0xfffffffeUL) 164 return 0; 165 else 166 return (lim + 1); 167} 168 169/* 170 * Convert a time to rquota representation. 171 */ 172static uint32_t 173time_to_rquota(time_t when, time_t now) 174{ 175 if (when == QUOTA_NOTIME) { 176 return 0; 177 } else { 178 return when - now; 179 } 180} 181 182/* 183 * Convert to rquota representation. 184 */ 185static void 186quotavals_to_rquota(const struct quotaval *blocks, 187 const struct quotaval *files, 188 struct rquota *rq) 189{ 190 struct timeval now; 191 192 gettimeofday(&now, NULL); 193 194 rq->rq_active = TRUE; 195 rq->rq_bsize = DEV_BSIZE; 196 197 rq->rq_bhardlimit = limit_to_rquota(blocks->qv_hardlimit); 198 rq->rq_bsoftlimit = limit_to_rquota(blocks->qv_softlimit); 199 rq->rq_curblocks = blocks->qv_usage; 200 rq->rq_btimeleft = time_to_rquota(blocks->qv_expiretime, now.tv_sec); 201 202 rq->rq_fhardlimit = limit_to_rquota(files->qv_hardlimit); 203 rq->rq_fsoftlimit = limit_to_rquota(files->qv_softlimit); 204 rq->rq_curfiles = files->qv_usage; 205 rq->rq_ftimeleft = time_to_rquota(files->qv_expiretime, now.tv_sec); 206} 207 208/* read quota for the specified id, and send it */ 209static void 210sendquota(struct svc_req *request, int vers, SVCXPRT *transp) 211{ 212 struct getquota_args getq_args; 213 struct ext_getquota_args ext_getq_args; 214 struct getquota_rslt getq_rslt; 215 struct quotahandle *qh; 216 struct quotakey qk; 217 struct quotaval blocks, files; 218 int idtype; 219 220 memset((char *)&getq_args, 0, sizeof(getq_args)); 221 memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args)); 222 switch (vers) { 223 case RQUOTAVERS: 224 if (!svc_getargs(transp, xdr_getquota_args, 225 (caddr_t)&getq_args)) { 226 svcerr_decode(transp); 227 return; 228 } 229 ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 230 ext_getq_args.gqa_id = getq_args.gqa_uid; 231 ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 232 break; 233 case EXT_RQUOTAVERS: 234 if (!svc_getargs(transp, xdr_ext_getquota_args, 235 (caddr_t)&ext_getq_args)) { 236 svcerr_decode(transp); 237 return; 238 } 239 break; 240 } 241 switch (ext_getq_args.gqa_type) { 242 case RQUOTA_USRQUOTA: 243 idtype = QUOTA_IDTYPE_USER; 244 break; 245 case RQUOTA_GRPQUOTA: 246 idtype = QUOTA_IDTYPE_GROUP; 247 break; 248 default: 249 getq_rslt.status = Q_NOQUOTA; 250 goto out; 251 } 252 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 253 /* bad auth */ 254 getq_rslt.status = Q_EPERM; 255 goto out; 256 } 257 258 /* 259 * XXX validate the path... 260 */ 261 262 qh = quota_open(ext_getq_args.gqa_pathp); 263 if (qh == NULL) { 264 /* 265 * There are only three possible responses: success, 266 * permission denied, and "no quota", so we return 267 * the last for essentially all errors. 268 */ 269 if (errno == EPERM || errno == EACCES) { 270 getq_rslt.status = Q_EPERM; 271 goto out; 272 } 273 getq_rslt.status = Q_NOQUOTA; 274 goto out; 275 } 276 277 qk.qk_id = ext_getq_args.gqa_id; 278 qk.qk_idtype = idtype; 279 qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS; 280 if (quota_get(qh, &qk, &blocks) < 0) { 281 /* failed, return noquota */ 282 quota_close(qh); 283 getq_rslt.status = Q_NOQUOTA; 284 goto out; 285 } 286 287 qk.qk_objtype = QUOTA_OBJTYPE_FILES; 288 if (quota_get(qh, &qk, &files) < 0) { 289 /* failed, return noquota */ 290 quota_close(qh); 291 getq_rslt.status = Q_NOQUOTA; 292 goto out; 293 } 294 295 quota_close(qh); 296 297 quotavals_to_rquota(&blocks, &files, 298 &getq_rslt.getquota_rslt_u.gqr_rquota); 299 getq_rslt.status = Q_OK; 300 301out: 302 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, (char *)&getq_rslt)) 303 svcerr_systemerr(transp); 304 if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 305 syslog(LOG_ERR, "unable to free arguments"); 306 exit(1); 307 } 308} 309