#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include "../../config.h"

#include "../include/qlmpi.h"
#include "../include/md5.h"

#define MCEXEC "mcexec"
#define QL_PIPE_PATH "/tmp/"
#define QL_PIPE_IN_EXTENTION ".in"
#define QL_PIPE_OUT_EXTENTION ".out"
#define QL_SERVER_EXECUTION SBINDIR "/ql_server"
#define QL_TALKER_EXECUTION SBINDIR "/ql_talker"

extern char **environ;

struct mpi_opt {
	const char *opt;
	int n;
	int flags;
#define HOSTFILE_OPT  1
#define HOSTLIST_OPT  2
#define NODE_OPT      4
#define UNSUPP_OPT    8
#define ENVLIST_OPT  16
#define ENVNONE_OPT  32
#define ENVALL_OPT   64
#define ENV_OPT     128
};

const struct mpi_opt opts[] = {
	{"help", 0, 0},
	{"h", 0, 0},
	{"genv", -1, ENV_OPT},
	{"genvlist", 1, ENVLIST_OPT},
	{"genvnone", 0, ENVNONE_OPT},
	{"genvall", 0, ENVALL_OPT},
	{"f", 1, HOSTFILE_OPT},
	{"hostfile", 1, HOSTFILE_OPT},
	{"machinefile", 1, HOSTFILE_OPT},
	{"machine", 1, HOSTLIST_OPT},
	{"machines", 1, HOSTLIST_OPT},
	{"machinelist", 1, HOSTLIST_OPT},
	{"host", 1, HOSTLIST_OPT},
	{"hosts", 1, HOSTLIST_OPT},
	{"hostlist", 1, HOSTLIST_OPT},
	{"ppn", 1, 0},
	{"profile", 0, 0},
	{"prepend-rank", 0, 0},
	{"l", 0, 0},
	{"prepend-pattern", 1, 0},
	{"outfile-pattern", 1, 0},
	{"outfile", 1, 0},
	{"errfile-pattern", 1, 0},
	{"errfile", 1, 0},
	{"wdir", 1, 0},
	{"configfile", 1, 0},
	{"env", -1, ENV_OPT},
	{"envlist", 1, ENVLIST_OPT},
	{"envnone", 0, ENVNONE_OPT},
	{"envall", 0, ENVALL_OPT},
	{"n", 1, NODE_OPT},
	{"np", 1, NODE_OPT},
	{"launcher", 1, 0},
	{"launcher-exec", 1, 0},
	{"bootstrap", 1, 0},
	{"bootstrap-exec", 1, 0},
	{"enable-x", 0, 0},
	{"disable-x", 0, 0},
	{"rmk", 1, 0},
	{"bind-to", 1, 0},
	{"binding", 1, 0},
	{"map-by", 1, 0},
	{"membind", 1, 0},
	{"topolib", 1, 0},
	{"ckpoint-interval", 1, 0},
	{"ckpoint-prefix", 1, 0},
	{"ckpoint-num", 1, 0},
	{"ckpointlib", 1, 0},
	{"demux", 1, 0},
	{"verbose", 0, 0},
	{"v", 0, 0},
	{"debug", 0, 0},
	{"info", 0, 0},
	{"version", 0, 0},
	{"print-all-exitcodes", 0, 0},
	{"iface", 1, 0},
	{"nameserver", 1, 0},
	{"disable-auto-cleanup", 0, 0},
	{"dac", 0, 0},
	{"enable-auto-cleanup", 0, 0},
	{"disable-hostname-propagation", 0, 0},
	{"enable-hostname-propagation", 0, 0},
	{"order-nodes", 1, 0},
	{"localhost", 1, 0},
	{"usize", 1, 0},
	{NULL, 0, 0}
};

char **mpi_opt_top;
char **usr_opt_top;

int fdstdin = -1;
int fdstdout = -1;
int fdstderr = -1;
char ql_name[33] = "";
char ql_sock_file[1024] = "";
char target_host[256] = "";
struct sockaddr_un wsock;

char *
trim(char *buf)
{
	char *p;
	char *q;

	for(p = buf; *p && (isspace(*p)); p++);
	if(!*p)
		return p;
	for(q = strchr(p, '\0') - 1; isspace(*q); q--)
		*q = '\0';
	return p;
}

void
esc_put(FILE *fp, char type, const char *buf)
{
	const char *t;

	fprintf(fp, "%c %ld ", type, strlen(buf));
	for (t = buf; *t; t++) {
		if (*t == '%' || *t < ' ')
			fprintf(fp, "%%%02x", *t);
		else
			fputc(*t, fp);
	}
	fputc('\n', fp);
}

static void
ql_setenv(char **env, char *k, char *v)
{
	char **e;
	char *w;
	char *t;
	int l;

	if (!*k || *k == '=')
		return;

	l = strlen(k);
	if (v)
		l += strlen(v) + 1;
	else if (!strchr(k, '='))
		l++;
	w = malloc(l + 1);
	strcpy(w, k);
	if (!(t = strchr(w, '=')))
		strcat(w, "=");
	else
		t[1] = '\0';
	l = strlen(w);
	for (e = env; *e; e++)
		if (!strncmp(w, *e, l))
			break;
	if (v)
		strcat(w, v);
	else if (t)
		strcpy(w, k);
	if (!*e)
		e[1] = NULL;
	*e = w;
}

static void
ql_envlist(char **env, char *list)
{
	char *w = strdup(list);
	char *p = w;

	for (;;) {
		char *q = strchr(p, ',');

		if (q) {
			*q = '\0';
			ql_setenv(env, p, NULL);
			p = q + 1;
		}
		else {
			ql_setenv(env, p, NULL);
			break;
		}
	}

	free(w);
}

static int
sendfd(int sock, int fd)
{
	struct msghdr msg;
	struct iovec iov;
	char cmsgbuf[CMSG_SPACE(sizeof(int))];
	struct cmsghdr *cmsg = (struct cmsghdr*)cmsgbuf;
	char c;

	iov.iov_base = &c;
	iov.iov_len = 1;
	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	*((int *)CMSG_DATA(cmsg)) = fd;
	memset(&msg, 0, sizeof(msg));
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	msg.msg_flags = 0;
	if (sendmsg(sock, &msg, 0) == -1) {
		return -1;
	}

	return 0;
}

#ifndef QL_MPIEXEC_FINALIZE
static int
recvfd(int sock)
{
	struct msghdr msg;
	struct iovec iov;
	char cmsgbuf[CMSG_SPACE(sizeof(int))];
	struct cmsghdr *cmsg = (struct cmsghdr *)cmsgbuf;
	char c;

	iov.iov_base = &c;
	iov.iov_len = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	msg.msg_flags = MSG_WAITALL;
	if (recvmsg(sock, &msg, 0) == -1) {
		return -1;
	}

	return *(int *)CMSG_DATA(cmsg);
}

int eventfds[2];

static void
chld(int s)
{
	write(eventfds[1], "Q", 1);
}

static void
term_server()
{
	char buf[1024];

	sprintf(buf,"ssh %s %s %c %s %s %s", target_host, QL_TALKER_EXECUTION,
	        QL_RET_FINAL, "-n", ql_name ,ql_sock_file);
	system(buf);
}

#define RBUFSIZE 65536
struct rbuf {
	int rpos;
	int wpos;
	char buf[RBUFSIZE];
};

struct rbuf *
buf_new()
{
	struct rbuf *rbuf = malloc(sizeof(struct rbuf));

	rbuf->rpos = 0;
	rbuf->wpos = 0;
	return rbuf;
}

int
buf_empty(struct rbuf *bp)
{
	return bp->rpos == bp->wpos;
}

int
buf_full(struct rbuf *bp)
{
	if (bp->wpos)
		return bp->rpos == bp->wpos - 1;
	return bp->rpos == RBUFSIZE - 1;
}

int
buf_read(int fd, struct rbuf *bp)
{
	struct iovec iov[2];
	int iovlen = 1;
	int rc;

	iov[0].iov_base = bp->buf + bp->rpos;
	if (bp->rpos >= bp->wpos) {
		if (bp->wpos == 0)
			iov[0].iov_len = RBUFSIZE - bp->rpos - 1;
		else {
			iovlen = 2;
			iov[0].iov_len = RBUFSIZE - bp->rpos;
			iov[1].iov_base = bp->buf;
			iov[1].iov_len = bp->wpos - 1;
		}
	}
	else
		iov[0].iov_len = bp->wpos - bp->rpos - 1;
	rc = readv(fd, iov, iovlen);
	if (rc <= 0)
		return rc;
	bp->rpos += rc;
	if (bp->rpos >= RBUFSIZE)
		bp->rpos -= RBUFSIZE;
	return rc;
}

int
buf_write(int fd, struct rbuf *bp)
{
	struct iovec iov[2];
	int iovlen = 1;
	int rc;

	iov[0].iov_base = bp->buf + bp->wpos;
	if (bp->wpos > bp->rpos) {
		iov[0].iov_len = sizeof(bp->buf) - bp->wpos;
		iov[1].iov_base = bp->buf;
		if ((iov[1].iov_len = bp->rpos))
			iovlen = 2;
	}
	else
		iov[0].iov_len = bp->rpos - bp->wpos;
	rc = writev(fd, iov, iovlen);
	if (rc <= 0)
		return rc;
	bp->wpos += rc;
	if (bp->wpos >= RBUFSIZE)
		bp->wpos -= RBUFSIZE;
	return rc;
}

struct fds {
	struct fds *next;
	int in_fd;
	int out_fd;
	struct rbuf *buf;
};

struct fds *
fds_new(int in_fd, int out_fd)
{
	struct fds *fdp;

	fdp = malloc(sizeof(struct fds));

	fdp->next = NULL;
	fdp->buf = buf_new();
	fdp->in_fd = in_fd;
	fdp->out_fd = out_fd;
	return fdp;
}

static void
ql_wrapper(char **args, int afd)
{
	pid_t pid;
	char c;
	int pfds0[2];
	int pfds1[2];
	int pfds2[2];
	int pfds3[2];
	int rc;
	int maxfd = afd;
	fd_set readfds;
	fd_set writefds;
	int first = 1;
	int cfd = -1;
	int nflg = 0;
	struct fds *fds0;
	struct fds *fds1;
	struct fds *fds2;
	struct fds *fdtop;
	struct fds *fdp;
	int exitcode = 1;

	pipe(eventfds);
	if (eventfds[0] > maxfd)
		maxfd = eventfds[0];

	pipe(pfds0);
	pipe(pfds1);
	pipe(pfds2);

	fds0 = fds_new(-1, pfds0[1]);
	fds1 = fds_new(pfds1[0], -1);
	fds2 = fds_new(pfds2[0], -1);
	fdtop = fds0;
	fds0->next = fds1;
	fds1->next = fds2;

	socketpair(AF_UNIX, SOCK_STREAM, 0, pfds3);
	fcntl(pfds3[1], F_SETFD, FD_CLOEXEC);
	pid = fork();
	if (pid == 0) {
		close(afd);
		close(pfds0[1]);
		if (pfds0[0] != 0) {
			dup2(pfds0[0], 0);
			close(pfds0[0]);
		}
		close(pfds1[0]);
		if (pfds1[1] != 1) {
			dup2(pfds1[1], 1);
			close(pfds1[1]);
		}
		close(pfds2[0]);
		if (pfds2[1] != 1) {
			dup2(pfds2[1], 2);
			close(pfds2[1]);
		}
		close(pfds3[0]);

		// wait for client
		while ((rc = read(pfds3[1], &c, 1)) == -1 && errno == EINTR);
		if (rc != 1) //client is already terminated
			exit(0);
		execvp("mpiexec", args);

		// exec fail
		rc = errno;
		write(pfds3[1], &rc, sizeof rc);
		exit(1);
	}
	close(pfds0[0]);
	close(pfds1[1]);
	close(pfds2[1]);
	close(pfds3[1]);
	if (pfds0[1] > maxfd)
		maxfd = pfds0[1];
	if (pfds1[0] > maxfd)
		maxfd = pfds1[0];
	if (pfds2[0] > maxfd)
		maxfd = pfds2[0];

	signal(SIGPIPE, SIG_IGN);
	signal(SIGCHLD, chld);

	for (;;) {
		if (afd == -1 &&
		    (fds1->out_fd == -1 || buf_empty(fds1->buf)) &&
		    (fds2->out_fd == -1 || buf_empty(fds2->buf)))
			break;
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		FD_SET(eventfds[0], &readfds);
		if (cfd == -1)
			FD_SET(afd, &readfds);
		else
			FD_SET(cfd, &readfds);

		for (fdp = fdtop; fdp; fdp = fdp->next) {
			if (fdp->out_fd != -1 && !buf_empty(fdp->buf))
				FD_SET(fdp->out_fd, &writefds);
			if (fdp->in_fd != -1 && !buf_full(fdp->buf))
				FD_SET(fdp->in_fd, &readfds);
		}

		rc = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
		if (rc == -1 && errno == EINTR)
			continue;
		if (rc == 0)
			continue;

		if (FD_ISSET(eventfds[0], &readfds)) { // child die
			int c;
			int st;

			read(eventfds[0], &c, 1);
			while (waitpid(pid, &st, 0) == -1 && errno == EINTR);
			term_server();
			if (cfd != -1) {
				write(cfd, "X", 1);
				write(cfd, &st, 4);
			}
			exitcode = 0;
			close(afd);
			afd = -1;
		}

		if (FD_ISSET(afd, &readfds)) {
			struct sockaddr_un sock;
			socklen_t len;

			len = sizeof sock;
			cfd = accept(afd, (struct sockaddr *)&sock, &len);
			if (cfd == -1) {
				goto end;
			}
			if (cfd > maxfd)
				maxfd = cfd;
		}
		if (FD_ISSET(cfd, &readfds)) {
			int fd;

			rc = read(cfd, &c, 1);
			if (rc == 0) {
				close(cfd);
				cfd = -1;
				if (!nflg) { // abormally terminated
					kill(pid, SIGINT);
					term_server();
					close(afd);
					afd = -1;
				}
			}
			else if (c == 'C') {
				nflg = 0;
			}
			else if (c == '0') {
				fd = recvfd(cfd);
				if (fd > maxfd)
					maxfd = fd;
				fds0->out_fd = fd;
			}
			else if (c == '1') {
				fd = recvfd(cfd);
				if (fd > maxfd)
					maxfd = fd;
				fds1->out_fd = fd;
			}
			else if (c == '2') {
				fd = recvfd(cfd);
				if (fd > maxfd)
					maxfd = fd;
				fds2->out_fd = fd;
				if (first) {
					char buf[256];
					int e = 0;

					first = 0;
					write(pfds3[0], " ", 1);
					rc = read(pfds3[0], &e, sizeof e);
					if (rc == sizeof e) { // failed to exec
						sprintf(buf, "mpiexec: exec(%s)"
						        "\n", strerror(e));
						write(fd, buf, strlen(buf));
						goto end;
					}
					else if (rc == -1) {
						sprintf(buf, "mpiexec: read(%s)"
						        "\n", strerror(errno));
						write(fd, buf, strlen(buf));
					}
					close(pfds3[0]);
				}
			}
			else if (c == 'E') {
				nflg = 1;
			}
			else if (c == 'F') {
				nflg = 1;
			}
			if (c != 'F')
				write(cfd, " ", 1);
		}
		for (fdp = fdtop; fdp; fdp = fdp->next) {
			if (fdp->out_fd != -1 &&
			    FD_ISSET(fdp->out_fd, &writefds)) {
				rc = buf_write(fdp->out_fd, fdp->buf);
				if (fdp->in_fd == -1 &&
				    buf_empty(fdp->buf)) {
					close(fdp->out_fd);
					fdp->out_fd = -1;
				}
			}
			if (fdp->in_fd != -1 &&
			    FD_ISSET(fdp->in_fd, &readfds)) {
				rc = buf_read(fdp->in_fd, fdp->buf);
				if (rc == 0) {
					close(fdp->in_fd);
					fdp->in_fd = -1;
					if (buf_empty(fdp->buf)) {
						close(fdp->out_fd);
						fdp->out_fd = -1;
					}
				}
			}
		}
	}
end:
	unlink(wsock.sun_path);
	exit(exitcode);
}
#endif

int ql_check_directory( char * path,char * file ,char *filep){
	struct stat st;
	int rc;

	sprintf(filep,"%s/%s",path,file);

	rc = stat(filep,&st);
	if (rc == 0) {
	/* file exist */
		return 1;
	}
	else {
		rc = stat(path,&st);
		/* file or directory exist */
		if ( rc == 0) {
			mode_t m = st.st_mode;
			if (S_ISDIR(m)) {
			/* directory exist */
				return 1; 
			}
			return 0;
		}
		else {
			mode_t m = st.st_mode;
			if (S_ISDIR(m)) {
			/* directory exist */
				return rc; 
			}
			else {
				if (mkdir(path, (S_IRUSR | S_IWUSR | S_IRWXU |
						S_IRGRP | S_IWGRP | S_IRWXG |
						S_IROTH | S_IWOTH | S_IRWXO)) == 0) {
					return 1;
				}
				return 0; /* mkdir error */
			}
		}
	}
}





/* ex: ql_mpiexec_start -machinefile file_name -n 4 mcexec a.out arg1 arg2 */
// stdin, stdout, stderr
#define PIPE_HANDLE_NUM 3
int main(int argc, char *argv[])
{
	char *machinefile = NULL;
	char ql_param_file[1024] = ""; /* */
	char ql_sock_path[1024] = "";
	char ql_file[1024] = "";
	char *exe_name =NULL;
	char  tmp[4096];
	struct stat st; /* for file check */
	int fd;
	int i;
	md5_state_t state;
	md5_byte_t digest[16];
	FILE *fp;
	char base[1024]; 
	char *ptr;
	char **a;
	char **b;
	int rc;
	char **env;
	int n;
	int uid;
	char *pt;
	int wfd;
	socklen_t wlen;
	int exitcode = 0;
	char c;

#ifndef QL_MPIEXEC_FINALIZE
	int f_flg = 0;
#endif

	for (a = environ, n = 0; *a; a++, n++);
	for (a = argv; *a; a++) {
		if (!strcmp(*a, "-genv") ||
		    !strcmp(*a, "-env")) {
			n++;
		}
		else if ((!strcmp(*a, "-genvlist") ||
		          !strcmp(*a, "-envlist")) &&
		         a[1]) {
			char *t;

			n++;
			for (t = a[1]; *a; t++)
				if (*t == ',')
					n++;
		}
	}
	env = malloc(sizeof(char *) * (n + 2));
	for (a = environ, b = env; (*b = *a); a++, b++);

	md5_init(&state);
	mpi_opt_top = argv + 1;
	for (a = mpi_opt_top; *a; a++) {
		char *opt;
		const struct mpi_opt *o;
		int i;

		if ((*a)[0] != '-')
			break;
		opt = (*a) + 1;
		for (o = opts; o->opt; o++) {
			if (!strcmp(opt, o->opt))
				break;
		}
		if (!o->opt) {
			fprintf(stderr, "unknown option: %s\n", *a);
			exit(1);
		}
		if (o->n < 0) { // -genv, -env
			a++;
			if (!*a) {
				fprintf(stderr, "bad option: -%s\n", o->opt);
				exit(1);
			}
			if (!strchr(*a, '=')) {
				char *k = *a;
				a++;
				if (!*a) {
					fprintf(stderr, "bad option: -%s\n",
					        o->opt);
					exit(1);
				}
				if (o->flags & ENV_OPT)
					ql_setenv(env, k, *a);
			}
			else {
				if (o->flags & ENV_OPT)
					ql_setenv(env, *a, NULL);
			}
		}
		else {
			for (i = 0; i < o->n; i++) {
				a++;
				if (!*a) {
					fprintf(stderr, "bad option: -%s\n",
					        o->opt);
					exit(1);
				}
			}
			if (o->flags & UNSUPP_OPT) {
					fprintf(stderr, "unsupported option: "
					        "-%s\n", o->opt);
					exit(1);
			}
			if (o->flags & HOSTFILE_OPT)
				machinefile = *a;
			if (o->flags & NODE_OPT) {
				md5_append(&state, (const md5_byte_t *)*a,
				           strlen(*a));
			}
			if (o->flags & ENVNONE_OPT) {
				env[0] = NULL;
			}
			if (o->flags & ENVALL_OPT) {
				for (a = environ, b = env; (*b = *a); a++, b++);
			}
			if (o->flags & ENVLIST_OPT) {
				ql_envlist(env, *a);
			}
		}
	}
	usr_opt_top = a;
	if (!*a) {
		fprintf(stderr, "no user program\n");
		exit(1);
	}
	exe_name = *a;
	md5_append(&state, (const md5_byte_t *)exe_name, strlen(exe_name));

	for (; *a; a++)
		if (!strcmp(*a, ":")) {
			fprintf(stderr, "':' is unsupported\n");
			exit(1);
		}

	if (machinefile) { 
		/* get target_host from -machinefile */
		if (!stat(machinefile, &st)) { /* file exist*/
			char *b;
			size_t siz;
			FILE *f;
			char line[65536];

			siz = st.st_size;
			fd = open(machinefile, O_RDONLY);
			b = mmap(NULL, siz, PROT_READ, MAP_PRIVATE, fd, 0);
			close(fd);
			if (b == (void *)-1) {
				fprintf(stderr, "unable to read hostfile(%s): %s\n", machinefile, strerror(errno));
				exit(1);
			}
			md5_append(&state, (const md5_byte_t *)b, siz);
			munmap(b, siz);

			if (!(f = fopen(machinefile, "r"))) {
				fprintf(stderr, "could not open hostfile(%s): %s\n", machinefile, strerror(errno));
				exit(1);
			}
			while (fgets(line, sizeof line, f)) {
				char *w;
				char *t;
				if ((w = strchr(line, '#')))
					*w = '\0';
				if ((w = strchr(line, ':')))
					*w = '\0';
				if ((w = strchr(line, ',')))
					*w = '\0';
				t = trim(line);
				if ((w = strchr(line, ' ')))
					*w = '\0';
				if ((w = strchr(line, '\t')))
					*w = '\0';
				if (t[0] == '\0')
					continue;
				strcpy(target_host, t);
				break;
			}
			fclose(f);
#ifdef QL_DEBUG
			printf(" target_host %s\n", target_host);
#endif
		}
		else {
			fprintf(stderr, "-machinefile not exist\n");
			exit(1);
		}
	}
	else {
		fprintf(stderr, "specify -machinefile option\n");
		exit(1);
	}

	md5_finish(&state, digest);
	for (i = 0; i < 16; i++) {
		sprintf(ql_name + i * 2, "%02x", digest[i]);
	}
	if ((ptr = getenv(QL_PARAM_ENV)) == NULL) {
		sprintf(base, "%s", getenv("HOME"));
	}
	else{
		sprintf(base, "%s", ptr);
	}

	setenv("QL_NAME", ql_name, 1);
	ql_setenv(env, "QL_NAME", ql_name);

	uid  = (int)getuid();
	if ((pt = getenv(QL_SOCK_ENV)) != NULL) {
		sprintf(ql_sock_path,"%s/%s",pt,QL_SOCK);
	}
	else {
		sprintf(ql_sock_path,"%s/%d/%s",QL_SOCKT_PATH,uid,QL_SOCK); 
	} 
	sprintf(ql_file,"%s.%d", QL_SOCK, uid);

    if(!ql_check_directory(ql_sock_path,ql_file,ql_sock_file)) {
		fprintf(stderr, "socket directory not exist\n");
		exit(1);
	}

	setenv("QL_SOCKET_FILE", ql_sock_file, 1);
#ifdef QL_DEBUG
	printf(" socket path  %s\n", ql_sock_file);
#endif

#ifndef QL_MPIEXEC_FINALIZE
	sprintf(tmp, "ssh %s ""%s %s %s""", target_host, QL_SERVER_EXECUTION ,ql_sock_path ,ql_file);

#ifdef QL_DEBUG
	printf(" system   %s\n", tmp);
#endif
	if((rc = system(tmp)) == -1){
		fprintf(stderr, "ql_server not execution %s", strerror(errno));
		exit(-1);
	}
#endif

	memset(&wsock, '\0', sizeof wsock);
	wsock.sun_family = AF_UNIX;
	sprintf(wsock.sun_path, "%s/%s.s", ql_sock_path, ql_name);
	wlen = sizeof wsock.sun_family + strlen(wsock.sun_path) + 1;

	if(stat(wsock.sun_path, &st)){ /* socket file not exist */
#ifdef QL_MPIEXEC_FINALIZE
		fprintf(stderr,"not found mpi process\n");
		exit(1);
#else
		pid_t pid;
		int wst;

		f_flg = 1;

		if ((wfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
			fprintf(stderr, "ql_mpiexec_start: socket(%s)\n",
			        strerror(errno));
			exit(1);
		}
		if (bind(wfd, (struct sockaddr *)&wsock, wlen) == -1) {
			fprintf(stderr, "ql_mpiexec_start: bind(%s)\n",
			        strerror(errno));
			exit(1);
		}
		if (listen(wfd, 5) == -1) {
			fprintf(stderr, "ql_mpiexec_start: listen(%s)\n",
			        strerror(errno));
			exit(1);
		}

		if ((pid = fork()) == 0) { 
			int i;
			char **args;
			char **b;

			if (fork())
				exit(0);

			setsid();
			if (wfd < 3) {
				dup2(wfd, 3);
				wfd = 3;
			}
			for (i = 0; i < 4096; i++)
				if (i != wfd)
					close(i);
			open("/dev/null", O_RDONLY);
			open("/dev/null", O_WRONLY);
			open("/dev/null", O_WRONLY);

			args = (char **)malloc(sizeof(char *) * (argc + 2));
			*args = "mpiexec";
			for (a = mpi_opt_top, b = args + 1; a != usr_opt_top;
			     a++)
				*(b++) = *a;
			*(b++) = BINDIR "/mcexec";
			for (; *a; a++)
				*(b++) = *a;
			*b = NULL;
			ql_wrapper(args, wfd);
			exit(-1); /*not reach */
		}
		close(wfd);
		while(waitpid(pid, &wst, 0) == -1 && errno == EINTR);
#endif
	}
	else{
		int env_n;
		int arg_n;

		for (arg_n = 0, a = usr_opt_top; *a; a++, arg_n++);
		for (env_n = 0, a = env; *a; a++, env_n++);

		/* param file output */
		sprintf(ql_param_file, "%s/%s%s", base, ql_name, QL_PARAM_EXTE);
		fp = fopen(ql_param_file, "w");
#ifdef QL_MPIEXEC_FINALIZE
		fprintf(fp, "%c COM=%c\n", QL_COMMAND, QL_RET_FINAL);
#else
		fprintf(fp, "%c COM=%c %d %d\n", QL_COMMAND, QL_RET_RESUME,
		        arg_n, env_n);

		for (a = usr_opt_top; *a; a++)
			esc_put(fp, QL_ARG, *a);
		for (a = env; *a; a++)
			esc_put(fp, QL_ENV, *a);
#endif

		fclose(fp);
	}

	if ((wfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
		fprintf(stderr, "ql_mpiexec_start: socket(%s)\n",
		        strerror(errno));
		exit(1);
	}
	rc = connect(wfd, (struct sockaddr *)&wsock, wlen);
	if (rc == -1) {
		fprintf(stderr, "ql_mpiexec_start: connect(%s)\n",
		        strerror(errno));
		exit(1);
	}
	write(wfd, "C", 1);
	if ((rc = read(wfd, &c, 1)) <= 0)
		exit(1);
	write(wfd, "0", 1);
	sendfd(wfd, 0);
	if ((rc = read(wfd, &c, 1)) <= 0)
		exit(1);
	write(wfd, "1", 1);
	sendfd(wfd, 1);
	if ((rc = read(wfd, &c, 1)) <= 0)
		exit(1);
	write(wfd, "2", 1);
	sendfd(wfd, 2);
	if ((rc = read(wfd, &c, 1)) <= 0)
		exit(1);

#ifdef QL_MPIEXEC_FINALIZE
	sprintf(tmp,"ssh %s %s %c %s %s %s",
	        target_host, QL_TALKER_EXECUTION, QL_RET_RESUME, "-n", ql_name , ql_sock_file);
	rc = system(tmp);
	write(wfd, "F", 1);
#else
	if (f_flg == 1) {
		sprintf(tmp,"ssh %s %s %c %c %s %s",
		        target_host, QL_TALKER_EXECUTION, QL_COM_CONN,
		        QL_EXEC_END, ql_name ,ql_sock_file);
		rc = system(tmp);
		/* send N and recv E */
	}
	else{
		sprintf(tmp,"ssh %s %s %c %c %s %s",
		        target_host, QL_TALKER_EXECUTION, QL_RET_RESUME,
		        QL_EXEC_END, ql_name , ql_sock_file);
		rc = system(tmp);
		/* send R and recv E */
	}
	write(wfd, "E", 1);
#endif

	if ((rc = read(wfd, &c, 1)) <= 0)
		goto end;
	if (c == 'X') {
		int wst;

		read(wfd, &wst, sizeof st);
		if (WIFSIGNALED(wst)) {
			int sig = WTERMSIG(wst);
			signal(sig, SIG_DFL);
			kill(getpid(), sig);
			pause();
		}
		exitcode = WEXITSTATUS(wst);
	}
	close(wfd);

end:
	unlink(ql_param_file);
	exit(exitcode);
}
