On Saturday 11 June 2005 11:32, Dinh Duong Ha wrote: > > Ky~ thua^.t ma` /me du`ng la` cha(.n signal SGN_CHILD, trong ha`m xu+? > li' signal na`y, go.i ha`m wait dde^? cho+` child process exit. > The^m: Ne^'u parent process ke^'t thu'c tru+o+'c child process thi` > child process se~ co' parent process la` 1- init process, va` init > process se~ gia?i pho'ng tho^ng tin ve^` child process khi child process > ke^'t thu'c => gia?i pho'ng process id cho tie^'n tri`nh kha'c. >
Xin cha`o ba'c. Ca'm o+n ba'c dda~ go+.i y'. Em dda~ la`m ddu+o+.c... su+? du.ng signal SGN_CHILD. Tuy nhie^n, va^~n sinh ra tru.c tra(.c kha'c: ddo' la` ddoi khi child_process thoa't , nhu+ng winefish ko^ the^? gia?i pho'ng resource du+o+.c. * tru+o+'c khi cha.y child_process: resource chie^'m bo+?i winefish la` 0% * khi cha.y child_process: winefish chie^'m 0%, child chie^'m khoa?ng 30% ta`i nguyene * sau khi child_process ke^'t thu'c (kho^ng bie^'t ke^' thu'c kie^?u gi`), winefish chie^'m 30% ta`i nguye^n <=== lo^~i. Y' cu?a em la`: ta`i nguye^n cu?a tie^'n tri`nh con ddu+o+.c gia?i pho'ng the^' na`o, trong hai tru+o+`ng ho+.p: * tie^'n tri`nh con thoa't bi`nh thu+o+`ng? * tie^'n tri`nh con bi. gu+?i signal SIGTERM? Xem ta^.p tin ddi'nh ke`m. Ca'm o+n ba'c ra^'t nhie^`u. -- kyanh [ http://kyanh.dotgeek.org/ ]
/* $Id: outputbox.c,v 1.3 2005/07/04 03:30:56 kyanh Exp $ */ /* Winefish LaTeX Editor (based on Bluefish HTML Editor) * outputbox.c the output box * * Copyright (C) 2002 Olivier Sessink * Modified for Winefish (C) 2005 Ky Anh <[EMAIL PROTECTED]> * * 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 */ /* #define DEBUG */ #include <gtk/gtk.h> #include <sys/types.h> #include <signal.h> /* kill() */ #include <sys/stat.h> /* open() */ #include <fcntl.h> /* kyanh, open() */ /* kyanh, 20050301, Thanks to M. Garoche <[EMAIL PROTECTED]> Move from <wait.h> to <sys/wait.h> */ #include <sys/wait.h> /* wait(), waitid() */ #include <regex.h> #include <stdlib.h> #include <string.h> /* strlen() */ /* Using waitpid() to free child process's resources, we don't need anymore <signal.h>. Anyway, thanks to M. Garoche for reporting a problem with Mac OS */ #include "bluefish.h" #include "outputbox.h" /* myself */ #include "bf_lib.h" #include "document.h" #include "gtk_easy.h" #include "stringlist.h" #include "pixmap.h" static void ob_lview_row_activated_lcb( GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, Toutputbox *ob ) { GtkTreeIter iter; gchar *file, *line; gint lineval; gtk_tree_model_get_iter( GTK_TREE_MODEL( ob->lstore ), &iter, path ); gtk_tree_model_get( GTK_TREE_MODEL( ob->lstore ), &iter, 0, &file, 1, &line, -1 ); g_print( "ob_lview_row_activated_lcb, file=%s, line=%s\n", file, line ); if ( file && strlen( file ) ) { doc_new_with_file( ob->bfwin, file, FALSE, FALSE ); } if ( line && strlen( line ) ) { lineval = atoi( line ); flush_queue(); doc_select_line( ob->bfwin->current_document, lineval, TRUE ); } g_free( line ); g_free( file ); } static void outputbox_close_clicked_lcb( GtkWidget *widget, Toutputbox *ob ) { setup_toggle_item_from_widget( ob->bfwin->menubar, N_( "/View/View Outputbox" ), FALSE ); gtk_widget_hide( ob->hbox ); } static sig_atomic_t child_exit_status; static void clean_up_child_process (gint signal_number) { /* Clean up the child process. */ gint status; wait (&status); /* Store its exit status in a global variable. */ child_exit_status = status; } /* static GtkWidget *create_popup_menu () { GtkWidget *menu; GtkWidget *menu_item; menu = gtk_menu_new (); / * Add the separator * / menu_item = gtk_separator_menu_item_new (); gtk_widget_show (menu_item); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item); return menu; } */ static Toutputbox *init_outputbox( Tbfwin *bfwin ) { /* we call this once */ /* Handle SIGCHLD by calling clean_up_child_process. */ struct sigaction sigchld_action; memset (&sigchld_action, 0, sizeof (sigchld_action)); sigchld_action.sa_handler = &clean_up_child_process; sigaction (SIGCHLD, &sigchld_action, NULL); /* create the backend */ GtkTreeViewColumn * column; GtkWidget *scrolwin; GtkCellRenderer *renderer; GtkWidget *vbox2, *but, *image; Toutputbox *ob; ob = g_new0( Toutputbox, 1 ); DEBUG_MSG( "init_output_box, created %p\n", ob ); bfwin->outputbox = ob; ob->bfwin = bfwin; ob->def = NULL; ob->pid=0; ob->hbox = gtk_hbox_new( FALSE, 0 ); gtk_paned_add2( GTK_PANED( bfwin->vpane ), ob->hbox ); gtk_paned_set_position( GTK_PANED( bfwin->vpane ), ( gint ) ( bfwin->vpane->allocation.height * 0.8 ) ); ob->lstore = gtk_list_store_new ( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); ob->lview = gtk_tree_view_new_with_model( GTK_TREE_MODEL( ob->lstore ) ); g_object_unref( ob->lstore ); /* the view widget now holds the only reference, if the view is destroyed, the model will be destroyed */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ( _("Filename"), renderer, "text", 0, NULL ); gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column ); column = gtk_tree_view_column_new_with_attributes ( _("Line"), renderer, "text", 1, NULL ); gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column ); column = gtk_tree_view_column_new_with_attributes ( _("Output"), renderer, "markup", 2, NULL ); gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column ); scrolwin = gtk_scrolled_window_new( NULL, NULL ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolwin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_container_add( GTK_CONTAINER( scrolwin ), ob->lview ); gtk_widget_set_size_request( scrolwin, 150, 150 ); gtk_box_pack_start( GTK_BOX( ob->hbox ), scrolwin, TRUE, TRUE, 0 ); g_signal_connect( G_OBJECT( ob->lview ), "row-activated", G_CALLBACK( ob_lview_row_activated_lcb ), ob ); vbox2 = gtk_vbox_new( FALSE, 0 ); but = gtk_button_new(); image = new_pixmap( 4 ); gtk_widget_show( image ); gtk_container_add( GTK_CONTAINER( but ), image ); gtk_container_set_border_width( GTK_CONTAINER( but ), 0 ); gtk_widget_set_size_request( but, 16, 16 ); g_signal_connect( G_OBJECT( but ), "clicked", G_CALLBACK( outputbox_close_clicked_lcb ), ob ); gtk_box_pack_start( GTK_BOX( ob->hbox ), vbox2, FALSE, FALSE, 0 ); gtk_box_pack_start( GTK_BOX( vbox2 ), but, FALSE, FALSE, 0 ); gtk_widget_show_all( ob->hbox ); setup_toggle_item_from_widget( bfwin->menubar, N_( "/View/View Outputbox" ), TRUE ); /* create the popup menu */ /* GtkWidget * menu = create_popup_menu(); */ return ob; } /* kyanh, added, 20050227 Send some specific message to outputbox. Please DONOT use for general purpose :! @markup: should be: b, i, u */ static void outputbox_message( Toutputbox *ob, const char *string, const char *markup ) { GtkTreeIter iter; gchar *tmpstr = g_markup_escape_text(string,-1); if (markup) { tmpstr = g_strdup_printf("> <%s>%s</%s>", markup, string, markup); }else{ tmpstr = g_strdup_printf("> %s", string); } gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmpstr, -1 ); g_free(tmpstr); /* TODO: Scroll as an Optional */ /* The Outputbox may *NOT* be shown before scrolling :) */ /* kyanh, added, 20050301 */ GtkTreePath *treepath = gtk_tree_model_get_path( GTK_TREE_MODEL( ob->lstore ), &iter ); gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW( ob->lview ), treepath, NULL, FALSE /* skip align */, 0, 0 ); gtk_tree_path_free( treepath ); } static void fill_outputbox( Toutputbox *ob, const gchar *source ) { GtkTreeIter iter; gchar *tmp_src = NULL; if ( ob->def->show_all_output ) { tmp_src = g_markup_escape_text(source,-1); gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 ); g_free(tmp_src); } else if ( regexec( &ob->def->preg, source, NUM_MATCH, ob->def->pmatch, 0 ) == 0 ) { /* we have a valid line */ gchar * filename, *line, *output; filename = line = output = NULL; gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); if ( ob->def->file_subpat >= 0 && ob->def->pmatch[ ob->def->file_subpat ].rm_so >= 0 ) { DEBUG_MSG( "fill_outputbox, filename from %d to %d\n", ob->def->pmatch[ ob->def->file_subpat ].rm_so , ob->def->pmatch[ ob->def->file_subpat ].rm_eo ); filename = g_strndup( &source[ ob->def->pmatch[ ob->def->file_subpat ].rm_so ], ob->def->pmatch[ ob->def->file_subpat ].rm_eo - ob->def->pmatch[ ob->def->file_subpat ].rm_so ); } if ( ob->def->line_subpat >= 0 && ob->def->pmatch[ ob->def->line_subpat ].rm_so >= 0 ) { DEBUG_MSG( "fill_outputbox, line from %d to %d\n", ob->def->pmatch[ ob->def->line_subpat ].rm_so , ob->def->pmatch[ ob->def->line_subpat ].rm_eo ); line = g_strndup( &source[ ob->def->pmatch[ ob->def->line_subpat ].rm_so ], ob->def->pmatch[ ob->def->line_subpat ].rm_eo - ob->def->pmatch[ ob->def->line_subpat ].rm_so ); } if ( ob->def->output_subpat >= 0 && ob->def->pmatch[ ob->def->output_subpat ].rm_so >= 0 ) { DEBUG_MSG( "fill_outputbox, output from %d to %d\n", ob->def->pmatch[ ob->def->output_subpat ].rm_so , ob->def->pmatch[ ob->def->output_subpat ].rm_eo ); output = g_strndup( &source[ ob->def->pmatch[ ob->def->output_subpat ].rm_so ], ob->def->pmatch[ ob->def->output_subpat ].rm_eo - ob->def->pmatch[ ob->def->output_subpat ].rm_so ); } if ( filename ) { gchar * fullpath; /* create_full_path uses the current directory of no basedir is set */ /* TODO: better hanlder with full path :) */ fullpath = create_full_path( filename, NULL ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 0, fullpath, -1 ); g_free( filename ); g_free( fullpath ); } if ( line ) { gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 1, line, -1 ); g_free( line ); } if ( output ) { tmp_src = g_markup_escape_text(output,-1); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 ); g_free( output ); g_free(tmp_src); } /* TODO: filter function */ } else if ( strstr( source, "LaTeX Error" ) || strstr( source, "Output written on") || strstr( source, "Warning") || strstr( source, "Overfull") ) { /* kyanh, 20050226, special filter for latex *.log file; this happens for e.g. when a Package not found (! LaTeX Error: .... ) */ tmp_src = g_markup_escape_text(source,-1); gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 ); g_free(tmp_src); } } /* kyanh, added, 20050301 */ static void free_ob( Toutputbox *ob, gboolean have_retfile ) { if ( have_retfile ) { /* free temporarily file */ remove_secure_dir_and_filename( ob->retfile ); DEBUG_MSG( "continue_execute: retfile=%s\n", ob->retfile ); g_free( ob->retfile ); } g_free( ob->def->pattern ); regfree( &ob->def->preg ); g_free( ob->def->command ); g_free( ob->def ); ob->def = NULL; /* to be check for next using */ ob->pid = 0; } /* kyanh, 20050302, moved from continute_execute() */ static void finish_execute( Toutputbox *ob ) { kill( ob->pid, SIGTERM ); waitpid( ob->pid, &child_exit_status, WNOHANG ); { gint exitcode = WEXITSTATUS( child_exit_status ); gchar *str_status = g_strdup_printf(_("Exit code: %d"), exitcode); outputbox_message( ob, str_status, "b" ); g_free( str_status ); } gtk_tree_view_columns_autosize( GTK_TREE_VIEW( ob->lview ) ); g_io_channel_unref( ob->io_channel ); gtk_timeout_remove( ob->pollID ); free_ob( ob, 1 ); } /* Idea taken from SciTTEGTK.cxx Modified by [EMAIL PROTECTED] kyanh, version 2 (20050302): use g_io_channel() kyanh, version 2.1 (20050315): readline(), not readchars() */ static void continue_execute( Toutputbox *ob ) { gsize count = 0; GIOStatus io_status; GError *error = NULL; gchar *buf = NULL; gsize terminator_pos = 0; gboolean continued = TRUE; while ( continued ) { continued = FALSE; buf = NULL; io_status = g_io_channel_read_line( ob->io_channel, &buf, &count, &terminator_pos, &error ); switch ( io_status ) { case G_IO_STATUS_ERROR: { gchar * tmpstr; tmpstr = g_strdup_printf( _("IOChannel Error: %s"), error->message ); outputbox_message( ob, tmpstr, "b" ); g_free( tmpstr ); finish_execute( ob ); } break; case G_IO_STATUS_EOF: /* without this, we dump into an infinite loop */ finish_execute( ob ); break; case G_IO_STATUS_NORMAL: continued = TRUE; if ( terminator_pos < count ) { buf[ terminator_pos ] = '\0'; } fill_outputbox( ob, buf ); break; default: break; } } g_free( buf ); g_clear_error( &error ); } /* continue to read data */ /* written by kyanh, 20050301 */ static void io_signal( GIOChannel *source, GIOCondition condition, Toutputbox *ob ) { continue_execute( ob ); } /* Taken from SciTTEGTK.cxx Detect if the tool has exited without producing any output */ static int poll_tool( Toutputbox *ob ) { continue_execute( ob ); return TRUE; } /* Taken from SciTTEGTK.cxx kyanh: everything emitted from `running' will be captured [ 2>&1 ] */ static gint xsystem( const gchar *command, const gchar *outfile ) { gint pid = 0; /* fork(): create a child proccess the differs from the parent only in its PID and PPID; the resouce ultilisation are set to 0 */ if ( ( pid = fork() ) == 0 ) { close( 0 ); gint fh = open( outfile, O_WRONLY ); close( 1 ); dup( fh ); /* dup uses the lowest?numbered unused descriptor for the new descriptor. */ close( 2 ); dup( fh ); DEBUG_MSG( "xsystem: running now [%s]\n", command ); execlp( "/bin/sh", "sh", "-c", command, NULL ); /* Functions that contain the letter l (execl, execlp, and execle) accept the argument list using the C language varargs mechanism. */ /* The execvp function returns only if an error occurs. */ exit( 127 ); } /* This is the parent process. */ return pid; } static void run_command( Toutputbox *ob ) { file_save_cb( NULL, ob->bfwin ); outputbox_message( ob, ob->def->command, "i" ); { gchar *project_mode; if ( main_v->props.project_mode ) { project_mode = g_strdup( _("Project Mode: ON") ); } else { project_mode = g_strdup( _("Project Mode: OFF") ); } outputbox_message( ob, project_mode, "i" ); g_free( project_mode ); } if ( ob->bfwin->current_document->filename ) { /* if the user clicked cancel at file_save -> return */ { gchar * tmpstring; if ( main_v->props.project_mode && ob->bfwin->project && ob->bfwin->project->basedir ) { tmpstring = g_strdup( ob->bfwin->project->basedir ); } else { tmpstring = g_path_get_dirname( ob->bfwin->current_document->filename ); } /* outputbox_message(ob, g_strconcat("> Working dir: ", tmpstring, NULL)); */ chdir( tmpstring ); g_free( tmpstring ); } gchar *command = convert_command( ob->bfwin, ob->def->command ); outputbox_message( ob, command, "b"); ob->retfile = create_secure_dir_return_filename(); gint fd = 1; if ( ob->retfile ) { fd = mkfifo( ob->retfile, S_IRUSR | S_IWUSR ); if ( fd == 0 ) { ob->pid = xsystem( command, ob->retfile ); GError *error = NULL; ob->io_channel = g_io_channel_new_file( ob->retfile, "r", &error ); if ( ob->io_channel != NULL ) { /* Fix the BUGS[200503]#20 */ g_io_channel_set_encoding( ob->io_channel, NULL, NULL ); g_io_add_watch( ob->io_channel, G_IO_IN, ( GIOFunc ) io_signal, ob ); /* add a background task in case there is no output from the tool */ ob->pollID = g_timeout_add( 200, ( GSourceFunc ) poll_tool, ob ); } else { gchar *tmpstr; tmpstr = g_strdup_printf( _("Error: %s"), error->message ); outputbox_message( ob, tmpstr, "b" ); if ( error->code == G_FILE_ERROR_INTR ) { outputbox_message( ob, _("Hint: You may call the tool again"), "i" ); } g_free( tmpstr ); outputbox_message( ob, _("Tool finished."), "b" ); free_ob( ob, 1 ); } g_clear_error( &error ); } } if ( fd != 0 ) { outputbox_message( ob, _("Error: Cannot create PIPE."), "b" ); free_ob( ob, 1 ); } g_free( command ); } else { outputbox_message( ob, _("Tool canceled."), "b" ); free_ob( ob, 0 ); } } void outputbox( Tbfwin *bfwin, gchar *pattern, gint file_subpat, gint line_subpat, gint output_subpat, gchar *command, gboolean show_all_output ) { Toutputbox * ob; if ( bfwin->outputbox ) { ob = OUTPUTBOX( bfwin->outputbox ); gtk_widget_show_all( ob->hbox ); /* fix BUGS[200503]#24 */ setup_toggle_item_from_widget(bfwin->menubar, N_("/View/View Outputbox"), TRUE); /* fix BUGS[200503]#25 */ } else { ob = init_outputbox( bfwin ); } if ( ob->pid ) { /* stop older output box */ outputbox_message( ob, _("Tool is running. Try 'Stop' intead."), "i" ); return; /* gchar * tmpstr; tmpstr = g_strdup_printf(_("Multiple calls... stopping: %s"), ob->def->command ); outputbox_message( ob, tmpstr, "i" ); g_free( tmpstr ); finish_execute( ob ); */ } gtk_list_store_clear( GTK_LIST_STORE( ob->lstore ) ); ob->def = g_new0( Toutput_def, 1 ); ob->def->pattern = g_strdup( pattern ); ob->def->file_subpat = file_subpat; ob->def->line_subpat = line_subpat; ob->def->output_subpat = output_subpat; ob->def->show_all_output = show_all_output; regcomp( &ob->def->preg, ob->def->pattern, REG_EXTENDED ); ob->def->command = g_strdup( command ); /* kyanh */ ob->retfile = NULL; ob->io_channel = NULL; ob->pollID = 0; ob->pid = 0; run_command( ob ); /* gtk_widget_show_all(ob->hbox); */ }