2006-05-21 18:11:05 -05:00
|
|
|
/*
|
2006-08-20 03:49:03 -05:00
|
|
|
* moospawn.c
|
2005-11-28 02:12:50 +00:00
|
|
|
*
|
2010-12-21 20:15:45 -08:00
|
|
|
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
|
2005-11-28 02:12:50 +00:00
|
|
|
*
|
2008-09-05 17:20:50 -05:00
|
|
|
* This file is part of medit. medit is free software; you can
|
|
|
|
* redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2.1 of the License,
|
|
|
|
* or (at your option) any later version.
|
2005-11-28 02:12:50 +00:00
|
|
|
*
|
2008-09-05 17:20:50 -05:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
|
2005-11-28 02:12:50 +00:00
|
|
|
*/
|
|
|
|
|
2006-08-20 03:49:03 -05:00
|
|
|
#include "mooutils/moospawn.h"
|
2008-02-18 19:42:41 -06:00
|
|
|
#include "marshals.h"
|
2006-06-10 03:33:42 -05:00
|
|
|
#include "mooutils/mooutils-misc.h"
|
2016-01-10 01:12:50 -08:00
|
|
|
#include "moocpp/gobjectutils.h"
|
2006-08-20 03:49:03 -05:00
|
|
|
#include <string.h>
|
2005-12-24 19:36:47 +00:00
|
|
|
|
|
|
|
#ifndef __WIN32__
|
2005-11-28 02:12:50 +00:00
|
|
|
#include <sys/wait.h>
|
2007-02-27 22:47:42 -06:00
|
|
|
#include <sys/types.h>
|
2005-11-28 02:12:50 +00:00
|
|
|
#include <unistd.h>
|
2006-02-13 20:37:25 +00:00
|
|
|
#include <signal.h>
|
2005-12-24 19:36:47 +00:00
|
|
|
#else
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2016-01-10 01:12:50 -08:00
|
|
|
using namespace moo;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
typedef struct {
|
|
|
|
MooCmd *cmd;
|
|
|
|
} ChildWatchData;
|
|
|
|
|
2005-11-28 02:12:50 +00:00
|
|
|
struct _MooCmdPrivate {
|
|
|
|
GSpawnFlags flags;
|
|
|
|
MooCmdFlags cmd_flags;
|
|
|
|
gboolean running;
|
|
|
|
int exit_status;
|
|
|
|
GPid pid;
|
2015-12-25 22:23:10 -08:00
|
|
|
MgwFd out;
|
|
|
|
MgwFd err;
|
2005-11-28 02:12:50 +00:00
|
|
|
GIOChannel *stdout_io;
|
|
|
|
GIOChannel *stderr_io;
|
2008-01-09 12:05:28 -06:00
|
|
|
ChildWatchData *child_watch_data;
|
2005-11-28 02:12:50 +00:00
|
|
|
guint stdout_watch;
|
|
|
|
guint stderr_watch;
|
2006-08-20 03:49:03 -05:00
|
|
|
GString *out_buffer;
|
|
|
|
GString *err_buffer;
|
2005-11-28 02:12:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void moo_cmd_finalize (GObject *object);
|
|
|
|
static void moo_cmd_dispose (GObject *object);
|
|
|
|
|
|
|
|
static void moo_cmd_check_stop (MooCmd *cmd);
|
|
|
|
static void moo_cmd_cleanup (MooCmd *cmd);
|
|
|
|
|
|
|
|
static gboolean moo_cmd_abort_real (MooCmd *cmd);
|
2006-09-02 02:33:09 -05:00
|
|
|
static gboolean moo_cmd_stdout_line (MooCmd *cmd,
|
|
|
|
const char *line);
|
|
|
|
static gboolean moo_cmd_stderr_line (MooCmd *cmd,
|
|
|
|
const char *line);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
static gboolean moo_cmd_run_command (MooCmd *cmd,
|
|
|
|
const char *working_dir,
|
|
|
|
char **argv,
|
|
|
|
char **envp,
|
|
|
|
GSpawnFlags flags,
|
|
|
|
MooCmdFlags cmd_flags,
|
|
|
|
GSpawnChildSetupFunc child_setup,
|
|
|
|
gpointer user_data,
|
|
|
|
GError **error);
|
|
|
|
|
2006-05-23 12:27:28 -05:00
|
|
|
#if 0 && defined(__WIN32__)
|
|
|
|
static gboolean moo_win32_spawn_async_with_pipes (const gchar *working_directory,
|
|
|
|
gchar **argv,
|
|
|
|
gchar **envp,
|
|
|
|
GSpawnFlags flags,
|
|
|
|
GPid *child_pid,
|
|
|
|
gint *standard_input,
|
|
|
|
gint *standard_output,
|
|
|
|
gint *standard_error,
|
|
|
|
GError **error);
|
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ABORT,
|
|
|
|
CMD_EXIT,
|
2006-09-02 02:33:09 -05:00
|
|
|
STDOUT_LINE,
|
|
|
|
STDERR_LINE,
|
2005-11-28 02:12:50 +00:00
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
|
|
|
|
|
|
|
|
/* MOO_TYPE_CMD */
|
2006-08-20 03:49:03 -05:00
|
|
|
G_DEFINE_TYPE (MooCmd, _moo_cmd, G_TYPE_OBJECT)
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-08-20 03:49:03 -05:00
|
|
|
_moo_cmd_class_init (MooCmdClass *klass)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->finalize = moo_cmd_finalize;
|
|
|
|
gobject_class->dispose = moo_cmd_dispose;
|
|
|
|
|
|
|
|
klass->abort = moo_cmd_abort_real;
|
|
|
|
klass->cmd_exit = NULL;
|
2006-09-02 02:33:09 -05:00
|
|
|
klass->stdout_line = moo_cmd_stdout_line;
|
|
|
|
klass->stderr_line = moo_cmd_stderr_line;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-08-20 03:49:03 -05:00
|
|
|
g_type_class_add_private (klass, sizeof (MooCmdPrivate));
|
|
|
|
|
2005-11-28 02:12:50 +00:00
|
|
|
signals[ABORT] =
|
|
|
|
g_signal_new ("abort",
|
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
G_STRUCT_OFFSET (MooCmdClass, abort),
|
|
|
|
g_signal_accumulator_true_handled, NULL,
|
|
|
|
_moo_marshal_BOOL__VOID,
|
|
|
|
G_TYPE_BOOLEAN, 0);
|
|
|
|
|
|
|
|
signals[CMD_EXIT] =
|
|
|
|
g_signal_new ("cmd-exit",
|
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (MooCmdClass, cmd_exit),
|
|
|
|
g_signal_accumulator_true_handled, NULL,
|
|
|
|
_moo_marshal_BOOL__INT,
|
|
|
|
G_TYPE_BOOLEAN, 1,
|
|
|
|
G_TYPE_INT);
|
|
|
|
|
2006-09-02 02:33:09 -05:00
|
|
|
signals[STDOUT_LINE] =
|
|
|
|
g_signal_new ("stdout-line",
|
2005-11-28 02:12:50 +00:00
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
2006-09-02 02:33:09 -05:00
|
|
|
G_STRUCT_OFFSET (MooCmdClass, stdout_line),
|
2005-11-28 02:12:50 +00:00
|
|
|
g_signal_accumulator_true_handled, NULL,
|
|
|
|
_moo_marshal_BOOL__STRING,
|
|
|
|
G_TYPE_BOOLEAN, 1,
|
|
|
|
G_TYPE_STRING);
|
|
|
|
|
2006-09-02 02:33:09 -05:00
|
|
|
signals[STDERR_LINE] =
|
|
|
|
g_signal_new ("stderr-line",
|
2005-11-28 02:12:50 +00:00
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
2006-09-02 02:33:09 -05:00
|
|
|
G_STRUCT_OFFSET (MooCmdClass, stderr_line),
|
2005-11-28 02:12:50 +00:00
|
|
|
g_signal_accumulator_true_handled, NULL,
|
|
|
|
_moo_marshal_BOOL__STRING,
|
|
|
|
G_TYPE_BOOLEAN, 1,
|
|
|
|
G_TYPE_STRING);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-08-20 03:49:03 -05:00
|
|
|
_moo_cmd_init (MooCmd *cmd)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2006-08-20 03:49:03 -05:00
|
|
|
cmd->priv = G_TYPE_INSTANCE_GET_PRIVATE (cmd, MOO_TYPE_CMD, MooCmdPrivate);
|
|
|
|
cmd->priv->out_buffer = g_string_new (NULL);
|
|
|
|
cmd->priv->err_buffer = g_string_new (NULL);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
moo_cmd_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
MooCmd *cmd = MOO_CMD (object);
|
|
|
|
|
2006-08-20 03:49:03 -05:00
|
|
|
g_string_free (cmd->priv->out_buffer, TRUE);
|
|
|
|
g_string_free (cmd->priv->err_buffer, TRUE);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-08-20 03:49:03 -05:00
|
|
|
G_OBJECT_CLASS (_moo_cmd_parent_class)->finalize (object);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
moo_cmd_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
MooCmd *cmd = MOO_CMD (object);
|
|
|
|
|
|
|
|
if (cmd->priv->pid)
|
2006-08-20 03:49:03 -05:00
|
|
|
_moo_cmd_abort (cmd);
|
2005-11-28 02:12:50 +00:00
|
|
|
moo_cmd_cleanup (cmd);
|
|
|
|
|
2006-08-20 03:49:03 -05:00
|
|
|
G_OBJECT_CLASS (_moo_cmd_parent_class)->dispose (object);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MooCmd*
|
2006-08-20 03:49:03 -05:00
|
|
|
_moo_cmd_new (const char *working_dir,
|
|
|
|
char **argv,
|
|
|
|
char **envp,
|
|
|
|
GSpawnFlags flags,
|
|
|
|
MooCmdFlags cmd_flags,
|
|
|
|
GSpawnChildSetupFunc child_setup,
|
|
|
|
gpointer user_data,
|
|
|
|
GError **error)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
MooCmd *cmd;
|
|
|
|
gboolean result;
|
|
|
|
|
|
|
|
g_return_val_if_fail (argv && *argv, NULL);
|
|
|
|
|
2016-01-10 01:12:50 -08:00
|
|
|
cmd = new_object<MooCmd>();
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
result = moo_cmd_run_command (cmd, working_dir, argv, envp,
|
|
|
|
flags, cmd_flags, child_setup,
|
|
|
|
user_data, error);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
{
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_object_unref (cmd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2008-01-09 12:05:28 -06:00
|
|
|
command_exit (GPid pid,
|
|
|
|
gint status,
|
|
|
|
ChildWatchData *data)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2008-01-09 12:05:28 -06:00
|
|
|
MooCmd *cmd;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
cmd = data->cmd;
|
|
|
|
data->cmd = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
g_spawn_close_pid (pid);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
if (cmd)
|
|
|
|
{
|
|
|
|
g_return_if_fail (cmd->priv->child_watch_data == data);
|
|
|
|
cmd->priv->exit_status = status;
|
|
|
|
cmd->priv->child_watch_data = NULL;
|
2008-01-16 17:41:57 -06:00
|
|
|
cmd->priv->pid = 0;
|
2008-01-09 12:05:28 -06:00
|
|
|
moo_cmd_check_stop (cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
child_watch_removed (ChildWatchData *data)
|
|
|
|
{
|
|
|
|
if (data->cmd)
|
|
|
|
{
|
|
|
|
g_critical ("%s: oops", G_STRFUNC);
|
|
|
|
data->cmd->priv->child_watch_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (data);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2006-09-02 02:33:09 -05:00
|
|
|
process_line (MooCmd *cmd,
|
2008-02-03 02:02:26 -06:00
|
|
|
gboolean err,
|
2006-09-02 02:33:09 -05:00
|
|
|
const char *line,
|
2008-02-03 02:02:26 -06:00
|
|
|
gssize len)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
gboolean dummy = FALSE;
|
2008-02-03 02:02:26 -06:00
|
|
|
char *real_line = NULL;
|
2008-01-17 19:27:55 -06:00
|
|
|
const char *end;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-02-03 02:02:26 -06:00
|
|
|
g_return_if_fail (line != NULL);
|
|
|
|
|
|
|
|
if (len < 0)
|
|
|
|
len = strlen (line);
|
|
|
|
|
2008-01-17 19:27:55 -06:00
|
|
|
if ((cmd->priv->cmd_flags & MOO_CMD_UTF8_OUTPUT) &&
|
2008-02-03 02:02:26 -06:00
|
|
|
!g_utf8_validate (line, len, &end))
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
const char *charset;
|
|
|
|
|
|
|
|
if (g_get_charset (&charset))
|
|
|
|
{
|
2011-01-14 02:37:04 -08:00
|
|
|
g_warning ("invalid unicode:\n%s", line);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-01-17 19:27:55 -06:00
|
|
|
if (end > line)
|
2008-02-03 02:02:26 -06:00
|
|
|
real_line = g_strndup (line, end - line);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GError *error = NULL;
|
2008-04-27 22:54:54 -05:00
|
|
|
gsize bytes_written;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-02-03 02:02:26 -06:00
|
|
|
real_line = g_convert_with_fallback (line, len, "UTF-8", charset,
|
|
|
|
NULL, NULL, &bytes_written,
|
|
|
|
&error);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-02-03 02:02:26 -06:00
|
|
|
if (!real_line)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2011-01-14 02:37:04 -08:00
|
|
|
g_warning ("could not convert text to UTF-8:\n%.*s",
|
|
|
|
(int) len, line);
|
|
|
|
g_warning ("%s", moo_error_message (error));
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-03 02:02:26 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
real_line = g_strndup (line, len);
|
|
|
|
}
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-09-02 02:33:09 -05:00
|
|
|
if (real_line)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2006-06-11 06:02:54 -05:00
|
|
|
if (err)
|
2006-09-02 02:33:09 -05:00
|
|
|
g_signal_emit (cmd, signals[STDERR_LINE], 0, real_line, &dummy);
|
2005-11-28 02:12:50 +00:00
|
|
|
else
|
2006-09-02 02:33:09 -05:00
|
|
|
g_signal_emit (cmd, signals[STDOUT_LINE], 0, real_line, &dummy);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
2008-02-03 02:02:26 -06:00
|
|
|
g_free (real_line);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
command_out_or_err (MooCmd *cmd,
|
|
|
|
GIOChannel *channel,
|
|
|
|
GIOCondition condition,
|
2006-06-11 06:02:54 -05:00
|
|
|
gboolean out)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2007-01-15 05:04:39 -06:00
|
|
|
GSList *lines;
|
2005-11-28 02:12:50 +00:00
|
|
|
GError *error = NULL;
|
2009-11-14 11:57:35 -08:00
|
|
|
GIOStatus status = G_IO_STATUS_NORMAL;
|
2007-01-15 05:04:39 -06:00
|
|
|
gsize count;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2007-01-15 05:04:39 -06:00
|
|
|
count = 0;
|
|
|
|
lines = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2007-01-15 05:04:39 -06:00
|
|
|
while (count < 4096)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2007-01-15 05:04:39 -06:00
|
|
|
char *line = NULL;
|
|
|
|
gsize line_end;
|
|
|
|
|
|
|
|
status = g_io_channel_read_line (channel, &line, NULL, &line_end, &error);
|
|
|
|
|
|
|
|
if (!line)
|
|
|
|
break;
|
|
|
|
|
|
|
|
count += line_end;
|
2005-11-28 02:12:50 +00:00
|
|
|
line[line_end] = 0;
|
2007-01-15 05:04:39 -06:00
|
|
|
lines = g_slist_prepend (lines, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
lines = g_slist_reverse (lines);
|
|
|
|
|
|
|
|
while (lines)
|
|
|
|
{
|
2016-01-10 01:12:50 -08:00
|
|
|
process_line (cmd, !out, reinterpret_cast<char*> (lines->data), -1);
|
2007-01-15 05:04:39 -06:00
|
|
|
g_free (lines->data);
|
|
|
|
lines = g_slist_delete_link (lines, lines);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
2011-01-14 02:37:04 -08:00
|
|
|
g_warning ("%s", moo_error_message (error));
|
2005-11-28 02:12:50 +00:00
|
|
|
g_error_free (error);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (condition & (G_IO_ERR | G_IO_HUP))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (status == G_IO_STATUS_EOF)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
command_out (GIOChannel *channel,
|
|
|
|
GIOCondition condition,
|
|
|
|
MooCmd *cmd)
|
|
|
|
{
|
|
|
|
return command_out_or_err (cmd, channel, condition, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
command_err (GIOChannel *channel,
|
|
|
|
GIOCondition condition,
|
|
|
|
MooCmd *cmd)
|
|
|
|
{
|
|
|
|
return command_out_or_err (cmd, channel, condition, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
try_channel_leftover (MooCmd *cmd,
|
|
|
|
GIOChannel *channel,
|
2006-06-11 06:02:54 -05:00
|
|
|
gboolean out)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
char *text;
|
|
|
|
|
|
|
|
g_io_channel_read_to_end (channel, &text, NULL, NULL);
|
|
|
|
|
|
|
|
if (text)
|
|
|
|
{
|
2008-02-03 02:02:26 -06:00
|
|
|
MooLineReader lr;
|
|
|
|
const char *line;
|
|
|
|
gsize line_len;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2008-02-03 02:02:26 -06:00
|
|
|
for (moo_line_reader_init (&lr, text, -1);
|
|
|
|
(line = moo_line_reader_get_line (&lr, &line_len, NULL)); )
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2008-02-03 02:02:26 -06:00
|
|
|
process_line (cmd, !out, line, line_len);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_free (text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
stdout_watch_removed (MooCmd *cmd)
|
|
|
|
{
|
|
|
|
if (cmd->priv->stdout_io)
|
|
|
|
{
|
|
|
|
try_channel_leftover (cmd, cmd->priv->stdout_io, TRUE);
|
|
|
|
g_io_channel_unref (cmd->priv->stdout_io);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->priv->stdout_io = NULL;
|
|
|
|
cmd->priv->stdout_watch = 0;
|
|
|
|
moo_cmd_check_stop (cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
stderr_watch_removed (MooCmd *cmd)
|
|
|
|
{
|
|
|
|
if (cmd->priv->stderr_io)
|
|
|
|
{
|
2006-09-02 02:33:09 -05:00
|
|
|
try_channel_leftover (cmd, cmd->priv->stderr_io, FALSE);
|
2005-11-28 02:12:50 +00:00
|
|
|
g_io_channel_unref (cmd->priv->stderr_io);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->priv->stderr_io = NULL;
|
|
|
|
cmd->priv->stderr_watch = 0;
|
|
|
|
moo_cmd_check_stop (cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-24 19:36:47 +00:00
|
|
|
#ifndef __WIN32__
|
2005-11-28 02:12:50 +00:00
|
|
|
static void
|
|
|
|
real_child_setup (gpointer user_data)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
GSpawnChildSetupFunc child_setup;
|
|
|
|
gpointer user_data;
|
|
|
|
} *data = user_data;
|
|
|
|
|
2006-02-13 20:37:25 +00:00
|
|
|
setpgid (0, 0);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
if (data->child_setup)
|
|
|
|
data->child_setup (data->user_data);
|
|
|
|
}
|
2005-12-24 19:36:47 +00:00
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
|
2006-08-31 01:26:26 -05:00
|
|
|
char **
|
|
|
|
_moo_env_add (char **add)
|
2006-08-22 23:24:36 -05:00
|
|
|
{
|
|
|
|
GPtrArray *new_env;
|
|
|
|
char **names, **p;
|
|
|
|
|
|
|
|
if (!add)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new_env = g_ptr_array_new ();
|
|
|
|
names = g_listenv ();
|
|
|
|
|
|
|
|
for (p = names; p && *p; ++p)
|
|
|
|
{
|
|
|
|
const char *val = g_getenv (*p);
|
|
|
|
|
|
|
|
if (val)
|
|
|
|
g_ptr_array_add (new_env, g_strdup_printf ("%s=%s", *p, val));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (p = add; p && *p; ++p)
|
|
|
|
g_ptr_array_add (new_env, g_strdup (*p));
|
|
|
|
|
|
|
|
g_strfreev (names);
|
|
|
|
|
|
|
|
g_ptr_array_add (new_env, NULL);
|
|
|
|
return (char**) g_ptr_array_free (new_env, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-28 02:12:50 +00:00
|
|
|
static gboolean
|
|
|
|
moo_cmd_run_command (MooCmd *cmd,
|
|
|
|
const char *working_dir,
|
|
|
|
char **argv,
|
|
|
|
char **envp,
|
|
|
|
GSpawnFlags flags,
|
|
|
|
MooCmdFlags cmd_flags,
|
2006-02-24 19:52:41 -06:00
|
|
|
G_GNUC_UNUSED GSpawnChildSetupFunc child_setup,
|
|
|
|
G_GNUC_UNUSED gpointer user_data,
|
2005-11-28 02:12:50 +00:00
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean result;
|
2015-12-25 22:23:10 -08:00
|
|
|
MgwFd *outp, *errp;
|
2006-08-22 23:24:36 -05:00
|
|
|
char **new_env;
|
2008-01-09 12:05:28 -06:00
|
|
|
ChildWatchData *child_watch_data;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-02-24 19:52:41 -06:00
|
|
|
#ifndef __WIN32__
|
2005-11-28 02:12:50 +00:00
|
|
|
struct {
|
|
|
|
GSpawnChildSetupFunc child_setup;
|
|
|
|
gpointer user_data;
|
2006-10-18 00:24:35 -05:00
|
|
|
} data;
|
2006-02-24 19:52:41 -06:00
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
g_return_val_if_fail (MOO_IS_CMD (cmd), FALSE);
|
|
|
|
g_return_val_if_fail (argv && argv[0], FALSE);
|
|
|
|
g_return_val_if_fail (!cmd->priv->running, FALSE);
|
|
|
|
|
2006-05-23 12:27:28 -05:00
|
|
|
#ifdef __WIN32__
|
|
|
|
if (cmd_flags & MOO_CMD_OPEN_CONSOLE)
|
|
|
|
{
|
2005-11-28 02:12:50 +00:00
|
|
|
outp = NULL;
|
|
|
|
errp = NULL;
|
2006-05-23 12:27:28 -05:00
|
|
|
}
|
2005-11-28 02:12:50 +00:00
|
|
|
else
|
2006-05-23 12:27:28 -05:00
|
|
|
#endif
|
|
|
|
{
|
2010-11-11 22:19:50 -08:00
|
|
|
flags |= MOO_SPAWN_WIN32_HIDDEN_CONSOLE;
|
|
|
|
|
2006-05-23 12:27:28 -05:00
|
|
|
if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) || (cmd_flags & MOO_CMD_STDOUT_TO_PARENT))
|
|
|
|
outp = NULL;
|
|
|
|
else
|
2006-06-11 06:02:54 -05:00
|
|
|
outp = &cmd->priv->out;
|
2006-05-23 12:27:28 -05:00
|
|
|
|
|
|
|
if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) || (cmd_flags & MOO_CMD_STDERR_TO_PARENT))
|
|
|
|
errp = NULL;
|
|
|
|
else
|
2006-06-11 06:02:54 -05:00
|
|
|
errp = &cmd->priv->err;
|
2006-05-23 12:27:28 -05:00
|
|
|
}
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-08-31 01:26:26 -05:00
|
|
|
new_env = _moo_env_add (envp);
|
2006-08-22 23:24:36 -05:00
|
|
|
|
2006-10-18 00:24:35 -05:00
|
|
|
#ifndef __WIN32__
|
|
|
|
data.child_setup = child_setup;
|
|
|
|
data.user_data = user_data;
|
|
|
|
#endif
|
|
|
|
|
2006-05-23 12:27:28 -05:00
|
|
|
#if 0 && defined(__WIN32__)
|
|
|
|
if (!(cmd_flags & MOO_CMD_OPEN_CONSOLE))
|
|
|
|
result = moo_win32_spawn_async_with_pipes (working_dir,
|
2006-08-22 23:24:36 -05:00
|
|
|
argv, new_env,
|
2006-05-23 12:27:28 -05:00
|
|
|
flags | G_SPAWN_DO_NOT_REAP_CHILD,
|
|
|
|
&cmd->priv->pid,
|
|
|
|
NULL, outp, errp, error);
|
|
|
|
else
|
|
|
|
#endif
|
2015-12-25 22:23:10 -08:00
|
|
|
result = mgw_spawn_async_with_pipes (working_dir,
|
|
|
|
argv, new_env,
|
|
|
|
flags | G_SPAWN_DO_NOT_REAP_CHILD,
|
2005-12-24 19:36:47 +00:00
|
|
|
#ifndef __WIN32__
|
2015-12-25 22:23:10 -08:00
|
|
|
real_child_setup, &data,
|
2005-12-24 19:36:47 +00:00
|
|
|
#else
|
2015-12-25 22:23:10 -08:00
|
|
|
NULL, NULL,
|
2005-12-24 19:36:47 +00:00
|
|
|
#endif
|
2015-12-25 22:23:10 -08:00
|
|
|
&cmd->priv->pid,
|
|
|
|
NULL, outp, errp, error);
|
2005-11-28 02:12:50 +00:00
|
|
|
|
2006-08-22 23:24:36 -05:00
|
|
|
g_strfreev (new_env);
|
|
|
|
|
2005-11-28 02:12:50 +00:00
|
|
|
if (!result)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
cmd->priv->running = TRUE;
|
|
|
|
cmd->priv->flags = flags;
|
|
|
|
cmd->priv->cmd_flags = cmd_flags;
|
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
child_watch_data = g_new0 (ChildWatchData, 1);
|
|
|
|
child_watch_data->cmd = cmd;
|
|
|
|
g_child_watch_add_full (G_PRIORITY_DEFAULT,
|
|
|
|
cmd->priv->pid,
|
|
|
|
(GChildWatchFunc) command_exit,
|
|
|
|
child_watch_data,
|
|
|
|
(GDestroyNotify) child_watch_removed);
|
|
|
|
cmd->priv->child_watch_data = child_watch_data;
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
if (outp)
|
|
|
|
{
|
2015-12-25 22:23:10 -08:00
|
|
|
cmd->priv->stdout_io = mgw_io_channel_unix_new (cmd->priv->out);
|
2005-11-28 02:12:50 +00:00
|
|
|
g_io_channel_set_encoding (cmd->priv->stdout_io, NULL, NULL);
|
|
|
|
g_io_channel_set_buffered (cmd->priv->stdout_io, TRUE);
|
|
|
|
g_io_channel_set_flags (cmd->priv->stdout_io, G_IO_FLAG_NONBLOCK, NULL);
|
|
|
|
g_io_channel_set_close_on_unref (cmd->priv->stdout_io, TRUE);
|
|
|
|
cmd->priv->stdout_watch =
|
2007-01-19 20:47:59 -06:00
|
|
|
_moo_io_add_watch_full (cmd->priv->stdout_io,
|
|
|
|
G_PRIORITY_DEFAULT_IDLE,
|
|
|
|
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
|
|
|
|
(GIOFunc) command_out, cmd,
|
|
|
|
(GDestroyNotify) stdout_watch_removed);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (errp)
|
|
|
|
{
|
2015-12-25 22:23:10 -08:00
|
|
|
cmd->priv->stderr_io = mgw_io_channel_unix_new (cmd->priv->err);
|
2005-11-28 02:12:50 +00:00
|
|
|
g_io_channel_set_encoding (cmd->priv->stderr_io, NULL, NULL);
|
|
|
|
g_io_channel_set_buffered (cmd->priv->stderr_io, TRUE);
|
|
|
|
g_io_channel_set_flags (cmd->priv->stderr_io, G_IO_FLAG_NONBLOCK, NULL);
|
|
|
|
g_io_channel_set_close_on_unref (cmd->priv->stderr_io, TRUE);
|
|
|
|
cmd->priv->stderr_watch =
|
2007-01-19 20:47:59 -06:00
|
|
|
_moo_io_add_watch_full (cmd->priv->stderr_io,
|
|
|
|
G_PRIORITY_DEFAULT_IDLE,
|
|
|
|
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
|
|
|
|
(GIOFunc) command_err, cmd,
|
|
|
|
(GDestroyNotify) stderr_watch_removed);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
moo_cmd_check_stop (MooCmd *cmd)
|
|
|
|
{
|
|
|
|
gboolean result;
|
|
|
|
|
|
|
|
if (!cmd->priv->running)
|
|
|
|
return;
|
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
if (!cmd->priv->child_watch_data && !cmd->priv->stdout_watch && !cmd->priv->stderr_watch)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
2005-11-29 17:32:57 +00:00
|
|
|
g_object_ref (cmd);
|
2005-11-28 02:12:50 +00:00
|
|
|
g_signal_emit (cmd, signals[CMD_EXIT], 0, cmd->priv->exit_status, &result);
|
|
|
|
moo_cmd_cleanup (cmd);
|
2005-11-29 17:32:57 +00:00
|
|
|
g_object_unref (cmd);
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
moo_cmd_cleanup (MooCmd *cmd)
|
|
|
|
{
|
|
|
|
if (!cmd->priv->running)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cmd->priv->running = FALSE;
|
|
|
|
|
2008-01-09 12:05:28 -06:00
|
|
|
if (cmd->priv->child_watch_data)
|
|
|
|
cmd->priv->child_watch_data->cmd = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
if (cmd->priv->stdout_watch)
|
|
|
|
g_source_remove (cmd->priv->stdout_watch);
|
|
|
|
if (cmd->priv->stderr_watch)
|
|
|
|
g_source_remove (cmd->priv->stderr_watch);
|
|
|
|
|
|
|
|
if (cmd->priv->stdout_io)
|
|
|
|
g_io_channel_unref (cmd->priv->stdout_io);
|
|
|
|
if (cmd->priv->stderr_io)
|
|
|
|
g_io_channel_unref (cmd->priv->stderr_io);
|
|
|
|
|
|
|
|
if (cmd->priv->pid)
|
|
|
|
{
|
2005-12-24 19:36:47 +00:00
|
|
|
#ifndef __WIN32__
|
2005-11-28 02:12:50 +00:00
|
|
|
kill (-cmd->priv->pid, SIGHUP);
|
2005-12-24 19:36:47 +00:00
|
|
|
#else
|
2009-11-14 11:57:35 -08:00
|
|
|
TerminateProcess (cmd->priv->pid, 1);
|
2005-12-24 19:36:47 +00:00
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cmd->priv->pid = 0;
|
2015-12-25 22:23:10 -08:00
|
|
|
cmd->priv->out.value = -1;
|
|
|
|
cmd->priv->err.value = -1;
|
2008-01-09 12:05:28 -06:00
|
|
|
cmd->priv->child_watch_data = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
cmd->priv->stdout_watch = 0;
|
|
|
|
cmd->priv->stderr_watch = 0;
|
|
|
|
cmd->priv->stdout_io = NULL;
|
|
|
|
cmd->priv->stderr_io = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
moo_cmd_abort_real (MooCmd *cmd)
|
|
|
|
{
|
|
|
|
if (!cmd->priv->running)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (cmd->priv->pid != 0, TRUE);
|
|
|
|
|
2008-01-16 17:41:57 -06:00
|
|
|
/* TODO: on windows it gets exit status of 0 for some reason */
|
|
|
|
|
2005-12-24 19:36:47 +00:00
|
|
|
#ifndef __WIN32__
|
2005-11-28 02:12:50 +00:00
|
|
|
kill (-cmd->priv->pid, SIGHUP);
|
2005-12-24 19:36:47 +00:00
|
|
|
#else
|
2009-11-14 11:57:35 -08:00
|
|
|
TerminateProcess (cmd->priv->pid, 1);
|
2005-12-24 19:36:47 +00:00
|
|
|
#endif
|
2005-11-28 02:12:50 +00:00
|
|
|
|
|
|
|
if (cmd->priv->stdout_watch)
|
|
|
|
{
|
2008-01-16 17:41:57 -06:00
|
|
|
/* close it to avoid final read after watch is removed */
|
2008-01-17 09:04:32 -06:00
|
|
|
g_io_channel_shutdown (cmd->priv->stdout_io, FALSE, NULL);
|
2008-01-16 17:41:57 -06:00
|
|
|
cmd->priv->stdout_io = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
g_source_remove (cmd->priv->stdout_watch);
|
|
|
|
cmd->priv->stdout_watch = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd->priv->stderr_watch > 0)
|
|
|
|
{
|
2008-01-16 17:41:57 -06:00
|
|
|
/* close it to avoid final read after watch is removed */
|
2008-01-17 09:04:32 -06:00
|
|
|
g_io_channel_shutdown (cmd->priv->stderr_io, FALSE, NULL);
|
2008-01-16 17:41:57 -06:00
|
|
|
cmd->priv->stderr_io = NULL;
|
2005-11-28 02:12:50 +00:00
|
|
|
g_source_remove (cmd->priv->stderr_watch);
|
|
|
|
cmd->priv->stderr_watch = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-08-20 03:49:03 -05:00
|
|
|
_moo_cmd_abort (MooCmd *cmd)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
gboolean handled;
|
|
|
|
g_return_if_fail (MOO_IS_CMD (cmd));
|
|
|
|
g_signal_emit (cmd, signals[ABORT], 0, &handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2006-09-02 02:33:09 -05:00
|
|
|
moo_cmd_stdout_line (MooCmd *cmd,
|
|
|
|
const char *line)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
if (cmd->priv->cmd_flags & MOO_CMD_COLLECT_STDOUT)
|
2006-09-02 02:33:09 -05:00
|
|
|
g_string_append (cmd->priv->out_buffer, line);
|
2005-11-28 02:12:50 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2006-09-02 02:33:09 -05:00
|
|
|
moo_cmd_stderr_line (MooCmd *cmd,
|
|
|
|
const char *line)
|
2005-11-28 02:12:50 +00:00
|
|
|
{
|
|
|
|
if (cmd->priv->cmd_flags & MOO_CMD_COLLECT_STDERR)
|
2006-09-02 02:33:09 -05:00
|
|
|
g_string_append (cmd->priv->err_buffer, line);
|
2005-11-28 02:12:50 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2006-08-20 03:49:03 -05:00
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
_moo_unix_spawn_async (char **argv,
|
|
|
|
GSpawnFlags g_flags,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
return g_spawn_async (NULL, argv, NULL, g_flags,
|
|
|
|
NULL, NULL, NULL, error);
|
|
|
|
}
|
2010-12-27 20:31:05 -08:00
|
|
|
|
|
|
|
gboolean
|
|
|
|
moo_spawn_command_line_async_with_flags (const gchar *command_line,
|
|
|
|
GSpawnFlags g_flags,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean retval;
|
|
|
|
gchar **argv = 0;
|
|
|
|
|
|
|
|
g_return_val_if_fail (command_line != NULL, FALSE);
|
|
|
|
|
|
|
|
if (!g_shell_parse_argv (command_line,
|
|
|
|
NULL, &argv,
|
|
|
|
error))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
retval = g_spawn_async (NULL,
|
|
|
|
argv,
|
|
|
|
NULL,
|
|
|
|
G_SPAWN_SEARCH_PATH | g_flags,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
error);
|
|
|
|
g_strfreev (argv);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|