execbox.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 2002 The Exult Team
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #  include <config.h>
00027 #endif
00028 
00029 #include "execbox.h"
00030 #include <iostream> /* Debugging only */
00031 #include <string>
00032 
00033 using std::cout;
00034 using std::endl;
00035 
00036 #ifndef WIN32
00037 
00038 #ifdef HAVE_SYS_TYPES_H
00039 #include <sys/types.h>
00040 #endif
00041 #include <unistd.h>
00042 #include <signal.h>
00043 #include <sys/wait.h>
00044 
00045 /*
00046  *  Create.
00047  */
00048 
00049 Exec_process::Exec_process
00050   (
00051   ) : child_stdin(-1), child_stdout(-1), child_stderr(-1),
00052       child_pid(-1), reader(0),
00053       stdout_tag(-1), stderr_tag(-1)
00054   {
00055   }
00056 
00057 /*
00058  *  Clean up.
00059  */
00060 
00061 Exec_process::~Exec_process
00062   (
00063   )
00064   {
00065   kill_child();
00066   }
00067 
00068 /*
00069  *  End process and close pipes.
00070  */
00071 
00072 void Exec_process::kill_child
00073   (
00074   )
00075   {
00076   if (child_pid > 0)
00077     kill(child_pid, SIGINT);
00078   if (child_stdin >= 0)
00079     close(child_stdin);
00080   if (child_stdout >= 0)
00081     close(child_stdout);
00082   if (child_stderr >= 0)
00083     close(child_stderr);
00084   if (stdout_tag >= 0)
00085     gdk_input_remove(stdout_tag);
00086   if (stderr_tag >= 0)
00087     gdk_input_remove(stderr_tag);
00088   child_pid = child_stdin = child_stdout = child_stderr = 
00089   stdout_tag = stderr_tag = -1;
00090   }
00091 
00092 /*
00093  *  Read from child & call client routine.
00094  */
00095 
00096 static void Read_from_child
00097   (
00098   gpointer data,      // ->Exex_process
00099   gint id,      // Pipe ID.
00100   GdkInputCondition condition
00101   )
00102   {
00103   Exec_process *ex = (Exec_process *) data;
00104   ex->read_from_child(id);
00105   }
00106 void Exec_process::read_from_child
00107   (
00108   int id        // Pipe to read from.
00109   )
00110   {
00111   char buf[1024];
00112   int len;
00113   while ((len = read(id, buf, sizeof(buf))) > 0)
00114     if (reader)
00115       (*reader)(buf, len, 0, reader_data);
00116   int exit_code;
00117   if (!check_child(exit_code))  // Child done?
00118     {
00119     kill_child();   // Clean up.
00120     if (reader)   // Tell client.
00121       (*reader)(0, 0, exit_code, reader_data);
00122     }
00123   }
00124 
00125 
00126 
00127 /*
00128  *  Close a pipe.
00129  */
00130 
00131 inline void Close_pipe
00132   (
00133   int p[2]
00134   )
00135   {
00136   if (p[0] >= 0) close(p[0]);
00137   if (p[1] >= 0) close(p[1]);
00138   }
00139 
00140 /*
00141  *  Close 3 sets of pipes.
00142  */
00143 
00144 static void Close_pipes
00145   (
00146   int p0[2], int p1[2], int p2[2]
00147   )
00148   {
00149   Close_pipe(p0);
00150   Close_pipe(p1);
00151   Close_pipe(p2);
00152   }
00153 
00154 /*
00155  *  Execute a new process.
00156  *
00157  *  Output: False if failed.
00158  */
00159 
00160 bool Exec_process::exec
00161   (
00162   const char *file,     // PATH will be searched.
00163   char *argv[],     // Args.  1st is filename, last is 0.
00164   Reader_fun rfun,    // Called when child writes, exits.
00165   void *udata     // User_data for rfun.
00166   )
00167   {
00168   reader = rfun;      // Store callback.
00169   reader_data = udata;
00170           // Pipes for talking to child:
00171   int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
00172   stdin_pipe[0] = stdin_pipe[1] = stdout_pipe[0] = stdout_pipe[1] = 
00173   stderr_pipe[0] = stderr_pipe[1] = -1;
00174   kill_child();     // Kill running process.
00175           // Create pipes.
00176   if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0 ||
00177       pipe(stderr_pipe) != 0)
00178     {     // Error.
00179     Close_pipes(stdin_pipe, stdout_pipe, stderr_pipe);
00180     return false;
00181     }
00182   child_pid = fork();   // Split in two.
00183   if (child_pid == -1)    // Failed?
00184     {
00185     Close_pipes(stdin_pipe, stdout_pipe, stderr_pipe);
00186     return false;
00187     }
00188   if (child_pid == 0)   // Are we the child?
00189     {
00190     close(0);   // Want to read from the pipe.
00191     dup(stdin_pipe[0]);
00192     Close_pipe(stdin_pipe); // Done with these.
00193     close(1);   // Write to stdout through pipe.
00194     dup(stdout_pipe[1]);
00195     Close_pipe(stdout_pipe);
00196     close(2);   // Write to stderr through pipe.
00197     dup(stderr_pipe[1]);
00198     Close_pipe(stderr_pipe);
00199     execvp(file, argv); // Become the new command.
00200     exit(-1);   // Gets here if exec failed.
00201     }
00202           // HERE, we're the parent.
00203   child_stdin = stdin_pipe[1];  // Writing here goes to child stdin.
00204   close(stdin_pipe[0]);
00205   child_stdout = stdout_pipe[0];  // This gets child's stdout.
00206   close(stdout_pipe[1]);
00207   child_stderr = stderr_pipe[0];
00208   close(stderr_pipe[1]);
00209 cout << "Child_stdout is " << child_stdout << ", Child_stderr is " <<
00210     child_stderr << endl;
00211   stdout_tag = gdk_input_add(child_stdout,
00212       GDK_INPUT_READ, Read_from_child, this);
00213   stderr_tag = gdk_input_add(child_stderr,
00214       GDK_INPUT_READ, Read_from_child, this);
00215   return true;
00216   }
00217 
00218 /*
00219  *  See if child is still alive.
00220  *
00221  *  Output: True if child is still running.
00222  *    If false, exit code is returned in 'exit_code'.
00223  */
00224 
00225 bool Exec_process::check_child
00226   (
00227   int& exit_code      // Exit code returned.
00228   )
00229   {
00230   if (child_pid < 0)
00231     return false;   // No child.
00232   int status;
00233           // Don't wait.
00234   int ret = waitpid(child_pid, &status, WNOHANG);
00235   if (ret != child_pid)
00236     return true;    // Still running.
00237   else
00238     {
00239     cout << "Exec_box:  Child done." << endl;
00240     exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
00241     return false;
00242     }
00243   }
00244 
00245 #endif
00246 
00247 /*
00248  *  Create.
00249  */
00250 
00251 Exec_box::Exec_box
00252   (
00253   GtkTextView *b,
00254   GtkStatusbar *s,
00255   Exec_done_fun dfun,   // Called when child exits.
00256   gpointer udata      // Passed to dfun.
00257   ) : box(b), status(s), done_fun(dfun), user_data(udata)
00258   {
00259   executor = new Exec_process;
00260   status_ctx = gtk_statusbar_get_context_id(status, "execstatus");
00261           // Keep one msg. always on stack.
00262   gtk_statusbar_push(status, status_ctx, "");
00263   }
00264 
00265 /*
00266  *  Close.
00267  */
00268 
00269 Exec_box::~Exec_box
00270   (
00271   )
00272   {
00273   delete executor;    // Kills child.
00274   }
00275 
00276 /*
00277  *  Show status.
00278  */
00279 
00280 void Exec_box::show_status
00281   (
00282   const char *msg
00283   )
00284   {
00285   gtk_statusbar_pop(status, status_ctx);
00286   gtk_statusbar_push(status, status_ctx, msg);
00287   }
00288 
00289 /*
00290  *  Read from child & display in the text box.
00291  */
00292 
00293 static void Exec_callback
00294   (
00295   char *data,     // Data read, or NULL.
00296   int datalen,      // Length, or 0 if child exited.
00297   int exit_code,      // Exit code if datalen = 0.
00298   void *user_data     // ->Exex_box
00299   )
00300   {
00301   Exec_box *box = (Exec_box *) user_data;
00302   box->read_from_child(data, datalen, exit_code);
00303   }
00304 void Exec_box::read_from_child
00305   (
00306   char *data,     // Data read, or NULL.
00307   int datalen,      // Length, or 0 if child exited.
00308   int exit_code     // Exit code if datalen = 0.
00309   )
00310   {
00311   if (datalen > 0)
00312     {
00313     GtkTextBuffer *buffer = gtk_text_view_get_buffer(box);
00314     gtk_text_buffer_insert_at_cursor(buffer,data,datalen);
00315     return;
00316     }
00317   if (exit_code == 0)   // Child is done, so check result.
00318     show_status("Done:  Success");
00319   else
00320     show_status("Done:  Errors occurred");
00321   if (done_fun)
00322     done_fun(exit_code, this, user_data);
00323   }
00324 
00325 /*
00326  *  Add a message to the box.
00327  */
00328 
00329 void Exec_box::add_message
00330   (
00331   const char *txt
00332   )
00333   {
00334   GtkTextBuffer *buffer = gtk_text_view_get_buffer(box);
00335   gtk_text_buffer_insert_at_cursor(buffer,txt,strlen(txt));
00336   }
00337 
00338 /*
00339  *  Execute a new process.
00340  *
00341  *  Output: False if failed.
00342  */
00343 
00344 bool Exec_box::exec
00345   (
00346   const char *file,     // PATH will be searched.
00347   char *argv[]      // Args.  1st is filename, last is 0.
00348   )
00349   {
00350   GtkTextBuffer *buffer = gtk_text_view_get_buffer(box);
00351   gtk_text_buffer_set_text(buffer, "", 0);  // Clear out old text
00352   if (!executor->exec(file, argv, Exec_callback, this))
00353     return false;
00354   return true;
00355   }
00356 
00357 /*
00358  *  End process.
00359  */
00360 
00361 void Exec_box::kill_child
00362   (
00363   )
00364   {
00365   executor->kill_child();
00366   }

Generated on Mon Jul 9 14:42:47 2007 for ExultEngine by  doxygen 1.5.1