summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2021-05-07 23:08:27 +0200
committerThomas Haller <thaller@redhat.com>2021-05-14 11:41:32 +0200
commit071ef784cfb05abbda6c811000ed4cd93026e6a9 (patch)
tree7fcab08e281a4a454144033ec157c4430238c94f
parent62027350f7e29447ee7976785ccf56f918a635cc (diff)
downloadNetworkManager-071ef784cfb05abbda6c811000ed4cd93026e6a9.tar.gz
glib-aux: add nm_g_subprocess_terminate_in_background() helper
-rw-r--r--src/libnm-glib-aux/nm-io-utils.c73
-rw-r--r--src/libnm-glib-aux/nm-io-utils.h2
2 files changed, 75 insertions, 0 deletions
diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c
index 0176abf7f8..894c8726c0 100644
--- a/src/libnm-glib-aux/nm-io-utils.c
+++ b/src/libnm-glib-aux/nm-io-utils.c
@@ -491,3 +491,76 @@ nm_utils_fd_read(int fd, NMStrBuf *out_string)
return n_read;
}
+
+/*****************************************************************************/
+
+typedef struct {
+ GSubprocess *subprocess;
+ GSource * timeout_source;
+} SubprocessTerminateData;
+
+static void
+_subprocess_terminate_wait_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ SubprocessTerminateData *term_data = user_data;
+
+ g_subprocess_wait_finish(G_SUBPROCESS(source), result, NULL);
+
+ nm_clear_g_source_inst(&term_data->timeout_source);
+ g_object_unref(term_data->subprocess);
+ nm_g_slice_free(term_data);
+}
+
+static gboolean
+_subprocess_terminate_timeout_cb(gpointer user_data)
+{
+ SubprocessTerminateData *term_data = user_data;
+
+ nm_clear_g_source_inst(&term_data->timeout_source);
+ g_subprocess_send_signal(term_data->subprocess, SIGKILL);
+ return G_SOURCE_REMOVE;
+}
+
+void
+nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill)
+{
+ SubprocessTerminateData *term_data;
+ GMainContext * main_context;
+
+ nm_assert(timeout_msec_before_kill > 0);
+
+ /* The GSubprocess stays alive until the child is reaped (an internal reference is held).
+ *
+ * This function first sends SIGTERM to the process right away, and after a
+ * timeout "timeout_msec_before_kill" send a SIGKILL.
+ *
+ * Otherwise, it does nothing, it does not log, there is no notification when the process
+ * completes and there is no way to abort the thing.
+ *
+ * It honors the current g_main_context_get_thread_default(). */
+
+ if (!subprocess)
+ return;
+
+ g_return_if_fail(G_IS_SUBPROCESS(subprocess));
+
+ main_context = g_main_context_get_thread_default();
+
+ term_data = g_slice_new(SubprocessTerminateData);
+ *term_data = (SubprocessTerminateData){
+ .subprocess = g_object_ref(subprocess),
+ .timeout_source = NULL,
+ };
+
+ g_subprocess_send_signal(subprocess, SIGTERM);
+
+ g_subprocess_wait_async(subprocess, NULL, _subprocess_terminate_wait_cb, term_data);
+
+ term_data->timeout_source =
+ nm_g_source_attach(nm_g_timeout_source_new(timeout_msec_before_kill,
+ G_PRIORITY_DEFAULT,
+ _subprocess_terminate_timeout_cb,
+ term_data,
+ NULL),
+ main_context);
+}
diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h
index 2b132ff07d..98e63ac01e 100644
--- a/src/libnm-glib-aux/nm-io-utils.h
+++ b/src/libnm-glib-aux/nm-io-utils.h
@@ -56,4 +56,6 @@ struct stat;
int nm_utils_file_stat(const char *filename, struct stat *out_st);
+void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill);
+
#endif /* __NM_IO_UTILS_H__ */