r15660 - gnucash/trunk/src - Add wrappable functions to spawn, chat with and kill processes.

Andreas Köhler andi5 at cvs.gnucash.org
Sun Feb 25 12:35:59 EST 2007


Author: andi5
Date: 2007-02-25 12:35:57 -0500 (Sun, 25 Feb 2007)
New Revision: 15660
Trac: http://svn.gnucash.org/trac/changeset/15660

Modified:
   gnucash/trunk/src/app-utils/app-utils.i
   gnucash/trunk/src/app-utils/guile-util.c
   gnucash/trunk/src/app-utils/guile-util.h
   gnucash/trunk/src/core-utils/gnc-glib-utils.c
   gnucash/trunk/src/core-utils/gnc-glib-utils.h
Log:
Add wrappable functions to spawn, chat with and kill processes.

* gnc_spawn_process_async, gnc_process_get_fd and gnc_detach_process in
  app-utils/guile-util
* gnc_gpid_kill in core-utils/gnc-glib-utils. Works on Windows too.
  Defined there to avoid clashes with GUID definitions from winbase.h.


Modified: gnucash/trunk/src/app-utils/app-utils.i
===================================================================
--- gnucash/trunk/src/app-utils/app-utils.i	2007-02-25 16:42:38 UTC (rev 15659)
+++ gnucash/trunk/src/app-utils/app-utils.i	2007-02-25 17:35:57 UTC (rev 15660)
@@ -11,6 +11,7 @@
 #include <gnc-accounting-period.h>
 #include <gnc-session.h>
 #include <gnc-component-manager.h>
+#include <guile-util.h>
 
 #include "engine-helpers.h"
 
@@ -83,3 +84,23 @@
 SCM gnc_make_kvp_options(QofIdType id_type);
 void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator);
 
+%typemap(in) GList * {
+  SCM path_scm = $input;
+  GList *path = NULL;
+  while (!SCM_NULLP (path_scm))
+  {
+    SCM key_scm = SCM_CAR (path_scm);
+    char *key;
+    if (!SCM_STRINGP (key_scm))
+      break;
+    key = g_strdup (SCM_STRING_CHARS (key_scm));
+    path = g_list_prepend (path, key);
+    path_scm = SCM_CDR (path_scm);
+  }
+  $1 = g_list_reverse (path);
+}
+Process *gnc_spawn_process_async(GList *argl, const gboolean search_path);
+%clear GList *;
+
+gint gnc_process_get_fd(const Process *proc, const guint std_fd);
+void gnc_detach_process(Process *proc, const gboolean kill_it);

Modified: gnucash/trunk/src/app-utils/guile-util.c
===================================================================
--- gnucash/trunk/src/app-utils/guile-util.c	2007-02-25 16:42:38 UTC (rev 15659)
+++ gnucash/trunk/src/app-utils/guile-util.c	2007-02-25 17:35:57 UTC (rev 15660)
@@ -25,11 +25,16 @@
 #include <string.h>
 #include "swig-runtime.h"
 #include <libguile.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include "qof.h"
 #include "engine-helpers.h"
 #include "glib-helpers.h"
 #include "gnc-gconf-utils.h"
+#include "gnc-glib-utils.h"
 #include "guile-util.h"
 #include "guile-mappings.h"
 
@@ -74,7 +79,17 @@
   SCM is_trans_scm;
 } predicates;
 
+struct _Process
+{
+  GPid pid;
+  gint fd_stdin;
+  gint fd_stdout;
+  gint fd_stderr;
+  gboolean dead;
+  gboolean detached;
+};
 
+
 static void
 initialize_scm_functions()
 {
@@ -1158,3 +1173,117 @@
   g_strfreev(splits);
   return text;
 }
+
+
+static void
+on_child_exit (GPid pid, gint status, gpointer data)
+{
+  Process *proc = data;
+  g_return_if_fail (proc && proc->pid == pid);
+
+  g_spawn_close_pid (proc->pid);
+
+  /* free if the process is both dead and detached */
+  if (!proc->detached)
+    proc->dead = TRUE;
+  else
+    g_free (proc);
+}
+
+Process *
+gnc_spawn_process_async (GList *argl, const gboolean search_path)
+{
+  gboolean retval;
+  Process *proc;
+  GList *l_iter;
+  guint argc;
+  gchar **argv, **v_iter;
+  GSpawnFlags flags;
+  GError *error = NULL;
+
+  proc = g_new0 (Process, 1);
+
+  argc = g_list_length (argl);
+  argv = g_malloc ((argc+1) * sizeof(gchar*));
+
+  for (l_iter=argl, v_iter=argv; l_iter; l_iter=l_iter->next, v_iter++) {
+    *v_iter = (gchar*) l_iter->data;
+  }
+  *v_iter = NULL;
+  g_list_free (argl);
+
+  flags = G_SPAWN_DO_NOT_REAP_CHILD;
+  if (search_path)
+    flags |= G_SPAWN_SEARCH_PATH;
+
+  retval = g_spawn_async_with_pipes (
+    NULL, argv, NULL, flags, NULL, NULL, &proc->pid,
+    &proc->fd_stdin, &proc->fd_stdout, &proc->fd_stderr, &error);
+
+  if (!retval) {
+    g_warning ("Could not spawn %s: %s", *argv ? *argv : "(null)",
+               error->message ? error->message : "(null)");
+    g_free (proc);
+    proc = NULL;
+  }
+  g_strfreev (argv);
+
+  g_child_watch_add (proc->pid, on_child_exit, proc);
+
+  return proc;
+}
+
+gint
+gnc_process_get_fd (const Process *proc, const gint std_fd)
+{
+  const gint *retptr = NULL;
+  g_return_val_if_fail (proc, -1);
+
+  if (std_fd == 0)
+    retptr = &proc->fd_stdin;
+  else if (std_fd == 1)
+    retptr = &proc->fd_stdout;
+  else if (std_fd == 2)
+    retptr = &proc->fd_stderr;
+  else
+    g_return_val_if_reached (-1);
+
+  if (*retptr == -1)
+    g_warning ("Pipe to childs file descriptor %d is -1", std_fd);
+  return *retptr;
+}
+
+void
+gnc_detach_process (Process *proc, const gboolean kill_it)
+{
+  g_return_if_fail (proc && proc->pid);
+
+  errno = 0;
+  close (proc->fd_stdin);
+  if (errno) {
+    g_warning ("Close of childs stdin (%d) failed: %s", proc->fd_stdin,
+               g_strerror (errno));
+    errno = 0;
+  }
+  close (proc->fd_stdout);
+  if (errno) {
+    g_warning ("Close of childs stdout (%d) failed: %s", proc->fd_stdout,
+               g_strerror(errno));
+    errno = 0;
+  }
+  close (proc->fd_stderr);
+  if (errno) {
+    g_warning ("Close of childs stderr (%d) failed: %s", proc->fd_stderr,
+               g_strerror(errno));
+    errno = 0;
+  }
+
+  if (kill_it)
+    gnc_gpid_kill (proc->pid);
+
+  /* free if the process is both dead and detached */
+  if (!proc->dead)
+    proc->detached = TRUE;
+  else
+    g_free (proc);
+}

Modified: gnucash/trunk/src/app-utils/guile-util.h
===================================================================
--- gnucash/trunk/src/app-utils/guile-util.h	2007-02-25 16:42:38 UTC (rev 15659)
+++ gnucash/trunk/src/app-utils/guile-util.h	2007-02-25 17:35:57 UTC (rev 15660)
@@ -103,4 +103,40 @@
  *  comments. */
 gchar *gnc_guile_strip_comments (const gchar *text);
 
+/** An opaque process structure returned by gnc_spawn_process_async. */
+typedef struct _Process Process;
+
+/** Wraps g_spawn_async_with_pipes minimally.  Use gnc_process_get_fd to access
+ *  the file descriptors to the child.  To close them them and free the memory
+ *  allocated for the process once it has exited, call gnc_detach_process.
+ *
+ *  @param argl A list of null-terminated strings used as arguments for spawning,
+ *  i.e. "perl" "-w" "my-perl-script".  Will be freed inside.
+ *
+ *  @param search_path Determines whether the first element of argl will be
+ *  looked for in the user's PATH.
+ *
+ *  @return A pointer to a structure representing the process or NULL on failure.
+ */
+Process *gnc_spawn_process_async(GList *argl, const gboolean search_path);
+
+/** Accesses a given process structure and returns the file descriptor connected
+ *  to the childs stdin, stdout or stderr.
+ *
+ *  @param proc A process structure returned by gnc_spawn_process_async.
+ *
+ *  @param std_fd 0, 1 or 2.
+ *
+ *  @return The file descriptor to write to the child on 0, or read from the
+ *  childs output or error on 1 or 2, resp. */
+gint gnc_process_get_fd(const Process *proc, const gint std_fd);
+
+/** Close the file descriptors to a given process and declare it as detached.  If
+ *  it is both dead and detached, the allocated memory will be freed.
+ *
+ *  @param proc A process structure returned by gnc_spawn_process_async.
+ *
+ *  @param kill_it If TRUE, kill the process. */
+void gnc_detach_process(Process *proc, const gboolean kill_it);
+
 #endif

Modified: gnucash/trunk/src/core-utils/gnc-glib-utils.c
===================================================================
--- gnucash/trunk/src/core-utils/gnc-glib-utils.c	2007-02-25 16:42:38 UTC (rev 15659)
+++ gnucash/trunk/src/core-utils/gnc-glib-utils.c	2007-02-25 17:35:57 UTC (rev 15660)
@@ -22,11 +22,17 @@
 \********************************************************************/
 
 #include "config.h"
+#include <errno.h>
 #include <stdio.h>
+#include <signal.h>
 #include <string.h>
 
 #include "gnc-glib-utils.h"
 
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
 int
 safe_utf8_collate (const char * da, const char * db)
 {
@@ -274,3 +280,17 @@
 {
     g_log("gnc.scm", G_LOG_LEVEL_DEBUG, msg);
 }
+
+void gnc_gpid_kill(GPid pid)
+{
+#ifdef G_OS_WIN32
+    if (!TerminateProcess((HANDLE) pid, 0)) {
+        gchar *msg = g_win32_error_message(GetLastError());
+        g_warning("Could not kill child process: %s", msg ? msg : "(null)");
+    }
+#else /* !G_OS_WIN32 */
+    if (kill(pid, SIGKILL)) {
+        g_warning("Could not kill child process: %s", g_strerror(errno));
+    }
+#endif /* G_OS_WIN32 */
+}

Modified: gnucash/trunk/src/core-utils/gnc-glib-utils.h
===================================================================
--- gnucash/trunk/src/core-utils/gnc-glib-utils.h	2007-02-25 16:42:38 UTC (rev 15659)
+++ gnucash/trunk/src/core-utils/gnc-glib-utils.h	2007-02-25 17:35:57 UTC (rev 15660)
@@ -100,6 +100,11 @@
 void gnc_scm_log_msg(const gchar *msg);
 void gnc_scm_log_debug(const gchar *msg);
 
+/** Kill a process.  On UNIX send a SIGKILL, on Windows call TerminateProcess.
+ *
+ *  @param pid The process ID. */
+void gnc_gpid_kill(GPid pid);
+
 /** @} */
 
 #endif /* GNC_GLIB_UTILS_H */



More information about the gnucash-changes mailing list