| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * by Manuel Bouyer (bouyer@ensta.fr) | |
| 3 | * | |
| 4 | * There is no copyright, you can use it as you want. | |
| 1de703da MD |
5 | * |
| 6 | * $FreeBSD: src/libexec/rpc.rquotad/rquotad.c,v 1.3.2.1 2001/07/02 23:46:27 mikeh Exp $ | |
| 97659ac1 | 7 | * $DragonFly: src/libexec/rpc.rquotad/rquotad.c,v 1.6 2008/11/19 18:41:30 swildner Exp $ |
| 984263bc MD |
8 | */ |
| 9 | ||
| 984263bc MD |
10 | #include <sys/param.h> |
| 11 | #include <sys/types.h> | |
| 12 | #include <sys/mount.h> | |
| 13 | #include <sys/file.h> | |
| 14 | #include <sys/stat.h> | |
| 15 | #include <sys/socket.h> | |
| 16 | #include <signal.h> | |
| 17 | ||
| 18 | #include <ctype.h> | |
| 19 | #include <errno.h> | |
| 20 | #include <fstab.h> | |
| 21 | #include <grp.h> | |
| 22 | #include <pwd.h> | |
| 23 | #include <stdio.h> | |
| 24 | #include <stdlib.h> | |
| 25 | #include <string.h> | |
| 26 | #include <unistd.h> | |
| 27 | ||
| 28 | #include <syslog.h> | |
| 29 | #include <varargs.h> | |
| 30 | ||
| 38a690d7 | 31 | #include <vfs/ufs/quota.h> |
| 984263bc MD |
32 | #include <rpc/rpc.h> |
| 33 | #include <rpc/pmap_clnt.h> | |
| 34 | #include <rpcsvc/rquota.h> | |
| 35 | #include <arpa/inet.h> | |
| 36 | ||
| 97659ac1 SW |
37 | static void cleanup(int); |
| 38 | static void rquota_service(struct svc_req *request, SVCXPRT *transp); | |
| 39 | static void sendquota(struct svc_req *request, SVCXPRT *transp); | |
| 40 | static void initfs(void); | |
| 41 | static int getfsquota(long id, char *path, struct ufs_dqblk *dqblk); | |
| 42 | static int hasquota(struct fstab *fs, char **qfnamep); | |
| 984263bc MD |
43 | |
| 44 | /* | |
| 45 | * structure containing informations about ufs filesystems | |
| 46 | * initialised by initfs() | |
| 47 | */ | |
| 48 | struct fs_stat { | |
| 49 | struct fs_stat *fs_next; /* next element */ | |
| 50 | char *fs_file; /* mount point of the filesystem */ | |
| 51 | char *qfpathname; /* pathname of the quota file */ | |
| 52 | dev_t st_dev; /* device of the filesystem */ | |
| 53 | } fs_stat; | |
| 54 | struct fs_stat *fs_begin = NULL; | |
| 55 | ||
| 56 | int from_inetd = 1; | |
| 57 | ||
| 58 | void | |
| 97659ac1 | 59 | cleanup(int signo __unused) |
| 984263bc MD |
60 | { |
| 61 | (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); | |
| 62 | exit(0); | |
| 63 | } | |
| 64 | ||
| 65 | int | |
| 97659ac1 | 66 | main(void) |
| 984263bc MD |
67 | { |
| 68 | SVCXPRT *transp; | |
| 69 | int sock = 0; | |
| 70 | int proto = 0; | |
| 71 | struct sockaddr_in from; | |
| 72 | int fromlen; | |
| 73 | ||
| 74 | fromlen = sizeof(from); | |
| 75 | if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { | |
| 76 | from_inetd = 0; | |
| 77 | sock = RPC_ANYSOCK; | |
| 78 | proto = IPPROTO_UDP; | |
| 79 | } | |
| 80 | ||
| 81 | if (!from_inetd) { | |
| 82 | daemon(0, 0); | |
| 83 | ||
| 84 | (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); | |
| 85 | ||
| 86 | (void) signal(SIGINT, cleanup); | |
| 87 | (void) signal(SIGTERM, cleanup); | |
| 88 | (void) signal(SIGHUP, cleanup); | |
| 89 | } | |
| 90 | ||
| 91 | openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON); | |
| 92 | ||
| 93 | /* create and register the service */ | |
| 94 | transp = svcudp_create(sock); | |
| 95 | if (transp == NULL) { | |
| 96 | syslog(LOG_ERR, "couldn't create udp service"); | |
| 97 | exit(1); | |
| 98 | } | |
| 99 | if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) { | |
| 100 | syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s)", proto?"udp":"(inetd)"); | |
| 101 | exit(1); | |
| 102 | } | |
| 103 | ||
| 104 | initfs(); /* init the fs_stat list */ | |
| 105 | svc_run(); | |
| 106 | syslog(LOG_ERR, "svc_run returned"); | |
| 107 | exit(1); | |
| 108 | } | |
| 109 | ||
| 110 | void | |
| 97659ac1 | 111 | rquota_service(struct svc_req *request, SVCXPRT *transp) |
| 984263bc MD |
112 | { |
| 113 | switch (request->rq_proc) { | |
| 114 | case NULLPROC: | |
| 5b0d3e5e | 115 | (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); |
| 984263bc MD |
116 | break; |
| 117 | ||
| 118 | case RQUOTAPROC_GETQUOTA: | |
| 119 | case RQUOTAPROC_GETACTIVEQUOTA: | |
| 120 | sendquota(request, transp); | |
| 121 | break; | |
| 122 | ||
| 123 | default: | |
| 124 | svcerr_noproc(transp); | |
| 125 | break; | |
| 126 | } | |
| 127 | if (from_inetd) | |
| 128 | exit(0); | |
| 129 | } | |
| 130 | ||
| 131 | /* read quota for the specified id, and send it */ | |
| 132 | void | |
| 97659ac1 | 133 | sendquota(struct svc_req *request, SVCXPRT *transp) |
| 984263bc MD |
134 | { |
| 135 | struct getquota_args getq_args; | |
| 136 | struct getquota_rslt getq_rslt; | |
| 286555ab | 137 | struct ufs_dqblk dqblk; |
| 984263bc MD |
138 | struct timeval timev; |
| 139 | ||
| 140 | bzero((char *)&getq_args, sizeof(getq_args)); | |
| 5b0d3e5e | 141 | if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, (caddr_t)&getq_args)) { |
| 984263bc MD |
142 | svcerr_decode(transp); |
| 143 | return; | |
| 144 | } | |
| 145 | if (request->rq_cred.oa_flavor != AUTH_UNIX) { | |
| 146 | /* bad auth */ | |
| 147 | getq_rslt.status = Q_EPERM; | |
| 148 | } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { | |
| 149 | /* failed, return noquota */ | |
| 150 | getq_rslt.status = Q_NOQUOTA; | |
| 151 | } else { | |
| 152 | gettimeofday(&timev, NULL); | |
| 153 | getq_rslt.status = Q_OK; | |
| 154 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; | |
| 155 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; | |
| 156 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = | |
| 157 | dqblk.dqb_bhardlimit; | |
| 158 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = | |
| 159 | dqblk.dqb_bsoftlimit; | |
| 160 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = | |
| 161 | dqblk.dqb_curblocks; | |
| 162 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = | |
| 163 | dqblk.dqb_ihardlimit; | |
| 164 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = | |
| 165 | dqblk.dqb_isoftlimit; | |
| 166 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = | |
| 167 | dqblk.dqb_curinodes; | |
| 168 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = | |
| 169 | dqblk.dqb_btime - timev.tv_sec; | |
| 170 | getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = | |
| 171 | dqblk.dqb_itime - timev.tv_sec; | |
| 172 | } | |
| 5b0d3e5e | 173 | if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, (char *)&getq_rslt)) { |
| 984263bc MD |
174 | svcerr_systemerr(transp); |
| 175 | } | |
| 5b0d3e5e | 176 | if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, (caddr_t)&getq_args)) { |
| 984263bc MD |
177 | syslog(LOG_ERR, "unable to free arguments"); |
| 178 | exit(1); | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 984263bc MD |
182 | /* initialise the fs_tab list from entries in /etc/fstab */ |
| 183 | void | |
| 97659ac1 | 184 | initfs(void) |
| 984263bc MD |
185 | { |
| 186 | struct fs_stat *fs_current = NULL; | |
| 187 | struct fs_stat *fs_next = NULL; | |
| 188 | char *qfpathname; | |
| 189 | struct fstab *fs; | |
| 190 | struct stat st; | |
| 191 | ||
| 192 | setfsent(); | |
| 193 | while ((fs = getfsent())) { | |
| 194 | if (strcmp(fs->fs_vfstype, "ufs")) | |
| 195 | continue; | |
| 196 | if (!hasquota(fs, &qfpathname)) | |
| 197 | continue; | |
| 198 | ||
| 199 | fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); | |
| 200 | fs_current->fs_next = fs_next; /* next element */ | |
| 201 | ||
| 202 | fs_current->fs_file = malloc(sizeof(char) * (strlen(fs->fs_file) + 1)); | |
| 203 | strcpy(fs_current->fs_file, fs->fs_file); | |
| 204 | ||
| 205 | fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1)); | |
| 206 | strcpy(fs_current->qfpathname, qfpathname); | |
| 207 | ||
| 208 | stat(fs_current->fs_file, &st); | |
| 209 | fs_current->st_dev = st.st_dev; | |
| 210 | ||
| 211 | fs_next = fs_current; | |
| 212 | } | |
| 213 | endfsent(); | |
| 214 | fs_begin = fs_current; | |
| 215 | } | |
| 216 | ||
| 217 | /* | |
| 218 | * gets the quotas for id, filesystem path. | |
| 219 | * Return 0 if fail, 1 otherwise | |
| 220 | */ | |
| 221 | int | |
| 97659ac1 | 222 | getfsquota(long id, char *path, struct ufs_dqblk *dqblk) |
| 984263bc MD |
223 | { |
| 224 | struct stat st_path; | |
| 225 | struct fs_stat *fs; | |
| 226 | int qcmd, fd, ret = 0; | |
| 227 | ||
| 228 | if (stat(path, &st_path) < 0) | |
| 229 | return (0); | |
| 230 | ||
| 231 | qcmd = QCMD(Q_GETQUOTA, USRQUOTA); | |
| 232 | ||
| 233 | for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { | |
| 234 | /* where the devise is the same as path */ | |
| 235 | if (fs->st_dev != st_path.st_dev) | |
| 236 | continue; | |
| 237 | ||
| 238 | /* find the specified filesystem. get and return quota */ | |
| 239 | if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) | |
| 240 | return (1); | |
| 241 | ||
| 242 | if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) { | |
| 243 | syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname); | |
| 244 | return (0); | |
| 245 | } | |
| 286555ab | 246 | if (lseek(fd, (off_t)(id * sizeof(struct ufs_dqblk)), L_SET) == (off_t)-1) { |
| 984263bc MD |
247 | close(fd); |
| 248 | return (1); | |
| 249 | } | |
| 286555ab | 250 | switch (read(fd, dqblk, sizeof(struct ufs_dqblk))) { |
| 984263bc MD |
251 | case 0: |
| 252 | /* | |
| 253 | * Convert implicit 0 quota (EOF) | |
| 254 | * into an explicit one (zero'ed dqblk) | |
| 255 | */ | |
| 286555ab | 256 | bzero((caddr_t) dqblk, sizeof(struct ufs_dqblk)); |
| 984263bc MD |
257 | ret = 1; |
| 258 | break; | |
| 286555ab | 259 | case sizeof(struct ufs_dqblk): /* OK */ |
| 984263bc MD |
260 | ret = 1; |
| 261 | break; | |
| 262 | default: /* ERROR */ | |
| 263 | syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname); | |
| 264 | close(fd); | |
| 265 | return (0); | |
| 266 | } | |
| 267 | close(fd); | |
| 268 | } | |
| 269 | return (ret); | |
| 270 | } | |
| 271 | ||
| 272 | /* | |
| 273 | * Check to see if a particular quota is to be enabled. | |
| 274 | * Comes from quota.c, NetBSD 0.9 | |
| 275 | */ | |
| 276 | int | |
| 97659ac1 | 277 | hasquota(struct fstab *fs, char **qfnamep) |
| 984263bc MD |
278 | { |
| 279 | static char initname, usrname[100]; | |
| 280 | static char buf[BUFSIZ]; | |
| 281 | char *opt, *cp; | |
| 97659ac1 | 282 | const char *qfextension[] = INITQFNAMES; |
| 984263bc MD |
283 | |
| 284 | if (!initname) { | |
| 285 | sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME); | |
| 286 | initname = 1; | |
| 287 | } | |
| 288 | strcpy(buf, fs->fs_mntops); | |
| 289 | for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { | |
| 290 | if ((cp = index(opt, '='))) | |
| 291 | *cp++ = '\0'; | |
| 292 | if (strcmp(opt, usrname) == 0) | |
| 293 | break; | |
| 294 | } | |
| 295 | if (!opt) | |
| 296 | return (0); | |
| 297 | if (cp) { | |
| 298 | *qfnamep = cp; | |
| 299 | return (1); | |
| 300 | } | |
| 301 | sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[USRQUOTA]); | |
| 302 | *qfnamep = buf; | |
| 303 | return (1); | |
| 304 | } |