/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: repquota.c 2.6 89/08/07 $"

#ident	"@(#)repquota.c	1.2 87/07/24 3.2/4.3NFSSRC"
/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ident	"@(#)repquota.c	5.4 (Berkeley) 3/5/86"

/*
 * repquota.c
 *	Quota report
 */

/* $Log:	repquota.c,v $
 */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ufsquota.h>
#include <sys/stat.h>
#include <mntent.h>
#include <pwd.h>

#define LOGINNAMESIZE	 8
struct username {
	struct username *u_next;
	ushort	u_uid;
	char u_name[LOGINNAMESIZE + 1];
};
#define UHASH 997
struct username *uhead[UHASH];
struct username *lookup();
struct username *adduid();
int highuid;

char	*malloc();

int	vflag;		/* verbose */
int	aflag;		/* all file systems */

char *listbuf[50];

#define QFNAME "quotas"

extern	struct passwd *getpwent();
extern	void	exit(), perror(), setpwent(), endpwent();
extern	char	*strcpy(), *strncpy();
extern	time_t	time();

main(argc, argv)
	int argc;
	char **argv;
{
	register struct mntent *mntp;
	register struct passwd *pwp;
	register struct username *up;
	char **listp;
	int listcnt;
	char quotafile[MAXPATHLEN + 1];
	FILE *mtab, *fstab;
	int errs = 0;

again:
	argc--, argv++;
	if (argc > 0 && strcmp(*argv, "-v") == 0) {
		vflag++;
		goto again;
	}
	if (argc > 0 && strcmp(*argv, "-a") == 0) {
		aflag++;
		goto again;
	}
	if (argc <= 0 && !aflag) {
		(void) fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
			"repquota [-v] -a",
			"repquota [-v] filesys ...");
		exit(1);
	}
	(void) setpwent();
	while ((pwp = getpwent()) != 0) {
		up = lookup((ushort)pwp->pw_uid);
		if (up == 0) {
			up = adduid((ushort)pwp->pw_uid);
			(void) strncpy(up->u_name, pwp->pw_name,
				 sizeof(up->u_name));
		}
	}
	endpwent();
	/*
	 * If aflag go through fstab and make a list of appropriate
	 * filesystems.
	 */
	if (aflag) {
		listp = listbuf;
		listcnt = 0;
		if ((fstab = setmntent(FSTABNAME, "r")) == NULL) {
			(void) fprintf(stderr, "Can't open ");
			perror(FSTABNAME);
			exit(8);
		}
		while (mntp = getmntent(fstab)) {
			if (strcmp(mntp->mnt_type, MNTTYPE_42) != 0 ||
			    !hasmntopt(mntp, MNTOPT_QUOTA) ||
			    hasmntopt(mntp, MNTOPT_RO))
				continue;
			*listp = malloc((uint) strlen(mntp->mnt_fsname) + 1);
			(void) strcpy(*listp, mntp->mnt_fsname);
			listp++;
			listcnt++;
		}
		(void) endmntent(fstab);
		*listp = (char *)0;
		listp = listbuf;
	} else {
		listp = argv;
		listcnt = argc;
	}
	if ((mtab = setmntent(MNTTABNAME, "r")) == NULL) {
		(void) fprintf(stderr, "Can't open ");
		perror(MNTTABNAME);
		exit(8);
	}
	while (mntp = getmntent(mtab)) {
		if (strcmp(mntp->mnt_type, MNTTYPE_42) == 0 &&
		    !hasmntopt(mntp, MNTOPT_RO) &&
		    (oneof(mntp->mnt_fsname, listp, listcnt) ||
		     oneof(mntp->mnt_dir, listp, listcnt)) ) {
			(void) sprintf(quotafile,
				"%s/%s", mntp->mnt_dir, QFNAME);
			errs +=
			    repquota(mntp->mnt_fsname,mntp->mnt_dir,quotafile);
		}
	}
	(void) endmntent(mtab);
	while (listcnt--) {
		if (*listp)
			(void) fprintf(stderr, "Cannot report on %s\n",
				*listp);
	}
	exit(errs);
	/* NOTREACHED */
}

repquota(fsdev, fsfile, qffile)
	char *fsdev;
	char *fsfile;
	char *qffile;
{
	FILE *qf;
	ushort uid;
	struct dqblk dqbuf;
	struct stat statb;
	static int warned = 0;
	extern int errno;

	(void) printf("*** Quota report for %s (%s)\n", fsdev, fsfile);
	qf = fopen(qffile, "r");
	if (qf == NULL) {
		perror(qffile);
		return (1);
	}
	if (fstat(fileno(qf), &statb) < 0) {
		perror(qffile);
		(void) fclose(qf);
		return (1);
	}
	if (quotactl(Q_SYNC, fsdev, 0, NULL) < 0 &&
	    errno == EINVAL && !warned && vflag) {
		warned++;
		(void) printf(
"*** Warning: Quotas are not configured into this kernel\n");
	}
	header();
	for (uid = 0; ; uid++) {
		(void) fread(&dqbuf, sizeof(struct dqblk), 1, qf);
		if (feof(qf))
			break;
		if (!vflag &&
		    dqbuf.dqb_curfiles == 0 && dqbuf.dqb_curblocks == 0)
			continue;
		prquota(uid, &dqbuf);
	}
	(void) fclose(qf);
	return (0);
}

header()
{

	(void) printf(
"                      Block limits                      File limits\n"
	);
	(void) printf(
"User           used   soft   hard    timeleft    used   soft   hard    timeleft\n"
	);
}

prquota(uid, dqp)
	ushort uid;
	struct dqblk *dqp;
{
	time_t t_sec;
	register struct username *up;
	char ftimeleft[80], btimeleft[80];

	if (dqp->dqb_bsoftlimit == 0 && dqp->dqb_bhardlimit == 0 &&
	    dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0)
		return;
	t_sec = time(NULL);
	up = lookup(uid);
	if (up)
		(void) printf("%-10s", up->u_name);
	else
		(void) printf("#%-9d", uid);
	if (dqp->dqb_bsoftlimit && dqp->dqb_curblocks>=dqp->dqb_bsoftlimit) {
		if (dqp->dqb_btimelimit == 0) {
			(void) strcpy(btimeleft, "NOT STARTED");
		} else if (dqp->dqb_btimelimit > t_sec) {
			fmttime(btimeleft,
			    (long)(dqp->dqb_btimelimit - t_sec));
		} else {
			(void) strcpy(btimeleft, "EXPIRED");
		}
	} else {
		btimeleft[0] = '\0';
	}
	if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles>=dqp->dqb_fsoftlimit) {
		if (dqp->dqb_ftimelimit == 0) {
			(void) strcpy(ftimeleft, "NOT STARTED");
		} else if (dqp->dqb_ftimelimit > t_sec) {
			fmttime(ftimeleft,
			    (long)(dqp->dqb_ftimelimit - t_sec));
		} else {
			(void) strcpy(ftimeleft, "EXPIRED");
		}
	} else {
		ftimeleft[0] = '\0';
	}
	(void) printf("%c%c%7d%7d%7d%12s %7d%7d%7d%12s\n",
		dqp->dqb_bsoftlimit && 
		    dqp->dqb_curblocks >= 
		    dqp->dqb_bsoftlimit ? '+' : '-',
		dqp->dqb_fsoftlimit &&
		    dqp->dqb_curfiles >=
		    dqp->dqb_fsoftlimit ? '+' : '-',
		dqp->dqb_curblocks / btodb(1024),
		dqp->dqb_bsoftlimit / btodb(1024),
		dqp->dqb_bhardlimit / btodb(1024),
		btimeleft,
		dqp->dqb_curfiles,
		dqp->dqb_fsoftlimit,
		dqp->dqb_fhardlimit,
		ftimeleft
	);
}

fmttime(buf, time_left)
	char *buf;
	register long time_left;
{
	int i;
	static struct {
		int c_secs;		/* conversion units in secs */
		char * c_str;		/* unit string */
	} cunits [] = {
		{60*60*24*28, "months"},
		{60*60*24*7, "weeks"},
		{60*60*24, "days"},
		{60*60, "hours"},
		{60, "mins"},
		{1, "secs"}
	};

	if (time_left <= 0) {
		(void) strcpy(buf, "EXPIRED");
		return;
	}
	for (i = 0; i < sizeof(cunits)/sizeof(cunits[0]); i++) {
		if (time_left >= cunits[i].c_secs)
			break;
	}
	(void) sprintf(buf, "%.1f %s", (double)time_left/cunits[i].c_secs,
		cunits[i].c_str);
}

oneof(target, listp, n)
	char *target;
	register char **listp;
	register int n;
{

	while (n--) {
		if (*listp && strcmp(target, *listp) == 0) {
			*listp = (char *)0;
			return (1);
		}
		listp++;
	}
	return (0);
}

struct username *
lookup(uid)
	ushort uid;
{
	register struct username *up;

	for (up = uhead[uid % UHASH]; up != 0; up = up->u_next)
		if (up->u_uid == uid)
			return (up);
	return ((struct username *)0);
}

struct username *
adduid(uid)
	ushort uid;
{
	struct username *up, **uhp;
	extern char *calloc();

	up = lookup(uid);
	if (up != 0)
		return (up);
	up = (struct username *)calloc(1, sizeof(struct username));
	if (up == 0) {
		(void) fprintf(stderr,
			 "out of memory for username structures\n");
		exit(1);
	}
	uhp = &uhead[uid % UHASH];
	up->u_next = *uhp;
	*uhp = up;
	up->u_uid = uid;
	if (uid > highuid)
		highuid = uid;
	return (up);
}
