void * sol_mainloop_impl_child_watch_add(uint64_t pid, void (*cb)(void *data, uint64_t pid, int status), const void *data) { struct child_wrap_data *wrap_data = malloc(sizeof(*wrap_data)); SOL_NULL_CHECK(wrap_data, NULL); wrap_data->cb = cb; wrap_data->data = data; return (void *)(long)g_child_watch_add_full(0, pid, on_child, wrap_data, free); }
void deja_dup_async_command_run (DejaDupAsyncCommand* self) { GPid _tmp7_; guint _tmp8_ = 0U; GError * _inner_error_ = NULL; g_return_if_fail (self != NULL); { gchar** _tmp0_; gint _tmp0__length1; gchar** _tmp1_; gint _tmp1__length1; GPid _tmp2_ = 0; gboolean _tmp3_ = FALSE; gboolean _tmp4_; _tmp0_ = deja_dup_async_command_get_argv (self, &_tmp0__length1); _tmp1_ = _tmp0_; _tmp1__length1 = _tmp0__length1; _tmp3_ = g_spawn_async (NULL, _tmp1_, NULL, ((G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL) | G_SPAWN_DO_NOT_REAP_CHILD) | G_SPAWN_SEARCH_PATH, NULL, NULL, &_tmp2_, &_inner_error_); self->priv->pid = _tmp2_; _tmp4_ = _tmp3_; if (_inner_error_ != NULL) { goto __catch0_g_error; } if (!_tmp4_) { g_signal_emit_by_name (self, "done", FALSE); } } goto __finally0; __catch0_g_error: { GError* e = NULL; GError* _tmp5_; const gchar* _tmp6_; e = _inner_error_; _inner_error_ = NULL; _tmp5_ = e; _tmp6_ = _tmp5_->message; g_warning ("AsyncCommand.vala:60: %s\n", _tmp6_); g_signal_emit_by_name (self, "done", FALSE); _g_error_free0 (e); } __finally0: if (_inner_error_ != NULL) { g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code); g_clear_error (&_inner_error_); return; } _tmp7_ = self->priv->pid; _tmp8_ = g_child_watch_add_full (G_PRIORITY_DEFAULT_IDLE, _tmp7_, _deja_dup_async_command_handle_done_gchild_watch_func, g_object_ref (self), g_object_unref); self->priv->watch = _tmp8_; }
/* Returns pid if succesful, returns -1 if errors happen. */ GPid run_app( const char* cmd, gboolean guarded ) { GPid pid = -1; wordexp_t we; GSpawnFlags flags = guarded ? G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH : G_SPAWN_SEARCH_PATH; if( wordexp(cmd, &we, 0) == 0) { g_spawn_async( NULL, we.we_wordv, NULL, flags, NULL, NULL, &pid, NULL ); wordfree(&we); } if(guarded && pid > 0) { g_child_watch_add_full( G_PRIORITY_DEFAULT_IDLE, pid, (GChildWatchFunc)on_child_exit, g_strdup( cmd ), (GDestroyNotify)g_free ); } return pid; }
static void thunar_uca_provider_activated (ThunarUcaProvider *uca_provider, GtkAction *action) { GtkTreeRowReference *row; ThunarUcaContext *uca_context; GtkTreePath *path; GtkTreeIter iter; GtkWidget *dialog; GtkWidget *window; gboolean succeed; GSource *source; GError *error = NULL; GList *files; gchar **argv; gchar *working_directory = NULL; gchar *filename; gchar *label; gchar *uri; gint argc; gint pid; g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider)); g_return_if_fail (GTK_IS_ACTION (action)); /* check if the row reference is still valid */ row = g_object_get_qdata (G_OBJECT (action), thunar_uca_row_quark); if (G_UNLIKELY (!gtk_tree_row_reference_valid (row))) return; /* determine the iterator for the item */ path = gtk_tree_row_reference_get_path (row); gtk_tree_model_get_iter (GTK_TREE_MODEL (uca_provider->model), &iter, path); gtk_tree_path_free (path); /* determine the files and the window for the action */ uca_context = g_object_get_qdata (G_OBJECT (action), thunar_uca_context_quark); window = thunar_uca_context_get_window (uca_context); files = thunar_uca_context_get_files (uca_context); /* determine the argc/argv for the item */ succeed = thunar_uca_model_parse_argv (uca_provider->model, &iter, files, &argc, &argv, &error); if (G_LIKELY (succeed)) { /* determine the working from the first file */ if (G_LIKELY (files != NULL)) { /* determine the filename of the first selected file */ uri = thunarx_file_info_get_uri (files->data); filename = g_filename_from_uri (uri, NULL, NULL); if (G_LIKELY (filename != NULL)) { /* if this is a folder action, we just use the filename as working directory */ if (g_object_get_qdata (G_OBJECT (action), thunar_uca_folder_quark) != NULL) { working_directory = filename; filename = NULL; } else { working_directory = g_path_get_dirname (filename); } } g_free (filename); g_free (uri); } /* spawn the command on the window's screen */ succeed = gdk_spawn_on_screen (gtk_widget_get_screen (GTK_WIDGET (window)), working_directory, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, &error); /* check if we succeed */ if (G_LIKELY (succeed)) { /* check if we already have a child watch */ if (G_UNLIKELY (uca_provider->child_watch_id >= 0)) { /* reset the callback function to g_spawn_close_pid() so the plugin can be * safely unloaded and the child will still not become a zombie afterwards. */ source = g_main_context_find_source_by_id (NULL, uca_provider->child_watch_id); g_source_set_callback (source, (GSourceFunc) g_spawn_close_pid, NULL, NULL); } /* schedule the new child watch */ uca_provider->child_watch_id = g_child_watch_add_full (G_PRIORITY_LOW, pid, thunar_uca_provider_child_watch, uca_provider, thunar_uca_provider_child_watch_destroy); /* take over ownership of the working directory as child watch path */ uca_provider->child_watch_path = working_directory; working_directory = NULL; } /* cleanup */ g_free (working_directory); g_strfreev (argv); } /* present error message to the user */ if (G_UNLIKELY (!succeed)) { g_object_get (G_OBJECT (action), "label", &label, NULL); dialog = gtk_message_dialog_new ((GtkWindow *) window, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Failed to launch action \"%s\"."), label); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_error_free (error); g_free (label); } }
static gboolean launch_test_binary (const char *binary, guint skip_tests) { GTestLogBuffer *tlb; GSList *slist, *free_list = NULL; GError *error = NULL; int argc = 0; const gchar **argv; GPid pid = 0; gint report_pipe[2] = { -1, -1 }; guint child_report_cb_id = 0; gboolean loop_pending; gint i = 0; if (!g_unix_open_pipe (report_pipe, FD_CLOEXEC, &error)) { if (subtest_mode_fatal) g_error ("Failed to open pipe for test binary: %s: %s", binary, error->message); else g_warning ("Failed to open pipe for test binary: %s: %s", binary, error->message); g_clear_error (&error); return FALSE; } /* setup argc */ for (slist = subtest_args; slist; slist = slist->next) argc++; /* argc++; */ if (subtest_quiet) argc++; if (subtest_verbose) argc++; if (!subtest_mode_fatal) argc++; /* Either -m=quick or -m=slow is always appended. */ argc++; if (subtest_mode_perf) argc++; if (!subtest_mode_undefined) argc++; if (gtester_list_tests) argc++; if (subtest_seedstr) argc++; argc++; if (skip_tests) argc++; for (slist = subtest_paths; slist; slist = slist->next) argc++; for (slist = skipped_paths; slist; slist = slist->next) argc++; /* setup argv */ argv = g_malloc ((argc + 2) * sizeof(gchar *)); argv[i++] = binary; for (slist = subtest_args; slist; slist = slist->next) argv[i++] = (gchar*) slist->data; /* argv[i++] = "--debug-log"; */ if (subtest_quiet) argv[i++] = "--quiet"; if (subtest_verbose) argv[i++] = "--verbose"; if (!subtest_mode_fatal) argv[i++] = "--keep-going"; if (subtest_mode_quick) argv[i++] = "-m=quick"; else argv[i++] = "-m=slow"; if (subtest_mode_perf) argv[i++] = "-m=perf"; if (!subtest_mode_undefined) argv[i++] = "-m=no-undefined"; if (gtester_list_tests) argv[i++] = "-l"; if (subtest_seedstr) argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--seed=%s", subtest_seedstr)); argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--GTestLogFD=%u", report_pipe[1])); if (skip_tests) argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--GTestSkipCount=%u", skip_tests)); for (slist = subtest_paths; slist; slist = slist->next) argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-p=%s", (gchar*) slist->data)); for (slist = skipped_paths; slist; slist = slist->next) argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-s=%s", (gchar*) slist->data)); argv[i++] = NULL; g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */ (gchar**) argv, NULL, /* envp */ G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */ unset_cloexec_fdp, &report_pipe[1], /* pre-exec callback */ &pid, NULL, /* standard_input */ NULL, /* standard_output */ NULL, /* standard_error */ &error); g_slist_foreach (free_list, (void(*)(void*,void*)) g_free, NULL); g_slist_free (free_list); free_list = NULL; close (report_pipe[1]); if (!gtester_quiet) g_print ("(pid=%lu)\n", (unsigned long) pid); if (error) { close (report_pipe[0]); if (subtest_mode_fatal) g_error ("Failed to execute test binary: %s: %s", argv[0], error->message); else g_warning ("Failed to execute test binary: %s: %s", argv[0], error->message); g_clear_error (&error); g_free (argv); return FALSE; } g_free (argv); subtest_running = TRUE; subtest_io_pending = TRUE; tlb = g_test_log_buffer_new(); if (report_pipe[0] >= 0) { ioc_report = g_io_channel_unix_new (report_pipe[0]); g_io_channel_set_flags (ioc_report, G_IO_FLAG_NONBLOCK, NULL); g_io_channel_set_encoding (ioc_report, NULL, NULL); g_io_channel_set_buffered (ioc_report, FALSE); child_report_cb_id = g_io_add_watch_full (ioc_report, G_PRIORITY_DEFAULT - 1, G_IO_IN | G_IO_ERR | G_IO_HUP, child_report_cb, tlb, NULL); g_io_channel_unref (ioc_report); } g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, child_watch_cb, NULL, NULL); loop_pending = g_main_context_pending (NULL); while (subtest_running || /* FALSE once child exits */ subtest_io_pending || /* FALSE once ioc_report closes */ loop_pending) /* TRUE while idler, etc are running */ { /* g_print ("LOOPSTATE: subtest_running=%d subtest_io_pending=%d\n", subtest_running, subtest_io_pending); */ /* check for unexpected hangs that are not signalled on report_pipe */ if (!subtest_running && /* child exited */ subtest_io_pending && /* no EOF detected on report_pipe */ !loop_pending) /* no IO events pending however */ break; g_main_context_iteration (NULL, TRUE); loop_pending = g_main_context_pending (NULL); } if (subtest_io_pending) g_source_remove (child_report_cb_id); close (report_pipe[0]); g_test_log_buffer_free (tlb); return TRUE; }
gboolean process_run (CommandData *command_data, GIOFunc io_callback, ProcessFinishCallback finish_callback, gpointer user_data, GError **error) { ProcessData *data; //struct termios term; struct winsize win = { .ws_col = 80, .ws_row = 24, .ws_xpixel = 480, .ws_ypixel = 192, }; data = g_slice_new0 (ProcessData); data->localwatchdog = FALSE; data->command_data = command_data; data->finish_callback = finish_callback; data->user_data = user_data; data->pid = forkpty (&data->fd, NULL, NULL, &win); if (data->pid < 0) { /* Failed to fork */ g_set_error (error, RESTRAINT_PROCESS_ERROR, RESTRAINT_PROCESS_FORK_ERROR, "Failed to fork: %s", g_strerror (errno)); return FALSE; } else if (data->pid == 0) { /* Child process. */ if (command_data->path && (chdir (command_data->path) == -1)) { /* command_path was supplied and we failed to chdir to it. */ g_warning ("Failed to chdir() to %s: %s\n", command_data->path, g_strerror (errno)); exit (1); } environ = (gchar **) command_data->environ; // Print the command being executed. gchar *command = g_strjoinv (" ", (gchar **) command_data->command); g_print ("%s\n", command); g_free (command); /* Spawn the command */ if (execvp (*command_data->command, (gchar **) command_data->command) == -1) { g_warning ("Failed to exec() %s, %s error:%s\n", *command_data->command, command_data->path, g_strerror (errno)); exit (1); } } /* Parent process. */ // close file descriptors on exec. Should prevent leaking fd's to child processes. fcntl (data->fd, F_SETFD, FD_CLOEXEC); // Localwatchdog handler if (command_data->max_time != 0) { data->timeout_handler_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, command_data->max_time, process_timeout_callback, data, NULL); } // IO handler if (io_callback != NULL) { GIOChannel *io = g_io_channel_unix_new (data->fd); g_io_channel_set_flags (io, G_IO_FLAG_NONBLOCK, NULL); // Set Encoding to NULL to keep g_io_channel from trying to decode it. g_io_channel_set_encoding (io, NULL, NULL); data->io_handler_id = g_io_add_watch_full (io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP, io_callback, data, process_io_finish); } // Monitor pid for return code data->pid_handler_id = g_child_watch_add_full (G_PRIORITY_DEFAULT, data->pid, process_pid_callback, data, process_pid_finish); return TRUE; } void process_pid_callback (GPid pid, gint status, gpointer user_data) { ProcessData *process_data = (ProcessData *) user_data; process_data->pid_result = status; }
/** * _gcr_gnupg_process_run_async: * @self: The process * @argv: (array zero-terminated=1): The arguments for the process, not including executable, terminated with %NULL. * @envp: (allow-none) (array zero-terminated=1): The environment for new process, terminated with %NULL. * @flags: Flags for starting the process. * @cancellable: (allow-none): Cancellation object * @callback: Will be called when operation completes. * @user_data: (closure): Data passed to callback. * * Run the gpg process. Only one 'run' operation can run per GcrGnupgProcess * object. The GcrGnupgProcess:output_data and GcrGnupgProcess:error_line * signals will be emitted when data is received from the gpg process. * * Unless the %GCR_GNUPG_PROCESS_RESPECT_LOCALE flag is specified, the process * will be run in the 'C' locale. If the %GCR_GNUPG_PROCESS_WITH_STATUS or * %GCR_GNUPG_PROCESS_WITH_ATTRIBUTES flags are set, then the gpg process * will be status and attribute output respectively. The * GcrGnupgProcess:status_record and GcrGnupgProcess:attribute_data signals * will provide this data. */ void _gcr_gnupg_process_run_async (GcrGnupgProcess *self, const gchar **argv, const gchar **envp, GcrGnupgProcessFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GPtrArray *args; GPtrArray *envs; int child_fds[NUM_FDS]; int status_fds[2] = { -1, -1 }; int attribute_fds[2] = { -1, -1 }; int output_fd = -1; int error_fd = -1; int input_fd = -1; GnupgSource *gnupg_source; GSource *source; GPid pid; guint i; g_return_if_fail (GCR_IS_GNUPG_PROCESS (self)); g_return_if_fail (argv); g_return_if_fail (callback); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); g_return_if_fail (self->pv->running == FALSE); g_return_if_fail (self->pv->complete == FALSE); g_return_if_fail (self->pv->executable); self->pv->async_callback = callback; self->pv->user_data = user_data; for (i = 0; i < NUM_FDS; i++) child_fds[i] = -1; /* The command needs to be updated with these status and attribute fds */ args = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (args, g_strdup (self->pv->executable)); /* Spawn/child will close all other attributes, besides thesthose in child_fds */ child_fds[FD_INPUT] = 0; child_fds[FD_OUTPUT] = 1; child_fds[FD_ERROR] = 2; if (flags & GCR_GNUPG_PROCESS_WITH_STATUS) { if (pipe (status_fds) < 0) g_return_if_reached (); child_fds[FD_STATUS] = status_fds[1]; g_ptr_array_add (args, g_strdup ("--status-fd")); g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_STATUS])); } if (flags & GCR_GNUPG_PROCESS_WITH_ATTRIBUTES) { if (pipe (attribute_fds) < 0) g_return_if_reached (); child_fds[FD_ATTRIBUTE] = attribute_fds[1]; g_ptr_array_add (args, g_strdup ("--attribute-fd")); g_ptr_array_add (args, g_strdup_printf ("%d", child_fds[FD_ATTRIBUTE])); } if (self->pv->directory) { g_ptr_array_add (args, g_strdup ("--homedir")); g_ptr_array_add (args, g_strdup (self->pv->directory)); } /* All the remaining arguments */ for (i = 0; argv[i] != NULL; i++) g_ptr_array_add (args, g_strdup (argv[i])); g_ptr_array_add (args, NULL); envs = g_ptr_array_new (); for (i = 0; envp && envp[i] != NULL; i++) { if (flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE || !g_str_has_prefix (envp[i], "LOCALE=")) g_ptr_array_add (envs, (gpointer)envp[i]); } if (!(flags & GCR_GNUPG_PROCESS_RESPECT_LOCALE)) g_ptr_array_add (envs, (gpointer)"LOCALE=C"); g_ptr_array_add (envs, NULL); gchar *command = g_strjoinv (" ", (gchar**)args->pdata); gchar *environ = g_strjoinv (", ", (gchar**)envs->pdata); g_debug ("running command: %s", command); g_debug ("process environment: %s", environ); g_free (command); g_free (environ); g_spawn_async_with_pipes (self->pv->directory, (gchar**)args->pdata, (gchar**)envs->pdata, G_SPAWN_DO_NOT_REAP_CHILD, on_gnupg_process_child_setup, child_fds, &pid, &input_fd, &output_fd, &error_fd, &error); g_ptr_array_free (args, TRUE); g_ptr_array_free (envs, TRUE); /* Close 'wrong' ends of extra file descriptors */ close_fd (&(status_fds[1])); close_fd (&(attribute_fds[1])); self->pv->complete = FALSE; self->pv->running = TRUE; if (error) { close_fd (&(status_fds[0])); close_fd (&(attribute_fds[0])); g_assert (!self->pv->error); self->pv->error = error; complete_run_process (self); run_async_ready_callback_later (self); return; } g_debug ("process started: %d", (int)pid); source = g_source_new (&gnupg_source_funcs, sizeof (GnupgSource)); /* Initialize the source */ gnupg_source = (GnupgSource*)source; for (i = 0; i < NUM_FDS; i++) gnupg_source->polls[i].fd = -1; gnupg_source->error_buf = g_string_sized_new (128); gnupg_source->status_buf = g_string_sized_new (128); gnupg_source->process = g_object_ref (self); gnupg_source->child_pid = pid; gnupg_source->polls[FD_INPUT].fd = input_fd; if (input_fd >= 0) { gnupg_source->polls[FD_INPUT].events = G_IO_HUP | G_IO_OUT; g_source_add_poll (source, &gnupg_source->polls[FD_INPUT]); } gnupg_source->polls[FD_OUTPUT].fd = output_fd; if (output_fd >= 0) { gnupg_source->polls[FD_OUTPUT].events = G_IO_HUP | G_IO_IN; g_source_add_poll (source, &gnupg_source->polls[FD_OUTPUT]); } gnupg_source->polls[FD_ERROR].fd = error_fd; if (error_fd >= 0) { gnupg_source->polls[FD_ERROR].events = G_IO_HUP | G_IO_IN; g_source_add_poll (source, &gnupg_source->polls[FD_ERROR]); } gnupg_source->polls[FD_STATUS].fd = status_fds[0]; if (status_fds[0] >= 0) { gnupg_source->polls[FD_STATUS].events = G_IO_HUP | G_IO_IN; g_source_add_poll (source, &gnupg_source->polls[FD_STATUS]); } gnupg_source->polls[FD_ATTRIBUTE].fd = attribute_fds[0]; if (attribute_fds[0] >= 0) { gnupg_source->polls[FD_ATTRIBUTE].events = G_IO_HUP | G_IO_IN; g_source_add_poll (source, &gnupg_source->polls[FD_ATTRIBUTE]); } if (cancellable) { gnupg_source->cancellable = g_object_ref (cancellable); gnupg_source->cancel_sig = g_cancellable_connect (cancellable, G_CALLBACK (on_cancellable_cancelled), g_source_ref (source), (GDestroyNotify)g_source_unref); } g_assert (self->pv->source_sig == 0); g_source_set_callback (source, unused_callback, NULL, NULL); self->pv->source_sig = g_source_attach (source, g_main_context_default ()); /* This assumes the outstanding reference to source */ g_assert (gnupg_source->child_sig == 0); gnupg_source->child_sig = g_child_watch_add_full (G_PRIORITY_DEFAULT, pid, on_gnupg_process_child_exited, g_source_ref (source), (GDestroyNotify)g_source_unref); /* source is unreffed in complete_if_source_is_done() */ }
GPid storage_daemon_spawn_for_variant (StorageDaemon *daemon, const gchar **argv, const GVariantType *type, void (*callback) (GPid, GVariant *, GError *, gpointer), gpointer user_data) { GError *error = NULL; struct VariantReaderData *data; gchar *prog = NULL; GPid pid; gint output_fd; gchar *cmd; /* * This is so we can override the location of storaged-lvm-helper * during testing. */ if (!strchr (argv[0], '/')) { prog = storage_daemon_get_resource_path (daemon, TRUE, argv[0]); argv[0] = prog; } cmd = g_strjoinv (" ", (gchar **)argv); g_debug ("spawning for variant: %s", cmd); g_free (cmd); if (!g_spawn_async_with_pipes (NULL, (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL, &output_fd, NULL, &error)) { callback (0, NULL, error, user_data); g_error_free (error); return 0; } data = g_new0 (struct VariantReaderData, 1); data->type = type; data->callback = callback; data->user_data = user_data; data->pid = pid; data->output = g_byte_array_new (); data->output_channel = g_io_channel_unix_new (output_fd); g_io_channel_set_encoding (data->output_channel, NULL, NULL); g_io_channel_set_flags (data->output_channel, G_IO_FLAG_NONBLOCK, NULL); data->output_watch = g_io_add_watch (data->output_channel, G_IO_IN, variant_reader_child_output, data); g_child_watch_add_full (G_PRIORITY_DEFAULT_IDLE, pid, variant_reader_watch_child, data, variant_reader_destroy); g_free (prog); return pid; }
void process_run (const gchar *command, const gchar **envp, const gchar *path, guint64 max_time, GIOFunc io_callback, ProcessFinishCallback finish_callback, GCancellable *cancellable, gpointer user_data) { ProcessData *process_data; //struct termios term; struct winsize win = { .ws_col = 80, .ws_row = 24, .ws_xpixel = 480, .ws_ypixel = 192, }; process_data = g_slice_new0 (ProcessData); process_data->localwatchdog = FALSE; process_data->command = g_strsplit (command, " ", 0); process_data->path = path; process_data->max_time = max_time; process_data->io_callback = io_callback; process_data->finish_callback = finish_callback; process_data->user_data = user_data; process_data->io = NULL; process_data->cancellable = cancellable; if (fflush (stdout) != 0) g_warning ("Failed to flush stdout: %s\n", g_strerror (errno)); if (fflush (stderr) != 0) g_warning ("Failed to flush stderr: %s\n", g_strerror (errno)); process_data->pid = forkpty (&process_data->fd, NULL, NULL, &win); if (process_data->pid < 0) { /* Failed to fork */ g_set_error (&process_data->error, RESTRAINT_PROCESS_ERROR, RESTRAINT_PROCESS_FORK_ERROR, "Failed to fork: %s", g_strerror (errno)); g_idle_add (process_pid_finish, process_data); return; } else if (process_data->pid == 0) { /* Child process. */ // Flush any input that hasn't been read if (fflush (stdin) != 0) g_warning ("Failed to flush stdin: %s\n", g_strerror (errno)); setbuf (stdout, NULL); setbuf (stderr, NULL); if (process_data->path && (chdir (process_data->path) == -1)) { /* command_path was supplied and we failed to chdir to it. */ g_warning ("Failed to chdir() to %s: %s\n", process_data->path, g_strerror (errno)); exit (1); } if (envp) environ = (gchar **) envp; // Print the command being executed. gchar *pcommand = g_strjoinv (" ", (gchar **) process_data->command); g_print ("%s\n", pcommand); g_free (pcommand); /* Spawn the command */ if (execvp (*process_data->command, (gchar **) process_data->command) == -1) { g_warning ("Failed to exec() %s, %s error:%s\n", *process_data->command, process_data->path, g_strerror (errno)); exit (1); } } /* Parent process. */ // If we get the cancel signal kill any running process if (process_data->cancellable) { process_data->cancel_handler = g_cancellable_connect (process_data->cancellable, G_CALLBACK(process_cancelled_cb), process_data, NULL); } // close file descriptors on exec. Should prevent leaking fd's to child processes. if (fcntl (process_data->fd, F_SETFD, FD_CLOEXEC) < 0) { g_warning("Failed to set close on exec"); } // Localwatchdog handler if (process_data->max_time != 0) { process_data->timeout_handler_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, process_data->max_time, process_timeout_callback, process_data, NULL); } // IO handler if (io_callback != NULL) { GIOChannel *io = g_io_channel_unix_new (process_data->fd); g_io_channel_set_flags (io, G_IO_FLAG_NONBLOCK, NULL); // Set Encoding to NULL to keep g_io_channel from trying to decode it. g_io_channel_set_encoding (io, NULL, NULL); // Disable Buffering g_io_channel_set_buffered (io, FALSE); process_data->io = io; process_data->io_handler_id = g_io_add_watch_full (io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_NVAL, process_io_cb, process_data, process_io_finish); } // Monitor pid for return code process_data->pid_handler_id = g_child_watch_add_full (G_PRIORITY_DEFAULT, process_data->pid, process_pid_callback, process_data, NULL); } void process_pid_callback (GPid pid, gint status, gpointer user_data) { ProcessData *process_data = (ProcessData *) user_data; process_data->pid_result = status; process_data->pid = 0; if (process_data->fd != -1 ) { close (process_data->fd); process_data->fd = -1; } if (process_data->finish_handler_id == 0) { process_data->finish_handler_id = g_idle_add (process_pid_finish, process_data); } }