/* btjdel.c -- main program for gbch-jdel

   Copyright 2009 Free Software Foundation, Inc.

   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 of the License, 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, see <http://www.gnu.org/licenses/>.  */

#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <errno.h>
#ifdef	HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "incl_unix.h"
#include "incl_sig.h"
#include "incl_net.h"
#include "defaults.h"
#include "network.h"
#include "incl_ugid.h"
#include "btconst.h"
#include "btmode.h"
#include "btvar.h"
#include "timecon.h"
#include "bjparam.h"
#include "btjob.h"
#include "cmdint.h"
#include "btuser.h"
#include "ecodes.h"
#include "statenums.h"
#include "errnums.h"
#include "ipcstuff.h"
#include "shreq.h"
#include "files.h"
#include "helpargs.h"
#include "cfile.h"
#include "jvuprocs.h"
#include "optflags.h"
#include "cgifndjb.h"
#include "shutilmsg.h"

#define	DEFSLEEP	4

char	*Curr_pwd;

#define	IPC_MODE	0600

#ifndef	USING_FLOCK
int	Sem_chan;
#endif

ULONG		indx;

int	killtype = SIGTERM;
unsigned	sleeptime = DEFSLEEP;

int	exit_code,
	force,
	nodel,
	unqueue;

char	*jobprefix,
	*cmdprefix;

enum	cmdtype	{ CMD_DELETE, CMD_GO, CMD_GOADV, CMD_ADV };

void  nomem(const char *fl, const int ln)
{
	fprintf(stderr, "%s:Mem alloc fault: %s line %d\n", progname, fl, ln);
	exit(E_NOMEM);
}

/* Run job dump program if possible */

static void  dounqueue(CBtjobRef jp)
{
	PIDTYPE	pid;
	int	ac;
	char	*argv[7], *udprog, jnobuf[HOSTNSIZE+10], cprefbuf[30], jprefbuf[30];

	if  ((pid = fork()))  {
		int	status;

		if  (pid < 0)  {
			print_error($E{btjdel cannot fork});
			return;
		}
#ifdef	HAVE_WAITPID
		while  (waitpid(pid, &status, 0) < 0)
			;
#else
		while  (wait(&status) != pid)
			;
#endif
		if  (status == 0)	/* All ok */
			return;
		if  (status & 0xff)  {
			disp_arg[0] = status & 0xff;
			print_error($E{Unqueue process aborted});
			return;
		}
		status = (status >> 8) & 0xff;
		disp_arg[0] = jp->h.bj_job;
		disp_str = title_of(jp);
		switch  (status)  {
		default:
			disp_arg[1] = status;
			print_error($E{Undef err unq});
			return;
		case  E_JDFNFND:
			print_error($E{Unq file not found});
			return;
		case  E_JDNOCHDIR:
			print_error($E{Unq dir not found});
			return;
		case  E_JDFNOCR:
			print_error($E{Unq cannot create file});
			return;
		case  E_JDJNFND:
			print_error($E{Unq job not found});
			return;
		case  E_CANTDEL:
			print_error($E{Unq cannot del});
			return;
		}
	}

	/* Child process */

	udprog = envprocess(DUMPJOB);
	setuid(Realuid);
	Ignored_error = chdir(Curr_pwd);	/* So that it picks up config file correctly */
	if  (jp->h.bj_hostid)
		sprintf(jnobuf, "%s:%ld", look_host(jp->h.bj_hostid), (long) jp->h.bj_job);
	else
		sprintf(jnobuf, "%ld", (long) jp->h.bj_job);
	sprintf(cprefbuf, "%s%.6ld", cmdprefix, (long) jp->h.bj_job);
	sprintf(jprefbuf, "%s%.6ld", jobprefix, (long) jp->h.bj_job);
	if  (!(argv[0] = strrchr(udprog, '/')))
		argv[0] = udprog;
	else
		argv[0]++;
	ac = 0;
	if  (nodel)
		argv[++ac] = "-n";
	argv[++ac] = jnobuf;
	argv[++ac] = job_cwd;
	argv[++ac] = cprefbuf;
	argv[++ac] = jprefbuf;
	argv[++ac] = (char *) 0;
	execv(udprog, argv);
	exit(E_SETUP);
}

/* This is the main processing routine for delete */

void  process(char **joblist)
{
	char	*jobc, **jobp;
	int	sleepfor = 0;

	for  (jobp = joblist;  (jobc = *jobp);  jobp++)  {
		int			retc;
		struct	jobswanted	jw;
		CBtjobRef		jp;

		if  ((retc = decode_jnum(jobc, &jw)) != 0)  {
			print_error(retc);
			exit_code = E_NOJOB;
			continue;
		}
		if  (!find_job(&jw))  {
			disp_str = jobc;
			print_error($E{Cannot find job});
			exit_code = E_NOJOB;
			continue;
		}

		jp = jw.jp;
		if  (jp->h.bj_progress >= BJP_STARTUP1)  {
			if  (!force)  {
				disp_str = jobc;
				print_error($E{btjdel job running});
				continue;
			}
			wjimsg_param(J_KILL, killtype, jp);
			if  ((retc = readreply()) != J_OK)
				print_error(dojerror(retc, jp));
			else
				sleepfor++;
			continue;
		}
		if  (unqueue)
			dounqueue(jp);
		else  if  (!nodel)  {
			wjimsg(J_DELETE, jp);
			if  ((retc = readreply()) != J_OK)
				print_error(dojerror(retc, jp));
		}
	}

	if  (sleepfor)  {
		sleep(sleeptime);
		rjobfile(1);

		for  (jobp = joblist;  (jobc = *jobp);  jobp++)  {
			int			retc;
			struct	jobswanted	jw;
			CBtjobRef		jp;

			if  (decode_jnum(jobc, &jw) != 0)
				continue;

			if  (!find_job(&jw))
				continue;

			jp = jw.jp;

			if  (jp->h.bj_progress >= BJP_STARTUP1)  {
				disp_str = jobc;
				disp_str2 = title_of(jp);
				disp_arg[0] = sleeptime;
				print_error($E{btjdel job still running});
				exit_code = E_FALSE;
				continue;
			}

			if  (unqueue)
				dounqueue(jp);
			else  if  (!nodel)  {
				wjimsg(J_DELETE, jp);
				if  ((retc = readreply()) != J_OK)
					dojerror(retc, jp);
			}
		}
	}
}

/* This is the main processing routine for other operations */

void  cprocess(enum cmdtype which, char **joblist)
{
	unsigned  cmd = which == CMD_GOADV? J_FORCE: J_FORCENA;
	char	*jobc, **jobp;

	for  (jobp = joblist;  (jobc = *jobp);  jobp++)  {
		int			retc;
		struct	jobswanted	jw;
		CBtjobRef		jp;

		if  ((retc = decode_jnum(jobc, &jw)) != 0)  {
			print_error(retc);
			exit_code = E_NOJOB;
			continue;
		}

		if  (!find_job(&jw))  {
			disp_str = jobc;
			print_error($E{Cannot find job});
			exit_code = E_NOJOB;
			continue;
		}

		jp = jw.jp;

		if  (jp->h.bj_progress >= BJP_STARTUP1)  {
			if  (!force)  {
				disp_str = jobc;
				print_error($E{btjdel job running});
				continue;
			}
			continue;
		}
		else  {
			wjimsg(cmd, jp);
			if  ((retc = readreply()) != J_OK)
				dojerror(retc, jp);
		}
	}
}

void  advprocess(char **joblist)
{
	char	*jobc, **jobp;

	for  (jobp = joblist;  (jobc = *jobp);  jobp++)  {
		int			retc;
		struct	jobswanted	jw;
		CBtjobRef		jp;

		if  ((retc = decode_jnum(jobc, &jw)) != 0)  {
			print_error(retc);
			exit_code = E_NOJOB;
			continue;
		}

		if  (!find_job(&jw))  {
			disp_str = jobc;
			print_error($E{Cannot find job});
			exit_code = E_NOJOB;
			continue;
		}

		jp = jw.jp;

		if  (jp->h.bj_progress >= BJP_STARTUP1)  {
			disp_str = jobc;
			print_error($E{btjdel job running});
			continue;
		}
		if  (!jp->h.bj_times.tc_istime)
			continue;
		*JREQ = *jp;
		JREQ->h.bj_times.tc_nexttime = advtime(&JREQ->h.bj_times);
		wjxfermsg(J_CHANGE, indx);
		if  ((retc = readreply()) != J_OK)
			dojerror(retc, jp);
	}
}


#define	BTJDEL_INLINE

OPTION(o_explain)
{
	print_error($E{btjdel explain});
	exit(0);
	return  0;		/* Silence compilers */
}

OPTION(o_noforce)
{
	force = 0;
	return  OPTRESULT_OK;
}

OPTION(o_force)
{
	force = 1;
	return  OPTRESULT_OK;
}

OPTION(o_nodel)
{
	nodel = 1;
	return  OPTRESULT_OK;
}

OPTION(o_del)
{
	nodel = 0;
	return  OPTRESULT_OK;
}

OPTION(o_signo)
{
	if  (!arg)
		return  OPTRESULT_MISSARG;
	killtype = atoi(arg);
	if  (killtype <= 0 || killtype >= NSIG)  {
		disp_arg[0] = 0;
		disp_arg[1] = NSIG;
		arg_errnum = $E{Bad kill type};
		return  OPTRESULT_ERROR;
	}
	return  OPTRESULT_ARG_OK;
}

OPTION(o_sleeptime)
{
	if  (!arg)
		return  OPTRESULT_MISSARG;
	sleeptime = atoi(arg);
	return  OPTRESULT_ARG_OK;
}

OPTION(o_nounqueue)
{
	unqueue = 0;
	return  OPTRESULT_OK;
}

OPTION(o_unqueue)
{
	unqueue = 1;
	return  OPTRESULT_OK;
}

OPTION(o_jobprefix)
{
	if  (!arg)
		return  OPTRESULT_MISSARG;
	free(jobprefix);
	jobprefix = stracpy(arg);
	return  OPTRESULT_ARG_OK;
}

OPTION(o_cmdprefix)
{
	if  (!arg)
		return  OPTRESULT_MISSARG;
	free(cmdprefix);
	cmdprefix = stracpy(arg);
	return  OPTRESULT_ARG_OK;
}

DEOPTION(o_directory);
DEOPTION(o_freezecd);
DEOPTION(o_freezehd);

/* Defaults and proc table for arg interp.  */

const	Argdefault	Adefs[] = {
  { '?', $A{btjdel arg explain} },
  { 'n', $A{btjdel arg noforce} },
  { 'N', $A{btjdel arg noforce} },
  { 'y', $A{btjdel arg force} },
  { 'Y', $A{btjdel arg force} },
  { 'k', $A{btjdel arg nodel} },
  { 'd', $A{btjdel arg del} },
  { 'K', $A{btjdel arg wsig} },
  { 'S', $A{btjdel arg sleep} },
  { 'u', $A{btjdel arg unqueue} },
  { 'e', $A{btjdel arg nounqueue} },
  { 'J', $A{btjdel arg jobpref} },
  { 'C', $A{btjdel arg cmdpref} },
  { 'D', $A{btjdel arg dir} },
  { 0, 0 }
};

optparam	optprocs[] = {
o_explain,	o_noforce,	o_force,	o_nodel,
o_del,		o_signo,	o_sleeptime,	o_unqueue,
o_nounqueue,	o_jobprefix,	o_cmdprefix,	o_directory,
o_freezecd,	o_freezehd
};

void  spit_options(FILE *dest, const char *name)
{
	int	cancont = 0;
	fprintf(dest, "%s", name);
	cancont = spitoption(force? $A{btjdel arg force}: $A{btjdel arg noforce}, $A{btjdel arg explain}, dest, '=', 0);
	cancont = spitoption(nodel? $A{btjdel arg nodel}: $A{btjdel arg del}, $A{btjdel arg explain}, dest, ' ', cancont);
	cancont = spitoption(unqueue? $A{btjdel arg unqueue}: $A{btjdel arg nounqueue}, $A{btjdel arg explain}, dest, ' ', cancont);
	spitoption($A{btjdel arg wsig}, $A{btjdel arg explain}, dest, ' ', 0);
	fprintf(dest, " %d", killtype);
	spitoption($A{btjdel arg sleep}, $A{btjdel arg explain}, dest, ' ', 0);
	fprintf(dest, " %d", sleeptime);
	spitoption($A{btjdel arg jobpref}, $A{btjdel arg explain}, dest, ' ', 0);
	fprintf(dest, " %s", jobprefix);
	spitoption($A{btjdel arg cmdpref}, $A{btjdel arg explain}, dest, ' ', 0);
	fprintf(dest, " %s", cmdprefix);
	if  (Procparchanges & OF_DIR_CHANGES)  {
		spitoption($A{btjdel arg dir}, $A{btjdel arg explain}, dest, ' ', 0);
		fprintf(dest, " %s", job_cwd);
	}
	putc('\n', dest);
}

/* Ye olde main routine.  */

MAINFN_TYPE  main(int argc, char **argv)
{
#if	defined(NHONSUID) || defined(DEBUG)
	int_ugid_t	chk_uid;
#endif
	enum	cmdtype	which = CMD_DELETE;

	versionprint(argv, "$Revision: 1.6 $", 0);

	if  ((progname = strrchr(argv[0], '/')))
		progname++;
	else
		progname = argv[0];

	if  (ncstrcmp(progname, "btjgo") == 0)
		which = CMD_GO;
	else  if  (ncstrcmp(progname, "btjgoadv") == 0)
		which = CMD_GOADV;
	else  if  (ncstrcmp(progname, "btjadv") == 0)
		which = CMD_ADV;

	init_mcfile();

	Realuid = getuid();
	Realgid = getgid();
	Effuid = geteuid();
	Effgid = getegid();
	INIT_DAEMUID
	Cfile = open_cfile(MISC_UCONFIG, "btrest.help");
	jobprefix = gprompt($P{Default job file prefix});
	cmdprefix = gprompt($P{Default cmd file prefix});
	SCRAMBLID_CHECK
	SWAP_TO(Daemuid);
	mypriv = getbtuser(Realuid);
	SWAP_TO(Realuid);
	argv = optprocess(argv, Adefs, optprocs, $A{btjdel arg explain}, $A{btjdel arg freeze home}, 0);

	if  (unqueue)  {
		if  (!Curr_pwd  &&  !(Curr_pwd = getenv("PWD")))
			Curr_pwd = runpwd();
		if  (!job_cwd)
			job_cwd = Curr_pwd;
	}

	SWAP_TO(Daemuid);

#define	FREEZE_EXIT
#include "inline/freezecode.c"

	if  (argv[0] == (char *) 0)  {
		print_error($E{No jobs specified});
		exit(E_USAGE);
	}

	/* Grab message id */

	if  ((Ctrl_chan = msgget(MSGID+envselect_value, 0)) < 0)  {
		print_error($E{Scheduler not running});
		exit(E_NOTRUN);
	}

#ifndef	USING_FLOCK
	/* Set up semaphores */

	if  ((Sem_chan = semget(SEMID+envselect_value, SEMNUMS + XBUFJOBS, IPC_MODE)) < 0)  {
		print_error($E{Cannot open semaphore});
		exit(E_SETUP);
	}
#endif

	/* Open the other files.  */

	openjfile(0, 0);
	rjobfile(1);
	switch  (which)  {
	default:
		process(&argv[0]);
		break;
	case  CMD_GO:
	case  CMD_GOADV:
		cprocess(which, &argv[0]);
		break;
	case  CMD_ADV:
		initxbuffer(0);
		JREQ = &Xbuffer->Ring[indx = getxbuf()];
		advprocess(&argv[0]);
		break;
	}

	return  exit_code;
}
