all: merge branch 'th/c-list'

https://bugzilla.gnome.org/show_bug.cgi?id=782286
This commit is contained in:
Thomas Haller 2017-05-11 18:52:31 +02:00
commit 3063a5eda3
9 changed files with 783 additions and 364 deletions

View file

@ -4403,6 +4403,7 @@ EXTRA_DIST += \
shared/nm-dispatcher-api.h \
shared/nm-test-libnm-utils.h \
shared/nm-test-utils-impl.c \
shared/nm-utils/c-list.h \
shared/nm-utils/gsystem-local-alloc.h \
shared/nm-utils/nm-glib.h \
shared/nm-utils/nm-macros-internal.h \

View file

@ -34,6 +34,7 @@
#include "nm-active-connection.h"
#include "nm-vpn-connection.h"
#include "nm-dbus-helpers.h"
#include "nm-utils/c-list.h"
#include "introspection/org.freedesktop.NetworkManager.h"
@ -71,7 +72,7 @@ typedef struct {
/* Activations waiting for their NMActiveConnection
* to appear and then their callback to be called.
*/
GSList *pending_activations;
CList pending_activations;
gboolean networking_enabled;
gboolean wireless_enabled;
@ -128,6 +129,8 @@ nm_manager_init (NMManager *manager)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
c_list_init (&priv->pending_activations);
priv->state = NM_STATE_UNKNOWN;
priv->connectivity = NM_CONNECTIVITY_UNKNOWN;
@ -735,6 +738,7 @@ nm_manager_get_activating_connection (NMManager *manager)
}
typedef struct {
CList lst;
NMManager *manager;
GSimpleAsyncResult *simple;
GCancellable *cancellable;
@ -748,15 +752,13 @@ activate_info_complete (ActivateInfo *info,
NMActiveConnection *active,
GError *error)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager);
if (active)
g_simple_async_result_set_op_res_gpointer (info->simple, g_object_ref (active), g_object_unref);
else
g_simple_async_result_set_from_error (info->simple, error);
g_simple_async_result_complete (info->simple);
priv->pending_activations = g_slist_remove (priv->pending_activations, info);
c_list_unlink (&info->lst);
g_free (info->active_path);
g_free (info->new_connection_path);
@ -790,7 +792,7 @@ static void
recheck_pending_activations (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GSList *iter, *next;
CList *iter, *safe;
NMActiveConnection *candidate;
const GPtrArray *devices;
NMDevice *device;
@ -804,12 +806,10 @@ recheck_pending_activations (NMManager *self)
* device have both updated their properties to point to each other, and
* call the pending connection's callback.
*/
for (iter = priv->pending_activations; iter; iter = next) {
ActivateInfo *info = iter->data;
c_list_for_each_safe (iter, safe, &priv->pending_activations) {
ActivateInfo *info = c_list_entry (iter, ActivateInfo, lst);
gs_unref_object GDBusObject *dbus_obj = NULL;
next = g_slist_next (iter);
if (!info->active_path)
continue;
@ -901,14 +901,15 @@ nm_manager_activate_connection_async (NMManager *manager,
if (connection)
g_return_if_fail (NM_IS_CONNECTION (connection));
priv = NM_MANAGER_GET_PRIVATE (manager);
info = g_slice_new0 (ActivateInfo);
info->manager = manager;
info->simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_activate_connection_async);
info->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
priv = NM_MANAGER_GET_PRIVATE (manager);
priv->pending_activations = g_slist_prepend (priv->pending_activations, info);
c_list_link_tail (&priv->pending_activations, &info->lst);
nmdbus_manager_call_activate_connection (priv->proxy,
connection ? nm_connection_get_path (connection) : "/",
@ -977,14 +978,15 @@ nm_manager_add_and_activate_connection_async (NMManager *manager,
if (partial)
g_return_if_fail (NM_IS_CONNECTION (partial));
priv = NM_MANAGER_GET_PRIVATE (manager);
info = g_slice_new0 (ActivateInfo);
info->manager = manager;
info->simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_add_and_activate_connection_async);
info->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
priv = NM_MANAGER_GET_PRIVATE (manager);
priv->pending_activations = g_slist_prepend (priv->pending_activations, info);
c_list_link_tail (&priv->pending_activations, &info->lst);
if (partial)
dict = nm_connection_to_dbus (partial, NM_CONNECTION_SERIALIZE_ALL);
@ -1294,7 +1296,7 @@ dispose (GObject *object)
/* Each activation should hold a ref on @manager, so if we're being disposed,
* there shouldn't be any pending.
*/
g_warn_if_fail (priv->pending_activations == NULL);
g_warn_if_fail (c_list_is_empty (&priv->pending_activations));
g_hash_table_destroy (priv->permissions);
priv->permissions = NULL;

View file

@ -33,6 +33,7 @@
#include "nm-dbus-helpers.h"
#include "nm-client.h"
#include "nm-core-internal.h"
#include "nm-utils/c-list.h"
static gboolean debug = FALSE;
#define dbgmsg(f,...) if (G_UNLIKELY (debug)) { g_message (f, ## __VA_ARGS__ ); }
@ -77,14 +78,12 @@ typedef struct {
GSList *waiters; /* if async init did not finish, users of this object need
* to defer their notifications by adding themselves here. */
GSList *notify_items;
guint32 notify_id;
CList notify_items;
guint notify_id;
GSList *reload_results;
guint reload_remaining;
GError *reload_error;
GSList *pending; /* ordered list of pending property updates. */
CList pending; /* ordered list of pending property updates. */
GPtrArray *proxies;
} NMObjectPrivate;
@ -152,6 +151,7 @@ typedef enum {
} NotifySignalPending;
typedef struct {
CList lst;
const char *property;
const char *signal_prefix;
NotifySignalPending pending;
@ -161,6 +161,7 @@ typedef struct {
static void
notify_item_free (NotifyItem *item)
{
c_list_unlink (&item->lst);
g_clear_object (&item->changed);
g_slice_free (NotifyItem, item);
}
@ -171,7 +172,8 @@ deferred_notify_cb (gpointer data)
NMObject *object = NM_OBJECT (data);
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
NMObjectClass *object_class = NM_OBJECT_GET_CLASS (object);
GSList *props, *iter;
CList props;
CList *iter, *safe;
priv->notify_id = 0;
@ -184,16 +186,16 @@ deferred_notify_cb (gpointer data)
* during the g_object_notify() call separately from the property
* list we're iterating.
*/
props = g_slist_reverse (priv->notify_items);
priv->notify_items = NULL;
c_list_link_after (&priv->notify_items, &props);
c_list_unlink_init (&priv->notify_items);
g_object_ref (object);
/* Emit added/removed signals first since some of our internal objects
* use the added/removed signals for new object processing.
*/
for (iter = props; iter; iter = g_slist_next (iter)) {
NotifyItem *item = iter->data;
c_list_for_each (iter, &props) {
NotifyItem *item = c_list_entry (iter, NotifyItem, lst);
char buf[50];
gint ret = 0;
@ -219,8 +221,8 @@ deferred_notify_cb (gpointer data)
}
/* Emit property change notifications second */
for (iter = props; iter; iter = g_slist_next (iter)) {
NotifyItem *item = iter->data;
c_list_for_each (iter, &props) {
NotifyItem *item = c_list_entry (iter, NotifyItem, lst);
if (item->property)
g_object_notify (G_OBJECT (object), item->property);
@ -228,7 +230,9 @@ deferred_notify_cb (gpointer data)
g_object_unref (object);
g_slist_free_full (props, (GDestroyNotify) notify_item_free);
c_list_for_each_safe (iter, safe, &props)
notify_item_free (c_list_entry (iter, NotifyItem, lst));
return G_SOURCE_REMOVE;
}
@ -250,7 +254,7 @@ _nm_object_queue_notify_full (NMObject *object,
{
NMObjectPrivate *priv;
NotifyItem *item;
GSList *iter;
CList *iter;
g_return_if_fail (NM_IS_OBJECT (object));
g_return_if_fail (!signal_prefix != !property);
@ -261,8 +265,8 @@ _nm_object_queue_notify_full (NMObject *object,
property = g_intern_string (property);
signal_prefix = g_intern_string (signal_prefix);
for (iter = priv->notify_items; iter; iter = g_slist_next (iter)) {
item = iter->data;
c_list_for_each (iter, &priv->notify_items) {
item = c_list_entry (iter, NotifyItem, lst);
if (property && (property == item->property))
return;
@ -314,7 +318,7 @@ _nm_object_queue_notify_full (NMObject *object,
item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED;
item->changed = changed ? g_object_ref (changed) : NULL;
}
priv->notify_items = g_slist_prepend (priv->notify_items, item);
c_list_link_tail (&priv->notify_items, &item->lst);
}
void
@ -324,6 +328,7 @@ _nm_object_queue_notify (NMObject *object, const char *property)
}
typedef struct {
CList lst_pending;
NMObject *self;
PropertyInfo *pi;
@ -339,6 +344,7 @@ odata_free (gpointer data)
{
ObjectCreatedData *odata = data;
c_list_unlink (&odata->lst_pending);
g_object_unref (odata->self);
g_free (odata->objects);
g_slice_free (ObjectCreatedData, odata);
@ -445,9 +451,10 @@ object_property_maybe_complete (NMObject *self)
/* The odata may hold the last reference. */
_nm_unused gs_unref_object NMObject *self_keep_alive = g_object_ref (self);
int i;
CList *iter, *safe;
while (priv->pending) {
ObjectCreatedData *odata = priv->pending->data;
c_list_for_each_safe (iter, safe, &priv->pending) {
ObjectCreatedData *odata = c_list_entry (iter, ObjectCreatedData, lst_pending);
PropertyInfo *pi = odata->pi;
gboolean different = TRUE;
@ -505,16 +512,16 @@ object_property_maybe_complete (NMObject *self)
/* Emit added & removed */
for (i = 0; i < removed->len; i++) {
queue_added_removed_signal (self,
pi->signal_prefix,
g_ptr_array_index (removed, i),
FALSE);
pi->signal_prefix,
g_ptr_array_index (removed, i),
FALSE);
}
for (i = 0; i < added->len; i++) {
queue_added_removed_signal (self,
pi->signal_prefix,
g_ptr_array_index (added, i),
TRUE);
pi->signal_prefix,
g_ptr_array_index (added, i),
TRUE);
}
different = removed->len || added->len;
@ -547,7 +554,6 @@ object_property_maybe_complete (NMObject *self)
if (--priv->reload_remaining == 0)
reload_complete (self, TRUE);
priv->pending = g_slist_remove (priv->pending, odata);
odata_free (odata);
}
}
@ -588,7 +594,8 @@ handle_object_property (NMObject *self, const char *property_name, GVariant *val
odata->array = FALSE;
odata->property_name = property_name;
priv->pending = g_slist_append (priv->pending, odata);
c_list_link_tail (&priv->pending, &odata->lst_pending);
priv->reload_remaining++;
path = g_variant_get_string (value, NULL);
@ -644,7 +651,8 @@ handle_object_array_property (NMObject *self, const char *property_name, GVarian
odata->array = TRUE;
odata->property_name = property_name;
priv->pending = g_slist_append (priv->pending, odata);
c_list_link_tail (&priv->pending, &odata->lst_pending);
priv->reload_remaining++;
if (npaths == 0) {
@ -944,7 +952,7 @@ _nm_object_register_properties (NMObject *object,
proxy = _nm_object_get_proxy (object, interface);
g_signal_connect (proxy, "g-properties-changed",
G_CALLBACK (properties_changed), object);
G_CALLBACK (properties_changed), object);
g_ptr_array_add (priv->proxies, proxy);
instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@ -1004,34 +1012,12 @@ static void
reload_complete (NMObject *object, gboolean emit_now)
{
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
GSimpleAsyncResult *simple;
GSList *results, *iter;
GError *error;
if (emit_now) {
nm_clear_g_source (&priv->notify_id);
deferred_notify_cb (object);
} else
_nm_object_defer_notify (object);
results = priv->reload_results;
priv->reload_results = NULL;
error = priv->reload_error;
priv->reload_error = NULL;
for (iter = results; iter; iter = iter->next) {
simple = iter->data;
if (error)
g_simple_async_result_set_from_error (simple, error);
else
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
}
g_slist_free (results);
g_clear_error (&error);
}
GDBusObjectManager *
@ -1194,7 +1180,11 @@ nm_object_async_initable_iface_init (GAsyncInitableIface *iface)
static void
nm_object_init (NMObject *object)
{
NM_OBJECT_GET_PRIVATE (object)->proxies = g_ptr_array_new ();
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
c_list_init (&priv->notify_items);
c_list_init (&priv->pending);
priv->proxies = g_ptr_array_new ();
}
static void
@ -1249,12 +1239,13 @@ static void
dispose (GObject *object)
{
NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
CList *iter, *safe;
guint i;
nm_clear_g_source (&priv->notify_id);
g_slist_free_full (priv->notify_items, (GDestroyNotify) notify_item_free);
priv->notify_items = NULL;
c_list_for_each_safe (iter, safe, &priv->notify_items)
notify_item_free (c_list_entry (iter, NotifyItem, lst));
g_slist_free_full (priv->waiters, odata_free);

436
shared/nm-utils/c-list.h Normal file
View file

@ -0,0 +1,436 @@
#pragma once
/*
* Circular Double Linked List Implementation in Standard ISO-C11
*
* This implements a generic circular double linked list. List entries must
* embed the CList object, which provides pointers to the next and previous
* element. Insertion and removal can be done in O(1) due to the double links.
* Furthermore, the list is circular, thus allows access to front/tail in O(1)
* as well, even if you only have a single head pointer (which is not how the
* list is usually operated, though).
*
* Note that you are free to use the list implementation without a head
* pointer. However, usual operation uses a single CList object as head, which
* is itself linked in the list and as such must be identified as list head.
* This allows very simply list operations and avoids a lot of special cases.
* Most importantly, you can unlink entries without requiring a head pointer.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
typedef struct CList CList;
/**
* struct CList - Entry of a circular double linked list
* @next: next entry
* @prev: previous entry
*
* Each entry in a list must embed a CList object. This object contains
* pointers to its next and previous elements, which can be freely accessed by
* the API user at any time. Note that the list is circular, and the list head
* is linked in the list as well.
*
* The list head must be initialized via C_LIST_INIT before use. There is no
* reason to initialize entry objects before linking them. However, if you need
* a boolean state that tells you whether the entry is linked or not, you should
* initialize the entry via C_LIST_INIT as well.
*/
struct CList {
CList *next;
CList *prev;
};
#define C_LIST_INIT(_var) { .next = &(_var), .prev = &(_var) }
/**
* c_list_init() - initialize list entry
* @what: list entry to initialize
*/
static inline void c_list_init(CList *what) {
*what = (CList)C_LIST_INIT(*what);
}
/**
* c_list_entry() - get parent container of list entry
* @_what: list entry, or NULL
* @_t: type of parent container
* @_m: member name of list entry in @_t
*
* If the list entry @_what is embedded into a surrounding structure, this will
* turn the list entry pointer @_what into a pointer to the parent container
* (using offsetof(3), or sometimes called container_of(3)).
*
* If @_what is NULL, this will also return NULL.
*
* Return: Pointer to parent container, or NULL.
*/
#define c_list_entry(_what, _t, _m) \
((_t *)(void *)(((unsigned long)(void *)(_what) ?: \
offsetof(_t, _m)) - offsetof(_t, _m)))
/**
* c_list_is_linked() - check whether a entry is linked
* @what: entry to check, or NULL
*
* Return: True if @what is linked in a list, false if not.
*/
static inline _Bool c_list_is_linked(const CList *what) {
return what && what->next != what;
}
/**
* c_list_is_empty() - check whether a list is empty
* @list: list to check, or NULL
*
* Return: True if @list is empty, false if not.
*/
static inline _Bool c_list_is_empty(const CList *list) {
return !list || !c_list_is_linked(list);
}
/**
* c_list_link_before() - link entry into list
* @where: linked list entry used as anchor
* @what: entry to link
*
* This links @what directly in front of @where. @where can either be a list
* head or any entry in the list.
*
* If @where points to the list head, this effectively links @what as new tail
* element. Hence, the macro c_list_link_tail() is an alias to this.
*
* @what is not inspected prior to being linked. Hence, it better not be linked
* into another list, or the other list will be corrupted.
*/
static inline void c_list_link_before(CList *where, CList *what) {
CList *prev = where->prev, *next = where;
next->prev = what;
what->next = next;
what->prev = prev;
prev->next = what;
}
#define c_list_link_tail(_list, _what) c_list_link_before((_list), (_what))
/**
* c_list_link_after() - link entry into list
* @where: linked list entry used as anchor
* @what: entry to link
*
* This links @what directly after @where. @where can either be a list head or
* any entry in the list.
*
* If @where points to the list head, this effectively links @what as new front
* element. Hence, the macro c_list_link_front() is an alias to this.
*
* @what is not inspected prior to being linked. Hence, it better not be linked
* into another list, or the other list will be corrupted.
*/
static inline void c_list_link_after(CList *where, CList *what) {
CList *prev = where, *next = where->next;
next->prev = what;
what->next = next;
what->prev = prev;
prev->next = what;
}
#define c_list_link_front(_list, _what) c_list_link_after((_list), (_what))
/**
* c_list_unlink() - unlink element from list
* @what: element to unlink
*
* This unlinks @what. If @what was initialized via C_LIST_INIT(), it has no
* effect. If @what was never linked, nor initialized, behavior is undefined.
*
* Note that this does not modify @what. It just modifies the previous and next
* elements in the list to no longer reference @what. If you want to make sure
* @what is re-initialized after removal, use c_list_unlink_init().
*/
static inline void c_list_unlink(CList *what) {
CList *prev = what->prev, *next = what->next;
next->prev = prev;
prev->next = next;
}
/**
* c_list_unlink_init() - unlink element from list and re-initialize
* @what: element to unlink
*
* This is like c_list_unlink() but re-initializes @what after removal.
*/
static inline void c_list_unlink_init(CList *what) {
/* condition is not needed, but avoids STOREs in fast-path */
if (c_list_is_linked(what)) {
c_list_unlink(what);
*what = (CList)C_LIST_INIT(*what);
}
}
/**
* c_list_swap() - exchange the contents of two lists
* @list1: the list to operate on
* @list2: the list to operate on
*
* This replaces the contents of the list @list1 with the contents
* of @list2, and vice versa.
*/
static inline void c_list_swap(CList *list1, CList *list2) {
CList t;
/* make neighbors of list1 point to list2, and vice versa */
t = *list1;
t.next->prev = list2;
t.prev->next = list2;
t = *list2;
t.next->prev = list1;
t.prev->next = list1;
/* swap list1 and list2 now that their neighbors were fixed up */
t = *list1;
*list1 = *list2;
*list2 = t;
}
/**
* c_list_splice() - splice one list into another
* @target: the list to splice into
* @source: the list to splice
*
* This removes all the entries from @source and splice them into @target.
* The order of the two lists is preserved and the source is appended
* to the end of target.
*/
static inline void c_list_splice(CList *target, CList *source) {
if (c_list_is_empty(source))
return;
/* attach the front of @source to the tail of @target */
source->next->prev = target->prev;
target->prev->next = source->next;
/* attach the tail of @source to the front of @target */
source->prev->next = target;
target->prev = source->prev;
}
/**
* c_list_loop_first() - return first list element, or head if empty
* @list: list to operate on
*
* This is an O(1) accessor to the first list element. If the list is empty,
* this returns a pointer to the list head. Hence, this never returns NULL.
*
* Return: Pointer to first list element, or pointer to head if empty.
*/
static inline CList *c_list_loop_first(CList *list) {
return list->next;
}
/**
* c_list_loop_last() - return last list element, or head if empty
* @list: list to operate on
*
* This is an O(1) accessor to the last list element. If the list is empty,
* this returns a pointer to the list head. Hence, this never returns NULL.
*
* Return: Pointer to last list element, or pointer to head if empty.
*/
static inline CList *c_list_loop_last(CList *list) {
return list->prev;
}
/**
* c_list_loop_next() - return next list element, or head if none
* @what: list entry to operate on
*
* This is an O(1) accessor to the next list element. If @what is the list tail
* this will return a pointer to the list head. Hence, this never returns NULL.
*
* Return: Pointer to next list element, or pointer to head if none.
*/
static inline CList *c_list_loop_next(CList *what) {
return what->next;
}
/**
* c_list_loop_prev() - return previous list element, or head if none
* @what: list entry to operate on
*
* This is an O(1) accessor to the previous list element. If @what is the list
* front this will return a pointer to the list head. Hence, this never returns
* NULL.
*
* Return: Pointer to previous list element, or pointer to head if none.
*/
static inline CList *c_list_loop_prev(CList *what) {
return what->prev;
}
/**
* c_list_for_each() - loop over all list entries
* @_iter: iterator to use
* @_list: list to loop over
*
* This is a macro to use as for-loop to iterate an entire list. It is meant as
* convenience macro. Feel free to code your own loop iterator.
*/
#define c_list_for_each(_iter, _list) \
for (_iter = c_list_loop_first(_list); \
_iter != (_list); \
_iter = c_list_loop_next(_iter))
/**
* c_list_for_each_safe() - loop over all list entries, safe for removal
* @_iter: iterator to use
* @_safe: used to store pointer to next element
* @_list: list to loop over
*
* This is a macro to use as for-loop to iterate an entire list, safe against
* removal of the current element. It is meant as convenience macro. Feel free
* to code your own loop iterator.
*
* Note that this fetches the next element prior to executing the loop body.
* This makes it safe against removal of the current entry, but it will go
* havoc if you remove other list entries. You better not modify anything but
* the current list entry.
*/
#define c_list_for_each_safe(_iter, _safe, _list) \
for (_iter = c_list_loop_first(_list), _safe = c_list_loop_next(_iter); \
_iter != (_list); \
_iter = _safe, _safe = c_list_loop_next(_safe))
/**
* c_list_for_each_entry() - loop over all list entries
* @_iter: iterator to use
* @_list: list to loop over
* @_m: member name of CList object in list type
*
* This combines c_list_for_each() with c_list_entry(), making it easy to
* iterate over a list of a specific type.
*/
#define c_list_for_each_entry(_iter, _list, _m) \
for (_iter = c_list_entry(c_list_loop_first(_list), __typeof__(*_iter), _m); \
&_iter->_m != (_list); \
_iter = c_list_entry(c_list_loop_next(&_iter->_m), __typeof__(*_iter), _m))
/**
* c_list_for_each_entry_safe() - loop over all list entries, safe for removal
* @_iter: iterator to use
* @_safe: used to store pointer to next element
* @_list: list to loop over
* @_m: member name of CList object in list type
*
* This combines c_list_for_each_safe() with c_list_entry(), making it easy to
* iterate over a list of a specific type.
*/
#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \
for (_iter = c_list_entry(c_list_loop_first(_list), __typeof__(*_iter), _m), \
_safe = c_list_entry(c_list_loop_next(&_iter->_m), __typeof__(*_iter), _m);\
&_iter->_m != (_list); \
_iter = _safe, \
_safe = c_list_entry(c_list_loop_next(&_safe->_m), __typeof__(*_iter), _m))
/**
* c_list_first() - return pointer to first element, or NULL if empty
* @list: list to operate on, or NULL
*
* This returns a pointer to the first element, or NULL if empty. This never
* returns a pointer to the list head.
*
* Return: Pointer to first list element, or NULL if empty.
*/
static inline CList *c_list_first(CList *list) {
return c_list_is_empty(list) ? NULL : list->next;
}
/**
* c_list_last() - return pointer to last element, or NULL if empty
* @list: list to operate on, or NULL
*
* This returns a pointer to the last element, or NULL if empty. This never
* returns a pointer to the list head.
*
* Return: Pointer to last list element, or NULL if empty.
*/
static inline CList *c_list_last(CList *list) {
return c_list_is_empty(list) ? NULL : list->prev;
}
/**
* c_list_first_entry() - return pointer to first entry, or NULL if empty
* @_list: list to operate on, or NULL
* @_t: type of list entries
* @_m: name of CList member in @_t
*
* This is like c_list_first(), but also applies c_list_entry() on the result.
*
* Return: Pointer to first list entry, or NULL if empty.
*/
#define c_list_first_entry(_list, _t, _m) \
c_list_entry(c_list_first(_list), _t, _m)
/**
* c_list_last_entry() - return pointer to last entry, or NULL if empty
* @_list: list to operate on, or NULL
* @_t: type of list entries
* @_m: name of CList member in @_t
*
* This is like c_list_last(), but also applies c_list_entry() on the result.
*
* Return: Pointer to last list entry, or NULL if empty.
*/
#define c_list_last_entry(_list, _t, _m) \
c_list_entry(c_list_last(_list), _t, _m)
/**
* c_list_length() - return the number of linked entries, excluding the head
* @list: list to operate on
*
* Returns the number of entires in the list, excluding the list head
* @list. That is, for a list that is empty according to c_list_is_empty(),
* the returned length is 0. This requires to iterate the list and has
* thus O(n) runtime.
*
* Return: the number of items in the list
*/
static inline size_t c_list_length(const CList *list) {
CList *iter;
size_t n = 0;
c_list_for_each(iter, (CList *)list)
n++;
return n;
}
/**
* c_list_contains() - whether an item is linked in a certain list
* @list: list to operate on
* @what: the list entry to find
*
* Searches @list whether @what is a linked entry of the list
* in O(n). For the head @list, this also returns True.
*
* Return: True if @what is in @list
*/
static inline _Bool c_list_contains(const CList *list, const CList *what) {
const CList *iter = list;
do {
if (iter == what)
return 1;
iter = iter->next;
} while (iter != list);
return 0;
}
#ifdef __cplusplus
}
#endif

View file

@ -25,6 +25,7 @@
#include <string.h>
#include "NetworkManagerUtils.h"
#include "nm-utils/c-list.h"
/*****************************************************************************/
@ -39,7 +40,7 @@ typedef struct {
GDBusProxy *proxy;
GCancellable *proxy_cancellable;
GHashTable *pending_calls;
CList pending_calls;
bool running;
} NMFirewallManagerPrivate;
@ -76,6 +77,7 @@ typedef enum {
} CBInfoMode;
struct _NMFirewallManagerCallId {
CList lst;
NMFirewallManager *self;
CBInfoOpsType ops_type;
union {
@ -176,8 +178,7 @@ _cb_info_create (NMFirewallManager *self,
} else
info->mode_mutable = CB_INFO_MODE_IDLE;
if (!nm_g_hash_table_add (priv->pending_calls, info))
nm_assert_not_reached ();
c_list_link_tail (&priv->pending_calls, &info->lst);
return info;
}
@ -185,6 +186,7 @@ _cb_info_create (NMFirewallManager *self,
static void
_cb_info_free (CBInfo *info)
{
c_list_unlink (&info->lst);
if (info->mode != CB_INFO_MODE_IDLE) {
if (info->dbus.arg)
g_variant_unref (info->dbus.arg);
@ -209,8 +211,9 @@ _cb_info_complete_normal (CBInfo *info, GError *error)
{
NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (info->self);
if (!g_hash_table_remove (priv->pending_calls, info))
g_return_if_reached ();
nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
c_list_unlink_init (&info->lst);
_cb_info_callback (info, error);
_cb_info_free (info);
@ -423,8 +426,9 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
self = info->self;
priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
if (!g_hash_table_remove (priv->pending_calls, info))
g_return_if_reached ();
nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
c_list_unlink_init (&info->lst);
nm_utils_error_set_cancelled (&error, FALSE, "NMFirewallManager");
@ -488,8 +492,8 @@ _proxy_new_cb (GObject *source_object,
NMFirewallManagerPrivate *priv;
GDBusProxy *proxy;
gs_free_error GError *error = NULL;
GHashTableIter iter;
CBInfo *info;
CList *iter;
proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
if ( !proxy
@ -513,8 +517,9 @@ _proxy_new_cb (GObject *source_object,
_LOGD (NULL, "firewall %s", "initialized (not running)");
again:
g_hash_table_iter_init (&iter, priv->pending_calls);
while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL)) {
c_list_for_each (iter, &priv->pending_calls) {
info = c_list_entry (iter, CBInfo, lst);
if (info->mode != CB_INFO_MODE_DBUS_WAITING)
continue;
if (priv->running) {
@ -522,7 +527,7 @@ again:
_handle_dbus_start (self, info);
} else {
_LOGD (info, "complete: fake success");
g_hash_table_iter_remove (&iter);
c_list_unlink_init (&info->lst);
_cb_info_callback (info, NULL);
_cb_info_free (info);
goto again;
@ -541,7 +546,7 @@ nm_firewall_manager_init (NMFirewallManager * self)
{
NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
priv->pending_calls = g_hash_table_new (g_direct_hash, g_direct_equal);
c_list_init (&priv->pending_calls);
}
static void
@ -572,13 +577,9 @@ dispose (GObject *object)
NMFirewallManager *self = NM_FIREWALL_MANAGER (object);
NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
if (priv->pending_calls) {
/* as every pending operation takes a reference to the manager,
* we don't expect pending operations at this point. */
g_assert (g_hash_table_size (priv->pending_calls) == 0);
g_hash_table_unref (priv->pending_calls);
priv->pending_calls = NULL;
}
/* as every pending operation takes a reference to the manager,
* we don't expect pending operations at this point. */
nm_assert (c_list_is_empty (&priv->pending_calls));
nm_clear_g_cancellable (&priv->proxy_cancellable);
g_clear_object (&priv->proxy);

View file

@ -27,8 +27,7 @@
#include "nm-proxy-config.h"
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
static void pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data);
#include "nm-utils/c-list.h"
#define PACRUNNER_DBUS_SERVICE "org.pacrunner"
#define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
@ -37,11 +36,15 @@ static void pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointe
/*****************************************************************************/
struct _NMPacrunnerCallId {
NMPacrunnerManager *manager;
CList lst;
/* this might be a dangling pointer after the async operation
* is cancelled. */
NMPacrunnerManager *manager_maybe_dangling;
GVariant *args;
char *path;
guint refcount;
bool removed;
};
typedef struct _NMPacrunnerCallId Config;
@ -49,8 +52,8 @@ typedef struct _NMPacrunnerCallId Config;
typedef struct {
char *iface;
GDBusProxy *pacrunner;
GCancellable *pacrunner_cancellable;
GList *configs;
GCancellable *cancellable;
CList configs;
} NMPacrunnerManagerPrivate;
struct _NMPacrunnerManager {
@ -80,49 +83,59 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP
G_STMT_START { \
nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
"%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
"pacrunner: call[", \
_NMLOG2_PREFIX_NAME": call[", \
(config) \
_NM_UTILS_MACRO_REST(__VA_ARGS__)); \
} G_STMT_END
/*****************************************************************************/
static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data);
/*****************************************************************************/
static Config *
config_new (NMPacrunnerManager *manager, GVariant *args)
{
Config *config;
config = g_slice_new0 (Config);
config->manager = manager;
config->manager_maybe_dangling = manager;
config->args = g_variant_ref_sink (args);
config->refcount = 1;
c_list_link_tail (&NM_PACRUNNER_MANAGER_GET_PRIVATE (manager)->configs,
&config->lst);
return config;
}
static void
static Config *
config_ref (Config *config)
{
g_assert (config);
g_assert (config->refcount > 0);
nm_assert (config);
nm_assert (config->refcount > 0);
config->refcount++;
return config;
}
static void
config_unref (Config *config)
{
g_assert (config);
g_assert (config->refcount > 0);
nm_assert (config);
nm_assert (config->refcount > 0);
if (config->refcount == 1) {
g_variant_unref (config->args);
g_free (config->path);
c_list_unlink (&config->lst);
g_slice_free (Config, config);
} else
config->refcount--;
}
/*****************************************************************************/
static void
add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config)
{
@ -220,8 +233,18 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6)
}
}
/*****************************************************************************/
static GCancellable *
_ensure_cancellable (NMPacrunnerManagerPrivate *priv)
{
if (G_UNLIKELY (!priv->cancellable))
priv->cancellable = g_cancellable_new ();
return priv->cancellable;
}
static void
pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data)
{
Config *config = user_data;
NMPacrunnerManager *self;
@ -230,15 +253,13 @@ pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
gs_unref_variant GVariant *variant = NULL;
const char *path = NULL;
g_return_if_fail (!config->path);
nm_assert (!config->path);
variant = g_dbus_proxy_call_finish (proxy, res, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
config_unref (config);
return;
}
variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto out;
self = NM_PACRUNNER_MANAGER (config->manager);
self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling);
priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
if (!variant)
@ -246,20 +267,23 @@ pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
else {
g_variant_get (variant, "(&o)", &path);
config->path = g_strdup (path);
_LOG2D (config, "sent");
if (config->removed) {
if (c_list_is_empty (&config->lst)) {
_LOG2D (config, "sent (%s), but destory it right away", path);
g_dbus_proxy_call (priv->pacrunner,
"DestroyProxyConfiguration",
g_variant_new ("(o)", config->path),
g_variant_new ("(o)", path),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
priv->pacrunner_cancellable,
(GAsyncReadyCallback) pacrunner_remove_done,
config);
_ensure_cancellable (priv),
pacrunner_remove_done,
config_ref (config));
} else {
_LOG2D (config, "sent (%s)", path);
config->path = g_strdup (path);
}
}
out:
config_unref (config);
}
@ -271,17 +295,15 @@ pacrunner_send_config (NMPacrunnerManager *self, Config *config)
if (priv->pacrunner) {
_LOG2T (config, "sending...");
config_ref (config);
g_clear_pointer (&config->path, g_free);
nm_assert (!config->path);
g_dbus_proxy_call (priv->pacrunner,
"CreateProxyConfiguration",
config->args,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
priv->pacrunner_cancellable,
(GAsyncReadyCallback) pacrunner_send_done,
config);
_ensure_cancellable (priv),
pacrunner_send_done,
config_ref (config));
}
}
@ -290,15 +312,18 @@ name_owner_changed (NMPacrunnerManager *self)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
gs_free char *owner = NULL;
GList *iter = NULL;
CList *iter;
owner = g_dbus_proxy_get_name_owner (priv->pacrunner);
if (owner) {
_LOGD ("name owner appeared (%s)", owner);
for (iter = g_list_first (priv->configs); iter; iter = g_list_next (iter))
pacrunner_send_config (self, iter->data);
c_list_for_each (iter, &priv->configs)
pacrunner_send_config (self, c_list_entry (iter, Config, lst));
} else {
_LOGD ("name owner disappeared");
nm_clear_g_cancellable (&priv->cancellable);
c_list_for_each (iter, &priv->configs)
nm_clear_g_free (&c_list_entry (iter, Config, lst)->path);
}
}
@ -315,22 +340,19 @@ pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
NMPacrunnerManager *self = user_data;
NMPacrunnerManagerPrivate *priv;
GError *error = NULL;
gs_free_error GError *error = NULL;
GDBusProxy *proxy;
proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (!proxy) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
_LOGW ("failed to connect to pacrunner via DBus: %s", error->message);
g_error_free (error);
_LOGE ("failed to create D-Bus proxy for pacrunner: %s", error->message);
return;
}
priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
priv->pacrunner = proxy;
nm_clear_g_cancellable (&priv->pacrunner_cancellable);
g_signal_connect (priv->pacrunner, "notify::g-name-owner",
G_CALLBACK (name_owner_changed_cb), self);
name_owner_changed (self);
@ -420,7 +442,6 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self,
}
config = config_new (self, g_variant_new ("(a{sv})", &proxy_data));
priv->configs = g_list_append (priv->configs, config);
{
gs_free char *args_str = NULL;
@ -439,26 +460,24 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self,
}
static void
pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data)
{
Config *config = user_data;
NMPacrunnerManager *self;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *ret = NULL;
ret = g_dbus_proxy_call_finish (proxy, res, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
config_unref (config);
return;
}
self = NM_PACRUNNER_MANAGER (config->manager);
ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
goto out;
self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling);
if (!ret)
_LOG2D (config, "remove failed: %s", error->message);
else
_LOG2D (config, "removed");
out:
config_unref (config);
}
@ -472,7 +491,6 @@ nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_i
{
NMPacrunnerManagerPrivate *priv;
Config *config;
GList *list;
g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
g_return_if_fail (call_id);
@ -482,31 +500,29 @@ nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_i
_LOG2T (config, "removing...");
list = g_list_find (priv->configs, config);
if (!list)
g_return_if_reached ();
nm_assert (c_list_contains (&priv->configs, &config->lst));
if (priv->pacrunner) {
if (!config->path) {
/* send() failed or is still pending. Mark the item as
* removed, so that we ask pacrunner to drop it when the
* send() completes.
/* send() failed or is still pending. The item is unlinked from
* priv->configs, so pacrunner_send_done() knows to call
* DestroyProxyConfiguration right away.
*/
config->removed = TRUE;
config_unref (config);
} else {
g_dbus_proxy_call (priv->pacrunner,
"DestroyProxyConfiguration",
g_variant_new ("(o)", config->path),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
priv->pacrunner_cancellable,
(GAsyncReadyCallback) pacrunner_remove_done,
config);
_ensure_cancellable (priv),
pacrunner_remove_done,
config_ref (config));
nm_clear_g_free (&config->path);
}
} else
config_unref (config);
priv->configs = g_list_delete_link (priv->configs, list);
}
c_list_unlink_init (&config->lst);
config_unref (config);
}
gboolean
@ -532,16 +548,15 @@ nm_pacrunner_manager_init (NMPacrunnerManager *self)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
priv->pacrunner_cancellable = g_cancellable_new ();
c_list_init (&priv->configs);
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
PACRUNNER_DBUS_SERVICE,
PACRUNNER_DBUS_PATH,
PACRUNNER_DBUS_INTERFACE,
priv->pacrunner_cancellable,
(GAsyncReadyCallback) pacrunner_proxy_cb,
_ensure_cancellable (priv),
pacrunner_proxy_cb,
self);
}
@ -549,14 +564,22 @@ static void
dispose (GObject *object)
{
NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object);
CList *iter, *safe;
c_list_for_each_safe (iter, safe, &priv->configs) {
c_list_unlink_init (iter);
config_unref (c_list_entry (iter, Config, lst));
}
/* we cancel all pending operations. Note that pacrunner automatically
* removes all configuration once NetworkManager disconnects from
* the bus -- which happens soon after we destroy the pacrunner manager.
*/
nm_clear_g_cancellable (&priv->cancellable);
g_clear_pointer (&priv->iface, g_free);
nm_clear_g_cancellable (&priv->pacrunner_cancellable);
g_clear_object (&priv->pacrunner);
g_list_free_full (priv->configs, (GDestroyNotify) config_unref);
priv->configs = NULL;
G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object);
}

View file

@ -36,6 +36,7 @@
#include "nm-simple-connection.h"
#include "NetworkManagerUtils.h"
#include "nm-core-internal.h"
#include "nm-utils/c-list.h"
#include "introspection/org.freedesktop.NetworkManager.AgentManager.h"
@ -50,6 +51,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
NMAuthManager *auth_mgr;
NMSessionMonitor *session_monitor;
/* Auth chains for checking agent permissions */
GSList *chains;
@ -59,7 +61,7 @@ typedef struct {
*/
GHashTable *agents;
GHashTable *requests;
CList requests;
} NMAgentManagerPrivate;
struct _NMAgentManager {
@ -123,7 +125,7 @@ typedef struct _NMAgentManagerCallId Request;
static void request_add_agent (Request *req, NMSecretAgent *agent);
static void request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs);
static void request_remove_agent (Request *req, NMSecretAgent *agent);
static void request_next_agent (Request *req);
@ -155,14 +157,62 @@ _request_type_to_string (RequestType request_type, gboolean verbose)
/*****************************************************************************/
struct _NMAgentManagerCallId {
CList lst_request;
NMAgentManager *self;
RequestType request_type;
char *detail;
NMAuthSubject *subject;
/* Current agent being asked for secrets */
NMSecretAgent *current;
NMSecretAgentCallId current_call_id;
/* Stores the sorted list of NMSecretAgents which will be asked for secrets */
GSList *pending;
guint idle_id;
union {
struct {
char *path;
NMConnection *connection;
NMAuthChain *chain;
/* Whether the agent currently being asked for secrets
* has the system.modify privilege.
*/
gboolean current_has_modify;
union {
struct {
NMSecretAgentGetSecretsFlags flags;
char *setting_name;
char **hints;
GVariant *existing_secrets;
NMAgentSecretsResultFunc callback;
gpointer callback_data;
} get;
};
} con;
};
};
/*****************************************************************************/
static gboolean
remove_agent (NMAgentManager *self, const char *owner)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
NMSecretAgent *agent;
GHashTableIter iter;
gpointer data;
GSList *pending_reqs = NULL;
CList *iter, *safe;
g_return_val_if_fail (owner != NULL, FALSE);
@ -174,16 +224,8 @@ remove_agent (NMAgentManager *self, const char *owner)
_LOGD (agent, "agent unregistered or disappeared");
/* Remove this agent from any in-progress secrets requests */
g_hash_table_iter_init (&iter, priv->requests);
while (g_hash_table_iter_next (&iter, &data, NULL))
request_remove_agent ((Request *) data, agent, &pending_reqs);
/* We cannot call request_next_agent() from within hash iterating loop,
* because it may remove the request from the hash table, which invalidates
* the iterator. So, only remove the agent from requests. And store the requests
* that should be sent to other agent to a temporary list to proceed afterwards.
*/
g_slist_free_full (pending_reqs, (GDestroyNotify) request_next_agent);
c_list_for_each_safe (iter, safe, &priv->requests)
request_remove_agent (c_list_entry (iter, Request, lst_request), agent);
/* And dispose of the agent */
g_hash_table_remove (priv->agents, owner);
@ -270,8 +312,7 @@ agent_register_permissions_done (NMAuthChain *chain,
const char *sender;
GError *local = NULL;
NMAuthCallResult result;
GHashTableIter iter;
Request *req;
CList *iter;
g_assert (context);
@ -304,9 +345,8 @@ agent_register_permissions_done (NMAuthChain *chain,
g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent);
/* Add this agent to any in-progress secrets requests */
g_hash_table_iter_init (&iter, priv->requests);
while (g_hash_table_iter_next (&iter, (gpointer) &req, NULL))
request_add_agent (req, agent);
c_list_for_each (iter, &priv->requests)
request_add_agent (c_list_entry (iter, Request, lst_request), agent);
}
nm_auth_chain_unref (chain);
@ -450,52 +490,6 @@ done:
/*****************************************************************************/
struct _NMAgentManagerCallId {
NMAgentManager *self;
RequestType request_type;
char *detail;
NMAuthSubject *subject;
/* Current agent being asked for secrets */
NMSecretAgent *current;
NMSecretAgentCallId current_call_id;
/* Stores the sorted list of NMSecretAgents which will be asked for secrets */
GSList *pending;
guint idle_id;
union {
struct {
char *path;
NMConnection *connection;
NMAuthChain *chain;
/* Whether the agent currently being asked for secrets
* has the system.modify privilege.
*/
gboolean current_has_modify;
union {
struct {
NMSecretAgentGetSecretsFlags flags;
char *setting_name;
char **hints;
GVariant *existing_secrets;
NMAgentSecretsResultFunc callback;
gpointer callback_data;
} get;
};
} con;
};
};
static Request *
request_new (NMAgentManager *self,
RequestType request_type,
@ -509,6 +503,7 @@ request_new (NMAgentManager *self,
req->request_type = request_type;
req->detail = g_strdup (detail);
req->subject = g_object_ref (subject);
c_list_link_tail (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request);
return req;
}
@ -597,7 +592,7 @@ req_complete_cancel (Request *req, gboolean is_disposing)
gs_free_error GError *error = NULL;
nm_assert (req && req->self);
nm_assert (!g_hash_table_contains (req->self->_priv.requests, req));
nm_assert (!c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (req->self)->requests, &req->lst_request));
nm_utils_error_set_cancelled (&error, is_disposing, "NMAgentManager");
req_complete_release (req, NULL, NULL, NULL, error);
@ -611,10 +606,11 @@ req_complete (Request *req,
GError *error)
{
NMAgentManager *self = req->self;
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
if (!g_hash_table_remove (priv->requests, req))
g_return_if_reached ();
nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request));
c_list_unlink_init (&req->lst_request);
req_complete_release (req, secrets, agent_dbus_owner, agent_username, error);
}
@ -630,6 +626,7 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
NMSecretAgent *a = (NMSecretAgent *)aa;
NMSecretAgent *b = (NMSecretAgent *)bb;
Request *req = user_data;
NMSessionMonitor *sm;
gboolean a_active, b_active;
gulong a_pid, b_pid, requester;
@ -648,8 +645,9 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
}
/* Prefer agents in active sessions */
a_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (a), TRUE);
b_active = nm_session_monitor_session_exists (nm_session_monitor_get (), nm_secret_agent_get_owner_uid (b), TRUE);
sm = NM_AGENT_MANAGER_GET_PRIVATE (req->self)->session_monitor;
a_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (a), TRUE);
b_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (b), TRUE);
if (a_active && !b_active)
return -1;
else if (a_active == b_active)
@ -734,7 +732,7 @@ request_next_agent (Request *req)
nm_secret_agent_cancel_secrets (req->current, req->current_call_id);
g_clear_object (&req->current);
}
g_warn_if_fail (!req->current_call_id);
nm_assert (!req->current_call_id);
if (req->pending) {
/* Send the request to the next agent */
@ -769,7 +767,7 @@ request_next_agent (Request *req)
}
static void
request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs)
request_remove_agent (Request *req, NMSecretAgent *agent)
{
NMAgentManager *self;
@ -798,7 +796,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent, GSList **pending_reqs)
g_assert_not_reached ();
}
*pending_reqs = g_slist_prepend (*pending_reqs, req);
request_next_agent (req);
} else if (g_slist_find (req->pending, agent)) {
req->pending = g_slist_remove (req->pending, agent);
@ -1219,7 +1217,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self,
NMAgentSecretsResultFunc callback,
gpointer callback_data)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
Request *req;
g_return_val_if_fail (self != NULL, NULL);
@ -1253,9 +1250,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self,
req->con.get.callback = callback;
req->con.get.callback_data = callback_data;
if (!nm_g_hash_table_add (priv->requests, req))
g_assert_not_reached ();
/* Kick off the request */
if (!(req->con.get.flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM))
request_add_agents (self, req);
@ -1271,9 +1265,9 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self,
g_return_if_fail (request_id);
g_return_if_fail (request_id->request_type == REQUEST_TYPE_CON_GET);
if (!g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (self)->requests,
request_id))
g_return_if_reached ();
nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &request_id->lst_request));
c_list_unlink_init (&request_id->lst_request);
req_complete_cancel (request_id, FALSE);
}
@ -1341,7 +1335,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self,
NMConnection *connection,
NMAuthSubject *subject)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
Request *req;
g_return_if_fail (self);
@ -1359,8 +1352,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self,
subject);
req->con.path = g_strdup (path);
req->con.connection = g_object_ref (connection);
if (!nm_g_hash_table_add (priv->requests, req))
g_assert_not_reached ();
/* Kick off the request */
request_add_agents (self, req);
@ -1426,7 +1417,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self,
const char *path,
NMConnection *connection)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
NMAuthSubject *subject;
Request *req;
@ -1447,8 +1437,6 @@ nm_agent_manager_delete_secrets (NMAgentManager *self,
req->con.path = g_strdup (path);
req->con.connection = g_object_ref (connection);
g_object_unref (subject);
if (!nm_g_hash_table_add (priv->requests, req))
g_assert_not_reached ();
/* Kick off the request */
request_add_agents (self, req);
@ -1570,8 +1558,8 @@ nm_agent_manager_init (NMAgentManager *self)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
c_list_init (&priv->requests);
priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal);
}
static void
@ -1582,6 +1570,7 @@ constructed (GObject *object)
G_OBJECT_CLASS (nm_agent_manager_parent_class)->constructed (object);
priv->auth_mgr = g_object_ref (nm_auth_manager_get ());
priv->session_monitor = g_object_ref (nm_session_monitor_get ());
nm_exported_object_export (NM_EXPORTED_OBJECT (object));
@ -1589,28 +1578,19 @@ constructed (GObject *object)
NM_AUTH_MANAGER_SIGNAL_CHANGED,
G_CALLBACK (authority_changed_cb),
object);
NM_UTILS_KEEP_ALIVE (object, nm_session_monitor_get (), "NMAgentManager-depends-on-NMSessionMonitor");
}
static void
dispose (GObject *object)
{
NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE ((NMAgentManager *) object);
if (priv->requests) {
GHashTableIter iter;
Request *req;
CList *iter;
cancel_more:
g_hash_table_iter_init (&iter, priv->requests);
if (g_hash_table_iter_next (&iter, (gpointer *) &req, NULL)) {
g_hash_table_iter_remove (&iter);
req_complete_cancel (req, TRUE);
goto cancel_more;
}
g_hash_table_unref (priv->requests);
priv->requests = NULL;
c_list_for_each (iter, &priv->requests) {
c_list_unlink_init (iter);
req_complete_cancel (c_list_entry (iter, Request, lst_request), TRUE);
goto cancel_more;
}
g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref);
@ -1630,6 +1610,8 @@ cancel_more:
nm_exported_object_unexport (NM_EXPORTED_OBJECT (object));
g_clear_object (&priv->session_monitor);
G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object);
}

View file

@ -30,6 +30,7 @@
#include "nm-auth-subject.h"
#include "nm-simple-connection.h"
#include "NetworkManagerUtils.h"
#include "nm-utils/c-list.h"
#include "introspection/org.freedesktop.NetworkManager.SecretAgent.h"
@ -58,7 +59,7 @@ typedef struct {
gboolean connection_is_private;
gulong on_disconnected_id;
GHashTable *requests;
CList requests;
} NMSecretAgentPrivate;
struct _NMSecretAgent {
@ -99,6 +100,7 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
/*****************************************************************************/
struct _NMSecretAgentCallId {
CList lst;
NMSecretAgent *agent;
GCancellable *cancellable;
char *path;
@ -129,6 +131,8 @@ request_new (NMSecretAgent *self,
r->callback = callback;
r->callback_data = callback_data;
r->cancellable = g_cancellable_new ();
c_list_link_tail (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests,
&r->lst);
_LOGt ("request "LOG_REQ_FMT": created", LOG_REQ_ARG (r));
return r;
}
@ -140,6 +144,7 @@ request_free (Request *r)
NMSecretAgent *self = r->agent;
_LOGt ("request "LOG_REQ_FMT": destroyed", LOG_REQ_ARG (r));
c_list_unlink (&r->lst);
g_free (r->path);
g_free (r->setting_name);
if (r->cancellable)
@ -150,17 +155,15 @@ request_free (Request *r)
static gboolean
request_check_return (Request *r)
{
NMSecretAgentPrivate *priv;
if (!r->cancellable)
return FALSE;
g_return_val_if_fail (NM_IS_SECRET_AGENT (r->agent), FALSE);
priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (r->agent)->requests,
&r->lst));
if (!g_hash_table_remove (priv->requests, r))
g_return_val_if_reached (FALSE);
c_list_unlink_init (&r->lst);
return TRUE;
}
@ -373,7 +376,6 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data);
r->is_get_secrets = TRUE;
g_hash_table_add (priv->requests, r);
/* Increase the timeout only for this call */
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), 120000);
@ -467,15 +469,15 @@ do_cancel_secrets (NMSecretAgent *self, Request *r, gboolean disposing)
void
nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId call_id)
{
NMSecretAgentPrivate *priv;
Request *r = call_id;
g_return_if_fail (NM_IS_SECRET_AGENT (self));
g_return_if_fail (r);
priv = NM_SECRET_AGENT_GET_PRIVATE (self);
if (!g_hash_table_remove (priv->requests, r))
g_return_if_reached ();
nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests,
&r->lst));
c_list_unlink_init (&r->lst);
do_cancel_secrets (self, r, FALSE);
}
@ -523,7 +525,6 @@ nm_secret_agent_save_secrets (NMSecretAgent *self,
dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data);
g_hash_table_add (priv->requests, r);
nmdbus_secret_agent_call_save_secrets (priv->proxy,
dict,
path,
@ -576,7 +577,6 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self,
dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data);
g_hash_table_add (priv->requests, r);
nmdbus_secret_agent_call_delete_secrets (priv->proxy,
dict,
path,
@ -747,7 +747,7 @@ nm_secret_agent_init (NMSecretAgent *self)
{
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal);
c_list_init (&priv->requests);
}
static void
@ -755,13 +755,13 @@ dispose (GObject *object)
{
NMSecretAgent *self = NM_SECRET_AGENT (object);
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
GHashTableIter iter;
Request *r;
CList *iter;
g_hash_table_iter_init (&iter, priv->requests);
while (g_hash_table_iter_next (&iter, (gpointer *) &r, NULL)) {
g_hash_table_iter_remove (&iter);
do_cancel_secrets (self, r, TRUE);
again:
c_list_for_each (iter, &priv->requests) {
c_list_unlink_init (iter);
do_cancel_secrets (self, c_list_entry (iter, Request, lst), TRUE);
goto again;
}
_on_disconnected_cleanup (priv);
@ -783,7 +783,6 @@ finalize (GObject *object)
g_free (priv->dbus_owner);
g_slist_free_full (priv->permissions, g_free);
g_hash_table_destroy (priv->requests);
G_OBJECT_CLASS (nm_secret_agent_parent_class)->finalize (object);

View file

@ -39,10 +39,14 @@
#include "nm-core-internal.h"
#include "nm-core-utils.h"
#include "nm-utils/nm-enum-utils.h"
#include "nm-utils/c-list.h"
/*****************************************************************************/
struct _shvarLine {
CList lst;
/* There are three cases:
*
* 1) the line is not a valid variable assignment (that is, it doesn't
@ -69,7 +73,7 @@ typedef struct _shvarLine shvarLine;
struct _shvarFile {
char *fileName;
int fd;
GList *lineList;
CList lst_head;
gboolean modified;
};
@ -628,6 +632,7 @@ svFile_new (const char *name)
s = g_slice_new0 (shvarFile);
s->fd = -1;
s->fileName = g_strdup (name);
c_list_init (&s->lst_head);
return s;
}
@ -688,6 +693,7 @@ line_new_parse (const char *value, gsize len)
nm_assert (value);
line = g_slice_new0 (shvarLine);
c_list_init (&line->lst);
for (k = 0; k < len; k++) {
if (g_ascii_isspace (value[k]))
@ -725,6 +731,7 @@ line_new_build (const char *key, const char *value)
value = svEscape (value, &value_escaped);
line = g_slice_new (shvarLine);
c_list_init (&line->lst);
line->line = value_escaped ?: g_strdup (value);
line->key_with_prefix = g_strdup (key);
line->key = line->key_with_prefix;
@ -769,6 +776,7 @@ line_free (shvarLine *line)
ASSERT_shvarLine (line);
g_free (line->line);
g_free (line->key_with_prefix);
c_list_unlink (&line->lst);
g_slice_free (shvarLine, line);
}
@ -788,7 +796,6 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
const char *p, *q;
GError *local = NULL;
nm_auto_close int fd = -1;
GList *lineList = NULL;
if (create)
fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
@ -824,15 +831,13 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
return NULL;
}
for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
lineList = g_list_prepend (lineList, line_new_parse (p, q - p));
if (p[0])
lineList = g_list_prepend (lineList, line_new_parse (p, strlen (p)));
g_free (arena);
lineList = g_list_reverse (lineList);
s = svFile_new (name);
s->lineList = lineList;
for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst);
if (p[0])
c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst);
g_free (arena);
/* closefd is set if we opened the file read-only, so go ahead and
* close it, because we can't write to it anyway */
@ -862,37 +867,17 @@ svCreateFile (const char *name)
/*****************************************************************************/
static const GList *
shlist_find (const GList *current, const char *key)
{
nm_assert (_shell_is_name (key, -1));
if (current) {
do {
shvarLine *line = current->data;
ASSERT_shvarLine (line);
if (line->key && nm_streq (line->key, key))
return current;
current = current->next;
} while (current);
}
return NULL;
}
/*****************************************************************************/
GHashTable *
svGetKeys (shvarFile *s)
{
GHashTable *keys = NULL;
const GList *current;
CList *current;
const shvarLine *line;
nm_assert (s);
for (current = s->lineList; current; current = current->next) {
line = current->data;
c_list_for_each (current, &s->lst_head) {
line = c_list_entry (current, shvarLine, lst);
if (line->key && line->line) {
/* we don't clone the keys. The keys are only valid
* until @s gets modified. */
@ -909,34 +894,31 @@ svGetKeys (shvarFile *s)
static const char *
_svGetValue (shvarFile *s, const char *key, char **to_free)
{
const GList *current, *last;
const shvarLine *line;
CList *current;
const shvarLine *line, *l;
const char *v;
nm_assert (s);
nm_assert (_shell_is_name (key, -1));
nm_assert (to_free);
last = NULL;
current = s->lineList;
while ((current = shlist_find (current, key))) {
last = current;
current = current->next;
line = NULL;
c_list_for_each (current, &s->lst_head) {
l = c_list_entry (current, shvarLine, lst);
if (l->key && nm_streq (l->key, key))
line = l;
}
if (last) {
line = last->data;
if (line->line) {
const char *v;
v = svUnescape (line->line, to_free);
if (!v) {
/* a wrongly quoted value is treated like the empty string.
* See also svWriteFile(), which handles unparsable values
* that way. */
nm_assert (!*to_free);
return "";
}
return v;
if (line && line->line) {
v = svUnescape (line->line, to_free);
if (!v) {
/* a wrongly quoted value is treated like the empty string.
* See also svWriteFile(), which handles unparsable values
* that way. */
nm_assert (!*to_free);
return "";
}
return v;
}
*to_free = NULL;
return NULL;
@ -1118,40 +1100,39 @@ svGetValueEnum (shvarFile *s, const char *key,
void
svSetValue (shvarFile *s, const char *key, const char *value)
{
GList *current, *last;
CList *current;
shvarLine *line, *l;
g_return_if_fail (s != NULL);
g_return_if_fail (key != NULL);
nm_assert (_shell_is_name (key, -1));
last = NULL;
current = s->lineList;
while ((current = (GList *) shlist_find (current, key))) {
if (last) {
/* if we find multiple entries for the same key, we can
* delete all but the last. */
line_free (last->data);
s->lineList = g_list_delete_link (s->lineList, last);
s->modified = TRUE;
line = NULL;
c_list_for_each (current, &s->lst_head) {
l = c_list_entry (current, shvarLine, lst);
if (l->key && nm_streq (l->key, key)) {
if (line) {
/* if we find multiple entries for the same key, we can
* delete all but the last. */
line_free (line);
s->modified = TRUE;
}
line = l;
}
last = current;
current = current->next;
}
if (!value) {
if (last) {
shvarLine *line = last->data;
if (line) {
if (nm_clear_g_free (&line->line))
s->modified = TRUE;
}
} else {
if (!last) {
s->lineList = g_list_append (s->lineList, line_new_build (key, value));
if (!line) {
c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst);
s->modified = TRUE;
} else {
if (line_set (last->data, value))
if (line_set (line, value))
s->modified = TRUE;
}
}
@ -1200,13 +1181,13 @@ svUnsetValue (shvarFile *s, const char *key)
void
svUnsetValuesWithPrefix (shvarFile *s, const char *prefix)
{
GList *current;
CList *current;
g_return_if_fail (s);
g_return_if_fail (prefix);
for (current = s->lineList; current; current = current->next) {
shvarLine *line = current->data;
c_list_for_each (current, &s->lst_head) {
shvarLine *line = c_list_entry (current, shvarLine, lst);
ASSERT_shvarLine (line);
if ( line->key
@ -1231,7 +1212,7 @@ svWriteFile (shvarFile *s, int mode, GError **error)
{
FILE *f;
int tmpfd;
const GList *current;
CList *current;
if (s->modified) {
if (s->fd == -1)
@ -1264,8 +1245,8 @@ svWriteFile (shvarFile *s, int mode, GError **error)
}
f = fdopen (tmpfd, "w");
fseek (f, 0, SEEK_SET);
for (current = s->lineList; current; current = current->next) {
const shvarLine *line = current->data;
c_list_for_each (current, &s->lst_head) {
const shvarLine *line = c_list_entry (current, shvarLine, lst);
const char *str;
char *s_tmp;
gboolean valid_value;
@ -1304,11 +1285,14 @@ svWriteFile (shvarFile *s, int mode, GError **error)
void
svCloseFile (shvarFile *s)
{
CList *current, *safe;
g_return_if_fail (s != NULL);
if (s->fd != -1)
close (s->fd);
g_free (s->fileName);
g_list_free_full (s->lineList, (GDestroyNotify) line_free);
c_list_for_each_safe (current, safe, &s->lst_head)
line_free (c_list_entry (current, shvarLine, lst));
g_slice_free (shvarFile, s);
}