mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-09 05:58:01 +02:00
nm-sudo,dispatcher: merge branch 'th/nm-sudo-cleanup'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/951
This commit is contained in:
commit
ec126740ce
4 changed files with 281 additions and 164 deletions
|
|
@ -6548,3 +6548,33 @@ nm_utils_thread_local_register_destroy(gpointer tls_data, GDestroyNotify destroy
|
||||||
entry->destroy_notify = destroy_notify;
|
entry->destroy_notify = destroy_notify;
|
||||||
c_list_link_tail(lst_head, &entry->lst);
|
c_list_link_tail(lst_head, &entry->lst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_iterate_for_msec_timeout(gpointer user_data)
|
||||||
|
{
|
||||||
|
GSource **p_source = user_data;
|
||||||
|
|
||||||
|
nm_clear_g_source_inst(p_source);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nm_g_main_context_iterate_for_msec(GMainContext *context, guint timeout_msec)
|
||||||
|
{
|
||||||
|
GSource *source;
|
||||||
|
|
||||||
|
/* In production is this function not very useful. It is however useful to
|
||||||
|
* have in the toolbox for printf debugging. */
|
||||||
|
|
||||||
|
source = g_timeout_source_new(timeout_msec);
|
||||||
|
g_source_set_callback(source, _iterate_for_msec_timeout, &source, NULL);
|
||||||
|
|
||||||
|
if (!context)
|
||||||
|
context = g_main_context_default();
|
||||||
|
|
||||||
|
g_source_attach(source, context);
|
||||||
|
while (source)
|
||||||
|
g_main_context_iteration(context, TRUE);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1901,6 +1901,8 @@ nm_g_main_context_iterate_ready(GMainContext *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nm_g_main_context_iterate_for_msec(GMainContext *context, guint timeout_msec);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,15 @@
|
||||||
|
|
||||||
#include "libnm-client-aux-extern/nm-default-client.h"
|
#include "libnm-client-aux-extern/nm-default-client.h"
|
||||||
|
|
||||||
#include <syslog.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "libnm-core-aux-extern/nm-dispatcher-api.h"
|
#include "libnm-core-aux-extern/nm-dispatcher-api.h"
|
||||||
#include "libnm-glib-aux/nm-dbus-aux.h"
|
#include "libnm-glib-aux/nm-dbus-aux.h"
|
||||||
|
|
@ -33,29 +33,36 @@
|
||||||
|
|
||||||
typedef struct Request Request;
|
typedef struct Request Request;
|
||||||
|
|
||||||
static struct {
|
typedef struct {
|
||||||
GDBusConnection *dbus_connection;
|
GDBusConnection *dbus_connection;
|
||||||
GCancellable * quit_cancellable;
|
GCancellable * quit_cancellable;
|
||||||
bool log_verbose;
|
|
||||||
bool log_stdout;
|
bool log_verbose;
|
||||||
gboolean persist;
|
bool log_stdout;
|
||||||
GSource * quit_source;
|
|
||||||
guint request_id_counter;
|
GSource *source_idle_timeout;
|
||||||
guint dbus_regist_id;
|
|
||||||
|
|
||||||
gint64 start_timestamp_msec;
|
gint64 start_timestamp_msec;
|
||||||
|
|
||||||
bool name_requested;
|
guint request_id_counter;
|
||||||
|
guint service_regist_id;
|
||||||
|
|
||||||
bool exit_with_failure;
|
gboolean persist;
|
||||||
|
|
||||||
bool shutdown_timeout;
|
|
||||||
bool shutdown_quitting;
|
|
||||||
|
|
||||||
Request *current_request;
|
Request *current_request;
|
||||||
GQueue * requests_waiting;
|
GQueue * requests_waiting;
|
||||||
int num_requests_pending;
|
int num_requests_pending;
|
||||||
} gl;
|
|
||||||
|
bool exit_with_failure;
|
||||||
|
|
||||||
|
bool name_requested;
|
||||||
|
bool reject_new_requests;
|
||||||
|
|
||||||
|
bool shutdown_timeout;
|
||||||
|
bool shutdown_quitting;
|
||||||
|
} GlobalData;
|
||||||
|
|
||||||
|
GlobalData gl;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Request *request;
|
Request *request;
|
||||||
|
|
@ -204,18 +211,20 @@ request_free(Request *request)
|
||||||
g_slice_free(Request, request);
|
g_slice_free(Request, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
quit_timeout_cb(gpointer user_data)
|
_idle_timeout_cb(gpointer user_data)
|
||||||
{
|
{
|
||||||
nm_clear_g_source_inst(&gl.quit_source);
|
nm_clear_g_source_inst(&gl.source_idle_timeout);
|
||||||
gl.shutdown_timeout = TRUE;
|
gl.shutdown_timeout = TRUE;
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
quit_timeout_reschedule(void)
|
_idle_timeout_restart(void)
|
||||||
{
|
{
|
||||||
nm_clear_g_source_inst(&gl.quit_source);
|
nm_clear_g_source_inst(&gl.source_idle_timeout);
|
||||||
|
|
||||||
if (gl.persist)
|
if (gl.persist)
|
||||||
return;
|
return;
|
||||||
|
|
@ -226,9 +235,11 @@ quit_timeout_reschedule(void)
|
||||||
if (gl.num_requests_pending > 0)
|
if (gl.num_requests_pending > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gl.quit_source = nm_g_timeout_add_source(10000, quit_timeout_cb, NULL);
|
gl.source_idle_timeout = nm_g_timeout_add_source(10000, _idle_timeout_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* next_request:
|
* next_request:
|
||||||
*
|
*
|
||||||
|
|
@ -276,7 +287,7 @@ next_request(Request *request)
|
||||||
* Checks if all the scripts for the request have terminated and in such case
|
* Checks if all the scripts for the request have terminated and in such case
|
||||||
* it sends the D-Bus response and releases the request resources.
|
* it sends the D-Bus response and releases the request resources.
|
||||||
*
|
*
|
||||||
* It also decreases @num_requests_pending and possibly does quit_timeout_reschedule().
|
* It also decreases @num_requests_pending and possibly does _idle_timeout_restart().
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
complete_request(Request *request)
|
complete_request(Request *request)
|
||||||
|
|
@ -315,7 +326,7 @@ complete_request(Request *request)
|
||||||
nm_assert(gl.num_requests_pending > 0);
|
nm_assert(gl.num_requests_pending > 0);
|
||||||
if (--gl.num_requests_pending <= 0) {
|
if (--gl.num_requests_pending <= 0) {
|
||||||
nm_assert(!gl.current_request && !g_queue_peek_head(gl.requests_waiting));
|
nm_assert(!gl.current_request && !g_queue_peek_head(gl.requests_waiting));
|
||||||
quit_timeout_reschedule();
|
_idle_timeout_restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -693,7 +704,7 @@ script_must_wait(const char *path)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_method_call_action(GDBusMethodInvocation *invocation, GVariant *parameters)
|
_handle_action(GDBusMethodInvocation *invocation, GVariant *parameters)
|
||||||
{
|
{
|
||||||
const char * action;
|
const char * action;
|
||||||
gs_unref_variant GVariant *connection = NULL;
|
gs_unref_variant GVariant *connection = NULL;
|
||||||
|
|
@ -811,7 +822,7 @@ _method_call_action(GDBusMethodInvocation *invocation, GVariant *parameters)
|
||||||
|
|
||||||
gl.num_requests_pending++;
|
gl.num_requests_pending++;
|
||||||
gl.shutdown_timeout = FALSE;
|
gl.shutdown_timeout = FALSE;
|
||||||
nm_clear_g_source_inst(&gl.quit_source);
|
nm_clear_g_source_inst(&gl.source_idle_timeout);
|
||||||
|
|
||||||
for (i = 0; i < request->scripts->len; i++) {
|
for (i = 0; i < request->scripts->len; i++) {
|
||||||
ScriptInfo *s = g_ptr_array_index(request->scripts, i);
|
ScriptInfo *s = g_ptr_array_index(request->scripts, i);
|
||||||
|
|
@ -857,7 +868,7 @@ _method_call_action(GDBusMethodInvocation *invocation, GVariant *parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_method_call_ping(GDBusMethodInvocation *invocation, GVariant *parameters)
|
_handle_ping(GDBusMethodInvocation *invocation, GVariant *parameters)
|
||||||
{
|
{
|
||||||
gs_free char *msg = NULL;
|
gs_free char *msg = NULL;
|
||||||
gint64 running_msec;
|
gint64 running_msec;
|
||||||
|
|
@ -877,22 +888,29 @@ _method_call_ping(GDBusMethodInvocation *invocation, GVariant *parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_method_call(GDBusConnection * connection,
|
_bus_method_call(GDBusConnection * connection,
|
||||||
const char * sender,
|
const char * sender,
|
||||||
const char * object_path,
|
const char * object_path,
|
||||||
const char * interface_name,
|
const char * interface_name,
|
||||||
const char * method_name,
|
const char * method_name,
|
||||||
GVariant * parameters,
|
GVariant * parameters,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
if (gl.reject_new_requests) {
|
||||||
|
g_dbus_method_invocation_return_error(invocation,
|
||||||
|
G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_NO_SERVER,
|
||||||
|
"Server is exiting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (nm_streq(interface_name, NM_DISPATCHER_DBUS_INTERFACE)) {
|
if (nm_streq(interface_name, NM_DISPATCHER_DBUS_INTERFACE)) {
|
||||||
if (nm_streq(method_name, "Action")) {
|
if (nm_streq(method_name, "Action")) {
|
||||||
_method_call_action(invocation, parameters);
|
_handle_action(invocation, parameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nm_streq(method_name, "Ping")) {
|
if (nm_streq(method_name, "Ping")) {
|
||||||
_method_call_ping(invocation, parameters);
|
_handle_ping(invocation, parameters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -935,7 +953,7 @@ static gboolean
|
||||||
_bus_register_service(void)
|
_bus_register_service(void)
|
||||||
{
|
{
|
||||||
static const GDBusInterfaceVTable interface_vtable = {
|
static const GDBusInterfaceVTable interface_vtable = {
|
||||||
.method_call = _method_call,
|
.method_call = _bus_method_call,
|
||||||
};
|
};
|
||||||
gs_free_error GError * error = NULL;
|
gs_free_error GError * error = NULL;
|
||||||
NMDBusConnectionCallBlockingData data = {
|
NMDBusConnectionCallBlockingData data = {
|
||||||
|
|
@ -944,7 +962,7 @@ _bus_register_service(void)
|
||||||
gs_unref_variant GVariant *ret = NULL;
|
gs_unref_variant GVariant *ret = NULL;
|
||||||
guint32 ret_val;
|
guint32 ret_val;
|
||||||
|
|
||||||
gl.dbus_regist_id =
|
gl.service_regist_id =
|
||||||
g_dbus_connection_register_object(gl.dbus_connection,
|
g_dbus_connection_register_object(gl.dbus_connection,
|
||||||
NM_DISPATCHER_DBUS_PATH,
|
NM_DISPATCHER_DBUS_PATH,
|
||||||
interface_info,
|
interface_info,
|
||||||
|
|
@ -952,7 +970,7 @@ _bus_register_service(void)
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
&error);
|
&error);
|
||||||
if (gl.dbus_regist_id == 0) {
|
if (gl.service_regist_id == 0) {
|
||||||
_LOG_X_W("dbus: could not export dispatcher D-Bus interface %s: %s",
|
_LOG_X_W("dbus: could not export dispatcher D-Bus interface %s: %s",
|
||||||
NM_DISPATCHER_DBUS_PATH,
|
NM_DISPATCHER_DBUS_PATH,
|
||||||
error->message);
|
error->message);
|
||||||
|
|
@ -1050,7 +1068,7 @@ logging_shutdown(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
signal_handler(gpointer user_data)
|
_signal_callback_term(gpointer user_data)
|
||||||
{
|
{
|
||||||
if (!gl.shutdown_quitting) {
|
if (!gl.shutdown_quitting) {
|
||||||
gl.shutdown_quitting = TRUE;
|
gl.shutdown_quitting = TRUE;
|
||||||
|
|
@ -1060,16 +1078,62 @@ signal_handler(gpointer user_data)
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_bus_release_name_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
_bus_release_name_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
{
|
{
|
||||||
nm_assert(gl.num_requests_pending > 0);
|
nm_assert(gl.num_requests_pending > 0);
|
||||||
|
gl.reject_new_requests = TRUE;
|
||||||
gl.num_requests_pending--;
|
gl.num_requests_pending--;
|
||||||
g_main_context_wakeup(NULL);
|
g_main_context_wakeup(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
parse_command_line(int *p_argc, char ***p_argv, GError **error)
|
_bus_release_name(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* We already requested a name. To exit-on-idle without race, we need to dance.
|
||||||
|
* See https://lists.freedesktop.org/archives/dbus/2015-May/016671.html . */
|
||||||
|
|
||||||
|
if (!gl.name_requested)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gl.name_requested = FALSE;
|
||||||
|
gl.shutdown_quitting = TRUE;
|
||||||
|
|
||||||
|
_LOG_X_T("shutdown: release-name");
|
||||||
|
|
||||||
|
/* we create a fake pending request. */
|
||||||
|
gl.num_requests_pending++;
|
||||||
|
nm_clear_g_source_inst(&gl.source_idle_timeout);
|
||||||
|
|
||||||
|
r = nm_sd_notify("STOPPING=1");
|
||||||
|
if (r < 0)
|
||||||
|
_LOG_X_W("shutdown: sd_notifiy(STOPPING=1) failed: %s", nm_strerror_native(-r));
|
||||||
|
else
|
||||||
|
_LOG_X_T("shutdown: sd_notifiy(STOPPING=1) succeeded");
|
||||||
|
|
||||||
|
g_dbus_connection_call(gl.dbus_connection,
|
||||||
|
DBUS_SERVICE_DBUS,
|
||||||
|
DBUS_PATH_DBUS,
|
||||||
|
DBUS_INTERFACE_DBUS,
|
||||||
|
"ReleaseName",
|
||||||
|
g_variant_new("(s)", NM_DISPATCHER_DBUS_SERVICE),
|
||||||
|
G_VARIANT_TYPE("(u)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
10000,
|
||||||
|
NULL,
|
||||||
|
_bus_release_name_cb,
|
||||||
|
NULL);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_initial_setup(int *p_argc, char ***p_argv, GError **error)
|
||||||
{
|
{
|
||||||
GOptionContext *opt_ctx;
|
GOptionContext *opt_ctx;
|
||||||
gboolean arg_debug = FALSE;
|
gboolean arg_debug = FALSE;
|
||||||
|
|
@ -1115,6 +1179,8 @@ parse_command_line(int *p_argc, char ***p_argv, GError **error)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -1123,14 +1189,16 @@ main(int argc, char **argv)
|
||||||
GSource * source_int = NULL;
|
GSource * source_int = NULL;
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
source_term = nm_g_unix_signal_add_source(SIGTERM, signal_handler, GINT_TO_POINTER(SIGTERM));
|
source_term =
|
||||||
source_int = nm_g_unix_signal_add_source(SIGINT, signal_handler, GINT_TO_POINTER(SIGINT));
|
nm_g_unix_signal_add_source(SIGTERM, _signal_callback_term, GINT_TO_POINTER(SIGTERM));
|
||||||
|
source_int =
|
||||||
|
nm_g_unix_signal_add_source(SIGINT, _signal_callback_term, GINT_TO_POINTER(SIGINT));
|
||||||
|
|
||||||
gl.start_timestamp_msec = nm_utils_clock_gettime_msec(CLOCK_BOOTTIME);
|
gl.start_timestamp_msec = nm_utils_clock_gettime_msec(CLOCK_BOOTTIME);
|
||||||
|
|
||||||
gl.quit_cancellable = g_cancellable_new();
|
gl.quit_cancellable = g_cancellable_new();
|
||||||
|
|
||||||
if (!parse_command_line(&argc, &argv, &error)) {
|
if (!_initial_setup(&argc, &argv, &error)) {
|
||||||
_LOG_X_W("Error parsing command line arguments: %s", error->message);
|
_LOG_X_W("Error parsing command line arguments: %s", error->message);
|
||||||
gl.exit_with_failure = TRUE;
|
gl.exit_with_failure = TRUE;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1164,7 +1232,7 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
gl.requests_waiting = g_queue_new();
|
gl.requests_waiting = g_queue_new();
|
||||||
|
|
||||||
quit_timeout_reschedule();
|
_idle_timeout_restart();
|
||||||
|
|
||||||
if (!_bus_register_service()) {
|
if (!_bus_register_service()) {
|
||||||
/* we failed to start the D-Bus service, and will shut down. However,
|
/* we failed to start the D-Bus service, and will shut down. However,
|
||||||
|
|
@ -1173,68 +1241,39 @@ main(int argc, char **argv)
|
||||||
if (!g_cancellable_is_cancelled(gl.quit_cancellable))
|
if (!g_cancellable_is_cancelled(gl.quit_cancellable))
|
||||||
gl.exit_with_failure = TRUE;
|
gl.exit_with_failure = TRUE;
|
||||||
gl.shutdown_quitting = TRUE;
|
gl.shutdown_quitting = TRUE;
|
||||||
|
|
||||||
|
if (!gl.name_requested)
|
||||||
|
gl.reject_new_requests = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
|
if (gl.shutdown_quitting)
|
||||||
|
_bus_release_name();
|
||||||
|
|
||||||
if (gl.num_requests_pending > 0) {
|
if (gl.num_requests_pending > 0) {
|
||||||
/* while we have requests pending, we cannot stop processing them... */
|
/* while we have requests pending, we cannot stop processing them... */
|
||||||
} else if (gl.shutdown_timeout || gl.shutdown_quitting) {
|
} else if (gl.shutdown_timeout || gl.shutdown_quitting) {
|
||||||
if (gl.name_requested) {
|
if (!_bus_release_name())
|
||||||
int r;
|
break;
|
||||||
|
|
||||||
/* We already requested a name. To exit-on-idle without race, we need to dance.
|
|
||||||
* See https://lists.freedesktop.org/archives/dbus/2015-May/016671.html . */
|
|
||||||
|
|
||||||
gl.name_requested = FALSE;
|
|
||||||
gl.shutdown_quitting = TRUE;
|
|
||||||
|
|
||||||
_LOG_X_T("shutdown: release-name");
|
|
||||||
|
|
||||||
/* we create a fake pending request. */
|
|
||||||
gl.num_requests_pending++;
|
|
||||||
nm_clear_g_source_inst(&gl.quit_source);
|
|
||||||
|
|
||||||
r = nm_sd_notify("STOPPING=1");
|
|
||||||
if (r < 0)
|
|
||||||
_LOG_X_W("shutdown: sd_notifiy(STOPPING=1) failed: %s", nm_strerror_native(-r));
|
|
||||||
else
|
|
||||||
_LOG_X_T("shutdown: sd_notifiy(STOPPING=1) succeeded");
|
|
||||||
|
|
||||||
g_dbus_connection_call(gl.dbus_connection,
|
|
||||||
DBUS_SERVICE_DBUS,
|
|
||||||
DBUS_PATH_DBUS,
|
|
||||||
DBUS_INTERFACE_DBUS,
|
|
||||||
"ReleaseName",
|
|
||||||
g_variant_new("(s)", NM_DISPATCHER_DBUS_SERVICE),
|
|
||||||
G_VARIANT_TYPE("(u)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
10000,
|
|
||||||
NULL,
|
|
||||||
_bus_release_name_cb,
|
|
||||||
NULL);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_main_context_iteration(NULL, TRUE);
|
g_main_context_iteration(NULL, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
nm_g_main_context_iterate_ready(NULL);
|
|
||||||
|
|
||||||
gl.shutdown_quitting = TRUE;
|
gl.shutdown_quitting = TRUE;
|
||||||
g_cancellable_cancel(gl.quit_cancellable);
|
g_cancellable_cancel(gl.quit_cancellable);
|
||||||
|
|
||||||
nm_assert(gl.num_requests_pending == 0);
|
nm_assert(gl.num_requests_pending == 0);
|
||||||
|
|
||||||
if (gl.dbus_regist_id != 0)
|
if (gl.service_regist_id != 0) {
|
||||||
g_dbus_connection_unregister_object(gl.dbus_connection, nm_steal_int(&gl.dbus_regist_id));
|
g_dbus_connection_unregister_object(gl.dbus_connection,
|
||||||
|
nm_steal_int(&gl.service_regist_id));
|
||||||
|
}
|
||||||
|
|
||||||
nm_clear_pointer(&gl.requests_waiting, g_queue_free);
|
nm_clear_pointer(&gl.requests_waiting, g_queue_free);
|
||||||
|
|
||||||
nm_clear_g_source_inst(&gl.quit_source);
|
nm_clear_g_source_inst(&gl.source_idle_timeout);
|
||||||
|
|
||||||
if (gl.dbus_connection) {
|
if (gl.dbus_connection) {
|
||||||
g_dbus_connection_flush_sync(gl.dbus_connection, NULL, NULL);
|
g_dbus_connection_flush_sync(gl.dbus_connection, NULL, NULL);
|
||||||
|
|
@ -1250,7 +1289,6 @@ done:
|
||||||
|
|
||||||
nm_clear_g_source_inst(&source_term);
|
nm_clear_g_source_inst(&source_term);
|
||||||
nm_clear_g_source_inst(&source_int);
|
nm_clear_g_source_inst(&source_int);
|
||||||
|
|
||||||
g_clear_object(&gl.quit_cancellable);
|
g_clear_object(&gl.quit_cancellable);
|
||||||
|
|
||||||
return gl.exit_with_failure ? 1 : 0;
|
return gl.exit_with_failure ? 1 : 0;
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
#include <gio/gunixfdlist.h>
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
#include "c-list/src/c-list.h"
|
#include "c-list/src/c-list.h"
|
||||||
|
#include "libnm-base/nm-sudo-utils.h"
|
||||||
#include "libnm-glib-aux/nm-dbus-aux.h"
|
#include "libnm-glib-aux/nm-dbus-aux.h"
|
||||||
#include "libnm-glib-aux/nm-io-utils.h"
|
#include "libnm-glib-aux/nm-io-utils.h"
|
||||||
#include "libnm-glib-aux/nm-logging-base.h"
|
#include "libnm-glib-aux/nm-logging-base.h"
|
||||||
#include "libnm-glib-aux/nm-shared-utils.h"
|
#include "libnm-glib-aux/nm-shared-utils.h"
|
||||||
#include "libnm-glib-aux/nm-time-utils.h"
|
#include "libnm-glib-aux/nm-time-utils.h"
|
||||||
#include "libnm-base/nm-sudo-utils.h"
|
|
||||||
|
|
||||||
/* nm-sudo doesn't link with libnm-core nor libnm-base, but these headers
|
/* nm-sudo doesn't link with libnm-core nor libnm-base, but these headers
|
||||||
* can be used independently. */
|
* can be used independently. */
|
||||||
|
|
@ -37,28 +37,35 @@ typedef struct {
|
||||||
} PendingJobData;
|
} PendingJobData;
|
||||||
|
|
||||||
struct _GlobalData {
|
struct _GlobalData {
|
||||||
GCancellable * quit_cancellable;
|
|
||||||
GDBusConnection *dbus_connection;
|
GDBusConnection *dbus_connection;
|
||||||
GSource * source_sigterm;
|
GCancellable * quit_cancellable;
|
||||||
|
|
||||||
|
GSource *source_sigterm;
|
||||||
|
|
||||||
CList pending_jobs_lst_head;
|
CList pending_jobs_lst_head;
|
||||||
|
|
||||||
GSource *source_idle_timeout;
|
GSource *source_idle_timeout;
|
||||||
char * name_owner;
|
|
||||||
guint name_owner_changed_id;
|
char *name_owner;
|
||||||
guint service_regist_id;
|
|
||||||
gint64 start_timestamp_msec;
|
gint64 start_timestamp_msec;
|
||||||
guint32 timeout_msec;
|
|
||||||
bool name_owner_initialized;
|
guint name_owner_changed_id;
|
||||||
bool name_requested;
|
guint service_regist_id;
|
||||||
|
|
||||||
|
guint32 timeout_msec;
|
||||||
|
|
||||||
|
bool name_owner_initialized;
|
||||||
|
|
||||||
/* This is controlled by $NM_SUDO_NO_AUTH_FOR_TESTING. It disables authentication
|
/* This is controlled by $NM_SUDO_NO_AUTH_FOR_TESTING. It disables authentication
|
||||||
* of the request, so it is ONLY for testing. */
|
* of the request, so it is ONLY for testing. */
|
||||||
bool no_auth_for_testing;
|
bool no_auth_for_testing;
|
||||||
|
|
||||||
bool is_shutting_down_quitting;
|
bool name_requested;
|
||||||
bool is_shutting_down_timeout;
|
bool reject_new_requests;
|
||||||
bool is_shutting_down_cleanup;
|
|
||||||
|
bool shutdown_quitting;
|
||||||
|
bool shutdown_timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
@ -134,7 +141,7 @@ _signal_callback_term(gpointer user_data)
|
||||||
_LOGD("sigterm received (%s)",
|
_LOGD("sigterm received (%s)",
|
||||||
c_list_is_empty(&gl->pending_jobs_lst_head) ? "quit mainloop" : "cancel operations");
|
c_list_is_empty(&gl->pending_jobs_lst_head) ? "quit mainloop" : "cancel operations");
|
||||||
|
|
||||||
gl->is_shutting_down_quitting = TRUE;
|
gl->shutdown_quitting = TRUE;
|
||||||
g_cancellable_cancel(gl->quit_cancellable);
|
g_cancellable_cancel(gl->quit_cancellable);
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +290,22 @@ _bus_method_call(GDBusConnection * connection,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_pending_job_register_object(gl, G_OBJECT(invocation));
|
if (gl->reject_new_requests) {
|
||||||
|
/* after the name was released, we must not accept new requests. This new
|
||||||
|
* request was probably targeted against the unique-name. But we already
|
||||||
|
* gave up the well-known name. If we'd accept new request now, they would
|
||||||
|
* keep the service running indefinitely (and thus preventing the service
|
||||||
|
* to restart and serve the well-known name. */
|
||||||
|
_LOGT("dbus: request sender=%s, %s%s, SERVER SHUTTING DOWN",
|
||||||
|
sender,
|
||||||
|
method_name,
|
||||||
|
g_variant_get_type_string(parameters));
|
||||||
|
g_dbus_method_invocation_return_error(invocation,
|
||||||
|
G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_NO_SERVER,
|
||||||
|
"Server is exiting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_LOGT("dbus: request sender=%s, %s%s",
|
_LOGT("dbus: request sender=%s, %s%s",
|
||||||
sender,
|
sender,
|
||||||
|
|
@ -399,7 +421,8 @@ _idle_timeout_cb(gpointer user_data)
|
||||||
GlobalData *gl = user_data;
|
GlobalData *gl = user_data;
|
||||||
|
|
||||||
_LOGT("idle-timeout: expired");
|
_LOGT("idle-timeout: expired");
|
||||||
gl->is_shutting_down_timeout = TRUE;
|
nm_clear_g_source_inst(&gl->source_idle_timeout);
|
||||||
|
gl->shutdown_timeout = TRUE;
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,10 +431,7 @@ _idle_timeout_restart(GlobalData *gl)
|
||||||
{
|
{
|
||||||
nm_clear_g_source_inst(&gl->source_idle_timeout);
|
nm_clear_g_source_inst(&gl->source_idle_timeout);
|
||||||
|
|
||||||
if (gl->is_shutting_down_quitting)
|
if (gl->shutdown_quitting)
|
||||||
return;
|
|
||||||
|
|
||||||
if (gl->is_shutting_down_cleanup)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!c_list_is_empty(&gl->pending_jobs_lst_head))
|
if (!c_list_is_empty(&gl->pending_jobs_lst_head))
|
||||||
|
|
@ -457,7 +477,7 @@ _pending_job_register_object(GlobalData *gl, GObject *obj)
|
||||||
PendingJobData *idle_data;
|
PendingJobData *idle_data;
|
||||||
|
|
||||||
/* if we just hit the timeout, we can ignore it. */
|
/* if we just hit the timeout, we can ignore it. */
|
||||||
gl->is_shutting_down_timeout = FALSE;
|
gl->shutdown_timeout = FALSE;
|
||||||
|
|
||||||
if (nm_clear_g_source_inst(&gl->source_idle_timeout))
|
if (nm_clear_g_source_inst(&gl->source_idle_timeout))
|
||||||
_LOGT("idle-timeout: suspend timeout for pending request");
|
_LOGT("idle-timeout: suspend timeout for pending request");
|
||||||
|
|
@ -475,11 +495,59 @@ _pending_job_register_object(GlobalData *gl, GObject *obj)
|
||||||
static void
|
static void
|
||||||
_bus_release_name_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
_bus_release_name_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
{
|
{
|
||||||
_nm_unused gs_unref_object GObject *keep_alive_object = user_data;
|
_nm_unused gs_unref_object GObject *keep_alive_object = NULL;
|
||||||
|
GlobalData * gl;
|
||||||
|
|
||||||
|
nm_utils_user_data_unpack(user_data, &gl, &keep_alive_object);
|
||||||
|
|
||||||
|
gl->reject_new_requests = TRUE;
|
||||||
g_main_context_wakeup(NULL);
|
g_main_context_wakeup(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_bus_release_name(GlobalData *gl)
|
||||||
|
{
|
||||||
|
gs_unref_object GObject *keep_alive_object = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* We already requested a name. To exit-on-idle without race, we need to dance.
|
||||||
|
* See https://lists.freedesktop.org/archives/dbus/2015-May/016671.html . */
|
||||||
|
|
||||||
|
if (!gl->name_requested)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gl->name_requested = FALSE;
|
||||||
|
gl->shutdown_quitting = TRUE;
|
||||||
|
|
||||||
|
_LOGT("shutdown: release-name");
|
||||||
|
|
||||||
|
keep_alive_object = g_object_new(G_TYPE_OBJECT, NULL);
|
||||||
|
|
||||||
|
/* we use the _pending_job_register_object() mechanism to make the loop busy during
|
||||||
|
* shutdown. */
|
||||||
|
_pending_job_register_object(gl, keep_alive_object);
|
||||||
|
|
||||||
|
r = nm_sd_notify("STOPPING=1");
|
||||||
|
if (r < 0)
|
||||||
|
_LOGW("shutdown: sd_notifiy(STOPPING=1) failed: %s", nm_strerror_native(-r));
|
||||||
|
else
|
||||||
|
_LOGT("shutdown: sd_notifiy(STOPPING=1) succeeded");
|
||||||
|
|
||||||
|
g_dbus_connection_call(gl->dbus_connection,
|
||||||
|
DBUS_SERVICE_DBUS,
|
||||||
|
DBUS_PATH_DBUS,
|
||||||
|
DBUS_INTERFACE_DBUS,
|
||||||
|
"ReleaseName",
|
||||||
|
g_variant_new("(s)", NM_SUDO_DBUS_BUS_NAME),
|
||||||
|
G_VARIANT_TYPE("(u)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
10000,
|
||||||
|
NULL,
|
||||||
|
_bus_release_name_cb,
|
||||||
|
nm_utils_user_data_pack(gl, g_steal_pointer(&keep_alive_object)));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -499,6 +567,8 @@ _initial_setup(GlobalData *gl)
|
||||||
gl->source_sigterm = nm_g_unix_signal_add_source(SIGTERM, _signal_callback_term, gl);
|
gl->source_sigterm = nm_g_unix_signal_add_source(SIGTERM, _signal_callback_term, gl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -553,55 +623,32 @@ main(int argc, char **argv)
|
||||||
* Let's fake a shutdown signal, and still process the request below. */
|
* Let's fake a shutdown signal, and still process the request below. */
|
||||||
if (!g_cancellable_is_cancelled(gl->quit_cancellable))
|
if (!g_cancellable_is_cancelled(gl->quit_cancellable))
|
||||||
exit_code = EXIT_FAILURE;
|
exit_code = EXIT_FAILURE;
|
||||||
gl->is_shutting_down_quitting = TRUE;
|
gl->shutdown_quitting = TRUE;
|
||||||
|
|
||||||
|
if (gl->name_requested) {
|
||||||
|
/* We requested a name, but something went wrong. Below we will release
|
||||||
|
* the name right away. */
|
||||||
|
} else {
|
||||||
|
/* In case we didn't even went as far to request the name. New requests
|
||||||
|
* can only come via the unique name, and as we are shutting down, they
|
||||||
|
* are rejected. */
|
||||||
|
gl->reject_new_requests = TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
|
if (gl->shutdown_quitting)
|
||||||
|
_bus_release_name(gl);
|
||||||
|
|
||||||
if (!c_list_is_empty(&gl->pending_jobs_lst_head)) {
|
if (!c_list_is_empty(&gl->pending_jobs_lst_head)) {
|
||||||
/* we must first reply to all requests. No matter what. */
|
/* we must first reply to all requests. No matter what. */
|
||||||
} else if (gl->is_shutting_down_quitting || gl->is_shutting_down_timeout) {
|
} else if (gl->shutdown_quitting || gl->shutdown_timeout) {
|
||||||
/* we either hit the idle timeout or received SIGTERM. Note that
|
/* we either hit the idle timeout or received SIGTERM. Note that
|
||||||
* if we received an idle-timeout and the very moment afterwards
|
* if we received an idle-timeout and the very moment afterwards
|
||||||
* a new request, then _bus_method_call() will clear gl->is_shutting_down_timeout
|
* a new request, then _bus_method_call() will clear gl->shutdown_timeout
|
||||||
* (via _pending_job_register_object()). */
|
* (via _pending_job_register_object()). */
|
||||||
|
if (!_bus_release_name(gl))
|
||||||
if (gl->name_requested) {
|
break;
|
||||||
gs_unref_object GObject *keep_alive_object = g_object_new(G_TYPE_OBJECT, NULL);
|
|
||||||
|
|
||||||
/* We already requested a name. To exit-on-idle without race, we need to dance.
|
|
||||||
* See https://lists.freedesktop.org/archives/dbus/2015-May/016671.html . */
|
|
||||||
|
|
||||||
gl->name_requested = FALSE;
|
|
||||||
gl->is_shutting_down_quitting = TRUE;
|
|
||||||
|
|
||||||
_LOGT("shutdown: release-name");
|
|
||||||
|
|
||||||
/* we use the _pending_job_register_object() mechanism to make the loop busy during
|
|
||||||
* shutdown. */
|
|
||||||
_pending_job_register_object(gl, keep_alive_object);
|
|
||||||
|
|
||||||
r = nm_sd_notify("STOPPING=1");
|
|
||||||
if (r < 0)
|
|
||||||
_LOGW("shutdown: sd_notifiy(STOPPING=1) failed: %s", nm_strerror_native(-r));
|
|
||||||
else
|
|
||||||
_LOGT("shutdown: sd_notifiy(STOPPING=1) succeeded");
|
|
||||||
|
|
||||||
g_dbus_connection_call(gl->dbus_connection,
|
|
||||||
DBUS_SERVICE_DBUS,
|
|
||||||
DBUS_PATH_DBUS,
|
|
||||||
DBUS_INTERFACE_DBUS,
|
|
||||||
"ReleaseName",
|
|
||||||
g_variant_new("(s)", NM_SUDO_DBUS_BUS_NAME),
|
|
||||||
G_VARIANT_TYPE("(u)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
10000,
|
|
||||||
NULL,
|
|
||||||
_bus_release_name_cb,
|
|
||||||
g_steal_pointer(&keep_alive_object));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_main_context_iteration(NULL, TRUE);
|
g_main_context_iteration(NULL, TRUE);
|
||||||
|
|
@ -610,7 +657,7 @@ main(int argc, char **argv)
|
||||||
done:
|
done:
|
||||||
_LOGD("shutdown: cleanup");
|
_LOGD("shutdown: cleanup");
|
||||||
|
|
||||||
gl->is_shutting_down_cleanup = TRUE;
|
gl->shutdown_quitting = TRUE;
|
||||||
g_cancellable_cancel(gl->quit_cancellable);
|
g_cancellable_cancel(gl->quit_cancellable);
|
||||||
|
|
||||||
nm_assert(c_list_is_empty(&gl->pending_jobs_lst_head));
|
nm_assert(c_list_is_empty(&gl->pending_jobs_lst_head));
|
||||||
|
|
@ -623,19 +670,19 @@ done:
|
||||||
g_dbus_connection_signal_unsubscribe(gl->dbus_connection,
|
g_dbus_connection_signal_unsubscribe(gl->dbus_connection,
|
||||||
nm_steal_int(&gl->name_owner_changed_id));
|
nm_steal_int(&gl->name_owner_changed_id));
|
||||||
}
|
}
|
||||||
nm_clear_g_source_inst(&gl->source_sigterm);
|
|
||||||
nm_clear_g_source_inst(&gl->source_idle_timeout);
|
|
||||||
nm_clear_g_free(&gl->name_owner);
|
|
||||||
|
|
||||||
nm_g_main_context_iterate_ready(NULL);
|
|
||||||
|
|
||||||
if (gl->dbus_connection) {
|
if (gl->dbus_connection) {
|
||||||
g_dbus_connection_flush_sync(gl->dbus_connection, NULL, NULL);
|
g_dbus_connection_flush_sync(gl->dbus_connection, NULL, NULL);
|
||||||
g_clear_object(&gl->dbus_connection);
|
g_clear_object(&gl->dbus_connection);
|
||||||
nm_g_main_context_iterate_ready(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nm_clear_g_cancellable(&gl->quit_cancellable);
|
nm_g_main_context_iterate_ready(NULL);
|
||||||
|
|
||||||
|
nm_clear_g_free(&gl->name_owner);
|
||||||
|
|
||||||
|
nm_clear_g_source_inst(&gl->source_sigterm);
|
||||||
|
nm_clear_g_source_inst(&gl->source_idle_timeout);
|
||||||
|
g_clear_object(&gl->quit_cancellable);
|
||||||
|
|
||||||
_LOGD("exit (%d)", exit_code);
|
_LOGD("exit (%d)", exit_code);
|
||||||
return exit_code;
|
return exit_code;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue