/* setimgr - Copyright (c) 2000 Bernard Hatt  */
/* modified 2003 by RTC <www.linux-related.de> */
#define VERSION	"0.04e-1"
/* latest change Jul 08, 2003 */

#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

/*
 * This program may be freely used and distributed as modified or
 * unmodified in source or binary form provided that:
 * a) I am acknowledged as the copyright owner of the original source.
 * b) All source distributions contain this complete list of conditions
 *    copyright notice and disclaimer.
 * c) All distibutions make it clear whether or not it is a version
 *    modified by someone other than myself.
 * d) All binary distributions make it clear that the source for the
 *    original unmodified version is available for free.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Bernard Hatt - 11th Febuary 2002
 * bmh@arkady.demon.co.uk
 */

/* Changes since v.0.04e
 *
 **	all changes in version 0.04e-1 are made by RTC <www.linux-ralted.de/rtc@linux-related.de>
 **	new command line options:
 *		-s		to print the current status of the WUs
 *				(WUs get not loaded, setimgr is exiting afterwards)
 *		-k alarm	to send SIGALRM to setimgr
 */

/*
 * Changes since v0.03a
 *
 * Major Changes 
 *
 **	Several setimgr's can operate on the same set of procn directories, so
 *	that several machines can use the same NFS mounted cache. (Contributed
 *	by Brian Shaver) 
 **	It is now possible to restrict the times/days which setimgr will up/down
 *	load WU's. (to avoid peak times on your network/ISP or at setiathome). 
 *
 * Minor Changes 
 *
 **	setimgr creates a setimgr.pid file so that scripts can send it signals
 *	more easily
 **	If setimgr gets sent SIGTERM it is passed onto all setiathome children before
 *  setimgr exits. 
 **	Code tidy up (mostly Brian Shaver) 
 **	Config file can be specified with -c if you don't want the default. 
 **	Default config file can be created with -C filename.conf 
 */

/* Best viewed with tabs=4 , compile with "cc setimgr.c -o setimgr" */

/* ================ CONFIGURATION SECTION ================ */

/*
 * Most of the configuration is in the file "setimgr.conf" which
 * should be in the current directory. Use "setimgr -C setimgr.conf"
 * to create a default config file.
 */

	/* Max number of proc<n> directories to use */
#define MAXDIR	32

	/* do you want timestamps and upload/down load time restrictions */
	/* to be local or GMT based (1=local, 0=GMT) */
#define LOCAL_TIME	1

/* PIDFILE now a config item "pid_file" - set to name of file */
/* or "none" to disable this feature */

/* ================ END OF CONFIG SECTION ================ */

	/* defaults for integer config items in setimgr.conf */
int nproc=1,nxfer=3,timer=3600,process_delay=5,xfer_delay=30, state=0;
int max_fail=3,max_attempt=3;
	/* not used, but left in to allow old config files to work */
int stuck_wait=0,stuck_level=0;

#define T_ENV	1
#define T_NUM	2
#define T_STR	3

typedef struct config_struct
{
	char *name;
	char *comment;
	int type;
	void *value;
} CONFIG;

CONFIG *findconfig(char *name);

CONFIG config[]=
{
	{"nproc","Number of seti instances to run simultaniously",T_NUM,&nproc},
	{"nxfer","Max number of donwloads to run in parallel",T_NUM,&nxfer},
	{"base_dir","location of procN directories, defaults to current directory '.'",T_STR,"."},
	{"env","Environment variable to set in the form VAR=value" ,T_ENV,"HTTP_PROXY=some_proxy_host.domain"},
	{"start","Actions at startup (\"load\", \"status\" and/or \"rescue\")",T_STR,"status,load"},
	{"sighup","Actions on receiving SIGHUP (\"load\", \"status\" or \"load,status\")",T_STR,"load,status"},
	{"alarm","Actions on alarm timeout (\"load\", \"status\" or \"load,status\")",T_STR,"status,load"},
	{"finish","Actions on finishing a WU (\"load\" or \"none\")",T_STR,"load"},
	{"timer","Timer interval (seconds)",T_NUM,&timer},
	{"xfer_del","Delay after starting a transfer (seconds)",T_NUM,&xfer_delay},
	{"process_del","Delay after starting a process (seconds)",T_NUM,&process_delay},
	{"xfer_log","Name of file for logging tranfers (or stdout or /dev/null)",T_STR,"xfer.log"},
	{"process_log","Name of file for logging processing (or stdout or /dev/null)",T_STR,"process.log"},
	{"max_fail","Number of xfers which have to fail before we give up on a transfer attempt",T_NUM,&max_fail},
#if 1	/* remove these in the next version ? */
	{"stuck_wait","stuck_wait not used (removed in 0.04e)",T_NUM,&stuck_wait},
	{"stuck_level","stuck_level not used (removed in 0.04e)",T_NUM,&stuck_level},
#endif
 	{"max_attempt","Number of failed executions in a single directory before moving on",T_NUM,&max_attempt},
	{"pid_file","Name of file where setimgr will store it's pid (or \"none\" to disable)",T_STR,"setimgr.pid"},
	{"seti_binary","Name of the SETI@Home binary, as run from the proc[n] directory",T_STR,"../setiathome"},
	{"xfer_day_default","Times that transfers are allowed - Default Day",T_STR,"y"},
	{"xfer_day_sun","Times that transfers are allowed - Sunday",T_STR,NULL},
	{"xfer_day_mon","Times that transfers are allowed - Monday",T_STR,NULL},
	{"xfer_day_tue","Times that transfers are allowed - Tuesday",T_STR,NULL},
	{"xfer_day_wed","Times that transfers are allowed - Wednesday",T_STR,NULL},
	{"xfer_day_thu","Times that transfers are allowed - Thursday",T_STR,NULL},
	{"xfer_day_fri","Times that transfers are allowed - Friday",T_STR,NULL},
	{"xfer_day_sat","Times that transfers are allowed - Saturday",T_STR,NULL},
	{NULL,NULL,0,NULL}
};

#define S_NONE		0	/* no process dir */
#define S_RUN		1	/* running - processing */
#define S_READY		2	/* ready to run */
#define S_XFER		3	/* running - returning result */
#define S_RETURN	4	/* ready to return result */

char *s_text[]={"None","Run","Ready","Xfer","Done"};

typedef struct proc_struct
{
	int pid;
	int status;
	time_t stime;
	time_t wtime;
	char *dirname;
	int attempts;
	float doneness;
	int xfer_reason;
} PROC;

PROC proc[MAXDIR];

int runnum=0;
int runable=0;
int xfernum=0;
int xferable=0;
int xfer_failed=0;
int xfer_stopped=0;
char *config_file="setimgr.conf";
char *pid_file=NULL;

#if LOCAL_TIME
#define SPLITTIME(x)	localtime(x)
#else
#define SPLITTIME(x)	gmtime(x)
#endif


/*
 * A version of signal() which does what I expect
 * ie. for wait() to exit when the signal arrives
 * as stated in the manual page.
 * (Some UNIX versions seem to think that wait() is
 * restartable system call, but I don't !).
 */

void
mysignal(int signum, void (*sigfunc)())
{
	struct sigaction sa;
	sa.sa_handler=sigfunc;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags=0;

	sigaction(signum,&sa,NULL);
}

	/* modification time of dname/fname */
time_t
filetime(char *dname,char *fname)
{
	struct stat sbuf;
	int r;
	char *name;
	name=malloc(strlen(dname)+strlen(fname)+5);
	sprintf(name,"%s/%s",dname,fname);

	r=stat(name,&sbuf);
	free(name);
	if(r<0)
		return(-1);
	else
		return(sbuf.st_mtime);
}

int
xfer_ok(void)
{
	time_t tnow;
	int tindex;
	struct tm *now;
	char *str=NULL;

	tnow=time(0);
	now=SPLITTIME(&tnow);
	switch(now->tm_wday)
	{
	case 0:
		str=findconfig("xfer_day_sun")->value;
		break;
	case 1:
		str=findconfig("xfer_day_mon")->value;
		break;
	case 2:
		str=findconfig("xfer_day_tue")->value;
		break;
	case 3:
		str=findconfig("xfer_day_wed")->value;
		break;
	case 4:
		str=findconfig("xfer_day_thu")->value;
		break;
	case 5:
		str=findconfig("xfer_day_fri")->value;
		break;
	case 6:
		str=findconfig("xfer_day_sat")->value;
		break;
	}
	if(str==NULL)
		str=findconfig("xfer_day_default")->value;
	if((str!=NULL)&&(strlen(str)>0))
	{
		tindex=(((now->tm_hour)*60)+now->tm_min)*60+now->tm_sec;
		tindex=(tindex*strlen(str))/(24*60*60);
		if(str[tindex]!='y')
			return(0);
	}
	return(1);
}

	/* reason for requesting a return */
#define R_NONE		0	/* no return requested */
#define R_REQUEST	1	/* user requested (SIGHUP) */
#define R_FINISH	2	/* finished WU */
#define R_TIMER		3	/* timer (SIGALRM) */
#define R_START		4	/* startup */

	/* run a return of a WU and a fetch */
void
run_return(int n)
{
	int cpid,fd;
	char *lfile;

		/* re-check time, as a lot of units transferred sequentialy */
		/* could take some time */
	if(proc[n].xfer_reason!=R_REQUEST)
	{
		if(!xfer_ok())
		{
			xfer_stopped=1;
			return;
		}
	}

	proc[n].xfer_reason=R_NONE;

	fflush(stdout);
	fflush(stderr);
	cpid=fork();
	switch(cpid)
	{
	case 0:	/* child process */
		chdir(proc[n].dirname);
		lfile=findconfig("xfer_log")->value;
		if(strcmp(lfile,"stdout")!=0)
		{
			fd=open(lfile,O_WRONLY|O_CREAT|O_TRUNC,0644);
			if(fd<0)
			{
				perror(lfile);
				exit(1);
			}
			dup2(fd,1);
			dup2(fd,2);
		}

		execl(findconfig("seti_binary")->value,proc[n].dirname,"-stop_after_xfer","-nice","20",0);

		perror("Exec of setiathome");
		exit(1);
		break;
	case -1:
		printf("fork() failed\n");
		proc[n].pid=0;
		break;
	default:
		proc[n].pid=cpid;
		proc[n].status=S_XFER;
		proc[n].stime=time(0);
		printf("Started xfer pid=%d in dir proc%d at %s",cpid,n,ctime((time_t*)&proc[n].stime));
		xferable--;
		xfernum++;
		fflush(stdout);
		sleep(*(int*)findconfig("xfer_del")->value);
	}
}


	/* start a transfer */
void
start_xfer(int reason)
{
	int i;

		/* if the return was not user requested check if we are */
		/* allowed up up/download at this time */
	if(reason!=R_REQUEST)
	{
		if(!xfer_ok())
		{
			xfer_stopped=1;
			return;
		}
	}
	for(i=0;i<MAXDIR;i++)
		if(proc[i].status==S_RETURN)
			proc[i].xfer_reason=reason;
	xfer_failed=0;
	xfer_stopped=0;
	for(i=0;i<MAXDIR;i++)
	{
		if(xfernum>=nxfer)
			break;
		if(proc[i].xfer_reason!=R_NONE)
			run_return(i);
	}
}

	/* process a work unit */
void
run_process(int n)
{
	int cpid,fd;
	char *lfile;
	fflush(stdout);
	fflush(stderr);
	cpid=fork();
	switch(cpid)
	{
	case 0:	/* child process */
		chdir(proc[n].dirname);
		lfile=findconfig("process_log")->value;
		if(strcmp(lfile,"stdout")!=0)
		{
			fd=open(lfile,O_WRONLY|O_CREAT|O_TRUNC,0644);
			if(fd<0)
			{
				perror(lfile);
				exit(1);
			}
			dup2(fd,1);
			dup2(fd,2);
		}

		execl(findconfig("seti_binary")->value,proc[n].dirname,"-stop_after_process","-nice","20",0);

		perror("Exec of setiathome");
		exit(1);
		break;
	case -1:
		printf("fork() failed\n");
		proc[n].pid=0;
		break;
	default:
		proc[n].status=S_RUN;
		proc[n].pid=cpid;
		proc[n].stime=time(0);
		proc[n].attempts=0;
		runable--;
		runnum++;
		printf("Started process pid=%d in dir proc%d at %s",cpid,n,ctime((time_t*)&proc[n].stime));
		fflush(stdout);
		sleep(*(int*)findconfig("process_del")->value);
	}
}

	/* setiathome seems to use fcntl() rather than flock() */
int
check_lock(char *dname, char *fname)
{
	struct flock f;
	char *name;
	int fd,r;

	name=malloc(strlen(dname)+strlen(fname)+5);
	sprintf(name,"%s/%s",dname,fname);

	fd=open(name,O_RDWR);
	free(name);
	if(fd<0)
		return(0);
	f.l_type=F_WRLCK;
	f.l_whence=SEEK_SET;
	f.l_start=0;
	f.l_len=0;
	r=fcntl(fd,F_GETLK,&f);
	close(fd);
	if(r<0)
		return(0);
	if(f.l_type==F_UNLCK)
		return(0);
	return(1);
}

void
run_old(void)
{
	int i,oldn=(-1);
	if(runnum>=nproc)
		return;
	for(i=0;i<MAXDIR;i++)
	{
		if((proc[i].status==S_READY)&&(proc[i].attempts < max_attempt))
		{
			if(check_lock(proc[i].dirname,"lock.sah"))
				continue;
			if(oldn==(-1))
			{
				oldn=i;
			}
			else
			{
				if((proc[i].wtime<proc[oldn].wtime)||(proc[i].attempts<proc[oldn].attempts))
					oldn=i;
			}
		}
	}
	if(oldn>=0)
		run_process(oldn);
}

#define STATE_INIT	"start"
#define STATE_ALARM	"alarm"
#define STATE_HUP	"sighup"
char *wait_state=STATE_INIT;


void
do_alarm(int x)
{
	mysignal(SIGALRM,do_alarm);
	alarm(timer);
	wait_state=STATE_ALARM;
}

void
do_sighup(int x)
{
	mysignal(SIGHUP,do_sighup);
	wait_state=STATE_HUP;
}

	/* sigterm caught - clean up setiathome child processes */
void
do_sigterm(int x)
{
	int i;
	printf("Got SIGTERM\n");
	for(i=0;i<MAXDIR;i++)
	{
		if(((proc[i].status==S_RUN)||(proc[i].status==S_XFER))&&(proc[i].pid!=0))
		{
			printf("killing process proc%d [%d] (status %s)\n",i,proc[i].pid,s_text[proc[i].status]);
			kill(proc[i].pid,SIGTERM);
		}
	}
	if(pid_file!=NULL)
		unlink(pid_file);
	printf("Exiting\n");
	fflush(stdout);
	exit(1);
}

void
writeconfig(char *fname)
{
	FILE *fp;
	CONFIG *c;

	if(strcmp(fname,"-")==0)
		fp=stdout;
	else
		fp=fopen(fname,"w");
	if(fp==NULL)
	{
		perror(fname);
		exit(1);
	}

	c=config;
	while(c->name!=NULL)
	{
		fprintf(fp,"# %s\n",c->comment);
		switch(c->type)
		{
		case T_NUM:
			if(c->value==NULL)
				fprintf(fp,"# %s\n",c->name);
			else
				fprintf(fp,"%s %d\n",c->name,*(int*)c->value);
			break;
		case T_STR:
		case T_ENV:
			if(c->value==NULL)
				fprintf(fp,"# %s\n",c->name);
			else
				fprintf(fp,"%s %s\n",c->name,(char*)c->value);
			break;
		}
		c++;
	}
	if(fp!=stdout)
		fclose(fp);
}

void
getconfig(void)
{
	FILE *fp;
	char buf[256],*var,*val;
	CONFIG *c;
	fp=fopen(config_file,"r");
	if(fp==NULL)
	{
		perror(config_file);
		printf("No config file found, specifiy an existing one with -c or create one with -C setimgr.conf\n");
		exit(1);
	}
	while(fgets(buf,255,fp)!=NULL)
	{
		if(*buf=='#')
			continue;
		var=strtok(buf," \t\n");
		if(var==NULL)
			continue;
		val=strtok(NULL," \t\n");
		if(val==NULL)
		{
			printf("variable %s no value\n",var);
			exit(1);
		}
		c=config;
		while(c->name!=NULL)
		{
			if(strcmp(c->name,var)==0)
			{
				switch(c->type)
				{
				case T_NUM:
					*(int*)c->value=atoi(val);
					break;
				case T_STR:
					c->value=strdup(val);
					break;
				case T_ENV:
					putenv(strdup(val));
					break;
				}
				break;
			}
			c++;
		}
		if(c->name==NULL)
		{
			printf("variable %s unknown\n",var);
			exit(1);
		}
	}
	fclose(fp);
}

CONFIG *
findconfig(char *name)
{
	CONFIG *c;
	c=config;
	while(c->name!=NULL)
	{
		if(strcmp(c->name,name)==0)
			return(c);
		c++;
	}
	printf("Invalid config lookup variable=%s\n",name);
	if(pid_file!=NULL)
		unlink(pid_file);
	exit(1);
}


int
set_status(int n)
{
	FILE *fp;
	char *name;
	char buf[256];
	int old_stat,i;

	float done;
	if ( ! proc[n].dirname )
	{
		proc[n].status = S_NONE;
		proc[n].doneness = 0;
		return(S_NONE);
	}

	old_stat=proc[n].status;

	if(filetime(proc[n].dirname,"user_info.sah")<0)
	{
		printf("Found dir proc%d, has no user_info.sah - IGNORED\n",n);
		proc[n].status=S_NONE;
	}
	else if( ( filetime(proc[n].dirname,"result.sah")>0) ||
			 ( filetime(proc[n].dirname,"key.sah")<0) )
	{
		proc[n].status=S_RETURN;
	}
	else if ( ( proc[n].wtime=filetime(proc[n].dirname,"work_unit.sah") ) > 0 )
	{
		proc[n].doneness = 0.0;
		if(old_stat==S_RUN && proc[n].pid)
			proc[n].status=S_RUN;
		else
			proc[n].status=S_READY;
	}

	if(old_stat==S_XFER)
	{
		switch(proc[n].status)
		{
		case S_XFER:
			if(proc[n].pid!=0)
				break;
			/* fall through */
		case S_NONE: 	/* partial transfer, upload OK, download failed */
			proc[n].status=S_RETURN;
			break;
		case S_READY:	/* transfer OK */
			proc[n].attempts=0;
			break;
		case S_RETURN:	/* transfer failed */
			break;
		}
	}
	if(old_stat==S_RETURN)
	{
		switch(proc[n].status)
		{
		case S_NONE:	/* another process did a partial transfer */
			proc[n].status=S_RETURN;
			break;
		case S_READY:	/* another process did a complete transfer */
			proc[n].attempts=0;
			break;
		}
	}

	/* set doneness */
	name=malloc(strlen(proc[n].dirname)+16);
	sprintf(name,"%s/state.sah",proc[n].dirname);
	fp=fopen(name,"r");
	free(name);
	if(fp==NULL)
		proc[n].doneness = 100.0;
	else
	{
		while(fgets(buf,255,fp)!=NULL)
		{
			if(strncmp(buf,"prog=",5)==0)
			{
				sscanf(buf,"prog=%f",&done);
				proc[n].doneness = done * 100.0;
				break;
			}
		}
		fclose(fp);
	}

		/* reset stats from scratch as they could have been changed */
		/* as the result of another setimgr process */
	runnum=0;
	runable=0;
	xfernum=0;
	xferable=0;
	for(i=0;i<MAXDIR;i++)
	{
		switch(proc[i].status)
		{
		case S_RUN:
			runnum++;
			break;
		case S_READY:
			if(proc[i].attempts<max_attempt)
				runable++;
			break;
		case S_XFER:
			xfernum++;
			break;
		case S_RETURN:
			xferable++;
			break;
		}
	}
	return(proc[n].status);
}

void
run(void)
{
	int i,j;
	int pid=0,pstatus;
	time_t tt;
	char *action;
 	char stat_time[30];
 	int last_stat;

	while(1)
	{
		if(!state)
			pid=wait(&pstatus);
			/* alarm or SIGHUP or startup*/
		if(pid==(-1) || state)
		{

			action=findconfig(wait_state)->value;
			if(strstr(action,"status")!=NULL)
			{
				tt = time(NULL);
				strftime(stat_time,sizeof(stat_time),"%d.%m.%Y|%H:%M:%S",SPLITTIME(&tt));
				printf("[%s]: ",stat_time);
				for(i=0;i<MAXDIR;i++)
					/* update status information */
					if(set_status(i)!=S_NONE)
					{
						if((proc[i].status==S_RUN || proc[i].status == S_READY )&&(proc[i].doneness!=0.0))
						{
							printf("%d:%s(%4.2f%%) ",i,state?"":s_text[proc[i].status],proc[i].doneness);
						}
						else
							printf("%d:%s ",i,state?(strcmp(s_text[proc[i].status], "Done")==0)?"(Done)":"(0.0%)":s_text[proc[i].status]);
					}
				putchar('\n');
				fflush(stdout);
			}
			if(state!=0)
				exit(0);

				/* check for any ready to be run */
			for(i=runnum;i<nproc;i++)
				run_old();

			if(((xfernum==0)&&(xfer_stopped))&&(runnum==0))
			{
				printf("No running proccesses sleeping for %d\n",timer);
				xfer_stopped=0;
				fflush(stdout);
				sleep(timer);
				fflush(stdout);
				continue;
			}

			if(strstr(action,"load")!=NULL)
			{
				for(i=0;i<MAXDIR;i++)
					set_status(i);
				if(strcmp(wait_state,STATE_INIT)==0)
				{
					if(!xfer_stopped)
						start_xfer(R_START);
				}
				else
				if(strcmp(wait_state,STATE_ALARM)==0)
					start_xfer(R_TIMER);
				else
				if(strcmp(wait_state,STATE_HUP)==0)
					start_xfer(R_REQUEST);
			}
			fflush(stdout);
			continue;
		}
		for(i=0;i<MAXDIR;i++)
		{
			if(pid==proc[i].pid)
			{
				int td,h,m,s;
				td=(int)(time(0)-proc[i].stime);
				s=td%60;
				m=(td/60)%60;
				h=(td/3600);
				proc[i].pid=0;
				last_stat = proc[i].status;
				printf("Finished %s%s%s %d (dir %s) exit(%d) time %dh%02dm%02ds\n",
				       last_stat == S_RUN ? "process" : "",
				       last_stat == S_XFER ? "xfer" : "",
				       last_stat != S_RUN && last_stat != S_XFER ? "unexpected": "",
				       pid,proc[i].dirname,WEXITSTATUS(pstatus),h,m,s);
				fflush(stdout);
				set_status(i);
				switch(last_stat)
				{
				case S_RUN:
					if( proc[i].status == S_RETURN )
					{
						/* attempt succeeded */
						for(j=runnum;j<nproc;j++)
							run_old();
						action=findconfig("finish")->value;
						if(strstr(action,"load")!=NULL)
							start_xfer(R_FINISH);
					}
					else if ( proc[i].status == S_READY )
					{
						/* count this as an attempt */
						proc[i].attempts++;
						printf("Run finished but result.sah does not exist\n");
						fflush(stdout);
					}
					break;
				case S_XFER:
					if(proc[i].status == S_READY )
					{
						for(j=runnum;j<nproc;j++)
							run_old();
						xfer_failed=0;
					}
					else
					{
						xfer_failed++;
						printf("Xfer finished but work_unit.sah does not exist xfer_failed=%d\n",xfer_failed);
						fflush(stdout);
					}

					if(xfer_failed>=max_fail)
					{
						xfer_stopped=1;
						for(i=0;i<MAXDIR;i++)
							proc[i].xfer_reason=R_NONE;
						break;
					}
					for(i=0;i<MAXDIR;i++)
					{
						if(xfernum>=nxfer)
							break;
						if(proc[i].xfer_reason!=R_NONE)
							run_return(i);
					}
					break;
				default:
					printf("Process %d finished but status=%s (last=%s)\n",proc[i].pid,s_text[proc[i].status],s_text[last_stat]);
				}
				break;
			}
		}
	}
}

int
main(int argc, char *argv[])
{
	DIR *dir;
	int n,i,r;
	int pid=0;
	time_t tt;
	struct dirent *dent;
 	char stat_time[30];
 	int kill_mode=0;
	char c;
	extern char *optarg;
	extern int optind;
	FILE *fp;
	char *base_dir = NULL;

        /* process options */
        while ((c = getopt(argc, argv, "c:C:b:hk:s")) != EOF)
	{
	    switch(c)
	    {
	      case 'c':
		config_file = optarg;
		break;
	      case 'C':
		writeconfig(optarg);
		exit(0);
	      case 'b':
		base_dir = optarg;
		if(chdir(base_dir) < 0)
		{
		    printf("Invalid base directory %s: %s\n",base_dir,strerror(errno));
		    exit(1);
		}
		break;
	      case 's':
	        printf("--- %s (%s) - current status: ", argv[0], VERSION);
		state=1;
		break;
	      case 'k':
		if(strcmp(optarg,"term")==0)
		{
			kill_mode=SIGTERM;
		  	break;
		}
		if(strcmp(optarg,"alarm")==0)
		{
		  	kill_mode=SIGALRM;
		  	break;
		}
		if(strcmp(optarg,"hup")==0)
		{
		  	kill_mode=SIGHUP;
		  	break;
		}
		/* fall through */
	      case 'h':
	      default:
		printf("Usage: %s [-[Cc] config_file]\n",argv[0]);
		printf("%s  (run with default config file \"setimgr.conf\"\n",argv[0]);
		printf("%s -c config_file  (run with config_file)\n",argv[0]);
		printf("%s -C config_file  (create config_file with defaults)\n",argv[0]);
		printf("%s -b base_dir     (specify a location for the procN directories)\n",argv[0]);
		printf("%s -k term         (send SIGTERM to setimgr (clean up and exit))\n",argv[0]);
		printf("%s -k hup          (send SIGHUP to setimgr)\n",argv[0]);
		printf("%s -k alarm        (send SIGALRM to setimgr)\n", argv[0]);
		printf("%s -s              (print status of current WUs)\n", argv[0]);
		printf("%s -h              (print this help message)\n",argv[0]);
		exit(1);
	    }
	}
	
	getconfig();
	/* change to the base directory, command line overrides config file */
	if ( ! base_dir )
		if( chdir(findconfig("base_dir")->value) < 0 )
		{
		    printf("Invalid base directory in config %s: %s\n",
			   (char*)findconfig("base_dir")->value,
			   strerror(errno));
		    exit(1);
		}

	pid_file=findconfig("pid_file")->value;
	if((pid_file)&&(strcmp(pid_file,"none")==0))
		pid_file=NULL;

	if(kill_mode!=0)
	{
		if(pid_file==NULL)
		{
			fprintf(stderr,"%s: Cannot send kill, pid_file is not defined in config\n",argv[0]);
			exit(1);
		}
		fp=fopen(pid_file,"r");
		if(fp!=NULL)
		{
			fscanf(fp,"%d",&pid);
			fclose(fp);
		}
		else
		{
			perror(pid_file);
			exit(1);
		}
		if(pid!=0)
		{
			r=kill(pid,kill_mode);
			if(r<0)
			{
				fprintf(stderr,"kill(%d,%d) failed\n",pid,kill_mode);
				exit(1);
			}
				fprintf(stderr,"kill(%d,%d) OK\n",pid,kill_mode);
				exit(0);
		}
	}

	if(state)
		printf(" %sRUNNING...\n", (access(pid_file, F_OK)==-1) ? "NOT " : "");
	else if(pid_file!=NULL)
	{	
		unlink(pid_file);
		fp=fopen(pid_file,"w");
		if(fp!=NULL)
		{
			fprintf(fp,"%d\n",getpid());
			fclose(fp);
		}
		else
		{
			perror(pid_file);
			exit(1);
		}
	}

	for(i=0;i<MAXDIR;i++)
 		memset(&proc[i],0,sizeof(PROC));

	tt=time(0);
	if(!state)
		printf("%s (v%s) started pid=%d at %s",argv[0],VERSION,getpid(),ctime(&tt));

	dir=opendir(".");
 	while((dent=readdir(dir))!=NULL)
	{
		if(strncmp(dent->d_name,"proc",4)==0)
		{
			n=atoi(dent->d_name+4);
			if(n>MAXDIR)
				continue;
			proc[n].dirname=strdup(dent->d_name);
			set_status(n);
			strftime(stat_time,sizeof(stat_time),"%d.%m.%Y|%H:%M:%S",SPLITTIME(&proc[n].wtime));
			if(!state)
				printf("Found dir proc%d, date=%s state=%s\n",n,(proc[n].wtime==0)?"none":stat_time,s_text[proc[n].status]);

				/* if start option contains rescue, assume that any dir where */
				/* we don't know the status but there is a userinfo.sah */
				/* is ready for a download */
			if((proc[n].status==S_NONE)&&(strstr(findconfig("start")->value,"rescue")!=NULL))
			{
				if(filetime(proc[n].dirname,"user_info.sah")>0)
				{
					proc[n].status=S_RETURN;
					runable++;
				}
			}
		}
	}
	closedir(dir);

	mysignal(SIGHUP,do_sighup);
	mysignal(SIGTERM,do_sigterm);
	if(timer!=0)
	{
		mysignal(SIGALRM,do_alarm);
		alarm(timer);
	}
	run();
	return(0);
}


