1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2004 - 2012 Red Hat, Inc.
19 * Copyright (C) 2005 - 2008 Novell, Inc.
22 #include "nm-default.h"
27 #include "nm-dispatcher.h"
28 #include "nm-dispatcher-api.h"
29 #include "NetworkManagerUtils.h"
31 #include "nm-device.h"
32 #include "nm-dhcp4-config.h"
33 #include "nm-dhcp6-config.h"
34 #include "nm-ip4-config.h"
35 #include "nm-ip6-config.h"
36 #include "nm-settings-connection.h"
37 #include "nm-platform.h"
38 #include "nm-core-internal.h"
40 #define CALL_TIMEOUT (1000 * 60 * 10) /* 10 minutes for all scripts */
42 #define _NMLOG_DOMAIN LOGD_DISPATCH
43 #define _NMLOG_PREFIX_NAME "dispatcher"
44 #define _NMLOG(level, ...) \
46 nm_log ((level), _NMLOG_DOMAIN, \
47 "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
49 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
52 static GDBusProxy *dispatcher_proxy;
53 static GHashTable *requests = NULL;
56 GFileMonitor *monitor;
57 const char *const description;
58 const char *const dir;
59 const guint16 dir_len;
64 MONITOR_INDEX_DEFAULT,
66 MONITOR_INDEX_PRE_DOWN,
69 static Monitor monitors[3] = {
70 #define MONITORS_INIT_SET(INDEX, USE, SCRIPT_DIR) [INDEX] = { .dir_len = NM_STRLEN (SCRIPT_DIR), .dir = SCRIPT_DIR, .description = ("" USE), .has_scripts = TRUE }
71 MONITORS_INIT_SET (MONITOR_INDEX_DEFAULT, "default", NMD_SCRIPT_DIR_DEFAULT),
72 MONITORS_INIT_SET (MONITOR_INDEX_PRE_UP, "pre-up", NMD_SCRIPT_DIR_PRE_UP),
73 MONITORS_INIT_SET (MONITOR_INDEX_PRE_DOWN, "pre-down", NMD_SCRIPT_DIR_PRE_DOWN),
77 _get_monitor_by_action (DispatcherAction action)
80 case DISPATCHER_ACTION_PRE_UP:
81 case DISPATCHER_ACTION_VPN_PRE_UP:
82 return &monitors[MONITOR_INDEX_PRE_UP];
83 case DISPATCHER_ACTION_PRE_DOWN:
84 case DISPATCHER_ACTION_VPN_PRE_DOWN:
85 return &monitors[MONITOR_INDEX_PRE_DOWN];
87 return &monitors[MONITOR_INDEX_DEFAULT];
92 dump_ip4_to_props (NMIP4Config *ip4, GVariantBuilder *builder)
94 GVariantBuilder int_builder;
96 const NMPlatformIP4Address *addr;
97 const NMPlatformIP4Route *route;
101 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau"));
102 n = nm_ip4_config_get_num_addresses (ip4);
103 for (i = 0; i < n; i++) {
104 addr = nm_ip4_config_get_address (ip4, i);
105 array[0] = addr->address;
106 array[1] = addr->plen;
107 array[2] = (i == 0) ? nm_ip4_config_get_gateway (ip4) : 0;
108 g_variant_builder_add (&int_builder, "@au",
109 g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
110 array, 3, sizeof (guint32)));
112 g_variant_builder_add (builder, "{sv}",
114 g_variant_builder_end (&int_builder));
117 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("au"));
118 n = nm_ip4_config_get_num_nameservers (ip4);
119 for (i = 0; i < n; i++)
120 g_variant_builder_add (&int_builder, "u", nm_ip4_config_get_nameserver (ip4, i));
121 g_variant_builder_add (builder, "{sv}",
123 g_variant_builder_end (&int_builder));
126 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("as"));
127 n = nm_ip4_config_get_num_domains (ip4);
128 for (i = 0; i < n; i++)
129 g_variant_builder_add (&int_builder, "s", nm_ip4_config_get_domain (ip4, i));
130 g_variant_builder_add (builder, "{sv}",
132 g_variant_builder_end (&int_builder));
135 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("au"));
136 n = nm_ip4_config_get_num_wins (ip4);
137 for (i = 0; i < n; i++)
138 g_variant_builder_add (&int_builder, "u", nm_ip4_config_get_wins (ip4, i));
139 g_variant_builder_add (builder, "{sv}",
141 g_variant_builder_end (&int_builder));
144 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aau"));
145 n = nm_ip4_config_get_num_routes (ip4);
146 for (i = 0; i < n; i++) {
147 route = nm_ip4_config_get_route (ip4, i);
148 array[0] = route->network;
149 array[1] = route->plen;
150 array[2] = route->gateway;
151 array[3] = route->metric;
152 g_variant_builder_add (&int_builder, "@au",
153 g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32,
154 array, 4, sizeof (guint32)));
156 g_variant_builder_add (builder, "{sv}",
158 g_variant_builder_end (&int_builder));
162 dump_ip6_to_props (NMIP6Config *ip6, GVariantBuilder *builder)
164 GVariantBuilder int_builder;
166 const NMPlatformIP6Address *addr;
167 const struct in6_addr *gw_bytes;
168 const NMPlatformIP6Route *route;
172 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuay)"));
173 n = nm_ip6_config_get_num_addresses (ip6);
174 for (i = 0; i < n; i++) {
175 addr = nm_ip6_config_get_address (ip6, i);
176 gw_bytes = nm_ip6_config_get_gateway (ip6);
177 ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
179 sizeof (struct in6_addr), 1);
180 gw = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
181 (i == 0 && gw_bytes) ? gw_bytes : &in6addr_any,
182 sizeof (struct in6_addr), 1);
183 g_variant_builder_add (&int_builder, "(@ayu@ay)", ip, addr->plen, gw);
185 g_variant_builder_add (builder, "{sv}",
187 g_variant_builder_end (&int_builder));
190 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("aay"));
191 n = nm_ip6_config_get_num_nameservers (ip6);
192 for (i = 0; i < n; i++) {
193 ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
194 nm_ip6_config_get_nameserver (ip6, i),
195 sizeof (struct in6_addr), 1);
196 g_variant_builder_add (&int_builder, "@ay", ip);
198 g_variant_builder_add (builder, "{sv}",
200 g_variant_builder_end (&int_builder));
203 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("as"));
204 n = nm_ip6_config_get_num_domains (ip6);
205 for (i = 0; i < n; i++)
206 g_variant_builder_add (&int_builder, "s", nm_ip6_config_get_domain (ip6, i));
207 g_variant_builder_add (builder, "{sv}",
209 g_variant_builder_end (&int_builder));
212 g_variant_builder_init (&int_builder, G_VARIANT_TYPE ("a(ayuayu)"));
213 n = nm_ip6_config_get_num_routes (ip6);
214 for (i = 0; i < n; i++) {
215 route = nm_ip6_config_get_route (ip6, i);
216 ip = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
218 sizeof (struct in6_addr), 1);
219 gw = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
221 sizeof (struct in6_addr), 1);
222 g_variant_builder_add (&int_builder, "(@ayu@ayu)", ip, route->plen, gw, route->metric);
224 g_variant_builder_add (builder, "{sv}",
226 g_variant_builder_end (&int_builder));
230 fill_device_props (NMDevice *device,
231 GVariantBuilder *dev_builder,
232 GVariantBuilder *ip4_builder,
233 GVariantBuilder *ip6_builder,
234 GVariant **dhcp4_props,
235 GVariant **dhcp6_props)
237 NMIP4Config *ip4_config;
238 NMIP6Config *ip6_config;
239 NMDhcp4Config *dhcp4_config;
240 NMDhcp6Config *dhcp6_config;
242 /* If the action is for a VPN, send the VPN's IP interface instead of the device's */
243 g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_IP_INTERFACE,
244 g_variant_new_string (nm_device_get_ip_iface (device)));
245 g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_INTERFACE,
246 g_variant_new_string (nm_device_get_iface (device)));
247 g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_TYPE,
248 g_variant_new_uint32 (nm_device_get_device_type (device)));
249 g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_STATE,
250 g_variant_new_uint32 (nm_device_get_state (device)));
251 if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (device)))
252 g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_PATH,
253 g_variant_new_object_path (nm_exported_object_get_path (NM_EXPORTED_OBJECT (device))));
255 ip4_config = nm_device_get_ip4_config (device);
257 dump_ip4_to_props (ip4_config, ip4_builder);
259 ip6_config = nm_device_get_ip6_config (device);
261 dump_ip6_to_props (ip6_config, ip6_builder);
263 dhcp4_config = nm_device_get_dhcp4_config (device);
265 *dhcp4_props = nm_dhcp4_config_get_options (dhcp4_config);
267 dhcp6_config = nm_device_get_dhcp6_config (device);
269 *dhcp6_props = nm_dhcp6_config_get_options (dhcp6_config);
273 fill_vpn_props (NMIP4Config *ip4_config,
274 NMIP6Config *ip6_config,
275 GVariantBuilder *ip4_builder,
276 GVariantBuilder *ip6_builder)
279 dump_ip4_to_props (ip4_config, ip4_builder);
281 dump_ip6_to_props (ip6_config, ip6_builder);
285 DispatcherAction action;
287 DispatcherFunc callback;
293 dispatcher_info_free (DispatchInfo *info)
296 g_source_remove (info->idle_id);
301 _ensure_requests (void)
303 if (G_UNLIKELY (requests == NULL)) {
304 requests = g_hash_table_new_full (g_direct_hash,
307 (GDestroyNotify) dispatcher_info_free);
312 dispatcher_info_cleanup (DispatchInfo *info)
314 g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id));
318 dispatch_result_to_string (DispatchResult result)
321 case DISPATCH_RESULT_UNKNOWN:
323 case DISPATCH_RESULT_SUCCESS:
325 case DISPATCH_RESULT_EXEC_FAILED:
326 return "exec failed";
327 case DISPATCH_RESULT_FAILED:
329 case DISPATCH_RESULT_TIMEOUT:
332 g_assert_not_reached ();
336 dispatcher_results_process (guint request_id, DispatcherAction action, GVariantIter *results)
338 const char *script, *err;
340 const Monitor *monitor = _get_monitor_by_action (action);
342 g_return_if_fail (results != NULL);
344 if (g_variant_iter_n_children (results) == 0) {
345 _LOGD ("(%u) succeeded but no scripts invoked", request_id);
349 while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) {
350 const char *script_validation_msg = "";
353 script_validation_msg = " (path is NULL)";
354 script = "(unknown)";
355 } else if (!strncmp (script, monitor->dir, monitor->dir_len) /* check: prefixed by script directory */
356 && script[monitor->dir_len] == '/' && script[monitor->dir_len+1] /* check: with additional "/?" */
357 && !strchr (&script[monitor->dir_len+1], '/')) { /* check: and no further '/' */
358 /* we expect the script to lie inside monitor->dir. If it does,
359 * strip the directory name. Otherwise show the full path and a warning. */
360 script += monitor->dir_len + 1;
362 script_validation_msg = " (unexpected path)";
364 if (result == DISPATCH_RESULT_SUCCESS) {
365 _LOGD ("(%u) %s succeeded%s",
367 script, script_validation_msg);
369 _LOGW ("(%u) %s failed (%s): %s%s",
372 dispatch_result_to_string (result),
374 script_validation_msg);
380 dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
382 DispatchInfo *info = user_data;
384 GVariantIter *results;
385 GError *error = NULL;
387 ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
388 G_VARIANT_TYPE ("(a(sus))"),
391 g_variant_get (ret, "(a(sus))", &results);
392 dispatcher_results_process (info->request_id, info->action, results);
393 g_variant_iter_free (results);
394 g_variant_unref (ret);
396 if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) {
397 g_dbus_error_strip_remote_error (error);
398 _LOGW ("(%u) failed to call dispatcher scripts: %s",
399 info->request_id, error->message);
401 _LOGD ("(%u) failed to call dispatcher scripts: %s",
402 info->request_id, error->message);
404 g_clear_error (&error);
408 info->callback (info->request_id, info->user_data);
410 dispatcher_info_cleanup (info);
413 static const char *action_table[] = {
414 [DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME,
415 [DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP,
416 [DISPATCHER_ACTION_UP] = NMD_ACTION_UP,
417 [DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN,
418 [DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN,
419 [DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP,
420 [DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP,
421 [DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
422 [DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN,
423 [DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
424 [DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
428 action_to_string (DispatcherAction action)
430 g_assert ((gsize) action < G_N_ELEMENTS (action_table));
431 return action_table[action];
435 dispatcher_idle_cb (gpointer user_data)
437 DispatchInfo *info = user_data;
441 info->callback (info->request_id, info->user_data);
442 dispatcher_info_cleanup (info);
443 return G_SOURCE_REMOVE;
447 _dispatcher_call (DispatcherAction action,
449 NMSettingsConnection *settings_connection,
450 NMConnection *applied_connection,
452 const char *vpn_iface,
453 NMIP4Config *vpn_ip4_config,
454 NMIP6Config *vpn_ip6_config,
455 DispatcherFunc callback,
459 GVariant *connection_dict;
460 GVariantBuilder connection_props;
461 GVariantBuilder device_props;
462 GVariantBuilder device_ip4_props;
463 GVariantBuilder device_ip6_props;
464 GVariant *device_dhcp4_props = NULL;
465 GVariant *device_dhcp6_props = NULL;
466 GVariantBuilder vpn_ip4_props;
467 GVariantBuilder vpn_ip6_props;
468 DispatchInfo *info = NULL;
469 gboolean success = FALSE;
470 GError *error = NULL;
471 static guint request_counter = 0;
472 guint reqid = ++request_counter;
474 if (!dispatcher_proxy)
477 /* Wrapping protection */
478 if (G_UNLIKELY (!reqid))
479 reqid = ++request_counter;
481 g_assert (!blocking || (!callback && !user_data));
485 /* All actions except 'hostname' require a device */
486 if (action == DISPATCHER_ACTION_HOSTNAME) {
487 _LOGD ("(%u) dispatching action '%s'%s",
488 reqid, action_to_string (action),
491 : (callback ? " (with callback)" : ""));
493 g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
495 _LOGD ("(%u) (%s) dispatching action '%s'%s",
497 vpn_iface ? vpn_iface : nm_device_get_iface (device),
498 action_to_string (action),
501 : (callback ? " (with callback)" : ""));
504 if (!_get_monitor_by_action(action)->has_scripts) {
505 if (blocking == FALSE && (out_call_id || callback)) {
506 info = g_malloc0 (sizeof (*info));
507 info->action = action;
508 info->request_id = reqid;
509 info->callback = callback;
510 info->user_data = user_data;
511 info->idle_id = g_idle_add (dispatcher_idle_cb, info);
512 _LOGD ("(%u) simulate request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir);
514 _LOGD ("(%u) ignoring request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir);
519 if (applied_connection)
520 connection_dict = nm_connection_to_dbus (applied_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
522 connection_dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
524 g_variant_builder_init (&connection_props, G_VARIANT_TYPE_VARDICT);
525 if (settings_connection) {
526 const char *connection_path;
527 const char *filename;
529 connection_path = nm_connection_get_path (NM_CONNECTION (settings_connection));
530 if (connection_path) {
531 g_variant_builder_add (&connection_props, "{sv}",
532 NMD_CONNECTION_PROPS_PATH,
533 g_variant_new_object_path (connection_path));
535 filename = nm_settings_connection_get_filename (settings_connection);
537 g_variant_builder_add (&connection_props, "{sv}",
538 NMD_CONNECTION_PROPS_FILENAME,
539 g_variant_new_string (filename));
541 if (nm_settings_connection_get_nm_generated_assumed (settings_connection)) {
542 g_variant_builder_add (&connection_props, "{sv}",
543 NMD_CONNECTION_PROPS_EXTERNAL,
544 g_variant_new_boolean (TRUE));
548 g_variant_builder_init (&device_props, G_VARIANT_TYPE_VARDICT);
549 g_variant_builder_init (&device_ip4_props, G_VARIANT_TYPE_VARDICT);
550 g_variant_builder_init (&device_ip6_props, G_VARIANT_TYPE_VARDICT);
551 g_variant_builder_init (&vpn_ip4_props, G_VARIANT_TYPE_VARDICT);
552 g_variant_builder_init (&vpn_ip6_props, G_VARIANT_TYPE_VARDICT);
554 /* hostname actions only send the hostname */
555 if (action != DISPATCHER_ACTION_HOSTNAME) {
556 fill_device_props (device,
561 &device_dhcp6_props);
562 if (vpn_ip4_config || vpn_ip6_config) {
563 fill_vpn_props (vpn_ip4_config,
570 if (!device_dhcp4_props)
571 device_dhcp4_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
572 if (!device_dhcp6_props)
573 device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
575 /* Send the action to the dispatcher */
578 GVariantIter *results;
580 ret = _nm_dbus_proxy_call_sync (dispatcher_proxy, "Action",
581 g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}sa{sv}a{sv}b)",
582 action_to_string (action),
590 vpn_iface ? vpn_iface : "",
593 nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)),
594 G_VARIANT_TYPE ("(a(sus))"),
595 G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT,
598 g_variant_get (ret, "(a(sus))", &results);
599 dispatcher_results_process (reqid, action, results);
600 g_variant_iter_free (results);
601 g_variant_unref (ret);
604 g_dbus_error_strip_remote_error (error);
605 _LOGW ("(%u) failed: %s", reqid, error->message);
606 g_clear_error (&error);
610 info = g_malloc0 (sizeof (*info));
611 info->action = action;
612 info->request_id = reqid;
613 info->callback = callback;
614 info->user_data = user_data;
615 g_dbus_proxy_call (dispatcher_proxy, "Action",
616 g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}sa{sv}a{sv}b)",
617 action_to_string (action),
625 vpn_iface ? vpn_iface : "",
628 nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)),
629 G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT,
630 NULL, dispatcher_done_cb, info);
634 g_variant_unref (device_dhcp4_props);
635 g_variant_unref (device_dhcp6_props);
638 if (success && info) {
639 /* Track the request in case of cancelation */
640 g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info);
642 *out_call_id = info->request_id;
643 } else if (out_call_id)
650 * nm_dispatcher_call:
651 * @action: the %DispatcherAction
652 * @settings_connection: the #NMSettingsConnection the action applies to
653 * @applied_connection: the currently applied connection
654 * @device: the #NMDevice the action applies to
655 * @callback: a caller-supplied callback to execute when done
656 * @user_data: caller-supplied pointer passed to @callback
657 * @out_call_id: on success, a call identifier which can be passed to
658 * nm_dispatcher_call_cancel()
660 * This method always invokes the dispatcher action asynchronously. To ignore
661 * the result, pass %NULL to @callback.
663 * Returns: %TRUE if the action was dispatched, %FALSE on failure
666 nm_dispatcher_call (DispatcherAction action,
667 NMSettingsConnection *settings_connection,
668 NMConnection *applied_connection,
670 DispatcherFunc callback,
674 return _dispatcher_call (action, FALSE, settings_connection, applied_connection, device, NULL, NULL,
675 NULL, callback, user_data, out_call_id);
679 * nm_dispatcher_call_sync():
680 * @action: the %DispatcherAction
681 * @settings_connection: the #NMSettingsConnection the action applies to
682 * @applied_connection: the currently applied connection
683 * @device: the #NMDevice the action applies to
685 * This method always invokes the dispatcher action synchronously and it may
686 * take a long time to return.
688 * Returns: %TRUE if the action was dispatched, %FALSE on failure
691 nm_dispatcher_call_sync (DispatcherAction action,
692 NMSettingsConnection *settings_connection,
693 NMConnection *applied_connection,
696 return _dispatcher_call (action, TRUE, settings_connection, applied_connection, device, NULL, NULL,
697 NULL, NULL, NULL, NULL);
701 * nm_dispatcher_call_vpn():
702 * @action: the %DispatcherAction
703 * @settings_connection: the #NMSettingsConnection the action applies to
704 * @applied_connection: the currently applied connection
705 * @parent_device: the parent #NMDevice of the VPN connection
706 * @vpn_iface: the IP interface of the VPN tunnel, if any
707 * @vpn_ip4_config: the #NMIP4Config of the VPN connection
708 * @vpn_ip6_config: the #NMIP6Config of the VPN connection
709 * @callback: a caller-supplied callback to execute when done
710 * @user_data: caller-supplied pointer passed to @callback
711 * @out_call_id: on success, a call identifier which can be passed to
712 * nm_dispatcher_call_cancel()
714 * This method always invokes the dispatcher action asynchronously. To ignore
715 * the result, pass %NULL to @callback.
717 * Returns: %TRUE if the action was dispatched, %FALSE on failure
720 nm_dispatcher_call_vpn (DispatcherAction action,
721 NMSettingsConnection *settings_connection,
722 NMConnection *applied_connection,
723 NMDevice *parent_device,
724 const char *vpn_iface,
725 NMIP4Config *vpn_ip4_config,
726 NMIP6Config *vpn_ip6_config,
727 DispatcherFunc callback,
731 return _dispatcher_call (action, FALSE, settings_connection, applied_connection, parent_device, vpn_iface,
732 vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id);
736 * nm_dispatcher_call_vpn_sync():
737 * @action: the %DispatcherAction
738 * @settings_connection: the #NMSettingsConnection the action applies to
739 * @applied_connection: the currently applied connection
740 * @parent_device: the parent #NMDevice of the VPN connection
741 * @vpn_iface: the IP interface of the VPN tunnel, if any
742 * @vpn_ip4_config: the #NMIP4Config of the VPN connection
743 * @vpn_ip6_config: the #NMIP6Config of the VPN connection
745 * This method always invokes the dispatcher action synchronously and it may
746 * take a long time to return.
748 * Returns: %TRUE if the action was dispatched, %FALSE on failure
751 nm_dispatcher_call_vpn_sync (DispatcherAction action,
752 NMSettingsConnection *settings_connection,
753 NMConnection *applied_connection,
754 NMDevice *parent_device,
755 const char *vpn_iface,
756 NMIP4Config *vpn_ip4_config,
757 NMIP6Config *vpn_ip6_config)
759 return _dispatcher_call (action, TRUE, settings_connection, applied_connection, parent_device, vpn_iface,
760 vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL);
764 nm_dispatcher_call_cancel (guint call_id)
770 /* Canceling just means the callback doesn't get called, so set the
771 * DispatcherInfo's callback to NULL.
773 info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id));
774 g_return_if_fail (info);
776 if (info && info->callback) {
777 _LOGD ("(%u) cancelling dispatcher callback action", call_id);
778 info->callback = NULL;
783 dispatcher_dir_changed (GFileMonitor *monitor,
786 GFileMonitorEvent event_type,
792 GError *error = NULL;
794 dir = g_dir_open (item->dir, 0, &error);
798 item->has_scripts = FALSE;
800 while (!item->has_scripts
801 && (name = g_dir_read_name (dir))) {
802 full_name = g_build_filename (item->dir, name, NULL);
803 item->has_scripts = g_file_test (full_name, G_FILE_TEST_IS_EXECUTABLE);
808 if (item->has_scripts)
809 _LOGD ("%s script directory '%s' has scripts", item->description, item->dir);
811 _LOGD ("%s script directory '%s' has no scripts", item->description, item->dir);
813 _LOGD ("%s script directory '%s' error reading (%s)", item->description, item->dir, strerror (errsv));
814 item->has_scripts = TRUE;
817 if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
818 _LOGD ("%s script directory '%s' does not exist", item->description, item->dir);
819 item->has_scripts = FALSE;
821 _LOGD ("%s script directory '%s' error (%s)", item->description, item->dir, error->message);
822 item->has_scripts = TRUE;
824 g_error_free (error);
830 nm_dispatcher_init (void)
834 GError *error = NULL;
836 for (i = 0; i < G_N_ELEMENTS (monitors); i++) {
837 file = g_file_new_for_path (monitors[i].dir);
838 monitors[i].monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
839 if (monitors[i].monitor) {
840 g_signal_connect (monitors[i].monitor, "changed", G_CALLBACK (dispatcher_dir_changed), &monitors[i]);
841 dispatcher_dir_changed (monitors[i].monitor, file, NULL, 0, &monitors[i]);
843 g_object_unref (file);
846 dispatcher_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
847 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
848 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
850 NM_DISPATCHER_DBUS_SERVICE,
851 NM_DISPATCHER_DBUS_PATH,
852 NM_DISPATCHER_DBUS_INTERFACE,
854 if (!dispatcher_proxy) {
855 _LOGE ("could not get dispatcher proxy! %s", error->message);
856 g_clear_error (&error);