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