[VTE] Alternative API for



Hi

We are trying to make anjuta use VTE instead of ZVT, but we're stuck
because we need a replacement for zvt_term_forkpty. This is bacause
we're listening to the pty for the password prompt (for handling CVS
commands, etc.) and opening a dialog to get teh password from the user,
which we're feeding back to the child process.

Can you suggest how we can do this using VTE ?

I'm attaching the relevant code for your reference.

Thanks in advance.

-- 
Biswa.

/*
    launcher.c
    Copyright (C) 2000  Kh. Naba Kumar Singh

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <sys/time.h>
#include <errno.h>
#include <libzvt/libzvt.h>

#include "anjuta.h"
#include "resources.h"
#include "launcher.h"
#include "controls.h"
#include "global.h"
#include "pixmaps.h"

/* #define DEBUG */

#ifdef __FreeBSD__
#ifndef O_SYNC
#define O_SYNC 0
#endif /* O_SYNC */
#endif /* __FreeBSD__ */ 

static void to_terminal_child_terminated (GtkWidget* term, gpointer data);
static gint launcher_poll_inputs_on_idle (gpointer data);
static gboolean launcher_execution_done (gpointer data);
static void launcher_set_busy (gboolean flag);

static void launcher_send_ptyin (gchar * input_str);
static void launcher_pty_check_password(gchar* buffer);
static gboolean launcher_pty_check_child_exit_code (gchar* line);
static GtkWidget* create_password_dialog (gchar* prompt);

/************************************************************
*    Due to some programming restriction only one instance of
*    Launcher is declared. Moreover, I have no intension of using
*    more than one instance of this structure.
*
*    That's why I have defined it here and not in launcher.h
*************************************************************/
struct				/* Launcher */
{
/*
 *  Busy flag is TRUE if the Launcher
 *  is currently executing a child.
 */
  gboolean busy;

/* Controlling terminal */
  GtkWidget* terminal;
/* The position where the last buffer was delivered */
  glong char_pos;

/* These flags are used to synchronize the IO operations. */
  gboolean stdout_is_done;
  gboolean stderr_is_done;
  gboolean pty_is_done;
  gboolean child_has_terminated;

/* Child's stdin, stdout and stderr pipes */
  gint stderr_pipe[2];
  gint stdout_pipe[2];
  gint stdin_pipe[2];

/* Gdk  Input Tags */
  gint idle_id;
  gint poll_id;

/* The child */
  pid_t child_pid;
  gint child_status;

/* Start time of execution */
  time_t start_time;

  /* Callbacks */
  void (*stdout_arrived) (gchar *);
  void (*stderr_arrived) (gchar *);
  void (*child_terminated) (gint status, time_t time);

} launcher;

/* By any means DO NOT call launcher_init() more than once */
void
launcher_init ()
{
  launcher_set_busy (FALSE);
}

gboolean launcher_is_busy ()
{
  return launcher.busy;
}

static void
launcher_set_busy (gboolean flag)
{
  launcher.busy = flag;
  main_toolbar_update ();
  extended_toolbar_update ();
}

static void
launcher_scan_output ()
{
  int n;
  gchar buffer[FILE_BUFFER_SIZE];

  n = read (launcher.stdout_pipe[0], buffer, FILE_BUFFER_SIZE-1);
  if (n > 0)			/*    There is output  */
  {
    buffer[n] = '\0';
    if (launcher.stdout_arrived)
      (*(launcher.stdout_arrived)) (buffer);
  }
  /* The pipe is closed on the other side */
  else if (n == 0)
  {
    launcher.stdout_is_done = TRUE;
  }
  /* Error - abort if not related to non blocking read or interrupted syscall */
  else if (errno != EAGAIN && errno != EINTR) {
    g_warning(_("launcher.c: error while reading child stdout - %s\n"), strerror(errno));
    launcher.stdout_is_done = TRUE;      
  }
}

static void
launcher_scan_error ()
{
  int n;
  gchar buffer[FILE_BUFFER_SIZE];

  n = read (launcher.stderr_pipe[0], buffer, FILE_BUFFER_SIZE-1);
  if (n > 0)			/*    There is stderr output  */
  {
    buffer[n] = '\0';
    if (launcher.stderr_arrived)
      (*(launcher.stderr_arrived)) (buffer);
  }
  else if (n == 0)			/* The pipe is closed on the other side */
  {
    #ifdef DEBUG
      g_warning("launcher_scan_error - EOF");
    #endif
    launcher.stderr_is_done = TRUE;
  }
  /* Error - abort if not related to non blocking read or interrupted syscall */
 else if (errno != EAGAIN && errno != EINTR) {
    g_warning(_("launcher.c: error while reading child stderr - %s\n"), strerror(errno));
    launcher.stderr_is_done = TRUE;      
  }
}

static void
launcher_scan_pty()
{
	if (launcher.terminal)
	{
		gint len;
		gchar* chars = NULL;
		
		vt_clear_selection ((ZVT_TERM (launcher.terminal))->vx);
		chars = zvt_term_get_buffer(ZVT_TERM(launcher.terminal),
		                            &len, VT_SELTYPE_LINE, -10000,
                                    0, 10000, 0);
		vt_clear_selection (ZVT_TERM (launcher.terminal)->vx);
		
		zvt_term_reset(ZVT_TERM(launcher.terminal), TRUE);
		launcher.char_pos = 1;
		if (chars && strlen(chars) > launcher.char_pos)
		{
			glong start, end;
			gchar *last_line;
#ifdef DEBUG
			g_print("Chars buffer = %s, len = %d", chars, len);
#endif
			end = strlen(chars)-1;
			while (end > 0 && chars[end] == '\n') end--;
			start = end;
			while (start > 0 && chars[start-1] != '\n') start--;

			if (end > start)
			{
				last_line = g_strndup(&chars[start], end-start+1);

#ifdef DEBUG
				g_print("Last line = %s", last_line);
#endif
				if (!launcher.child_has_terminated) /* TTimo: not sure what this check is really worth */
					launcher_pty_check_password(last_line);
				launcher.pty_is_done = launcher_pty_check_child_exit_code(last_line);
				g_free(last_line);
			}
			launcher.char_pos = strlen(chars);
		}
		if (chars) g_free(chars);
	}
};

/* call regularly scheduled by a gtk_timeout_add - stops upon first FALSE return value */
static gint launcher_poll_inputs_on_idle (gpointer data)
{
  if (launcher.stderr_is_done == FALSE) {
    launcher_scan_error ();
  }
  if (launcher.stdout_is_done == FALSE) {
    launcher_scan_output ();
  }
  if (launcher.pty_is_done == FALSE) {
	launcher_scan_pty();	
	if (launcher.child_has_terminated) {
		launcher.pty_is_done = TRUE;
	}
  }
  /* keep running me as long as there is at least one not done yet */
  return (!launcher.stderr_is_done || !launcher.stdout_is_done || !launcher.pty_is_done);
}

void
launcher_send_stdin (gchar * input_str)
{
  write (launcher.stdin_pipe[1], input_str,
	 strlen (input_str) * sizeof (char));
  write (launcher.stdin_pipe[1], "\n", sizeof (char));
}

void
launcher_send_ptyin (gchar * input_str)
{
  if (!input_str || strlen(input_str) == 0) return;
  zvt_term_writechild(ZVT_TERM(launcher.terminal), input_str, strlen(input_str));
}

void
launcher_reset (void)
{
  if (launcher_is_busy ())
    /* kill (launcher.child_pid, SIGTERM); */
	zvt_term_killchild(ZVT_TERM(launcher.terminal), SIGTERM);
}

void
launcher_signal (int sig)
{
  if (launcher_is_busy ())
    /* kill (launcher.child_pid + 1, sig); */
	zvt_term_killchild(ZVT_TERM(launcher.terminal), sig);
}

pid_t launcher_get_child_pid ()
{
  if (launcher_is_busy ())
    return launcher.child_pid;
  else
    return -1;
}

gboolean
launcher_execute (gchar * command_str,
		  void (*so_line_arrived) (gchar *),
		  void (*se_line_arrived) (gchar *),
		  void (*cmd_terminated) (gint status, time_t time))
{
  int md;
  gchar *shell;

  if (launcher_is_busy ())
    return FALSE;

  launcher_set_busy (TRUE);

/* The callback functions */
  launcher.stdout_arrived = so_line_arrived;
  launcher.stderr_arrived = se_line_arrived;
  launcher.child_terminated = cmd_terminated;

/* The pipes */
  pipe (launcher.stderr_pipe);
  pipe (launcher.stdout_pipe);
  pipe (launcher.stdin_pipe);

  launcher.start_time = time (NULL);
  launcher.child_has_terminated = FALSE;
  launcher.child_status = 0;
  launcher.stdout_is_done = FALSE;
  launcher.stderr_is_done = FALSE;
  launcher.pty_is_done = FALSE;
  
  launcher.char_pos = 1;

  shell = gnome_util_user_shell();
  if (NULL == shell || '\0' == shell[0])
    shell = "sh";
  
  launcher.terminal = zvt_term_new ();
  zvt_term_set_size(ZVT_TERM (launcher.terminal), 100, 100);
  gtk_signal_connect (GTK_OBJECT (launcher.terminal), "child_died", 
  		GTK_SIGNAL_FUNC (to_terminal_child_terminated), NULL);

#ifdef LAUNCHER_DEBUG
  if (launcher.terminal) {
	  GtkWindow* win;
	  win = gtk_window_new(0);
	  gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(app->widgets.window));
	  gtk_container_add(GTK_CONTAINER(win), launcher.terminal);
	  gtk_widget_show_all(win);
  }
#endif
  
  launcher.child_pid = zvt_term_forkpty (ZVT_TERM (launcher.terminal), 
		ZVT_TERM_DO_UTMP_LOG | ZVT_TERM_DO_WTMP_LOG);
  if (launcher.child_pid == 0)
  {
    char *total_cmd;
	
    close (2);
    dup (launcher.stderr_pipe[1]);
    close (1);
    dup (launcher.stdout_pipe[1]);
    close (0);
    dup (launcher.stdin_pipe[0]);

    /* Close unnecessary pipes */
    close (launcher.stderr_pipe[0]);
    close (launcher.stdout_pipe[0]);
    close (launcher.stdin_pipe[1]);

	/* Set no delays for the write pipes (non_buffered) so
	  that we get all the outputs immidiately */
	if ((md = fcntl (launcher.stdout_pipe[1], F_GETFL)) != -1)
		fcntl (launcher.stdout_pipe[1], F_SETFL, O_SYNC | md);
	if ((md = fcntl (launcher.stderr_pipe[1], F_GETFL)) != -1)
		fcntl (launcher.stderr_pipe[1], F_SETFL, O_SYNC | md);
  
	/* This is a quick hack to get the child's exit code */
	/* Don't complain!! */
	total_cmd = g_strconcat(command_str,
		"; echo -n \\(Child exit code: $?\\) > /dev/tty",
		NULL);
	execlp (shell, shell, "-c", total_cmd, NULL);
	g_error (_("Cannot execute command shell"));
  }
  
#ifdef DEBUG
  printf("zvt_term_forkpty %d\n", launcher.child_pid);
#endif  
  
  close (launcher.stderr_pipe[1]);
  close (launcher.stdout_pipe[1]);
  close (launcher.stdin_pipe[0]);

/*
 *  Set pipes none blocking, so we can read big buffers
 *  in the callback without having to use FIONREAD
 *  to make sure the callback doesn't block.
 */
  if ((md = fcntl (launcher.stdout_pipe[0], F_GETFL)) != -1)
    fcntl (launcher.stdout_pipe[0], F_SETFL, O_NONBLOCK | md);
  if ((md = fcntl (launcher.stderr_pipe[0], F_GETFL)) != -1)
    fcntl (launcher.stderr_pipe[0], F_SETFL, O_NONBLOCK | md);

  launcher.poll_id = gtk_timeout_add (150, launcher_poll_inputs_on_idle, NULL);
  return TRUE;
}

static void
to_terminal_child_terminated (GtkWidget* term, gpointer data)
{
#ifdef DEBUG	
  printf("Terminal child terminated called\n");
#endif

  launcher.child_has_terminated = TRUE;
  launcher.idle_id = gtk_timeout_add (50, launcher_execution_done, NULL);
}

static gboolean
launcher_execution_done (gpointer data)
{
#ifdef DEBUG	
  printf("launcher_execution_done %d %d %d\n", launcher.stdout_is_done ? 1 : 0, launcher.stderr_is_done ? 1 : 0, launcher.pty_is_done ? 1 : 0);
#endif

  if (launcher.stdout_is_done == FALSE ||
	  launcher.stderr_is_done == FALSE ||
	  launcher.pty_is_done == FALSE)
    return TRUE; 
    
  close (launcher.stdin_pipe[1]);
  close (launcher.stdout_pipe[0]);
  close (launcher.stderr_pipe[0]);
	
  zvt_term_closepty(ZVT_TERM(launcher.terminal));
  zvt_term_reset(ZVT_TERM(launcher.terminal), 1);
  gtk_widget_destroy(launcher.terminal);
  launcher.terminal = NULL;
  launcher_set_busy (FALSE);
  
  /* Call this here, after set_busy(FALSE)so we are able to 
	 launch a new child from the terminate function.
	 (by clubfan 2002-04-07) */
#ifdef DEBUG
  g_print("Exit status: %d\n", launcher.child_status);
#endif
  if (launcher.child_terminated)
    (*(launcher.child_terminated)) (launcher.child_status, time (NULL) - launcher.start_time);
  
  return FALSE;
}

static gboolean
launcher_pty_check_child_exit_code (gchar* line)
{
	gboolean ret;
	gboolean exit_code;
	gchar* prompt = "(Child exit code: ";

#ifdef DEBUG
	g_print ("Child exit code called: %s\n", line);
#endif
	
	if (!line) return FALSE;
	if (strlen(line) <= (strlen(prompt)+1)) return FALSE;
	
	ret = FALSE;
	if (strncmp(line, prompt, strlen(prompt)) == 0) {
		gchar *ascii_str = g_strdup(&line[strlen(prompt)]);
		gchar *ptr = strstr(ascii_str, ")");
		if (ptr) {
			*ptr = '\0';
			exit_code = atoi(ascii_str);
			launcher.child_status = exit_code;
#ifdef DEBUG
			g_print ("Exit code: %d\n", exit_code);
#endif
			ret = TRUE;
		}
		g_free(ascii_str);
	}
	return ret;
}

/* pty buffer check for password authentication */
static void
launcher_pty_check_password(gchar* last_line)
{
	gchar *prompt = "assword: ";
	gchar *prompt_index;
	
	if (launcher_is_busy() == FALSE) return;
	
	if (last_line) {
#ifdef DEBUG
		printf("%s\n", last_line);
#endif
		if (strlen(last_line) < strlen(prompt))
			return;
		prompt_index = &last_line[strlen(last_line) - strlen(prompt)];
		if (strcasecmp(prompt_index, prompt) == 0) {
			/* Password prompt detected */
			GtkWidget* dialog;
			gint button;
			const gchar* passwd;
			gchar* line;
			
			dialog = create_password_dialog (last_line);
			button = gtk_dialog_run(GTK_DIALOG(dialog));
			switch(button) {
				case 0:
					passwd = gtk_entry_get_text(
						GTK_ENTRY(g_object_get_data(G_OBJECT(dialog),
									"password_entry")));
					line = g_strconcat(passwd, "\n", NULL);
					launcher_send_ptyin(line);
					g_free(line);
					break;
				case 1:
					launcher_send_ptyin("<canceled>\n");
					launcher_reset();
					break;
				default:
					break;
			}
			gtk_widget_destroy(dialog);
		}
	}
}

/* Password dialog */
static GtkWidget*
create_password_dialog (gchar* prompt)
{
	GtkWidget *dialog;
	GtkWidget *hbox;
	GtkWidget *box;
	GtkWidget *icon;
	GtkWidget *label;
	GtkWidget *entry;
	
	g_return_val_if_fail(prompt, NULL);
	
	dialog = gtk_dialog_new_with_buttons (prompt,
	                        GTK_WINDOW (app->widgets.window),
	                        GTK_DIALOG_DESTROY_WITH_PARENT,
	                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	                        GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

	gtk_window_set_wmclass (GTK_WINDOW (dialog), "launcher-password-prompt",
				"anjuta");
	gtk_window_set_transient_for (GTK_WINDOW(dialog),
								  GTK_WINDOW(app->widgets.window));
	
	hbox = gtk_hbox_new(FALSE, 10);
	gtk_widget_show(hbox);
	gtk_container_add (GTK_CONTAINER (dialog), hbox);
	
	icon = anjuta_res_get_image (ANJUTA_PIXMAP_PASSWORD);
	gtk_widget_show(icon);
	gtk_box_pack_start_defaults (GTK_BOX(hbox), icon);
	
	if (strlen(prompt) < 20) {
		box = gtk_hbox_new(FALSE, 5);
	} else {
		box = gtk_vbox_new(FALSE, 5);
	}
	gtk_widget_show(box);
	gtk_box_pack_start_defaults (GTK_BOX(hbox), box);
	
	label = gtk_label_new (_(prompt));
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
	
	entry = gtk_entry_new ();
	gtk_widget_show (entry);
	gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
	gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
	
	gtk_widget_ref(entry);
	gtk_object_set_data_full(GTK_OBJECT(dialog), "password_entry",
				entry, (GtkDestroyNotify)gtk_widget_unref);
	gtk_widget_grab_focus(entry);
	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
	
	return dialog;
}
/* 
    launcher.h
    Copyright (C) 2000  Kh. Naba Kumar Singh

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifndef _LAUNCHER_H_
#define _LAUNCHER_H_

#include <gnome.h>

/* This is only called once, before anything starts */
void launcher_init(void);

/* Get the pid of the currently executing child */
pid_t launcher_get_child_pid(void);

/* Reset the launcher. This will terminate the current job */
void launcher_reset(void);

/* Send a signal to the currently executing process */
void launcher_signal(int);

/* Checks if there is a job being executed */
gboolean launcher_is_busy(void);

/* Start a job. */
/* command_str is the command to be executed */
/* stdout_func is the callback to be called when chars are received in STDOUT from the process */
/* stderr_func is the callback to be called when chars are received in STDERR from the process */
/* commnad_terminated is the callback to be called when the job is completed or terminated */
gboolean launcher_execute(gchar* command_str,
                               void (*stdout_func)(gchar*),
                               void (*stderr_func)(gchar*),
                               void (*command_terminated)(gint status, time_t time));

/* sends the given string to the STDIN of the process */
void launcher_send_stdin(gchar* input_str);

#endif


****** Message from InterScan E-Mail VirusWall NT ******

** No virus found in attached file launcher.c
** No virus found in attached file launcher.h

NO VIRUS FOUND: SERVER GENERATED MESSAGE
*****************     End of message     ***************



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]