/* swverify_lib.c -- routines for iswverify.

 Copyright (C) 2007,2008 James H. Lowe, Jr. 
 All Rights Reserved.
 
 COPYING TERMS AND CONDITIONS:
 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/

#define FILENEEDDEBUG 1
#undef FILENEEDDEBUG

#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "vplob.h"
#include "strob.h"
#include "uxfio.h"
#include "ahs.h"
#include "taru.h"
#include "swlib.h"
#include "swheader.h"
#include "swparse.h"
#include "swlex_supp.h"
#include "swheaderline.h"
#include "swgp.h"
#include "swssh.h"
#include "swfork.h"
#include "swcommon.h"
#include "swevents.h"
#include "swutillib.h"
#include "atomicio.h"
#include "swi.h"
#include "swutilname.h"
#include "globalblob.h"
#include "swproglib.h"
#include "swlist_lib.h"
#include "shlib.h"
#include "swgpg.h"
#include "swicat.h"
#include "swicat_e.h"
#include "swverify_lib.h"
#include "ls_list.h"

#define SWREMOVE_ARG_MAX_THRESHOLD 900

static
void
print_lines(char * name, char * pref, STRAR * ar)
{
	int fd;
	char * s;
	int i;
	i = 0;

	fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);	 
	s = strar_get(ar, i++);
	while (s) {
		write(fd, s, strlen(s));
		write(fd, "\n", 1);
		/* fprintf(stderr, "%s%s\n", pref, s); */
		s = strar_get(ar, i++);
	}
	close(fd);
	return;
}

static
STRAR *
linize(char * text)
{
	char * s;
	STROB * tmp;
	STRAR * strar;
	tmp = strob_open(100);
	strar = strar_open();
	s = strob_strtok(tmp, text, "\n");
	while (s) {
		strar_add(strar, s);	
		s = strob_strtok(tmp, NULL, "\n");
	}
	strob_close(tmp);
	return strar;
}

static
void
looper_abort(GB * G, SWICOL * swicol, int ofd)
{
	swpl_send_abort(swicol, ofd, G->g_swi_event_fd,  SWBIS_TS_Abort);
}

static  /* same as in swproglib.c */
int
check_here_document_word(SWI_FILELIST * fl, char * stop_word)
{
	char * name;
	int ix;

	E_DEBUG("");
	ix = 0;
	while ((name=swi_fl_get_path(fl, ix)) != NULL) {
		if (strcmp(stop_word, name) == 0) {
			return 0;
		}
		ix++;
	}
	E_DEBUG("");
	return 1;
}

static  /* same as in swproglib.c */
int
determine_here_document_stop_word(SWI_FILELIST * fl, STROB * buf)
{
	int ret;
	int n;
	n = 0;
	E_DEBUG("");
	do {
		strob_set_length(buf, L_tmpnam+1);		
		tmpnam(strob_str(buf));
		ret = check_here_document_word(fl, strob_str(buf));
		if (ret != 0) {
			E_DEBUG("");
			return 0;
		}	
		n++;
	} while (ret == 0 && n < 10);
	E_DEBUG("");
	return 1; /* error */
}


static  /* same as in swproglib.c */
int
show_file_list(GB * G, SWI_FILELIST * fl)
{
	char * name;
	int ix;

	E_DEBUG("");
	swi_fl_qsort_forward(fl);
	ix = 0;
	while ((name=swi_fl_get_path(fl, ix)) != NULL) {
		sw_l_msg(G, SWC_VERBOSE_1, "%s\n", name);
		ix++;
	}
	E_DEBUG("");
	return 0;
}

static /* same as in swproglib.c */
int
make_verify_command_script_fragment(GB * G, STROB * scriptbuf, SWI_FILELIST * fl, char * pax_write_command_key)
{
	int ret;
	STROB * stop_word;
	char * name;
	int ix;
	int check_volatile;
	
	/* Tag every file in a volatile directory as being volatile itself
	   FIXME ??? is this the correct thing to do */

	check_volatile = swextopt_is_option_true(SW_E_check_volatile, G->optaM);

	E_DEBUG("");
	swpl_tag_volatile_files(fl);
	
	stop_word = strob_open(32);

	ret = determine_here_document_stop_word(fl, stop_word);
	if (ret != 0) {
		/* this really should never happen */
		exit(12);
	}

	E_DEBUG("");

	/* sort the files in forward direction */
	swi_fl_qsort_forward(fl);

	strob_sprintf(scriptbuf, STROB_DO_APPEND, "rm_retval=0\n");

	/* Construct the here document of filenames that feed
	   GNU tar's stdin to make an archive */
	/* Something like this:
		(
		cat <<'/tmp/End'
		a
		c
		/tmp/End
		) | tar  cf - --no-recursion --files-from=-
	*/

	/* Start of hear document */
	strob_sprintf(scriptbuf, STROB_DO_APPEND,
		"(\n"
		"cat <<'%s'\n",
		strob_str(stop_word));

	/* priint each file */
	E_DEBUG("");
	ix = 0;
	while ((name=swi_fl_get_path(fl, ix)) != NULL) {
		if (G->devel_verboseM) {
			SWLIB_INFO3("INFO; file=[%s] type=[%c]", name, (int)swi_fl_get_type(fl, ix));
		}

		/* check_volatile */
		if (check_volatile == 0 && swi_fl_is_volatile(fl, ix)) {
			/* skip */
			;
		} else {
			strob_sprintf(scriptbuf, STROB_DO_APPEND, "%s\n", name);
		}
		ix++;
	}

	/* End of here document */
	E_DEBUG("");
	strob_sprintf(scriptbuf, STROB_DO_APPEND,
		"%s\n"
		") |",
		strob_str(stop_word));


	/* Sanity check on pax_write_command_key */

	if (strcmp(pax_write_command_key, "tar") == 0) {
		;
	} else if (strcmp(pax_write_command_key, "gtar") == 0) {
		;
	} else {
		/* This is an error, should write a warning  */
		pax_write_command_key = "tar";
	}
	
	/* write the tar writing command */

	E_DEBUG("");
	strob_sprintf(scriptbuf, STROB_DO_APPEND, " %s vcf - -b 1 --no-recursion --files-from=- \n", pax_write_command_key);
	strob_sprintf(scriptbuf, STROB_DO_APPEND, "retrieve_retval=$?\n");

	E_DEBUG("");
	strob_close(stop_word);
	return 0;
}

static
char *
looper_locked_region(STROB * buf, int vlv)
{
	strob_sprintf(buf, 0,
		"				# -----------------------------\n"
		"				# The locked region begins here\n"
		"				# -----------------------------\n"
		"				export rp_status\n"

		"				#\n"
		"				# The code for selection processing can begin here\n"
		"				#\n"

		"				read swspec_string\n"                  /* SWBIS_DT_0001 */
		"				%s\n" /* SW_SELECTION_EXECUTION_BEGINS */

		"				# echo Processing Now \"$xxb\" 1>&2\n"
		"				echo \"$xxb\"\n"			/* SWBIS_DT_0002 */

		"				tar cbf 1 - \"$catalog_entry_dir\" 2>/dev/null\n"
		"				sw_retval=$?\n"
		"				# echo \"here is the swspec string: [$swspec_string]\" 1>&2\n"

		"				rp_status=$sw_retval\n"
		"				# rp_status=8\n"
		"				shls_bashin2 \"" SWBIS_TS_report_status "\"\n"
		"				sw_retval=$?\n"
		"				case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"				ret=$sw_retval\n"
		"				swexec_status=$sw_retval\n"
		"				case $sw_retval in 0) %s ;; *) ;; esac\n" /* clear to send */
	
		"				shls_bashin2 \"" SWBIS_TS_Catalog_unpack "\"\n"
		"				sw_retval=$?\n"
		"				case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"				sw_retval=$?\n"
		"				swexec_status=$sw_retval\n"
	
		"				shls_bashin2 \"" SWBIS_TS_retrieve_files_archive "\"\n"
		"				sw_retval=$?\n"
		"				case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"				sw_retval=$?\n"
		"				swexec_status=$sw_retval\n"

		"				shls_bashin2 \"" SWBIS_TS_Catalog_dir_remove "\"\n"
		"				sw_retval=$?\n"
		"				case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"				sw_retval=$?\n"
		"				swexec_status=$sw_retval\n"

		"				shls_bashin2 \"" SWBIS_TS_post_verify "\"\n"
		"				sw_retval=$?\n"
		"				case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"				sw_retval=$?\n"
		"				swexec_status=$sw_retval\n"

		"				%s\n" /* SW_SELECTION_EXECUTION_ENDS */
		,
/*_% */		TEVENT(2, vlv,  SW_SELECTION_EXECUTION_BEGINS, "$swspec_string"),
/*_% */		TEVENT(2, -1, SWI_TASK_CTS, "Clear to Send: status=0"),
/*_% */		TEVENT(2, vlv,  SW_SELECTION_EXECUTION_ENDS, "${swspec_string}: status=$sw_retval")
		);
	return strob_str(buf);
}



static
int
get_name_len(char * line)
{
	char * s;
	char * l;

	E_DEBUG("");
	l = NULL;	
	s = strstr(line, " " LS_LIST_LINKNAME_MARK);
	while(s) {
		E_DEBUG("");
		/* FIXME: this is supposed to get the last occurance but
			  is not immune to all attacks */
		l = s;
		s = strstr(s+1, " " LS_LIST_LINKNAME_MARK);
	}
	if (! l) {
		SWLIB_FATAL("");
	}
	E_DEBUG2("returning %d", (int)(l - line));
	return (int)(l - line); /* length of the filename */
}

static
int
compare_names(char * mname, char * sname)
{
	int ml;
	int sl;
	E_DEBUG("");
	ml = get_name_len(mname);
	E_DEBUG("");
	sl = get_name_len(sname);
	E_DEBUG("");
	if (ml != sl) return 1;
	E_DEBUG("");
	return strncmp(mname, sname, ml);
}

static
int
compare_diff(GB * G, char * host, char * swspec_string, char * target_path, STRAR * meta, STRAR * sys)
{
	int retval;
	char * m_s;
	char * s_s;
	int did_print_header;
	STROB * diff_header;
	STROB * diff;
	int is;
	int im;

	retval = 0;
	did_print_header = 0;

	diff_header = strob_open(80);
	diff = strob_open(80);

	strob_sprintf(diff_header, 0,
			"1c1\n"
			"< INFO %s @ %s:%s using  installed_software_catalog=%s\n"
			"---\n"
			"> SYSTEM located at %s:%s\n",
		 	swspec_string, host, target_path, get_opta(G->optaM, SW_E_installed_software_catalog),
		 	host, target_path
			);

	/* Make a psuedo diff-like output, this is easy because
	   we can assume that the files are sorted, and, only files will
	   be missing from the system list (STRAR*)sys or else they will
	   be changed 1-to-1 with (STRAR*)meta */

	im = 0;
	is = 0;
	m_s = strar_get(meta, im);
	s_s = strar_get(sys, is);
	while (m_s) {
		E_DEBUG("");
		if (m_s && s_s && strcmp(m_s, s_s) == 0) {
			/* good, lines match */
			im++;			
			is++;			
			E_DEBUG("match");
			;
		} else if (m_s && s_s && strcmp(m_s, s_s) != 0) {
			/* either changed of deleted file */
			if (did_print_header++ == 0) {
				fprintf(stderr, "%s", strob_str(diff_header));	
			}
			E_DEBUG("mis match");
			if (compare_names(m_s, s_s) == 0) {
				/* changed file */
				strob_sprintf(diff, 0,
					"%dc%d\n"
					"< %s\n"
					"---\n"
					"> %s\n",
		 			im, im, m_s, s_s);
				fprintf(stdout, "%s", strob_str(diff));
				im++;
				is++;
			} else {
				/* missing file */
				E_DEBUG("missing file");
				if (did_print_header++ == 0) {
					fprintf(stderr, "%s", strob_str(diff_header));	
				}
				strob_sprintf(diff, 0,
					"%dd%d\n"
					"< %s\n",
	 				im, is, m_s);
				fprintf(stdout, "%s", strob_str(diff));
				im++;
			}
			E_DEBUG("diff");
			retval++;
		} else if (m_s && !s_s) {
			/* files missing at end */
			if (did_print_header++ == 0) {
				fprintf(stderr, "%s", strob_str(diff_header));	
			}
			strob_sprintf(diff, 0,
				"%dd%d\n"
				"< %s\n",
	 			im, is, m_s);
			fprintf(stdout, "%s", strob_str(diff));
			retval++;
			im++;
			E_DEBUG("missing2");
		} else {
			E_DEBUG("never here");
			im++;
		}
		m_s = strar_get(meta, im);
		s_s = strar_get(sys, is);
	}
	E_DEBUG2("retunring %d", retval);
	strob_close(diff);
	strob_close(diff_header);
	return retval;
}

static
int
compare_fileset(GB * G, char * swspec_string, char * target_path, SWUTS * uts, SWI * swi, int archive_fd, int * compare_status)
{
	int nullfd;
	int ret;
	XFORMAT * xformat;
	STROB * tmp;
	FILE_DIGS * digs;
	struct new_cpio_header * file_hdr;
	int ls_attributes;
	int available_attributes;
	int meta_fd;
	int sys_fd;
	char * s;
	char * meta_text;
	char * sys_text;
	STRAR * sys_str;
	STRAR * meta_str;
	int check_volatile;
	int check_mtime;
	int check_permissions;

	*compare_status = -1;
	meta_fd = swlib_open_memfd();
	sys_fd = swlib_open_memfd();
	nullfd = swlib_open_nullfd();
	tmp = strob_open(100);

	/* list the catalog metadata */

	check_volatile = swextopt_is_option_true(SW_E_check_volatile, G->optaM);
	check_mtime = swextopt_is_option_true(SW_E_swbis_check_mtime, G->optaM);
	check_permissions = swextopt_is_option_true(SW_E_check_permissions, G->optaM);
	
	ls_attributes = LS_LIST_VERBOSE_COMP2;
	if (check_mtime == 0) {
		ls_attributes &= ~(LS_LIST_VERBOSE_WITH_REG_DATES|LS_LIST_VERBOSE_WITH_ALL_DATES);
	}

	swpl_ls_fileset_from_iscat(swi, meta_fd, ls_attributes, &available_attributes, !check_volatile);

	/* Now use the value of &available_attributes to set the
	   LS_LIST_VERBOSE_* controls.  This is required, for example,
           when the package INFO file lists only the uid/gid and not
           the uname and gname.  */	

	/* The catalog meta-data is audited and a inclusion policy is
	   formed for uid/uname and digests that are in the meta-data.
	   This is represented in (int)available_attributes */

	available_attributes |= LS_LIST_VERBOSE_ALTER_FORM|LS_LIST_VERBOSE_NORMAL|LS_LIST_VERBOSE_STRIPSLASH|LS_LIST_VERBOSE_WITH_REG_DATES;
	available_attributes &= ~LS_LIST_VERBOSE_OFF;
	if (check_mtime == 0) {
		available_attributes &= ~(LS_LIST_VERBOSE_WITH_REG_DATES|LS_LIST_VERBOSE_WITH_ALL_DATES);
	}

	xformat = xformat_open(-1, -1, arf_ustar);
	xformat_set_ifd(xformat, archive_fd);

	file_hdr = ahs_vfile_hdr(xformat_ahs_object(xformat));
	taru_init_header_digs(file_hdr);
	digs = file_hdr->digsM;
	if (available_attributes & LS_LIST_VERBOSE_WITH_MD5)
		digs->do_md5 = DIGS_ENABLE_ON;
	if (available_attributes & LS_LIST_VERBOSE_WITH_SHA1)
		digs->do_sha1 = DIGS_ENABLE_ON;
	if (available_attributes & LS_LIST_VERBOSE_WITH_SHA512)
		digs->do_sha512 = DIGS_ENABLE_ON;

	E_DEBUG("");
	uxfio_lseek(archive_fd, 0, SEEK_SET);
	while ((ret = xformat_read_header(xformat)) > 0) {
		if (xformat_is_end_of_archive(xformat)){
			break;
		}
		E_DEBUG("");

		if (xformat_get_tar_typeflag(xformat) == REGTYPE) {
			E_DEBUG("");
			ret = xformat_copy_pass_digs(xformat, nullfd, archive_fd, digs);
		}
		E_DEBUG("");
		taru_print_tar_ls_list(tmp, file_hdr, available_attributes);
		uxfio_write(sys_fd, strob_str(tmp), strob_strlen(tmp));
	}
	
	meta_text = uxfio_get_fd_mem(meta_fd, (int *)NULL);
	sys_text = uxfio_get_fd_mem(sys_fd, (int *)NULL);

	/* 
	fprintf(stdout, "%s", meta_text);
	fprintf(stdout, "%s", sys_text);
	*/

	/* split into lines */
	meta_str = linize(meta_text);
	sys_str = linize(sys_text);

	/* sort the lines */
	strar_qsort(meta_str, swi_fl_compare);
	strar_qsort(sys_str, swi_fl_compare);

	/* Now compare these line by line */

	*compare_status = compare_diff(G, G->g_target_terminal_host, swspec_string, target_path, meta_str, sys_str);

	print_lines("/tmp/meta.txt", "", meta_str);
	print_lines("/tmp/sys.txt", "", sys_str);

	strar_close(meta_str);
	strar_close(sys_str);
	uxfio_close(sys_fd);
	uxfio_close(meta_fd);
	uxfio_close(nullfd);
	strob_close(tmp);
	xformat_close(xformat);
	return 0;
}

int
swverify_write_source_copy_script2(GB * G,
	int ofd, 
	char * targetpath, 
	int do_get_file_type, 
	int verbose_threshold,
	int delaytime,
	int nhops,
	char * pax_write_command_key,
	char * hostname,
	char * blocksize
	)

{
	int ret;
	char * dirname;
	char * basename;
	char * pax_write_command;
	char * opt_force_lock;
	char * opt_allow_no_lock;
	char * ignore_scripts;
	STROB * locked_region;
	STROB * looper_routine;
	STROB * buffer;
	STROB * shell_lib_buf;
	STROB * is_archive_msg;
	STROB * is_directory_msg;
	STROB * subsh;
	STROB * subsh2;
	STROB * tmp;
	STROB * set_vx;
	STROB * to_devnull;
	STROB * isc_msg;
	int vlv;
	char * debug_task_shell;
	
	basename = (char*)NULL;
	dirname = (char*)NULL;
	buffer = strob_open(100);
	to_devnull = strob_open(100);
	is_archive_msg = strob_open(100);
	is_directory_msg = strob_open(100);
	set_vx = strob_open(100);
	tmp = strob_open(100);
	subsh = strob_open(100);
	subsh2 = strob_open(100);
	isc_msg = strob_open(100);
	shell_lib_buf = strob_open(100);
	locked_region = strob_open(100);
	looper_routine = strob_open(100);

	vlv = G->g_verboseG;
	if (G->g_do_task_shell_debug == 0) {
		debug_task_shell="";
	} else {
		debug_task_shell="x";
	}

	/* Sanity checks on targetpath */
	if (
		strstr(targetpath, "..") == targetpath ||
		strstr(targetpath, "../") ||
		swlib_is_sh_tainted_string(targetpath) ||
		0
	) { 
		return 1;
	}
	swlib_squash_double_slash(targetpath);

	/* assemble the the script verbosity controls */

	if (vlv >= SWC_VERBOSE_SWIDB) {
		strob_strcpy(set_vx, "set -vx\n");
	}
	
	if (vlv <= verbose_threshold ) {
		strob_strcpy(to_devnull, "2>/dev/null");
	}

	if (G->g_force_locks) {
		opt_allow_no_lock = "true";
		opt_force_lock = "true";
	} else {
		opt_allow_no_lock = "";
		opt_force_lock = "";
	}
	
	if (swextopt_is_option_true(SW_E_swbis_ignore_scripts, G->optaM)) {
		ignore_scripts="yes";
	} else {
		ignore_scripts="no";
	}

	/* Split targetpath into a basename and leading direcctory parts */
	
	/* leading directories */
	swlib_dirname(tmp, targetpath);
	dirname = strdup(strob_str(tmp));
	if (swlib_is_sh_tainted_string(dirname)) { return 1; }

	/* basename which may be a directory */
	swlib_basename(tmp, targetpath);
	basename = strdup(strob_str(tmp));      
	if (swlib_is_sh_tainted_string(basename)) { return 1; }

	/* This is a sanity check required by the assumption of the
	   code that reads this control message. */
	if (strchr(basename, '\n') || 
		strlen(basename) > MAX_CONTROL_MESG_LEN - 
			strlen(SWBIS_SWINSTALL_SOURCE_CTL_ARCHIVE ":")
	) {
		return 1;
	}

	/* these are the control messages that tell the management host
           whether the target path is a regular file or directory */
	strob_sprintf(is_archive_msg, 0, 
		"echo " "\"" SWBIS_SWINSTALL_SOURCE_CTL_ARCHIVE ": %s\"", basename);

	strob_sprintf(is_directory_msg, 0, 
		"echo " "\""SWBIS_SWINSTALL_SOURCE_CTL_DIRECTORY ": %s\"", basename);

	/* Determine what archive writing utility to use */
	
	pax_write_command = swc_get_pax_write_command(G->g_pax_write_commands,
						pax_write_command_key,
						G->g_verboseG, DEFAULT_PAX_W);


	/* Make a message for the SOURCE_ACCESS_BEGINS event when
	   reading the catalog */

	strob_sprintf(isc_msg, 0, "installed catalog at %s", get_opta(G->optaM, SW_E_installed_software_catalog));


	/* Make a final adjustment if the path is '/', FIXME */
	if (
		strcmp(basename, "/") == 0 &&
		strcmp(dirname, "/") == 0
	) {
		free(basename);
		basename = strdup(".");	
	}


	looper_locked_region(locked_region, vlv);
	swpl_looper_payload_routine(looper_routine,  vlv, strob_str(locked_region));

	/* Now here is the script */
	ret = swlib_writef(ofd, buffer, 	
									/* ism_begin */
		"trap '/bin/rm -f ${LOCKPATH}.lock; exit 1' 1 2 15\n"
		"echo " SWBIS_TARGET_CTL_MSG_125 ": " KILL_PID "\n"
		"export LOCKENTRY\n"
		"LOCKPATH=\"\"\n"
		"%s\n"  			/* SEVENT: SW_SESSION_BEGINS */
		"%s" 				/* swicol_subshell_marks */
		"export lock_did_lock\n"
		"export opt_force_lock\n"
		"export swbis_ignore_scripts\n"
		"lock_did_lock=\"\"\n"
		"opt_force_lock=\"%s\"\n"
		"opt_allow_no_lock=\"%s\"\n"
		"export PATH\n"
		"PATH=`getconf PATH`:$PATH\n"
		"swbis_ignore_scripts=\"%s\"\n"
		"export swutilname\n"
		"swutilname=swverify\n"
		"%s\n"				/* shls_bashin2 from shell_lib.sh */
		"%s\n"				/* shls_false_ from shell_lib.sh */
		"%s\n"				/* shls_looper from  shell_lib.sh */
		"%s\n"				/* shls_looper_payload from shell_lib.sh */
		"%s\n"			/* lf_ lock routine */
		"%s\n"			/* lf_ lock routine */
		"%s\n"			/* lf_ lock routine */
		"%s\n"			/* lf_ lock routine */
		"%s" 			/* set statement for verbosity */
		"export opt_to_stdout\n"
		"opt_to_stdout=\"\"\n"
		"blocksize=\"%s\"\n"
		"dirname=\"%s\"\n"
		"basename=\"%s\"\n"
		"targetpath=\"%s\"\n"
		"sw_targettype=unset\n"
		"sw_retval=0\n"
		"sb__delaytime=%d\n"
		"export sh_dash_s\n"
		"sh_dash_s=\"%s\"\n"
		"debug_task_shell=\"%s\"\n"
		"swexec_status=0\n"
		"export LOCKPATH\n"
		"# Here is the override of the shls_looper_payload function\n"
		"#\n"
		"%s\n"    /* <<<--- shls_looper_payload */

		/* Here, in this first if-then statement we will classify and
		   type-test the target path */
									/* ism_test_begin */
		"if test -e \"$dirname\"; then\n"
		"	cd \"$dirname\"\n"
		"	sw_retval=$?\n"
		"	case $sw_retval in\n"
		"	0)\n"
				/* chdir succeeded */
		"		if test -d \"$basename\" -a -r \"$basename\"; then\n"
									/* ism_d1 */

						/* This must be a installation root with an 
						   installed_software catalog at the expected
						   location which is :
							<path>/<installed_software catalog>/  */
		"			sw_targettype=dir\n"
		"			%s\n"                           /* Send is_directory message */
		"		elif test -e \"$basename\" -a -r \"$basename\"; then\n"
									/* ism_a1 */
		"			sw_targettype=regfile\n"
		"			%s\n"                           /* Send is_archive message */
		"		else\n"
									/* access_error */
					/* error */
		"			sw_targettype=error\n"
		"			%s\n"
		"			%s\n"
		"		fi\n"
		"		;;\n"
		"	*)\n"
				/* chdir_failed */
		"		sw_targettype=error\n"
		"		%s\n"
		"		%s\n"
		"		;;\n"
		"	esac\n"
		"else\n"
									/* ism_else_fail */
			/* Bad file name or no access ... */
		"	%s\n"
		"	%s\n"
		"fi\n"

		/* Here is where the real work begins, the target has been
			typed and tested, the management host has been
			notified via the "is_archive_msg" and "is_directory_msg"
			and the $sw_targettype var has been set.  */

		"case \"$sw_targettype\" in\n"
		"	\"regfile\")\n"
				/* This is an error for swverify */
				/* throw an error */
		"		sw_retval=1\n"
		"		;;\n"
		"	\"dir\")\n"
		"		%s\n"
		"		%s\n"  
		"		swexec_status=0\n"

		"shls_bashin2 \"" SWBIS_TS_uts "\"\n"
		"sw_retval=$?\n"
		"case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"sw_retval=$?\n"

		"shls_bashin2 \"" SWBIS_TS_Do_nothing "\"\n"
		"sw_retval=$?\n"
		"case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"sw_retval=$?\n"
		"swexec_status=$sw_retval\n"

		"shls_bashin2 \"" SWBIS_TS_Get_iscs_listing "\"\n"
		"sw_retval=$?\n"
		"case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"sw_retval=$?\n"
		"swexec_status=$sw_retval\n"

		/* Loop over the selections */
		"		shls_looper \"$sw_retval\"\n"
		"		sw_retval=$?\n"
		"		swexec_status=$sw_retval\n"

		"		shls_bashin2 \"" SWBIS_TS_Do_nothing "\"\n"
		"		sw_retval=$?\n"
		"		case $sw_retval in 0) $sh_dash_s;; *) shls_false_;; esac\n"
		"		sw_retval=$?\n"
		"		swexec_status=$sw_retval\n"

		"		%s\n"
		"		;;\n"
		"	\"unset\")\n"
					/* This is an error */
		"		sw_retval=1\n"
		"		;;\n"
		"	*)\n"
					/* This is an error */
		"		sw_retval=1\n"
		"		;;\n"
		"esac\n"

		"if test \"$sw_retval\" != \"0\"; then\n"
		"	 sb__delaytime=0;\n"
		"fi\n"
		"sleep \"$sb__delaytime\"\n"
		"%s\n"
		"%s\n"
		"%s\n"
		,
									/* ism_begin */
/*_% */		TEVENT(2, vlv, SW_SESSION_BEGINS, ""),
/*_% */		swicol_subshell_marks(subsh, "install_target", 'L', nhops, vlv),
/*_% */		opt_force_lock,
/*_% */		opt_allow_no_lock,
/*_% */		ignore_scripts,
/*_% */		shlib_get_function_text_by_name("shls_bashin2", shell_lib_buf, NULL),
/*_% */		shlib_get_function_text_by_name("shls_false_", shell_lib_buf, NULL),
/*_% */		shlib_get_function_text_by_name("shls_looper", shell_lib_buf, NULL),
/*_% */		shlib_get_function_text_by_name("shls_looper_payload", shell_lib_buf, NULL),
/*_% */	shlib_get_function_text_by_name("lf_make_lockfile_name", shell_lib_buf, NULL),
/*_% */	shlib_get_function_text_by_name("lf_make_lockfile_entry", shell_lib_buf, NULL),
/*_% */	shlib_get_function_text_by_name("lf_test_lock", shell_lib_buf, NULL),
/*_% */	shlib_get_function_text_by_name("lf_remove_lock", shell_lib_buf, NULL),
/*_% */		strob_str(set_vx),
/*_% */		blocksize,
/*_% */		dirname,
/*_% */		basename,
/*_% */		targetpath,
/*_% */		delaytime,
/*_% */		G->g_sh_dash_s, 
/*_% */		debug_task_shell,
/*_% */		strob_str(looper_routine),
/*_% */		strob_str(is_directory_msg),
/*_% */		strob_str(is_archive_msg),
/*_% */		TEVENT(2, vlv, SW_SOURCE_ACCESS_ERROR, basename),
/*_% */		TEVENT(1, -1, SW_SOURCE_ACCESS_ERROR, basename),
/*_% */		TEVENT(2, vlv, SW_SOURCE_ACCESS_ERROR, dirname),
/*_% */		TEVENT(1, -1, SW_SOURCE_ACCESS_ERROR, dirname),
/*_% */		TEVENT(2, vlv,  SW_SOURCE_ACCESS_ERROR, targetpath),
/*_% */		TEVENT(1, -1, SW_SOURCE_ACCESS_ERROR, targetpath),
/*_% */		TEVENT(1, -1, SW_SOURCE_ACCESS_BEGINS, strob_str(isc_msg)),
/*_% */		TEVENT(2, vlv, SW_SOURCE_ACCESS_BEGINS, strob_str(isc_msg)),
/*_% */		TEVENT(2, vlv, SW_SOURCE_ACCESS_ENDS, "status=$sw_retval"),
/*_% */		TEVENT(2, -1, SWI_MAIN_SCRIPT_ENDS, "status=0"),
/*_% */		TEVENT(2, vlv, SW_SESSION_ENDS, "status=$sw_retval"),
/*_% */		swicol_subshell_marks(subsh2, "install_target", 'R', nhops, vlv)
		);

	free(basename);
	free(dirname);
	strob_close(tmp);
	if (G->g_source_script_name) {
		swlib_tee_to_file(G->g_source_script_name, -1, strob_str(buffer), -1, 0);
	}

	strob_close(locked_region);
	strob_close(looper_routine);
	strob_close(buffer);
	strob_close(is_archive_msg);
	strob_close(is_directory_msg);
	strob_close(set_vx);
	strob_close(to_devnull);
	strob_close(subsh);
	strob_close(subsh2);
	strob_close(isc_msg);
	strob_close(shell_lib_buf);
	/*
	 * 0 is OK
	 * !0 is error
	 */
	return !(ret > 0);
}

int
swverify_looper_sr_payload(GB * G, char * target_path, SWICOL * swicol,
		SWICAT_SC * sc, SWICAT_SR * sr, int ofd, int ifd, int * p_rp_status,
		SWUTS * uts, char * pax_write_command_key, int * compare_status)
{
	int retval;
	int rstatus;
	int ret;
	int do_skip_entry;
	char * pax_read_command;
	char * epath;
	char * installed_software_catalog;
	char * catalog_entry_directory;
	STROB * tmp;
	STROB * btmp;
	STROB * btmp2;
	STROB * swspec_string;
	SWICAT_E * e;
	SWI * swi;
	SWI_FILELIST * file_list;
	SWGPG_VALIDATE * swgpg;
	int archive_fd;
	
	*compare_status = -1;
	archive_fd = -1;
	do_skip_entry = 0;
	retval = 0;
	rstatus = 0;
	tmp = strob_open(100);
	btmp = strob_open(100);
	btmp2 = strob_open(100);
	swspec_string = strob_open(100);
	swi = NULL;
	file_list = NULL;
	swgpg = NULL;
	e = NULL;

	pax_read_command = swc_get_pax_read_command(G->g_pax_read_commands,
		"tar", G->g_verboseG >= SWC_VERBOSE_5,
		0 /*keep_old_files*/, DEFAULT_PAX_R);
	E_DEBUG("");
	installed_software_catalog = get_opta(G->optaM, SW_E_installed_software_catalog);
	catalog_entry_directory = swicat_sr_form_catalog_path(sr, installed_software_catalog, NULL);

	E_DEBUG2("catalog_entry_directory = [%s]", catalog_entry_directory);

	swicat_sr_form_swspec(sr, swspec_string);

	*p_rp_status = 0;
	if (strlen(catalog_entry_directory) == 0) {
		/* return with no error
		   this happens for `the empty response` to a query */
		return 1;
	}

	/* fprintf(stderr, "ENTRY: [%s]\n", catalog_entry_directory); */

	E_DEBUG2("servicing looper: writing: [%s]", strob_str(btmp));
	strob_sprintf(btmp, 0, "%s\n", catalog_entry_directory);

	/* write the catalog entry directory which becomes arg1 to
	   the shls_looper_payload() routine */

	ret = atomicio((ssize_t (*)(int, void *, size_t))write,
			ofd,
			strob_str(btmp),
			strob_strlen(btmp)
			);
					
	if (swicol_get_master_alarm_status(swicol) != 0 ||
		 ret != (int)strob_strlen(btmp)
	) {
		/* error */
		sw_e_msg(G, "error from atomicio\n");
		return -1;
	}

	/* here is a gratuitous task shell that does nothing */

	E_DEBUG("");
	ret = swpl_send_success(swicol, ofd, G->g_swi_event_fd,
			SWBIS_TS_check_loop);
	if (ret != 0) {
		sw_e_msg(G, "error from swpl_send_success()\n");
		return -2;
	}

	/* Make a session lock */

	E_DEBUG("");
	ret = swpl_session_lock(G, swicol, target_path, ofd, G->g_swi_event_fd);
	sw_d_msg(G, "swpl_session_lock returned [%d]\n", ret);

	E_DEBUG2("swpl_session_lock returned %d", ret);
	swlib_squash_trailing_vnewline(strob_str(btmp));	
	if (ret < 0) {
		/* Internal error */
		sw_d_msg(G, "swpl_session_lock: lock fail for %s, ret=%d\n", strob_str(btmp), ret);
		sw_e_msg(G, "error from swpl_session_lock: status=%d\n", ret);
		return -4;
	} else if (ret > 0) {
		/* session in progress, or no access to make lock */
		sw_e_msg(G, "swpl_session_lock lock failed for %s, ret=%d\n", strob_str(btmp), ret);
		/* sw_d_msg(G, "swpl_session_lock lock failed for %s, ret=%d\n", strob_str(btmp), ret); */
		*p_rp_status = 1;
		return 1;
	} 

	/*
	 * ---------------------------------------------
	 * If we're here we got the lock
	 * ---------------------------------------------
	 */
	
	/* Here is where the real work begins
	   Perform the required actions, in accord with
	   the contents of shls_looper_payload() shell routine */

	E_DEBUG("");
	
	/*
	 * Send a line of text to use in the status messages.
	 */

	/* SWBIS_DT_0001 */
	swgp_write_as_echo_line(ofd, strob_str(swspec_string));

	/*
	 * read the line echo'ed in the remote script
	 */

	/* SWBIS_DT_0002 */
	swgp_read_line(ifd, btmp2, DO_APPEND);

	if (G->devel_verboseM)
		fprintf(stderr, "rp_status is %d\n", ret);

	e = swicat_e_create();
	ret = swicat_e_open_entry_tarball(e, ifd);
	if (ret < 0) {
		sw_e_msg(G, "error opening catalog entry tarball, ret=%d\n", ret);
		rstatus = 1;
		retval = -1;  /* protocol internal error */
		looper_abort(G, swicol, ofd);
		goto error_out;
	}

	/* e->entry_prefixM has the installed_software_catalog
	  path already prefixed */
	epath = swicat_e_form_catalog_path(e, btmp,
			NULL /* get_opta(G->optaM, SW_E_installed_software_catalog) */,
			SWICAT_ACTIVE_ENTRY);
	if (G->devel_verboseM)
		SWLIB_INFO2("catalog entry (active) path = %s", epath);
	
	epath = swicat_e_form_catalog_path(e, btmp,
			NULL /* get_opta(G->optaM, SW_E_installed_software_catalog) */,
			SWICAT_DEACTIVE_ENTRY);
	if (G->devel_verboseM)
		SWLIB_INFO2("catalog entry path (inactive) path = %s", epath);

	epath = swicat_e_form_catalog_path(e, btmp,
			NULL /* get_opta(G->optaM, SW_E_installed_software_catalog) */,
			SWICAT_ACTIVE_ENTRY);

	/* epath is now a clean relative path to such as:
		var/lib/swbis/catalog/emacs/emacs/21.2.1/0  */

	E_DEBUG("");
	ret = swicat_e_reset_fd(e);
	if (ret < 0) {
		rstatus = 2;
		retval = -1;
		sw_e_msg(G, "error reseting swicat_e object, ret=%d\n", ret);
		looper_abort(G, swicol, ofd);
		goto error_out;
	}

	E_DEBUG("");
	/* Now obtain the verification status for each signature */
	if (G->devel_verboseM)
		SWLIB_INFO2("NOPEN=%d", uxfio_uxfio_get_nopen());

	swgpg = swgpg_create();
	if (G->devel_verboseM)
		SWLIB_INFO2("NOPEN=%d", uxfio_uxfio_get_nopen());
	ret = swicat_e_verify_gpg_signature(e, swgpg);
	if (ret != 0) {
		rstatus = 3;
		retval = 0;
		sw_e_msg(G, "error from swicat_e_verify_gpg_signature, ret=%d\n", ret);
	}

	/* Now interpret the verification results according to extended option
	   requirements */

	E_DEBUG("");
	ret = swpl_signature_policy_accept(G, swgpg, G->g_verboseG, strob_str(swspec_string));
	if (ret != 0) {
		/* Bad signature or not enough good signatures
		   Can't use the blob to make a removal file list
		   because were assuming its tainted. */
		rstatus = SWVERIFY_RP_STATUS_NO_INTEGRITY;
		retval = 0;
		do_skip_entry = 1;
	} else {
		/* Signatures OK, or don't care */
		;
	}

	E_DEBUG("");
	/*
	 * If we're here its OK to decode the catalog.tar file to create (SWI*)swi
	 * because its authenticated integrity or we don't care.
	 */

	E_DEBUG("");
	swi = swicat_e_open_swi(e);
	if (swi == NULL) {
		/* swi might be NULL due to file system permission,
		   handle NULL gracefully */
		do_skip_entry = 1;
		sw_e_msg(G, "catalog read error, possible permission denied: %s/%s\n", target_path, epath);
	}

	E_DEBUG("");
	if (swi) swi->swi_pkgM->catalog_entryM = strdup(epath); /* FIXME, this must set explicitly */
	if (swi) swi->swi_pkgM->target_pathM = strdup(target_path); /* FIXME, this must set explicitly */

	if (G->devel_verboseM)
		SWLIB_INFO2("NOPEN=%d", uxfio_uxfio_get_nopen());
	if (swi) swi->optaM = G->optaM;
	if (G->devel_verboseM)
		SWLIB_INFO2("NOPEN=%d", uxfio_uxfio_get_nopen());

	error_out_and_report:

	E_DEBUG("");
	/* SWBIS_TS_report_status
	The purpose of this is solely to provide an opportunity for
	the remote script to report a problem and to re-verify script-data
	sychronization. */
	
	E_DEBUG("");
	ret = swpl_report_status(swicol, ofd, G->g_swi_event_fd);
	*p_rp_status = ret;
	if (ret) {
		/* looper_abort(G, swicol, ofd); */
		rstatus = 6;
		retval = 0;
		do_skip_entry = 1;
	}
	if (G->g_do_debug_events)
		swicol_show_events_to_fd(swicol, STDERR_FILENO, -1);

	/* wait for clear-to-send */

	ret = swicol_rpsh_wait_cts(swicol, G->g_swi_event_fd);
	*p_rp_status = ret;
	if (ret) {
		looper_abort(G, swicol, ofd);
	}

	E_DEBUG("");
	if (G->g_do_debug_events)
		swicol_show_events_to_fd(swicol, STDERR_FILENO, -1);

	E_DEBUG("");
	if (swi) 
		file_list = swicat_e_make_file_list(e, uts, swi);
	else
		file_list = NULL;

	E_DEBUG("");
	if (swi && file_list == NULL) {
		rstatus = 7;
		retval = 0;
		do_skip_entry = 1;
		/* 
		looper_abort(G, swicol, ofd);
		goto error_out;
		*/
	}
	
	E_DEBUG("");
	if (
		G->g_opt_previewM  ||
		G->g_verboseG > SWC_VERBOSE_3 ||
		G->devel_verboseM ||
		0
	) {
		if (file_list) show_file_list(G, file_list);
	}

	E_DEBUG("");
	if (G->devel_verboseM)
		SWLIB_INFO2("%s", strob_str(btmp));

	skip:

	E_DEBUG("");
	if (
		G->g_opt_previewM ||
		do_skip_entry ||
		0
	) {
		ret = swpl_send_nothing_and_wait(swicol, ofd, G->g_swi_event_fd,
                        SWBIS_TS_Catalog_unpack,
			SWICOL_TL_8,
			SW_SUCCESS);
		if (ret != 0) {
			looper_abort(G, swicol, ofd);
			goto error_out;
			retval = -1;
		}
	} else {
		ret = swpl_unpack_catalog_tarfile(swi, ofd,
			catalog_entry_directory,
			pax_read_command,
			0 /*alt_catalog_root*/,
			G->g_swi_event_fd);
		if (ret != 0) {
			looper_abort(G, swicol, ofd);
			goto error_out;
			retval = -1;
		}
	}

	/* SWBIS_TS_retrieve_files_archive */
	if (
		G->g_opt_previewM ||
		do_skip_entry ||
		0
	) {
		ret = swpl_send_nothing_and_wait(swicol, ofd, G->g_swi_event_fd,
                        SWBIS_TS_retrieve_files_archive,
			SWICOL_TL_8,
			SW_SUCCESS);
		if (ret != 0) {
			looper_abort(G, swicol, ofd);
			goto error_out;
			retval = -1;
		}
	} else {
		if (1 && 0) {
			ret = swpl_send_nothing_and_wait(swicol, ofd, G->g_swi_event_fd,
       		                SWBIS_TS_retrieve_files_archive,
				SWICOL_TL_8,
				SW_SUCCESS);
			if (ret != 0) {
				looper_abort(G, swicol, ofd);
				goto error_out;
				retval = -1;
			}
		} else {
			E_DEBUG("");
			archive_fd = swlib_open_memfd();
			SWLIB_ASSERT(archive_fd > 0);

			E_DEBUG("");
			ret = swpl_retrieve_files(G, swi, swicol, e, file_list,
					ofd, ifd, archive_fd,
					pax_write_command_key, -1, -1, (FILE_DIGS*)NULL);
			E_DEBUG("");
			if (ret < 0) {
				rstatus = 8;
				retval = -1;
				looper_abort(G, swicol, ofd);
				goto error_out;
			} else if (ret > 0) {
				rstatus = 9;
			}
		}
	}

	/* Compare 
	   The authenticated metatdata is in (SWI*)swi and the tar archive of the
	   files is in (int)archive_fd */
	E_DEBUG("");

	if (archive_fd > 0) {
		/* compare the archive to the catalog */
		ret = compare_fileset(G, strob_str(swspec_string), target_path, uts, swi, archive_fd, compare_status);
		uxfio_close(archive_fd);
	}
	archive_fd = -1;

	E_DEBUG("");

	/* SWBIS_TS_Catalog_dir_remove
	   This removes the .../export/ directory
	   for example: var/lib/swbis/catalog/foo/foo/1.1/0/export */

	if (
		G->g_opt_previewM ||
		do_skip_entry ||
		0
	) {
		ret = swpl_send_nothing_and_wait(swicol, ofd, G->g_swi_event_fd,
                        SWBIS_TS_Catalog_dir_remove,
			SWICOL_TL_8,
			SW_SUCCESS);
		if (ret != 0) {
			looper_abort(G, swicol, ofd);
			goto error_out;
			retval = -1;
		}
        } else {
                /* TS_Catalog_dir_remove */
                ret = swpl_remove_catalog_directory(swi, ofd,
			catalog_entry_directory,
			pax_read_command,
			0 /*alt_catalog_root*/,
			G->g_swi_event_fd);
		if (ret != 0) {
			looper_abort(G, swicol, ofd);
			goto error_out;
			retval = -1;
		}
	}

	E_DEBUG("");
	/* SWBIS_TS_post_verify */
	if (
		G->g_opt_previewM ||
		do_skip_entry ||
		0 ||
		1  /* Temporary */
	) {
		ret = swpl_send_nothing_and_wait(swicol, ofd, G->g_swi_event_fd,
                        SWBIS_TS_post_verify,
			SWICOL_TL_8,
			SW_SUCCESS);
		if (ret != 0) {
			retval = -1;
		}
	} else {
		/* post verify */
	}

	E_DEBUG("");
	error_out:
	E_DEBUG("");

	strob_close(tmp);
	strob_close(btmp);
	strob_close(btmp2);
	strob_close(swspec_string);
	E_DEBUG("");
	if (file_list)
		swi_fl_delete(file_list);

	if (G->g_do_debug_events)
		swicol_show_events_to_fd(swicol, STDERR_FILENO, -1);
	if (rstatus)
		*p_rp_status = rstatus;
	if (swi) swi_delete(swi);
	if (swgpg) swgpg_delete(swgpg);
	if (e) swicat_e_delete(e);
	if (G->devel_verboseM)
		SWLIB_INFO2("NOPEN=%d", uxfio_uxfio_get_nopen());
	E_DEBUG("");
	return retval;
}
