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 __FBSDID("$FreeBSD$"); 9 10 #include <sys/param.h> 11 #include <sys/mount.h> 12 #include <sys/file.h> 13 #include <sys/stat.h> 14 #include <sys/socket.h> 15 16 #include <ufs/ufs/quota.h> 17 #include <rpc/rpc.h> 18 #include <rpcsvc/rquota.h> 19 #include <arpa/inet.h> 20 #include <netdb.h> 21 22 #include <ctype.h> 23 #include <errno.h> 24 #include <fstab.h> 25 #include <grp.h> 26 #include <libutil.h> 27 #include <pwd.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <unistd.h> 34 35 static void rquota_service(struct svc_req *request, SVCXPRT *transp); 36 static void sendquota(struct svc_req *request, SVCXPRT *transp); 37 static void initfs(void); 38 static int getfsquota(long id, char *path, struct dqblk *dqblk); 39 40 static struct quotafile **qfa; /* array of qfs */ 41 static int nqf, szqf; /* number of qfs and size of array */ 42 static int from_inetd = 1; 43 44 static void 45 cleanup(int sig) 46 { 47 48 (void)sig; 49 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 50 exit(0); 51 } 52 53 int 54 main(void) 55 { 56 SVCXPRT *transp; 57 int ok; 58 struct sockaddr_storage from; 59 socklen_t fromlen; 60 61 fromlen = sizeof(from); 62 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 63 from_inetd = 0; 64 65 if (!from_inetd) { 66 daemon(0, 0); 67 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 68 (void)signal(SIGINT, cleanup); 69 (void)signal(SIGTERM, cleanup); 70 (void)signal(SIGHUP, cleanup); 71 } 72 73 openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); 74 75 /* create and register the service */ 76 if (from_inetd) { 77 transp = svc_tli_create(0, NULL, NULL, 0, 0); 78 if (transp == NULL) { 79 syslog(LOG_ERR, "couldn't create udp service."); 80 exit(1); 81 } 82 ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS, 83 rquota_service, NULL); 84 } else { 85 ok = svc_create(rquota_service, 86 RQUOTAPROG, RQUOTAVERS, "udp"); 87 } 88 if (!ok) { 89 syslog(LOG_ERR, 90 "unable to register (RQUOTAPROG, RQUOTAVERS, %s)", 91 from_inetd ? "(inetd)" : "udp"); 92 exit(1); 93 } 94 95 initfs(); 96 svc_run(); 97 syslog(LOG_ERR, "svc_run returned"); 98 exit(1); 99 } 100 101 static void 102 rquota_service(struct svc_req *request, SVCXPRT *transp) 103 { 104 105 switch (request->rq_proc) { 106 case NULLPROC: 107 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); 108 break; 109 case RQUOTAPROC_GETQUOTA: 110 case RQUOTAPROC_GETACTIVEQUOTA: 111 sendquota(request, transp); 112 break; 113 default: 114 svcerr_noproc(transp); 115 break; 116 } 117 if (from_inetd) 118 exit(0); 119 } 120 121 /* read quota for the specified id, and send it */ 122 static void 123 sendquota(struct svc_req *request, SVCXPRT *transp) 124 { 125 struct getquota_args getq_args; 126 struct getquota_rslt getq_rslt; 127 struct dqblk dqblk; 128 struct timeval timev; 129 int scale; 130 131 bzero(&getq_args, sizeof(getq_args)); 132 if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 133 svcerr_decode(transp); 134 return; 135 } 136 if (request->rq_cred.oa_flavor != AUTH_UNIX) { 137 /* bad auth */ 138 getq_rslt.status = Q_EPERM; 139 } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { 140 /* failed, return noquota */ 141 getq_rslt.status = Q_NOQUOTA; 142 } else { 143 gettimeofday(&timev, NULL); 144 getq_rslt.status = Q_OK; 145 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; 146 scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); 147 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = 148 DEV_BSIZE * scale; 149 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = 150 dqblk.dqb_bhardlimit / scale; 151 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = 152 dqblk.dqb_bsoftlimit / scale; 153 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = 154 dqblk.dqb_curblocks / scale; 155 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = 156 dqblk.dqb_ihardlimit; 157 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = 158 dqblk.dqb_isoftlimit; 159 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = 160 dqblk.dqb_curinodes; 161 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = 162 dqblk.dqb_btime - timev.tv_sec; 163 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = 164 dqblk.dqb_itime - timev.tv_sec; 165 } 166 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) 167 svcerr_systemerr(transp); 168 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { 169 syslog(LOG_ERR, "unable to free arguments"); 170 exit(1); 171 } 172 } 173 174 static void 175 initfs(void) 176 { 177 struct fstab *fs; 178 179 setfsent(); 180 szqf = 8; 181 if ((qfa = malloc(szqf * sizeof *qfa)) == NULL) 182 goto enomem; 183 while ((fs = getfsent())) { 184 if (strcmp(fs->fs_vfstype, "ufs")) 185 continue; 186 if (nqf >= szqf) { 187 szqf *= 2; 188 if ((qfa = reallocf(qfa, szqf * sizeof *qfa)) == NULL) 189 goto enomem; 190 } 191 if ((qfa[nqf] = quota_open(fs, USRQUOTA, O_RDONLY)) == NULL) { 192 if (errno != EOPNOTSUPP) 193 goto fserr; 194 continue; 195 } 196 ++nqf; 197 /* XXX */ 198 } 199 endfsent(); 200 return; 201 enomem: 202 syslog(LOG_ERR, "out of memory"); 203 exit(1); 204 fserr: 205 syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno)); 206 exit(1); 207 } 208 209 /* 210 * gets the quotas for id, filesystem path. 211 * Return 0 if fail, 1 otherwise 212 */ 213 static int 214 getfsquota(long id, char *path, struct dqblk *dqblk) 215 { 216 int i; 217 218 for (i = 0; i < nqf; ++i) 219 if (quota_check_path(qfa[i], path) == 1) 220 return (quota_read(qfa[i], dqblk, id) == 0); 221 return (0); 222 } 223