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