/*
 * Simple util to print out DMAPI info about a file
 *
 * This code was written by Peter Lawthers, and placed in the public
 * domain for use by DMAPI implementors and app writers.
 *
 * Standard disclaimer:
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>

#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>

#include <lib/hsm.h>

#define MAX_RGNS	8	/* Arbitrary for this release */
#define NUM_EXTENTS	4

extern char	*optarg;
extern int	 optind, optopt, opterr;
char		*Progname;

void		 usage(char *);
int		 mr_info(dm_sessid_t, void *, size_t);
int		 alloc_info(dm_sessid_t, void *, size_t);
int		 event_info(dm_sessid_t, void *, size_t);
int		 handle_info(dm_sessid_t, void *, size_t);

extern int	 setup_dmapi(dm_sessid_t *);
extern void	 errno_msg(char *, ...);
extern void	 print_handle(void *, size_t);


void
usage(
	char *prog)
{
	fprintf(stderr, "Usage: %s <options> filename \n ", prog);
	fprintf(stderr, "\t-m	managed region info\n");
	fprintf(stderr, "\t-a	allocation info\n");
	fprintf(stderr, "\t-e	event info\n");
	fprintf(stderr, "\t-h	handle\n");
}


int
main(
	int	argc, 
	char	*argv[])
{
	int		 c;
	int	 	 error;
	int		 mr_flag, alloc_flag, handle_flag, event_flag;
	void		*hanp;
	size_t	 	 hlen;
	char		*filename;
	dm_sessid_t	 sid;


	Progname = argv[0];
	mr_flag  = alloc_flag =  handle_flag = event_flag = 0;
	
	while ((c = getopt(argc, argv, "maeh")) != EOF) {
		switch (c) {
		case 'm':
			mr_flag = 1;
			break;
		case 'a':
			alloc_flag = 1;
			break;
		case 'e':
			event_flag = 1;
			break;
		case 'h':
			handle_flag = 1;
			break;
		case '?':
		default:
			usage(Progname);
			exit(1);
		}
	}
	if (optind >= argc) {
		usage(Progname);
		exit(1);
	}
	filename = argv[optind];
	if (filename == NULL) {
		usage(Progname);
		exit(1);
	}
	

	/*
	 * Set up our link to the DMAPI, and get a handle for
	 * the file we want to query
	 */
	error = setup_dmapi(&sid);
	if (error)
		exit(1);

	if (dm_path_to_handle(filename, &hanp, &hlen) == -1) {
		printf("Can't get handle for path %s", filename);
		error = 1;
		goto out;
	}

	printf("File %s:\n", filename);
	if (mr_flag) {
		error = mr_info(sid, hanp, hlen);
		if (error) {
			error = 1;
			goto out;
		}
	}
	if (alloc_flag) {
		error = alloc_info(sid, hanp, hlen);
		if (error) {
			error = 1;
			goto out;
		}
	}
	if (event_flag) {
		error = event_info(sid, hanp, hlen);
		if (error) {
			error = 1;
			goto out;
		}
	}
	if (handle_flag) {
		error = handle_info(sid, hanp, hlen);
		if (error) {
			error = 1;
			goto out;
		}
	}

out:
	if (dm_destroy_session(sid)) {
		errno_msg("Can't shut down session");
		error = 1;
	}

	return(error);
}

/*
 * Get the complete list of all managed regions for a file. For now,
 * we know that most implementations only support a small number of
 * regions, so we don't handle the E2BIG error here.
 */
int
mr_info(
	dm_sessid_t	 sid,
	void		*hanp,
	size_t		 hlen)
{
	u_int		i;
	u_int		ret;
	dm_region_t	rgn[MAX_RGNS];

	memset((char *)&rgn, 0, (sizeof(dm_region_t) * MAX_RGNS));
	if (dm_get_region(sid, hanp, hlen, DM_NO_TOKEN, MAX_RGNS, rgn, &ret)) {
		errno_msg("Can't get list of managed regions");
		return(1);
	}
	printf("\n");
	for (i=0; i<ret; i++) {
		printf("\tRegion %d:\n", i);
		printf("\t\toffset %lld, ", rgn[i].rg_offset);
		printf("size %lld, ", rgn[i].rg_size);
		printf("flags 0x%x", rgn[i].rg_flags);
		printf(" ( ");
		if (rgn[i].rg_flags & DM_REGION_NOEVENT)
			printf("noevent ");
		if (rgn[i].rg_flags & DM_REGION_READ)
			printf("read ");
		if (rgn[i].rg_flags & DM_REGION_WRITE)
			printf("write ");
		if (rgn[i].rg_flags & DM_REGION_TRUNCATE)
			printf("trunc ");
		printf(" )\n");
	}
	return(0);
}

/*
 * Get the complete list of events for a file.
 */
int
event_info(
	dm_sessid_t	 sid,
	void		*hanp,
	size_t		 hlen)
{
	u_int		ret;
	dm_eventset_t	eventlist;

	DMEV_ZERO(eventlist);
	if (dm_get_eventlist(sid, hanp, hlen, DM_NO_TOKEN, DM_EVENT_MAX, 
				&eventlist, &ret)) {
		errno_msg("Can't get list of events");
		return(1);
	}
	printf("\n\tEvent list: \n\t\t");
	if (DMEV_ISSET(DM_EVENT_MOUNT, eventlist)) 
		printf("mount ");
	if (DMEV_ISSET(DM_EVENT_PREUNMOUNT, eventlist)) 
		printf("preunmount ");
	if (DMEV_ISSET(DM_EVENT_UNMOUNT, eventlist)) 
		printf("unmount ");
	if (DMEV_ISSET(DM_EVENT_DEBUT, eventlist)) 
		printf("debut ");
	if (DMEV_ISSET(DM_EVENT_CREATE, eventlist)) 
		printf("create ");
	if (DMEV_ISSET(DM_EVENT_POSTCREATE, eventlist)) 
		printf("postcreate ");
	if (DMEV_ISSET(DM_EVENT_REMOVE, eventlist)) 
		printf("remove ");
	if (DMEV_ISSET(DM_EVENT_POSTREMOVE, eventlist)) 
		printf("postmount ");
	if (DMEV_ISSET(DM_EVENT_RENAME, eventlist)) 
		printf("rename ");
	if (DMEV_ISSET(DM_EVENT_POSTRENAME, eventlist)) 
		printf("postrename ");
	if (DMEV_ISSET(DM_EVENT_LINK, eventlist)) 
		printf("link ");
	if (DMEV_ISSET(DM_EVENT_POSTLINK, eventlist)) 
		printf("postlink ");
	if (DMEV_ISSET(DM_EVENT_SYMLINK, eventlist)) 
		printf("symlink ");
	if (DMEV_ISSET(DM_EVENT_POSTSYMLINK, eventlist)) 
		printf("postsymlink ");
	if (DMEV_ISSET(DM_EVENT_READ, eventlist)) 
		printf("read ");
	if (DMEV_ISSET(DM_EVENT_WRITE, eventlist)) 
		printf("write ");
	if (DMEV_ISSET(DM_EVENT_TRUNCATE, eventlist)) 
		printf("truncate ");
	if (DMEV_ISSET(DM_EVENT_ATTRIBUTE, eventlist)) 
		printf("attribute ");
	if (DMEV_ISSET(DM_EVENT_DESTROY, eventlist)) 
		printf("destroy ");
	if (DMEV_ISSET(DM_EVENT_NOSPACE, eventlist)) 
		printf("nospace ");
	if (DMEV_ISSET(DM_EVENT_USER, eventlist)) 
		printf("user ");

	printf("\n");
	return(0);
}

/*
 * Print out the handle for a file
 */
int
handle_info(
	dm_sessid_t	 sid,
	void		*hanp,
	size_t		 hlen)
{
	printf("\n\tHandle (handle length, value) \n\t\t");
	print_handle(hanp, hlen);
	return(0);
}

/*
 * Get the allocation info for a file. We pick some small number
 * of extents to get the residency info on at one time
 */
int
alloc_info(
	dm_sessid_t	 sid,
	void		*hanp,
	size_t		 hlen)
{
	int		i, more;
	dm_off_t	offset;
	u_int		nextents, nret;
	dm_extent_t	ext[NUM_EXTENTS];


	nextents = NUM_EXTENTS;
	offset   = 0;
	printf("\n\tAllocation info \n");
	do {
		more = dm_get_allocinfo(sid, hanp, hlen, DM_NO_TOKEN, &offset, 
					nextents, ext, &nret);
		if (more == -1) {
			errno_msg("Can't get alloc info for file");
			return(1);
		}
		for (i=0; i<nret; i++) {
			printf("\t\tExtent %d ", i);
			if (ext[i].ex_type == DM_EXTENT_RES)
				printf("(resident): ");
			if (ext[i].ex_type == DM_EXTENT_HOLE)
				printf("(hole): ");
			printf("offset %lld, ", ext[i].ex_offset);
			printf("len %lld\n", ext[i].ex_length);
		}
	} while (more == 1);
	return(0);
}
