#include "amanda.h"
#include "pipespawn.h"
#include "clock.h"
#include "amutil.h"

char skip_argument[1];

pid_t pipespawnv_passwd(char *prog, int pipedef, int need_root,
                  int *stdinfd, int *stdoutfd, int *stderrfd,
                  char **my_argv);


/*
 * this used to be a function in it's own write but became a wrapper around
 * pipespawnv to eliminate redundancy...
 */
pid_t
pipespawn(
    char *	prog,
    int		pipedef,
    int         need_root,
    int *	stdinfd,
    int *	stdoutfd,
    int *	stderrfd,
    ...)
{
    va_list ap;
    int argc = 0, i;
    pid_t pid;
    char **argv;

    /* count args */
    arglist_start(ap, stderrfd);
    while(arglist_val(ap, char *) != NULL) {
	argc++;
    }
    arglist_end(ap);

    /*
     * Create the argument vector.
     */
    arglist_start(ap, stderrfd);
    argv = (char **)g_malloc((argc + 1) * sizeof(*argv));
    i = 0;
    while((argv[i] = arglist_val(ap, char *)) != NULL) {
        if (argv[i] != skip_argument) {
	    i++;
        }
    }
    arglist_end(ap);

    pid = pipespawnv_passwd(prog, pipedef, need_root,
			    stdinfd, stdoutfd, stderrfd, argv);
    amfree(argv);
    return pid;
}

pid_t
pipespawnv(
    char *	prog,
    int		pipedef,
    int		need_root,
    int *	stdinfd,
    int *	stdoutfd,
    int *	stderrfd,
    char **	my_argv)
{
    return pipespawnv_passwd(prog, pipedef, need_root,
			     stdinfd, stdoutfd, stderrfd,
	my_argv);
}

pid_t
pipespawnv_passwd(
    char *	prog,
    int		pipedef,
    int		need_root,
    int *	stdinfd,
    int *	stdoutfd,
    int *	stderrfd,
    char **	my_argv)
{
    pid_t pid;
    int i, inpipe[2], outpipe[2], errpipe[2], passwdpipe[2];
    char number[NUM_STR_SIZE];
    char **arg;
    char *e;
    char **env;
    char *cmdline;
    char **newenv;
    char *passwdvar = NULL;
    int  *passwdfd = NULL;
    GPtrArray *array = g_ptr_array_new();
    gchar **strings;

    /*
     * Log the command line and count the args.
     */
    if ((pipedef & PASSWD_PIPE) != 0) {
	passwdvar = *my_argv++;
	passwdfd  = (int *)*my_argv++;
    }
    memset(inpipe, -1, sizeof(inpipe));
    memset(outpipe, -1, sizeof(outpipe));
    memset(errpipe, -1, sizeof(errpipe));
    memset(passwdpipe, -1, sizeof(passwdpipe));

    g_ptr_array_add(array, g_strdup(prog));

    for(arg = my_argv; *arg != NULL; arg++) {
        if (*arg == skip_argument)
            continue;
        g_ptr_array_add(array, quote_string(*arg));
    }

    g_ptr_array_add(array, NULL);

    strings = (gchar **)g_ptr_array_free(array, FALSE);
    cmdline = g_strjoinv(" ", strings);
    g_strfreev(strings);

    dbprintf(_("Spawning \"%s\" in pipeline\n"), cmdline);

    /*
     * Create the pipes
     */
    if ((pipedef & STDIN_PIPE) != 0) {
	if(pipe(inpipe) == -1) {
	    error(_("error [open pipe to %s: %s]"), prog, strerror(errno));
	    /*NOTREACHED*/
	}
    }
    if ((pipedef & STDOUT_PIPE) != 0) {
	if(pipe(outpipe) == -1) {
	    error(_("error [open pipe to %s: %s]"), prog, strerror(errno));
	    /*NOTREACHED*/
	}
    }
    if ((pipedef & STDERR_PIPE) != 0) {
	if(pipe(errpipe) == -1) {
	    error(_("error [open pipe to %s: %s]"), prog, strerror(errno));
	    /*NOTREACHED*/
	}
    }
    if ((pipedef & PASSWD_PIPE) != 0) {
	if(pipe(passwdpipe) == -1) {
	    error(_("error [open pipe to %s: %s]"), prog, strerror(errno));
	    /*NOTREACHED*/
	}
    }

    /*
     * Fork and set up the return or run the program.
     */
    switch(pid = fork()) {
    case -1:
	e = strerror(errno);
	error(_("error [fork %s: %s]"), prog, e);
	/*NOTREACHED*/

    default:	/* parent process */
	if ((pipedef & STDIN_PIPE) != 0) {
	    aclose(inpipe[0]);		/* close input side of pipe */
	    *stdinfd = inpipe[1];
	}
	if ((pipedef & STDOUT_PIPE) != 0) {
	    aclose(outpipe[1]);		/* close output side of pipe */
	    *stdoutfd = outpipe[0];
	}
	if ((pipedef & STDERR_PIPE) != 0) {
	    aclose(errpipe[1]);		/* close output side of pipe */
	    *stderrfd = errpipe[0];
	}
	if ((pipedef & PASSWD_PIPE) != 0) {
	    aclose(passwdpipe[0]);	/* close input side of pipe */
	    *passwdfd = passwdpipe[1];
	}
	break;
    case 0:		/* child process */
	debug_dup_stderr_to_debug();
	if ((pipedef & STDIN_PIPE) != 0) {
	    aclose(inpipe[1]);		/* close output side of pipe */
	} else {
	    inpipe[0] = *stdinfd;
	}
	if ((pipedef & STDOUT_PIPE) != 0) {
	    aclose(outpipe[0]);		/* close input side of pipe */
	} else {
	    outpipe[1] = *stdoutfd;
	}
	if ((pipedef & STDERR_PIPE) != 0) {
	    aclose(errpipe[0]);		/* close input side of pipe */
	} else {
	    errpipe[1] = *stderrfd;
	}
        if ((pipedef & PASSWD_PIPE) != 0) { 
            aclose(passwdpipe[1]);      /* close output side of pipe */
        }

	/*
	 * Shift the pipes to the standard file descriptors as requested.
	 */
	if(dup2(inpipe[0], 0) == -1) {
	    g_fprintf(stderr, "error [spawn %s: dup2 in: %s]", prog, strerror(errno));
	    exit(1);
	    /*NOTREACHED*/
	}
	if(dup2(outpipe[1], 1) == -1) {
	    g_fprintf(stderr, "error [spawn %s: dup2 out: %s]", prog, strerror(errno));
	    exit(1);
	    /*NOTREACHED*/
	}
	if(dup2(errpipe[1], 2) == -1) {
	    g_fprintf(stderr, "error [spawn %s: dup2 err: %s]", prog, strerror(errno));
	    exit(1);
	    /*NOTREACHED*/
	}

	/*
	 * Get the "safe" environment.  If we are sending a password to
	 * the child via a pipe, add the environment variable for that.
	 */
	env = safe_env();
	if ((pipedef & PASSWD_PIPE) != 0) {
	    for (i = 0; env[i] != NULL; i++)
		(void)i; /* make lint happy and do nothing */	
	    newenv = (char **)g_malloc((i + 1 + 1) * sizeof(*newenv));
	    g_snprintf(number, sizeof(number), "%d", passwdpipe[0]);
	    newenv[0] = g_strjoin(NULL, passwdvar, "=", number, NULL);
	    for(i = 0; env[i] != NULL; i++)
	    	newenv[i + 1] = env[i];
	    newenv[i + 1] = NULL;
	    amfree(env);
	    env = newenv;
	    safe_fd(passwdpipe[0], 1);
	} else {
	    safe_fd(-1, 0);
	}

	if (need_root) {
	    become_root();
	} else {
	    /* if our real userid is zero, the child shouldn't inherit
	     * that, so drop privs permanently */
	    if (getuid() == 0 && !set_root_privs(-1)) {
		error(_("could not drop root privileges"));
	    }
	}

	execve(prog, my_argv, env);
	e = strerror(errno);
	error(_("error [exec %s: %s]"), prog, e);
	/*NOTREACHED*/
    }
    amfree(cmdline);
    return pid;
}
