| #include <gio/gio.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #ifdef G_OS_UNIX |
| #include <unistd.h> |
| #else |
| #include <io.h> |
| #endif |
| |
| static GOptionEntry options[] = { |
| G_OPTION_ENTRY_NULL |
| }; |
| |
| static void |
| write_all (int fd, |
| const guint8* buf, |
| gsize len) |
| { |
| while (len > 0) |
| { |
| gssize bytes_written = write (fd, buf, len); |
| int errsv = errno; |
| if (bytes_written < 0) |
| g_error ("Failed to write to fd %d: %s", |
| fd, g_strerror (errsv)); |
| buf += bytes_written; |
| len -= bytes_written; |
| } |
| } |
| |
| static int |
| echo_mode (int argc, |
| char **argv) |
| { |
| int i; |
| |
| for (i = 2; i < argc; i++) |
| { |
| write_all (1, (guint8*)argv[i], strlen (argv[i])); |
| write_all (1, (guint8*)"\n", 1); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| echo_stdout_and_stderr_mode (int argc, |
| char **argv) |
| { |
| int i; |
| |
| for (i = 2; i < argc; i++) |
| { |
| write_all (1, (guint8*)argv[i], strlen (argv[i])); |
| write_all (1, (guint8*)"\n", 1); |
| write_all (2, (guint8*)argv[i], strlen (argv[i])); |
| write_all (2, (guint8*)"\n", 1); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| cat_mode (int argc, |
| char **argv) |
| { |
| GIOChannel *chan_stdin; |
| GIOChannel *chan_stdout; |
| GIOStatus status; |
| char buf[1024]; |
| gsize bytes_read, bytes_written; |
| GError *local_error = NULL; |
| GError **error = &local_error; |
| |
| chan_stdin = g_io_channel_unix_new (0); |
| g_io_channel_set_encoding (chan_stdin, NULL, error); |
| g_assert_no_error (local_error); |
| chan_stdout = g_io_channel_unix_new (1); |
| g_io_channel_set_encoding (chan_stdout, NULL, error); |
| g_assert_no_error (local_error); |
| |
| while (TRUE) |
| { |
| do |
| status = g_io_channel_read_chars (chan_stdin, buf, sizeof (buf), |
| &bytes_read, error); |
| while (status == G_IO_STATUS_AGAIN); |
| |
| if (status == G_IO_STATUS_EOF || status == G_IO_STATUS_ERROR) |
| break; |
| |
| do |
| status = g_io_channel_write_chars (chan_stdout, buf, bytes_read, |
| &bytes_written, error); |
| while (status == G_IO_STATUS_AGAIN); |
| |
| if (status == G_IO_STATUS_EOF || status == G_IO_STATUS_ERROR) |
| break; |
| } |
| |
| g_io_channel_unref (chan_stdin); |
| g_io_channel_unref (chan_stdout); |
| |
| if (local_error) |
| { |
| g_printerr ("I/O error: %s\n", local_error->message); |
| g_clear_error (&local_error); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static gint |
| sleep_forever_mode (int argc, |
| char **argv) |
| { |
| GMainLoop *loop; |
| |
| loop = g_main_loop_new (NULL, TRUE); |
| g_main_loop_run (loop); |
| |
| return 0; |
| } |
| |
| static int |
| write_to_fds (int argc, char **argv) |
| { |
| int i; |
| |
| for (i = 2; i < argc; i++) |
| { |
| int fd = atoi (argv[i]); |
| FILE *f = fdopen (fd, "w"); |
| const char buf[] = "hello world\n"; |
| size_t bytes_written; |
| |
| g_assert (f != NULL); |
| |
| bytes_written = fwrite (buf, 1, sizeof (buf), f); |
| g_assert (bytes_written == sizeof (buf)); |
| |
| if (fclose (f) == -1) |
| g_assert_not_reached (); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| read_from_fd (int argc, char **argv) |
| { |
| int fd; |
| const char expected_result[] = "Yay success!"; |
| guint8 buf[sizeof (expected_result) + 1]; |
| gsize bytes_read; |
| FILE *f; |
| |
| if (argc != 3) |
| { |
| g_print ("Usage: %s read-from-fd FD\n", argv[0]); |
| return 1; |
| } |
| |
| fd = atoi (argv[2]); |
| if (fd == 0) |
| { |
| g_warning ("Argument \"%s\" does not look like a valid nonzero file descriptor", argv[2]); |
| return 1; |
| } |
| |
| f = fdopen (fd, "r"); |
| if (f == NULL) |
| { |
| g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno)); |
| return 1; |
| } |
| |
| bytes_read = fread (buf, 1, sizeof (buf), f); |
| if (bytes_read != sizeof (expected_result)) |
| { |
| g_warning ("Read %zu bytes, but expected %zu", bytes_read, sizeof (expected_result)); |
| return 1; |
| } |
| |
| if (memcmp (expected_result, buf, sizeof (expected_result)) != 0) |
| { |
| buf[sizeof (expected_result)] = '\0'; |
| g_warning ("Expected \"%s\" but read \"%s\"", expected_result, (char *)buf); |
| return 1; |
| } |
| |
| if (fclose (f) == -1) |
| g_assert_not_reached (); |
| |
| return 0; |
| } |
| |
| static int |
| env_mode (int argc, char **argv) |
| { |
| char **env; |
| int i; |
| |
| env = g_get_environ (); |
| |
| for (i = 0; env[i]; i++) |
| g_print ("%s\n", env[i]); |
| |
| g_strfreev (env); |
| |
| return 0; |
| } |
| |
| static int |
| cwd_mode (int argc, char **argv) |
| { |
| char *cwd; |
| |
| cwd = g_get_current_dir (); |
| g_print ("%s\n", cwd); |
| g_free (cwd); |
| |
| return 0; |
| } |
| |
| static int |
| printenv_mode (int argc, char **argv) |
| { |
| gint i; |
| |
| for (i = 2; i < argc; i++) |
| { |
| const gchar *value = g_getenv (argv[i]); |
| |
| if (value != NULL) |
| g_print ("%s=%s\n", argv[i], value); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef G_OS_UNIX |
| static void |
| on_sleep_exited (GObject *object, |
| GAsyncResult *result, |
| gpointer user_data) |
| { |
| GSubprocess *subprocess = G_SUBPROCESS (object); |
| gboolean *done = user_data; |
| GError *local_error = NULL; |
| gboolean ret; |
| |
| ret = g_subprocess_wait_finish (subprocess, result, &local_error); |
| g_assert_no_error (local_error); |
| g_assert_true (ret); |
| |
| *done = TRUE; |
| g_main_context_wakeup (NULL); |
| } |
| |
| static int |
| sleep_and_kill (int argc, char **argv) |
| { |
| GPtrArray *args = NULL; |
| GSubprocessLauncher *launcher = NULL; |
| GSubprocess *proc = NULL; |
| GError *local_error = NULL; |
| pid_t sleep_pid; |
| gboolean done = FALSE; |
| |
| args = g_ptr_array_new_with_free_func (g_free); |
| |
| /* Run sleep "forever" in a shell; this will trigger PTRACE_EVENT_EXEC */ |
| g_ptr_array_add (args, g_strdup ("sh")); |
| g_ptr_array_add (args, g_strdup ("-c")); |
| g_ptr_array_add (args, g_strdup ("exec sleep infinity")); |
| g_ptr_array_add (args, NULL); |
| launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); |
| proc = g_subprocess_launcher_spawnv (launcher, (const gchar **) args->pdata, &local_error); |
| g_assert_no_error (local_error); |
| g_assert_nonnull (proc); |
| |
| sleep_pid = atoi (g_subprocess_get_identifier (proc)); |
| |
| g_subprocess_wait_async (proc, NULL, on_sleep_exited, &done); |
| |
| kill (sleep_pid, SIGKILL); |
| |
| while (!done) |
| g_main_context_iteration (NULL, TRUE); |
| |
| g_assert_false (g_subprocess_get_successful (proc)); |
| |
| g_clear_pointer (&args, g_ptr_array_unref); |
| g_clear_object (&launcher); |
| g_clear_object (&proc); |
| |
| return EXIT_SUCCESS; |
| } |
| #endif |
| |
| int |
| main (int argc, char **argv) |
| { |
| GOptionContext *context; |
| GError *error = NULL; |
| const char *mode; |
| gboolean ret; |
| |
| context = g_option_context_new ("MODE - Test GSubprocess stuff"); |
| g_option_context_add_main_entries (context, options, NULL); |
| ret = g_option_context_parse (context, &argc, &argv, &error); |
| g_option_context_free (context); |
| |
| if (!ret) |
| { |
| g_printerr ("%s: %s\n", argv[0], error->message); |
| g_error_free (error); |
| return 1; |
| } |
| |
| if (argc < 2) |
| { |
| g_printerr ("MODE argument required\n"); |
| return 1; |
| } |
| |
| g_log_writer_default_set_use_stderr (TRUE); |
| |
| mode = argv[1]; |
| if (strcmp (mode, "noop") == 0) |
| return 0; |
| else if (strcmp (mode, "exit1") == 0) |
| return 1; |
| else if (strcmp (mode, "assert-argv0") == 0) |
| { |
| if (strcmp (argv[0], "moocow") == 0) |
| return 0; |
| g_printerr ("argv0=%s != moocow\n", argv[0]); |
| return 1; |
| } |
| else if (strcmp (mode, "echo") == 0) |
| return echo_mode (argc, argv); |
| else if (strcmp (mode, "echo-stdout-and-stderr") == 0) |
| return echo_stdout_and_stderr_mode (argc, argv); |
| else if (strcmp (mode, "cat") == 0) |
| return cat_mode (argc, argv); |
| else if (strcmp (mode, "sleep-forever") == 0) |
| return sleep_forever_mode (argc, argv); |
| else if (strcmp (mode, "write-to-fds") == 0) |
| return write_to_fds (argc, argv); |
| else if (strcmp (mode, "read-from-fd") == 0) |
| return read_from_fd (argc, argv); |
| else if (strcmp (mode, "env") == 0) |
| return env_mode (argc, argv); |
| else if (strcmp (mode, "cwd") == 0) |
| return cwd_mode (argc, argv); |
| else if (strcmp (mode, "printenv") == 0) |
| return printenv_mode (argc, argv); |
| #ifdef G_OS_UNIX |
| else if (strcmp (mode, "sleep-and-kill") == 0) |
| return sleep_and_kill (argc, argv); |
| #endif |
| else |
| { |
| g_printerr ("Unknown MODE %s\n", argv[1]); |
| return 1; |
| } |
| |
| return TRUE; |
| } |