summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-04-05 08:46:42 +0200
committerThomas Haller <thaller@redhat.com>2023-04-05 08:46:42 +0200
commitfba38266bc7cc21c7fa7ca628df97126ecec8671 (patch)
treed103dd04b10b1d745abc8f5f5e2ba6a715d95f26
parenta2db213a45f6be26a846fc1946d7a78ba2316d4d (diff)
parent3a76d717dab30e525ff7e451bd8418ebba9c7afc (diff)
downloadNetworkManager-fba38266bc7cc21c7fa7ca628df97126ecec8671.tar.gz
ovs: merge branch 'th/ovsdb'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1593
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c335
-rw-r--r--src/core/dns/nm-dns-manager.c2
-rw-r--r--src/core/nm-core-utils.c18
-rw-r--r--src/core/nm-manager.c10
-rw-r--r--src/libnm-client-test/nm-test-utils-impl.c3
-rw-r--r--src/libnm-glib-aux/nm-io-utils.c80
-rw-r--r--src/libnm-glib-aux/nm-io-utils.h10
-rw-r--r--src/libnm-platform/nm-netlink.c11
-rw-r--r--src/libnm-platform/nm-netlink.h2
-rw-r--r--src/libnm-platform/tests/test-nm-platform.c1
-rw-r--r--src/libnmc-base/nm-polkit-listener.c10
-rw-r--r--src/libnmc-base/nm-secret-agent-simple.c2
12 files changed, 300 insertions, 184 deletions
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 85b7953f7e..68366f7398 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -12,6 +12,7 @@
#include "libnm-glib-aux/nm-jansson.h"
#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-io-utils.h"
#include "nm-core-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "devices/nm-device.h"
@@ -134,14 +135,18 @@ enum {
static guint signals[LAST_SIGNAL] = {0};
typedef struct {
- NMPlatform *platform;
- GSocketConnection *conn;
- GCancellable *conn_cancellable;
- char buf[4096]; /* Input buffer */
- size_t bufp; /* Last decoded byte in the input buffer. */
- GString *input; /* JSON stream waiting for decoding. */
- GString *output; /* JSON stream to be sent. */
- guint64 call_id_counter;
+ NMPlatform *platform;
+ int conn_fd;
+ GSource *conn_fd_in_source;
+ GSource *conn_fd_out_source;
+ GCancellable *conn_cancellable;
+
+ NMStrBuf input_buf;
+ NMStrBuf output_buf;
+
+ GSource *input_timeout_source;
+
+ guint64 call_id_counter;
CList calls_lst_head;
@@ -177,12 +182,13 @@ NM_DEFINE_SINGLETON_GETTER(NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB);
/*****************************************************************************/
-static void ovsdb_try_connect(NMOvsdb *self);
-static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing);
-static void ovsdb_read(NMOvsdb *self);
-static void ovsdb_write(NMOvsdb *self);
-static void ovsdb_next_command(NMOvsdb *self);
-static void cleanup_check_ready(NMOvsdb *self);
+static void ovsdb_try_connect(NMOvsdb *self);
+static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing);
+static void ovsdb_read(NMOvsdb *self);
+static void ovsdb_write_try(NMOvsdb *self);
+static gboolean ovsdb_write_cb(int fd, GIOCondition condition, gpointer user_data);
+static void ovsdb_next_command(NMOvsdb *self);
+static void cleanup_check_ready(NMOvsdb *self);
/*****************************************************************************/
@@ -1446,10 +1452,10 @@ ovsdb_next_command(NMOvsdb *self)
{
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
OvsdbMethodCall *call;
- char *cmd;
+ nm_auto_free char *cmd = NULL;
nm_auto_decref_json json_t *msg = NULL;
- if (!priv->conn)
+ if (priv->conn_fd < 0)
return;
if (c_list_is_empty(&priv->calls_lst_head))
@@ -1586,10 +1592,9 @@ ovsdb_next_command(NMOvsdb *self)
cmd = json_dumps(msg, 0);
_LOGT_call(call, "send: call-id=%" G_GUINT64_FORMAT ", %s", call->call_id, cmd);
- g_string_append(priv->output, cmd);
- free(cmd);
+ nm_str_buf_append(&priv->output_buf, cmd);
- ovsdb_write(self);
+ ovsdb_write_try(self);
}
/**
@@ -2188,20 +2193,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
static void
ovsdb_got_echo(NMOvsdb *self, json_int_t id, json_t *data)
{
- NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- nm_auto_decref_json json_t *msg = NULL;
- char *reply;
- gboolean output_was_empty;
-
- output_was_empty = priv->output->len == 0;
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
+ nm_auto_decref_json json_t *msg = NULL;
+ nm_auto_free char *reply = NULL;
msg = json_pack("{s:I, s:O}", "id", id, "result", data);
reply = json_dumps(msg, 0);
- g_string_append(priv->output, reply);
- free(reply);
- if (output_was_empty)
- ovsdb_write(self);
+ _LOGT("send: echo: %s", reply);
+
+ nm_str_buf_append(&priv->output_buf, reply);
+
+ ovsdb_write_try(self);
}
/**
@@ -2274,13 +2277,13 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
/* This is a response to a method call. */
if (c_list_is_empty(&priv->calls_lst_head)) {
- _LOGE("there are no queued calls expecting response %" G_GUINT64_FORMAT, (guint64) id);
+ _LOGW("there are no queued calls expecting response %" G_GUINT64_FORMAT, (guint64) id);
ovsdb_disconnect(self, FALSE, FALSE);
return;
}
call = c_list_first_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst);
if (call->call_id != id) {
- _LOGE("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT,
+ _LOGW("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT,
call->call_id,
(guint64) id);
ovsdb_disconnect(self, FALSE, FALSE);
@@ -2305,7 +2308,7 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
/* Don't progress further commands in case the callback hit an error
* and disconnected us. */
- if (!priv->conn)
+ if (priv->conn_fd < 0)
return;
/* Now we're free to serialize and send the next command, if any. */
@@ -2320,138 +2323,197 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg)
/*****************************************************************************/
+typedef struct {
+ gsize bufp;
+ NMStrBuf *input;
+} JsonReadMsgData;
+
/* Lower level marshalling and demarshalling of the JSON-RPC traffic on the
* ovsdb socket. */
static size_t
-_json_callback(void *buffer, size_t buflen, void *user_data)
+_json_read_msg_cb(void *buffer, size_t buflen, void *user_data)
{
- NMOvsdb *self = NM_OVSDB(user_data);
- NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
+ JsonReadMsgData *data = user_data;
+
+ nm_assert(buffer);
+ nm_assert(buflen > 0);
- if (priv->bufp == priv->input->len) {
+ if (data->bufp == data->input->len) {
/* No more bytes buffered for decoding. */
return 0;
}
/* Pass one more byte to the JSON decoder. */
- *(char *) buffer = priv->input->str[priv->bufp];
- priv->bufp++;
-
- return (size_t) 1;
+ *(char *) buffer = nm_str_buf_get_char(data->input, data->bufp);
+ data->bufp++;
+ return 1;
}
-/**
- * ovsdb_read_cb:
- *
- * Read out the data available from the ovsdb socket and try to deserialize
- * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg().
- */
-static void
-ovsdb_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+static json_t *
+_json_read_msg(NMOvsdb *self, NMStrBuf *input)
{
- NMOvsdb *self = NM_OVSDB(user_data);
- NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- GInputStream *stream = G_INPUT_STREAM(source_object);
- GError *error = NULL;
- gssize size;
- json_t *msg;
- json_error_t json_error = {
+ gs_free char *ss = NULL;
+ JsonReadMsgData data = {
+ .bufp = 0,
+ .input = input,
+ };
+ json_error_t json_error = {
0,
};
+ json_t *msg;
- size = g_input_stream_read_finish(stream, res, &error);
- if (size == -1) {
- /* ovsdb-server was possibly restarted */
- _LOGW("short read from ovsdb: %s", error->message);
- priv->num_failures++;
- g_clear_error(&error);
- ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE);
- return;
- }
+ /* The callback always eats up only up to a single byte. This makes it
+ * possible for us to identify complete JSON objects in spite of us not
+ * knowing the length in advance. */
+ msg = json_load_callback(_json_read_msg_cb, &data, JSON_DISABLE_EOF_CHECK, &json_error);
+ if (!msg)
+ return NULL;
- g_string_append_len(priv->input, priv->buf, size);
- do {
- priv->bufp = 0;
- /* The callback always eats up only up to a single byte. This makes
- * it possible for us to identify complete JSON objects in spite of
- * us not knowing the length in advance. */
- msg = json_load_callback(_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error);
- if (msg) {
- ovsdb_got_msg(self, msg);
- g_string_erase(priv->input, 0, priv->bufp);
- }
- json_decref(msg);
- } while (msg);
+ nm_assert(data.bufp > 0);
- if (!priv->conn)
- return;
+ _LOGT("json: parse %zu bytes: \"%s\"",
+ data.bufp,
+ (ss = g_strndup(nm_str_buf_get_str_at_unsafe(input, 0), data.bufp)));
- if (size)
- ovsdb_read(self);
+ nm_str_buf_erase(input, 0, data.bufp, FALSE);
+ return msg;
}
-static void
-ovsdb_read(NMOvsdb *self)
+static gboolean
+_ovsdb_read_input_timeout_cb(gpointer user_data)
{
+ NMOvsdb *self = user_data;
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- g_input_stream_read_async(g_io_stream_get_input_stream(G_IO_STREAM(priv->conn)),
- priv->buf,
- sizeof(priv->buf),
- G_PRIORITY_DEFAULT,
- NULL,
- ovsdb_read_cb,
- self);
+ _LOGW("invalid/incomplete data in receive buffer. Reset");
+ priv->num_failures++;
+ ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE);
+ return G_SOURCE_CONTINUE;
}
static void
-ovsdb_write_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+ovsdb_read(NMOvsdb *self)
{
- GOutputStream *stream = G_OUTPUT_STREAM(source_object);
- NMOvsdb *self = NM_OVSDB(user_data);
- NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- GError *error = NULL;
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
gssize size;
- size = g_output_stream_write_finish(stream, res, &error);
- if (size == -1) {
+again:
+ size = nm_utils_fd_read(priv->conn_fd, &priv->input_buf);
+
+ if (size <= 0) {
+ if (size == -EAGAIN) {
+ if (priv->input_buf.len == 0)
+ nm_clear_g_source_inst(&priv->input_timeout_source);
+ else if (!priv->input_timeout_source) {
+ /* We have data in the buffer but nothing further to read. Schedule a timer,
+ * if we don't get the rest within timeout, it means that the buffer
+ * content is broken (_json_read_msg() cannot extract any data) and
+ * we disconnect. */
+ priv->input_timeout_source =
+ nm_g_timeout_add_seconds_source(5, _ovsdb_read_input_timeout_cb, NULL);
+ }
+ return;
+ }
+
/* ovsdb-server was possibly restarted */
- _LOGW("short write to ovsdb: %s", error->message);
+ _LOGW("short read from ovsdb: %s", nm_strerror_native(-size));
priv->num_failures++;
- g_clear_error(&error);
ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE);
return;
}
- if (!priv->conn)
- return;
+ nm_assert(priv->input_buf.len > 0);
+
+ while (TRUE) {
+ nm_auto_decref_json json_t *msg = NULL;
+
+ msg = _json_read_msg(self, &priv->input_buf);
+ if (!msg)
+ break;
- g_string_erase(priv->output, 0, size);
+ nm_clear_g_source_inst(&priv->input_timeout_source);
+ ovsdb_got_msg(self, msg);
- ovsdb_write(self);
+ if (priv->input_buf.len == 0)
+ break;
+ }
+
+ if (priv->input_buf.len > 0) {
+ if (priv->input_buf.len > 50 * 1024 * 1024) {
+ _LOGW("received too much data from ovsdb that is not valid JSON");
+ priv->num_failures++;
+ ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE);
+ return;
+ }
+ /* We have an incomplete message in the message buffer. Don't wait for another round
+ * of "poll", instead try to read it again. */
+ goto again;
+ }
+
+ nm_clear_g_source_inst(&priv->input_timeout_source);
+}
+
+static gboolean
+ovsdb_read_cb(int fd, GIOCondition condition, gpointer user_data)
+{
+ ovsdb_read(user_data);
+ return G_SOURCE_CONTINUE;
}
static void
ovsdb_write(NMOvsdb *self)
{
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- GOutputStream *stream;
+ gssize n;
- if (!priv->output->len)
+again:
+ if (priv->output_buf.len == 0) {
+ nm_clear_g_source_inst(&priv->conn_fd_out_source);
return;
+ }
+
+ n = write(priv->conn_fd,
+ nm_str_buf_get_str_at_unsafe(&priv->output_buf, 0),
+ priv->output_buf.len);
+
+ if (n < 0)
+ n = -NM_ERRNO_NATIVE(errno);
- stream = g_io_stream_get_output_stream(G_IO_STREAM(priv->conn));
- if (g_output_stream_has_pending(stream))
+ if (n == -EAGAIN) {
+ if (!priv->conn_fd_out_source) {
+ priv->conn_fd_out_source =
+ nm_g_unix_fd_add_source(priv->conn_fd, G_IO_OUT, ovsdb_write_cb, self);
+ }
return;
+ }
+
+ if (n <= 0) {
+ /* ovsdb-server was possibly restarted */
+ _LOGW("short write to ovsdb: %s", nm_strerror_native(-n));
+ priv->num_failures++;
+ ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE);
+ return;
+ }
- g_output_stream_write_async(stream,
- priv->output->str,
- priv->output->len,
- G_PRIORITY_DEFAULT,
- NULL,
- ovsdb_write_cb,
- self);
+ nm_str_buf_erase(&priv->output_buf, 0, n, FALSE);
+ goto again;
+}
+
+static void
+ovsdb_write_try(NMOvsdb *self)
+{
+ NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
+
+ if (priv->conn_fd >= 0 && !priv->conn_fd_out_source)
+ ovsdb_write(self);
+}
+
+static gboolean
+ovsdb_write_cb(int fd, GIOCondition condition, gpointer user_data)
+{
+ ovsdb_write(user_data);
+ return G_SOURCE_CONTINUE;
}
/*****************************************************************************/
@@ -2474,7 +2536,7 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
nm_assert(!retry || !is_disposing);
- if (!priv->conn && !priv->conn_cancellable)
+ if (priv->conn_fd < 0 && !priv->conn_cancellable)
return;
_LOGD("disconnecting from ovsdb, retry %d", retry);
@@ -2498,10 +2560,12 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
_call_complete(call, NULL, error);
}
- priv->bufp = 0;
- g_string_truncate(priv->input, 0);
- g_string_truncate(priv->output, 0);
- g_clear_object(&priv->conn);
+ nm_str_buf_reset(&priv->input_buf);
+ nm_str_buf_reset(&priv->output_buf);
+ nm_clear_fd(&priv->conn_fd);
+ nm_clear_g_source_inst(&priv->conn_fd_in_source);
+ nm_clear_g_source_inst(&priv->conn_fd_out_source);
+ nm_clear_g_source_inst(&priv->input_timeout_source);
nm_clear_g_free(&priv->db_uuid);
nm_clear_g_cancellable(&priv->conn_cancellable);
@@ -2702,15 +2766,12 @@ _ovsdb_connect_complete_with_fd(NMOvsdb *self, int fd_take)
gs_unref_object GSocket *socket = NULL;
gs_free_error GError *error = NULL;
- socket = g_socket_new_from_fd(nm_steal_fd(&fd_take), &error);
- if (!socket) {
- _LOGT("connect: failure to open socket for new FD: %s", error->message);
- ovsdb_disconnect(self, FALSE, FALSE);
- return;
- }
+ nm_clear_g_cancellable(&priv->conn_cancellable);
+
+ nm_io_fcntl_setfl_update_nonblock(fd_take);
- priv->conn = g_socket_connection_factory_create_connection(socket);
- g_clear_object(&priv->conn_cancellable);
+ priv->conn_fd = nm_steal_fd(&fd_take);
+ priv->conn_fd_in_source = nm_g_unix_fd_add_source(priv->conn_fd, G_IO_IN, ovsdb_read_cb, self);
ovsdb_read(self);
ovsdb_next_command(self);
@@ -2784,7 +2845,7 @@ ovsdb_try_connect(NMOvsdb *self)
{
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
- if (priv->conn || priv->conn_cancellable)
+ if (priv->conn_fd >= 0 || priv->conn_cancellable)
return;
_LOGT("connect: start connecting socket %s on idle", NM_OVSDB_SOCKET);
@@ -2964,11 +3025,15 @@ nm_ovsdb_init(NMOvsdb *self)
{
NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
+ priv->conn_fd = -1;
+
+ priv->input_buf = NM_STR_BUF_INIT(0, FALSE);
+ priv->output_buf = NM_STR_BUF_INIT(0, FALSE);
+
c_list_init(&priv->calls_lst_head);
priv->platform = g_object_ref(NM_PLATFORM_GET);
- priv->input = g_string_new(NULL);
- priv->output = g_string_new(NULL);
+
priv->bridges =
g_hash_table_new_full(nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _free_bridge, NULL);
priv->ports =
@@ -2989,14 +3054,8 @@ dispose(GObject *object)
nm_assert(c_list_is_empty(&priv->calls_lst_head));
- if (priv->input) {
- g_string_free(priv->input, TRUE);
- priv->input = NULL;
- }
- if (priv->output) {
- g_string_free(priv->output, TRUE);
- priv->output = NULL;
- }
+ nm_str_buf_destroy(&priv->input_buf);
+ nm_str_buf_destroy(&priv->output_buf);
g_clear_object(&priv->platform);
nm_clear_pointer(&priv->bridges, g_hash_table_destroy);
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index 6ee2e816a0..fb65afcadc 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -647,7 +647,7 @@ run_netconfig(NMDnsManager *self, GError **error, int *stdin_fd)
if (!g_spawn_async_with_pipes(NULL,
argv,
NULL,
- G_SPAWN_DO_NOT_REAP_CHILD,
+ G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&pid,
diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c
index 7482292034..628947a969 100644
--- a/src/core/nm-core-utils.c
+++ b/src/core/nm-core-utils.c
@@ -5090,7 +5090,6 @@ nm_utils_spawn_helper(const char *const *args,
gs_free_error GError *error = NULL;
gs_free char *commands = NULL;
HelperInfo *info;
- int fd_flags;
const char *const *arg;
GMainContext *context;
gsize n;
@@ -5099,16 +5098,13 @@ nm_utils_spawn_helper(const char *const *args,
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,
+ .task = nm_g_task_new(NULL, cancellable, nm_utils_spawn_helper, callback, cb_data),
};
if (!g_spawn_async_with_pipes("/",
(char **) NM_MAKE_STRV(LIBEXECDIR "/nm-daemon-helper"),
(char **) NM_MAKE_STRV(),
- G_SPAWN_DO_NOT_REAP_CHILD,
+ G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&info->pid,
@@ -5162,13 +5158,9 @@ nm_utils_spawn_helper(const char *const *args,
nm_g_timeout_source_new_seconds(20, G_PRIORITY_DEFAULT, helper_timeout, info, NULL);
g_source_attach(info->timeout_source, context);
- /* 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);
- fd_flags = fcntl(info->child_stderr, F_GETFD, 0);
- fcntl(info->child_stderr, F_SETFL, fd_flags | O_NONBLOCK);
+ nm_io_fcntl_setfl_update_nonblock(info->child_stdin);
+ nm_io_fcntl_setfl_update_nonblock(info->child_stdout);
+ nm_io_fcntl_setfl_update_nonblock(info->child_stderr);
/* Watch process stdin */
for (n = 1, arg = args; *arg; arg++)
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index b10e78bee3..f6902b9786 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -3021,7 +3021,7 @@ _rfkill_update_system(NMManager *self, NMRfkillType rtype, gboolean enabled)
nm_assert(NM_IN_SET(rtype, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN));
- fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
+ fd = open("/dev/rfkill", O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
if (errno == EACCES)
_LOGW(LOGD_RFKILL,
@@ -3030,14 +3030,6 @@ _rfkill_update_system(NMManager *self, NMRfkillType rtype, gboolean enabled)
return;
}
- if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
- _LOGW(LOGD_RFKILL,
- "rfkill: (%s): failed to set killswitch device for "
- "non-blocking operation",
- nm_rfkill_type_to_string(rtype));
- return;
- }
-
memset(&event, 0, sizeof(event));
event.op = KERN_RFKILL_OP_CHANGE_ALL;
switch (rtype) {
diff --git a/src/libnm-client-test/nm-test-utils-impl.c b/src/libnm-client-test/nm-test-utils-impl.c
index ad642d2530..ba541ad83a 100644
--- a/src/libnm-client-test/nm-test-utils-impl.c
+++ b/src/libnm-client-test/nm-test-utils-impl.c
@@ -107,7 +107,8 @@ nmtstc_service_init(void)
g_spawn_async_with_pipes(NULL,
(char **) args,
NULL,
- G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ G_SPAWN_CLOEXEC_PIPES | G_SPAWN_SEARCH_PATH
+ | G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&info->pid,
diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c
index 1340e1b45f..ec016ed8a3 100644
--- a/src/libnm-glib-aux/nm-io-utils.c
+++ b/src/libnm-glib-aux/nm-io-utils.c
@@ -21,6 +21,86 @@
/*****************************************************************************/
+int
+nm_io_fcntl_getfl(int fd)
+{
+ int f;
+
+ nm_assert(fd >= 0);
+
+ f = fcntl(fd, F_GETFL, 0);
+
+ /* The caller really must provide a valid FD. For a valid FD, there is not
+ * reason why this call could fail (or how we could handle the failure).
+ *
+ * Unlike plain fcntl(), nm_io_fcntl_getfl() cannot fail. */
+ nm_assert(f != -1);
+
+ /* We not only assert that the return value is "!= -1", but that it's not
+ * negative. Negative flags would be very odd, and not something we would
+ * expect for a successful call. */
+ nm_assert(f >= 0);
+
+ return f;
+}
+
+int
+nm_io_fcntl_setfl(int fd, int flags)
+{
+ int f;
+ int errsv;
+
+ nm_assert(fd >= 0);
+ nm_assert(flags >= 0);
+
+ f = fcntl(fd, F_SETFL, flags);
+ if (f != 0) {
+ errsv = errno;
+
+ nm_assert(errsv != EBADF);
+
+ return -NM_ERRNO_NATIVE(errsv);
+ }
+
+ return 0;
+}
+
+int
+nm_io_fcntl_setfl_update(int fd, int flags_mask, int flags_value)
+{
+ int flags_current;
+
+ nm_assert(fd >= 0);
+ nm_assert(flags_mask > 0);
+ nm_assert(flags_value >= 0);
+ nm_assert(((~flags_mask) & flags_value) == 0);
+
+ flags_current = nm_io_fcntl_getfl(fd);
+ return nm_io_fcntl_setfl(fd, (flags_current & ~flags_mask) | (flags_mask & flags_value));
+}
+
+void
+nm_io_fcntl_setfl_update_nonblock(int fd)
+{
+ int r;
+
+ nm_assert(fd >= 0);
+
+ r = nm_io_fcntl_setfl_update(fd, O_NONBLOCK, O_NONBLOCK);
+
+ /* nm_io_fcntl_setfl_update() already asserts that it cannot fail with
+ * EBADF.
+ *
+ * In nm_io_fcntl_setfl_update_nonblock() only sts O_NONBLOCK, where we
+ * don't expect any other error. Kernel should never reject setting this
+ * flags, and if it did, we have to find out how to handle that. Currently
+ * we don't handle it and assert against failure. */
+
+ nm_assert(r == 0);
+}
+
+/*****************************************************************************/
+
_nm_printf(4, 5) static int _get_contents_error(GError **error,
int errsv,
int *out_errsv,
diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h
index 5401814018..0021138f46 100644
--- a/src/libnm-glib-aux/nm-io-utils.h
+++ b/src/libnm-glib-aux/nm-io-utils.h
@@ -10,6 +10,16 @@
/*****************************************************************************/
+int nm_io_fcntl_getfl(int fd);
+
+int nm_io_fcntl_setfl(int fd, int flags);
+
+int nm_io_fcntl_setfl_update(int fd, int flags_mask, int flags_value);
+
+void nm_io_fcntl_setfl_update_nonblock(int fd);
+
+/*****************************************************************************/
+
/**
* NMUtilsFileGetContentsFlags:
* @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag
diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c
index 00b6880abb..5684b8cda5 100644
--- a/src/libnm-platform/nm-netlink.c
+++ b/src/libnm-platform/nm-netlink.c
@@ -1037,17 +1037,6 @@ nlmsg_get_dst(struct nl_msg *msg)
}
int
-nl_socket_set_nonblocking(const struct nl_sock *sk)
-{
- nm_assert_sk(sk);
-
- if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
- return -nm_errno_from_native(errno);
-
- return 0;
-}
-
-int
nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
{
int err;
diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h
index 6fed8189b7..35e2962984 100644
--- a/src/libnm-platform/nm-netlink.h
+++ b/src/libnm-platform/nm-netlink.h
@@ -605,8 +605,6 @@ int nl_socket_set_passcred(struct nl_sock *sk, int state);
int nl_socket_set_pktinfo(struct nl_sock *sk, int state);
-int nl_socket_set_nonblocking(const struct nl_sock *sk);
-
uint32_t nl_socket_get_local_port(const struct nl_sock *sk);
int nl_socket_add_memberships(struct nl_sock *sk, int group, ...);
diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c
index 90a2904599..5fc8a5dded 100644
--- a/src/libnm-platform/tests/test-nm-platform.c
+++ b/src/libnm-platform/tests/test-nm-platform.c
@@ -117,7 +117,6 @@ test_use_symbols(void)
(void (*)(void)) nl_socket_set_passcred,
(void (*)(void)) nl_socket_set_msg_buf_size,
(void (*)(void)) nlmsg_get_dst,
- (void (*)(void)) nl_socket_set_nonblocking,
(void (*)(void)) nl_socket_set_buffer_size,
(void (*)(void)) nl_socket_add_memberships,
(void (*)(void)) nl_wait_for_ack,
diff --git a/src/libnmc-base/nm-polkit-listener.c b/src/libnmc-base/nm-polkit-listener.c
index e7972faa48..c715b0494a 100644
--- a/src/libnmc-base/nm-polkit-listener.c
+++ b/src/libnmc-base/nm-polkit-listener.c
@@ -499,7 +499,6 @@ out:
static void
begin_authentication(AuthRequest *request)
{
- int fd_flags;
const char *helper_argv[] = {
POLKIT_AGENT_HELPER_1_PATH,
request->username,
@@ -514,7 +513,7 @@ begin_authentication(AuthRequest *request)
if (!g_spawn_async_with_pipes(NULL,
(char **) helper_argv,
NULL,
- G_SPAWN_STDERR_TO_DEV_NULL,
+ G_SPAWN_CLOEXEC_PIPES | G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
NULL,
@@ -534,11 +533,8 @@ begin_authentication(AuthRequest *request)
return;
}
- fd_flags = fcntl(request->child_stdin, F_GETFD, 0);
- fcntl(request->child_stdin, F_SETFL, fd_flags | O_NONBLOCK);
-
- fd_flags = fcntl(request->child_stdout, F_GETFD, 0);
- fcntl(request->child_stdout, F_SETFL, fd_flags | O_NONBLOCK);
+ nm_io_fcntl_setfl_update_nonblock(request->child_stdin);
+ nm_io_fcntl_setfl_update_nonblock(request->child_stdout);
request->child_stdout_watch_source = nm_g_unix_fd_source_new(request->child_stdout,
G_IO_IN | G_IO_ERR | G_IO_HUP,
diff --git a/src/libnmc-base/nm-secret-agent-simple.c b/src/libnmc-base/nm-secret-agent-simple.c
index d35ffd7975..1b9aa57142 100644
--- a/src/libnmc-base/nm-secret-agent-simple.c
+++ b/src/libnmc-base/nm-secret-agent-simple.c
@@ -787,7 +787,7 @@ try_spawn_vpn_auth_helper(RequestData *request, GPtrArray *secrets)
if (!g_spawn_async_with_pipes(NULL,
(char **) auth_dialog_argv->pdata,
NULL,
- G_SPAWN_DO_NOT_REAP_CHILD,
+ G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&auth_dialog_pid,