Code that works for me. Was: Proper way to fork() a GTK program




OK, what I came up with is attached below (some parts skipped, so it
is not supposed to compile), but it works for me. The problem is that
while the forked process is running the gtk main loop is not called
and you can not do anything with your interface except for watching
the lines scroll by ;-) . I can not even kill the child process now.

Actually I have a suggestion to make a GtkProcess widget which would
allow redirection of any of its I/O streams to/from GtkText
widgets. Something along the lines of:

	GtkProcess *prog = gtk_process_new("programname");
	FILE *file = fopen(...);
	int fd = socket(...); 
	GtkText *text = ...;
	extern void on_child_exit(GtkProcess *proc, int returncode);

	/* read data from the socket */
	gtk_process_attach_fd(prog, STDIN_FILENO, fd);
	/* save results to a file */
	gtk_process_attach_file(prog, STDOUT_FILENO, file);
	/* show errors/messages in a window */
	gtk_process_attach_text(prog, STDERR_FILENO, text);

	gtk_signal_connect (GTK_OBJECT (prog), "childexit",
                      GTK_SIGNAL_FUNC (on_child_exit),
                      NULL);

	gtk_process_run(prog);

Of course GtkProcess should do select() on the pipe attached to the
text widget and update it asynchronously.

What do you think of this? 

Sincerely, 

Paul	

D. Emilio Grimaldo Tunon writes:
 > Paul Bunyk wrote:
 > > 
 > > Hello, everyone!
 > > 
 > > Thank you so much for help, I've changed to _exit() in forked child
 > > and everything works now. Now I can run an external process with
 > > stderr redirected to a text widget (and if the process cares to say
 > > "ERROR" or "WARNING" it is drawn in bright colors and popup window
 > 
 > 
 >   Now that you ask, it would be nice to see, so where is it? I
 > just wish I had time for GTK these days, too hectic at work
 > and at home my computer room is inhabited by a guest for a month
 > (and then another for a month or two :'( ) so I am
 > computer deprived but not yet suffering withdrawal symptoms(sp?) :)
 > 

-------------- CODE ------------------

/*
 * Show an error message 
 */
void
showError(char *where, char *message)
{    
    GtkLabel *whereLabel = get_widget(ErrorDialog, "errorWhere");
    GtkLabel *messageLabel = get_widget(ErrorDialog, "errorMessage");
    
    HadError = 1;

    gtk_label_set(whereLabel, where);
    gtk_label_set(messageLabel, message);
    gtk_widget_show (ErrorDialog);
}

/*
 * Function used in runProgram: tell parent (via stderr pipe) 
 * about the problem and _exit (can not use exit() under GTK 
 * because it will screw up the parent). 
 */
void
childError(char *where)
{
    fprintf(stderr, "ERROR: %s: %s\n", 
	    where, strerror(errno));
    _exit(1);
}

/* 
 * Run an external program with specified stdin/stdout, 
 * show stderr in log window
 */
void
runProgram(char *program, char *argv[], char *infile, char *outfile)
{
    GtkText *logtext = get_widget(TopWindow, "logtext");
    char buffer[1024];
    int errpipe[2];
    int i = 0;

    pid_t pid;

    sprintf(buffer, "\n%s ", program);
    gtk_text_insert (logtext, fixed_font, &green , NULL, 
		     buffer, strlen(buffer));
    if (argv) 
	for (i=0; argv[i]; i++) 
	{
	    sprintf(buffer, "%s ", argv[i]);
	    gtk_text_insert (logtext, fixed_font, &green , NULL, 
			     buffer, strlen(buffer));
	}
    sprintf(buffer, " < %s > %s\n", infile, outfile);
    gtk_text_insert (logtext, fixed_font, &green , NULL, 
		     buffer, strlen(buffer));

    if (pipe(errpipe))
    {
	showError("pipe", strerror(errno));
	return;
    }
    
    if ((pid = fork()) == -1)
    {
	showError("fork", strerror(errno));
	return;
    }
    if (pid==0) /* child */
    {
	/* switch stderr to errpipe 
	 */
	close(errpipe[0]);
	if (dup2 (errpipe[1], STDERR_FILENO) == -1) 
	    childError("dup"); 
	/* well, this one will print error message 
	 * on terminal since dup2 was not successful :( 
	 */

	if (infile && freopen(infile, "r", stdin) == NULL)
	    childError(infile);

	if (outfile && freopen(outfile, "w", stdout) == NULL)
	    childError(outfile);

       	if (execvp(program, argv) == -1)
	    childError(program);
    }
    else /* parent */
    {
	FILE *errfile;

	close(errpipe[1]);
	/* I want to do string I/O, thus switch to FILE interface
	 */
	if ((errfile = fdopen(errpipe[0], "r"))== NULL)
	{
	    showError("fdopen", strerror(errno));
	    return; /* have to kill child here !!! */
	}
	
	while (fgets(buffer, sizeof(buffer), errfile))
	{
	    if (strncmp(buffer, "ERROR", 3) == 0)
	    {
		char *what, *where, *why;

		gtk_text_insert (logtext, fixed_font, &red , NULL, 
				 buffer, strlen(buffer));
		what = strtok(buffer, ":");
		where = strtok(NULL, ":");
		why = strtok(NULL, "\n");
		showError(where, why);
	    }
	    else if (strncmp(buffer, "WARNING", 4) == 0)
		gtk_text_insert (logtext, fixed_font, &purple , NULL, 
				 buffer, strlen(buffer));
	    else
		gtk_text_insert (logtext, fixed_font, &blue, NULL, 
				 buffer, strlen(buffer));
		
	}
    }
}

/* This is how I use runProgram() */
on_cif2dxcButton_clicked               (GtkButton       *button,
                                        gpointer         user_data)
{
    runProgram(entryGetText("cif2dxcPath"),
	       NULL,
	       entryGetText("CIFname"),
	       entryGetText("DXCname"));
}


--------------- END ------------------

-- 
  ("`-''-/").___..--''"`-._   UNIX *is* user-friendly, he is just very 
   `6_ 6  )   `-.  (     ).`-.__.`) picky about who his friends are...
   (_Y_.)'  ._   )  `._ `. ``-..-'      Paul Bunyk, Research Scientist
 _..`--'_..-_/  /--'_.' ,'art by           (and part-time UN*X sysadm)
(il),-''  (li),'  ((!.-' F. Lee http://pbunyk.physics.sunysb.edu/~paul



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