dispatcher: merge branch 'th/dispatcher-cleanup'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/162
This commit is contained in:
Thomas Haller
2019-05-27 12:44:25 +02:00
12 changed files with 726 additions and 527 deletions

View File

@@ -3885,6 +3885,25 @@ EXTRA_DIST += \
# dispatcher # dispatcher
############################################################################### ###############################################################################
dispatcher_nmdbus_dispatcher_sources = \
dispatcher/nmdbus-dispatcher.h \
dispatcher/nmdbus-dispatcher.c \
$(NULL)
dispatcher/nmdbus-dispatcher.h: dispatcher/nm-dispatcher.xml
@$(MKDIR_P) dispatcher/
$(AM_V_GEN) gdbus-codegen \
--generate-c-code $(basename $@) \
--c-namespace NMDBus \
--interface-prefix org.freedesktop \
$<
dispatcher/nmdbus-dispatcher.c: dispatcher/nmdbus-dispatcher.h
CLEANFILES += $(dispatcher_nmdbus_dispatcher_sources)
###############################################################################
libexec_PROGRAMS += dispatcher/nm-dispatcher libexec_PROGRAMS += dispatcher/nm-dispatcher
noinst_LTLIBRARIES += \ noinst_LTLIBRARIES += \
@@ -3905,25 +3924,6 @@ dispatcher_cppflags = \
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \
$(NULL) $(NULL)
dispatcher_nmdbus_dispatcher_sources = \
dispatcher/nmdbus-dispatcher.h \
dispatcher/nmdbus-dispatcher.c
dispatcher/nmdbus-dispatcher.h: dispatcher/nm-dispatcher.xml
@$(MKDIR_P) dispatcher/
$(AM_V_GEN) gdbus-codegen \
--generate-c-code $(basename $@) \
--c-namespace NMDBus \
--interface-prefix org.freedesktop \
$<
dispatcher/nmdbus-dispatcher.c: dispatcher/nmdbus-dispatcher.h
$(dispatcher_nm_dispatcher_OBJECTS): $(dispatcher_nmdbus_dispatcher_sources)
CLEANFILES += $(dispatcher_nmdbus_dispatcher_sources)
dispatcher_libnm_dispatcher_core_la_SOURCES = \ dispatcher_libnm_dispatcher_core_la_SOURCES = \
dispatcher/nm-dispatcher-utils.c \ dispatcher/nm-dispatcher-utils.c \
dispatcher/nm-dispatcher-utils.h \ dispatcher/nm-dispatcher-utils.h \
@@ -3939,8 +3939,6 @@ dispatcher_nm_dispatcher_SOURCES = \
dispatcher/nm-dispatcher.c \ dispatcher/nm-dispatcher.c \
$(NULL) $(NULL)
nodist_dispatcher_nm_dispatcher_SOURCES = $(dispatcher_nmdbus_dispatcher_sources)
dispatcher_nm_dispatcher_CPPFLAGS = $(dispatcher_cppflags) dispatcher_nm_dispatcher_CPPFLAGS = $(dispatcher_cppflags)
dispatcher_nm_dispatcher_LDFLAGS = \ dispatcher_nm_dispatcher_LDFLAGS = \
@@ -3998,12 +3996,21 @@ dispatcher_tests_test_dispatcher_envp_CPPFLAGS = \
-I$(srcdir)/libnm \ -I$(srcdir)/libnm \
-I$(builddir)/libnm \ -I$(builddir)/libnm \
-I$(srcdir)/dispatcher \ -I$(srcdir)/dispatcher \
-I$(builddir)/dispatcher \
-DNETWORKMANAGER_COMPILATION_TEST \ -DNETWORKMANAGER_COMPILATION_TEST \
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \
$(GLIB_CFLAGS) \ $(GLIB_CFLAGS) \
$(SANITIZER_EXEC_CFLAGS) \ $(SANITIZER_EXEC_CFLAGS) \
$(NULL) $(NULL)
dispatcher_tests_test_dispatcher_envp_SOURCES = \
dispatcher/tests/test-dispatcher-envp.c \
$(NULL)
nodist_dispatcher_tests_test_dispatcher_envp_SOURCES = $(dispatcher_nmdbus_dispatcher_sources)
$(dispatcher_tests_test_dispatcher_envp_OBJECTS): $(dispatcher_nmdbus_dispatcher_sources)
dispatcher_tests_test_dispatcher_envp_LDFLAGS = \ dispatcher_tests_test_dispatcher_envp_LDFLAGS = \
$(SANITIZER_EXEC_LDFLAGS) \ $(SANITIZER_EXEC_LDFLAGS) \
$(NULL) $(NULL)

View File

@@ -1,6 +1,12 @@
[Unit] [Unit]
Description=Network Manager Script Dispatcher Service Description=Network Manager Script Dispatcher Service
# Order the dispatcher before NetworkManager. While dispatcher
# is D-Bus activate (and not intended to be explicitly wanted by
# another service/target), the ordering dependency matters during
# shutdown. We want first NetworkManager to be stopped.
Before=NetworkManager.service
[Service] [Service]
Type=dbus Type=dbus
BusName=org.freedesktop.nm_dispatcher BusName=org.freedesktop.nm_dispatcher

View File

@@ -44,7 +44,7 @@ libnm_dispatcher_core = static_library(
sources = files('nm-dispatcher.c') sources = files('nm-dispatcher.c')
sources += gnome.gdbus_codegen( nmdbus_dispatcher_sources = gnome.gdbus_codegen(
'nmdbus-dispatcher', 'nmdbus-dispatcher',
name + '.xml', name + '.xml',
interface_prefix: 'org.freedesktop', interface_prefix: 'org.freedesktop',

View File

@@ -34,74 +34,24 @@
#include "nm-libnm-core-aux/nm-dispatcher-api.h" #include "nm-libnm-core-aux/nm-dispatcher-api.h"
#include "nm-dispatcher-utils.h" #include "nm-dispatcher-utils.h"
#include "nmdbus-dispatcher.h" /*****************************************************************************/
static GMainLoop *loop = NULL;
static gboolean debug = FALSE;
static gboolean persist = FALSE;
static guint quit_id;
static guint request_id_counter = 0;
typedef struct Request Request; typedef struct Request Request;
typedef struct { static struct {
GObject parent; GDBusConnection *dbus_connection;
GMainLoop *loop;
/* Private data */ gboolean debug;
NMDBusDispatcher *dbus_dispatcher; gboolean persist;
guint quit_id;
guint request_id_counter;
gboolean ever_acquired_name;
bool exit_with_failure;
Request *current_request; Request *current_request;
GQueue *requests_waiting; GQueue *requests_waiting;
int num_requests_pending; int num_requests_pending;
} Handler; } gl;
typedef struct {
GObjectClass parent;
} HandlerClass;
GType handler_get_type (void);
#define HANDLER_TYPE (handler_get_type ())
#define HANDLER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), HANDLER_TYPE, Handler))
#define HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HANDLER_TYPE, HandlerClass))
G_DEFINE_TYPE(Handler, handler, G_TYPE_OBJECT)
static gboolean
handle_action (NMDBusDispatcher *dbus_dispatcher,
GDBusMethodInvocation *context,
const char *str_action,
GVariant *connection_dict,
GVariant *connection_props,
GVariant *device_props,
GVariant *device_proxy_props,
GVariant *device_ip4_props,
GVariant *device_ip6_props,
GVariant *device_dhcp4_props,
GVariant *device_dhcp6_props,
const char *connectivity_state,
const char *vpn_ip_iface,
GVariant *vpn_proxy_props,
GVariant *vpn_ip4_props,
GVariant *vpn_ip6_props,
gboolean request_debug,
gpointer user_data);
static void
handler_init (Handler *h)
{
h->requests_waiting = g_queue_new ();
h->dbus_dispatcher = nmdbus_dispatcher_skeleton_new ();
g_signal_connect (h->dbus_dispatcher, "handle-action",
G_CALLBACK (handle_action), h);
}
static void
handler_class_init (HandlerClass *h_class)
{
}
static gboolean dispatch_one_script (Request *request);
typedef struct { typedef struct {
Request *request; Request *request;
@@ -117,8 +67,6 @@ typedef struct {
} ScriptInfo; } ScriptInfo;
struct Request { struct Request {
Handler *handler;
guint request_id; guint request_id;
GDBusMethodInvocation *context; GDBusMethodInvocation *context;
@@ -135,52 +83,86 @@ struct Request {
/*****************************************************************************/ /*****************************************************************************/
#define __LOG_print(print_cmd, _request, _script, ...) \ #define __LOG_print(print_cmd, ...) \
G_STMT_START { \ G_STMT_START { \
nm_assert ((_request) && (!(_script) || (_script)->request == (_request))); \ if (FALSE) { \
print_cmd ("req:%u '%s'%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ /* g_message() alone does not warn about invalid format. Add a dummy printf() statement to
(_request)->request_id, \ * get a compiler warning about wrong format. */ \
(_request)->action, \ printf (__VA_ARGS__); \
(_request)->iface ? " [" : "", \
(_request)->iface ?: "", \
(_request)->iface ? "]" : "", \
(_script) ? ", \"" : "", \
(_script) ? (_script)->script : "", \
(_script) ? "\"" : "" \
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
} G_STMT_END
#define _LOG(_request, _script, log_always, print_cmd, ...) \
G_STMT_START { \
const Request *__request = (_request); \
const ScriptInfo *__script = (_script); \
\
if (!__request) \
__request = __script->request; \
nm_assert (__request && (!__script || __script->request == __request)); \
if ((log_always) || _LOG_R_D_enabled (__request)) { \
if (FALSE) { \
/* g_message() alone does not warn about invalid format. Add a dummy printf() statement to
* get a compiler warning about wrong format. */ \
__LOG_print (printf, __request, __script, __VA_ARGS__); \
} \
__LOG_print (print_cmd, __request, __script, __VA_ARGS__); \
} \ } \
print_cmd (__VA_ARGS__); \
} G_STMT_END } G_STMT_END
static gboolean #define __LOG_print_R(print_cmd, _request, ...) \
_LOG_R_D_enabled (const Request *request) G_STMT_START { \
{ __LOG_print (print_cmd, \
return request->debug; "req:%u '%s'%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
} (_request)->request_id, \
(_request)->action, \
(_request)->iface ? " [" : "", \
(_request)->iface ?: "", \
(_request)->iface ? "]" : "" \
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
} G_STMT_END
#define _LOG_R_D(_request, ...) _LOG(_request, NULL, FALSE, g_debug, __VA_ARGS__) #define __LOG_print_S(print_cmd, _request, _script, ...) \
#define _LOG_R_I(_request, ...) _LOG(_request, NULL, TRUE, g_info, __VA_ARGS__) G_STMT_START { \
#define _LOG_R_W(_request, ...) _LOG(_request, NULL, TRUE, g_warning, __VA_ARGS__) __LOG_print_R (print_cmd, \
(_request), \
"%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
(_script) ? ", \"" : "", \
(_script) ? (_script)->script : "", \
(_script) ? "\"" : "" \
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
} G_STMT_END
#define _LOG_S_D(_script, ...) _LOG(NULL, _script, FALSE, g_debug, __VA_ARGS__) #define _LOG_X_(enabled_cmd, print_cmd, ...) \
#define _LOG_S_I(_script, ...) _LOG(NULL, _script, TRUE, g_info, __VA_ARGS__) G_STMT_START { \
#define _LOG_S_W(_script, ...) _LOG(NULL, _script, TRUE, g_warning, __VA_ARGS__) if (enabled_cmd) \
__LOG_print (print_cmd, __VA_ARGS__); \
} G_STMT_END
#define _LOG_R_(enabled_cmd, x_request, print_cmd, ...) \
G_STMT_START { \
const Request *const _request = (x_request); \
\
nm_assert (_request); \
if (enabled_cmd) \
__LOG_print_R (print_cmd, _request, ": "__VA_ARGS__); \
} G_STMT_END
#define _LOG_S_(enabled_cmd, x_script, print_cmd, ...) \
G_STMT_START { \
const ScriptInfo *const _script = (x_script); \
const Request *const _request = _script ? _script->request : NULL; \
\
nm_assert (_script && _request); \
if (enabled_cmd) \
__LOG_print_S (print_cmd, _request, _script, ": "__VA_ARGS__); \
} G_STMT_END
#define _LOG_X_D_enabled() (gl.debug)
#define _LOG_X_T_enabled() _LOG_X_D_enabled ()
#define _LOG_R_D_enabled(request) (_NM_ENSURE_TYPE_CONST (Request *, request)->debug)
#define _LOG_R_T_enabled(request) _LOG_R_D_enabled (request)
#define _LOG_X_T(...) _LOG_X_ (_LOG_X_T_enabled (), g_debug, __VA_ARGS__)
#define _LOG_X_D(...) _LOG_X_ (_LOG_X_D_enabled (), g_info, __VA_ARGS__)
#define _LOG_X_I(...) _LOG_X_ (TRUE, g_message, __VA_ARGS__)
#define _LOG_X_W(...) _LOG_X_ (TRUE, g_warning, __VA_ARGS__)
#define _LOG_R_T(request, ...) _LOG_R_ (_LOG_R_T_enabled (_request), request, g_debug, __VA_ARGS__)
#define _LOG_R_D(request, ...) _LOG_R_ (_LOG_R_D_enabled (_request), request, g_info, __VA_ARGS__)
#define _LOG_R_W(request, ...) _LOG_R_ (TRUE, request, g_warning, __VA_ARGS__)
#define _LOG_S_T(script, ...) _LOG_S_ (_LOG_R_T_enabled (_request), script, g_debug, __VA_ARGS__)
#define _LOG_S_D(script, ...) _LOG_S_ (_LOG_R_D_enabled (_request), script, g_info, __VA_ARGS__)
#define _LOG_S_W(script, ...) _LOG_S_ (TRUE, script, g_warning, __VA_ARGS__)
/*****************************************************************************/
static gboolean dispatch_one_script (Request *request);
/*****************************************************************************/ /*****************************************************************************/
@@ -211,23 +193,23 @@ request_free (Request *request)
static gboolean static gboolean
quit_timeout_cb (gpointer user_data) quit_timeout_cb (gpointer user_data)
{ {
g_main_loop_quit (loop); gl.quit_id = 0;
return FALSE; g_main_loop_quit (gl.loop);
return G_SOURCE_REMOVE;
} }
static void static void
quit_timeout_reschedule (void) quit_timeout_reschedule (void)
{ {
if (!persist) { if (!gl.persist) {
nm_clear_g_source (&quit_id); nm_clear_g_source (&gl.quit_id);
quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL); gl.quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
} }
} }
/** /**
* next_request: * next_request:
* *
* @h: the handler
* @request: (allow-none): the request to set as next. If %NULL, dequeue the next * @request: (allow-none): the request to set as next. If %NULL, dequeue the next
* waiting request. Otherwise, try to set the given request. * waiting request. Otherwise, try to set the given request.
* *
@@ -240,27 +222,27 @@ quit_timeout_reschedule (void)
* a new request as current. * a new request as current.
*/ */
static gboolean static gboolean
next_request (Handler *h, Request *request) next_request (Request *request)
{ {
if (request) { if (request) {
if (h->current_request) { if (gl.current_request) {
g_queue_push_tail (h->requests_waiting, request); g_queue_push_tail (gl.requests_waiting, request);
return FALSE; return FALSE;
} }
} else { } else {
/* when calling next_request() without explicit @request, we always /* when calling next_request() without explicit @request, we always
* forcefully clear @current_request. That one is certainly * forcefully clear @current_request. That one is certainly
* handled already. */ * handled already. */
h->current_request = NULL; gl.current_request = NULL;
request = g_queue_pop_head (h->requests_waiting); request = g_queue_pop_head (gl.requests_waiting);
if (!request) if (!request)
return FALSE; return FALSE;
} }
_LOG_R_I (request, "start running ordered scripts..."); _LOG_R_D (request, "start running ordered scripts...");
h->current_request = request; gl.current_request = request;
return TRUE; return TRUE;
} }
@@ -280,7 +262,6 @@ complete_request (Request *request)
GVariantBuilder results; GVariantBuilder results;
GVariant *ret; GVariant *ret;
guint i; guint i;
Handler *handler = request->handler;
nm_assert (request); nm_assert (request);
@@ -301,16 +282,16 @@ complete_request (Request *request)
ret = g_variant_new ("(a(sus))", &results); ret = g_variant_new ("(a(sus))", &results);
g_dbus_method_invocation_return_value (request->context, ret); g_dbus_method_invocation_return_value (request->context, ret);
_LOG_R_D (request, "completed (%u scripts)", request->scripts->len); _LOG_R_T (request, "completed (%u scripts)", request->scripts->len);
if (handler->current_request == request) if (gl.current_request == request)
handler->current_request = NULL; gl.current_request = NULL;
request_free (request); request_free (request);
g_assert_cmpuint (handler->num_requests_pending, >, 0); g_assert_cmpuint (gl.num_requests_pending, >, 0);
if (--handler->num_requests_pending <= 0) { if (--gl.num_requests_pending <= 0) {
nm_assert (!handler->current_request && !g_queue_peek_head (handler->requests_waiting)); nm_assert (!gl.current_request && !g_queue_peek_head (gl.requests_waiting));
quit_timeout_reschedule (); quit_timeout_reschedule ();
} }
} }
@@ -318,7 +299,6 @@ complete_request (Request *request)
static void static void
complete_script (ScriptInfo *script) complete_script (ScriptInfo *script)
{ {
Handler *handler;
Request *request; Request *request;
gboolean wait = script->wait; gboolean wait = script->wait;
@@ -331,9 +311,7 @@ complete_script (ScriptInfo *script)
return; return;
} }
handler = request->handler; nm_assert (!wait || gl.current_request == request);
nm_assert (!wait || handler->current_request == request);
/* Try to complete the request. @request will be possibly free'd, /* Try to complete the request. @request will be possibly free'd,
* making @script and @request a dangling pointer. */ * making @script and @request a dangling pointer. */
@@ -346,13 +324,13 @@ complete_script (ScriptInfo *script)
* requests. However, if this was the last "no-wait" script and * requests. However, if this was the last "no-wait" script and
* there are "wait" scripts ready to run, launch them. * there are "wait" scripts ready to run, launch them.
*/ */
if ( handler->current_request == request if ( gl.current_request == request
&& handler->current_request->num_scripts_nowait == 0) { && gl.current_request->num_scripts_nowait == 0) {
if (dispatch_one_script (handler->current_request)) if (dispatch_one_script (gl.current_request))
return; return;
complete_request (handler->current_request); complete_request (gl.current_request);
} else } else
return; return;
} else { } else {
@@ -365,11 +343,11 @@ complete_script (ScriptInfo *script)
* processed because only requests with "wait" scripts can become * processed because only requests with "wait" scripts can become
* @current_request. As there can only be one "wait" script running * @current_request. As there can only be one "wait" script running
* at any time, it means complete_request() above completed @request. */ * at any time, it means complete_request() above completed @request. */
nm_assert (!handler->current_request); nm_assert (!gl.current_request);
} }
while (next_request (handler, NULL)) { while (next_request (NULL)) {
request = handler->current_request; request = gl.current_request;
if (dispatch_one_script (request)) if (dispatch_one_script (request))
return; return;
@@ -418,7 +396,7 @@ script_watch_cb (GPid pid, int status, gpointer user_data)
} }
if (script->result == DISPATCH_RESULT_SUCCESS) { if (script->result == DISPATCH_RESULT_SUCCESS) {
_LOG_S_D (script, "complete"); _LOG_S_T (script, "complete");
} else { } else {
script->result = DISPATCH_RESULT_FAILED; script->result = DISPATCH_RESULT_FAILED;
_LOG_S_W (script, "complete: failed with %s", script->error); _LOG_S_W (script, "complete: failed with %s", script->error);
@@ -533,7 +511,7 @@ script_dispatch (ScriptInfo *script)
argv[2] = request->action; argv[2] = request->action;
argv[3] = NULL; argv[3] = NULL;
_LOG_S_D (script, "run script%s", script->wait ? "" : " (no-wait)"); _LOG_S_T (script, "run script%s", script->wait ? "" : " (no-wait)");
if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &script->pid, &error)) { if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &script->pid, &error)) {
script->watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script); script->watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script);
@@ -586,7 +564,7 @@ _compare_basenames (gconstpointer a, gconstpointer b)
} }
static void static void
_find_scripts (GHashTable *scripts, const char *base, const char *subdir) _find_scripts (Request *request, GHashTable *scripts, const char *base, const char *subdir)
{ {
const char *filename; const char *filename;
gs_free char *dirname = NULL; gs_free char *dirname = NULL;
@@ -596,8 +574,10 @@ _find_scripts (GHashTable *scripts, const char *base, const char *subdir)
dirname = g_build_filename (base, "dispatcher.d", subdir, NULL); dirname = g_build_filename (base, "dispatcher.d", subdir, NULL);
if (!(dir = g_dir_open (dirname, 0, &error))) { if (!(dir = g_dir_open (dirname, 0, &error))) {
g_message ("find-scripts: Failed to open dispatcher directory '%s': %s", if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
dirname, error->message); _LOG_R_W (request, "find-scripts: Failed to open dispatcher directory '%s': %s",
dirname, error->message);
}
g_error_free (error); g_error_free (error);
return; return;
} }
@@ -615,26 +595,28 @@ _find_scripts (GHashTable *scripts, const char *base, const char *subdir)
} }
static GSList * static GSList *
find_scripts (const char *str_action) find_scripts (Request *request)
{ {
gs_unref_hashtable GHashTable *scripts = NULL; gs_unref_hashtable GHashTable *scripts = NULL;
GSList *script_list = NULL; GSList *script_list = NULL;
GHashTableIter iter; GHashTableIter iter;
const char *subdir = NULL; const char *subdir;
char *path; char *path;
char *filename; char *filename;
if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0 if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_UP,
|| strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0) NMD_ACTION_VPN_PRE_UP))
subdir = "pre-up.d"; subdir = "pre-up.d";
else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0 else if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_DOWN,
|| strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0) NMD_ACTION_VPN_PRE_DOWN))
subdir = "pre-down.d"; subdir = "pre-down.d";
else
subdir = NULL;
scripts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); scripts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
_find_scripts (scripts, NMLIBDIR, subdir); _find_scripts (request, scripts, NMLIBDIR, subdir);
_find_scripts (scripts, NMCONFDIR, subdir); _find_scripts (request, scripts, NMCONFDIR, subdir);
g_hash_table_iter_init (&iter, scripts); g_hash_table_iter_init (&iter, scripts);
while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &path)) { while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &path)) {
@@ -652,11 +634,11 @@ find_scripts (const char *str_action)
err = stat (path, &st); err = stat (path, &st);
if (err) if (err)
g_warning ("find-scripts: Failed to stat '%s': %d", path, err); _LOG_R_W (request, "find-scripts: Failed to stat '%s': %d", path, err);
else if (!S_ISREG (st.st_mode)) else if (!S_ISREG (st.st_mode))
; /* silently skip. */ ; /* silently skip. */
else if (!check_permissions (&st, &err_msg)) else if (!check_permissions (&st, &err_msg))
g_warning ("find-scripts: Cannot execute '%s': %s", path, err_msg); _LOG_R_W (request, "find-scripts: Cannot execute '%s': %s", path, err_msg);
else { else {
/* success */ /* success */
script_list = g_slist_prepend (script_list, g_strdup (path)); script_list = g_slist_prepend (script_list, g_strdup (path));
@@ -695,27 +677,25 @@ script_must_wait (const char *path)
return TRUE; return TRUE;
} }
static gboolean static void
handle_action (NMDBusDispatcher *dbus_dispatcher, _method_call_action (GDBusMethodInvocation *invocation,
GDBusMethodInvocation *context, GVariant *parameters)
const char *str_action,
GVariant *connection_dict,
GVariant *connection_props,
GVariant *device_props,
GVariant *device_proxy_props,
GVariant *device_ip4_props,
GVariant *device_ip6_props,
GVariant *device_dhcp4_props,
GVariant *device_dhcp6_props,
const char *connectivity_state,
const char *vpn_ip_iface,
GVariant *vpn_proxy_props,
GVariant *vpn_ip4_props,
GVariant *vpn_ip6_props,
gboolean request_debug,
gpointer user_data)
{ {
Handler *h = user_data; const char *action;
gs_unref_variant GVariant *connection = NULL;
gs_unref_variant GVariant *connection_properties = NULL;
gs_unref_variant GVariant *device_properties = NULL;
gs_unref_variant GVariant *device_proxy_properties = NULL;
gs_unref_variant GVariant *device_ip4_config = NULL;
gs_unref_variant GVariant *device_ip6_config = NULL;
gs_unref_variant GVariant *device_dhcp4_config = NULL;
gs_unref_variant GVariant *device_dhcp6_config = NULL;
const char *connectivity_state;
const char *vpn_ip_iface;
gs_unref_variant GVariant *vpn_proxy_properties = NULL;
gs_unref_variant GVariant *vpn_ip4_config = NULL;
gs_unref_variant GVariant *vpn_ip6_config = NULL;
gboolean debug;
GSList *sorted_scripts = NULL; GSList *sorted_scripts = NULL;
GSList *iter; GSList *iter;
Request *request; Request *request;
@@ -723,33 +703,65 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
guint i, num_nowait = 0; guint i, num_nowait = 0;
const char *error_message = NULL; const char *error_message = NULL;
sorted_scripts = find_scripts (str_action); g_variant_get (parameters, "("
"&s" /* action */
"@a{sa{sv}}" /* connection */
"@a{sv}" /* connection_properties */
"@a{sv}" /* device_properties */
"@a{sv}" /* device_proxy_properties */
"@a{sv}" /* device_ip4_config */
"@a{sv}" /* device_ip6_config */
"@a{sv}" /* device_dhcp4_config */
"@a{sv}" /* device_dhcp6_config */
"&s" /* connectivity_state */
"&s" /* vpn_ip_iface */
"@a{sv}" /* vpn_proxy_properties */
"@a{sv}" /* vpn_ip4_config */
"@a{sv}" /* vpn_ip6_config */
"b" /* debug */
")",
&action,
&connection,
&connection_properties,
&device_properties,
&device_proxy_properties,
&device_ip4_config,
&device_ip6_config,
&device_dhcp4_config,
&device_dhcp6_config,
&connectivity_state,
&vpn_ip_iface,
&vpn_proxy_properties,
&vpn_ip4_config,
&vpn_ip6_config,
&debug);
request = g_slice_new0 (Request); request = g_slice_new0 (Request);
request->request_id = ++request_id_counter; request->request_id = ++gl.request_id_counter;
request->handler = h; request->debug = debug || gl.debug;
request->debug = request_debug || debug; request->context = invocation;
request->context = context; request->action = g_strdup (action);
request->action = g_strdup (str_action);
request->envp = nm_dispatcher_utils_construct_envp (str_action, request->envp = nm_dispatcher_utils_construct_envp (action,
connection_dict, connection,
connection_props, connection_properties,
device_props, device_properties,
device_proxy_props, device_proxy_properties,
device_ip4_props, device_ip4_config,
device_ip6_props, device_ip6_config,
device_dhcp4_props, device_dhcp4_config,
device_dhcp6_props, device_dhcp6_config,
connectivity_state, connectivity_state,
vpn_ip_iface, vpn_ip_iface,
vpn_proxy_props, vpn_proxy_properties,
vpn_ip4_props, vpn_ip4_config,
vpn_ip6_props, vpn_ip6_config,
&request->iface, &request->iface,
&error_message); &error_message);
request->scripts = g_ptr_array_new_full (5, script_info_free); request->scripts = g_ptr_array_new_full (5, script_info_free);
sorted_scripts = find_scripts (request);
for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) { for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) {
ScriptInfo *s; ScriptInfo *s;
@@ -761,11 +773,11 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
} }
g_slist_free (sorted_scripts); g_slist_free (sorted_scripts);
_LOG_R_I (request, "new request (%u scripts)", request->scripts->len); _LOG_R_D (request, "new request (%u scripts)", request->scripts->len);
if ( _LOG_R_D_enabled (request) if ( _LOG_R_T_enabled (request)
&& request->envp) { && request->envp) {
for (p = request->envp; *p; p++) for (p = request->envp; *p; p++)
_LOG_R_D (request, "environment: %s", *p); _LOG_R_T (request, "environment: %s", *p);
} }
if (error_message || request->scripts->len == 0) { if (error_message || request->scripts->len == 0) {
@@ -774,18 +786,18 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
if (error_message) if (error_message)
_LOG_R_W (request, "completed: invalid request: %s", error_message); _LOG_R_W (request, "completed: invalid request: %s", error_message);
else else
_LOG_R_I (request, "completed: no scripts"); _LOG_R_D (request, "completed: no scripts");
results = g_variant_new_array (G_VARIANT_TYPE ("(sus)"), NULL, 0); results = g_variant_new_array (G_VARIANT_TYPE ("(sus)"), NULL, 0);
g_dbus_method_invocation_return_value (context, g_variant_new ("(@a(sus))", results)); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(sus))", results));
request->num_scripts_done = request->scripts->len; request->num_scripts_done = request->scripts->len;
request_free (request); request_free (request);
return TRUE; return;
} }
nm_clear_g_source (&quit_id); nm_clear_g_source (&gl.quit_id);
h->num_requests_pending++; gl.num_requests_pending++;
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);
@@ -800,8 +812,8 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
/* The request has at least one wait script. /* The request has at least one wait script.
* Try next_request() to schedule the request for * Try next_request() to schedule the request for
* execution. This either enqueues the request or * execution. This either enqueues the request or
* sets it as h->current_request. */ * sets it as gl.current_request. */
if (next_request (h, request)) { if (next_request (request)) {
/* @request is now @current_request. Go ahead and /* @request is now @current_request. Go ahead and
* schedule the first wait script. */ * schedule the first wait script. */
if (!dispatch_one_script (request)) { if (!dispatch_one_script (request)) {
@@ -809,7 +821,7 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
* request. Try complete_request(). */ * request. Try complete_request(). */
complete_request (request); complete_request (request);
if (next_request (h, NULL)) { if (next_request (NULL)) {
/* As @request was successfully scheduled as next_request(), there is no /* As @request was successfully scheduled as next_request(), there is no
* other request in queue that can be scheduled afterwards. Assert against * other request in queue that can be scheduled afterwards. Assert against
* that, but call next_request() to clear current_request. */ * that, but call next_request() to clear current_request. */
@@ -822,24 +834,20 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
* the request right away (we might have failed to schedule any * the request right away (we might have failed to schedule any
* of the scripts). It will be either completed now, or later * of the scripts). It will be either completed now, or later
* when the pending scripts return. * when the pending scripts return.
* We don't enqueue it to h->requests_waiting. * We don't enqueue it to gl.requests_waiting.
* There is no need to handle next_request(), because @request is * There is no need to handle next_request(), because @request is
* not the current request anyway and does not interfere with requests * not the current request anyway and does not interfere with requests
* that have any "wait" scripts. */ * that have any "wait" scripts. */
complete_request (request); complete_request (request);
} }
return TRUE;
} }
static gboolean ever_acquired_name = FALSE;
static void static void
on_name_acquired (GDBusConnection *connection, on_name_acquired (GDBusConnection *connection,
const char *name, const char *name,
gpointer user_data) gpointer user_data)
{ {
ever_acquired_name = TRUE; gl.ever_acquired_name = TRUE;
} }
static void static void
@@ -848,22 +856,79 @@ on_name_lost (GDBusConnection *connection,
gpointer user_data) gpointer user_data)
{ {
if (!connection) { if (!connection) {
if (!ever_acquired_name) { if (!gl.ever_acquired_name) {
g_warning ("Could not get the system bus. Make sure the message bus daemon is running!"); _LOG_X_W ("Could not get the system bus. Make sure the message bus daemon is running!");
exit (1); gl.exit_with_failure = TRUE;
} else { } else {
g_message ("System bus stopped. Exiting"); _LOG_X_I ("System bus stopped. Exiting");
exit (0);
} }
} else if (!ever_acquired_name) { } else if (!gl.ever_acquired_name) {
g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service."); _LOG_X_W ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service.");
exit (1); gl.exit_with_failure = TRUE;
} else { } else
g_message ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting"); _LOG_X_I ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting");
exit (0);
} g_main_loop_quit (gl.loop);
} }
static void
_method_call (GDBusConnection *connection,
const char *sender,
const char *object_path,
const char *interface_name,
const char *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
if (nm_streq (interface_name, NM_DISPATCHER_DBUS_INTERFACE)) {
if (nm_streq (method_name, "Action")) {
_method_call_action (invocation, parameters);
return;
}
}
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method %s",
method_name);
}
static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
NM_DISPATCHER_DBUS_INTERFACE,
.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
NM_DEFINE_GDBUS_METHOD_INFO (
"Action",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
NM_DEFINE_GDBUS_ARG_INFO ("action", "s"),
NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
NM_DEFINE_GDBUS_ARG_INFO ("connection_properties", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_properties", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_proxy_properties", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_ip4_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_ip6_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp4_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp6_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("connectivity_state", "s"),
NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip_iface", "s"),
NM_DEFINE_GDBUS_ARG_INFO ("vpn_proxy_properties", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip4_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip6_config", "a{sv}"),
NM_DEFINE_GDBUS_ARG_INFO ("debug", "b"),
),
.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
NM_DEFINE_GDBUS_ARG_INFO ("results", "a(sus)"),
),
),
),
);
static const GDBusInterfaceVTable interface_vtable = {
.method_call = _method_call,
};
/*****************************************************************************/
static void static void
log_handler (const char *log_domain, log_handler (const char *log_domain,
GLogLevelFlags log_level, GLogLevelFlags log_level,
@@ -918,42 +983,55 @@ signal_handler (gpointer user_data)
{ {
int signo = GPOINTER_TO_INT (user_data); int signo = GPOINTER_TO_INT (user_data);
g_message ("Caught signal %d, shutting down...", signo); _LOG_X_I ("Caught signal %d, shutting down...", signo);
g_main_loop_quit (loop); g_main_loop_quit (gl.loop);
return G_SOURCE_REMOVE; return G_SOURCE_CONTINUE;
} }
int static gboolean
main (int argc, char **argv) parse_command_line (int *p_argc,
char ***p_argv,
GError **error)
{ {
GOptionContext *opt_ctx; GOptionContext *opt_ctx;
GError *error = NULL;
GDBusConnection *bus;
Handler *handler;
GOptionEntry entries[] = { GOptionEntry entries[] = {
{ "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, { "debug", 0, 0, G_OPTION_ARG_NONE, &gl.debug, "Output to console rather than syslog", NULL },
{ "persist", 0, 0, G_OPTION_ARG_NONE, &persist, "Don't quit after a short timeout", NULL }, { "persist", 0, 0, G_OPTION_ARG_NONE, &gl.persist, "Don't quit after a short timeout", NULL },
{ NULL } { NULL }
}; };
gboolean success;
opt_ctx = g_option_context_new (NULL); opt_ctx = g_option_context_new (NULL);
g_option_context_set_summary (opt_ctx, "Executes scripts upon actions by NetworkManager."); g_option_context_set_summary (opt_ctx, "Executes scripts upon actions by NetworkManager.");
g_option_context_add_main_entries (opt_ctx, entries, NULL); g_option_context_add_main_entries (opt_ctx, entries, NULL);
if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { success = g_option_context_parse (opt_ctx, p_argc, p_argv, error);
g_warning ("Error parsing command line arguments: %s", error->message);
g_error_free (error);
return 1;
}
g_option_context_free (opt_ctx); g_option_context_free (opt_ctx);
g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); return success;
g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); }
if (debug) { int
main (int argc, char **argv)
{
gs_free_error GError *error = NULL;
guint signal_id_term = 0;
guint signal_id_int = 0;
guint dbus_regist_id = 0;
guint dbus_own_name_id = 0;
if (!parse_command_line (&argc, &argv, &error)) {
_LOG_X_W ("Error parsing command line arguments: %s", error->message);
gl.exit_with_failure = TRUE;
goto done;
}
signal_id_term = g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM));
signal_id_int = g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT));
if (gl.debug) {
if (!g_getenv ("G_MESSAGES_DEBUG")) { if (!g_getenv ("G_MESSAGES_DEBUG")) {
/* we log our regular messages using g_debug() and g_info(). /* we log our regular messages using g_debug() and g_info().
* When we redirect glib logging to syslog, there is no problem. * When we redirect glib logging to syslog, there is no problem.
@@ -964,45 +1042,75 @@ main (int argc, char **argv)
} else } else
logging_setup (); logging_setup ();
loop = g_main_loop_new (NULL, FALSE); gl.loop = g_main_loop_new (NULL, FALSE);
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); gl.dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!bus) { if (!gl.dbus_connection) {
g_warning ("Could not get the system bus (%s). Make sure the message bus daemon is running!", _LOG_X_W ("Could not get the system bus (%s). Make sure the message bus daemon is running!",
error->message); error->message);
g_error_free (error); gl.exit_with_failure = TRUE;
return 1; goto done;
} }
handler = g_object_new (HANDLER_TYPE, NULL); gl.requests_waiting = g_queue_new ();
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (handler->dbus_dispatcher),
bus, dbus_regist_id = g_dbus_connection_register_object (gl.dbus_connection,
NM_DISPATCHER_DBUS_PATH, NM_DISPATCHER_DBUS_PATH,
&error); interface_info,
if (error) { NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable),
g_warning ("Could not export Dispatcher D-Bus interface: %s", error->message); NULL,
g_error_free (error); NULL,
return 1; &error);
if (dbus_regist_id == 0) {
_LOG_X_W ("Could not export Dispatcher D-Bus interface: %s", error->message);
gl.exit_with_failure = 1;
goto done;
} }
g_bus_own_name_on_connection (bus, dbus_own_name_id = g_bus_own_name_on_connection (gl.dbus_connection,
NM_DISPATCHER_DBUS_SERVICE, NM_DISPATCHER_DBUS_SERVICE,
G_BUS_NAME_OWNER_FLAGS_NONE, G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired, on_name_acquired,
on_name_lost, on_name_lost,
NULL, NULL); NULL, NULL);
g_object_unref (bus);
quit_timeout_reschedule (); quit_timeout_reschedule ();
g_main_loop_run (loop); g_main_loop_run (gl.loop);
g_queue_free (handler->requests_waiting); done:
g_object_unref (handler);
if (!debug) if (gl.num_requests_pending > 0) {
/* this only happens when we quit due to SIGTERM (not due to the idle timer).
*
* Log a warning about pending scripts.
*
* Maybe we should notify NetworkManager that these scripts are left in an unknown state.
* But this is either a bug of a dispatcher script (not terminating in time).
*
* FIXME(shutdown): Also, currently NetworkManager behaves wrongly on shutdown.
* Note that systemd would not terminate NetworkManager-dispatcher before NetworkManager.
* It's NetworkManager's responsibility to keep running long enough so that all requests
* can complete (with a watchdog timer, and a warning that user provided scripts hang). */
_LOG_X_W ("exiting but there are still %u requests pending", gl.num_requests_pending);
}
if (dbus_own_name_id != 0)
g_bus_unown_name (nm_steal_int (&dbus_own_name_id));
if (dbus_regist_id != 0)
g_dbus_connection_unregister_object (gl.dbus_connection, nm_steal_int (&dbus_regist_id));
nm_clear_pointer (&gl.requests_waiting, g_queue_free);
nm_clear_g_source (&signal_id_term);
nm_clear_g_source (&signal_id_int);
nm_clear_g_source (&gl.quit_id);
g_clear_pointer (&gl.loop, g_main_loop_unref);
g_clear_object (&gl.dbus_connection);
if (!gl.debug)
logging_shutdown (); logging_shutdown ();
return 0; return gl.exit_with_failure ? 1 : 0;
} }

View File

@@ -7,7 +7,7 @@ incs = [
exe = executable( exe = executable(
test_unit, test_unit,
test_unit + '.c', [ test_unit + '.c' ] + [ nmdbus_dispatcher_sources ],
include_directories: incs, include_directories: incs,
dependencies: libnm_core_dep, dependencies: libnm_core_dep,
c_args: [ c_args: [

View File

@@ -28,6 +28,8 @@
#include "nm-utils/nm-test-utils.h" #include "nm-utils/nm-test-utils.h"
#include "nmdbus-dispatcher.h"
#define TEST_DIR NM_BUILD_SRCDIR"/dispatcher/tests" #define TEST_DIR NM_BUILD_SRCDIR"/dispatcher/tests"
/*****************************************************************************/ /*****************************************************************************/
@@ -637,6 +639,17 @@ test_up_empty_vpn_iface (void)
/*****************************************************************************/ /*****************************************************************************/
static void
test_gdbus_codegen (void)
{
gs_unref_object NMDBusDispatcher *dbus_dispatcher = NULL;
dbus_dispatcher = nmdbus_dispatcher_skeleton_new ();
g_assert (NMDBUS_IS_DISPATCHER_SKELETON (dbus_dispatcher));
}
/*****************************************************************************/
NMTST_DEFINE (); NMTST_DEFINE ();
int int
@@ -653,6 +666,8 @@ main (int argc, char **argv)
g_test_add_func ("/dispatcher/up_empty_vpn_iface", test_up_empty_vpn_iface); g_test_add_func ("/dispatcher/up_empty_vpn_iface", test_up_empty_vpn_iface);
g_test_add_func ("/dispatcher/gdbus-codegen", test_gdbus_codegen);
return g_test_run (); return g_test_run ();
} }

View File

@@ -635,8 +635,10 @@ NM_G_ERROR_MSG (GError *error)
* It's useful to check the let the compiler ensure that @value is * It's useful to check the let the compiler ensure that @value is
* of a certain type. */ * of a certain type. */
#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) #define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value)))
#define _NM_ENSURE_TYPE_CONST(type, value) (_Generic ((value), const type: ((const type) (value)), type: ((const type) (value))))
#else #else
#define _NM_ENSURE_TYPE(type, value) (value) #define _NM_ENSURE_TYPE(type, value) (value)
#define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value))
#endif #endif
#if _NM_CC_SUPPORT_GENERIC #if _NM_CC_SUPPORT_GENERIC

View File

@@ -328,16 +328,18 @@ typedef struct _NMDevicePrivate {
ActivationHandleData act_handle4; /* for layer2 and IPv4. */ ActivationHandleData act_handle4; /* for layer2 and IPv4. */
ActivationHandleData act_handle6; ActivationHandleData act_handle6;
guint recheck_assume_id; guint recheck_assume_id;
struct { struct {
guint call_id; guint call_id;
NMDeviceStateReason available_reason; NMDeviceStateReason available_reason;
NMDeviceStateReason unavailable_reason; NMDeviceStateReason unavailable_reason;
} recheck_available; } recheck_available;
struct { struct {
guint call_id; NMDispatcherCallId *call_id;
NMDeviceState post_state; NMDeviceState post_state;
NMDeviceStateReason post_state_reason; NMDeviceStateReason post_state_reason;
} dispatcher; } dispatcher;
/* Link stuff */ /* Link stuff */
guint link_connected_id; guint link_connected_id;
@@ -12373,29 +12375,31 @@ nm_device_get_ip6_config (NMDevice *self)
/*****************************************************************************/ /*****************************************************************************/
static void static gboolean
dispatcher_cleanup (NMDevice *self) dispatcher_cleanup (NMDevice *self)
{ {
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->dispatcher.call_id) { if (!priv->dispatcher.call_id)
nm_dispatcher_call_cancel (priv->dispatcher.call_id); return FALSE;
priv->dispatcher.call_id = 0;
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher.call_id));
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
} priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
return TRUE;
} }
static void static void
dispatcher_complete_proceed_state (guint call_id, gpointer user_data) dispatcher_complete_proceed_state (NMDispatcherCallId *call_id, gpointer user_data)
{ {
NMDevice *self = NM_DEVICE (user_data); NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (call_id == priv->dispatcher.call_id); g_return_if_fail (call_id == priv->dispatcher.call_id);
priv->dispatcher.call_id = 0; priv->dispatcher.call_id = NULL;
nm_device_queue_state (self, priv->dispatcher.post_state, nm_device_queue_state (self,
priv->dispatcher.post_state,
priv->dispatcher.post_state_reason); priv->dispatcher.post_state_reason);
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
@@ -12408,10 +12412,8 @@ ip_check_pre_up (NMDevice *self)
{ {
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->dispatcher.call_id != 0) { if (dispatcher_cleanup (self))
g_warn_if_reached (); nm_assert_not_reached ();
dispatcher_cleanup (self);
}
priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
@@ -14822,7 +14824,7 @@ deactivate_async_ready (NMDevice *self,
} }
static void static void
deactivate_dispatcher_complete (guint call_id, gpointer user_data) deactivate_dispatcher_complete (NMDispatcherCallId *call_id, gpointer user_data)
{ {
NMDevice *self = NM_DEVICE (user_data); NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -14833,7 +14835,7 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data)
reason = priv->dispatcher.post_state_reason; reason = priv->dispatcher.post_state_reason;
priv->dispatcher.call_id = 0; priv->dispatcher.call_id = NULL;
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;

View File

@@ -436,8 +436,6 @@ main (int argc, char *argv[])
nm_manager_dbus_set_property_handle, nm_manager_dbus_set_property_handle,
manager); manager);
nm_dispatcher_init ();
g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
if (!nm_manager_start (manager, &error)) { if (!nm_manager_start (manager, &error)) {

View File

@@ -44,8 +44,121 @@
#define _NMLOG_DOMAIN LOGD_DISPATCH #define _NMLOG_DOMAIN LOGD_DISPATCH
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dispatcher", __VA_ARGS__) #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dispatcher", __VA_ARGS__)
static GDBusProxy *dispatcher_proxy; #define _NMLOG2_DOMAIN LOGD_DISPATCH
static GHashTable *requests = NULL; #define _NMLOG2(level, request_id, log_ifname, log_con_uuid, ...) \
nm_log ((level), \
_NMLOG2_DOMAIN, \
(log_ifname), \
(log_con_uuid), \
"dispatcher: (%u) " \
_NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
(request_id) \
_NM_UTILS_MACRO_REST (__VA_ARGS__))
#define _NMLOG3_DOMAIN LOGD_DISPATCH
#define _NMLOG3(level, call_id, ...) \
G_STMT_START { \
const NMDispatcherCallId *const _call_id = (call_id); \
\
_NMLOG2 (level, _call_id->request_id, _call_id->log_ifname, _call_id->log_con_uuid, __VA_ARGS__); \
} G_STMT_END
/*****************************************************************************/
struct NMDispatcherCallId {
NMDispatcherFunc callback;
gpointer user_data;
const char *log_ifname;
const char *log_con_uuid;
NMDispatcherAction action;
guint idle_id;
guint32 request_id;
char extra_strings[];
};
/*****************************************************************************/
/* FIXME(shutdown): on shutdown, we should not run dispatcher scripts synchronously.
* Instead, we should of course still run them asynchronously.
*
* Also, we should wait for all pending requests to complete before exiting the main-loop
* (with a watchdog). If we hit a timeout, we log a warning and quit (but leave the scripts
* running).
*
* Finally, cleanup the global structures. */
static struct {
GDBusConnection *dbus_connection;
GHashTable *requests;
guint request_id_counter;
} gl;
/*****************************************************************************/
static NMDispatcherCallId *
dispatcher_call_id_new (guint32 request_id,
NMDispatcherAction action,
NMDispatcherFunc callback,
gpointer user_data,
const char *log_ifname,
const char *log_con_uuid)
{
NMDispatcherCallId *call_id;
gsize l_log_ifname;
gsize l_log_con_uuid;
char *extra_strings;
l_log_ifname = log_ifname ? (strlen (log_ifname) + 1) : 0u;
l_log_con_uuid = log_con_uuid ? (strlen (log_con_uuid) + 1) : 0u;
call_id = g_malloc (sizeof (NMDispatcherCallId) + l_log_ifname + l_log_con_uuid);
call_id->action = action;
call_id->request_id = request_id;
call_id->callback = callback;
call_id->user_data = user_data;
call_id->idle_id = 0;
extra_strings = &call_id->extra_strings[0];
if (log_ifname) {
call_id->log_ifname = extra_strings;
memcpy (extra_strings, log_ifname, l_log_ifname);
extra_strings += l_log_ifname;
} else
call_id->log_ifname = NULL;
if (log_con_uuid) {
call_id->log_con_uuid = extra_strings;
memcpy (extra_strings, log_con_uuid, l_log_con_uuid);
} else
call_id->log_con_uuid = NULL;
return call_id;
}
static void
dispatcher_call_id_free (NMDispatcherCallId *call_id)
{
nm_clear_g_source (&call_id->idle_id);
g_free (call_id);
}
/*****************************************************************************/
static void
_init_dispatcher (void)
{
if (G_UNLIKELY (gl.requests == NULL)) {
gl.requests = g_hash_table_new (nm_direct_hash, NULL);
gl.dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
if (!gl.dbus_connection)
_LOGD ("No D-Bus connection to talk with NetworkManager-dispatcher service");
}
}
/*****************************************************************************/
static void static void
dump_proxy_to_props (NMProxyConfig *proxy, GVariantBuilder *builder) dump_proxy_to_props (NMProxyConfig *proxy, GVariantBuilder *builder)
@@ -287,39 +400,6 @@ fill_vpn_props (NMProxyConfig *proxy_config,
dump_ip6_to_props (ip6_config, ip6_builder); dump_ip6_to_props (ip6_config, ip6_builder);
} }
typedef struct {
NMDispatcherAction action;
guint request_id;
NMDispatcherFunc callback;
gpointer user_data;
guint idle_id;
} DispatchInfo;
static void
dispatcher_info_free (DispatchInfo *info)
{
if (info->idle_id)
g_source_remove (info->idle_id);
g_free (info);
}
static void
_ensure_requests (void)
{
if (G_UNLIKELY (requests == NULL)) {
requests = g_hash_table_new_full (nm_direct_hash,
NULL,
NULL,
(GDestroyNotify) dispatcher_info_free);
}
}
static void
dispatcher_info_cleanup (DispatchInfo *info)
{
g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id));
}
static const char * static const char *
dispatch_result_to_string (DispatchResult result) dispatch_result_to_string (DispatchResult result)
{ {
@@ -339,63 +419,71 @@ dispatch_result_to_string (DispatchResult result)
} }
static void static void
dispatcher_results_process (guint request_id, NMDispatcherAction action, GVariantIter *results) dispatcher_results_process (guint32 request_id,
const char *log_ifname,
const char *log_con_uuid,
GVariant *v_results)
{ {
nm_auto_free_variant_iter GVariantIter *results = NULL;
const char *script, *err; const char *script, *err;
guint32 result; guint32 result;
g_return_if_fail (results != NULL); g_variant_get (v_results, "(a(sus))", &results);
if (g_variant_iter_n_children (results) == 0) { if (g_variant_iter_n_children (results) == 0) {
_LOGD ("(%u) succeeded but no scripts invoked", request_id); _LOG2D (request_id, log_ifname, log_con_uuid, "succeeded but no scripts invoked");
return; return;
} }
while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) { while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) {
if (result == DISPATCH_RESULT_SUCCESS) { if (result == DISPATCH_RESULT_SUCCESS) {
_LOGD ("(%u) %s succeeded", request_id, script); _LOG2D (request_id, log_ifname, log_con_uuid, "%s succeeded", script);
} else { } else {
_LOGW ("(%u) %s failed (%s): %s", _LOG2W (request_id,
request_id, log_ifname,
script, log_con_uuid,
dispatch_result_to_string (result), "%s failed (%s): %s",
err); script,
dispatch_result_to_string (result),
err);
} }
} }
} }
static void static void
dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) dispatcher_done_cb (GObject *source, GAsyncResult *result, gpointer user_data)
{ {
DispatchInfo *info = user_data; gs_unref_variant GVariant *ret = NULL;
GVariant *ret; gs_free_error GError *error = NULL;
GVariantIter *results; NMDispatcherCallId *call_id = user_data;
GError *error = NULL;
ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, nm_assert ((gpointer) source == gl.dbus_connection);
G_VARIANT_TYPE ("(a(sus))"),
&error); ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
if (ret) { result,
g_variant_get (ret, "(a(sus))", &results); &error);
dispatcher_results_process (info->request_id, info->action, results); if (!ret) {
g_variant_iter_free (results);
g_variant_unref (ret);
} else {
if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) { if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) {
g_dbus_error_strip_remote_error (error); g_dbus_error_strip_remote_error (error);
_LOGW ("(%u) failed to call dispatcher scripts: %s", _LOG3W (call_id, "failed to call dispatcher scripts: %s",
info->request_id, error->message); error->message);
} else { } else {
_LOGD ("(%u) failed to call dispatcher scripts: %s", _LOG3D (call_id, "failed to call dispatcher scripts: %s",
info->request_id, error->message); error->message);
} }
g_clear_error (&error); } else {
dispatcher_results_process (call_id->request_id,
call_id->log_ifname,
call_id->log_con_uuid,
ret);
} }
if (info->callback) g_hash_table_remove (gl.requests, call_id);
info->callback (info->request_id, info->user_data);
dispatcher_info_cleanup (info); if (call_id->callback)
call_id->callback (call_id, call_id->user_data);
dispatcher_call_id_free (call_id);
} }
static const char *action_table[] = { static const char *action_table[] = {
@@ -416,8 +504,9 @@ static const char *action_table[] = {
static const char * static const char *
action_to_string (NMDispatcherAction action) action_to_string (NMDispatcherAction action)
{ {
g_assert ((gsize) action < G_N_ELEMENTS (action_table)); if (G_UNLIKELY ((gsize) action >= G_N_ELEMENTS (action_table)))
return action_table[action]; g_return_val_if_reached (NULL);
return action_table[(gsize) action];
} }
static gboolean static gboolean
@@ -434,7 +523,7 @@ _dispatcher_call (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id) NMDispatcherCallId **out_call_id)
{ {
GVariant *connection_dict; GVariant *connection_dict;
GVariantBuilder connection_props; GVariantBuilder connection_props;
@@ -442,47 +531,57 @@ _dispatcher_call (NMDispatcherAction action,
GVariantBuilder device_proxy_props; GVariantBuilder device_proxy_props;
GVariantBuilder device_ip4_props; GVariantBuilder device_ip4_props;
GVariantBuilder device_ip6_props; GVariantBuilder device_ip6_props;
GVariant *device_dhcp4_props = NULL; gs_unref_variant GVariant *parameters_floating = NULL;
GVariant *device_dhcp6_props = NULL; gs_unref_variant GVariant *device_dhcp4_props = NULL;
gs_unref_variant GVariant *device_dhcp6_props = NULL;
GVariantBuilder vpn_proxy_props; GVariantBuilder vpn_proxy_props;
GVariantBuilder vpn_ip4_props; GVariantBuilder vpn_ip4_props;
GVariantBuilder vpn_ip6_props; GVariantBuilder vpn_ip6_props;
DispatchInfo *info = NULL; NMDispatcherCallId *call_id;
gboolean success = FALSE; guint request_id;
GError *error = NULL;
static guint request_counter = 0;
guint reqid = ++request_counter;
const char *connectivity_state_string = "UNKNOWN"; const char *connectivity_state_string = "UNKNOWN";
const char *log_ifname;
const char *log_con_uuid;
if (!dispatcher_proxy) g_return_val_if_fail (!blocking || (!callback && !user_data), FALSE);
NM_SET_OUT (out_call_id, NULL);
_init_dispatcher ();
if (!gl.dbus_connection)
return FALSE; return FALSE;
/* Wrapping protection */ log_ifname = device ? nm_device_get_iface (device) : NULL;
if (G_UNLIKELY (!reqid)) log_con_uuid = settings_connection ? nm_settings_connection_get_uuid (settings_connection) : NULL;
reqid = ++request_counter;
g_assert (!blocking || (!callback && !user_data)); request_id = ++gl.request_id_counter;
if (G_UNLIKELY (!request_id))
_ensure_requests (); request_id = ++gl.request_id_counter;
/* All actions except 'hostname' and 'connectivity-change' require a device */ /* All actions except 'hostname' and 'connectivity-change' require a device */
if ( action == NM_DISPATCHER_ACTION_HOSTNAME if ( action == NM_DISPATCHER_ACTION_HOSTNAME
|| action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) { || action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
_LOGD ("(%u) dispatching action '%s'%s", _LOG2D (request_id,
reqid, action_to_string (action), log_ifname,
blocking log_con_uuid,
"dispatching action '%s'%s",
action_to_string (action),
blocking
? " (blocking)" ? " (blocking)"
: (callback ? " (with callback)" : "")); : (callback ? " (with callback)" : ""));
} else { } else {
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
_LOGD ("(%u) (%s) dispatching action '%s'%s", _LOG2D (request_id,
reqid, log_ifname,
vpn_iface ?: nm_device_get_iface(device), log_con_uuid,
action_to_string (action), "(%s) dispatching action '%s'%s",
blocking vpn_iface ?: nm_device_get_iface (device),
? " (blocking)" action_to_string (action),
: (callback ? " (with callback)" : "")); blocking
? " (blocking)"
: (callback ? " (with callback)" : ""));
} }
if (applied_connection) if (applied_connection)
@@ -542,90 +641,72 @@ _dispatcher_call (NMDispatcherAction action,
} }
} }
if (!device_dhcp4_props)
device_dhcp4_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
if (!device_dhcp6_props)
device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
connectivity_state_string = nm_connectivity_state_to_string (connectivity_state); connectivity_state_string = nm_connectivity_state_to_string (connectivity_state);
parameters_floating = g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)",
action_to_string (action),
connection_dict,
&connection_props,
&device_props,
&device_proxy_props,
&device_ip4_props,
&device_ip6_props,
device_dhcp4_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0),
device_dhcp6_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0),
connectivity_state_string,
vpn_iface ?: "",
&vpn_proxy_props,
&vpn_ip4_props,
&vpn_ip6_props,
nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH));
/* Send the action to the dispatcher */ /* Send the action to the dispatcher */
if (blocking) { if (blocking) {
GVariant *ret; gs_unref_variant GVariant *ret = NULL;
GVariantIter *results; gs_free_error GError *error = NULL;
ret = _nm_dbus_proxy_call_sync (dispatcher_proxy, "Action", ret = g_dbus_connection_call_sync (gl.dbus_connection,
g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", NM_DISPATCHER_DBUS_SERVICE,
action_to_string (action), NM_DISPATCHER_DBUS_PATH,
connection_dict, NM_DISPATCHER_DBUS_INTERFACE,
&connection_props, "Action",
&device_props, g_steal_pointer (&parameters_floating),
&device_proxy_props, G_VARIANT_TYPE ("(a(sus))"),
&device_ip4_props, G_DBUS_CALL_FLAGS_NONE,
&device_ip6_props, CALL_TIMEOUT,
device_dhcp4_props, NULL,
device_dhcp6_props, &error);
connectivity_state_string, if (!ret) {
vpn_iface ?: "",
&vpn_proxy_props,
&vpn_ip4_props,
&vpn_ip6_props,
nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)),
G_VARIANT_TYPE ("(a(sus))"),
G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT,
NULL, &error);
if (ret) {
g_variant_get (ret, "(a(sus))", &results);
dispatcher_results_process (reqid, action, results);
g_variant_iter_free (results);
g_variant_unref (ret);
success = TRUE;
} else {
g_dbus_error_strip_remote_error (error); g_dbus_error_strip_remote_error (error);
_LOGW ("(%u) failed: %s", reqid, error->message); _LOG2W (request_id, log_ifname, log_con_uuid, "failed: %s", error->message);
g_clear_error (&error); return FALSE;
success = FALSE;
} }
} else { dispatcher_results_process (request_id, log_ifname, log_con_uuid, ret);
info = g_malloc0 (sizeof (*info)); return TRUE;
info->action = action;
info->request_id = reqid;
info->callback = callback;
info->user_data = user_data;
g_dbus_proxy_call (dispatcher_proxy, "Action",
g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)",
action_to_string (action),
connection_dict,
&connection_props,
&device_props,
&device_proxy_props,
&device_ip4_props,
&device_ip6_props,
device_dhcp4_props,
device_dhcp6_props,
connectivity_state_string,
vpn_iface ?: "",
&vpn_proxy_props,
&vpn_ip4_props,
&vpn_ip6_props,
nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)),
G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT,
NULL, dispatcher_done_cb, info);
success = TRUE;
} }
g_variant_unref (device_dhcp4_props); call_id = dispatcher_call_id_new (request_id,
g_variant_unref (device_dhcp6_props); action,
callback,
user_data,
log_ifname,
log_con_uuid);
if (success && info) { g_dbus_connection_call (gl.dbus_connection,
/* Track the request in case of cancelation */ NM_DISPATCHER_DBUS_SERVICE,
g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info); NM_DISPATCHER_DBUS_PATH,
if (out_call_id) NM_DISPATCHER_DBUS_INTERFACE,
*out_call_id = info->request_id; "Action",
} else if (out_call_id) g_steal_pointer (&parameters_floating),
*out_call_id = 0; G_VARIANT_TYPE ("(a(sus))"),
G_DBUS_CALL_FLAGS_NONE,
return success; CALL_TIMEOUT,
NULL,
dispatcher_done_cb,
call_id);
g_hash_table_add (gl.requests, call_id);
NM_SET_OUT (out_call_id, call_id);
return TRUE;
} }
/** /**
@@ -642,7 +723,7 @@ _dispatcher_call (NMDispatcherAction action,
gboolean gboolean
nm_dispatcher_call_hostname (NMDispatcherFunc callback, nm_dispatcher_call_hostname (NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id) NMDispatcherCallId **out_call_id)
{ {
return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE, return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE,
NULL, NULL, NULL, FALSE, NULL, NULL, NULL, FALSE,
@@ -673,7 +754,7 @@ nm_dispatcher_call_device (NMDispatcherAction action,
NMActRequest *act_request, NMActRequest *act_request,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id) NMDispatcherCallId **out_call_id)
{ {
nm_assert (NM_IS_DEVICE (device)); nm_assert (NM_IS_DEVICE (device));
if (!act_request) { if (!act_request) {
@@ -757,7 +838,7 @@ nm_dispatcher_call_vpn (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id) NMDispatcherCallId **out_call_id)
{ {
return _dispatcher_call (action, FALSE, return _dispatcher_call (action, FALSE,
parent_device, parent_device,
@@ -821,7 +902,7 @@ gboolean
nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state, nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id) NMDispatcherCallId **out_call_id)
{ {
return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE, return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE,
NULL, NULL, NULL, FALSE, NULL, NULL, NULL, FALSE,
@@ -831,40 +912,16 @@ nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state,
} }
void void
nm_dispatcher_call_cancel (guint call_id) nm_dispatcher_call_cancel (NMDispatcherCallId *call_id)
{ {
DispatchInfo *info; if ( !call_id
|| g_hash_table_lookup (gl.requests, call_id) != call_id
_ensure_requests (); || !call_id->callback)
g_return_if_reached ();
/* Canceling just means the callback doesn't get called, so set the /* Canceling just means the callback doesn't get called, so set the
* DispatcherInfo's callback to NULL. * DispatcherInfo's callback to NULL.
*/ */
info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id)); _LOG3D (call_id, "cancelling dispatcher callback action");
g_return_if_fail (info); call_id->callback = NULL;
if (info && info->callback) {
_LOGD ("(%u) cancelling dispatcher callback action", call_id);
info->callback = NULL;
}
} }
void
nm_dispatcher_init (void)
{
GError *error = NULL;
dispatcher_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
NM_DISPATCHER_DBUS_SERVICE,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_INTERFACE,
NULL, &error);
if (!dispatcher_proxy) {
_LOGE ("could not get dispatcher proxy! %s", error->message);
g_clear_error (&error);
}
}

View File

@@ -39,18 +39,20 @@ typedef enum {
NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE
} NMDispatcherAction; } NMDispatcherAction;
typedef void (*NMDispatcherFunc) (guint call_id, gpointer user_data); typedef struct NMDispatcherCallId NMDispatcherCallId;
typedef void (*NMDispatcherFunc) (NMDispatcherCallId *call_id, gpointer user_data);
gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback, gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id); NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_device (NMDispatcherAction action, gboolean nm_dispatcher_call_device (NMDispatcherAction action,
NMDevice *device, NMDevice *device,
NMActRequest *act_request, NMActRequest *act_request,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id); NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action, gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action,
NMDevice *device, NMDevice *device,
@@ -66,7 +68,7 @@ gboolean nm_dispatcher_call_vpn (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config, NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id); NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action, gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
NMSettingsConnection *settings_connection, NMSettingsConnection *settings_connection,
@@ -80,10 +82,8 @@ gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
gboolean nm_dispatcher_call_connectivity (NMConnectivityState state, gboolean nm_dispatcher_call_connectivity (NMConnectivityState state,
NMDispatcherFunc callback, NMDispatcherFunc callback,
gpointer user_data, gpointer user_data,
guint *out_call_id); NMDispatcherCallId **out_call_id);
void nm_dispatcher_call_cancel (guint call_id); void nm_dispatcher_call_cancel (NMDispatcherCallId *call_id);
void nm_dispatcher_init (void);
#endif /* __NM_DISPATCHER_H__ */ #endif /* __NM_DISPATCHER_H__ */

View File

@@ -103,7 +103,7 @@ typedef struct {
char *username; char *username;
VpnState vpn_state; VpnState vpn_state;
guint dispatcher_id; NMDispatcherCallId *dispatcher_id;
NMActiveConnectionStateReason failure_reason; NMActiveConnectionStateReason failure_reason;
NMVpnServiceState service_state; NMVpnServiceState service_state;
@@ -418,22 +418,28 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
} }
static void static void
dispatcher_pre_down_done (guint call_id, gpointer user_data) dispatcher_pre_down_done (NMDispatcherCallId *call_id, gpointer user_data)
{ {
NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
priv->dispatcher_id = 0; nm_assert (call_id);
nm_assert (priv->dispatcher_id == call_id);
priv->dispatcher_id = NULL;
_set_vpn_state (self, STATE_DISCONNECTED, NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE); _set_vpn_state (self, STATE_DISCONNECTED, NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE);
} }
static void static void
dispatcher_pre_up_done (guint call_id, gpointer user_data) dispatcher_pre_up_done (NMDispatcherCallId *call_id, gpointer user_data)
{ {
NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
priv->dispatcher_id = 0; nm_assert (call_id);
nm_assert (priv->dispatcher_id == call_id);
priv->dispatcher_id = NULL;
_set_vpn_state (self, STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); _set_vpn_state (self, STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
} }
@@ -442,10 +448,8 @@ dispatcher_cleanup (NMVpnConnection *self)
{ {
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
if (priv->dispatcher_id) { if (priv->dispatcher_id)
nm_dispatcher_call_cancel (priv->dispatcher_id); nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher_id));
priv->dispatcher_id = 0;
}
} }
static void static void