mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-21 02:04:17 +00:00
dispatcher: support device-handler actions
"device-add" and "device-delete" actions are called for device-handlers of generic devices. They differ from other actions in the following aspects: - only one script is invoked, the one with name specified by the device-handler property; - the script is searched in the "device" subdirectory; - since there is only one script executed, the result and error string from that script are returned by NM in the callback function.
This commit is contained in:
parent
8fd0d39444
commit
ee5845063d
|
@ -52,18 +52,22 @@
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* Type for generic callback function; must be cast to either
|
||||||
|
* NMDispatcherFunc or NMDispatcherFuncDH before using. */
|
||||||
|
typedef void (*NMDispatcherCallback)(void);
|
||||||
|
|
||||||
struct NMDispatcherCallId {
|
struct NMDispatcherCallId {
|
||||||
NMDispatcherFunc callback;
|
NMDispatcherCallback callback;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
const char *log_ifname;
|
const char *log_ifname;
|
||||||
const char *log_con_uuid;
|
const char *log_con_uuid;
|
||||||
GVariant *action_params;
|
GVariant *action_params;
|
||||||
gint64 start_at_msec;
|
gint64 start_at_msec;
|
||||||
NMDispatcherAction action;
|
NMDispatcherAction action;
|
||||||
guint idle_id;
|
guint idle_id;
|
||||||
guint32 request_id;
|
guint32 request_id;
|
||||||
bool is_action2 : 1;
|
bool is_action2 : 1;
|
||||||
char extra_strings[];
|
char extra_strings[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -98,14 +102,20 @@ action_need_device(NMDispatcherAction action)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
action_is_device_handler(NMDispatcherAction action)
|
||||||
|
{
|
||||||
|
return NM_IN_SET(action, NM_DISPATCHER_ACTION_DEVICE_ADD, NM_DISPATCHER_ACTION_DEVICE_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
static NMDispatcherCallId *
|
static NMDispatcherCallId *
|
||||||
dispatcher_call_id_new(guint32 request_id,
|
dispatcher_call_id_new(guint32 request_id,
|
||||||
gint64 start_at_msec,
|
gint64 start_at_msec,
|
||||||
NMDispatcherAction action,
|
NMDispatcherAction action,
|
||||||
NMDispatcherFunc callback,
|
NMDispatcherCallback callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
const char *log_ifname,
|
const char *log_ifname,
|
||||||
const char *log_con_uuid)
|
const char *log_con_uuid)
|
||||||
{
|
{
|
||||||
NMDispatcherCallId *call_id;
|
NMDispatcherCallId *call_id;
|
||||||
gsize l_log_ifname;
|
gsize l_log_ifname;
|
||||||
|
@ -388,19 +398,42 @@ dispatch_result_to_string(DispatchResult result)
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dispatcher_results_process:
|
||||||
|
* @action: the dispatcher action
|
||||||
|
* @request_id: request id
|
||||||
|
* @start_at_msec: the timestamp at which the dispatcher call was started
|
||||||
|
* @now_msec: the current timestamp in milliseconds
|
||||||
|
* @log_ifname: the interface name for logging
|
||||||
|
* @log_con_uuid: the connection UUID for logging
|
||||||
|
* @out_success: (out): for device-handler actions, the result of the script
|
||||||
|
* @out_error_msg: (out)(transfer full): for device-handler actions, the
|
||||||
|
* error message in case of failure
|
||||||
|
* @v_results: the GVariant containing the results to parse
|
||||||
|
* @is_action2: whether the D-Bus method is "Action2()" (or "Action()")
|
||||||
|
*
|
||||||
|
* Process the results of the dispatcher call.
|
||||||
|
*
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
dispatcher_results_process(guint32 request_id,
|
dispatcher_results_process(NMDispatcherAction action,
|
||||||
gint64 start_at_msec,
|
guint32 request_id,
|
||||||
gint64 now_msec,
|
gint64 start_at_msec,
|
||||||
const char *log_ifname,
|
gint64 now_msec,
|
||||||
const char *log_con_uuid,
|
const char *log_ifname,
|
||||||
GVariant *v_results,
|
const char *log_con_uuid,
|
||||||
gboolean is_action2)
|
gboolean *out_success,
|
||||||
|
char **out_error_msg,
|
||||||
|
GVariant *v_results,
|
||||||
|
gboolean is_action2)
|
||||||
{
|
{
|
||||||
nm_auto_free_variant_iter GVariantIter *results = NULL;
|
nm_auto_free_variant_iter GVariantIter *results = NULL;
|
||||||
const char *script, *err;
|
const char *script, *err;
|
||||||
guint32 result;
|
guint32 result;
|
||||||
gsize n_children;
|
gsize n_children;
|
||||||
|
gboolean action_is_dh = action_is_device_handler(action);
|
||||||
|
|
||||||
|
nm_assert(!action_is_dh || is_action2);
|
||||||
|
|
||||||
if (is_action2)
|
if (is_action2)
|
||||||
g_variant_get(v_results, "(a(susa{sv}))", &results);
|
g_variant_get(v_results, "(a(susa{sv}))", &results);
|
||||||
|
@ -417,8 +450,13 @@ dispatcher_results_process(guint32 request_id,
|
||||||
(int) ((now_msec - start_at_msec) % 1000),
|
(int) ((now_msec - start_at_msec) % 1000),
|
||||||
n_children);
|
n_children);
|
||||||
|
|
||||||
if (n_children == 0)
|
if (n_children == 0) {
|
||||||
|
if (action_is_dh) {
|
||||||
|
NM_SET_OUT(out_success, FALSE);
|
||||||
|
NM_SET_OUT(out_error_msg, g_strdup("no result returned from dispatcher service"));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
gs_unref_variant GVariant *options = NULL;
|
gs_unref_variant GVariant *options = NULL;
|
||||||
|
@ -442,6 +480,11 @@ dispatcher_results_process(guint32 request_id,
|
||||||
dispatch_result_to_string(result),
|
dispatch_result_to_string(result),
|
||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
|
if (action_is_dh) {
|
||||||
|
NM_SET_OUT(out_success, result == DISPATCH_RESULT_SUCCESS);
|
||||||
|
NM_SET_OUT(out_error_msg, g_strdup(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,6 +495,9 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
gs_free_error GError *error = NULL;
|
gs_free_error GError *error = NULL;
|
||||||
NMDispatcherCallId *call_id = user_data;
|
NMDispatcherCallId *call_id = user_data;
|
||||||
gint64 now_msec;
|
gint64 now_msec;
|
||||||
|
gboolean action_is_dh;
|
||||||
|
gboolean success = TRUE;
|
||||||
|
gs_free char *error_msg = NULL;
|
||||||
|
|
||||||
nm_assert((gpointer) source == gl.dbus_connection);
|
nm_assert((gpointer) source == gl.dbus_connection);
|
||||||
|
|
||||||
|
@ -459,7 +505,7 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
|
|
||||||
ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
|
ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
|
||||||
|
|
||||||
if (!ret && call_id->is_action2
|
if (!ret && call_id->is_action2 && !action_is_device_handler(call_id->action)
|
||||||
&& g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
|
&& g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
|
||||||
_LOG3D(call_id,
|
_LOG3D(call_id,
|
||||||
"dispatcher service does not implement Action2() method, falling back to Action()");
|
"dispatcher service does not implement Action2() method, falling back to Action()");
|
||||||
|
@ -493,38 +539,54 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
|
||||||
(int) ((now_msec - call_id->start_at_msec) % 1000),
|
(int) ((now_msec - call_id->start_at_msec) % 1000),
|
||||||
error->message);
|
error->message);
|
||||||
} else {
|
} else {
|
||||||
dispatcher_results_process(call_id->request_id,
|
dispatcher_results_process(call_id->action,
|
||||||
|
call_id->request_id,
|
||||||
call_id->start_at_msec,
|
call_id->start_at_msec,
|
||||||
now_msec,
|
now_msec,
|
||||||
call_id->log_ifname,
|
call_id->log_ifname,
|
||||||
call_id->log_con_uuid,
|
call_id->log_con_uuid,
|
||||||
|
&success,
|
||||||
|
&error_msg,
|
||||||
ret,
|
ret,
|
||||||
call_id->is_action2);
|
call_id->is_action2);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_remove(gl.requests, call_id);
|
g_hash_table_remove(gl.requests, call_id);
|
||||||
|
action_is_dh = action_is_device_handler(call_id->action);
|
||||||
|
|
||||||
if (call_id->callback)
|
if (call_id->callback) {
|
||||||
call_id->callback(call_id, call_id->user_data);
|
if (action_is_dh) {
|
||||||
|
NMDispatcherFuncDH cb = (NMDispatcherFuncDH) call_id->callback;
|
||||||
|
|
||||||
|
cb(call_id, call_id->user_data, success, error_msg);
|
||||||
|
} else {
|
||||||
|
NMDispatcherFunc cb = (NMDispatcherFunc) call_id->callback;
|
||||||
|
|
||||||
|
cb(call_id, call_id->user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispatcher_call_id_free(call_id);
|
dispatcher_call_id_free(call_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *action_table[] = {[NM_DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME,
|
static const char *action_table[] = {
|
||||||
[NM_DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP,
|
[NM_DISPATCHER_ACTION_HOSTNAME] = NMD_ACTION_HOSTNAME,
|
||||||
[NM_DISPATCHER_ACTION_UP] = NMD_ACTION_UP,
|
[NM_DISPATCHER_ACTION_PRE_UP] = NMD_ACTION_PRE_UP,
|
||||||
[NM_DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN,
|
[NM_DISPATCHER_ACTION_UP] = NMD_ACTION_UP,
|
||||||
[NM_DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN,
|
[NM_DISPATCHER_ACTION_PRE_DOWN] = NMD_ACTION_PRE_DOWN,
|
||||||
[NM_DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP,
|
[NM_DISPATCHER_ACTION_DOWN] = NMD_ACTION_DOWN,
|
||||||
[NM_DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP,
|
[NM_DISPATCHER_ACTION_VPN_PRE_UP] = NMD_ACTION_VPN_PRE_UP,
|
||||||
[NM_DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
|
[NM_DISPATCHER_ACTION_VPN_UP] = NMD_ACTION_VPN_UP,
|
||||||
[NM_DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN,
|
[NM_DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
|
||||||
[NM_DISPATCHER_ACTION_DHCP_CHANGE_4] = NMD_ACTION_DHCP4_CHANGE,
|
[NM_DISPATCHER_ACTION_VPN_DOWN] = NMD_ACTION_VPN_DOWN,
|
||||||
[NM_DISPATCHER_ACTION_DHCP_CHANGE_6] = NMD_ACTION_DHCP6_CHANGE,
|
[NM_DISPATCHER_ACTION_DHCP_CHANGE_4] = NMD_ACTION_DHCP4_CHANGE,
|
||||||
[NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE] =
|
[NM_DISPATCHER_ACTION_DHCP_CHANGE_6] = NMD_ACTION_DHCP6_CHANGE,
|
||||||
NMD_ACTION_CONNECTIVITY_CHANGE,
|
[NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE] = NMD_ACTION_CONNECTIVITY_CHANGE,
|
||||||
[NM_DISPATCHER_ACTION_REAPPLY] = NMD_ACTION_REAPPLY,
|
[NM_DISPATCHER_ACTION_REAPPLY] = NMD_ACTION_REAPPLY,
|
||||||
[NM_DISPATCHER_ACTION_DNS_CHANGE] = NMD_ACTION_DNS_CHANGE};
|
[NM_DISPATCHER_ACTION_DNS_CHANGE] = NMD_ACTION_DNS_CHANGE,
|
||||||
|
[NM_DISPATCHER_ACTION_DEVICE_ADD] = NMD_ACTION_DEVICE_ADD,
|
||||||
|
[NM_DISPATCHER_ACTION_DEVICE_DELETE] = NMD_ACTION_DEVICE_DELETE,
|
||||||
|
};
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
action_to_string(NMDispatcherAction action)
|
action_to_string(NMDispatcherAction action)
|
||||||
|
@ -664,7 +726,7 @@ _dispatcher_call(NMDispatcherAction action,
|
||||||
NMConnectivityState connectivity_state,
|
NMConnectivityState connectivity_state,
|
||||||
const char *vpn_iface,
|
const char *vpn_iface,
|
||||||
const NML3ConfigData *l3cd,
|
const NML3ConfigData *l3cd,
|
||||||
NMDispatcherFunc callback,
|
NMDispatcherCallback callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
NMDispatcherCallId **out_call_id)
|
NMDispatcherCallId **out_call_id)
|
||||||
{
|
{
|
||||||
|
@ -784,11 +846,14 @@ _dispatcher_call(NMDispatcherAction action,
|
||||||
error->message);
|
error->message);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
dispatcher_results_process(request_id,
|
dispatcher_results_process(action,
|
||||||
|
request_id,
|
||||||
start_at_msec,
|
start_at_msec,
|
||||||
now_msec,
|
now_msec,
|
||||||
log_ifname,
|
log_ifname,
|
||||||
log_con_uuid,
|
log_con_uuid,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
ret,
|
ret,
|
||||||
is_action2);
|
is_action2);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -856,7 +921,7 @@ nm_dispatcher_call_hostname(NMDispatcherFunc callback,
|
||||||
NM_CONNECTIVITY_UNKNOWN,
|
NM_CONNECTIVITY_UNKNOWN,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
callback,
|
(NMDispatcherCallback) callback,
|
||||||
user_data,
|
user_data,
|
||||||
out_call_id);
|
out_call_id);
|
||||||
}
|
}
|
||||||
|
@ -866,7 +931,7 @@ _dispatcher_call_device(NMDispatcherAction action,
|
||||||
NMDevice *device,
|
NMDevice *device,
|
||||||
gboolean blocking,
|
gboolean blocking,
|
||||||
NMActRequest *act_request,
|
NMActRequest *act_request,
|
||||||
NMDispatcherFunc callback,
|
NMDispatcherCallback callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
NMDispatcherCallId **out_call_id)
|
NMDispatcherCallId **out_call_id)
|
||||||
{
|
{
|
||||||
|
@ -919,11 +984,48 @@ nm_dispatcher_call_device(NMDispatcherAction action,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
NMDispatcherCallId **out_call_id)
|
NMDispatcherCallId **out_call_id)
|
||||||
{
|
{
|
||||||
|
g_return_val_if_fail(!action_is_device_handler(action), FALSE);
|
||||||
|
|
||||||
return _dispatcher_call_device(action,
|
return _dispatcher_call_device(action,
|
||||||
device,
|
device,
|
||||||
FALSE,
|
FALSE,
|
||||||
act_request,
|
act_request,
|
||||||
callback,
|
(NMDispatcherCallback) callback,
|
||||||
|
user_data,
|
||||||
|
out_call_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nm_dispatcher_call_device_handler:
|
||||||
|
* @action: the %NMDispatcherAction, must be device-add or device-remove
|
||||||
|
* @device: the #NMDevice the action applies to
|
||||||
|
* @act_request: the #NMActRequest for the action. If %NULL, use the
|
||||||
|
* current request of the device.
|
||||||
|
* @callback: a caller-supplied device-handler callback to execute when done
|
||||||
|
* @user_data: caller-supplied pointer passed to @callback
|
||||||
|
* @out_call_id: on success, a call identifier which can be passed to
|
||||||
|
* nm_dispatcher_call_cancel()
|
||||||
|
*
|
||||||
|
* This method always invokes the device dispatcher action asynchronously. To ignore
|
||||||
|
* the result, pass %NULL to @callback.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the action was dispatched, %FALSE on failure
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
nm_dispatcher_call_device_handler(NMDispatcherAction action,
|
||||||
|
NMDevice *device,
|
||||||
|
NMActRequest *act_request,
|
||||||
|
NMDispatcherFuncDH callback,
|
||||||
|
gpointer user_data,
|
||||||
|
NMDispatcherCallId **out_call_id)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(action_is_device_handler(action), FALSE);
|
||||||
|
|
||||||
|
return _dispatcher_call_device(action,
|
||||||
|
device,
|
||||||
|
FALSE,
|
||||||
|
act_request,
|
||||||
|
(NMDispatcherCallback) callback,
|
||||||
user_data,
|
user_data,
|
||||||
out_call_id);
|
out_call_id);
|
||||||
}
|
}
|
||||||
|
@ -945,6 +1047,8 @@ nm_dispatcher_call_device_sync(NMDispatcherAction action,
|
||||||
NMDevice *device,
|
NMDevice *device,
|
||||||
NMActRequest *act_request)
|
NMActRequest *act_request)
|
||||||
{
|
{
|
||||||
|
g_return_val_if_fail(!action_is_device_handler(action), FALSE);
|
||||||
|
|
||||||
return _dispatcher_call_device(action, device, TRUE, act_request, NULL, NULL, NULL);
|
return _dispatcher_call_device(action, device, TRUE, act_request, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -986,7 +1090,7 @@ nm_dispatcher_call_vpn(NMDispatcherAction action,
|
||||||
NM_CONNECTIVITY_UNKNOWN,
|
NM_CONNECTIVITY_UNKNOWN,
|
||||||
vpn_iface,
|
vpn_iface,
|
||||||
l3cd,
|
l3cd,
|
||||||
callback,
|
(NMDispatcherCallback) callback,
|
||||||
user_data,
|
user_data,
|
||||||
out_call_id);
|
out_call_id);
|
||||||
}
|
}
|
||||||
|
@ -1013,6 +1117,8 @@ nm_dispatcher_call_vpn_sync(NMDispatcherAction action,
|
||||||
const char *vpn_iface,
|
const char *vpn_iface,
|
||||||
const NML3ConfigData *l3cd)
|
const NML3ConfigData *l3cd)
|
||||||
{
|
{
|
||||||
|
g_return_val_if_fail(!action_is_device_handler(action), FALSE);
|
||||||
|
|
||||||
return _dispatcher_call(action,
|
return _dispatcher_call(action,
|
||||||
TRUE,
|
TRUE,
|
||||||
parent_device,
|
parent_device,
|
||||||
|
@ -1054,7 +1160,7 @@ nm_dispatcher_call_connectivity(NMConnectivityState connectivity_state,
|
||||||
connectivity_state,
|
connectivity_state,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
callback,
|
(NMDispatcherCallback) callback,
|
||||||
user_data,
|
user_data,
|
||||||
out_call_id);
|
out_call_id);
|
||||||
}
|
}
|
||||||
|
@ -1086,7 +1192,10 @@ nm_dispatcher_call_dns_change(void)
|
||||||
void
|
void
|
||||||
nm_dispatcher_call_cancel(NMDispatcherCallId *call_id)
|
nm_dispatcher_call_cancel(NMDispatcherCallId *call_id)
|
||||||
{
|
{
|
||||||
if (!call_id || g_hash_table_lookup(gl.requests, call_id) != call_id || !call_id->callback)
|
if (!call_id || g_hash_table_lookup(gl.requests, call_id) != call_id)
|
||||||
|
g_return_if_reached();
|
||||||
|
|
||||||
|
if (!call_id->callback)
|
||||||
g_return_if_reached();
|
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
|
||||||
|
|
|
@ -24,6 +24,8 @@ typedef enum {
|
||||||
NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE,
|
NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE,
|
||||||
NM_DISPATCHER_ACTION_REAPPLY,
|
NM_DISPATCHER_ACTION_REAPPLY,
|
||||||
NM_DISPATCHER_ACTION_DNS_CHANGE,
|
NM_DISPATCHER_ACTION_DNS_CHANGE,
|
||||||
|
NM_DISPATCHER_ACTION_DEVICE_ADD,
|
||||||
|
NM_DISPATCHER_ACTION_DEVICE_DELETE,
|
||||||
} NMDispatcherAction;
|
} NMDispatcherAction;
|
||||||
|
|
||||||
#define NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4) \
|
#define NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4) \
|
||||||
|
@ -31,7 +33,13 @@ typedef enum {
|
||||||
|
|
||||||
typedef struct NMDispatcherCallId NMDispatcherCallId;
|
typedef struct NMDispatcherCallId NMDispatcherCallId;
|
||||||
|
|
||||||
|
/* Callback function for regular dispatcher calls */
|
||||||
typedef void (*NMDispatcherFunc)(NMDispatcherCallId *call_id, gpointer user_data);
|
typedef void (*NMDispatcherFunc)(NMDispatcherCallId *call_id, gpointer user_data);
|
||||||
|
/* Callback function for device-handler dispatcher calls */
|
||||||
|
typedef void (*NMDispatcherFuncDH)(NMDispatcherCallId *call_id,
|
||||||
|
gpointer user_data,
|
||||||
|
gboolean success,
|
||||||
|
const char *error_msg);
|
||||||
|
|
||||||
gboolean nm_dispatcher_call_hostname(NMDispatcherFunc callback,
|
gboolean nm_dispatcher_call_hostname(NMDispatcherFunc callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
|
@ -44,6 +52,13 @@ gboolean nm_dispatcher_call_device(NMDispatcherAction action,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
NMDispatcherCallId **out_call_id);
|
NMDispatcherCallId **out_call_id);
|
||||||
|
|
||||||
|
gboolean nm_dispatcher_call_device_handler(NMDispatcherAction action,
|
||||||
|
NMDevice *device,
|
||||||
|
NMActRequest *act_request,
|
||||||
|
NMDispatcherFuncDH callback_dh,
|
||||||
|
gpointer user_data,
|
||||||
|
NMDispatcherCallId **out_call_id);
|
||||||
|
|
||||||
gboolean nm_dispatcher_call_device_sync(NMDispatcherAction action,
|
gboolean nm_dispatcher_call_device_sync(NMDispatcherAction action,
|
||||||
NMDevice *device,
|
NMDevice *device,
|
||||||
NMActRequest *act_request);
|
NMActRequest *act_request);
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
#define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change"
|
#define NMD_ACTION_CONNECTIVITY_CHANGE "connectivity-change"
|
||||||
#define NMD_ACTION_REAPPLY "reapply"
|
#define NMD_ACTION_REAPPLY "reapply"
|
||||||
#define NMD_ACTION_DNS_CHANGE "dns-change"
|
#define NMD_ACTION_DNS_CHANGE "dns-change"
|
||||||
|
#define NMD_ACTION_DEVICE_ADD "device-add"
|
||||||
|
#define NMD_ACTION_DEVICE_DELETE "device-delete"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DISPATCH_RESULT_UNKNOWN = 0,
|
DISPATCH_RESULT_UNKNOWN = 0,
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct Request {
|
||||||
char **envp;
|
char **envp;
|
||||||
gboolean debug;
|
gboolean debug;
|
||||||
gboolean is_action2;
|
gboolean is_action2;
|
||||||
|
gboolean is_device_handler;
|
||||||
|
|
||||||
GPtrArray *scripts; /* list of ScriptInfo */
|
GPtrArray *scripts; /* list of ScriptInfo */
|
||||||
guint idx;
|
guint idx;
|
||||||
|
@ -615,6 +616,31 @@ _compare_basenames(gconstpointer a, gconstpointer b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_file(Request *request, const char *path)
|
||||||
|
{
|
||||||
|
gs_free char *link_target = NULL;
|
||||||
|
const char *err_msg = NULL;
|
||||||
|
struct stat st;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
link_target = g_file_read_link(path, NULL);
|
||||||
|
if (nm_streq0(link_target, "/dev/null"))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
err = stat(path, &st);
|
||||||
|
if (err) {
|
||||||
|
return FALSE;
|
||||||
|
} else if (!S_ISREG(st.st_mode) || st.st_size == 0) {
|
||||||
|
/* silently skip. */
|
||||||
|
return FALSE;
|
||||||
|
} else if (!check_permissions(&st, &err_msg)) {
|
||||||
|
_LOG_R_W(request, "find-scripts: Cannot execute '%s': %s", path, err_msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_find_scripts(Request *request, GHashTable *scripts, const char *base, const char *subdir)
|
_find_scripts(Request *request, GHashTable *scripts, const char *base, const char *subdir)
|
||||||
{
|
{
|
||||||
|
@ -647,7 +673,7 @@ _find_scripts(Request *request, GHashTable *scripts, const char *base, const cha
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *
|
static GSList *
|
||||||
find_scripts(Request *request)
|
find_scripts(Request *request, const char *device_handler)
|
||||||
{
|
{
|
||||||
gs_unref_hashtable GHashTable *scripts = NULL;
|
gs_unref_hashtable GHashTable *scripts = NULL;
|
||||||
GSList *script_list = NULL;
|
GSList *script_list = NULL;
|
||||||
|
@ -656,6 +682,33 @@ find_scripts(Request *request)
|
||||||
char *path;
|
char *path;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
|
if (request->is_device_handler) {
|
||||||
|
const char *const dirs[] = {NMCONFDIR, NMLIBDIR};
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
nm_assert(device_handler);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(dirs); i++) {
|
||||||
|
gs_free char *full_name = NULL;
|
||||||
|
|
||||||
|
full_name = g_build_filename(dirs[i], "dispatcher.d", "device", device_handler, NULL);
|
||||||
|
if (check_file(request, full_name)) {
|
||||||
|
script_list = g_slist_prepend(script_list, g_steal_pointer(&full_name));
|
||||||
|
return script_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOG_R_W(request,
|
||||||
|
"find-scripts: no device-handler script found with name \"%s\"",
|
||||||
|
device_handler);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_assert(!device_handler);
|
||||||
|
|
||||||
|
/* Use a hash-table to deduplicate scripts with same name from /etc and /usr */
|
||||||
|
scripts = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
|
||||||
|
|
||||||
if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_UP, NMD_ACTION_VPN_PRE_UP))
|
if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_UP, NMD_ACTION_VPN_PRE_UP))
|
||||||
subdir = "pre-up.d";
|
subdir = "pre-up.d";
|
||||||
else if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_DOWN, NMD_ACTION_VPN_PRE_DOWN))
|
else if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_DOWN, NMD_ACTION_VPN_PRE_DOWN))
|
||||||
|
@ -663,33 +716,13 @@ find_scripts(Request *request)
|
||||||
else
|
else
|
||||||
subdir = NULL;
|
subdir = NULL;
|
||||||
|
|
||||||
scripts = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
|
|
||||||
|
|
||||||
_find_scripts(request, scripts, NMLIBDIR, subdir);
|
_find_scripts(request, scripts, NMLIBDIR, subdir);
|
||||||
_find_scripts(request, 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)) {
|
||||||
gs_free char *link_target = NULL;
|
if (check_file(request, path)) {
|
||||||
const char *err_msg = NULL;
|
|
||||||
struct stat st;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
link_target = g_file_read_link(path, NULL);
|
|
||||||
if (nm_streq0(link_target, "/dev/null"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
err = stat(path, &st);
|
|
||||||
if (err)
|
|
||||||
_LOG_R_W(request, "find-scripts: Failed to stat '%s': %d", path, err);
|
|
||||||
else if (!S_ISREG(st.st_mode) || st.st_size == 0) {
|
|
||||||
/* silently skip. */
|
|
||||||
} else if (!check_permissions(&st, &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));
|
script_list = g_slist_prepend(script_list, g_strdup(path));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,6 +758,27 @@ script_must_wait(const char *path)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_device_handler(GVariant *connection)
|
||||||
|
{
|
||||||
|
gs_unref_variant GVariant *generic_setting = NULL;
|
||||||
|
const char *device_handler = NULL;
|
||||||
|
|
||||||
|
generic_setting = g_variant_lookup_value(connection,
|
||||||
|
NM_SETTING_GENERIC_SETTING_NAME,
|
||||||
|
NM_VARIANT_TYPE_SETTING);
|
||||||
|
if (generic_setting) {
|
||||||
|
if (g_variant_lookup(generic_setting,
|
||||||
|
NM_SETTING_GENERIC_DEVICE_HANDLER,
|
||||||
|
"&s",
|
||||||
|
&device_handler)) {
|
||||||
|
return g_strdup(device_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_handle_action(GDBusMethodInvocation *invocation, GVariant *parameters, gboolean is_action2)
|
_handle_action(GDBusMethodInvocation *invocation, GVariant *parameters, gboolean is_action2)
|
||||||
{
|
{
|
||||||
|
@ -739,6 +793,7 @@ _handle_action(GDBusMethodInvocation *invocation, GVariant *parameters, gboolean
|
||||||
gs_unref_variant GVariant *device_dhcp6_config = NULL;
|
gs_unref_variant GVariant *device_dhcp6_config = NULL;
|
||||||
const char *connectivity_state;
|
const char *connectivity_state;
|
||||||
const char *vpn_ip_iface;
|
const char *vpn_ip_iface;
|
||||||
|
gs_free char *device_handler = NULL;
|
||||||
gs_unref_variant GVariant *vpn_proxy_properties = NULL;
|
gs_unref_variant GVariant *vpn_proxy_properties = NULL;
|
||||||
gs_unref_variant GVariant *vpn_ip4_config = NULL;
|
gs_unref_variant GVariant *vpn_ip4_config = NULL;
|
||||||
gs_unref_variant GVariant *vpn_ip6_config = NULL;
|
gs_unref_variant GVariant *vpn_ip6_config = NULL;
|
||||||
|
@ -829,6 +884,8 @@ _handle_action(GDBusMethodInvocation *invocation, GVariant *parameters, gboolean
|
||||||
request->context = invocation;
|
request->context = invocation;
|
||||||
request->action = g_strdup(action);
|
request->action = g_strdup(action);
|
||||||
request->is_action2 = is_action2;
|
request->is_action2 = is_action2;
|
||||||
|
request->is_device_handler =
|
||||||
|
NM_IN_STRSET(action, NMD_ACTION_DEVICE_ADD, NMD_ACTION_DEVICE_DELETE);
|
||||||
|
|
||||||
request->envp = nm_dispatcher_utils_construct_envp(action,
|
request->envp = nm_dispatcher_utils_construct_envp(action,
|
||||||
connection,
|
connection,
|
||||||
|
@ -846,11 +903,14 @@ _handle_action(GDBusMethodInvocation *invocation, GVariant *parameters, gboolean
|
||||||
vpn_ip6_config,
|
vpn_ip6_config,
|
||||||
&request->iface,
|
&request->iface,
|
||||||
&error_message);
|
&error_message);
|
||||||
|
|
||||||
if (!error_message) {
|
if (!error_message) {
|
||||||
|
if (request->is_device_handler) {
|
||||||
|
device_handler = get_device_handler(connection);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
sorted_scripts = find_scripts(request, device_handler);
|
||||||
for (iter = sorted_scripts; iter; iter = g_slist_next(iter)) {
|
for (iter = sorted_scripts; iter; iter = g_slist_next(iter)) {
|
||||||
ScriptInfo *s;
|
ScriptInfo *s;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue