This is needed for e.g. passing a pass-phrase to cryptsetup(8).
Signed-off-by: David Zeuthen <davidz@redhat.com>
}
break;
+ case 7:
+ /* read from stdin.. echo that back */
+ {
+ GString *s;
+ gint c;
+
+ s = g_string_new (NULL);
+ while ((c = fgetc (stdin)) != EOF)
+ g_string_append_c (s, c);
+ g_print ("Woah, you said `%s', partner!\n", s->str);
+ g_string_free (s, TRUE);
+ ret = 0;
+ }
+ break;
+
default:
g_assert_not_reached ();
break;
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/true", NULL);
+ job = udisks_spawned_job_new ("/bin/true", NULL, NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_success), NULL);
g_object_unref (job);
}
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/false", NULL);
+ job = udisks_spawned_job_new ("/bin/false", NULL, NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `/bin/false' exited with non-zero exit status 1.\n"
"stdout: `'\n"
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/path/to/unknown/file", NULL);
+ job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Failed to execute command-line `/path/to/unknown/file': Error spawning command-line `/path/to/unknown/file': Failed to execute child process \"/path/to/unknown/file\" (No such file or directory) (g-exec-error-quark, 8)");
g_object_unref (job);
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
- job = udisks_spawned_job_new ("/bin/true", cancellable);
+ job = udisks_spawned_job_new ("/bin/true", NULL, cancellable);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Failed to execute command-line `/bin/true': Operation was cancelled (g-io-error-quark, 19)");
g_object_unref (job);
GCancellable *cancellable;
cancellable = g_cancellable_new ();
- job = udisks_spawned_job_new ("/bin/sleep 0.5", cancellable);
+ job = udisks_spawned_job_new ("/bin/sleep 0.5", NULL, cancellable);
g_timeout_add (10, on_timeout, cancellable); /* 10 msec */
g_main_loop_run (loop);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
UDisksSpawnedJob *job;
gboolean handler_ran;
- job = udisks_spawned_job_new ("/path/to/unknown/file", NULL /* GCancellable */);
+ job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, NULL /* GCancellable */);
handler_ran = FALSE;
g_signal_connect (job, "spawned-job-completed", G_CALLBACK (on_spawned_job_completed), &handler_ran);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/sleep 1000", NULL /* GCancellable */);
+ job = udisks_spawned_job_new ("/bin/sleep 1000", NULL, NULL /* GCancellable */);
g_object_unref (job);
}
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 0");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (read_stdout_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 1");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (read_stderr_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 2");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (exit_status_on_spawned_job_completed),
GINT_TO_POINTER (1));
g_object_unref (job);
g_free (s);
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 3");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (exit_status_on_spawned_job_completed),
GINT_TO_POINTER (2));
g_object_unref (job);
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 4");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `./udisks-test-helper 4' was signaled with signal SIGSEGV (11).\n"
"stdout: `OK, deliberately causing a segfault\n"
g_free (s);
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 5");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `./udisks-test-helper 5' was signaled with signal SIGABRT (6).\n"
"stdout: `OK, deliberately abort()'ing\n"
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 6");
- job = udisks_spawned_job_new (s, NULL);
+ job = udisks_spawned_job_new (s, NULL, NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (binary_output_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
/* ---------------------------------------------------------------------------------------------------- */
+static gboolean
+input_string_on_spawned_job_completed (UDisksSpawnedJob *job,
+ GError *error,
+ gint status,
+ GString *standard_output,
+ GString *standard_error,
+ gpointer user_data)
+{
+ g_assert_no_error (error);
+ g_assert_cmpstr (standard_error->str, ==, "");
+ g_assert (WIFEXITED (status));
+ g_assert (WEXITSTATUS (status) == 0);
+ g_assert_cmpstr (standard_output->str, ==, "Woah, you said `foobar', partner!\n");
+ return FALSE;
+}
+
+static void
+test_spawned_job_input_string (void)
+{
+ UDisksSpawnedJob *job;
+ gchar *s;
+
+ s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 7");
+ job = udisks_spawned_job_new (s, "foobar", NULL);
+ _g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (input_string_on_spawned_job_completed), NULL);
+ g_object_unref (job);
+ g_free (s);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
int
main (int argc,
char **argv)
g_test_add_func ("/udisks/daemon/spawned_job/exit_status", test_spawned_job_exit_status);
g_test_add_func ("/udisks/daemon/spawned_job/abnormal_termination", test_spawned_job_abnormal_termination);
g_test_add_func ("/udisks/daemon/spawned_job/binary_output", test_spawned_job_binary_output);
+ g_test_add_func ("/udisks/daemon/spawned_job/input_string", test_spawned_job_input_string);
ret = g_test_run();
* udisks_daemon_launch_spawned_job:
* @daemon: A #UDisksDaemon.
* @cancellable: A #GCancellable or %NULL.
+ * @input_string: A string to write to stdin of the spawned program or %NULL.
* @command_line_format: printf()-style format for the command line to spawn.
* @...: Arguments for @command_line_format.
*
UDisksSpawnedJob *
udisks_daemon_launch_spawned_job (UDisksDaemon *daemon,
GCancellable *cancellable,
+ const gchar *input_string,
const gchar *command_line_format,
...)
{
va_start (var_args, command_line_format);
command_line = g_strdup_vprintf (command_line_format, var_args);
va_end (var_args);
- job = udisks_spawned_job_new (command_line, cancellable);
+ job = udisks_spawned_job_new (command_line, input_string, cancellable);
g_free (command_line);
/* TODO: protect job_id by a mutex */
UDisksSpawnedJob *udisks_daemon_launch_spawned_job (UDisksDaemon *daemon,
GCancellable *cancellable,
+ const gchar *input_string,
const gchar *command_line_format,
- ...) G_GNUC_PRINTF (3, 4);
+ ...) G_GNUC_PRINTF (4, 5);
G_END_DECLS
job = UDISKS_JOB (udisks_daemon_launch_spawned_job (daemon,
NULL, /* GCancellable */
+ NULL, /* input string */
"sleep %d", 2));
/* this blows a little bit - would be nice to have an easier way to
* get back to the object from the job
* @short_description: Job that spawns a command
*
* This type provides an implementation of the #UDisksJob interface
- * for jobs that are implemented by spawning a command.
+ * for jobs that are implemented by spawning a command line.
*/
typedef struct _UDisksSpawnedJobClass UDisksSpawnedJobClass;
GMainContext *main_context;
+ gchar *input_string;
+ const gchar *input_string_cursor;
+
GPid child_pid;
+ gint child_stdin_fd;
gint child_stdout_fd;
gint child_stderr_fd;
+ GIOChannel *child_stdin_channel;
GIOChannel *child_stdout_channel;
GIOChannel *child_stderr_channel;
GSource *child_watch_source;
+ GSource *child_stdin_source;
GSource *child_stdout_source;
GSource *child_stderr_source;
{
PROP_0,
PROP_COMMAND_LINE,
+ PROP_INPUT_STRING,
PROP_CANCELLABLE
};
g_free (job->command_line);
+ /* input string may contain key material - nuke contents */
+ if (job->input_string != NULL)
+ {
+ memset (job->input_string, '\0', strlen (job->input_string));
+ g_free (job->input_string);
+ }
+
if (G_OBJECT_CLASS (udisks_spawned_job_parent_class)->finalize != NULL)
G_OBJECT_CLASS (udisks_spawned_job_parent_class)->finalize (object);
}
job->command_line = g_value_dup_string (value);
break;
+ case PROP_INPUT_STRING:
+ g_assert (job->input_string == NULL);
+ job->input_string = g_value_dup_string (value);
+ break;
+
case PROP_CANCELLABLE:
g_assert (job->cancellable == NULL);
job->cancellable = g_value_dup_object (value);
return TRUE;
}
+static gboolean
+write_child_stdin (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ UDisksSpawnedJob *job = UDISKS_SPAWNED_JOB (user_data);
+ gsize bytes_written;
+
+ if (job->input_string_cursor == NULL || *job->input_string_cursor == '\0')
+ {
+ /* nothing left to write; close our end so the child will get EOF */
+ g_io_channel_unref (job->child_stdin_channel);
+ g_source_destroy (job->child_stdin_source);
+ g_warn_if_fail (close (job->child_stdin_fd) == 0);
+ job->child_stdin_channel = NULL;
+ job->child_stdin_source = NULL;
+ job->child_stdin_fd = -1;
+ return FALSE;
+ }
+
+ g_io_channel_write_chars (channel,
+ job->input_string_cursor,
+ strlen (job->input_string_cursor),
+ &bytes_written,
+ NULL);
+ g_io_channel_flush (channel, NULL);
+ job->input_string_cursor += bytes_written;
+
+ /* keep writing */
+ return TRUE;
+}
+
static void
child_watch_cb (GPid pid,
gint status,
NULL, /* child_setup */
NULL, /* child_setup's user_data */
&(job->child_pid),
- NULL, // TODO:stdin: stdin_str != NULL ? &(job->stdin_fd) : NULL,
+ job->input_string != NULL ? &(job->child_stdin_fd) : NULL,
&(job->child_stdout_fd),
&(job->child_stderr_fd),
&error))
g_source_attach (job->child_watch_source, job->main_context);
g_source_unref (job->child_watch_source);
+ if (job->child_stdin_fd != -1)
+ {
+ job->input_string_cursor = job->input_string;
+
+ job->child_stdin_channel = g_io_channel_unix_new (job->child_stdin_fd);
+ g_io_channel_set_flags (job->child_stdin_channel, G_IO_FLAG_NONBLOCK, NULL);
+ job->child_stdin_source = g_io_create_watch (job->child_stdin_channel, G_IO_OUT);
+ g_source_set_callback (job->child_stdin_source, (GSourceFunc) write_child_stdin, job, NULL);
+ g_source_attach (job->child_stdin_source, job->main_context);
+ g_source_unref (job->child_stdin_source);
+ }
+
job->child_stdout = g_string_new (NULL);
job->child_stdout_channel = g_io_channel_unix_new (job->child_stdout_fd);
g_io_channel_set_flags (job->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
static void
udisks_spawned_job_init (UDisksSpawnedJob *job)
{
- job->child_stderr_fd = -1;
+ job->child_stdin_fd = -1;
job->child_stdout_fd = -1;
+ job->child_stderr_fd = -1;
}
static void
G_PARAM_STATIC_STRINGS));
/**
+ * UDisksSpawnedJob:input-string:
+ *
+ * String that will be written to stdin of the spawned program or
+ * %NULL to not write anything.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_INPUT_STRING,
+ g_param_spec_string ("input-string",
+ "Input String",
+ "String to write to stdin of the spawned program",
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* UDisksSpawnedJob:cancellable:
*
* The #GCancellable to use.
* @standard_output: Standard output from the command line that was run.
* @standard_error: Standard error output from the command line that was run.
*
- * Emitted when the spawned job has completed. If spawning the
- * command failed or if the job was cancelled, @error will
+ * Emitted when the spawned job is complete. If spawning the command
+ * failed or if the job was cancelled, @error will
* non-%NULL. Otherwise you can use macros such as WIFEXITED() and
* WEXITSTATUS() on the @status integer to obtain more information.
*
/**
* udisks_spawned_job_new:
* @command_line: The command line to run.
+ * @input_string: A string to write to stdin of the spawned program or %NULL.
* @cancellable: A #GCancellable or %NULL.
*
* Creates a new #UDisksSpawnedJob instance.
*/
UDisksSpawnedJob *
udisks_spawned_job_new (const gchar *command_line,
+ const gchar *input_string,
GCancellable *cancellable)
{
g_return_val_if_fail (command_line != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
return UDISKS_SPAWNED_JOB (g_object_new (UDISKS_TYPE_SPAWNED_JOB,
"command-line", command_line,
+ "input-string", input_string,
"cancellable", cancellable,
NULL));
}
job->child_stderr = NULL;
}
+ if (job->child_stdin_channel != NULL)
+ {
+ g_io_channel_unref (job->child_stdin_channel);
+ job->child_stdin_channel = NULL;
+ }
if (job->child_stdout_channel != NULL)
{
g_io_channel_unref (job->child_stdout_channel);
job->child_stderr_channel = NULL;
}
+ if (job->child_stdin_source != NULL)
+ {
+ g_source_destroy (job->child_stdin_source);
+ job->child_stdin_source = NULL;
+ }
if (job->child_stdout_source != NULL)
{
g_source_destroy (job->child_stdout_source);
job->child_stderr_source = NULL;
}
+ if (job->child_stdin_fd != -1)
+ {
+ g_warn_if_fail (close (job->child_stdin_fd) == 0);
+ job->child_stdin_fd = -1;
+ }
if (job->child_stdout_fd != -1)
{
g_warn_if_fail (close (job->child_stdout_fd) == 0);
#define UDISKS_IS_SPAWNED_JOB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_SPAWNED_JOB))
GType udisks_spawned_job_get_type (void) G_GNUC_CONST;
-UDisksSpawnedJob *udisks_spawned_job_new (const gchar *command_line,
+UDisksSpawnedJob *udisks_spawned_job_new (const gchar *command_line,
+ const gchar *input_string,
GCancellable *cancellable);
const gchar *udisks_spawned_job_get_command_line (UDisksSpawnedJob *job);