/*
 * Copyright (c) 2000-2002 Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <xfs/xfs.h>
#include <xfs/jdm.h>

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <uuid/uuid.h>

#include "config.h"

#include "types.h"
#include "mlog.h"
#include "inv_priv.h"
#include "getopt.h"


/*----------------------------------------------------------------------*/
/* inventory_open                                                       */
/*                                                                      */
/* INV_BY_MOUNTPT, INV_BY_UUID or INV_BY_DEVPATH                        */
/*----------------------------------------------------------------------*/

inv_idbtoken_t
inv_open(inv_predicate_t bywhat, inv_oflag_t forwhat, void *pred)
{
	int fd, stobjfd, num, retval;
	inv_idbtoken_t tok = INV_TOKEN_NULL;
	invt_sescounter_t *sescnt = 0;

	int index = 0;

	assert (pred);
	fd = retval = init_idb (pred, bywhat, forwhat, &tok);

	if (retval == I_DONE)
		return tok;

	/* if we just want to search the db, all we need is the invidx.
	   at this point, we know that a tok wasnt created in init_idb() */
	if (forwhat == INV_SEARCH_ONLY) {
		/* fd == I_EMPTYINV or fd == valid fd */
		tok = get_token(fd, -1);
		tok->d_oflag = forwhat;
		return tok;
	}

	/* XXX also, see if it is too full. if so, make another and leave a
	   reference to the new file in the old one */

	stobjfd = idx_get_stobj(fd, forwhat, &index);
	if (stobjfd < 0) {
		close(fd);
		return INV_TOKEN_NULL;
	}

	assert (index > 0);

	/* Now we need to make sure that this has enough space */
	INVLOCK(stobjfd, LOCK_SH);

	num = GET_SESCOUNTERS(stobjfd, &sescnt);
	if (num < 0) {
		close(fd);
		INVLOCK(stobjfd, LOCK_UN);
		close(stobjfd);
		return INV_TOKEN_NULL;
	}

	/* create another storage object (and, an inv_index entry for it
	   too) if we've filled this one up */

	if ((uint) num >= sescnt->ic_maxnum) {
		mlog(MLOG_DEBUG | MLOG_INV, "$ INV: creating a new storage obj & "
		      "index entry. \n");
		INVLOCK(stobjfd, LOCK_UN);
		close (stobjfd);

		INVLOCK(fd, LOCK_EX);
		stobjfd = idx_create_entry(&tok, fd, BOOL_FALSE);
		INVLOCK(fd, LOCK_UN);

		free (sescnt);
		if (stobjfd < 0) {
			close(fd);
			return INV_TOKEN_NULL;
		}
		return tok;
	}

	INVLOCK(stobjfd, LOCK_UN);

	free (sescnt);
	tok = get_token(fd, stobjfd);
	tok->d_invindex_off = IDX_HDR_OFFSET(index - 1);
	tok->d_oflag = forwhat;
	return tok;

}




/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/


bool_t
inv_close(inv_idbtoken_t tok)
{
	close (tok->d_invindex_fd);
	if (tok->d_stobj_fd >= 0)
		close (tok->d_stobj_fd);
	destroy_token(tok);
	return BOOL_TRUE;
}







/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/

inv_sestoken_t
inv_writesession_open(
	inv_idbtoken_t tok, 	/* token obtained by inventory_open() */
	uuid_t		*fsid,
	uuid_t		*sesid,
	char		*label,
	bool_t		ispartial,
	bool_t		isresumed,
	u_char		level,
	uint		nstreams,
	time32_t	time,
	char		*mntpt,
	char		*devpath)
{
	invt_session_t  ses;
	int		fd;
	int	rval;
	invt_sescounter_t *sescnt = NULL;
	invt_seshdr_t  	hdr;
	inv_sestoken_t	sestok;
	inv_oflag_t     forwhat;

	assert (tok != INV_TOKEN_NULL);
	assert (sesid && fsid && mntpt && devpath);
	forwhat = tok->d_oflag;
	fd = tok->d_stobj_fd;
	assert (forwhat != INV_SEARCH_ONLY);
	assert (fd > 0);

	if (!(tok->d_update_flag & FSTAB_UPDATED)) {
		if (fstab_put_entry(fsid, mntpt, devpath, forwhat) < 0) {
		       mlog(MLOG_NORMAL | MLOG_INV, _(
				"INV: put_fstab_entry failed.\n"));
		       return INV_TOKEN_NULL;
		}
		tok->d_update_flag |= FSTAB_UPDATED;
	}


	/* copy the session information to store */
	memset((void *)&ses, 0, sizeof(ses));	/* paranoia */
	memcpy(&ses.s_sesid, sesid, sizeof(uuid_t));
	memcpy(&ses.s_fsid, fsid, sizeof(uuid_t));
	strcpy(ses.s_label, label);
	strcpy(ses.s_mountpt, mntpt);
	strcpy(ses.s_devpath, devpath);
	ses.s_max_nstreams = nstreams;

        hdr.sh_pruned = 0; /* session is not pruned by invutil */
	hdr.sh_time = time;
	hdr.sh_level = level;
	hdr.sh_flag = (ispartial) ? INVT_PARTIAL: 0;
	hdr.sh_flag |= (isresumed) ? INVT_RESUMED : 0;
	/* sh_streams_off and sh_sess_off will be set in create_session() */

	sestok = get_sesstoken(tok);

	/* we need to put the new session in the appropriate place in
	   storage object. So first find out howmany sessions are there */

	INVLOCK(fd, LOCK_EX);
	if (GET_SESCOUNTERS(fd, &sescnt) < 0) {
		free (sestok);
		INVLOCK(fd, LOCK_UN);
		return INV_TOKEN_NULL;
	}

	/* create the writesession, and get ready for the streams to come
	   afterwards */
	rval = stobj_create_session(sestok, fd, sescnt, &ses, &hdr);
	assert (rval > 0);


	INVLOCK(fd, LOCK_UN);

	sestok->sd_sesstime = time;

	if (tok->d_update_flag & NEW_INVINDEX) {
		if (idx_put_sesstime(sestok, INVT_STARTTIME) < 0) {
			mlog(MLOG_NORMAL | MLOG_INV, _(
				"INV: put_starttime failed.\n"));
			return INV_TOKEN_NULL;
		}
		tok->d_update_flag &= ~(NEW_INVINDEX);
	}

	free (sescnt);


	return (rval < 0)? INV_TOKEN_NULL: sestok;
}





/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/


bool_t
inv_writesession_close(inv_sestoken_t tok)
{
	int		rval;

	assert (tok != INV_TOKEN_NULL);

	/* now update end_time in the inv index header */
	rval = idx_put_sesstime(tok, INVT_ENDTIME);

	memset(tok, 0, sizeof(invt_sesdesc_entry_t));
	free (tok);

	return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;

}



/*----------------------------------------------------------------------*/
/* inventory_stream_open                                                */
/*                                                                      */
/* Opens a stream for mediafiles to be put in.                          */
/*----------------------------------------------------------------------*/
inv_stmtoken_t
inv_stream_open(
	inv_sestoken_t tok,
	char		*cmdarg)
{
	inv_stmtoken_t stok;
	invt_stream_t  stream;
	invt_session_t ses;
	invt_seshdr_t  seshdr;
	int fd;
	bool_t err = BOOL_FALSE;

	assert (tok != INV_TOKEN_NULL);

	/* this memset is needed as a dump interrupted/crashed very soon
	 * after starting results in an inventory with exteremely large
	 * starting/ending inodes or offsets. This can be misleading.
	 * See bug #463702 for an example.
	 */
	memset((void *)&stream, 0, sizeof(invt_stream_t));

	stream.st_nmediafiles = 0;
	stream.st_interrupted = BOOL_TRUE; /* fix for 353197 */
	strcpy(stream.st_cmdarg, cmdarg);

	/* XXX yukk... make the token descriptors not pointers */
	stok = (inv_stmtoken_t) malloc(sizeof(invt_strdesc_entry_t));

	stok->md_sesstok = tok;
	stok->md_lastmfile = 0;

	/* get the session to find out where the stream is going to go */
	fd = tok->sd_invtok->d_stobj_fd;

	INVLOCK(fd, LOCK_EX);

	/* get the session header and the session */
	if (stobj_get_sessinfo(tok, &seshdr, &ses) <= 0)
		err = BOOL_TRUE;

	if ((!err)  && ses.s_cur_nstreams < ses.s_max_nstreams) {
		/* this is where this stream header will be written to */
		stok->md_stream_off = (off64_t) (sizeof(invt_stream_t) *
					         ses.s_cur_nstreams)
			                         + seshdr.sh_streams_off;
		ses.s_cur_nstreams++;

		/* write it back. */
		if (PUT_REC_NOLOCK(fd, &ses, sizeof(ses),
				     tok->sd_session_off) < 0)
			err = BOOL_TRUE;
	} else if (!err) {
		mlog (MLOG_NORMAL, _(
		       "INV: cant create more than %d streams."
		       " Max'd out..\n"), ses.s_cur_nstreams);
		err = BOOL_TRUE;
	}

	if (!err) {
		stream.st_firstmfile = stream.st_lastmfile =
			               stok->md_stream_off;

		/* now write the stream header on to the disk */
		if (PUT_REC_NOLOCK(fd, &stream, sizeof(invt_stream_t),
				    stok->md_stream_off) > 0) {
			/* we're all set */
			INVLOCK(fd, LOCK_UN);
			return stok;
		}
	}

	/* error occured somewhere */
	free (stok);
	INVLOCK(fd, LOCK_UN);
	return INV_TOKEN_NULL;

}




/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/

bool_t
inv_stream_close(
		inv_stmtoken_t	tok,
		bool_t wasinterrupted)
{
	invt_stream_t strm;
	int fd = tok->md_sesstok->sd_invtok->d_stobj_fd;
	int rval;
	bool_t dowrite = BOOL_FALSE;

	rval = idx_put_sesstime(tok->md_sesstok, INVT_ENDTIME);
	if (rval < 0)
		mlog(MLOG_NORMAL | MLOG_INV, _(
		      "INV: idx_put_sesstime failed in "
		      "inv_stream_close() \n"));
	INVLOCK(fd, LOCK_EX);
	if ((rval = GET_REC_NOLOCK(fd, &strm, sizeof(invt_stream_t),
			       tok->md_stream_off)) > 0) {

		if (strm.st_interrupted != wasinterrupted) {
			strm.st_interrupted = wasinterrupted;
			dowrite = BOOL_TRUE;
		}

		/* get the last media file to figure out what our last
		   ino was. we have a pointer to that in the stream token */
		if (tok->md_lastmfile){
			if (strm.st_endino.ino !=
			      tok->md_lastmfile->mf_endino.ino ||
			     strm.st_endino.offset !=
			      tok->md_lastmfile->mf_endino.offset) {

			      mlog(MLOG_DEBUG | MLOG_INV, "INV: stream_close() "
				    " - endinos dont match ! \n");
			      dowrite = BOOL_TRUE;
			      strm.st_endino = tok->md_lastmfile->mf_endino;
			}
		}

		if (dowrite) {
			rval = PUT_REC_NOLOCK(fd, &strm, sizeof(invt_stream_t),
					      tok->md_stream_off);
		}
	}

	INVLOCK(fd, LOCK_UN);

	if (tok->md_lastmfile) {
		free (tok->md_lastmfile);
	}
	memset(tok, 0, sizeof(invt_strdesc_entry_t));
	free (tok);

	return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;
}




/*----------------------------------------------------------------------*/
/*                                                                      */
/*                                                                      */
/*                                                                      */
/*----------------------------------------------------------------------*/

bool_t
inv_put_mediafile(
	inv_stmtoken_t 	tok,
	uuid_t 		*moid,
	char 		*label,
	uint		mfileindex,
	xfs_ino_t	startino,
	off64_t		startino_offset,
	xfs_ino_t	endino,
	off64_t		endino_offset,
	off64_t		size,
	bool_t          isgood,
	bool_t		isinvdump)
{
	invt_mediafile_t *mf;
	int 		 rval;


	assert (tok != INV_TOKEN_NULL);
	assert (tok->md_sesstok->sd_invtok->d_update_flag & FSTAB_UPDATED);
	assert (tok->md_sesstok->sd_invtok->d_stobj_fd >= 0);

	mf = (invt_mediafile_t *) calloc(1, sizeof(invt_mediafile_t));

	/* copy the media file information */
	memcpy(&mf->mf_moid, moid, sizeof(uuid_t));
	strcpy(mf->mf_label, label);
	mf->mf_mfileidx = mfileindex;
	mf->mf_startino.ino = startino;
	mf->mf_startino.offset = startino_offset;
	mf->mf_endino.ino = endino;
	mf->mf_endino.offset = endino_offset;
	mf->mf_size = size;
	mf->mf_flag = 0;
	if (isgood)
		mf->mf_flag |= INVT_MFILE_GOOD;

	/* This flag is used to indicate the media file that contains the
	   dump of the sessioninfo structure that contains all but this
	   media file */
	if (isinvdump)
		mf->mf_flag |= INVT_MFILE_INVDUMP;

	INVLOCK(tok->md_sesstok->sd_invtok->d_stobj_fd, LOCK_EX);
	rval = stobj_put_mediafile(tok, mf);
	INVLOCK(tok->md_sesstok->sd_invtok->d_stobj_fd, LOCK_UN);

	/* we dont free the mfile here. we always keep the last mfile
	   around, inside the inv_stmtoken, and when we add a new mfile,
	   we free the previous one. The last one is freed in stream_close()
	   */

	return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;

}





/*----------------------------------------------------------------------*/
/* inv_get_sessioninfo                                                  */
/*                                                                      */
/* This is to be called after a write-session is complete, but before it*/
/* is closed. ie. the token must still be valid, and all the streams    */
/* and their mediafiles put in the inventory.                           */
/*                                                                      */
/* On return, the buffer will be filled with all the data pertinent to  */
/* the session referred to by the session token. The application of this*/
/* is to dump the inventory of a session to a media object.             */
/*----------------------------------------------------------------------*/

bool_t
inv_get_sessioninfo(
	inv_sestoken_t		tok,
	void		      **bufpp,	/* buf to fill */
	size_t		       *bufszp)/* size of that buffer */
{
	invt_session_t 	ses;
	invt_seshdr_t	hdr;
	bool_t          rval;
	int		fd;


	assert(tok != INV_TOKEN_NULL);
	assert(tok->sd_invtok);
	*bufpp = NULL;
	*bufszp = 0;
	fd = tok->sd_invtok->d_stobj_fd;

	INVLOCK(fd, LOCK_SH);

	/* Next we get the session header, and the session information. Then
	   we can figure out how much space to allocate */
	if (stobj_get_sessinfo(tok, &hdr, &ses) <= 0) {
		INVLOCK(fd, LOCK_UN);
		return BOOL_FALSE;
	}

	rval = stobj_pack_sessinfo(fd, &ses, &hdr, bufpp, bufszp);
	INVLOCK(fd, LOCK_UN);


	return rval;
}




/*----------------------------------------------------------------------*/
/* inv_put_sessioninfo                                                  */
/*                                                                      */
/* This is used in reconstructing an inventory from dumped media objects*/
/*                                                                      */
/* Most importantly, note that this is not in anyway an alternative to  */
/* inv_open_writesession() - that is called while the dump is in progr- */
/* ess. This is really inv_put_dumpedsession(); We just didn't want to  */
/* be *that* dump-specific :)                                           */
/*----------------------------------------------------------------------*/

bool_t
inv_put_sessioninfo(invt_sessinfo_t *s)
{
	static bool_t invdir_ok = BOOL_FALSE;

	if (!invdir_ok) {
		if (make_invdirectory(INV_SEARCH_N_MOD) < 0)
			return BOOL_FALSE;
		else
			invdir_ok = BOOL_TRUE;
	}

      	return insert_session(s);

}




/*----------------------------------------------------------------------*/
/* inv_free_session							*/
/* 									*/
/* free the inv_session structure allocated as a result of calls to     */
/* inv_get_session_byuuid, etc.						*/
/*----------------------------------------------------------------------*/
void
inv_free_session(
	inv_session_t **ses)
{
	uint i;

	assert(ses);
	assert(*ses);

	for (i = 0; i < (*ses)->s_nstreams; i++) {
		/* the array of mediafiles is contiguous */
		free ((*ses)->s_streams[i].st_mediafiles);
	}

	/* all streams are contiguous too */
	free ((*ses)->s_streams);

	free (*ses);
	*ses = NULL;
}


/*----------------------------------------------------------------------*/
/* inv_lasttime_level_lessthan						*/
/*									*/
/* Given a file system uuid, token that refers to a file system, and a	*/
/* level, tm is populated with last time when a session of a lesser	*/
/* level was done.							*/
/*									*/
/* Returns TRUE on success.						*/
/*----------------------------------------------------------------------*/

bool_t
inv_lasttime_level_lessthan(
	uuid_t		*fsidp,
	inv_idbtoken_t	tok,
	u_char		level,
	time32_t	**tm)
{
	int 	rval;
	if (tok != INV_TOKEN_NULL) {
		rval =  search_invt(fsidp, tok->d_invindex_fd, &level,
				    (void **)tm,
				    (search_callback_t)tm_level_lessthan);

		return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;
	}

	return invmgr_query_all_sessions(fsidp,		 /* fs uuid ptr */
					 (void *)&level, /* in */
					 (void **)tm,	 /* out */
			       (search_callback_t)tm_level_lessthan);
}

/*----------------------------------------------------------------------*/
/* inv_lastsession_level_lessthan					*/
/*									*/
/* Given a file system uuid, token that refers to a file system, and a	*/
/* level, ses is populated with a session of lesser than the level	*/
/* passed in.								*/
/*									*/
/* Returns FALSE on an error, TRUE if not. If (*ses) is NULL, then the	*/
/* search failed.                                                       */
/*----------------------------------------------------------------------*/

bool_t
inv_lastsession_level_lessthan(
	uuid_t		*fsidp,
	inv_idbtoken_t	tok,
	u_char		level,
	inv_session_t	**ses)
{
	int 	rval;
	if (tok != INV_TOKEN_NULL) {
		rval = search_invt(fsidp, tok->d_invindex_fd, &level,
				   (void **)ses,
				   (search_callback_t)lastsess_level_lessthan);

		return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;
	}

	return invmgr_query_all_sessions(fsidp,		 /* fs uuid */
					 (void *)&level, /* in */
					 (void **)ses,	 /* out */
			       (search_callback_t)lastsess_level_lessthan);

}

/*----------------------------------------------------------------------*/
/* inv_lastsession_level_equalto					*/
/*									*/
/* Given a file system uuid, token that refers to a file system, and a	*/
/* level, this populates ses with last time when a session of a lesser	*/
/* level was done.							*/
/*									*/
/* Return FALSE on an error, TRUE if not. If (*ses) is NULL, then the   */
/* search failed.                                                       */
/*----------------------------------------------------------------------*/


bool_t
inv_lastsession_level_equalto(
	uuid_t		*fsidp,
	inv_idbtoken_t	tok,
	u_char		level,
	inv_session_t	**ses)
{
	int 	rval;
	if (tok != INV_TOKEN_NULL) {
		rval = search_invt(fsidp, tok->d_invindex_fd, &level,
				   (void **)ses,
				   (search_callback_t)lastsess_level_equalto);

		return (rval < 0) ? BOOL_FALSE: BOOL_TRUE;
	}

	return invmgr_query_all_sessions(fsidp,		 /* fs uuid */
					 (void *)&level, /* in */
					 (void **)ses,	 /* out */
			       (search_callback_t)lastsess_level_equalto);

}


/*----------------------------------------------------------------------*/
/* inv_getsession_byuuid                                                */
/*                                                                      */
/* Given a file system uuid and a session uuid, ses is populated with	*/
/* the session that contains the matching system uuid.			*/
/*									*/
/* Returns FALSE on an error, TRUE if the session was found.		*/
/*----------------------------------------------------------------------*/

bool_t
inv_get_session_byuuid(
	uuid_t		*fsidp,
	uuid_t		*sesid,
	inv_session_t	**ses)
{

	return invmgr_query_all_sessions(fsidp,		/* fs uuid */
					 (void *)sesid, /* in */
					 (void **)ses,	/* out */
			       (search_callback_t)stobj_getsession_byuuid);
}

/*----------------------------------------------------------------------*/
/* inv_getsession_bylabel						*/
/*                                                                      */
/* Given a file system uuid and a session uuid, ses is populated with	*/
/* the session that contains the matching system label.			*/
/*									*/
/* Returns FALSE on an error, TRUE if the session was found.		*/
/*----------------------------------------------------------------------*/

bool_t
inv_get_session_bylabel(
	uuid_t		*fsidp,
	char		*session_label,
	inv_session_t	**ses)
{

	return invmgr_query_all_sessions(fsidp,			/* fs uuid */
					 (void *)session_label, /* in */
					 (void **)ses,		/* out */
			       (search_callback_t)stobj_getsession_bylabel);
}


/*----------------------------------------------------------------------*/
/* inv_delete_mediaobj                                                  */
/*                                                                      */
/* We delete all the media files that are stored in the given mobj, and */
/* delete them one by one. We delete the session information only when  */
/* all its mediafiles are deleted. This is because a single session can */
/* be striped across multiple mediaobjects.                             */
/*----------------------------------------------------------------------*/

bool_t
inv_delete_mediaobj(uuid_t *moid)
{
	inv_oflag_t forwhat = INV_SEARCH_N_MOD;

	/* forall fsids (fs) in fstab {
	       open invindex table (t)
	       forall indices (i) in t {
	          open stobj (st)
		  forall sessions (s) in st {
		      forall streams (strm) in s {
		         forall mediafiles (m) in strm {
			     if (m.mediaobj == moid) {
			     // delete m
			     if (--strm.nmediafiles == 0)
			        if (--s.nstreams == 0)
			            delete-session (s)
			     }
			 }
		      }
		 }
	      }
	*/

	invt_counter_t *cnt;
	invt_fstab_t *arr;
	int numfs, i, fd, invfd;
	char fname[INV_STRLEN];

	fd = fstab_getall(&arr, &cnt, &numfs, forwhat);
	if (fd < 0 || numfs <= 0) {
		mlog(MLOG_NORMAL | MLOG_INV, _("INV: Error in fstab\n"));
		return BOOL_FALSE;
	}

	close(fd);

	for (i = 0; i < numfs; i++) {
		if (fstab_get_fname(&arr[i].ft_uuid,
				      fname,
				      (inv_predicate_t)INV_BY_UUID,
				      forwhat
				     )
		     < 0) {
			mlog(MLOG_NORMAL | MLOG_INV, _(
			      "INV: Cant get inv-name for uuid\n"));
			return BOOL_FALSE;
		}
		strcat(fname, INV_INVINDEX_PREFIX);
		invfd = open(fname, INV_OFLAG(forwhat));
		if (invfd < 0) {
			mlog(MLOG_NORMAL | MLOG_INV, _(
			     "INV: Open failed on %s\n"),
			     fname);
			return BOOL_FALSE;
		}

		if (search_invt(&arr[i].ft_uuid, invfd, NULL, (void **)&moid,
				(search_callback_t)stobj_delete_mobj)
		    < 0)
			return BOOL_FALSE;
		/* we have to delete the session, etc */
		close(invfd);
	}

	return BOOL_TRUE;
}



#define I_IFOUND	0x01
#define I_IDONE		0x02
#define I_IERR		0x04


static const char *myopts[] = {
#define OPT_MNT		0
	"mnt",

#define OPT_FSID	1
	"fsid",

#define OPT_DEV		2
	"dev",

#define OPT_DEPTH	3
	"depth",

#define OPT_MOBJID	4
	"mobjid",

#define OPT_MOBJLABEL	5
	"mobjlabel",

#define OPT_FSTAB	6
	"fstab",

#define OPT_INVIDX	7
	"invidx",

#define OPT_INVCHECK	8
	"check",

#define OPT_INVLEVEL	9
	"level",

	NULL
};


int
inv_getopt(int argc, char **argv, invt_pr_ctx_t *prctx)
{
	int rval = 0;
	void *fs = 0;
	char *options, *value;
	extern char *optarg;
	extern int optind, opterr;

	inv_predicate_t bywhat = -1;
	int c, d;
	uuid_t fsid;
	int npreds = 0;
	int npreds2 = 0;
	char invoptstring[128], *rptr, *wptr;
	optind = 1;
	opterr = 0;

	/*
	 * getopt doesn't handle both '-I' and '-I w/subopts' so...
	 * First set I_IFOUND if -I is set at all (with or without
	 * any suboptions).  Do this by taking out the ':' so getopt
	 * accepts it.  Later, reset opts and go through again to
	 * pick off any subopts.
	 */
	strcpy(invoptstring, GETOPT_CMDSTRING);
	wptr = strchr(invoptstring, 'I');
	if (wptr != NULL) {
		wptr++;
		rptr = wptr + 1;
		while (*wptr != '\0')
			*wptr++ = *rptr++;
		while ((c = getopt(argc, argv, invoptstring)) != EOF) {
			switch (c) {
			case GETOPT_INVPRINT:
				prctx->depth = 0;
				rval |= I_IFOUND;
				break;
			}
		}
		optind = 1;
		opterr = 0;
		optarg = NULL;
	}

	while ((c = getopt(argc, argv, GETOPT_CMDSTRING)) != EOF) {
		switch (c) {
		case GETOPT_INVPRINT:
			rval |= I_IFOUND;
			if ((options = optarg) == NULL)
				break;

			while (*options != '\0') {
				d = getsubopt(&options,(constpp)myopts,&value);
				if (value == NULL && d != OPT_FSTAB &&
				     d != OPT_INVIDX && d != OPT_INVCHECK)
					continue;
				switch(d) {
					/* process mntpt option */
				      case OPT_MNT:
					bywhat = (inv_predicate_t) INV_BY_MOUNTPT;
					fs = value;
					npreds++;
					break;

					/* process fsid option */
				      case OPT_FSID:
					bywhat = (inv_predicate_t) INV_BY_UUID;
					npreds++;

					uuid_parse(value, fsid);
					fs = fsid;
					break;

				      case OPT_DEV: /* process dev option */
					bywhat = (inv_predicate_t) INV_BY_DEVPATH;
					fs = value;
					npreds++;
					break;

				      case OPT_DEPTH:
					prctx->depth = atoi(value);
					if (prctx->depth < 0 ||
					    prctx->depth > 4)
						prctx->depth = 0;
					break;

				      case OPT_MOBJID:
					{
					uuid_t *u;
					u = malloc (sizeof(uuid_t));
					uuid_parse(value, *u);
					prctx->mobj.type = INVT_MOID;
					prctx->mobj.value = (void *)u;
				        }
					npreds2++;
					break;

				      case OPT_MOBJLABEL:
					prctx->mobj.type = INVT_LABEL;
					prctx->mobj.value = (void *)value;
					npreds2++;
					break;

				      case OPT_FSTAB:
					prctx->fstab = BOOL_TRUE;
					break;

				      case OPT_INVIDX:
					prctx->invidx = BOOL_TRUE;
					break;

				      case OPT_INVCHECK:
					prctx->invcheck = BOOL_TRUE;
					break;

				      case OPT_INVLEVEL:
					prctx->level = atoi(value);
					break;

				      default:
					if (strlen(value) == 1 &&
					     atoi(value) < PR_MAXDEPTH)
						prctx->depth = atoi(value);
					else {
						mlog(MLOG_NORMAL | MLOG_INV, _(
					       "INV: invalid sub-option %s"
					       " for -I option\n"), value);
						rval |= I_IERR;
					}
					break;
				}
			}
			break; /* case GETOPT_INVPRINT */
		}

	}

	if (npreds > 1) {
		mlog(MLOG_NORMAL | MLOG_INV, _(
		     "INV: Only one of mnt=,dev= and fsid=value can be used.\n")
		     );
		rval |= I_IERR;
	}
	else if (npreds2 > 1) {
		mlog(MLOG_NORMAL | MLOG_INV, _(
		     "INV: Only one of mobjid= and mobjlabel= can be used.\n")
		     );
		rval |= I_IERR;
	}
	else if ((rval & I_IFOUND) && !(rval & I_IERR) && fs
		 && !prctx->fstab && !prctx->invcheck) {
		inv_idbtoken_t tok;

		/* A filesystem could be backed up, mkfs'ed then restored
		 * to a new UUID value.  Therefore, we can't simply stop
		 * when we find the first matching mount point (pv564234).
		 * This code loops through all filesystems and does the
		 * comparison by hand. */
		if (bywhat == INV_BY_MOUNTPT) {
			int fd, numfs, i;
			invt_fstab_t *arr = NULL;
			invt_counter_t *cnt = NULL;
			inv_oflag_t forwhat = INV_SEARCH_ONLY;

			fd = fstab_getall(&arr, &cnt, &numfs, forwhat);
			free(cnt);

			rval |= I_IERR; /* Cleared if successful */

			if (fd >= 0) {
				for (i = 0; i < numfs; i++) {
					tok = inv_open(
						(inv_predicate_t)INV_BY_UUID,
						INV_SEARCH_ONLY,
						&arr[i].ft_uuid);
					if (tok == INV_TOKEN_NULL)
						break;
					if (STREQL(arr[i].ft_mountpt, fs)) {
						prctx->index = i;
						invmgr_inv_print(
						          tok->d_invindex_fd,
							  prctx);
						rval &= ~(I_IERR);
					}
					inv_close(tok);
				}
				free (arr);
				rval |= I_IDONE;
			}
			if ((rval&I_IERR)) {
				mlog(MLOG_NORMAL | MLOG_INV, _(
				    "INV: open failed on mount point \"%s\"\n"),
				     fs);
			}
			return rval;
		}

		/* We have to print only one file system to print by UUID */
		tok = inv_open(bywhat, INV_SEARCH_ONLY, fs);
		if (tok != INV_TOKEN_NULL) {
			prctx->index = 0;
			invmgr_inv_print(tok->d_invindex_fd, prctx);
			inv_close(tok);
			rval |= I_IDONE;
		} else {
			mlog(MLOG_NORMAL | MLOG_INV, _(
			     "INV: open failed on file system id \"%s\"\n"),
			     fs);
			rval |= I_IERR;
		}
	}

	return rval;
}

/* This prints out all the sessions of a filesystem that are in the inventory */
bool_t
inv_DEBUG_print(int argc, char **argv)
{
	invt_counter_t *cnt = NULL;
	invt_fstab_t *arr = NULL;
	int fd, numfs, i;
	inv_idbtoken_t tok;
	int rval;
	invt_pr_ctx_t prctx;
	inv_oflag_t forwhat = INV_SEARCH_ONLY;
	prctx.mobj.type = INVT_NULLTYPE;
	prctx.fstab = prctx.invidx = prctx.invcheck = BOOL_FALSE;
	prctx.level = PR_MAXLEVEL;

	/* If user didnt indicate -i option, we can't do anything */
	rval = inv_getopt(argc, argv, &prctx);

	if (!prctx.invcheck && !prctx.fstab) {
		if (!(rval & I_IFOUND)) {
			return BOOL_TRUE;
		} else if (rval & I_IERR || rval & I_IDONE) {
			return BOOL_FALSE;
		}
	}

	fd = fstab_getall(&arr, &cnt, &numfs, forwhat);
	free(cnt);

	if (fd >= 0) {
		 if (prctx.fstab) {
			 fstab_DEBUG_print(arr, numfs);
			 if (!prctx.invidx)
				 return BOOL_FALSE;
		 }

		for (i = 0; i < numfs; i++) {
			tok = inv_open((inv_predicate_t)INV_BY_UUID,
					forwhat,
				        &arr[i].ft_uuid);
			if (tok == INV_TOKEN_NULL) {
				free (arr);
				return BOOL_FALSE;
			}

			if (prctx.invcheck) {
				mlog(MLOG_VERBOSE | MLOG_INV, _(
				     "INV: checking fs \"%s\"\n"),
				     &arr[i].ft_mountpt
				     );
				invmgr_inv_check(tok->d_invindex_fd);
			}
			else {
				prctx.index = i;
				invmgr_inv_print(tok->d_invindex_fd,
						 &prctx);
			}
			inv_close(tok);
		}
	}

	return BOOL_FALSE;
}

#undef I_IFOUND
#undef I_IDONE
#undef I_IERR
