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_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
noinst_LTLIBRARIES += \
@@ -3905,25 +3924,6 @@ dispatcher_cppflags = \
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \
$(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/nm-dispatcher-utils.c \
dispatcher/nm-dispatcher-utils.h \
@@ -3939,8 +3939,6 @@ dispatcher_nm_dispatcher_SOURCES = \
dispatcher/nm-dispatcher.c \
$(NULL)
nodist_dispatcher_nm_dispatcher_SOURCES = $(dispatcher_nmdbus_dispatcher_sources)
dispatcher_nm_dispatcher_CPPFLAGS = $(dispatcher_cppflags)
dispatcher_nm_dispatcher_LDFLAGS = \
@@ -3998,12 +3996,21 @@ dispatcher_tests_test_dispatcher_envp_CPPFLAGS = \
-I$(srcdir)/libnm \
-I$(builddir)/libnm \
-I$(srcdir)/dispatcher \
-I$(builddir)/dispatcher \
-DNETWORKMANAGER_COMPILATION_TEST \
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \
$(GLIB_CFLAGS) \
$(SANITIZER_EXEC_CFLAGS) \
$(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 = \
$(SANITIZER_EXEC_LDFLAGS) \
$(NULL)

View File

@@ -1,6 +1,12 @@
[Unit]
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]
Type=dbus
BusName=org.freedesktop.nm_dispatcher

View File

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

View File

@@ -34,74 +34,24 @@
#include "nm-libnm-core-aux/nm-dispatcher-api.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 {
GObject parent;
/* Private data */
NMDBusDispatcher *dbus_dispatcher;
static struct {
GDBusConnection *dbus_connection;
GMainLoop *loop;
gboolean debug;
gboolean persist;
guint quit_id;
guint request_id_counter;
gboolean ever_acquired_name;
bool exit_with_failure;
Request *current_request;
GQueue *requests_waiting;
int num_requests_pending;
} Handler;
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);
} gl;
typedef struct {
Request *request;
@@ -117,8 +67,6 @@ typedef struct {
} ScriptInfo;
struct Request {
Handler *handler;
guint request_id;
GDBusMethodInvocation *context;
@@ -135,52 +83,86 @@ struct Request {
/*****************************************************************************/
#define __LOG_print(print_cmd, _request, _script, ...) \
#define __LOG_print(print_cmd, ...) \
G_STMT_START { \
nm_assert ((_request) && (!(_script) || (_script)->request == (_request))); \
print_cmd ("req:%u '%s'%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
(_request)->request_id, \
(_request)->action, \
(_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__); \
if (FALSE) { \
/* g_message() alone does not warn about invalid format. Add a dummy printf() statement to
* get a compiler warning about wrong format. */ \
printf (__VA_ARGS__); \
} \
print_cmd (__VA_ARGS__); \
} G_STMT_END
static gboolean
_LOG_R_D_enabled (const Request *request)
{
return request->debug;
}
#define __LOG_print_R(print_cmd, _request, ...) \
G_STMT_START { \
__LOG_print (print_cmd, \
"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_R_I(_request, ...) _LOG(_request, NULL, TRUE, g_info, __VA_ARGS__)
#define _LOG_R_W(_request, ...) _LOG(_request, NULL, TRUE, g_warning, __VA_ARGS__)
#define __LOG_print_S(print_cmd, _request, _script, ...) \
G_STMT_START { \
__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_S_I(_script, ...) _LOG(NULL, _script, TRUE, g_info, __VA_ARGS__)
#define _LOG_S_W(_script, ...) _LOG(NULL, _script, TRUE, g_warning, __VA_ARGS__)
#define _LOG_X_(enabled_cmd, print_cmd, ...) \
G_STMT_START { \
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
quit_timeout_cb (gpointer user_data)
{
g_main_loop_quit (loop);
return FALSE;
gl.quit_id = 0;
g_main_loop_quit (gl.loop);
return G_SOURCE_REMOVE;
}
static void
quit_timeout_reschedule (void)
{
if (!persist) {
nm_clear_g_source (&quit_id);
quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
if (!gl.persist) {
nm_clear_g_source (&gl.quit_id);
gl.quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
}
}
/**
* next_request:
*
* @h: the handler
* @request: (allow-none): the request to set as next. If %NULL, dequeue the next
* waiting request. Otherwise, try to set the given request.
*
@@ -240,27 +222,27 @@ quit_timeout_reschedule (void)
* a new request as current.
*/
static gboolean
next_request (Handler *h, Request *request)
next_request (Request *request)
{
if (request) {
if (h->current_request) {
g_queue_push_tail (h->requests_waiting, request);
if (gl.current_request) {
g_queue_push_tail (gl.requests_waiting, request);
return FALSE;
}
} else {
/* when calling next_request() without explicit @request, we always
* forcefully clear @current_request. That one is certainly
* 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)
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;
}
@@ -280,7 +262,6 @@ complete_request (Request *request)
GVariantBuilder results;
GVariant *ret;
guint i;
Handler *handler = request->handler;
nm_assert (request);
@@ -301,16 +282,16 @@ complete_request (Request *request)
ret = g_variant_new ("(a(sus))", &results);
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)
handler->current_request = NULL;
if (gl.current_request == request)
gl.current_request = NULL;
request_free (request);
g_assert_cmpuint (handler->num_requests_pending, >, 0);
if (--handler->num_requests_pending <= 0) {
nm_assert (!handler->current_request && !g_queue_peek_head (handler->requests_waiting));
g_assert_cmpuint (gl.num_requests_pending, >, 0);
if (--gl.num_requests_pending <= 0) {
nm_assert (!gl.current_request && !g_queue_peek_head (gl.requests_waiting));
quit_timeout_reschedule ();
}
}
@@ -318,7 +299,6 @@ complete_request (Request *request)
static void
complete_script (ScriptInfo *script)
{
Handler *handler;
Request *request;
gboolean wait = script->wait;
@@ -331,9 +311,7 @@ complete_script (ScriptInfo *script)
return;
}
handler = request->handler;
nm_assert (!wait || handler->current_request == request);
nm_assert (!wait || gl.current_request == request);
/* Try to complete the request. @request will be possibly free'd,
* 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
* there are "wait" scripts ready to run, launch them.
*/
if ( handler->current_request == request
&& handler->current_request->num_scripts_nowait == 0) {
if ( gl.current_request == request
&& gl.current_request->num_scripts_nowait == 0) {
if (dispatch_one_script (handler->current_request))
if (dispatch_one_script (gl.current_request))
return;
complete_request (handler->current_request);
complete_request (gl.current_request);
} else
return;
} else {
@@ -365,11 +343,11 @@ complete_script (ScriptInfo *script)
* processed because only requests with "wait" scripts can become
* @current_request. As there can only be one "wait" script running
* 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)) {
request = handler->current_request;
while (next_request (NULL)) {
request = gl.current_request;
if (dispatch_one_script (request))
return;
@@ -418,7 +396,7 @@ script_watch_cb (GPid pid, int status, gpointer user_data)
}
if (script->result == DISPATCH_RESULT_SUCCESS) {
_LOG_S_D (script, "complete");
_LOG_S_T (script, "complete");
} else {
script->result = DISPATCH_RESULT_FAILED;
_LOG_S_W (script, "complete: failed with %s", script->error);
@@ -533,7 +511,7 @@ script_dispatch (ScriptInfo *script)
argv[2] = request->action;
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)) {
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
_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;
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);
if (!(dir = g_dir_open (dirname, 0, &error))) {
g_message ("find-scripts: Failed to open dispatcher directory '%s': %s",
dirname, error->message);
if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
_LOG_R_W (request, "find-scripts: Failed to open dispatcher directory '%s': %s",
dirname, error->message);
}
g_error_free (error);
return;
}
@@ -615,26 +595,28 @@ _find_scripts (GHashTable *scripts, const char *base, const char *subdir)
}
static GSList *
find_scripts (const char *str_action)
find_scripts (Request *request)
{
gs_unref_hashtable GHashTable *scripts = NULL;
GSList *script_list = NULL;
GHashTableIter iter;
const char *subdir = NULL;
const char *subdir;
char *path;
char *filename;
if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0
|| strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0)
if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_UP,
NMD_ACTION_VPN_PRE_UP))
subdir = "pre-up.d";
else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0
|| strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0)
else if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_DOWN,
NMD_ACTION_VPN_PRE_DOWN))
subdir = "pre-down.d";
else
subdir = NULL;
scripts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
_find_scripts (scripts, NMLIBDIR, subdir);
_find_scripts (scripts, NMCONFDIR, subdir);
_find_scripts (request, scripts, NMLIBDIR, subdir);
_find_scripts (request, scripts, NMCONFDIR, subdir);
g_hash_table_iter_init (&iter, scripts);
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);
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))
; /* silently skip. */
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 {
/* success */
script_list = g_slist_prepend (script_list, g_strdup (path));
@@ -695,27 +677,25 @@ script_must_wait (const char *path)
return TRUE;
}
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
_method_call_action (GDBusMethodInvocation *invocation,
GVariant *parameters)
{
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 *iter;
Request *request;
@@ -723,33 +703,65 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
guint i, num_nowait = 0;
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->request_id = ++request_id_counter;
request->handler = h;
request->debug = request_debug || debug;
request->context = context;
request->action = g_strdup (str_action);
request->request_id = ++gl.request_id_counter;
request->debug = debug || gl.debug;
request->context = invocation;
request->action = g_strdup (action);
request->envp = nm_dispatcher_utils_construct_envp (str_action,
connection_dict,
connection_props,
device_props,
device_proxy_props,
device_ip4_props,
device_ip6_props,
device_dhcp4_props,
device_dhcp6_props,
request->envp = nm_dispatcher_utils_construct_envp (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_props,
vpn_ip4_props,
vpn_ip6_props,
vpn_proxy_properties,
vpn_ip4_config,
vpn_ip6_config,
&request->iface,
&error_message);
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)) {
ScriptInfo *s;
@@ -761,11 +773,11 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
}
g_slist_free (sorted_scripts);
_LOG_R_I (request, "new request (%u scripts)", request->scripts->len);
if ( _LOG_R_D_enabled (request)
_LOG_R_D (request, "new request (%u scripts)", request->scripts->len);
if ( _LOG_R_T_enabled (request)
&& request->envp) {
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) {
@@ -774,18 +786,18 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
if (error_message)
_LOG_R_W (request, "completed: invalid request: %s", error_message);
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);
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_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++) {
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.
* Try next_request() to schedule the request for
* execution. This either enqueues the request or
* sets it as h->current_request. */
if (next_request (h, request)) {
* sets it as gl.current_request. */
if (next_request (request)) {
/* @request is now @current_request. Go ahead and
* schedule the first wait script. */
if (!dispatch_one_script (request)) {
@@ -809,7 +821,7 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
* request. Try complete_request(). */
complete_request (request);
if (next_request (h, NULL)) {
if (next_request (NULL)) {
/* As @request was successfully scheduled as next_request(), there is no
* other request in queue that can be scheduled afterwards. Assert against
* 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
* of the scripts). It will be either completed now, or later
* 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
* not the current request anyway and does not interfere with requests
* that have any "wait" scripts. */
complete_request (request);
}
return TRUE;
}
static gboolean ever_acquired_name = FALSE;
static void
on_name_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
ever_acquired_name = TRUE;
gl.ever_acquired_name = TRUE;
}
static void
@@ -848,22 +856,79 @@ on_name_lost (GDBusConnection *connection,
gpointer user_data)
{
if (!connection) {
if (!ever_acquired_name) {
g_warning ("Could not get the system bus. Make sure the message bus daemon is running!");
exit (1);
if (!gl.ever_acquired_name) {
_LOG_X_W ("Could not get the system bus. Make sure the message bus daemon is running!");
gl.exit_with_failure = TRUE;
} else {
g_message ("System bus stopped. Exiting");
exit (0);
_LOG_X_I ("System bus stopped. Exiting");
}
} else if (!ever_acquired_name) {
g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service.");
exit (1);
} else {
g_message ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting");
exit (0);
}
} else if (!gl.ever_acquired_name) {
_LOG_X_W ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service.");
gl.exit_with_failure = TRUE;
} else
_LOG_X_I ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting");
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
log_handler (const char *log_domain,
GLogLevelFlags log_level,
@@ -918,42 +983,55 @@ signal_handler (gpointer user_data)
{
int signo = GPOINTER_TO_INT (user_data);
g_message ("Caught signal %d, shutting down...", signo);
g_main_loop_quit (loop);
_LOG_X_I ("Caught signal %d, shutting down...", signo);
g_main_loop_quit (gl.loop);
return G_SOURCE_REMOVE;
return G_SOURCE_CONTINUE;
}
int
main (int argc, char **argv)
static gboolean
parse_command_line (int *p_argc,
char ***p_argv,
GError **error)
{
GOptionContext *opt_ctx;
GError *error = NULL;
GDBusConnection *bus;
Handler *handler;
GOptionEntry entries[] = {
{ "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL },
{ "persist", 0, 0, G_OPTION_ARG_NONE, &persist, "Don't quit after a short timeout", NULL },
{ "debug", 0, 0, G_OPTION_ARG_NONE, &gl.debug, "Output to console rather than syslog", NULL },
{ "persist", 0, 0, G_OPTION_ARG_NONE, &gl.persist, "Don't quit after a short timeout", NULL },
{ NULL }
};
gboolean success;
opt_ctx = g_option_context_new (NULL);
g_option_context_set_summary (opt_ctx, "Executes scripts upon actions by NetworkManager.");
g_option_context_add_main_entries (opt_ctx, entries, NULL);
if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) {
g_warning ("Error parsing command line arguments: %s", error->message);
g_error_free (error);
return 1;
}
success = g_option_context_parse (opt_ctx, p_argc, p_argv, error);
g_option_context_free (opt_ctx);
g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM));
g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT));
return success;
}
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")) {
/* we log our regular messages using g_debug() and g_info().
* When we redirect glib logging to syslog, there is no problem.
@@ -964,45 +1042,75 @@ main (int argc, char **argv)
} else
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);
if (!bus) {
g_warning ("Could not get the system bus (%s). Make sure the message bus daemon is running!",
error->message);
g_error_free (error);
return 1;
gl.dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (!gl.dbus_connection) {
_LOG_X_W ("Could not get the system bus (%s). Make sure the message bus daemon is running!",
error->message);
gl.exit_with_failure = TRUE;
goto done;
}
handler = g_object_new (HANDLER_TYPE, NULL);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (handler->dbus_dispatcher),
bus,
NM_DISPATCHER_DBUS_PATH,
&error);
if (error) {
g_warning ("Could not export Dispatcher D-Bus interface: %s", error->message);
g_error_free (error);
return 1;
gl.requests_waiting = g_queue_new ();
dbus_regist_id = g_dbus_connection_register_object (gl.dbus_connection,
NM_DISPATCHER_DBUS_PATH,
interface_info,
NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable),
NULL,
NULL,
&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,
NM_DISPATCHER_DBUS_SERVICE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
NULL, NULL);
g_object_unref (bus);
dbus_own_name_id = g_bus_own_name_on_connection (gl.dbus_connection,
NM_DISPATCHER_DBUS_SERVICE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
on_name_lost,
NULL, NULL);
quit_timeout_reschedule ();
g_main_loop_run (loop);
g_main_loop_run (gl.loop);
g_queue_free (handler->requests_waiting);
g_object_unref (handler);
done:
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 ();
return 0;
return gl.exit_with_failure ? 1 : 0;
}

View File

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

View File

@@ -28,6 +28,8 @@
#include "nm-utils/nm-test-utils.h"
#include "nmdbus-dispatcher.h"
#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 ();
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/gdbus-codegen", test_gdbus_codegen);
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
* of a certain type. */
#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
#define _NM_ENSURE_TYPE(type, value) (value)
#define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value))
#endif
#if _NM_CC_SUPPORT_GENERIC

View File

@@ -328,16 +328,18 @@ typedef struct _NMDevicePrivate {
ActivationHandleData act_handle4; /* for layer2 and IPv4. */
ActivationHandleData act_handle6;
guint recheck_assume_id;
struct {
guint call_id;
NMDeviceStateReason available_reason;
NMDeviceStateReason unavailable_reason;
} recheck_available;
} recheck_available;
struct {
guint call_id;
NMDispatcherCallId *call_id;
NMDeviceState post_state;
NMDeviceStateReason post_state_reason;
} dispatcher;
} dispatcher;
/* Link stuff */
guint link_connected_id;
@@ -12373,29 +12375,31 @@ nm_device_get_ip6_config (NMDevice *self)
/*****************************************************************************/
static void
static gboolean
dispatcher_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->dispatcher.call_id) {
nm_dispatcher_call_cancel (priv->dispatcher.call_id);
priv->dispatcher.call_id = 0;
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
}
if (!priv->dispatcher.call_id)
return FALSE;
nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher.call_id));
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
return TRUE;
}
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);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (call_id == priv->dispatcher.call_id);
priv->dispatcher.call_id = 0;
nm_device_queue_state (self, priv->dispatcher.post_state,
priv->dispatcher.call_id = NULL;
nm_device_queue_state (self,
priv->dispatcher.post_state,
priv->dispatcher.post_state_reason);
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
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);
if (priv->dispatcher.call_id != 0) {
g_warn_if_reached ();
dispatcher_cleanup (self);
}
if (dispatcher_cleanup (self))
nm_assert_not_reached ();
priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES;
priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
@@ -14822,7 +14824,7 @@ deactivate_async_ready (NMDevice *self,
}
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);
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;
priv->dispatcher.call_id = 0;
priv->dispatcher.call_id = NULL;
priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
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,
manager);
nm_dispatcher_init ();
g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
if (!nm_manager_start (manager, &error)) {

View File

@@ -44,8 +44,121 @@
#define _NMLOG_DOMAIN LOGD_DISPATCH
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dispatcher", __VA_ARGS__)
static GDBusProxy *dispatcher_proxy;
static GHashTable *requests = NULL;
#define _NMLOG2_DOMAIN LOGD_DISPATCH
#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
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);
}
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 *
dispatch_result_to_string (DispatchResult result)
{
@@ -339,63 +419,71 @@ dispatch_result_to_string (DispatchResult result)
}
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;
guint32 result;
g_return_if_fail (results != NULL);
g_variant_get (v_results, "(a(sus))", &results);
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;
}
while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) {
if (result == DISPATCH_RESULT_SUCCESS) {
_LOGD ("(%u) %s succeeded", request_id, script);
_LOG2D (request_id, log_ifname, log_con_uuid, "%s succeeded", script);
} else {
_LOGW ("(%u) %s failed (%s): %s",
request_id,
script,
dispatch_result_to_string (result),
err);
_LOG2W (request_id,
log_ifname,
log_con_uuid,
"%s failed (%s): %s",
script,
dispatch_result_to_string (result),
err);
}
}
}
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;
GVariant *ret;
GVariantIter *results;
GError *error = NULL;
gs_unref_variant GVariant *ret = NULL;
gs_free_error GError *error = NULL;
NMDispatcherCallId *call_id = user_data;
ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
G_VARIANT_TYPE ("(a(sus))"),
&error);
if (ret) {
g_variant_get (ret, "(a(sus))", &results);
dispatcher_results_process (info->request_id, info->action, results);
g_variant_iter_free (results);
g_variant_unref (ret);
} else {
nm_assert ((gpointer) source == gl.dbus_connection);
ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
result,
&error);
if (!ret) {
if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) {
g_dbus_error_strip_remote_error (error);
_LOGW ("(%u) failed to call dispatcher scripts: %s",
info->request_id, error->message);
_LOG3W (call_id, "failed to call dispatcher scripts: %s",
error->message);
} else {
_LOGD ("(%u) failed to call dispatcher scripts: %s",
info->request_id, error->message);
_LOG3D (call_id, "failed to call dispatcher scripts: %s",
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)
info->callback (info->request_id, info->user_data);
g_hash_table_remove (gl.requests, call_id);
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[] = {
@@ -416,8 +504,9 @@ static const char *action_table[] = {
static const char *
action_to_string (NMDispatcherAction action)
{
g_assert ((gsize) action < G_N_ELEMENTS (action_table));
return action_table[action];
if (G_UNLIKELY ((gsize) action >= G_N_ELEMENTS (action_table)))
g_return_val_if_reached (NULL);
return action_table[(gsize) action];
}
static gboolean
@@ -434,7 +523,7 @@ _dispatcher_call (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id)
NMDispatcherCallId **out_call_id)
{
GVariant *connection_dict;
GVariantBuilder connection_props;
@@ -442,47 +531,57 @@ _dispatcher_call (NMDispatcherAction action,
GVariantBuilder device_proxy_props;
GVariantBuilder device_ip4_props;
GVariantBuilder device_ip6_props;
GVariant *device_dhcp4_props = NULL;
GVariant *device_dhcp6_props = NULL;
gs_unref_variant GVariant *parameters_floating = NULL;
gs_unref_variant GVariant *device_dhcp4_props = NULL;
gs_unref_variant GVariant *device_dhcp6_props = NULL;
GVariantBuilder vpn_proxy_props;
GVariantBuilder vpn_ip4_props;
GVariantBuilder vpn_ip6_props;
DispatchInfo *info = NULL;
gboolean success = FALSE;
GError *error = NULL;
static guint request_counter = 0;
guint reqid = ++request_counter;
NMDispatcherCallId *call_id;
guint request_id;
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;
/* Wrapping protection */
if (G_UNLIKELY (!reqid))
reqid = ++request_counter;
log_ifname = device ? nm_device_get_iface (device) : NULL;
log_con_uuid = settings_connection ? nm_settings_connection_get_uuid (settings_connection) : NULL;
g_assert (!blocking || (!callback && !user_data));
_ensure_requests ();
request_id = ++gl.request_id_counter;
if (G_UNLIKELY (!request_id))
request_id = ++gl.request_id_counter;
/* All actions except 'hostname' and 'connectivity-change' require a device */
if ( action == NM_DISPATCHER_ACTION_HOSTNAME
|| action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
_LOGD ("(%u) dispatching action '%s'%s",
reqid, action_to_string (action),
blocking
_LOG2D (request_id,
log_ifname,
log_con_uuid,
"dispatching action '%s'%s",
action_to_string (action),
blocking
? " (blocking)"
: (callback ? " (with callback)" : ""));
} else {
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
_LOGD ("(%u) (%s) dispatching action '%s'%s",
reqid,
vpn_iface ?: nm_device_get_iface(device),
action_to_string (action),
blocking
? " (blocking)"
: (callback ? " (with callback)" : ""));
_LOG2D (request_id,
log_ifname,
log_con_uuid,
"(%s) dispatching action '%s'%s",
vpn_iface ?: nm_device_get_iface (device),
action_to_string (action),
blocking
? " (blocking)"
: (callback ? " (with callback)" : ""));
}
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);
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 */
if (blocking) {
GVariant *ret;
GVariantIter *results;
gs_unref_variant GVariant *ret = NULL;
gs_free_error GError *error = NULL;
ret = _nm_dbus_proxy_call_sync (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_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 {
ret = g_dbus_connection_call_sync (gl.dbus_connection,
NM_DISPATCHER_DBUS_SERVICE,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_INTERFACE,
"Action",
g_steal_pointer (&parameters_floating),
G_VARIANT_TYPE ("(a(sus))"),
G_DBUS_CALL_FLAGS_NONE,
CALL_TIMEOUT,
NULL,
&error);
if (!ret) {
g_dbus_error_strip_remote_error (error);
_LOGW ("(%u) failed: %s", reqid, error->message);
g_clear_error (&error);
success = FALSE;
_LOG2W (request_id, log_ifname, log_con_uuid, "failed: %s", error->message);
return FALSE;
}
} else {
info = g_malloc0 (sizeof (*info));
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;
dispatcher_results_process (request_id, log_ifname, log_con_uuid, ret);
return TRUE;
}
g_variant_unref (device_dhcp4_props);
g_variant_unref (device_dhcp6_props);
call_id = dispatcher_call_id_new (request_id,
action,
callback,
user_data,
log_ifname,
log_con_uuid);
if (success && info) {
/* Track the request in case of cancelation */
g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info);
if (out_call_id)
*out_call_id = info->request_id;
} else if (out_call_id)
*out_call_id = 0;
return success;
g_dbus_connection_call (gl.dbus_connection,
NM_DISPATCHER_DBUS_SERVICE,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_INTERFACE,
"Action",
g_steal_pointer (&parameters_floating),
G_VARIANT_TYPE ("(a(sus))"),
G_DBUS_CALL_FLAGS_NONE,
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
nm_dispatcher_call_hostname (NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id)
NMDispatcherCallId **out_call_id)
{
return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE,
NULL, NULL, NULL, FALSE,
@@ -673,7 +754,7 @@ nm_dispatcher_call_device (NMDispatcherAction action,
NMActRequest *act_request,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id)
NMDispatcherCallId **out_call_id)
{
nm_assert (NM_IS_DEVICE (device));
if (!act_request) {
@@ -757,7 +838,7 @@ nm_dispatcher_call_vpn (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id)
NMDispatcherCallId **out_call_id)
{
return _dispatcher_call (action, FALSE,
parent_device,
@@ -821,7 +902,7 @@ gboolean
nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id)
NMDispatcherCallId **out_call_id)
{
return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE,
NULL, NULL, NULL, FALSE,
@@ -831,40 +912,16 @@ nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state,
}
void
nm_dispatcher_call_cancel (guint call_id)
nm_dispatcher_call_cancel (NMDispatcherCallId *call_id)
{
DispatchInfo *info;
_ensure_requests ();
if ( !call_id
|| g_hash_table_lookup (gl.requests, call_id) != call_id
|| !call_id->callback)
g_return_if_reached ();
/* Canceling just means the callback doesn't get called, so set the
* DispatcherInfo's callback to NULL.
*/
info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id));
g_return_if_fail (info);
if (info && info->callback) {
_LOGD ("(%u) cancelling dispatcher callback action", call_id);
info->callback = NULL;
}
_LOG3D (call_id, "cancelling dispatcher callback action");
call_id->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
} 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,
gpointer user_data,
guint *out_call_id);
NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_device (NMDispatcherAction action,
NMDevice *device,
NMActRequest *act_request,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id);
NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action,
NMDevice *device,
@@ -66,7 +68,7 @@ gboolean nm_dispatcher_call_vpn (NMDispatcherAction action,
NMIP6Config *vpn_ip6_config,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id);
NMDispatcherCallId **out_call_id);
gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
NMSettingsConnection *settings_connection,
@@ -80,10 +82,8 @@ gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
gboolean nm_dispatcher_call_connectivity (NMConnectivityState state,
NMDispatcherFunc callback,
gpointer user_data,
guint *out_call_id);
NMDispatcherCallId **out_call_id);
void nm_dispatcher_call_cancel (guint call_id);
void nm_dispatcher_init (void);
void nm_dispatcher_call_cancel (NMDispatcherCallId *call_id);
#endif /* __NM_DISPATCHER_H__ */

View File

@@ -103,7 +103,7 @@ typedef struct {
char *username;
VpnState vpn_state;
guint dispatcher_id;
NMDispatcherCallId *dispatcher_id;
NMActiveConnectionStateReason failure_reason;
NMVpnServiceState service_state;
@@ -418,22 +418,28 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
}
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);
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);
}
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);
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);
}
@@ -442,10 +448,8 @@ dispatcher_cleanup (NMVpnConnection *self)
{
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
if (priv->dispatcher_id) {
nm_dispatcher_call_cancel (priv->dispatcher_id);
priv->dispatcher_id = 0;
}
if (priv->dispatcher_id)
nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher_id));
}
static void