summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2017-11-23 15:01:26 +0100
committerThomas Haller <thaller@redhat.com>2017-11-23 15:01:26 +0100
commit4e214ce0afd58abc90b94d03ee190676fb48815c (patch)
tree0595e52494d12f6f7e68778fd88a7e2af1b17727
parent00c808c41084b23dcfcab839c6c1ea7d3d6c3796 (diff)
parente93ca7fc129ec0f29f5313a3aa12839914df8fa2 (diff)
downloadnetwork-manager-applet-4e214ce0afd58abc90b94d03ee190676fb48815c.tar.gz
applet: merge branch 'th/vpn-secrets-bgo790655'
https://bugzilla.gnome.org/show_bug.cgi?id=790655
-rw-r--r--Makefile.am3
-rw-r--r--m4/compiler_options.m48
-rw-r--r--po/POTFILES.in1
-rw-r--r--shared/nm-utils/nm-compat.c88
-rw-r--r--shared/nm-utils/nm-compat.h53
-rw-r--r--shared/nm-utils/nm-glib.h29
-rw-r--r--shared/nm-utils/nm-macros-internal.h388
-rw-r--r--shared/nm-utils/nm-shared-utils.c468
-rw-r--r--shared/nm-utils/nm-shared-utils.h180
-rw-r--r--shared/nm-utils/nm-test-utils.h56
-rw-r--r--src/applet-vpn-request.c516
11 files changed, 1434 insertions, 356 deletions
diff --git a/Makefile.am b/Makefile.am
index cb583b1b..e430516e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -775,6 +775,7 @@ nm_applet_c_gen = \
src/applet-resources.c
nm_applet_hc_real = \
+ shared/nm-utils/nm-compat.c \
src/main.c \
src/applet.c \
src/applet.h \
@@ -864,6 +865,8 @@ EXTRA_DIST += \
EXTRA_DIST += \
shared/nm-utils/gsystem-local-alloc.h \
+ shared/nm-utils/nm-compat.c \
+ shared/nm-utils/nm-compat.h \
shared/nm-utils/nm-glib.h \
shared/nm-utils/nm-macros-internal.h \
shared/nm-utils/nm-shared-utils.c \
diff --git a/m4/compiler_options.m4 b/m4/compiler_options.m4
index 5275281d..ccb51f5e 100644
--- a/m4/compiler_options.m4
+++ b/m4/compiler_options.m4
@@ -70,11 +70,13 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
-Wimplicit-fallthrough \
-Wimplicit-function-declaration \
-Winit-self \
+ -Wlogical-op \
-Wmissing-declarations \
-Wmissing-include-dirs \
-Wmissing-prototypes \
-Wpointer-arith \
-Wshadow \
+ -Wshift-negative-value \
-Wstrict-prototypes \
-Wundef \
-Wno-duplicate-decl-specifier \
@@ -119,6 +121,12 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
[int f () { int i = yolo; yolo; return i; }]
)
+ dnl clang 3.9 would like to see "{ { 0 } }" here, but that does not
+ dnl look too wise.
+ NM_COMPILER_WARNING([missing-braces],
+ [union { int a[1]; int b[2]; } c = { 0 }]
+ )
+
CFLAGS="$CFLAGS_MORE_WARNINGS $CFLAGS"
else
AC_MSG_RESULT(no)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 989dafc1..02e61e71 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -14,6 +14,7 @@ src/applet-device-bt.c
src/applet-device-ethernet.c
src/applet-device-wifi.c
src/applet-dialogs.c
+src/applet-vpn-request.c
src/applet.h
[type: gettext/glade]src/connection-editor/ce-ip4-routes.ui
[type: gettext/glade]src/connection-editor/ce-ip6-routes.ui
diff --git a/shared/nm-utils/nm-compat.c b/shared/nm-utils/nm-compat.c
new file mode 100644
index 00000000..22ab675d
--- /dev/null
+++ b/shared/nm-utils/nm-compat.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-compat.h"
+
+/*****************************************************************************/
+
+static void
+_get_keys_cb (const char *key, const char *val, gpointer user_data)
+{
+ GPtrArray *a = user_data;
+
+ g_ptr_array_add (a, g_strdup (key));
+}
+
+static const char **
+_get_keys (NMSettingVpn *setting,
+ gboolean is_secrets,
+ guint *out_length)
+{
+ guint len;
+ const char **keys = NULL;
+ gs_unref_ptrarray GPtrArray *a = NULL;
+
+ nm_assert (NM_IS_SETTING_VPN (setting));
+
+ a = g_ptr_array_new ();
+ if (is_secrets)
+ nm_setting_vpn_foreach_secret (setting, _get_keys_cb, a);
+ else
+ nm_setting_vpn_foreach_data_item (setting, _get_keys_cb, a);
+ len = a->len;
+
+ if (a->len) {
+ g_ptr_array_sort (a, nm_strcmp_p);
+ g_ptr_array_add (a, NULL);
+ keys = (const char **) g_ptr_array_free (g_steal_pointer (&a), FALSE);
+
+ /* we need to cache the keys *somewhere*. */
+ g_object_set_qdata_full (G_OBJECT (setting),
+ is_secrets
+ ? NM_CACHED_QUARK ("libnm._nm_setting_vpn_get_secret_keys")
+ : NM_CACHED_QUARK ("libnm._nm_setting_vpn_get_data_keys"),
+ keys,
+ (GDestroyNotify) g_strfreev);
+ }
+
+ NM_SET_OUT (out_length, len);
+ return keys;
+}
+
+const char **
+_nm_setting_vpn_get_data_keys (NMSettingVpn *setting,
+ guint *out_length)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return _get_keys (setting, FALSE, out_length);
+}
+
+const char **
+_nm_setting_vpn_get_secret_keys (NMSettingVpn *setting,
+ guint *out_length)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return _get_keys (setting, TRUE, out_length);
+}
diff --git a/shared/nm-utils/nm-compat.h b/shared/nm-utils/nm-compat.h
new file mode 100644
index 00000000..52341690
--- /dev/null
+++ b/shared/nm-utils/nm-compat.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_COMPAT_H__
+#define __NM_COMPAT_H__
+
+#include "nm-setting-vpn.h"
+
+const char **_nm_setting_vpn_get_data_keys (NMSettingVpn *setting,
+ guint *out_length);
+
+const char **_nm_setting_vpn_get_secret_keys (NMSettingVpn *setting,
+ guint *out_length);
+
+#if NM_CHECK_VERSION (1, 11, 0)
+#define nm_setting_vpn_get_data_keys(setting, out_length) \
+ ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ nm_setting_vpn_get_data_keys (setting, out_length); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+#define nm_setting_vpn_get_secret_keys(setting, out_length) \
+ ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ nm_setting_vpn_get_secret_keys (setting, out_length); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+#else
+#define nm_setting_vpn_get_data_keys(setting, out_length) \
+ _nm_setting_vpn_get_data_keys (setting, out_length)
+#define nm_setting_vpn_get_secret_keys(setting, out_length) \
+ _nm_setting_vpn_get_secret_keys (setting, out_length)
+#endif
+
+#endif /* __NM_COMPAT_H__ */
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index dd18756a..4ef538e4 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -452,4 +452,33 @@ _nm_g_variant_new_take_string (gchar *string)
}
#define g_variant_new_take_string _nm_g_variant_new_take_string
+#if !GLIB_CHECK_VERSION(2, 38, 0)
+_nm_printf (1, 2)
+static inline GVariant *
+_nm_g_variant_new_printf (const char *format_string, ...)
+{
+ char *string;
+ va_list ap;
+
+ g_return_val_if_fail (format_string, NULL);
+
+ va_start (ap, format_string);
+ string = g_strdup_vprintf (format_string, ap);
+ va_end (ap);
+
+ return g_variant_new_take_string (string);
+}
+#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__)
+#else
+#define g_variant_new_printf(...) \
+ ({ \
+ GVariant *_v; \
+ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ _v = g_variant_new_printf (__VA_ARGS__); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ _v; \
+ })
+#endif
+
#endif /* __NM_GLIB_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index d11766f9..cf9142fe 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -26,20 +26,41 @@
#include <stdlib.h>
#include <errno.h>
-#include "nm-glib.h"
+#define _nm_packed __attribute__ ((packed))
+#define _nm_unused __attribute__ ((unused))
+#define _nm_pure __attribute__ ((pure))
+#define _nm_const __attribute__ ((const))
+#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#define _nm_align(s) __attribute__ ((aligned (s)))
+#define _nm_alignof(type) __alignof (type)
+#define _nm_alignas(type) _nm_align (_nm_alignof (type))
+
+/*****************************************************************************/
+
+#ifdef thread_local
+#define _nm_thread_local thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define _nm_thread_local _Thread_local
+#else
+#define _nm_thread_local __thread
+#endif
/*****************************************************************************/
-#define _nm_packed __attribute__ ((packed))
-#define _nm_unused __attribute__ ((unused))
-#define _nm_pure __attribute__ ((pure))
-#define _nm_const __attribute__ ((const))
-#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#include "nm-glib.h"
+
+/*****************************************************************************/
#define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m))
#define nm_auto(fcn) __attribute__ ((cleanup(fcn)))
+static inline int nm_close (int fd);
+
/**
* nm_auto_free:
*
@@ -49,6 +70,30 @@
GS_DEFINE_CLEANUP_FUNCTION(void*, _nm_auto_free_impl, free)
static inline void
+nm_free_secret (char *secret)
+{
+ if (secret) {
+ memset (secret, 0, strlen (secret));
+ g_free (secret);
+ }
+}
+
+static inline void
+_nm_auto_free_secret_impl (char **v)
+{
+ nm_free_secret (*v);
+}
+
+/**
+ * nm_auto_free_secret:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out
+ * the secret.
+ */
+#define nm_auto_free_secret nm_auto(_nm_auto_free_secret_impl)
+
+static inline void
_nm_auto_unset_gvalue_impl (GValue *v)
{
g_value_unset (v);
@@ -77,7 +122,7 @@ _nm_auto_close_impl (int *pfd)
if (*pfd >= 0) {
int errsv = errno;
- (void) close (*pfd);
+ (void) nm_close (*pfd);
errno = errsv;
}
}
@@ -242,31 +287,137 @@ NM_G_ERROR_MSG (GError *error)
/*****************************************************************************/
-#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__))
+#ifndef _NM_CC_SUPPORT_AUTO_TYPE
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 )))
+#define _NM_CC_SUPPORT_AUTO_TYPE 1
+#else
+#define _NM_CC_SUPPORT_AUTO_TYPE 0
+#endif
+#endif
+
+#ifndef _NM_CC_SUPPORT_GENERIC
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__))
#define _NM_CC_SUPPORT_GENERIC 1
#else
#define _NM_CC_SUPPORT_GENERIC 0
#endif
+#endif
+
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _nm_auto_type __auto_type
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const alias_type3 *const: ((const type *) (obj)), \
+ const alias_type3 * : ((const type *) (obj)), \
+ alias_type3 *const: (( type *) (obj)), \
+ alias_type3 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const alias_type3 *const: ((const type *) (obj)), \
+ const alias_type3 * : ((const type *) (obj)), \
+ alias_type3 *const: (( type *) (obj)), \
+ alias_type3 * : (( type *) (obj)), \
+ const alias_type4 *const: ((const type *) (obj)), \
+ const alias_type4 * : ((const type *) (obj)), \
+ alias_type4 *const: (( type *) (obj)), \
+ alias_type4 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj, ##__VA_ARGS__))
+#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_x (type, obj_expr, obj, n, ##__VA_ARGS__))
+#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) (_NM_CONSTCAST_FULL_y (type, obj_expr, obj, NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__))
+#else
+#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) ((type *) (obj))
+#endif
+
+#define NM_CONSTCAST(type, obj, ...) \
+ NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__)
+
+#define NM_GOBJECT_CAST(type, obj, is_check, ...) \
+ ({ \
+ const void *_obj = (obj); \
+ \
+ nm_assert (_obj || (is_check (_obj))); \
+ NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+ })
+
+#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \
+ ({ \
+ const void *_obj = (obj); \
+ \
+ nm_assert (is_check (_obj)); \
+ NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+ })
+
+#if _NM_CC_SUPPORT_GENERIC
+/* returns @value, if the type of @value matches @type.
+ * This requires support for C11 _Generic(). If no support is
+ * present, this returns @value directly.
+ *
+ * It's useful to check the let the compiler ensure that @value is
+ * of a certain type. */
+#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value)))
+#else
+#define _NM_ENSURE_TYPE(type, value) (value)
+#endif
#if _NM_CC_SUPPORT_GENERIC
-#define _NM_CONSTCAST(type, obj) \
- (_Generic ((obj), \
- void * : ((type *) (obj)), \
- void *const : ((type *) (obj)), \
- const void * : ((const type *) (obj)), \
- const void *const: ((const type *) (obj)), \
- const type * : (obj), \
- const type *const: (obj), \
- type * : (obj), \
- type *const : (obj)))
+#define NM_PROPAGATE_CONST(test_expr, ptr) \
+ (_Generic ((test_expr), \
+ const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \
+ default: (_Generic ((test_expr), \
+ typeof (*(test_expr)) *: (ptr)))))
#else
-/* _NM_CONSTCAST() is there to preserve constness of a pointer.
- * It uses C11's _Generic(). If that is not supported, we fall back
- * to casting away constness. So, with _Generic, we get some additional
- * static type checking by preserving constness, without, we cast it
- * to a non-const pointer. */
-#define _NM_CONSTCAST(type, obj) \
- ((type *) (obj))
+#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr)
#endif
/*****************************************************************************/
@@ -410,21 +561,6 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
/*****************************************************************************/
-static inline guint
-NM_HASH_COMBINE (guint h, guint val)
-{
- /* see g_str_hash() for reasons */
- return (h << 5) + h + val;
-}
-
-static inline guint
-NM_HASH_COMBINE_UINT64 (guint h, guint64 val)
-{
- return NM_HASH_COMBINE (h, (((guint) val) & 0xFFFFFFFFu) + ((guint) (val >> 32)));
-}
-
-/*****************************************************************************/
-
/* NM_CACHED_QUARK() returns the GQuark for @string, but caches
* it in a static variable to speed up future lookups.
*
@@ -563,39 +699,17 @@ _notify (obj_type *obj, _PropertyEnums prop) \
/*****************************************************************************/
-/* these are implemented as a macro, because they accept self
- * as both (type*) and (const type*), and return a const
- * private pointer accordingly. */
-#define __NM_GET_PRIVATE(self, type, is_check, addrop) \
- ({ \
- /* preserve the const-ness of self. Unfortunately, that
- * way, @self cannot be a void pointer */ \
- typeof (self) _self = (self); \
- \
- /* Get compiler error if variable is of wrong type */ \
- _nm_unused const type *const _self2 = (_self); \
- \
- nm_assert (is_check (_self)); \
- ( addrop ( _NM_CONSTCAST (type, _self)->_priv) ); \
- })
-
-#define _NM_GET_PRIVATE(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, &)
-#define _NM_GET_PRIVATE_PTR(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, )
-
-#define __NM_GET_PRIVATE_VOID(self, type, is_check, result_cmd) \
+#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv))
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \
({ \
- /* (self) can be any non-const pointer. It will be cast to "type *".
- * We don't explicitly cast but assign first to (void *) which
- * will fail if @self is pointing to const. */ \
- void *const _self1 = (self); \
- type *const _self = _self1; \
+ _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \
\
- nm_assert (is_check (_self)); \
- ( result_cmd ); \
+ NM_PROPAGATE_CONST (_self, _self->_priv); \
})
-
-#define _NM_GET_PRIVATE_VOID(self, type, is_check) __NM_GET_PRIVATE_VOID(self, type, is_check, &_self->_priv)
-#define _NM_GET_PRIVATE_PTR_VOID(self, type, is_check) __NM_GET_PRIVATE_VOID(self, type, is_check, _self->_priv)
+#else
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv)
+#endif
/*****************************************************************************/
@@ -619,6 +733,36 @@ nm_g_object_unref (gpointer obj)
g_object_unref (obj);
}
+/* Assigns GObject @obj to destination @pdst, and takes an additional ref.
+ * The previous value of @pdst is unrefed.
+ *
+ * It makes sure to first increase the ref-count of @obj, and handles %NULL
+ * @obj correctly.
+ * */
+#define nm_g_object_ref_set(pp, obj) \
+ ({ \
+ typeof (*(pp)) *const _pp = (pp); \
+ typeof (**_pp) *const _obj = (obj); \
+ typeof (**_pp) *_p; \
+ gboolean _changed = FALSE; \
+ \
+ if ( _pp \
+ && ((_p = *_pp) != _obj)) { \
+ if (_obj) { \
+ nm_assert (G_IS_OBJECT (_obj)); \
+ g_object_ref (_obj); \
+ } \
+ if (_p) { \
+ nm_assert (G_IS_OBJECT (_p)); \
+ *_pp = NULL; \
+ g_object_unref (_p); \
+ } \
+ *_pp = _obj; \
+ _changed = TRUE; \
+ } \
+ _changed; \
+ })
+
/* basically, replaces
* g_clear_pointer (&location, g_free)
* with
@@ -631,13 +775,32 @@ nm_g_object_unref (gpointer obj)
#define nm_clear_g_free(pp) \
({ \
typeof (*(pp)) *_pp = (pp); \
- typeof (**_pp) *_p = *_pp; \
+ typeof (**_pp) *_p; \
+ gboolean _changed = FALSE; \
\
- if (_p) { \
+ if ( _pp \
+ && (_p = *_pp)) { \
*_pp = NULL; \
g_free (_p); \
+ _changed = TRUE; \
} \
- !!_p; \
+ _changed; \
+ })
+
+#define nm_clear_g_object(pp) \
+ ({ \
+ typeof (*(pp)) *_pp = (pp); \
+ typeof (**_pp) *_p; \
+ gboolean _changed = FALSE; \
+ \
+ if ( _pp \
+ && (_p = *_pp)) { \
+ nm_assert (G_IS_OBJECT (_p)); \
+ *_pp = NULL; \
+ g_object_unref (_p); \
+ _changed = TRUE; \
+ } \
+ _changed; \
})
static inline gboolean
@@ -809,6 +972,33 @@ nm_strstrip (char *str)
return str ? g_strstrip (str) : NULL;
}
+static inline const char *
+nm_strstrip_avoid_copy (const char *str, char **str_free)
+{
+ gsize l;
+ char *s;
+
+ nm_assert (str_free && !*str_free);
+
+ if (!str)
+ return NULL;
+
+ str = nm_str_skip_leading_spaces (str);
+ l = strlen (str);
+ if ( l == 0
+ || !g_ascii_isspace (str[l - 1]))
+ return str;
+ while ( l > 0
+ && g_ascii_isspace (str[l - 1]))
+ l--;
+
+ s = g_new (char, l + 1);
+ memcpy (s, str, l);
+ s[l] = '\0';
+ *str_free = s;
+ return s;
+}
+
/* g_ptr_array_sort()'s compare function takes pointers to the
* value. Thus, you cannot use strcmp directly. You can use
* nm_strcmp_p().
@@ -982,6 +1172,28 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
_buf; \
})
+/* aims to alloca() a buffer and fill it with printf(format, name).
+ * Note that format must not contain any format specifier except
+ * "%s".
+ * If the resulting string would be too large for stack allocation,
+ * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */
+#define nm_construct_name_a(format, name, p_val_to_free) \
+ ({ \
+ const char *const _name = (name); \
+ char **const _p_val_to_free = (p_val_to_free); \
+ const gsize _name_len = strlen (_name); \
+ char *_buf2; \
+ \
+ nm_assert (_p_val_to_free && !*_p_val_to_free); \
+ if (NM_STRLEN (format) + _name_len < 200) \
+ _buf2 = nm_sprintf_bufa (NM_STRLEN (format) + _name_len, format, _name); \
+ else { \
+ _buf2 = g_strdup_printf (format, _name); \
+ *_p_val_to_free = _buf2; \
+ } \
+ (const char *) _buf2; \
+ })
+
/*****************************************************************************/
/**
@@ -1057,4 +1269,34 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
/*****************************************************************************/
+static inline int
+nm_steal_fd (int *p_fd)
+{
+ int fd;
+
+ if ( p_fd
+ && ((fd = *p_fd) >= 0)) {
+ *p_fd = -1;
+ return fd;
+ }
+ return -1;
+}
+
+/**
+ * nm_close:
+ *
+ * Like close() but throws an assertion if the input fd is
+ * invalid. Closing an invalid fd is a programming error, so
+ * it's better to catch it early.
+ */
+static inline int
+nm_close (int fd)
+{
+ int r;
+
+ r = close (fd);
+ nm_assert (r != -1 || fd < 0 || errno != EBADF);
+ return r;
+}
+
#endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 0109818a..79d1e6aa 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -25,6 +25,8 @@
#include <errno.h>
#include <arpa/inet.h>
+#include <poll.h>
+#include <fcntl.h>
/*****************************************************************************/
@@ -32,6 +34,10 @@ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL };
/*****************************************************************************/
+const NMIPAddr nm_ip_addr_zero = { 0 };
+
+/*****************************************************************************/
+
void
nm_utils_strbuf_append_c (char **buf, gsize *len, char c)
{
@@ -112,6 +118,162 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
/*****************************************************************************/
/**
+ * nm_strquote:
+ * @buf: the output buffer of where to write the quoted @str argument.
+ * @buf_len: the size of @buf.
+ * @str: (allow-none): the string to quote.
+ *
+ * Writes @str to @buf with quoting. The resulting buffer
+ * is always NUL terminated, unless @buf_len is zero.
+ * If @str is %NULL, it writes "(null)".
+ *
+ * If @str needs to be truncated, the closing quote is '^' instead
+ * of '"'.
+ *
+ * This is similar to nm_strquote_a(), which however uses alloca()
+ * to allocate a new buffer. Also, here @buf_len is the size of @buf,
+ * while nm_strquote_a() has the number of characters to print. The latter
+ * doesn't include the quoting.
+ *
+ * Returns: the input buffer with the quoted string.
+ */
+const char *
+nm_strquote (char *buf, gsize buf_len, const char *str)
+{
+ const char *const buf0 = buf;
+
+ if (!str) {
+ nm_utils_strbuf_append_str (&buf, &buf_len, "(null)");
+ goto out;
+ }
+
+ if (G_UNLIKELY (buf_len <= 2)) {
+ switch (buf_len) {
+ case 2:
+ *(buf++) = '^';
+ /* fall-through */
+ case 1:
+ *(buf++) = '\0';
+ break;
+ }
+ goto out;
+ }
+
+ *(buf++) = '"';
+ buf_len--;
+
+ nm_utils_strbuf_append_str (&buf, &buf_len, str);
+
+ /* if the string was too long we indicate truncation with a
+ * '^' instead of a closing quote. */
+ if (G_UNLIKELY (buf_len <= 1)) {
+ switch (buf_len) {
+ case 1:
+ buf[-1] = '^';
+ break;
+ case 0:
+ buf[-2] = '^';
+ break;
+ default:
+ nm_assert_not_reached ();
+ break;
+ }
+ } else {
+ nm_assert (buf_len >= 2);
+ *(buf++) = '"';
+ *(buf++) = '\0';
+ }
+
+out:
+ return buf0;
+}
+
+/*****************************************************************************/
+
+char _nm_utils_to_string_buffer[];
+
+void
+nm_utils_to_string_buffer_init (char **buf, gsize *len)
+{
+ if (!*buf) {
+ *buf = _nm_utils_to_string_buffer;
+ *len = sizeof (_nm_utils_to_string_buffer);
+ }
+}
+
+gboolean
+nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len)
+{
+ nm_utils_to_string_buffer_init (buf, len);
+ if (!obj) {
+ g_strlcpy (*buf, "(null)", *len);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+ gsize n_descs,
+ unsigned flags,
+ char *buf,
+ gsize len)
+{
+ gsize i;
+ char *p;
+
+#if NM_MORE_ASSERTS > 10
+ nm_assert (descs);
+ nm_assert (n_descs > 0);
+ for (i = 0; i < n_descs; i++) {
+ gsize j;
+
+ nm_assert (descs[i].name && descs[i].name[0]);
+ for (j = 0; j < i; j++)
+ nm_assert (descs[j].flag != descs[i].flag);
+ }
+#endif
+
+ nm_utils_to_string_buffer_init (&buf, &len);
+
+ if (!len)
+ return buf;
+
+ buf[0] = '\0';
+ p = buf;
+ if (!flags) {
+ for (i = 0; i < n_descs; i++) {
+ if (!descs[i].flag) {
+ nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+ break;
+ }
+ }
+ return buf;
+ }
+
+ for (i = 0; flags && i < n_descs; i++) {
+ if ( descs[i].flag
+ && NM_FLAGS_ALL (flags, descs[i].flag)) {
+ flags &= ~descs[i].flag;
+
+ if (buf[0] != '\0')
+ nm_utils_strbuf_append_c (&p, &len, ',');
+ nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+ }
+ }
+ if (flags) {
+ if (buf[0] != '\0')
+ nm_utils_strbuf_append_c (&p, &len, ',');
+ nm_utils_strbuf_append (&p, &len, "0x%x", flags);
+ }
+ return buf;
+};
+
+/*****************************************************************************/
+
+/**
* _nm_utils_ip4_prefix_to_netmask:
* @prefix: a CIDR prefix
*
@@ -170,52 +332,79 @@ nm_utils_ip_is_site_local (int addr_family,
/*****************************************************************************/
gboolean
-nm_utils_parse_inaddr (const char *text,
- int family,
- char **out_addr)
+nm_utils_parse_inaddr_bin (int addr_family,
+ const char *text,
+ gpointer out_addr)
{
- union {
- in_addr_t v4;
- struct in6_addr v6;
- } addrbin;
- char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+ NMIPAddr addrbin;
g_return_val_if_fail (text, FALSE);
- if (family == AF_UNSPEC)
- family = strchr (text, ':') ? AF_INET6 : AF_INET;
+ if (addr_family == AF_UNSPEC)
+ addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
else
- g_return_val_if_fail (NM_IN_SET (family, AF_INET, AF_INET6), FALSE);
+ g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
- if (inet_pton (family, text, &addrbin) != 1)
+ /* use a temporary variable @addrbin, to guarantee that @out_addr
+ * is only modified on success. */
+ if (inet_pton (addr_family, text, &addrbin) != 1)
return FALSE;
- NM_SET_OUT (out_addr, g_strdup (inet_ntop (family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
+ if (out_addr) {
+ switch (addr_family) {
+ case AF_INET:
+ *((in_addr_t *) out_addr) = addrbin.addr4;
+ break;
+ case AF_INET6:
+ *((struct in6_addr *) out_addr) = addrbin.addr6;
+ break;
+ default:
+ nm_assert_not_reached ();
+ }
+ }
return TRUE;
}
gboolean
-nm_utils_parse_inaddr_prefix (const char *text,
- int family,
- char **out_addr,
- int *out_prefix)
+nm_utils_parse_inaddr (int addr_family,
+ const char *text,
+ char **out_addr)
+{
+ NMIPAddr addrbin;
+ char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+ nm_assert (!out_addr || !*out_addr);
+
+ if (!nm_utils_parse_inaddr_bin (addr_family, text, &addrbin))
+ return FALSE;
+ NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
+ return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr_prefix_bin (int addr_family,
+ const char *text,
+ gpointer out_addr,
+ int *out_prefix)
{
gs_free char *addrstr_free = NULL;
int prefix = -1;
const char *slash;
const char *addrstr;
- union {
- in_addr_t v4;
- struct in6_addr v6;
- } addrbin;
- char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+ NMIPAddr addrbin;
+ int addr_len;
g_return_val_if_fail (text, FALSE);
- if (family == AF_UNSPEC)
- family = strchr (text, ':') ? AF_INET6 : AF_INET;
+ if (addr_family == AF_UNSPEC)
+ addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+
+ if (addr_family == AF_INET)
+ addr_len = sizeof (in_addr_t);
+ else if (addr_family == AF_INET6)
+ addr_len = sizeof (struct in6_addr);
else
- g_return_val_if_fail (NM_IN_SET (family, AF_INET, AF_INET6), FALSE);
+ g_return_val_if_reached (FALSE);
slash = strchr (text, '/');
if (slash)
@@ -223,23 +412,39 @@ nm_utils_parse_inaddr_prefix (const char *text,
else
addrstr = text;
- if (inet_pton (family, addrstr, &addrbin) != 1)
+ if (inet_pton (addr_family, addrstr, &addrbin) != 1)
return FALSE;
if (slash) {
prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10,
0,
- family == AF_INET ? 32 : 128,
+ addr_family == AF_INET ? 32 : 128,
-1);
if (prefix == -1)
return FALSE;
}
- NM_SET_OUT (out_addr, g_strdup (inet_ntop (family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
+ if (out_addr)
+ memcpy (out_addr, &addrbin, addr_len);
NM_SET_OUT (out_prefix, prefix);
return TRUE;
}
+gboolean
+nm_utils_parse_inaddr_prefix (int addr_family,
+ const char *text,
+ char **out_addr,
+ int *out_prefix)
+{
+ NMIPAddr addrbin;
+ char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+ if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addrbin, out_prefix))
+ return FALSE;
+ NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
+ return TRUE;
+}
+
/*****************************************************************************/
/* _nm_utils_ascii_str_to_int64:
@@ -295,6 +500,118 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
/*****************************************************************************/
/**
+ * nm_utils_strsplit_set:
+ * @str: the string to split.
+ * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n",
+ * like bash's $IFS.
+ *
+ * This is a replacement for g_strsplit_set() which avoids copying
+ * each word once (the entire strv array), but instead copies it once
+ * and all words point into that internal copy.
+ *
+ * Another difference from g_strsplit_set() is that this never returns
+ * empty words. Multiple delimiters are combined and treated as one.
+ *
+ * Returns: %NULL if @str is %NULL or contains only delimiters.
+ * Otherwise, a %NULL terminated strv array containing non-empty
+ * words, split at the delimiter characters (delimiter characters
+ * are removed).
+ * The strings to which the result strv array points to are allocated
+ * after the returned result itself. Don't free the strings themself,
+ * but free everything with g_free().
+ */
+const char **
+nm_utils_strsplit_set (const char *str, const char *delimiters)
+{
+ const char **ptr, **ptr0;
+ gsize alloc_size, plen, i;
+ gsize str_len;
+ char *s0;
+ char *s;
+ guint8 delimiters_table[256];
+
+ if (!str)
+ return NULL;
+
+ /* initialize lookup table for delimiter */
+ if (!delimiters)
+ delimiters = " \t\n";
+ memset (delimiters_table, 0, sizeof (delimiters_table));
+ for (i = 0; delimiters[i]; i++)
+ delimiters_table[(guint8) delimiters[i]] = 1;
+
+#define _is_delimiter(ch, delimiters_table) \
+ ((delimiters_table)[(guint8) (ch)] != 0)
+
+ /* skip initial delimiters, and return of the remaining string is
+ * empty. */
+ while (_is_delimiter (str[0], delimiters_table))
+ str++;
+ if (!str[0])
+ return NULL;
+
+ str_len = strlen (str) + 1;
+ alloc_size = 8;
+
+ /* we allocate the buffer larger, so to copy @str at the
+ * end of it as @s0. */
+ ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+ s0 = (char *) &ptr0[alloc_size + 1];
+ memcpy (s0, str, str_len);
+
+ plen = 0;
+ s = s0;
+ ptr = ptr0;
+
+ while (TRUE) {
+ if (plen >= alloc_size) {
+ const char **ptr_old = ptr;
+
+ /* reallocate the buffer. Note that for now the string
+ * continues to be in ptr0/s0. We fix that at the end. */
+ alloc_size += 2;
+ ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+ memcpy (ptr, ptr_old, sizeof (const char *) * plen);
+ if (ptr_old != ptr0)
+ g_free (ptr_old);
+ }
+
+ ptr[plen++] = s;
+
+ nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table));
+
+ while (TRUE) {
+ s++;
+ if (_is_delimiter (s[0], delimiters_table))
+ break;
+ if (s[0] == '\0')
+ goto done;
+ }
+
+ s[0] = '\0';
+ s++;
+ while (_is_delimiter (s[0], delimiters_table))
+ s++;
+ if (s[0] == '\0')
+ break;
+ }
+done:
+ ptr[plen] = NULL;
+
+ if (ptr != ptr0) {
+ /* we reallocated the buffer. We must copy over the
+ * string @s0 and adjust the pointers. */
+ s = (char *) &ptr[alloc_size + 1];
+ memcpy (s, s0, str_len);
+ for (i = 0; i < plen; i++)
+ ptr[i] = &s[ptr[i] - s0];
+ g_free (ptr0);
+ }
+
+ return ptr;
+}
+
+/**
* nm_utils_strv_find_first:
* @list: the strv list to search
* @len: the length of the list, or a negative value if @list is %NULL terminated.
@@ -677,3 +994,98 @@ nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
}
return str;
}
+
+/*****************************************************************************/
+
+/* taken from systemd's fd_wait_for_event(). Note that the timeout
+ * is here in nano-seconds, not micro-seconds. */
+int
+nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
+{
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = event,
+ };
+ struct timespec ts, *pts;
+ int r;
+
+ if (timeout_ns < 0)
+ pts = NULL;
+ else {
+ ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND);
+ ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND);
+ pts = &ts;
+ }
+
+ r = ppoll (&pollfd, 1, pts, NULL);
+ if (r < 0)
+ return -errno;
+ if (r == 0)
+ return 0;
+ return pollfd.revents;
+}
+
+/* taken from systemd's loop_read() */
+ssize_t
+nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+ uint8_t *p = buf;
+ ssize_t n = 0;
+
+ g_return_val_if_fail (fd >= 0, -EINVAL);
+ g_return_val_if_fail (buf, -EINVAL);
+
+ /* If called with nbytes == 0, let's call read() at least
+ * once, to validate the operation */
+
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
+
+ do {
+ ssize_t k;
+
+ k = read (fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via read() */
+
+ (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1);
+ continue;
+ }
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (k == 0)
+ return n;
+
+ g_assert ((size_t) k <= nbytes);
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ } while (nbytes > 0);
+
+ return n;
+}
+
+/* taken from systemd's loop_read_exact() */
+int
+nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+ ssize_t n;
+
+ n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return (int) n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+
+ return 0;
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 8d1085f6..1e80b35a 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -22,6 +22,63 @@
#ifndef __NM_SHARED_UTILS_H__
#define __NM_SHARED_UTILS_H__
+#include <netinet/in.h>
+
+/*****************************************************************************/
+
+static inline char
+nm_utils_addr_family_to_char (int addr_family)
+{
+ switch (addr_family) {
+ case AF_INET: return '4';
+ case AF_INET6: return '6';
+ }
+ g_return_val_if_reached ('?');
+}
+
+static inline gsize
+nm_utils_addr_family_to_size (int addr_family)
+{
+ switch (addr_family) {
+ case AF_INET: return sizeof (in_addr_t);
+ case AF_INET6: return sizeof (struct in6_addr);
+ }
+ g_return_val_if_reached (0);
+}
+
+#define nm_assert_addr_family(addr_family) \
+ nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
+
+/*****************************************************************************/
+
+typedef struct {
+ union {
+ guint8 addr_ptr[1];
+ in_addr_t addr4;
+ struct in6_addr addr6;
+
+ /* NMIPAddr is really a union for IP addresses.
+ * However, as ethernet addresses fit in here nicely, use
+ * it also for an ethernet MAC address. */
+ guint8 addr_eth[6 /*ETH_ALEN*/];
+ };
+} NMIPAddr;
+
+extern const NMIPAddr nm_ip_addr_zero;
+
+static inline void
+nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
+{
+ nm_assert_addr_family (addr_family);
+ nm_assert (dst);
+ nm_assert (src);
+
+ if (addr_family != AF_INET6)
+ *((in_addr_t *) dst) = src->addr4;
+ else
+ *((struct in6_addr *) dst) = src->addr6;
+}
+
/*****************************************************************************/
#define NM_CMP_RETURN(c) \
@@ -132,8 +189,12 @@ void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _n
void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
+const char *nm_strquote (char *buf, gsize buf_len, const char *str);
+
/*****************************************************************************/
+const char **nm_utils_strsplit_set (const char *str, const char *delimiters);
+
gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
char **_nm_utils_strv_cleanup (char **strv,
@@ -151,12 +212,21 @@ gboolean nm_utils_ip_is_site_local (int addr_family,
/*****************************************************************************/
-gboolean nm_utils_parse_inaddr (const char *text,
- int family,
+gboolean nm_utils_parse_inaddr_bin (int addr_family,
+ const char *text,
+ gpointer out_addr);
+
+gboolean nm_utils_parse_inaddr (int addr_family,
+ const char *text,
char **out_addr);
-gboolean nm_utils_parse_inaddr_prefix (const char *text,
- int family,
+gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family,
+ const char *text,
+ gpointer out_addr,
+ int *out_prefix);
+
+gboolean nm_utils_parse_inaddr_prefix (int addr_family,
+ const char *text,
char **out_addr,
int *out_prefix);
@@ -167,6 +237,74 @@ gint _nm_utils_ascii_str_to_bool (const char *str,
/*****************************************************************************/
+extern char _nm_utils_to_string_buffer[2096];
+
+void nm_utils_to_string_buffer_init (char **buf, gsize *len);
+gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len);
+
+/*****************************************************************************/
+
+typedef struct {
+ unsigned flag;
+ const char *name;
+} NMUtilsFlags2StrDesc;
+
+#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, }
+
+#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \
+scope const char * \
+fcn_name (flags_type flags, char *buf, gsize len) \
+{ \
+ static const NMUtilsFlags2StrDesc descs[] = { \
+ __VA_ARGS__ \
+ }; \
+ G_STATIC_ASSERT (sizeof (flags_type) <= sizeof (unsigned)); \
+ return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); \
+};
+
+#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \
+ _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__)
+#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \
+ _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__)
+
+const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+ gsize n_descs,
+ unsigned flags,
+ char *buf,
+ gsize len);
+
+/*****************************************************************************/
+
+#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0
+#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0
+
+#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \
+scope const char * \
+fcn_name (lookup_type val, char *buf, gsize len) \
+{ \
+ nm_utils_to_string_buffer_init (&buf, &len); \
+ if (len) { \
+ const char *s = NULL; \
+ switch (val) { \
+ (void) 0, \
+ __VA_ARGS__ \
+ (void) 0; \
+ }; \
+ if (s) \
+ g_strlcpy (buf, s, len); \
+ else \
+ g_snprintf (buf, len, "(%"int_fmt")", val); \
+ } \
+ return buf; \
+}
+
+#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \
+ _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__)
+#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \
+ _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__)
+
+/*****************************************************************************/
+
#define _nm_g_slice_free_fcn_define(mem_size) \
static inline void \
_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \
@@ -178,6 +316,7 @@ _nm_g_slice_free_fcn_define (1)
_nm_g_slice_free_fcn_define (2)
_nm_g_slice_free_fcn_define (4)
_nm_g_slice_free_fcn_define (8)
+_nm_g_slice_free_fcn_define (12)
_nm_g_slice_free_fcn_define (16)
#define _nm_g_slice_free_fcn1(mem_size) \
@@ -192,6 +331,7 @@ _nm_g_slice_free_fcn_define (16)
case 2: _fcn = _nm_g_slice_free_fcn_2; break; \
case 4: _fcn = _nm_g_slice_free_fcn_4; break; \
case 8: _fcn = _nm_g_slice_free_fcn_8; break; \
+ case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
default: g_assert_not_reached (); _fcn = NULL; break; \
} \
@@ -267,4 +407,36 @@ char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flag
/*****************************************************************************/
+typedef struct {
+ const char *name;
+} NMUtilsNamedEntry;
+
+typedef struct {
+ union {
+ NMUtilsNamedEntry named_entry;
+ const char *name;
+ };
+ union {
+ const char *value_str;
+ gconstpointer value_ptr;
+ };
+} NMUtilsNamedValue;
+
+#define nm_utils_named_entry_cmp nm_strcmp_p
+#define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data
+
+/*****************************************************************************/
+
+#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000)
+#define NM_UTILS_NS_PER_MSEC ((gint64) 1000000)
+#define NM_UTILS_NS_TO_MSEC_CEIL(nsec) (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC)
+
+/*****************************************************************************/
+
+int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns);
+ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll);
+int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll);
+
+/*****************************************************************************/
+
#endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index f7a87aae..8d3ff10e 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1697,6 +1697,62 @@ nmtst_assert_setting_verifies (NMSetting *setting)
g_assert (success);
}
+#if defined(__NM_SIMPLE_CONNECTION_H__) && NM_CHECK_VERSION (1, 10, 0) && (!defined (NM_VERSION_MAX_ALLOWED) || NM_VERSION_MAX_ALLOWED >= NM_VERSION_1_10)
+static inline void
+_nmtst_assert_connection_has_settings (NMConnection *connection, gboolean has_at_least, gboolean has_at_most, ...)
+{
+ gs_unref_hashtable GHashTable *names = NULL;
+ gs_free NMSetting **settings = NULL;
+ va_list ap;
+ const char *name;
+ guint i, len;
+ gs_unref_ptrarray GPtrArray *names_arr = NULL;
+
+ g_assert (NM_IS_CONNECTION (connection));
+
+ names = g_hash_table_new (g_str_hash, g_str_equal);
+ names_arr = g_ptr_array_new ();
+
+ va_start (ap, has_at_most);
+ while ((name = va_arg (ap, const char *))) {
+ if (!nm_g_hash_table_add (names, (gpointer) name))
+ g_assert_not_reached ();
+ g_ptr_array_add (names_arr, (gpointer) name);
+ }
+ va_end (ap);
+
+ g_ptr_array_add (names_arr, NULL);
+
+ settings = nm_connection_get_settings (connection, &len);
+ for (i = 0; i < len; i++) {
+ if ( !g_hash_table_remove (names, nm_setting_get_name (settings[i]))
+ && has_at_most) {
+ g_error ("nmtst_assert_connection_has_settings(): has setting \"%s\" which is not expected",
+ nm_setting_get_name (settings[i]));
+ }
+ }
+ if ( g_hash_table_size (names) > 0
+ && has_at_least) {
+ gs_free char *expected_str = g_strjoinv (" ", (char **) names_arr->pdata);
+ gs_free const char **settings_names = NULL;
+ gs_free char *has_str = NULL;
+
+ settings_names = g_new0 (const char *, len + 1);
+ for (i = 0; i < len; i++)
+ settings_names[i] = nm_setting_get_name (settings[i]);
+ has_str = g_strjoinv (" ", (char **) settings_names);
+
+ g_error ("nmtst_assert_connection_has_settings(): the setting lacks %u expected settings (expected: [%s] vs. has: [%s])",
+ g_hash_table_size (names),
+ expected_str,
+ has_str);
+ }
+}
+#define nmtst_assert_connection_has_settings(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, TRUE, __VA_ARGS__, NULL)
+#define nmtst_assert_connection_has_settings_at_least(connection, ...) _nmtst_assert_connection_has_settings ((connection), TRUE, FALSE, __VA_ARGS__, NULL)
+#define nmtst_assert_connection_has_settings_at_most(connection, ...) _nmtst_assert_connection_has_settings ((connection), FALSE, TRUE, __VA_ARGS__, NULL)
+#endif
+
static inline void
nmtst_assert_setting_verify_fails (NMSetting *setting,
GQuark expect_error_domain,
diff --git a/src/applet-vpn-request.c b/src/applet-vpn-request.c
index 3395eb96..ccd98301 100644
--- a/src/applet-vpn-request.c
+++ b/src/applet-vpn-request.c
@@ -17,11 +17,13 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright 2004 - 2014 Red Hat, Inc.
+ * Copyright 2004 - 2017 Red Hat, Inc.
*/
#include "nm-default.h"
+#include "applet-vpn-request.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -30,36 +32,11 @@
#include <unistd.h>
#include <errno.h>
-#include <NetworkManager.h>
-
-#include "applet-vpn-request.h"
-
-#define APPLET_TYPE_VPN_REQUEST (applet_vpn_request_get_type ())
-#define APPLET_VPN_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APPLET_TYPE_VPN_REQUEST, AppletVpnRequest))
-#define APPLET_VPN_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APPLET_TYPE_VPN_REQUEST, AppletVpnRequestClass))
-#define APPLET_IS_VPN_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APPLET_TYPE_VPN_REQUEST))
-#define APPLET_IS_VPN_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APPLET_TYPE_VPN_REQUEST))
-#define APPLET_VPN_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APPLET_TYPE_VPN_REQUEST, AppletVpnRequestClass))
-
-typedef struct {
- GObject parent;
-} AppletVpnRequest;
-
-typedef struct {
- GObjectClass parent;
-} AppletVpnRequestClass;
-
-GType applet_vpn_request_get_type (void);
+#include "nm-utils/nm-compat.h"
-G_DEFINE_TYPE (AppletVpnRequest, applet_vpn_request, G_TYPE_OBJECT)
-
-#define APPLET_VPN_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
- APPLET_TYPE_VPN_REQUEST, \
- AppletVpnRequestPrivate))
+/*****************************************************************************/
typedef struct {
- gboolean disposed;
-
char *uuid;
char *id;
char *service_type;
@@ -73,24 +50,35 @@ typedef struct {
int num_newlines;
GIOChannel *channel;
guint channel_eventid;
-} AppletVpnRequestPrivate;
-
-/****************************************************************/
+} RequestData;
typedef struct {
SecretsRequest req;
- AppletVpnRequest *vpn;
+ RequestData *req_data;
} VpnSecretsInfo;
-static void
+/*****************************************************************************/
+
+static void request_data_free (RequestData *req_data);
+
+/*****************************************************************************/
+
+size_t
+applet_vpn_request_get_secrets_size (void)
+{
+ return sizeof (VpnSecretsInfo);
+}
+
+/*****************************************************************************/
+
+static void
child_finished_cb (GPid pid, gint status, gpointer user_data)
{
SecretsRequest *req = user_data;
VpnSecretsInfo *info = (VpnSecretsInfo *) req;
- AppletVpnRequest *self = info->vpn;
- AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
- GError *error = NULL;
- GVariant *settings = NULL;
+ RequestData *req_data = info->req_data;
+ gs_free_error GError *error = NULL;
+ gs_unref_variant GVariant *settings = NULL;
GVariantBuilder settings_builder, vpn_builder, secrets_builder;
if (status == 0) {
@@ -104,7 +92,7 @@ child_finished_cb (GPid pid, gint status, gpointer user_data)
* key:secret pairs with the key on one line and the associated secret
* on the next line.
*/
- for (iter = priv->lines; iter; iter = g_slist_next (iter)) {
+ for (iter = req_data->lines; iter; iter = g_slist_next (iter)) {
if (!iter->next)
break;
g_variant_builder_add (&secrets_builder, "{ss}", iter->data, iter->next->data);
@@ -113,7 +101,7 @@ child_finished_cb (GPid pid, gint status, gpointer user_data)
g_variant_builder_add (&vpn_builder, "{sv}",
NM_SETTING_VPN_SECRETS,
- g_variant_builder_end (&secrets_builder));
+ g_variant_builder_end (&secrets_builder));
g_variant_builder_add (&settings_builder, "{sa{sv}}",
NM_SETTING_VPN_SETTING_NAME,
&vpn_builder);
@@ -127,19 +115,15 @@ child_finished_cb (GPid pid, gint status, gpointer user_data)
/* Complete the secrets request */
applet_secrets_request_complete (req, settings, error);
applet_secrets_request_free (req);
-
- if (settings)
- g_variant_unref (settings);
- g_clear_error (&error);
}
-static gboolean
+/*****************************************************************************/
+
+static gboolean
child_stdout_data_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
{
VpnSecretsInfo *info = user_data;
- AppletVpnRequest *self = info->vpn;
- AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
- const char *buf = "QUIT\n\n";
+ RequestData *req_data = info->req_data;
char *str;
int len;
@@ -150,252 +134,302 @@ child_stdout_data_cb (GIOChannel *source, GIOCondition condition, gpointer user_
len = strlen (str);
if (len == 1 && str[0] == '\n') {
/* on second line with a newline newline */
- if (++priv->num_newlines == 2) {
+ if (++req_data->num_newlines == 2) {
+ const char *buf = "QUIT\n\n";
+
/* terminate the child */
- if (write (priv->child_stdin, buf, strlen (buf)) == -1)
+ if (write (req_data->child_stdin, buf, strlen (buf)) == -1)
return TRUE;
}
} else if (len > 0) {
/* remove terminating newline */
str[len - 1] = '\0';
- priv->lines = g_slist_append (priv->lines, str);
+ req_data->lines = g_slist_append (req_data->lines, str);
}
}
return TRUE;
}
+/*****************************************************************************/
+
+static void
+_str_append (GString *str,
+ const char *tag,
+ const char *val)
+{
+ const char *s;
+ gsize i;
+
+ nm_assert (str);
+ nm_assert (tag && tag[0]);
+ nm_assert (val);
+
+ g_string_append (str, tag);
+ g_string_append_c (str, '=');
+
+ s = strchr (val, '\n');
+ if (s) {
+ gs_free char *val2 = g_strdup (val);
+
+ for (i = 0; val2[i]; i++) {
+ if (val2[i] == '\n')
+ val2[i] = ' ';
+ }
+ g_string_append (str, val2);
+ } else
+ g_string_append (str, val);
+ g_string_append_c (str, '\n');
+}
+
static char *
-find_auth_dialog_binary (const char *service,
- gboolean *out_hints_supported,
- GError **error)
+connection_to_data (NMConnection *connection,
+ gsize *out_length,
+ GError **error)
{
- const char *auth_dialog;
- gs_unref_object NMVpnPluginInfo *plugin = NULL;
+ NMSettingVpn *s_vpn;
+ GString *buf;
+ const char **keys;
+ guint i, len;
- plugin = nm_vpn_plugin_info_new_search_file (NULL, service);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
- auth_dialog = plugin ? nm_vpn_plugin_info_get_auth_dialog (plugin) : NULL;
- if (!auth_dialog) {
- g_set_error (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_FAILED,
- "Could not find the authentication dialog for VPN connection type '%s'",
- service);
+ s_vpn = nm_connection_get_setting_vpn (connection);
+ if (!s_vpn) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ _("Connection had no VPN setting"));
return NULL;
}
- *out_hints_supported = nm_vpn_plugin_info_supports_hints (plugin);
- return g_strdup (auth_dialog);
-}
+ buf = g_string_new_len (NULL, 100);
-static void
-free_vpn_secrets_info (SecretsRequest *req)
-{
- VpnSecretsInfo *info = (VpnSecretsInfo *) req;
+ keys = nm_setting_vpn_get_data_keys (s_vpn, &len);
+ for (i = 0; i < len; i++) {
+ _str_append (buf, "DATA_KEY", keys[i]);
+ _str_append (buf, "DATA_VAL", nm_setting_vpn_get_data_item (s_vpn, keys[i]));
+ }
+ nm_clear_g_free (&keys);
- if (info->vpn)
- g_object_unref (info->vpn);
-}
+ keys = nm_setting_vpn_get_secret_keys (s_vpn, &len);
+ for (i = 0; i < len; i++) {
+ _str_append (buf, "SECRET_KEY", keys[i]);
+ _str_append (buf, "SECRET_VAL", nm_setting_vpn_get_secret (s_vpn, keys[i]));
+ }
+ nm_clear_g_free (&keys);
-size_t
-applet_vpn_request_get_secrets_size (void)
-{
- return sizeof (VpnSecretsInfo);
+ g_string_append (buf, "DONE\n\n");
+ NM_SET_OUT (out_length, buf->len);
+ return g_string_free (buf, FALSE);
}
-typedef struct {
- int fd;
- gboolean secret;
- GError **error;
-} WriteItemInfo;
-
-static const char *data_key_tag = "DATA_KEY=";
-static const char *data_val_tag = "DATA_VAL=";
-static const char *secret_key_tag = "SECRET_KEY=";
-static const char *secret_val_tag = "SECRET_VAL=";
+/*****************************************************************************/
static gboolean
-write_item (int fd, const char *item, GError **error)
+connection_to_fd (NMConnection *connection,
+ int fd,
+ GError **error)
{
- size_t item_len = strlen (item);
+ gs_free char *data = NULL;
+ gsize data_len;
+ gssize w;
+ int errsv;
- errno = 0;
- if (write (fd, item, item_len) != item_len) {
+ data = connection_to_data (connection, &data_len, error);
+ if (!data)
+ return FALSE;
+
+again:
+ w = write (fd, data, data_len);
+ if (w < 0) {
+ errsv = errno;
+ if (errsv == EINTR)
+ goto again;
g_set_error (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_FAILED,
- "Failed to write connection to VPN UI: errno %d", errno);
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ _("Failed to write connection to VPN UI: %s (%d)"), g_strerror (errsv), errsv);
return FALSE;
}
+
+ if ((gsize) w != data_len) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ _("Failed to write connection to VPN UI: incomplete write"));
+ return FALSE;
+ }
+
return TRUE;
}
+/*****************************************************************************/
+
static void
-write_one_key_val (const char *key, const char *value, gpointer user_data)
+vpn_child_setup (gpointer user_data)
{
- WriteItemInfo *info = user_data;
- const char *tag;
-
- if (info->error && *(info->error))
- return;
-
- /* Write the key name */
- tag = info->secret ? secret_key_tag : data_key_tag;
- if (!write_item (info->fd, tag, info->error))
- return;
- if (!write_item (info->fd, key, info->error))
- return;
- if (!write_item (info->fd, "\n", info->error))
- return;
-
- /* Write the key value */
- tag = info->secret ? secret_val_tag : data_val_tag;
- if (!write_item (info->fd, tag, info->error))
- return;
- if (!write_item (info->fd, value ? value : "", info->error))
- return;
- if (!write_item (info->fd, "\n\n", info->error))
- return;
+ /* We are in the child process at this point */
+ pid_t pid = getpid ();
+ setpgid (pid, pid);
}
static gboolean
-write_connection_to_child (int fd, NMConnection *connection, GError **error)
+auth_dialog_spawn (const char *con_id,
+ const char *con_uuid,
+ const char *const*hints,
+ const char *auth_dialog,
+ const char *service_type,
+ gboolean supports_hints,
+ guint32 flags,
+ GPid *out_pid,
+ int *out_stdin,
+ int *out_stdout,
+ GError **error)
{
- NMSettingVpn *s_vpn;
- WriteItemInfo info = { .fd = fd, .secret = FALSE, .error = error };
-
- s_vpn = nm_connection_get_setting_vpn (connection);
- if (!s_vpn) {
- g_set_error_literal (error,
- NM_SECRET_AGENT_ERROR,
- NM_SECRET_AGENT_ERROR_FAILED,
- "Connection had no VPN setting");
- return FALSE;
+ gsize hints_len;
+ gsize i, j;
+ gs_free const char **argv = NULL;
+ gs_free const char **envp = NULL;
+ gsize environ_len;
+
+ g_return_val_if_fail (con_id, FALSE);
+ g_return_val_if_fail (con_uuid, FALSE);
+ g_return_val_if_fail (auth_dialog, FALSE);
+ g_return_val_if_fail (service_type, FALSE);
+ g_return_val_if_fail (out_pid, FALSE);
+ g_return_val_if_fail (out_stdin, FALSE);
+ g_return_val_if_fail (out_stdout, FALSE);
+
+ hints_len = NM_PTRARRAY_LEN (hints);
+ argv = g_new (const char *, 10 + (2 * hints_len));
+ i = 0;
+ argv[i++] = auth_dialog;
+ argv[i++] = "-u";
+ argv[i++] = con_uuid;
+ argv[i++] = "-n";
+ argv[i++] = con_id;
+ argv[i++] = "-s";
+ argv[i++] = service_type;
+ if (flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)
+ argv[i++] = "-i";
+ if (flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW)
+ argv[i++] = "-r";
+ for (j = 0; supports_hints && (j < hints_len); j++) {
+ argv[i++] = "-t";
+ argv[i++] = hints[j];
}
-
- nm_setting_vpn_foreach_data_item (s_vpn, write_one_key_val, &info);
- if (error && *error)
- return FALSE;
-
- info.secret = TRUE;
- nm_setting_vpn_foreach_secret (s_vpn, write_one_key_val, &info);
- if (error && *error)
- return FALSE;
-
- if (!write_item (fd, "DONE\n\n", error))
+ nm_assert (i <= 10 + (2 * hints_len));
+ argv[i++] = NULL;
+
+ environ_len = NM_PTRARRAY_LEN (environ);
+ envp = g_new (const char *, environ_len + 1);
+ memcpy (envp, environ, sizeof (const char *) * environ_len);
+ for (i = 0, j = 0; i < environ_len; i++) {
+ const char *e = environ[i];
+
+ if (g_str_has_prefix (e, "G_MESSAGES_DEBUG=")) {
+ /* skip this environment variable. We interact with the auth-dialog via stdout.
+ * G_MESSAGES_DEBUG may enable additional debugging messages from GTK. */
+ continue;
+ }
+ envp[j++] = e;
+ }
+ envp[j] = NULL;
+
+ if (!g_spawn_async_with_pipes (NULL,
+ (char **) argv,
+ (char **) envp,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ vpn_child_setup,
+ NULL,
+ out_pid,
+ out_stdin,
+ out_stdout,
+ NULL,
+ error))
return FALSE;
return TRUE;
}
+/*****************************************************************************/
+
static void
-vpn_child_setup (gpointer user_data G_GNUC_UNUSED)
+free_vpn_secrets_info (SecretsRequest *req)
{
- /* We are in the child process at this point */
- pid_t pid = getpid ();
- setpgid (pid, pid);
+ request_data_free (((VpnSecretsInfo *) req)->req_data);
}
gboolean
applet_vpn_request_get_secrets (SecretsRequest *req, GError **error)
{
VpnSecretsInfo *info = (VpnSecretsInfo *) req;
- AppletVpnRequestPrivate *priv;
+ RequestData *req_data;
NMSettingConnection *s_con;
NMSettingVpn *s_vpn;
const char *connection_type;
const char *service_type;
- char *bin_path;
- char **argv = NULL;
- gboolean success = FALSE;
- guint i = 0, u, hints_len;
- gboolean supports_hints = FALSE;
+ const char *auth_dialog;
+ gs_unref_object NMVpnPluginInfo *plugin = NULL;
applet_secrets_request_set_free_func (req, free_vpn_secrets_info);
s_con = nm_connection_get_setting_connection (req->connection);
- g_return_val_if_fail (s_con != NULL, FALSE);
+ s_vpn = nm_connection_get_setting_vpn (req->connection);
connection_type = nm_setting_connection_get_connection_type (s_con);
- g_return_val_if_fail (connection_type != NULL, FALSE);
- g_return_val_if_fail (strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME) == 0, FALSE);
-
- s_vpn = nm_connection_get_setting_vpn (req->connection);
- g_return_val_if_fail (s_vpn != NULL, FALSE);
+ g_return_val_if_fail (nm_streq0 (connection_type, NM_SETTING_VPN_SETTING_NAME), FALSE);
service_type = nm_setting_vpn_get_service_type (s_vpn);
- g_return_val_if_fail (service_type != NULL, FALSE);
+ g_return_val_if_fail (service_type, FALSE);
- /* find the auth-dialog binary */
- bin_path = find_auth_dialog_binary (service_type, &supports_hints, error);
- if (!bin_path)
+ plugin = nm_vpn_plugin_info_new_search_file (NULL, service_type);
+ auth_dialog = plugin ? nm_vpn_plugin_info_get_auth_dialog (plugin) : NULL;
+ if (!auth_dialog) {
+ g_set_error (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_FAILED,
+ "Could not find the authentication dialog for VPN connection type '%s'",
+ service_type);
return FALSE;
+ }
- info->vpn = (AppletVpnRequest *) g_object_new (APPLET_TYPE_VPN_REQUEST, NULL);
- if (!info->vpn) {
+ info->req_data = g_slice_new0 (RequestData);
+ if (!info->req_data) {
g_set_error_literal (error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"Could not create VPN secrets request object");
- goto out;
- }
-
- priv = APPLET_VPN_REQUEST_GET_PRIVATE (info->vpn);
-
- hints_len = g_strv_length (req->hints);
- argv = g_new0 (char *, 10 + (2 * hints_len));
- argv[i++] = bin_path;
- argv[i++] = "-u";
- argv[i++] = (char *) nm_setting_connection_get_uuid (s_con);
- argv[i++] = "-n";
- argv[i++] = (char *) nm_setting_connection_get_id (s_con);
- argv[i++] = "-s";
- argv[i++] = (char *) service_type;
- if (req->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)
- argv[i++] = "-i";
- if (req->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW)
- argv[i++] = "-r";
-
- /* add hints */
- for (u = 0; supports_hints && (u < hints_len); u++) {
- argv[i++] = "-t";
- argv[i++] = req->hints[u];
+ return FALSE;
}
-
- if (!g_spawn_async_with_pipes (NULL, /* working_directory */
- argv, /* argv */
- NULL, /* envp */
- G_SPAWN_DO_NOT_REAP_CHILD, /* flags */
- vpn_child_setup, /* child_setup */
- NULL, /* user_data */
- &priv->pid, /* child_pid */
- &priv->child_stdin, /* standard_input */
- &priv->child_stdout, /* standard_output */
- NULL, /* standard_error */
- error)) /* error */
- goto out;
+ req_data = info->req_data;
+
+ if (!auth_dialog_spawn (nm_setting_connection_get_id (s_con),
+ nm_setting_connection_get_uuid (s_con),
+ (const char *const*) req->hints,
+ auth_dialog,
+ service_type,
+ nm_vpn_plugin_info_supports_hints (plugin),
+ req->flags,
+ &req_data->pid,
+ &req_data->child_stdin,
+ &req_data->child_stdout,
+ error))
+ return FALSE;
/* catch when child is reaped */
- priv->watch_id = g_child_watch_add (priv->pid, child_finished_cb, info);
+ req_data->watch_id = g_child_watch_add (req_data->pid, child_finished_cb, info);
/* listen to what child has to say */
- priv->channel = g_io_channel_unix_new (priv->child_stdout);
- priv->channel_eventid = g_io_add_watch (priv->channel, G_IO_IN, child_stdout_data_cb, info);
- g_io_channel_set_encoding (priv->channel, NULL, NULL);
+ req_data->channel = g_io_channel_unix_new (req_data->child_stdout);
+ req_data->channel_eventid = g_io_add_watch (req_data->channel, G_IO_IN, child_stdout_data_cb, info);
+ g_io_channel_set_encoding (req_data->channel, NULL, NULL);
/* Dump parts of the connection to the child */
- success = write_connection_to_child (priv->child_stdin, req->connection, error);
-
-out:
- g_free (argv);
- g_free (bin_path);
- return success;
+ return connection_to_fd (req->connection, req_data->child_stdin, error);
}
-static void
-applet_vpn_request_init (AppletVpnRequest *self)
-{
-}
+/*****************************************************************************/
static gboolean
ensure_killed (gpointer data)
@@ -410,54 +444,34 @@ ensure_killed (gpointer data)
}
static void
-dispose (GObject *object)
+request_data_free (RequestData *req_data)
{
- AppletVpnRequest *self = APPLET_VPN_REQUEST (object);
- AppletVpnRequestPrivate *priv = APPLET_VPN_REQUEST_GET_PRIVATE (self);
-
- if (priv->disposed)
- goto done;
-
- priv->disposed = TRUE;
+ if (!req_data)
+ return;
- g_free (priv->uuid);
- g_free (priv->id);
- g_free (priv->service_type);
+ g_free (req_data->uuid);
+ g_free (req_data->id);
+ g_free (req_data->service_type);
- if (priv->watch_id)
- g_source_remove (priv->watch_id);
+ nm_clear_g_source (&req_data->watch_id);
- if (priv->channel_eventid)
- g_source_remove (priv->channel_eventid);
- if (priv->channel)
- g_io_channel_unref (priv->channel);
+ nm_clear_g_source (&req_data->channel_eventid);
+ if (req_data->channel)
+ g_io_channel_unref (req_data->channel);
- if (priv->pid) {
- g_spawn_close_pid (priv->pid);
- if (kill (priv->pid, SIGTERM) == 0)
- g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+ if (req_data->pid) {
+ g_spawn_close_pid (req_data->pid);
+ if (kill (req_data->pid, SIGTERM) == 0)
+ g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (req_data->pid));
else {
- kill (priv->pid, SIGKILL);
+ kill (req_data->pid, SIGKILL);
/* ensure the child is reaped */
- waitpid (priv->pid, NULL, 0);
+ waitpid (req_data->pid, NULL, 0);
}
}
- g_slist_foreach (priv->lines, (GFunc) g_free, NULL);
- g_slist_free (priv->lines);
+ g_slist_foreach (req_data->lines, (GFunc) g_free, NULL);
+ g_slist_free (req_data->lines);
-done:
- G_OBJECT_CLASS (applet_vpn_request_parent_class)->dispose (object);
+ g_slice_free (RequestData, req_data);
}
-
-static void
-applet_vpn_request_class_init (AppletVpnRequestClass *req_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (req_class);
-
- g_type_class_add_private (req_class, sizeof (AppletVpnRequestPrivate));
-
- /* virtual methods */
- object_class->dispose = dispose;
-}
-