1 /* $NetBSD: rquotad.c,v 1.32 2012/01/09 15:37:34 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.32 2012/01/09 15:37:34 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 37 static void rquota_service(struct svc_req *request, SVCXPRT *transp); 38 static void ext_rquota_service(struct svc_req *request, SVCXPRT *transp); 39 static void sendquota(struct svc_req *request, int vers, SVCXPRT *transp); 40 __dead static void cleanup(int); 41 42 static int from_inetd = 1; 43 44 static void 45 cleanup(int dummy) 46 { 47 48 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 49 (void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 50 exit(0); 51 } 52 53 int 54 main(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 114 static void 115 rquota_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 135 static void 136 ext_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 */ 160 static uint32_t 161 limit_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 */ 172 static uint32_t 173 time_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 */ 185 static void 186 quotavals_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 */ 209 static void 210 sendquota(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 301 out: 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