g_exec functions



Hi,

One of the facilities Owen suggested merging to glib from libgnome a
while back was gnome_execute_async() etc. (see gnome-exec.[hc]).

If you look at gnome-exec.c, execing a process the Right Way is
actually quite difficult, and that code doesn't even include nice
error messages.

I'll append a note from Tom asking about gnome-exec.c, and an initial
thought on the API, to get discussion started.

Some of the issues (in addition to the one Tom mentions):
 - should error codes be simple equivalents of E* from the 
   UNIX API as I have them, or some kind of friendlier/simpler
   thing?
 - the idea is that error->message would be nice and user friendly.
   I believe glib doesn't use gettext right now, but it would have to
   in order to contain error messages.
 - I have no functions that use the shell, since I figure 
   people can just pass "/bin/sh" and "-c" as argv[0] and 
   argv[1], and anyway people should avoid the shell in 
   the vast majority of cases
 - also all my functions take argc/argv, and then 
   parse_argv is provided to parse a command line without
   using the shell if you like (as with poptParseArgvString)
 - I don't have versions that add to the environment before
   launching the child; are these really useful?
 - perhaps rather than supporting a million things to be done before
   launching the child (such as modifying the environment), we could
   just have a hook function that gets run in the child post-fork()
   and pre-exec()
 - I have two convenience functions; one sets up pipes to
   stdin/stdout/stderr for an async child, and one blocks
   waiting for child exit and returns stdout/stderr as strings.
   Both these will avoid creating pipes for args that are 
   NULL (i.e. pass NULL for gint *standard_out_pipe and the pipe 
   fd won't get created or returned).
 - These are UNIX-specific I think
 - maybe need pid_t in place of int, so that would be gpid or
   something I suppose 
 - the exit status will require interpretation with WIFEXITED() etc.
   which is somewhat odd for glib, so maybe we need glib wrappers for 
   the exit status stuff.

Havoc

From: Tom Tromey <tromey@cygnus.com>
Date: 19 Aug 2000 21:31:11 -0600

Havoc> I'm trying to put some sort of nice exec functions in glib,
Havoc> based on gnome_execute_*. Can you give me a short explanation
Havoc> of why you have the "child in the middle" and run the desired
Havoc> process as a grandchild?  I want to understand all the issues.

If you don't do that then you have to wait() at some random point in
the hosting program.  This is a complicated thing to try to do in a
library, since you have to decide whether/how you want to handle
SIGCHILD.  Handling this particular signal has some ugly associated
portability problems, as I recall there are actually two different
possible signals with slightly differing semantics depending on the
Unix variant.

Some libraries do handle this, like Tcl.  As I recall it works by
forcing you to use its model for child reaping.  It is probably more
efficient to avoid the double fork.

The double fork method lets you avoid this by immediately waiting for
the intermediate child.  The grandchild won't become a zombie because
its parent has already been reaped.

Tom

/* I'm not sure I remember our proposed naming convention here. */
#define G_EXEC_ERROR g_exec_error_quark ()

typedef enum
{
  G_EXEC_ERROR_PARSE,  /* failed to parse command line */
  G_EXEC_ERROR_FORK,   /* fork failed due to lack of memory */
  G_EXEC_ERROR_ACCES,  /* execve() returned EACCES */
  G_EXEC_ERROR_PERM,   /* execve() returned EPERM */
  G_EXEC_ERROR_2BIG,   /* execve() returned E2BIG */
  G_EXEC_ERROR_NOEXEC, /* execve() returned ENOEXEC */
  G_EXEC_ERROR_NAMETOOLONG, /* ""  "" ENAMETOOLONG */
  G_EXEC_ERROR_NOENT,       /* ""  "" ENOENT */
  G_EXEC_ERROR_NOMEM,       /* ""  "" ENOMEM */
  G_EXEC_ERROR_NOTDIR,      /* ""  "" ENOTDIR */
  G_EXEC_ERROR_LOOP,        /* ""  "" ELOOP   */
  G_EXEC_ERROR_TXTBUSY,     /* ""  "" ETXTBUSY */
  G_EXEC_ERROR_IO,          /* ""  "" EIO */
  G_EXEC_ERROR_NFILE,       /* ""  "" ENFILE */
  G_EXEC_ERROR_MFILE,       /* ""  "" EMFLE */
  G_EXEC_ERROR_INVAL,       /* ""  "" EINVAL */
  G_EXEC_ERROR_ISDIR,       /* ""  "" EISDIR */
  G_EXEC_ERROR_LIBBAD,      /* ""  "" ELIBBAD */
  G_EXEC_ERROR_FAILED       /* other fatal failure, error->message
                             * should explain
                             */
} GExecError;

GQuark g_exec_error_quark (void);

gboolean g_parse_argv (const gchar *command_line,
                       gint        *argc,
                       gchar     ***argv,
                       GError      *error);

gboolean g_exec_async (const gchar *working_directory,
                       gint         argc,
                       gchar      **argv,
                       gboolean     close_descriptors,
                       gint        *child_pid,
                       GError      *error);

/* Opens pipes for non-NULL standard_output, standard_input, standard_error,
 * and returns the parent's end of the pipes. If pipes are NULL then
 * the child gets attached to /dev/null
 */
gboolean g_exec_async_with_pipes (const gchar *working_directory,
                                  gint         argc,
                                  gchar      **argv,
                                  gboolean     close_descriptors,
                                  gint        *child_pid,
                                  gint        *standard_output,
                                  gint        *standard_input,
                                  gint        *standard_error,
                                  GError      *error);

/* If standard_output or standard_error are non-NULL, the full
 * standard output or error of the command will be placed there.
 * otherwise child output goes to /dev/null
 */
gboolean g_exec_sync             (const gchar *working_directory,
                                  gint         argc,
                                  gchar      **argv,
                                  gboolean     close_descriptors,
                                  gint        *child_pid,
                                  gchar      **standard_output,
                                  gchar      **standard_error,
                                  gint        *exit_status,
                                  GError      *error);
                                 







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