/* shell.c - suspend the viewer and drop into a shell
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include "hook.h"
#include "terminal.h"
#include "viewmenu.h"

typedef void (*sighandler_t) (int);


/* Drop into a shell with the current directory set to the menu path.
 */
void menuview_shell(menudata_t data)
{
	char buf[1024];
	sighandler_t osc;
	sighandler_t osr;
	struct passwd *p;
	pid_t c, r;
	char *a;
	int s;

	t_clear();
	printf("Type `exit' to return to the viewer.\n\n");
	t_echo_on();
	t_canon_on();

	bbs_hook(HOOK_SET_ACTION, 0, "Shelling Out");

	c = fork();

	if (c == 0) {

		bbs_hook(HOOK_DROP_PRIVS, 0, 0);

		p = getpwuid(getuid());
		if (!p)
			exit(1);

		chdir(p->pw_dir);	    /* start in home dir */

		if (data->file) {
			strncpy(data->buf, data->file, sizeof(data->buf));
			data->buf[sizeof(data->buf)] = 0;
			a = strrchr(data->buf, '/');
			if (a)
				*a = 0;
			chdir(data->buf);   /* try to go to current dir */
			if (!getcwd(buf, sizeof(buf) - 1))
				chdir(p->pw_dir);
		}

		signal(SIGCONT, SIG_DFL);
		signal(SIGTTIN, SIG_IGN);
		signal(SIGTTOU, SIG_IGN);

		execl(p->pw_shell, p->pw_shell, 0);
		exit(1);

	}

	osc = signal(SIGCONT, SIG_DFL);
	osr = signal(SIGWINCH, SIG_DFL);

	do {

		alarm(10);		    /* update status every 10 seconds */
		r = KEY_NONE;
		bbs_hook(HOOK_KEY_PRESSED, &c, &r);

		r = waitpid(c, &s, 0);
		if ((r <= 0) && ((errno == EAGAIN) || (errno == EINTR)))
			r = 1;

	} while (r > 0);

	t_init(getenv("TERM"));
	t_echo_off();
	t_canon_off();

	sleep(1);

	signal(SIGCONT, osc);
	signal(SIGWINCH, osr);
}

/* EOF */
