/*
** ATOP - System & Process Monitor
**
** The program 'atop' offers the possibility to view the activity of 
** the system on system-level as well as process-level.
** ==========================================================================
** Author:      Gerlof Langeveld
** E-mail:      gerlof.langeveld@atoptool.nl
** Date:        September 2002
** --------------------------------------------------------------------------
** Copyright (C) 2000-2010 Gerlof Langeveld
**
** 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; either version 2, or (at your option) any
** later version.
**
** This program is distributed in the hope that it will 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 to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** --------------------------------------------------------------------------
**
** $Log: rawlog.c,v $
** Revision 1.31  2010/11/17 12:43:31  gerlof
** The flag -r followed by exactly 8 'y' characters is not considered
** as 8 days ago, but as a literal filename.
**
** Revision 1.30  2010/10/23 14:03:03  gerlof
** Counters for total number of running and sleep threads (JC van Winkel).
**
** Revision 1.29  2010/04/23 13:55:52  gerlof
** Get rid of all setuid-root privs before activating other program.
**
** Revision 1.28  2010/04/23 12:19:35  gerlof
** Modified mail-address in header.
**
** Revision 1.27  2010/04/16 12:55:16  gerlof
** Automatically start another version of atop if the logfile to
** be read has not been created by the current version.
**
** Revision 1.26  2010/03/02 13:55:29  gerlof
** Struct stat size did not fit in short any more (modified to int).
**
** Revision 1.25  2009/12/17 08:16:06  gerlof
** Introduce branch-key to go to specific time in raw file.
**
** Revision 1.24  2009/11/27 15:26:29  gerlof
** Added possibility to specify y[y..] als filename for -r flag
** to access file of yesterday, day before yesterday, etc.
**
** Revision 1.23  2009/11/27 14:28:14  gerlof
** Rollback a "transaction" when not all parts could be
** written to the logfile (e.g. file system full) to avoid
** a corrupted logfile.
**
** Revision 1.22  2008/01/07 10:18:05  gerlof
** Implement possibility to make summaries.
**
** Revision 1.21  2007/08/16 12:01:25  gerlof
** Add support for atopsar reporting.
**
** Revision 1.20  2007/03/20 13:02:25  gerlof
** Introduction of variable supportflags.
**
** Revision 1.19  2007/03/20 11:11:57  gerlof
** Verify success of malloc's.
**
** Revision 1.18  2007/03/20 07:25:59  gerlof
** Avoid loop when incompatible raw file is read.
** Verify return code of compress/uncompress functions.
**
** Revision 1.17  2007/02/23 07:34:00  gerlof
** Changed unsigned short's into unsigned int's for process-counters
** in raw record.
**
** Revision 1.16  2007/02/13 10:33:27  gerlof
** Removal of external declarations.
** Store pagesize and hertz in raw logfile.
**
** Revision 1.15  2006/01/30 09:12:34  gerlof
** Minor bug-fix.
**
** Revision 1.14  2005/10/21 09:50:36  gerlof
** Per-user accumulation of resource consumption.
**
** Revision 1.13  2004/12/14 15:06:23  gerlof
** Implementation of patch-recognition for disk and network-statistics.
**
** Revision 1.12  2004/05/06 09:47:36  gerlof
** Ported to kernel-version 2.6.
**
** Revision 1.11  2003/07/07 09:26:48  gerlof
** Cleanup code (-Wall proof).
**
** Revision 1.10  2003/07/03 12:04:11  gerlof
** Implemented subcommand `r' (reset).
**
** Revision 1.9  2003/06/27 12:32:48  gerlof
** Removed rawlog compatibility.
**
** Revision 1.8  2003/02/06 14:08:33  gerlof
** Cosmetic changes.
**
** Revision 1.7  2003/01/17 07:33:39  gerlof
** Modified process statistics: add command-line.
** Implement compatibility for old pstat-structure read from logfiles.
**
** Revision 1.6  2002/10/30 13:46:11  gerlof
** Generate notification for statistics since boot.
** Adapt interval from ushort to ulong (bug-solution);
** this results in incompatible logfile but old logfiles can still be read.
**
** Revision 1.5  2002/10/24 12:22:25  gerlof
** In case of writing a raw logfile:
** If the logfile already exists, append new records to the existing file.
** If the logfile does not yet exist, create it.
**
** Revision 1.4  2002/10/08 11:35:22  gerlof
** Modified storage of raw filename.
**
** Revision 1.3  2002/10/03 10:41:51  gerlof
** Avoid end-less loop when using end-time specification (flag -e).
**
** Revision 1.2  2002/09/17 13:17:01  gerlof
** Allow key 'T' to be pressed to view previous sample in raw file.
**
**
*/

static const char rcsid[] = "$Id: rawlog.c,v 1.31 2010/11/17 12:43:31 gerlof Exp $";

#include <sys/types.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <string.h>
#include <regex.h>
#include <zlib.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#include "atop.h"
#include "showgeneric.h"
#include "photoproc.h"
#include "photosyst.h"

#define	BASEPATH	"/var/log/atop/"  

/*
** structure which describes the raw file contents
**
** layout raw file:    rawheader
**
**                     rawrecord                           \
**                     compressed system-level statistics   | sample 1
**                     compressed process-level statistics /
**
**                     rawrecord                           \
**                     compressed system-level statistics   | sample 2
**                     compressed process-level statistics /
**
** etcetera .....
*/
#define	MYMAGIC		(unsigned int) 0xfeedbeef

struct rawheader {
	unsigned int	magic;

	unsigned short	aversion;	/* creator atop version with MSB */
	unsigned short	future1;	/* can be reused 		 */
	unsigned short	future2;	/* can be reused 		 */
	unsigned short	rawheadlen;	/* length of struct rawheader    */
	unsigned short	rawreclen;	/* length of struct rawrecord    */
	unsigned short	hertz;		/* clock interrupts per second   */
	unsigned short	sfuture[6];	/* future use                    */
	unsigned int	sstatlen;	/* length of struct sstat        */
	unsigned int	pstatlen;	/* length of struct pstat        */
	struct utsname	utsname;	/* info about this system        */
	char		cfuture[8];	/* future use                    */

	unsigned int	pagesize;	/* size of memory page (bytes)   */
	int		supportflags;  	/* used features                 */
	int		osrel;		/* OS release number             */
	int		osvers;		/* OS version number             */
	int		ossub;		/* OS version subnumber          */
	int		ifuture[6];	/* future use                    */
};

struct rawrecord {
	time_t		curtime;	/* current time (epoch)         */

	unsigned short	flags;		/* various flags                */
	unsigned short	sfuture[3];	/* future use                   */

	unsigned int	scomplen;	/* length of compressed sstat   */
	unsigned int	pcomplen;	/* length of compressed pstat's */
	unsigned int	interval;	/* interval (number of seconds) */
	unsigned int	nlist;		/* number of processes in list  */
	unsigned int	npresent;	/* total number of processes    */
	unsigned int	nexit;		/* number of exited processes   */
	unsigned int    ntrun;		/* number of running  threads	*/
	unsigned int    ntslpi;		/* number of sleeping threads(S)*/
	unsigned int    ntslpu;		/* number of sleeping threads(D)*/
	unsigned int	nzombie;	/* number of zombie processes   */
	unsigned int	ifuture[6];	/* future use                   */
};

static int	getrawrec  (int, struct rawrecord *, int);
static int	getrawsstat(int, struct sstat *, int);
static int	getrawpstat(int, struct pstat *, int, int);
static int	rawwopen(void);
static int	lookslikedatetome(char *);
static void	testcompval(int, char *);
static void	try_other_version(int, int);

/*
** write a raw record to file
** (file is opened/created during the first call)
*/
char
rawwrite(time_t curtime, int numsecs, 
	 struct sstat *ss, struct pstat *ps,
	 int nlist, int npresent, int ntrun, int ntslpi, int ntslpu,
         int nzombie, int nexit, char flag)
{
	static int		rawfd = -1;
	struct rawrecord	rr;
	int			rv;
	struct stat		filestat;

	Byte			scompbuf[sizeof(struct sstat)], *pcompbuf;
	unsigned long		scomplen = sizeof scompbuf;
	unsigned long		pcomplen = sizeof(struct pstat) * nlist;

	/*
	** first call:
	**	take care that the log file is opened
	*/
	if (rawfd == -1)
		rawfd = rawwopen();

	/*
 	** register current size of file in order to "roll back"
	** writes that have been done while not *all* writes could
	** succeed, e.g. when file system full
	*/
	(void) fstat(rawfd, &filestat);

	/*
	** compress system- and process-level statistics
	*/
	rv = compress(scompbuf, &scomplen,
				(Byte *)ss, (unsigned long)sizeof *ss);

	testcompval(rv, "compress");

	if ( (pcompbuf = malloc(pcomplen)) == NULL)
	{
		perror("atop malloc");
		cleanstop(7);
	}

	rv = compress(pcompbuf, &pcomplen, (Byte *)ps, (unsigned long)pcomplen);

	testcompval(rv, "compress");

	/*
	** fill record header and write to file
	*/
	memset(&rr, 0, sizeof rr);

	rr.curtime	= curtime;
	rr.interval	= numsecs;
	rr.flags	= 0;
	rr.nlist	= nlist;
	rr.npresent	= npresent;
	rr.ntrun	= ntrun;
	rr.ntslpi	= ntslpi;
	rr.ntslpu	= ntslpu;
	rr.nzombie	= nzombie;
	rr.nexit	= nexit;
	rr.scomplen	= scomplen;
	rr.pcomplen	= pcomplen;

	if (flag&RRBOOT)
		rr.flags |= RRBOOT;

	if ( write(rawfd, &rr, sizeof rr) == -1)
	{
		fprintf(stderr, "%s - ", rawname);
		perror("write raw record");
		(void) ftruncate(rawfd, filestat.st_size);
		cleanstop(7);
	}

	/*
	** write compressed system status structure to file
	*/
	if ( write(rawfd, scompbuf, scomplen) == -1)
	{
		fprintf(stderr, "%s - ", rawname);
		perror("write raw status record");
		(void) ftruncate(rawfd, filestat.st_size);
		cleanstop(7);
	}

	/*
	** write compressed list of process status structures to file
	*/
	if ( write(rawfd, pcompbuf, pcomplen) == -1)
	{
		fprintf(stderr, "%s - ", rawname);
		perror("write raw process record");
		(void) ftruncate(rawfd, filestat.st_size);
		cleanstop(7);
	}

	free(pcompbuf);

	return '\0';
}


/*
** open a raw file for writing
**
** if the raw file exists already:
**    - read and validate the header record (be sure it is an atop-file)
**    - seek to the end of the file
**
** if the raw file does not yet exist:
**    - create the raw file
**    - write a header record
**
** return the filedescriptor of the raw file
*/
static int
rawwopen()
{
	struct rawheader	rh;
	int			fd;

	/*
	** check if the file exists already
	*/
	if ( (fd = open(rawname, O_RDWR)) >= 0)
	{
		/*
		** read and verify header record
		*/
		if ( read(fd, &rh, sizeof rh) < sizeof rh)
		{
			fprintf(stderr, "%s - cannot read header\n", rawname);
			cleanstop(7);
		}

		if (rh.magic != MYMAGIC)
		{
			fprintf(stderr,
				"file %s exists but does not contain raw "
				"atop output (wrong magic number)\n", rawname);

			cleanstop(7);
		}

		if ( rh.sstatlen	!= sizeof(struct sstat)		||
		     rh.pstatlen	!= sizeof(struct pstat)		||
	    	     rh.rawheadlen	!= sizeof(struct rawheader)	||
		     rh.rawreclen	!= sizeof(struct rawrecord)	||
		     rh.supportflags	!= supportflags			  )
		{
			fprintf(stderr,
				"%s exists but has incompatible header\n",
				rawname);

			if (rh.aversion & 0x8000)
			{
				fprintf(stderr,
					"(created by version %d.%d - "
					"current version %d.%d)\n",
					(rh.aversion >> 8) & 0x7f,
					 rh.aversion & 0xff,
					 getnumvers() >> 8,
					 getnumvers() & 0x7f);
			}

			cleanstop(7);
		}

		(void) lseek(fd, (off_t) 0, SEEK_END);

		return fd;
	}

	/*
	** file does not exist (or can not be opened)
	*/
	if ( (fd = creat(rawname, 0666)) == -1)
	{
		fprintf(stderr, "%s - ", rawname);
		perror("create raw file");
		cleanstop(7);
	}

	memset(&rh, 0, sizeof rh);

	rh.magic	= MYMAGIC;
	rh.aversion	= getnumvers() | 0x8000;
	rh.sstatlen	= sizeof(struct sstat);
	rh.pstatlen	= sizeof(struct pstat);
	rh.rawheadlen	= sizeof(struct rawheader);
	rh.rawreclen	= sizeof(struct rawrecord);
	rh.supportflags	= supportflags;
	rh.osrel	= osrel;
	rh.osvers	= osvers;
	rh.ossub	= ossub;
	rh.hertz	= hertz;
	rh.pagesize	= pagesize;

	memcpy(&rh.utsname, &utsname, sizeof rh.utsname);

	if ( write(fd, &rh, sizeof rh) == -1)
	{
		fprintf(stderr, "%s - ", rawname);
		perror("write raw header");
		cleanstop(7);
	}

	return fd;
}

/*
** read the contents of a raw file
*/
#define	OFFCHUNK	256

void
rawread(void)
{
	int			rawfd, len;
	char			*py;
	struct rawheader	rh;
	struct rawrecord	rr;
	struct sstat		devsstat;
	struct pstat		*devpstat;

	struct stat		filestat;

	/*
	** variables to maintain the offsets of the raw records
	** to be able to see previous samples again
	*/
	off_t			*offlist;
	unsigned int		offsize = 0;
	unsigned int		offcur  = 0;
	char			lastcmd = 'X', flags;

	time_t			timenow;
	struct tm		*tp;

	switch ( len = strlen(rawname) )
	{
	   /*
	   ** if no filename is specified, assemble the name of the raw file
	   */
	   case 0:
		timenow	= time(0);
		tp	= localtime(&timenow);

		snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d",
			BASEPATH, 
			tp->tm_year+1900,
			tp->tm_mon+1,
			tp->tm_mday);

		break;

	   /*
	   ** if date specified as filename in format YYYYMMDD, assemble
	   ** the full pathname of the raw file
	   */
	   case 8:
		if ( access(rawname, F_OK) == 0) 
			break;		/* existing file */

		if (lookslikedatetome(rawname))
		{
			char	savedname[RAWNAMESZ];

			strncpy(savedname, rawname, RAWNAMESZ-1);

			snprintf(rawname, RAWNAMESZ, "%s/atop_%s",
				BASEPATH, 
				savedname);
			break;
		}

	   /*
	   ** if one or more 'y' (yesterday) characters are used and that
	   ** string is not known as an existing file, the standard logfile
	   ** is shown from N days ago (N is determined by the number
	   ** of y's).
	   */
	   default:
		if ( access(rawname, F_OK) == 0) 
			break;		/* existing file */

		/*
		** make a string existing of y's to compare with
		*/
		py = malloc(len+1);
		memset(py, 'y', len);
		*(py+len) = '\0';

		if ( strcmp(rawname, py) == 0 )
		{
			timenow	 = time(0);
			timenow -= len*3600*24;
			tp	 = localtime(&timenow);

			snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d",
				BASEPATH, 
				tp->tm_year+1900,
				tp->tm_mon+1,
				tp->tm_mday);
		}

		free(py);
	}

	/*
	** open raw file
	*/
	if ( (rawfd = open(rawname, O_RDONLY)) == -1)
	{
		char	command[512], tmpname1[RAWNAMESZ], tmpname2[RAWNAMESZ];

		/*
		** check if a compressed raw file is present
		*/
		snprintf(tmpname1, sizeof tmpname1, "%s.gz", rawname);

		if ( access(tmpname1, F_OK|R_OK) == -1)
		{
			fprintf(stderr, "%s - ", rawname);
			perror("open raw file");
			cleanstop(7);
		}

		/*
		** compressed raw file to be decompressed via gunzip
		*/
		fprintf(stderr, "Decompressing logfile ....\n");

		snprintf(tmpname2, sizeof tmpname2, "/tmp/atopwrk%d", getpid());
		snprintf(command,  sizeof command, "gunzip -c %s > %s",
							tmpname1, tmpname2);
		system (command);

		if ( (rawfd = open(tmpname2, O_RDONLY)) == -1)
		{
			fprintf(stderr, "%s - ", rawname);
			perror("open decompressed raw file");
			cleanstop(7);
		}

		unlink(tmpname2);
	}

	/*
	** read the raw header and verify the magic
	*/
	if ( read(rawfd, &rh, sizeof rh) < sizeof rh)
	{
		fprintf(stderr, "can not read raw file header\n");
		cleanstop(7);
	}

	if (rh.magic != MYMAGIC)
	{
		fprintf(stderr, "file %s does not contain raw atop/atopsar "
				"output (wrong magic number)\n", rawname);
		cleanstop(7);
	}

	/*
	** magic okay, but file-layout might have been modified
	*/
	if (rh.sstatlen   != sizeof(struct sstat)		||
	    rh.pstatlen   != sizeof(struct pstat)		||
	    rh.rawheadlen != sizeof(struct rawheader)		||
	    rh.rawreclen  != sizeof(struct rawrecord)		  )
	{
		fprintf(stderr,
			"raw file %s has incompatible format\n", rawname);

		if (rh.aversion & 0x8000)
		{
			fprintf(stderr,
				"(created by version %d.%d - "
				"current version %d.%d)\n",
				(rh.aversion >> 8) & 0x7f,
				 rh.aversion       & 0xff,
				 getnumvers() >> 8,
				 getnumvers() & 0x7f);
		}

		close(rawfd);

		if (((rh.aversion >> 8) & 0x7f) != (getnumvers()   >> 8) ||
		     (rh.aversion       & 0xff) != (getnumvers() & 0x7f)   )
		{
			try_other_version((rh.aversion >> 8) & 0x7f,
			                rh.aversion       & 0xff);
		}

		cleanstop(7);
	}

	memcpy(&utsname, &rh.utsname, sizeof utsname);
	supportflags = rh.supportflags;
	osrel        = rh.osrel;
	osvers       = rh.osvers;
	ossub        = rh.ossub;
	interval     = 0;

	if (rh.hertz)
		hertz    = rh.hertz;

	if (rh.pagesize)
		pagesize = rh.pagesize;

	/*
	** allocate a list for backtracking of rawrecord-offsets
	*/
	if ( (offlist = malloc(sizeof(off_t) * OFFCHUNK)) == NULL)
	{
		perror("atop/atopsar malloc");
		cleanstop(7);
	}

	offsize = OFFCHUNK;

	*offlist = lseek(rawfd, 0, SEEK_CUR);
	offcur   = 1;

	/*
	** read a raw record header until end-of-file
	*/
	sampcnt = 0;

	while (lastcmd && lastcmd != 'q')
	{
		while ( getrawrec(rawfd, &rr, rh.rawreclen) == rh.rawreclen)
		{
			unsigned int	secsinday = daysecs(rr.curtime);

			/*
			** store the offset of the raw record in the offset list
			** if case of offset list overflow, extend the list
			*/
			*(offlist+offcur) = lseek(rawfd, 0, SEEK_CUR) -
								rh.rawreclen;

			if ( ++offcur >= offsize )
			{
				if ( (offlist = realloc(offlist,
				     (offsize+OFFCHUNK)*sizeof(off_t))) ==NULL)
				{
					perror("atop/atopsar realloc");
					cleanstop(7);
				}

				offsize+= OFFCHUNK;
			}
	
			/*
			** check if this sample is within the time-range
			** specified with the -b and -e flags (if any)
			*/
			if ( (begintime && begintime > secsinday) )
			{
				lastcmd = 1;
				lseek(rawfd, rr.scomplen+rr.pcomplen, SEEK_CUR);
				continue;
			}

			begintime = 0;

			if ( (endtime && endtime < secsinday) )
			{
				free(offlist);
				close(rawfd);
				return;
			}

			/*
			** allocate space, read compressed system-level
			** statistics and decompress
			*/
			if ( !getrawsstat(rawfd, &devsstat, rr.scomplen) )
				cleanstop(7);

			/*
			** allocate space, read compressed process-level
			** statistics and decompress
			*/
			if ( (devpstat =
			       malloc(sizeof(struct pstat) * rr.nlist)) ==NULL)
			{
				perror("atop/atopsar malloc");
				cleanstop(7);
			}

			if ( !getrawpstat(rawfd, devpstat,
					rr.pcomplen, rr.nlist) )
				cleanstop(7);

			/*
			** activate the installed print-function to visualize
			** the system- and process-level statistics
			*/
			sampcnt++;

			flags = rr.flags & RRBOOT;

			(void) fstat(rawfd, &filestat);

			if ( filestat.st_size -
			     lseek(rawfd, (off_t)0, SEEK_CUR) <= rh.rawreclen)
				flags |= RRLAST;

			lastcmd = (vis.show_samp)(rr.curtime, rr.interval,
						&devsstat,    devpstat,
					 	rr.nlist,     rr.npresent,
					 	rr.ntrun,     rr.ntslpi,
					 	rr.ntslpu,    rr.nzombie,
						rr.nexit,     flags);
			free(devpstat);
	
			switch (lastcmd)
			{
			   case MSAMPPREV:
				if (offcur >= 2)
					offcur-= 2;
				else
					offcur = 0;
	
				lseek(rawfd, *(offlist+offcur), SEEK_SET);
				break;

			   case MRESET:
				lseek(rawfd, *offlist, SEEK_SET);
				offcur = 1;
				break;

			   case MSAMPBRANCH:
				if (begintime && begintime < secsinday)
				{
					lseek(rawfd, *offlist, SEEK_SET);
					offcur = 1;
				}
			}
		}

		begintime = 0;

		if (offcur >= 1)
			offcur--;

		lseek(rawfd, *(offlist+offcur), SEEK_SET);
	}

	free(offlist);

	close(rawfd);
}

/*
** read the next raw record from the raw logfile
*/
static int
getrawrec(int rawfd, struct rawrecord *prr, int rrlen)
{
	return read(rawfd, prr, rrlen);
}

/*
** read the system-level statistics from the current offset
*/
static int
getrawsstat(int rawfd, struct sstat *sp, int complen)
{
	Byte		*compbuf;
	unsigned long	uncomplen = sizeof(struct sstat);
	int		rv;

	if ( (compbuf = malloc(complen)) == NULL)
	{
		perror("atop/atopsar malloc");
		cleanstop(7);
	}

	if ( read(rawfd, compbuf, complen) < complen)
		return 0;

	rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen);

	testcompval(rv, "uncompress");

	free(compbuf);

	return 1;
}

/*
** read the process-level statistics from the current offset
*/
static int
getrawpstat(int rawfd, struct pstat *pp, int complen, int nlist)
{
	Byte		*compbuf;
	unsigned long	uncomplen = sizeof(struct pstat) * nlist;
	int		rv;

	if ( (compbuf = malloc(complen)) == NULL)
	{
		perror("atop/atopsar malloc");
		cleanstop(7);
	}

	if ( read(rawfd, compbuf, complen) < complen)
		return 0;

	rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen);

	testcompval(rv, "uncompress");

	free(compbuf);

	return 1;
}

/* 
** verify if a particular ascii-string is in the format yyyymmdd
*/
static int
lookslikedatetome(char *p)
{
	register int 	i;

	for (i=0; i < 8; i++)
		if ( !isdigit(*(p+i)) )
			return 0;

	if (*p != '2')
		return 0;	/* adapt this in the year 3000 */

	if ( *(p+4) > '1')
		return 0;

	if ( *(p+6) > '3')
		return 0;

	return 1;	/* yes, looks like a date to me */
}

static void
testcompval(int rv, char *func)
{
	switch (rv)
	{
	   case Z_OK:
	   case Z_STREAM_END:
	   case Z_NEED_DICT:
		break;

	   case Z_MEM_ERROR:
		fprintf(stderr, "atop/atopsar - "
		        "%s: failed due to lack of memory\n", func);
		cleanstop(7);

	   case Z_BUF_ERROR:
		fprintf(stderr, "atop/atopsar - "
			"%s: failed due to lack of room in buffer\n", func);
		cleanstop(7);

   	   case Z_DATA_ERROR:
		fprintf(stderr, "atop/atopsar - "
		        "%s: failed due to corrupted/incomplete data\n", func);
		cleanstop(7);

	   default:
		fprintf(stderr, "atop/atopsar - "
		        "%s: unexpected error %d\n", func, rv);
		cleanstop(7);
	}
}

/*
** try to activate another atop- or atopsar-version
** to read this logfile
*/
static void
try_other_version(int majorversion, int minorversion)
{
	char		tmpbuf[1024], *p;
	extern char	**argvp;
	int		fds;
	struct rlimit	rlimit;
	int 		setresuid(uid_t, uid_t, uid_t);

	/*
 	** prepare name of executable file
	** the current pathname (if any) is stripped off
	*/
	snprintf(tmpbuf, sizeof tmpbuf, "%s-%d.%d",
		(p = strrchr(*argvp, '/')) ? p+1 : *argvp,
			majorversion, minorversion);

	fprintf(stderr, "trying to activate %s....\n", tmpbuf);

	/*
	** be sure no open file descriptors are passed
	** except stdin, stdout en stderr
	*/
	(void) getrlimit(RLIMIT_NOFILE, &rlimit);

	for (fds=3; fds < rlimit.rlim_cur; fds++)
		close(fds);

	/*
	** be absolutely sure not to pass setuid-root privileges
	** to the loaded program
	*/
	setresuid(getuid(), getuid(), getuid());

	/*
 	** load alternative executable image
	** at this moment the saved-uid might still be set
	** to 'root' but this is reset at the moment of exec
	*/
	(void) execvp(tmpbuf, argvp);

	/*
	** point of no return, except when exec failed
	*/
	fprintf(stderr, "activation of %s failed!\n", tmpbuf);
}
