merge: branch 'bg/resolve-helper'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/877
This commit is contained in:
Beniamino Galvani 2021-06-11 21:44:38 +02:00
commit a68b63a859
22 changed files with 799 additions and 63 deletions

1
.gitignore vendored
View file

@ -427,6 +427,7 @@ test-*.trs
/src/initrd/tests/test-ibft-reader
/src/ndisc/tests/test-ndisc-fake
/src/ndisc/tests/test-ndisc-linux
/src/nm-daemon-helper/nm-daemon-helper
/src/nm-iface-helper
/src/platform/tests/dump
/src/platform/tests/monitor

View file

@ -4598,6 +4598,31 @@ EXTRA_DIST += \
src/nm-dispatcher/tests/meson.build \
$(NULL)
###############################################################################
# src/nm-daemon-helper
###############################################################################
libexec_PROGRAMS += src/nm-daemon-helper/nm-daemon-helper
src_nm_daemon_helper_nm_daemon_helper_CPPFLAGS = \
$(dflt_cppflags) \
-I$(srcdir)/src \
-I$(builddir)/src \
$(NULL)
src_nm_daemon_helper_nm_daemon_helper_LDFLAGS = \
-Wl,--version-script="$(srcdir)/linker-script-binary.ver" \
$(SANITIZER_EXEC_LDFLAGS) \
$(NULL)
src_nm_daemon_helper_nm_daemon_helper_LDADD = \
src/libnm-std-aux/libnm-std-aux.la \
$(NULL)
EXTRA_DIST += \
src/nm-daemon-helper/meson.build \
$(NULL)
###############################################################################
# src/nm-online
###############################################################################

View file

@ -998,6 +998,7 @@ fi
%{_libexecdir}/nm-dispatcher
%{_libexecdir}/nm-iface-helper
%{_libexecdir}/nm-initrd-generator
%{_libexecdir}/nm-daemon-helper
%dir %{_libdir}/%{name}
%dir %{nmplugindir}
%{nmplugindir}/libnm-settings-plugin*.so

View file

@ -1,8 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "src/core/nm-default-daemon.h"
#include "src/core/dns/nm-dns-manager.h"
#include "src/core/dns/nm-dns-systemd-resolved.h"
#include "nm-device-utils.h"
#include "nm-core-utils.h"
/*****************************************************************************/
@ -152,3 +155,202 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_device_ip_state_to_str,
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_CONF, "conf"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_DONE, "done"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_FAIL, "fail"), );
/*****************************************************************************/
#define SD_RESOLVED_DNS (1UL << 0)
/* Don't answer request from locally synthesized records (which includes /etc/hosts) */
#define SD_RESOLVED_NO_SYNTHESIZE (1UL << 11)
typedef struct {
int addr_family;
NMIPAddr address;
gulong cancellable_id;
GTask * task;
NMDnsSystemdResolvedResolveHandle *resolved_handle;
} ResolveAddrInfo;
#define _NMLOG_PREFIX_NAME "resolve-addr"
#define _NMLOG_DOMAIN LOGD_CORE
#define _NMLOG2(level, info, ...) \
G_STMT_START \
{ \
if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \
ResolveAddrInfo *_info = (info); \
char _addr_str[NM_UTILS_INET_ADDRSTRLEN]; \
\
_nm_log((level), \
(_NMLOG_DOMAIN), \
0, \
NULL, \
NULL, \
_NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
",%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR(_info), \
nm_utils_inet_ntop(_info->addr_family, &_info->address, _addr_str) \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} \
G_STMT_END
static void
resolve_addr_info_free(ResolveAddrInfo *info)
{
nm_assert(info->cancellable_id == 0);
nm_assert(!info->resolved_handle);
g_object_unref(info->task);
g_free(info);
}
static void
resolve_addr_complete(ResolveAddrInfo *info, char *hostname_take, GError *error_take)
{
nm_assert(!!hostname_take != !!error_take);
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
if (error_take)
g_task_return_error(info->task, error_take);
else
g_task_return_pointer(info->task, hostname_take, g_free);
resolve_addr_info_free(info);
}
static void
resolve_addr_helper_cb(GObject *source, GAsyncResult *result, gpointer user_data)
{
ResolveAddrInfo *info = user_data;
gs_free_error GError *error = NULL;
gs_free char * output = NULL;
output = nm_utils_spawn_helper_finish(result, &error);
if (nm_utils_error_is_cancelled(error))
return;
_LOG2D(info, "helper returned hostname '%s'", output);
resolve_addr_complete(info, g_steal_pointer(&output), g_steal_pointer(&error));
}
static void
resolve_addr_spawn_helper(ResolveAddrInfo *info)
{
char addr_str[NM_UTILS_INET_ADDRSTRLEN];
nm_utils_inet_ntop(info->addr_family, &info->address, addr_str);
_LOG2D(info, "start lookup via nm-daemon-helper");
nm_utils_spawn_helper(NM_MAKE_STRV("resolve-address", addr_str),
g_task_get_cancellable(info->task),
resolve_addr_helper_cb,
info);
}
static void
resolve_addr_resolved_cb(NMDnsSystemdResolved * resolved,
NMDnsSystemdResolvedResolveHandle * handle,
const NMDnsSystemdResolvedAddressResult *names,
guint names_len,
guint64 flags,
GError * error,
gpointer user_data)
{
ResolveAddrInfo *info = user_data;
info->resolved_handle = NULL;
if (nm_utils_error_is_cancelled(error))
return;
if (error) {
gs_free char *dbus_error = NULL;
_LOG2D(info, "error resolving via systemd-resolved: %s", error->message);
dbus_error = g_dbus_error_get_remote_error(error);
if (nm_streq0(dbus_error, "org.freedesktop.resolve1.DnsError.NXDOMAIN")) {
resolve_addr_complete(info, NULL, g_error_copy(error));
return;
}
resolve_addr_spawn_helper(info);
return;
}
if (names_len == 0) {
_LOG2D(info, "systemd-resolved returned no result");
resolve_addr_complete(info, g_strdup(""), NULL);
return;
}
_LOG2D(info, "systemd-resolved returned hostname '%s'", names[0].name);
resolve_addr_complete(info, g_strdup(names[0].name), NULL);
}
static void
resolve_addr_cancelled(GObject *object, gpointer user_data)
{
ResolveAddrInfo *info = user_data;
GError * error = NULL;
nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
nm_clear_pointer(&info->resolved_handle, nm_dns_systemd_resolved_resolve_cancel);
nm_utils_error_set_cancelled(&error, FALSE, NULL);
resolve_addr_complete(info, NULL, error);
}
void
nm_device_resolve_address(int addr_family,
gconstpointer address,
GCancellable * cancellable,
GAsyncReadyCallback callback,
gpointer cb_data)
{
ResolveAddrInfo * info;
NMDnsSystemdResolved *resolved;
info = g_new(ResolveAddrInfo, 1);
*info = (ResolveAddrInfo){
.task = nm_g_task_new(NULL, cancellable, nm_device_resolve_address, callback, cb_data),
.addr_family = addr_family,
.address = nm_ip_addr_init(addr_family, address),
};
if (cancellable) {
gulong signal_id;
signal_id =
g_cancellable_connect(cancellable, G_CALLBACK(resolve_addr_cancelled), info, NULL);
if (signal_id == 0) {
/* the request is already cancelled. Return. */
return;
}
info->cancellable_id = signal_id;
}
resolved = (NMDnsSystemdResolved *) nm_dns_manager_get_systemd_resolved(nm_dns_manager_get());
if (resolved) {
_LOG2D(info, "start lookup via systemd-resolved");
info->resolved_handle =
nm_dns_systemd_resolved_resolve_address(resolved,
0,
addr_family,
address,
SD_RESOLVED_DNS | SD_RESOLVED_NO_SYNTHESIZE,
20000,
resolve_addr_resolved_cb,
info);
return;
}
resolve_addr_spawn_helper(info);
}
char *
nm_device_resolve_address_finish(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_device_resolve_address));
return g_task_propagate_pointer(task, error);
}

View file

@ -85,4 +85,14 @@ const char *nm_device_ip_state_to_str(NMDeviceIPState ip_state);
/*****************************************************************************/
/*****************************************************************************/
void nm_device_resolve_address(int addr_family,
gconstpointer address,
GCancellable * cancellable,
GAsyncReadyCallback callback,
gpointer cb_data);
char *nm_device_resolve_address_finish(GAsyncResult *result, GError **error);
#endif /* __DEVICES_NM_DEVICE_UTILS_H__ */

View file

@ -73,6 +73,7 @@
#include "nm-audit-manager.h"
#include "nm-connectivity.h"
#include "nm-dbus-interface.h"
#include "nm-hostname-manager.h"
#include "nm-device-generic.h"
#include "nm-device-vlan.h"
@ -220,7 +221,6 @@ typedef enum {
typedef struct {
ResolverState state;
GResolver * resolver;
GInetAddress *address;
GCancellable *cancellable;
char * hostname;
@ -777,7 +777,6 @@ _hostname_resolver_free(HostnameResolver *resolver)
nm_clear_g_source(&resolver->timeout_id);
nm_clear_g_cancellable(&resolver->cancellable);
nm_g_object_unref(resolver->resolver);
nm_g_object_unref(resolver->address);
g_free(resolver->hostname);
nm_g_slice_free(resolver);
@ -17666,21 +17665,37 @@ hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer use
NMDevice * self;
gs_free char * hostname = NULL;
gs_free char * addr_str = NULL;
gs_free char * output = NULL;
gs_free_error GError *error = NULL;
hostname = g_resolver_lookup_by_address_finish(G_RESOLVER(source), result, &error);
output = nm_device_resolve_address_finish(result, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
resolver = user_data;
self = resolver->device;
resolver->state = RESOLVER_DONE;
resolver->hostname = g_strdup(hostname);
resolver = user_data;
self = resolver->device;
resolver->state = RESOLVER_DONE;
_LOGD(LOGD_DNS,
"hostname-from-dns: lookup done for %s, result %s%s%s",
(addr_str = g_inet_address_to_string(resolver->address)),
NM_PRINT_FMT_QUOTE_STRING(hostname));
if (error) {
_LOGD(LOGD_DNS,
"hostname-from-dns: lookup error for %s: %s",
(addr_str = g_inet_address_to_string(resolver->address)),
error->message);
} else {
gboolean valid;
resolver->hostname = g_steal_pointer(&output);
valid = nm_hostname_manager_validate_hostname(resolver->hostname);
_LOGD(LOGD_DNS,
"hostname-from-dns: lookup done for %s, result %s%s%s%s",
(addr_str = g_inet_address_to_string(resolver->address)),
NM_PRINT_FMT_QUOTE_STRING(resolver->hostname),
valid ? "" : " (invalid)");
if (!valid)
g_clear_pointer(&resolver->hostname, g_free);
}
nm_clear_g_cancellable(&resolver->cancellable);
g_signal_emit(self, signals[DNS_LOOKUP_DONE], 0);
@ -17778,7 +17793,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
if (!resolver) {
resolver = g_slice_new(HostnameResolver);
*resolver = (HostnameResolver){
.resolver = g_resolver_get_default(),
.device = self,
.addr_family = addr_family,
.state = RESOLVER_WAIT_ADDRESS,
@ -17828,20 +17842,15 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean
}
if (address_changed && new_address) {
gs_free char *str = NULL;
_LOGT(LOGD_DNS,
"hostname-from-dns: starting lookup for address %s",
(str = g_inet_address_to_string(new_address)));
resolver->state = RESOLVER_IN_PROGRESS;
resolver->cancellable = g_cancellable_new();
resolver->address = g_steal_pointer(&new_address);
g_resolver_lookup_by_address_async(resolver->resolver,
resolver->address,
resolver->cancellable,
hostname_dns_lookup_callback,
resolver);
nm_device_resolve_address(addr_family,
g_inet_address_to_bytes(resolver->address),
resolver->cancellable,
hostname_dns_lookup_callback,
resolver);
nm_clear_g_source(&resolver->timeout_id);
}

View file

@ -517,20 +517,12 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data)
{
NMDhcpClient * self = NM_DHCP_CLIENT(user_data);
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
gs_free char * desc = NULL;
g_return_if_fail(priv->watch_id);
priv->watch_id = 0;
if (WIFEXITED(status))
_LOGI("client pid %d exited with status %d", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
_LOGI("client pid %d killed by signal %d", pid, WTERMSIG(status));
else if (WIFSTOPPED(status))
_LOGI("client pid %d stopped by signal %d", pid, WSTOPSIG(status));
else if (WIFCONTINUED(status))
_LOGI("client pid %d resumed (by SIGCONT)", pid);
else
_LOGW("client died abnormally");
_LOGI("client pid %d %s", pid, (desc = nm_utils_get_process_exit_status_desc(status)));
priv->pid = -1;

View file

@ -7,6 +7,7 @@
#define __NETWORKMANAGER_DNS_DNSMASQ_H__
#include "nm-dns-plugin.h"
#include "nm-dns-manager.h"
#define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type())
#define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq))

View file

@ -378,11 +378,11 @@ _mgr_get_configs_lst_head(NMDnsManager *self)
/*****************************************************************************/
gboolean
nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
NMDnsPlugin *
nm_dns_manager_get_systemd_resolved(NMDnsManager *self)
{
NMDnsManagerPrivate * priv;
NMDnsSystemdResolved *plugin = NULL;
NMDnsManagerPrivate *priv;
NMDnsPlugin * plugin = NULL;
g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
@ -390,11 +390,14 @@ nm_dns_manager_has_systemd_resolved(NMDnsManager *self)
if (priv->sd_resolve_plugin) {
nm_assert(!NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin));
plugin = NM_DNS_SYSTEMD_RESOLVED(priv->sd_resolve_plugin);
plugin = priv->sd_resolve_plugin;
} else if (NM_IS_DNS_SYSTEMD_RESOLVED(priv->plugin))
plugin = NM_DNS_SYSTEMD_RESOLVED(priv->plugin);
plugin = priv->plugin;
return plugin && nm_dns_systemd_resolved_is_running(plugin);
if (plugin && nm_dns_systemd_resolved_is_running(NM_DNS_SYSTEMD_RESOLVED(plugin)))
return plugin;
return NULL;
}
/*****************************************************************************/

View file

@ -11,6 +11,7 @@
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
#include "nm-setting-connection.h"
#include "nm-dns-plugin.h"
typedef enum {
NM_DNS_IP_CONFIG_TYPE_REMOVED = -1,
@ -148,7 +149,7 @@ typedef enum {
void nm_dns_manager_stop(NMDnsManager *self);
gboolean nm_dns_manager_has_systemd_resolved(NMDnsManager *self);
NMDnsPlugin *nm_dns_manager_get_systemd_resolved(NMDnsManager *self);
/*****************************************************************************/

View file

@ -6,7 +6,8 @@
#ifndef __NM_DNS_PLUGIN_H__
#define __NM_DNS_PLUGIN_H__
#include "nm-dns-manager.h"
#include "c-list/src/c-list.h"
#include "nm-config-data.h"
#define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type())

View file

@ -767,7 +767,7 @@ _resolve_handle_call_cb(GObject *source, GAsyncResult *result, gpointer user_dat
n = nm_g_array_append_new(v_names, NMDnsSystemdResolvedAddressResult);
*n = (NMDnsSystemdResolvedAddressResult){
.name = g_strdup(v_name),
.name = g_steal_pointer(&v_name),
.ifindex = v_ifindex,
};
}

View file

@ -8,6 +8,7 @@
#define __NETWORKMANAGER_DNS_SYSTEMD_RESOLVED_H__
#include "nm-dns-plugin.h"
#include "nm-dns-manager.h"
#define NM_TYPE_DNS_SYSTEMD_RESOLVED (nm_dns_systemd_resolved_get_type())
#define NM_DNS_SYSTEMD_RESOLVED(obj) \

View file

@ -904,7 +904,7 @@ nm_connectivity_check_start(NMConnectivity * self,
* This is relatively cumbersome to avoid, because we would have to go through
* NMDnsSystemdResolved trying to asynchronously start the service, to ensure there
* is only one attempt to start the service. */
has_systemd_resolved = nm_dns_manager_has_systemd_resolved(nm_dns_manager_get());
has_systemd_resolved = !!nm_dns_manager_get_systemd_resolved(nm_dns_manager_get());
if (has_systemd_resolved) {
GDBusConnection *dbus_connection;

View file

@ -29,6 +29,7 @@
#include "libnm-glib-aux/nm-io-utils.h"
#include "libnm-glib-aux/nm-secret-utils.h"
#include "libnm-glib-aux/nm-time-utils.h"
#include "libnm-glib-aux/nm-str-buf.h"
#include "nm-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-setting-connection.h"
@ -4656,3 +4657,323 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_activation_type_to_string,
NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_MANAGED, "managed"),
NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_ASSUME, "assume"),
NM_UTILS_LOOKUP_STR_ITEM(NM_ACTIVATION_TYPE_EXTERNAL, "external"), );
/*****************************************************************************/
typedef struct {
GPid pid;
GTask * task;
gulong cancellable_id;
GSource *child_watch_source;
GSource *timeout_source;
int child_stdin;
int child_stdout;
GSource *input_source;
GSource *output_source;
NMStrBuf in_buffer;
NMStrBuf out_buffer;
gsize out_buffer_offset;
} HelperInfo;
#define _NMLOG_PREFIX_NAME "helper"
#define _NMLOG_DOMAIN LOGD_CORE
#define _NMLOG2(level, info, ...) \
G_STMT_START \
{ \
if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \
HelperInfo *_info = (info); \
\
_nm_log((level), \
(_NMLOG_DOMAIN), \
0, \
NULL, \
NULL, \
_NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
",%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
NM_HASH_OBFUSCATE_PTR(_info), \
_info->pid _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} \
} \
G_STMT_END
static void
helper_info_free(gpointer data)
{
HelperInfo *info = data;
nm_clear_g_source_inst(&info->child_watch_source);
nm_clear_g_source_inst(&info->timeout_source);
g_object_unref(info->task);
nm_str_buf_destroy(&info->in_buffer);
nm_str_buf_destroy(&info->out_buffer);
nm_clear_g_source_inst(&info->input_source);
nm_clear_g_source_inst(&info->output_source);
if (info->child_stdout != -1)
nm_close(info->child_stdout);
if (info->child_stdin != -1)
nm_close(info->child_stdin);
if (info->pid != -1) {
nm_assert(info->pid > 1);
nm_utils_kill_child_async(info->pid, SIGKILL, LOGD_CORE, _NMLOG_PREFIX_NAME, 0, NULL, NULL);
}
g_free(info);
}
static void
helper_complete(HelperInfo *info, GError *error)
{
if (error) {
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task),
&info->cancellable_id);
g_task_return_error(info->task, error);
helper_info_free(info);
return;
}
if (info->input_source || info->output_source || info->pid != -1) {
/* Wait that pipes are closed and process has terminated */
return;
}
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
g_task_return_pointer(info->task, nm_str_buf_finalize(&info->in_buffer, NULL), g_free);
helper_info_free(info);
}
static gboolean
helper_can_write(int fd, GIOCondition condition, gpointer user_data)
{
HelperInfo *info = user_data;
gssize n_written;
int errsv;
if (NM_FLAGS_HAS(condition, G_IO_ERR)) {
errsv = EIO;
goto out_error;
} else if (NM_FLAGS_HAS(condition, G_IO_HUP)) {
errsv = EPIPE;
goto out_error;
}
n_written = write(info->child_stdin,
&((nm_str_buf_get_str_unsafe(&info->out_buffer))[info->out_buffer_offset]),
info->out_buffer.len - info->out_buffer_offset);
errsv = errno;
if (n_written < 0 && errsv != EAGAIN)
goto out_error;
if (n_written > 0) {
if ((gsize) n_written >= (info->out_buffer.len - info->out_buffer_offset)) {
nm_assert((gsize) n_written == (info->out_buffer.len - info->out_buffer_offset));
nm_clear_g_source_inst(&info->output_source);
nm_close(info->child_stdin);
info->child_stdin = -1;
return G_SOURCE_CONTINUE;
}
info->out_buffer_offset += (gsize) n_written;
}
return G_SOURCE_CONTINUE;
out_error:
nm_clear_g_source_inst(&info->output_source);
helper_complete(info,
g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"error writing to helper: %d (%s)",
errsv,
nm_strerror_native(errsv)));
return G_SOURCE_CONTINUE;
}
static gboolean
helper_have_data(int fd, GIOCondition condition, gpointer user_data)
{
HelperInfo *info = user_data;
gssize n_read;
GError * error = NULL;
n_read = nm_utils_fd_read(fd, &info->in_buffer);
_LOG2T(info, "read returns %ld", (long) n_read);
if (n_read > 0)
return G_SOURCE_CONTINUE;
nm_clear_g_source_inst(&info->input_source);
nm_close(info->child_stdout);
info->child_stdout = -1;
_LOG2T(info, "stdout closed");
if (n_read < 0) {
error = g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"read from process returned %d (%s)",
(int) -n_read,
nm_strerror_native((int) -n_read));
}
helper_complete(info, error);
return G_SOURCE_CONTINUE;
}
static void
helper_child_terminated(GPid pid, int status, gpointer user_data)
{
HelperInfo * info = user_data;
GError * error = NULL;
gs_free char *status_desc = NULL;
_LOG2D(info, "process %s", (status_desc = nm_utils_get_process_exit_status_desc(status)));
info->pid = -1;
nm_clear_g_source_inst(&info->child_watch_source);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (!status_desc)
status_desc = nm_utils_get_process_exit_status_desc(status);
error =
g_error_new(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "helper process %s", status_desc);
}
helper_complete(info, error);
}
static gboolean
helper_timeout(gpointer user_data)
{
HelperInfo *info = user_data;
nm_clear_g_source_inst(&info->timeout_source);
helper_complete(info, g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "timed out"));
return G_SOURCE_CONTINUE;
}
static void
helper_cancelled(GObject *object, gpointer user_data)
{
HelperInfo *info = user_data;
GError * error = NULL;
nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
nm_utils_error_set_cancelled(&error, FALSE, NULL);
helper_complete(info, error);
}
void
nm_utils_spawn_helper(const char *const * args,
GCancellable * cancellable,
GAsyncReadyCallback callback,
gpointer cb_data)
{
gs_free_error GError *error = NULL;
gs_free char * commands = NULL;
HelperInfo * info;
int fd_flags;
const char *const * arg;
nm_assert(args && args[0]);
info = g_new(HelperInfo, 1);
*info = (HelperInfo){
.task = nm_g_task_new(NULL, cancellable, nm_utils_spawn_helper, callback, cb_data),
.child_stdin = -1,
.child_stdout = -1,
.pid = -1,
};
if (!g_spawn_async_with_pipes("/",
(char **) NM_MAKE_STRV(LIBEXECDIR "/nm-daemon-helper"),
(char **) NM_MAKE_STRV(),
G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&info->pid,
&info->child_stdin,
&info->child_stdout,
NULL,
&error)) {
info->child_stdin = -1;
info->child_stdout = -1;
info->pid = -1;
g_task_return_error(info->task,
g_error_new(NM_UTILS_ERROR,
NM_UTILS_ERROR_UNKNOWN,
"error spawning nm-helper: %s",
error->message));
helper_info_free(info);
return;
}
_LOG2D(info, "spawned process with args: %s", (commands = g_strjoinv(" ", (char **) args)));
info->child_watch_source = g_child_watch_source_new(info->pid);
g_source_set_callback(info->child_watch_source,
G_SOURCE_FUNC(helper_child_terminated),
info,
NULL);
g_source_attach(info->child_watch_source, g_main_context_get_thread_default());
info->timeout_source =
nm_g_timeout_source_new_seconds(20, G_PRIORITY_DEFAULT, helper_timeout, info, NULL);
g_source_attach(info->timeout_source, g_main_context_get_thread_default());
/* Set file descriptors as non-blocking */
fd_flags = fcntl(info->child_stdin, F_GETFD, 0);
fcntl(info->child_stdin, F_SETFL, fd_flags | O_NONBLOCK);
fd_flags = fcntl(info->child_stdout, F_GETFD, 0);
fcntl(info->child_stdout, F_SETFL, fd_flags | O_NONBLOCK);
/* Watch process stdin */
nm_str_buf_init(&info->out_buffer, 32, TRUE);
for (arg = args; *arg; arg++) {
nm_str_buf_append(&info->out_buffer, *arg);
nm_str_buf_append_c(&info->out_buffer, '\0');
}
info->output_source = nm_g_unix_fd_source_new(info->child_stdin,
G_IO_OUT | G_IO_ERR | G_IO_HUP,
G_PRIORITY_DEFAULT,
helper_can_write,
info,
NULL);
g_source_attach(info->output_source, g_main_context_get_thread_default());
/* Watch process stdout */
nm_str_buf_init(&info->in_buffer, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
info->input_source = nm_g_unix_fd_source_new(info->child_stdout,
G_IO_IN | G_IO_ERR | G_IO_HUP,
G_PRIORITY_DEFAULT,
helper_have_data,
info,
NULL);
g_source_attach(info->input_source, g_main_context_get_thread_default());
if (cancellable) {
gulong signal_id;
signal_id = g_cancellable_connect(cancellable, G_CALLBACK(helper_cancelled), info, NULL);
if (signal_id == 0) {
/* the request is already cancelled. Return. */
return;
}
info->cancellable_id = signal_id;
}
}
char *
nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error)
{
GTask *task = G_TASK(result);
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_spawn_helper));
return g_task_propagate_pointer(task, error);
}

View file

@ -420,4 +420,13 @@ guint8 nm_wifi_utils_level_to_quality(int val);
#define NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN "Unable to determine UID of the request"
#define NM_UTILS_ERROR_MSG_INSUFF_PRIV "Insufficient privileges"
/*****************************************************************************/
void nm_utils_spawn_helper(const char *const * args,
GCancellable * cancellable,
GAsyncReadyCallback callback,
gpointer cb_data);
char *nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error);
#endif /* __NM_CORE_UTILS_H__ */

View file

@ -6306,3 +6306,20 @@ nm_crypto_md5_hash(const guint8 *salt,
g_checksum_update(ctx, digest.ptr, NM_UTILS_CHECKSUM_LENGTH_MD5);
}
}
/*****************************************************************************/
char *
nm_utils_get_process_exit_status_desc(int status)
{
if (WIFEXITED(status))
return g_strdup_printf("exited with status %d", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
return g_strdup_printf("killed by signal %d", WTERMSIG(status));
else if (WIFSTOPPED(status))
return g_strdup_printf("stopped by signal %d", WSTOPSIG(status));
else if (WIFCONTINUED(status))
return g_strdup("resumed by SIGCONT)");
else
return g_strdup_printf("exited with unknown status 0x%x", status);
}

View file

@ -277,7 +277,29 @@ nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src)
nm_assert(dst);
nm_assert(src);
memcpy(dst, src, (addr_family != AF_INET6) ? sizeof(in_addr_t) : sizeof(struct in6_addr));
memcpy(dst, src, NM_IS_IPv4(addr_family) ? sizeof(in_addr_t) : sizeof(struct in6_addr));
}
static inline NMIPAddr
nm_ip_addr_init(int addr_family, gconstpointer src)
{
NMIPAddr a;
nm_assert_addr_family(addr_family);
nm_assert(src);
G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr));
if (NM_IS_IPv4(addr_family)) {
memcpy(&a, src, sizeof(in_addr_t));
/* ensure all bytes of the union are initialized. If only to make
* valgrind happy. */
memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t));
} else
memcpy(&a, src, sizeof(struct in6_addr));
return a;
}
gboolean nm_ip_addr_set_from_untrusted(int addr_family,
@ -2920,4 +2942,8 @@ void nm_crypto_md5_hash(const guint8 *salt,
guint8 * buffer,
gsize buflen);
/*****************************************************************************/
char *nm_utils_get_process_exit_status_desc(int status);
#endif /* __NM_SHARED_UTILS_H__ */

View file

@ -93,6 +93,7 @@ if enable_nmtui
endif
subdir('nmcli')
subdir('nm-dispatcher')
subdir('nm-daemon-helper')
subdir('nm-online')
if enable_nmtui
subdir('nmtui')

View file

@ -0,0 +1,15 @@
executable(
'nm-daemon-helper',
'nm-daemon-helper.c',
include_directories : [
src_inc,
top_inc,
],
link_with: [
libnm_std_aux,
],
link_args: ldflags_linker_script_binary,
link_depends: linker_script_binary,
install: true,
install_dir: nm_libexecdir,
)

View file

@ -0,0 +1,112 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* Copyright (C) 2021 Red Hat, Inc. */
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <nss.h>
#include "libnm-std-aux/nm-std-aux.h"
enum {
RETURN_SUCCESS = 0,
RETURN_INVALID_CMD = 1,
RETURN_INVALID_ARGS = 2,
RETURN_ERROR = 3,
};
static char *
read_arg(void)
{
nm_auto_free char *arg = NULL;
size_t len = 0;
if (getdelim(&arg, &len, '\0', stdin) < 0)
return NULL;
return nm_steal_pointer(&arg);
}
static int
more_args(void)
{
nm_auto_free char *arg = NULL;
arg = read_arg();
return !!arg;
}
static int
cmd_version(void)
{
if (more_args())
return RETURN_INVALID_ARGS;
printf("1");
return RETURN_SUCCESS;
}
static int
cmd_resolve_address(void)
{
nm_auto_free char *address = NULL;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} sockaddr;
socklen_t sockaddr_size;
char name[NI_MAXHOST];
address = read_arg();
if (!address)
return RETURN_INVALID_ARGS;
if (more_args())
return RETURN_INVALID_ARGS;
memset(&sockaddr, 0, sizeof(sockaddr));
__nss_configure_lookup("hosts", "dns");
if (inet_pton(AF_INET, address, &sockaddr.in.sin_addr) == 1) {
sockaddr.in.sin_family = AF_INET;
sockaddr_size = sizeof(struct sockaddr_in);
} else if (inet_pton(AF_INET6, address, &sockaddr.in6.sin6_addr) == 1) {
sockaddr.in6.sin6_family = AF_INET6;
sockaddr_size = sizeof(struct sockaddr_in6);
} else
return RETURN_INVALID_ARGS;
if (getnameinfo((struct sockaddr *) &sockaddr,
sockaddr_size,
name,
sizeof(name),
NULL,
0,
NI_NAMEREQD)
!= 0)
return RETURN_ERROR;
printf("%s", name);
return RETURN_SUCCESS;
}
int
main(int argc, char **argv)
{
nm_auto_free char *cmd = NULL;
cmd = read_arg();
if (!cmd)
return RETURN_INVALID_CMD;
if (nm_streq(cmd, "version"))
return cmd_version();
if (nm_streq(cmd, "resolve-address"))
return cmd_resolve_address();
return RETURN_INVALID_CMD;
}

View file

@ -361,8 +361,8 @@ complete_script(ScriptInfo *script)
static void
script_watch_cb(GPid pid, int status, gpointer user_data)
{
ScriptInfo *script = user_data;
guint err;
ScriptInfo * script = user_data;
gs_free char *status_desc = NULL;
g_assert(pid == script->pid);
@ -372,23 +372,11 @@ script_watch_cb(GPid pid, int status, gpointer user_data)
if (!script->wait)
script->request->num_scripts_nowait--;
if (WIFEXITED(status)) {
err = WEXITSTATUS(status);
if (err == 0)
script->result = DISPATCH_RESULT_SUCCESS;
else {
script->error =
g_strdup_printf("Script '%s' exited with error status %d.", script->script, err);
}
} else if (WIFSTOPPED(status)) {
script->error = g_strdup_printf("Script '%s' stopped unexpectedly with signal %d.",
script->script,
WSTOPSIG(status));
} else if (WIFSIGNALED(status)) {
script->error =
g_strdup_printf("Script '%s' died with signal %d", script->script, WTERMSIG(status));
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
script->result = DISPATCH_RESULT_SUCCESS;
} else {
script->error = g_strdup_printf("Script '%s' died from an unknown cause", script->script);
status_desc = nm_utils_get_process_exit_status_desc(status);
script->error = g_strdup_printf("Script '%s' %s.", script->script, status_desc);
}
if (script->result == DISPATCH_RESULT_SUCCESS) {