/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#define STREAM_LIB

#include	<errno.h>
#include	<sys/types.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<limits.h>
#include	<pthread.h>
#include	"memory_debug.h"
#include	"task.h"
#include	"utils.h"
#include	"lock_level.h"
#include	"pri_level.h"
#include	"machine/fork_lock.h"

extern S_TABLE s_file_table;
extern SEM stream_lock;


char *
setup_ret_str_child(char * msg,char * child)
{
int len;
char * p, * q;
	if ( child == 0 || *child == 0 )
		return msg;
	len = 0;
	for ( p = child ; *p ; p ++ )
		if ( *p < 0x21 || *p == ']' || *p == '\\' )
			len += 4;
		else	len ++;
	msg = d_re_alloc(msg,strlen(msg)+len+3);
	q = &msg[strlen(msg)];
	p = child;
	*q++ = '[';
	for ( ; *p ; p ++ )
		if ( *p < 0x21 || *p == ']' || *p == '\\' ) {
			q[0] = '\\';
			q[1] = (((*p)>>6)&0x7)+'0';
			q[2] = (((*p)>>3)&0x7)+'0';
			q[3] = ((*p)&0x7)+'0';
			q += 4;
		}
		else {
			*q++ = *p;
		}
	*q++ = ']';
	*q = 0;
	return msg;		
}

int
launch_proc(
	char * str,
	char ** argv,
	STREAM ** stpp,
	char * close_flags,
	char * ret_str_parent,
	char * ret_str_child)
{
char ** _argv;
int id;
int i,j,target;
char *fp;
int len;
int p[2];
char ch;
int slen;
int er;
int * stpfd;
//int * _stpfd;
PROC_SEND ps;
char * msg;
char * _close_flags;
//char * cf;
int ret_errno;
int cer;

STREAM ** stpp_ptr;
char * cf_ptr;

	lock_task(stream_lock);
	for ( len = 0 ; stpp[len] ; len ++ );
	stpfd = d_alloc((len*2+1)*sizeof(int));
	_close_flags = d_alloc(len*2+1);
	msg = copy_str("+");
	target = 0;
	for ( i = 0 ; i < len ; i ++ ) {
		if ( (*stpp[i]->h.tbl->proc_send)(stpp[i],&ps) < 0 )
			er_panic("cannot send stream");
		msg = d_re_alloc(msg,strlen(msg)+strlen(ps.msg)+1);
		strcpy(&msg[strlen(msg)],ps.msg);
		for ( j = 0 ; j < 2 ; j ++ ) {
			if ( ps.fid[j] < 0 )
				break;
			_close_flags[target] = close_flags[i];
			stpfd[target++] = ps.fid[j];
		}
	}
	unlock_task(stream_lock,"launch_proc");

	len = target;

	if ( strlen(msg) == 1 ) {
		d_f_ree(msg);
		msg = 0;
		_argv = 0;
	}
	else {
	int a_len;
		msg = setup_ret_str_child(msg,ret_str_child);
		for ( i = 0 ; argv[i] ; i ++ );
		a_len = i;
		_argv = d_alloc(sizeof(char*)*(a_len+2));
		_argv[0] = argv[0];
		_argv[1] = msg;
		for ( i = 1 ; argv[i] ; i ++ )
			_argv[i+1] = argv[i];
		_argv[i+1] = 0;
		argv = _argv;
	}

	fp = d_alloc(sys_param.max_fd+1);

	er = pipe(p);
retry:
	errno = 0;
	wlock_fork();
	id = fork();
	if ( id == 0 ) {
		/* child process */

	p1:
		cer = close(p[1]);
		if ( cer < 0 && errno == EINTR )
			goto p1;
		for ( i = 0 ; i < sys_param.max_fd+1 ; i ++ )
			fp[i] = 0;
		fp[p[0]] = 1;
		for ( i = 0 ; i < len ; i ++ )
			fp[stpfd[i]] = 1;
		for ( i = 3 ; i <= sys_param.max_fd ; i ++ )
			if ( fp[i] == 0 ) {
			p2:
				cer = close(i);
				if ( cer < 0 && errno == EINTR )
					goto p2;
			}
		if ( read(p[0],&ch,1) < 0 ) {
			printf("pp p %i\n",p[0]);
			perror("launch");
		}
	p3:
		cer = close(p[0]);
		if ( cer < 0 && errno == EINTR )
			goto p3;

		pthread_detach((pthread_t)_get_tid().tid);
		er = execvp(str,argv);
		exit(1);
	}
	wunlock_fork();
	if ( id < 0 && errno == EAGAIN ) {
		sleep_sec(1);
		goto retry;
	}
	ret_errno = errno;
p4:
	cer = close(p[0]);
	if ( cer < 0 && errno == EINTR )
		goto p4;

	if ( ret_str_parent )
		slen = strlen(ret_str_parent);
/*
	cf = _close_flags;
	for ( i = 0 , _stpfd = stpfd ; i < len ; _stpfd ++ , cf ++ , i ++ ) {
		if ( ret_str_parent )
			write(*_stpfd,ret_str_parent,slen);
		if ( *cf ) {
		p5:
			cer = close(*_stpfd);
			if ( cer < 0 && errno == EINTR )
				goto p5;
		}
	}
*/
	cf_ptr = close_flags;
	for ( stpp_ptr = stpp ; *stpp_ptr ; stpp_ptr ++ , cf_ptr ++ ) {
		if ( ret_str_parent )
			s_write(*stpp_ptr,ret_str_parent,slen);
		if ( cf_ptr )
			s_close(*stpp_ptr);
	}

	if ( id == -1 ) {
	p6:
		cer = close(p[1]);
		if ( cer < 0 && errno == EINTR )
			goto p6;
		d_f_ree(fp);
		d_f_ree(stpfd);
		if ( _argv ) {
			d_f_ree(_argv);
			d_f_ree(msg);
		}
		return - ret_errno;
	}
	errno = 0;
	er = write(p[1],&ch,1);
	if ( errno )
		printf("write %i\n",errno);
p7:
	close(p[1]);
	if ( cer < 0 && errno == EINTR )
		goto p7;
	d_f_ree(fp);
	d_f_ree(stpfd);
	d_f_ree(_close_flags);
	if ( _argv ) {
		d_f_ree(_argv);
		d_f_ree(msg);
	}
	return id;
}
