diff options
author | Thomas Haller <thaller@redhat.com> | 2017-11-23 15:01:26 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2017-11-23 15:01:26 +0100 |
commit | 4e214ce0afd58abc90b94d03ee190676fb48815c (patch) | |
tree | 0595e52494d12f6f7e68778fd88a7e2af1b17727 | |
parent | 00c808c41084b23dcfcab839c6c1ea7d3d6c3796 (diff) | |
parent | e93ca7fc129ec0f29f5313a3aa12839914df8fa2 (diff) | |
download | network-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.am | 3 | ||||
-rw-r--r-- | m4/compiler_options.m4 | 8 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | shared/nm-utils/nm-compat.c | 88 | ||||
-rw-r--r-- | shared/nm-utils/nm-compat.h | 53 | ||||
-rw-r--r-- | shared/nm-utils/nm-glib.h | 29 | ||||
-rw-r--r-- | shared/nm-utils/nm-macros-internal.h | 388 | ||||
-rw-r--r-- | shared/nm-utils/nm-shared-utils.c | 468 | ||||
-rw-r--r-- | shared/nm-utils/nm-shared-utils.h | 180 | ||||
-rw-r--r-- | shared/nm-utils/nm-test-utils.h | 56 | ||||
-rw-r--r-- | src/applet-vpn-request.c | 516 |
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; -} - |