/*******************************************************************************
 * Copyright (c) 2004, 2010 QNX Software Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     QNX Software Systems - initial API and implementation
 *     Wind River Systems, Inc.  
 *     Mikhail Zabaluev (Nokia) - bug 82744
 *     Mikhail Sennikovsky - bug 145737
 *******************************************************************************/
#include "exec0.h"
#include "openpty.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <stdlib.h>
#include <termios.h>

/* from pfind.c */
extern char *pfind(const char *name, char * const envp[]);

pid_t
exec_pty(const char *path, char *const argv[], char *const envp[],
      const char *dirpath, int channels[3], const char *pts_name, int fdm, int console)
{
	int pipe2[2];
	pid_t childpid;
	char *full_path;

	/*
	 * We use pfind() to check that the program exists and is an executable.
	 * If not pass the error up.  Also execve() wants a full path.
	 */ 
	full_path = pfind(path, envp);
	if (full_path == NULL) {
		fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : "");
		return -1;
	}

	/*
	 *  Make sure we can create our pipes before forking.
	 */ 
	if (channels != NULL && console) {
		if (pipe(pipe2) < 0) { 
			fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
			free(full_path);
			return -1;
		}
	}

	childpid = fork();

	if (childpid < 0) {
		fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
		free(full_path);
		return -1;
	} else if (childpid == 0) { /* child */

		chdir(dirpath);

		if (channels != NULL) {
			int fds;

			if (!console && setsid() < 0) {
				perror("setsid()");
				return -1;
			}

			fds = ptys_open(fdm, pts_name);
			if (fds < 0) {
				fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno));
				return -1;
			}

			/* Close the read end of pipe2 */
			if (console && close(pipe2[0]) == -1) {
				perror("close(pipe2[0]))");
			}

			/* close the master, no need in the child */
			close(fdm);

			if (console) {
				set_noecho(fds);
				if (setpgid(getpid(), getpid()) < 0) {
					perror("setpgid()");
					return -1;
				}
			}

			/* redirections */
			dup2(fds, STDIN_FILENO);   /* dup stdin */
			dup2(fds, STDOUT_FILENO);  /* dup stdout */
			if (console) {
				dup2(pipe2[1], STDERR_FILENO);  /* dup stderr */
			} else {
				dup2(fds, STDERR_FILENO);  /* dup stderr */
			}
			close(fds);  /* done with fds. */
		}

		/* Close all the fd's in the child */
		{
			int fdlimit = sysconf(_SC_OPEN_MAX);
			int fd = 3;

			while (fd < fdlimit)
				close(fd++);
		}

		if (envp[0] == NULL) {
			execv(full_path, argv);
		} else {
			execve(full_path, argv, envp);
		}

		_exit(127);

	} else if (childpid != 0) { /* parent */
		if (console) {
			set_noecho(fdm);
		}
		if (channels != NULL) {
			channels[0] = fdm; /* Input Stream. */
			channels[1] = fdm; /* Output Stream.  */
			if (console) {
				/* close the write end of pipe1 */
				if (close(pipe2[1]) == -1)
					perror("close(pipe2[1])");
				channels[2] = pipe2[0]; /* stderr Stream.  */
			} else {
				channels[2] = fdm; /* Error Stream.  */
			}
		}

		free(full_path);
		return childpid;
	}

	free(full_path);
	return -1;                  /*NOT REACHED */
}
#ifdef __STAND_ALONE__
int main(int argc, char **argv, char **envp) {
	const char *path = "./bufferring_test";
	int channels[3] = { -1, -1, -1};
	int status;	
	FILE *app_stdin;
	FILE *app_stdout;
	FILE *app_stderr;
	char pts_name[32];
	int fdm;	
	char buffer[32];

	fdm = ptym_open(pts_name);
	status =  exec_pty(path, argv, envp, ".", channels, pts_name, fdm);
	if (status >= 0) {
		app_stdin = fdopen(channels[0], "w");	
		app_stdout = fdopen(channels[1], "r");	
		app_stderr = fdopen(channels[2], "r");	
		if (app_stdout == NULL || app_stderr == NULL || app_stdin == NULL) {
			fprintf(stderr, "PROBLEMS\n");
		} else {
			fputs("foo\n", app_stdin);
			fputs("bar\n", app_stdin);
			while(fgets(buffer, sizeof buffer, app_stdout) != NULL) {
				fprintf(stdout, "STDOUT: %s\n", buffer);
			}
			while(fgets(buffer, sizeof buffer, app_stderr) != NULL) {
				fprintf(stdout, "STDERR: %s\n", buffer);
			}
		}
	}
	fputs("bye\n", stdout);
	close(channels[0]);
	close(channels[1]);
	close(channels[2]);
	return 0;	
}
#endif
