1 /* 2 * by Manuel Bouyer (bouyer@ensta.fr) 3 * 4 * There is no copyright, you can use it as you want. 5 */ 6 7 #include <sys/param.h> 8 #include <sys/mount.h> 9 #include <sys/file.h> 10 #include <sys/stat.h> 11 #include <sys/socket.h> 12 13 #include <ufs/ufs/quota.h> 14 #include <rpc/rpc.h> 15 #include <rpcsvc/rquota.h> 16 #include <arpa/inet.h> 17 #include <netdb.h> 18 19 #include <ctype.h> 20 #include <errno.h> 21 #include <fstab.h> 22 #include <grp.h> 23 #include <libutil.h> 24 #include <pwd.h> 25 #include <signal.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <err.h> 29 #include <string.h> 30 #include <syslog.h> 31 #include <unistd.h> 32 33 static void rquota_service_1(struct svc_req *request, SVCXPRT *transp); 34 static void rquota_service_2(struct svc_req *request, SVCXPRT *transp); 35 static void sendquota(struct svc_req *request, SVCXPRT *transp); 36 static void sendquota_extended(struct svc_req *request, SVCXPRT *transp); 37 static int getfsquota(int type, long id, char *path, struct dqblk *dqblk); 38 39 static int from_inetd = 1; 40 static int debug = 0; 41 42 static void 43 cleanup(int sig) 44 { 45 46 (void)sig; 47 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 48 exit(0); 49 } 50 51 int 52 main(int argc, char **argv) 53 { 54 SVCXPRT *transp; 55 int ok; 56 struct sockaddr_storage from; 57 socklen_t fromlen; 58 int vers; 59 int ch; 60 61 while ((ch = getopt(argc, argv, "d")) != -1) { 62 switch (ch) { 63 case 'd': 64 debug++; 65 break; 66 default: 67 break; 68 } 69 } 70 71 fromlen = sizeof(from); 72 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 73 from_inetd = 0; 74 75 if (!from_inetd) { 76 if (!debug) 77 daemon(0, 0); 78 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 79 (void)signal(SIGINT, cleanup); 80 (void)signal(SIGTERM, cleanup); 81 (void)signal(SIGHUP, cleanup); 82 } 83 84 openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 85 86 /* create and register the service */ 87 if (from_inetd) { 88 transp = svc_tli_create(0, NULL, NULL, 0, 0); 89 if (transp == NULL) { 90 syslog(LOG_ERR, "couldn't create udp service."); 91 exit(1); 92 } 93 vers = RQUOTAVERS; 94 ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS, 95 rquota_service_1, NULL); 96 if (ok) { 97 vers = EXT_RQUOTAVERS; 98 ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 99 rquota_service_2, NULL); 100 } 101 } else { 102 vers = RQUOTAVERS; 103 ok = svc_create(rquota_service_1, 104 RQUOTAPROG, RQUOTAVERS, "udp"); 105 if (ok) { 106 vers = EXT_RQUOTAVERS; 107 ok = svc_create(rquota_service_2, 108 RQUOTAPROG, EXT_RQUOTAVERS, "udp"); 109 110 } 111 } 112 if (!ok) { 113 syslog(LOG_ERR, 114 "unable to register (RQUOTAPROG, %s, %s)", 115 vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS", 116 from_inetd ? "(inetd)" : "udp"); 117 exit(1); 118 } 119 120 svc_run(); 121 syslog(LOG_ERR, "svc_run returned"); 122 exit(1); 123 } 124 125 static void 126 rquota_service_2(struct svc_req *request, SVCXPRT *transp) 127 { 128 129 switch (request->rq_proc) { 130 case NULLPROC: 131 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 132 break; 133 case RQUOTAPROC_GETQUOTA: 134 case RQUOTAPROC_GETACTIVEQUOTA: 135 sendquota_extended(request, transp); 136 break; 137 default: 138 svcerr_noproc(transp); 139 break; 140 } 141 if (from_inetd) 142 exit(0); 143 } 144 145 static void 146 rquota_service_1(struct svc_req *request, SVCXPRT *transp) 147 { 148 149 switch (request->rq_proc) { 150 case NULLPROC: 151 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 152 break; 153 case RQUOTAPROC_GETQUOTA: 154 case RQUOTAPROC_GETACTIVEQUOTA: 155 sendquota(request, transp); 156 break; 157 default: 158 svcerr_noproc(transp); 159 break; 160 } 161 if (from_inetd) 162 exit(0); 163 } 164 165 /* read quota for the specified id, and send it */ 166 static void 167 sendquota(struct svc_req *request, SVCXPRT *transp) 168 { 169 struct getquota_args getq_args; 170 struct getquota_rslt getq_rslt; 171 struct dqblk dqblk; 172 struct timeval timev; 173 int scale; 174 175 bzero(&getq_args, sizeof(getq_args)); 176 if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 177 svcerr_decode(transp); 178 return; 179 } 180 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 181 /* bad auth */ 182 getq_rslt.status = Q_EPERM; 183 } else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { 184 /* failed, return noquota */ 185 getq_rslt.status = Q_NOQUOTA; 186 } else { 187 gettimeofday(&timev, NULL); 188 getq_rslt.status = Q_OK; 189 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 190 scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); 191 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = 192 DEV_BSIZE * scale; 193 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 194 dqblk.dqb_bhardlimit / scale; 195 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 196 dqblk.dqb_bsoftlimit / scale; 197 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 198 dqblk.dqb_curblocks / scale; 199 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 200 dqblk.dqb_ihardlimit; 201 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 202 dqblk.dqb_isoftlimit; 203 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 204 dqblk.dqb_curinodes; 205 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 206 dqblk.dqb_btime - timev.tv_sec; 207 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 208 dqblk.dqb_itime - timev.tv_sec; 209 } 210 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 211 svcerr_systemerr(transp); 212 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 213 syslog(LOG_ERR, "unable to free arguments"); 214 exit(1); 215 } 216 } 217 218 static void 219 sendquota_extended(struct svc_req *request, SVCXPRT *transp) 220 { 221 struct ext_getquota_args getq_args; 222 struct getquota_rslt getq_rslt; 223 struct dqblk dqblk; 224 struct timeval timev; 225 int scale; 226 227 bzero(&getq_args, sizeof(getq_args)); 228 if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) { 229 svcerr_decode(transp); 230 return; 231 } 232 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 233 /* bad auth */ 234 getq_rslt.status = Q_EPERM; 235 } else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) { 236 /* failed, return noquota */ 237 getq_rslt.status = Q_NOQUOTA; 238 } else { 239 gettimeofday(&timev, NULL); 240 getq_rslt.status = Q_OK; 241 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 242 scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); 243 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = 244 DEV_BSIZE * scale; 245 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 246 dqblk.dqb_bhardlimit / scale; 247 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 248 dqblk.dqb_bsoftlimit / scale; 249 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 250 dqblk.dqb_curblocks / scale; 251 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 252 dqblk.dqb_ihardlimit; 253 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 254 dqblk.dqb_isoftlimit; 255 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 256 dqblk.dqb_curinodes; 257 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 258 dqblk.dqb_btime - timev.tv_sec; 259 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 260 dqblk.dqb_itime - timev.tv_sec; 261 } 262 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 263 svcerr_systemerr(transp); 264 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 265 syslog(LOG_ERR, "unable to free arguments"); 266 exit(1); 267 } 268 } 269 270 /* 271 * gets the quotas for id, filesystem path. 272 * Return 0 if fail, 1 otherwise 273 */ 274 static int 275 getfsquota(int type, long id, char *path, struct dqblk *dqblk) 276 { 277 struct quotafile *qf; 278 /* 279 * Remote quota checking is limited to mounted filesystems. 280 * Since UFS and ZFS support the quota system calls, we 281 * only need to make an fstab object that has the path, and 282 * a blank name for the filesystem type. 283 * This allows the quota_open() call to work the way we 284 * expect it to. 285 * 286 * The static char declaration is because compiler warnings 287 * don't allow passing a const char * to a char *. 288 */ 289 int rv; 290 static char blank[] = ""; 291 struct fstab fst; 292 293 fst.fs_file = path; 294 fst.fs_mntops = blank; 295 fst.fs_vfstype = blank; 296 297 if (type != USRQUOTA && type != GRPQUOTA) 298 return (0); 299 300 qf = quota_open(&fst, type, O_RDONLY); 301 if (debug) 302 warnx("quota_open(<%s, %s>, %d) returned %p", 303 fst.fs_file, fst.fs_mntops, type, 304 qf); 305 if (qf == NULL) 306 return (0); 307 308 rv = quota_read(qf, dqblk, id) == 0; 309 quota_close(qf); 310 if (debug) 311 warnx("getfsquota(%d, %ld, %s, %p) -> %d", 312 type, id, path, dqblk, rv); 313 return (rv); 314 } 315