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