1 /* $NetBSD: rquotad.c,v 1.33 2014/03/18 11:00:20 gson 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.33 2014/03/18 11:00:20 gson 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 (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 66 (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 67 } 68 69 openlog("rpc.rquotad", LOG_PID, LOG_DAEMON); 70 71 /* create and register the service */ 72 if (from_inetd) { 73 transp = svc_dg_create(0, 0, 0); 74 if (transp == NULL) { 75 syslog(LOG_ERR, "couldn't create udp service."); 76 exit(1); 77 } 78 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, 79 NULL)) { 80 syslog(LOG_ERR, 81 "unable to register (RQUOTAPROG, RQUOTAVERS)."); 82 exit(1); 83 } 84 if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 85 ext_rquota_service, NULL)) { 86 syslog(LOG_ERR, 87 "unable to register (RQUOTAPROG, EXT_RQUOTAVERS)."); 88 exit(1); 89 } 90 } else { 91 if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){ 92 syslog(LOG_ERR, 93 "unable to create (RQUOTAPROG, RQUOTAVERS)."); 94 exit(1); 95 } 96 if (!svc_create(ext_rquota_service, RQUOTAPROG, 97 EXT_RQUOTAVERS, "udp")){ 98 syslog(LOG_ERR, 99 "unable to create (RQUOTAPROG, EXT_RQUOTAVERS)."); 100 exit(1); 101 } 102 } 103 104 if (!from_inetd) { 105 daemon(0, 0); 106 (void) signal(SIGINT, cleanup); 107 (void) signal(SIGTERM, cleanup); 108 (void) signal(SIGHUP, cleanup); 109 } 110 svc_run(); 111 syslog(LOG_ERR, "svc_run returned"); 112 exit(1); 113 } 114 115 static void 116 rquota_service(struct svc_req *request, SVCXPRT *transp) 117 { 118 switch (request->rq_proc) { 119 case NULLPROC: 120 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 121 break; 122 123 case RQUOTAPROC_GETQUOTA: 124 case RQUOTAPROC_GETACTIVEQUOTA: 125 sendquota(request, RQUOTAVERS, transp); 126 break; 127 128 default: 129 svcerr_noproc(transp); 130 break; 131 } 132 if (from_inetd) 133 exit(0); 134 } 135 136 static void 137 ext_rquota_service(struct svc_req *request, SVCXPRT *transp) 138 { 139 switch (request->rq_proc) { 140 case NULLPROC: 141 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 142 break; 143 144 case RQUOTAPROC_GETQUOTA: 145 case RQUOTAPROC_GETACTIVEQUOTA: 146 sendquota(request, EXT_RQUOTAVERS, transp); 147 break; 148 149 default: 150 svcerr_noproc(transp); 151 break; 152 } 153 if (from_inetd) 154 exit(0); 155 } 156 157 /* 158 * Convert a limit to rquota representation (where 0 == unlimited). 159 * Clamp the result into a uint32_t. 160 */ 161 static uint32_t 162 limit_to_rquota(uint64_t lim) 163 { 164 if (lim == QUOTA_NOLIMIT || lim > 0xfffffffeUL) 165 return 0; 166 else 167 return (lim + 1); 168 } 169 170 /* 171 * Convert a time to rquota representation. 172 */ 173 static uint32_t 174 time_to_rquota(time_t when, time_t now) 175 { 176 if (when == QUOTA_NOTIME) { 177 return 0; 178 } else { 179 return when - now; 180 } 181 } 182 183 /* 184 * Convert to rquota representation. 185 */ 186 static void 187 quotavals_to_rquota(const struct quotaval *blocks, 188 const struct quotaval *files, 189 struct rquota *rq) 190 { 191 struct timeval now; 192 193 gettimeofday(&now, NULL); 194 195 rq->rq_active = TRUE; 196 rq->rq_bsize = DEV_BSIZE; 197 198 rq->rq_bhardlimit = limit_to_rquota(blocks->qv_hardlimit); 199 rq->rq_bsoftlimit = limit_to_rquota(blocks->qv_softlimit); 200 rq->rq_curblocks = blocks->qv_usage; 201 rq->rq_btimeleft = time_to_rquota(blocks->qv_expiretime, now.tv_sec); 202 203 rq->rq_fhardlimit = limit_to_rquota(files->qv_hardlimit); 204 rq->rq_fsoftlimit = limit_to_rquota(files->qv_softlimit); 205 rq->rq_curfiles = files->qv_usage; 206 rq->rq_ftimeleft = time_to_rquota(files->qv_expiretime, now.tv_sec); 207 } 208 209 /* read quota for the specified id, and send it */ 210 static void 211 sendquota(struct svc_req *request, int vers, SVCXPRT *transp) 212 { 213 struct getquota_args getq_args; 214 struct ext_getquota_args ext_getq_args; 215 struct getquota_rslt getq_rslt; 216 struct quotahandle *qh; 217 struct quotakey qk; 218 struct quotaval blocks, files; 219 int idtype; 220 221 memset((char *)&getq_args, 0, sizeof(getq_args)); 222 memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args)); 223 switch (vers) { 224 case RQUOTAVERS: 225 if (!svc_getargs(transp, xdr_getquota_args, 226 (caddr_t)&getq_args)) { 227 svcerr_decode(transp); 228 return; 229 } 230 ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 231 ext_getq_args.gqa_id = getq_args.gqa_uid; 232 ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 233 break; 234 case EXT_RQUOTAVERS: 235 if (!svc_getargs(transp, xdr_ext_getquota_args, 236 (caddr_t)&ext_getq_args)) { 237 svcerr_decode(transp); 238 return; 239 } 240 break; 241 } 242 switch (ext_getq_args.gqa_type) { 243 case RQUOTA_USRQUOTA: 244 idtype = QUOTA_IDTYPE_USER; 245 break; 246 case RQUOTA_GRPQUOTA: 247 idtype = QUOTA_IDTYPE_GROUP; 248 break; 249 default: 250 getq_rslt.status = Q_NOQUOTA; 251 goto out; 252 } 253 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 254 /* bad auth */ 255 getq_rslt.status = Q_EPERM; 256 goto out; 257 } 258 259 /* 260 * XXX validate the path... 261 */ 262 263 qh = quota_open(ext_getq_args.gqa_pathp); 264 if (qh == NULL) { 265 /* 266 * There are only three possible responses: success, 267 * permission denied, and "no quota", so we return 268 * the last for essentially all errors. 269 */ 270 if (errno == EPERM || errno == EACCES) { 271 getq_rslt.status = Q_EPERM; 272 goto out; 273 } 274 getq_rslt.status = Q_NOQUOTA; 275 goto out; 276 } 277 278 qk.qk_id = ext_getq_args.gqa_id; 279 qk.qk_idtype = idtype; 280 qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS; 281 if (quota_get(qh, &qk, &blocks) < 0) { 282 /* failed, return noquota */ 283 quota_close(qh); 284 getq_rslt.status = Q_NOQUOTA; 285 goto out; 286 } 287 288 qk.qk_objtype = QUOTA_OBJTYPE_FILES; 289 if (quota_get(qh, &qk, &files) < 0) { 290 /* failed, return noquota */ 291 quota_close(qh); 292 getq_rslt.status = Q_NOQUOTA; 293 goto out; 294 } 295 296 quota_close(qh); 297 298 quotavals_to_rquota(&blocks, &files, 299 &getq_rslt.getquota_rslt_u.gqr_rquota); 300 getq_rslt.status = Q_OK; 301 302 out: 303 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, (char *)&getq_rslt)) 304 svcerr_systemerr(transp); 305 if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 306 syslog(LOG_ERR, "unable to free arguments"); 307 exit(1); 308 } 309 } 310