~ubuntu-branches/ubuntu/trusty/libnl3/trusty

« back to all changes in this revision

Viewing changes to lib/cache_mngr.c

  • Committer: Bazaar Package Importer
  • Author(s): Heiko Stuebner
  • Date: 2011-05-21 19:25:13 UTC
  • Revision ID: james.westby@ubuntu.com-20110521192513-1ieyu9w9kym4bt16
Tags: upstream-3.0
ImportĀ upstreamĀ versionĀ 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lib/cache_mngr.c     Cache Manager
 
3
 *
 
4
 *      This library is free software; you can redistribute it and/or
 
5
 *      modify it under the terms of the GNU Lesser General Public
 
6
 *      License as published by the Free Software Foundation version 2.1
 
7
 *      of the License.
 
8
 *
 
9
 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
 
10
 */
 
11
 
 
12
/**
 
13
 * @ingroup cache_mngt
 
14
 * @defgroup cache_mngr Manager
 
15
 * @brief Helps keeping caches up to date.
 
16
 *
 
17
 * The purpose of a cache manager is to keep track of caches and
 
18
 * automatically receive event notifications to keep the caches
 
19
 * up to date with the kernel state. Each manager has exactly one
 
20
 * netlink socket assigned which limits the scope of each manager
 
21
 * to exactly one netlink family. Therefore all caches committed
 
22
 * to a manager must be part of the same netlink family. Due to the
 
23
 * nature of a manager, it is not possible to have a cache maintain
 
24
 * two instances of the same cache type. The socket is subscribed
 
25
 * to the event notification group of each cache and also put into
 
26
 * non-blocking mode. Functions exist to poll() on the socket to
 
27
 * wait for new events to be received.
 
28
 *
 
29
 * @code
 
30
 * App       libnl                        Kernel
 
31
 *        |                            |
 
32
 *            +-----------------+        [ notification, link change ]
 
33
 *        |   |  Cache Manager  |      | [   (IFF_UP | IFF_RUNNING)  ]
 
34
 *            |                 |                |
 
35
 *        |   |   +------------+|      |         |  [ notification, new addr ]
 
36
 *    <-------|---| route/link |<-------(async)--+  [  10.0.1.1/32 dev eth1  ]
 
37
 *        |   |   +------------+|      |                      |
 
38
 *            |   +------------+|                             |
 
39
 *    <---|---|---| route/addr |<------|-(async)--------------+
 
40
 *            |   +------------+|
 
41
 *        |   |   +------------+|      |
 
42
 *    <-------|---| ...        ||
 
43
 *        |   |   +------------+|      |
 
44
 *            +-----------------+
 
45
 *        |                            |
 
46
 * @endcode
 
47
 *
 
48
 * @par 1) Creating a new cache manager
 
49
 * @code
 
50
 * struct nl_cache_mngr *mngr;
 
51
 *
 
52
 * // Allocate a new cache manager for RTNETLINK and automatically
 
53
 * // provide the caches added to the manager.
 
54
 * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE);
 
55
 * @endcode
 
56
 *
 
57
 * @par 2) Keep track of a cache
 
58
 * @code
 
59
 * struct nl_cache *cache;
 
60
 *
 
61
 * // Create a new cache for links/interfaces and ask the manager to
 
62
 * // keep it up to date for us. This will trigger a full dump request
 
63
 * // to initially fill the cache.
 
64
 * cache = nl_cache_mngr_add(mngr, "route/link");
 
65
 * @endcode
 
66
 *
 
67
 * @par 3) Make the manager receive updates
 
68
 * @code
 
69
 * // Give the manager the ability to receive updates, will call poll()
 
70
 * // with a timeout of 5 seconds.
 
71
 * if (nl_cache_mngr_poll(mngr, 5000) > 0) {
 
72
 *         // Manager received at least one update, dump cache?
 
73
 *         nl_cache_dump(cache, ...);
 
74
 * }
 
75
 * @endcode
 
76
 *
 
77
 * @par 4) Release cache manager
 
78
 * @code
 
79
 * nl_cache_mngr_free(mngr);
 
80
 * @endcode
 
81
 * @{
 
82
 */
 
83
 
 
84
#include <netlink-local.h>
 
85
#include <netlink/netlink.h>
 
86
#include <netlink/cache.h>
 
87
#include <netlink/utils.h>
 
88
 
 
89
static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
 
90
{
 
91
        struct nl_cache_assoc *ca = p->pp_arg;
 
92
 
 
93
        NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
 
94
#ifdef NL_DEBUG
 
95
        if (nl_debug >= 4)
 
96
                nl_object_dump(obj, &nl_debug_dp);
 
97
#endif
 
98
        return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
 
99
}
 
100
 
 
101
static int event_input(struct nl_msg *msg, void *arg)
 
102
{
 
103
        struct nl_cache_mngr *mngr = arg;
 
104
        int protocol = nlmsg_get_proto(msg);
 
105
        int type = nlmsg_hdr(msg)->nlmsg_type;
 
106
        struct nl_cache_ops *ops;
 
107
        int i, n;
 
108
        struct nl_parser_param p = {
 
109
                .pp_cb = include_cb,
 
110
        };
 
111
 
 
112
        NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
 
113
               mngr, msg);
 
114
#ifdef NL_DEBUG
 
115
        if (nl_debug >= 4)
 
116
                nl_msg_dump(msg, stderr);
 
117
#endif
 
118
 
 
119
        if (mngr->cm_protocol != protocol)
 
120
                BUG();
 
121
 
 
122
        for (i = 0; i < mngr->cm_nassocs; i++) {
 
123
                if (mngr->cm_assocs[i].ca_cache) {
 
124
                        ops = mngr->cm_assocs[i].ca_cache->c_ops;
 
125
                        for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
 
126
                                if (ops->co_msgtypes[n].mt_id == type)
 
127
                                        goto found;
 
128
                }
 
129
        }
 
130
 
 
131
        return NL_SKIP;
 
132
 
 
133
found:
 
134
        NL_DBG(2, "Associated message %p to cache %p\n",
 
135
               msg, mngr->cm_assocs[i].ca_cache);
 
136
        p.pp_arg = &mngr->cm_assocs[i];
 
137
 
 
138
        return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
 
139
}
 
140
 
 
141
/**
 
142
 * Allocate new cache manager
 
143
 * @arg sk              Netlink socket.
 
144
 * @arg protocol        Netlink Protocol this manager is used for
 
145
 * @arg flags           Flags
 
146
 *
 
147
 * @return Newly allocated cache manager or NULL on failure.
 
148
 */
 
149
int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
 
150
                        struct nl_cache_mngr **result)
 
151
{
 
152
        struct nl_cache_mngr *mngr;
 
153
        int err = -NLE_NOMEM;
 
154
 
 
155
        if (sk == NULL)
 
156
                BUG();
 
157
 
 
158
        mngr = calloc(1, sizeof(*mngr));
 
159
        if (!mngr)
 
160
                goto errout;
 
161
 
 
162
        mngr->cm_handle = sk;
 
163
        mngr->cm_nassocs = 32;
 
164
        mngr->cm_protocol = protocol;
 
165
        mngr->cm_flags = flags;
 
166
        mngr->cm_assocs = calloc(mngr->cm_nassocs,
 
167
                                 sizeof(struct nl_cache_assoc));
 
168
        if (!mngr->cm_assocs)
 
169
                goto errout;
 
170
 
 
171
        nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
 
172
                            event_input, mngr);
 
173
 
 
174
        /* Required to receive async event notifications */
 
175
        nl_socket_disable_seq_check(mngr->cm_handle);
 
176
 
 
177
        if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
 
178
                goto errout;
 
179
 
 
180
        if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
 
181
                goto errout;
 
182
 
 
183
        NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
 
184
               mngr, protocol, mngr->cm_nassocs);
 
185
 
 
186
        *result = mngr;
 
187
        return 0;
 
188
 
 
189
errout:
 
190
        nl_cache_mngr_free(mngr);
 
191
        return err;
 
192
}
 
193
 
 
194
/**
 
195
 * Add cache responsibility to cache manager
 
196
 * @arg mngr            Cache manager.
 
197
 * @arg name            Name of cache to keep track of
 
198
 * @arg cb              Function to be called upon changes.
 
199
 * @arg result          Pointer to store added cache.
 
200
 *
 
201
 * Allocates a new cache of the specified type and adds it to the manager.
 
202
 * The operation will trigger a full dump request from the kernel to
 
203
 * initially fill the contents of the cache. The manager will subscribe
 
204
 * to the notification group of the cache to keep track of any further
 
205
 * changes.
 
206
 *
 
207
 * @return 0 on success or a negative error code.
 
208
 */
 
209
int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
 
210
                      change_func_t cb, void *data, struct nl_cache **result)
 
211
{
 
212
        struct nl_cache_ops *ops;
 
213
        struct nl_cache *cache;
 
214
        struct nl_af_group *grp;
 
215
        int err, i;
 
216
 
 
217
        ops = nl_cache_ops_lookup(name);
 
218
        if (!ops)
 
219
                return -NLE_NOCACHE;
 
220
 
 
221
        if (ops->co_protocol != mngr->cm_protocol)
 
222
                return -NLE_PROTO_MISMATCH;
 
223
 
 
224
        if (ops->co_groups == NULL)
 
225
                return -NLE_OPNOTSUPP;
 
226
 
 
227
        for (i = 0; i < mngr->cm_nassocs; i++)
 
228
                if (mngr->cm_assocs[i].ca_cache &&
 
229
                    mngr->cm_assocs[i].ca_cache->c_ops == ops)
 
230
                        return -NLE_EXIST;
 
231
 
 
232
retry:
 
233
        for (i = 0; i < mngr->cm_nassocs; i++)
 
234
                if (!mngr->cm_assocs[i].ca_cache)
 
235
                        break;
 
236
 
 
237
        if (i >= mngr->cm_nassocs) {
 
238
                mngr->cm_nassocs += 16;
 
239
                mngr->cm_assocs = realloc(mngr->cm_assocs,
 
240
                                          mngr->cm_nassocs *
 
241
                                          sizeof(struct nl_cache_assoc));
 
242
                if (mngr->cm_assocs == NULL)
 
243
                        return -NLE_NOMEM;
 
244
                else {
 
245
                        NL_DBG(1, "Increased capacity of cache manager %p " \
 
246
                                  "to %d\n", mngr, mngr->cm_nassocs);
 
247
                        goto retry;
 
248
                }
 
249
        }
 
250
 
 
251
        cache = nl_cache_alloc(ops);
 
252
        if (!cache)
 
253
                return -NLE_NOMEM;
 
254
 
 
255
        for (grp = ops->co_groups; grp->ag_group; grp++) {
 
256
                err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
 
257
                if (err < 0)
 
258
                        goto errout_free_cache;
 
259
        }
 
260
 
 
261
        err = nl_cache_refill(mngr->cm_handle, cache);
 
262
        if (err < 0)
 
263
                goto errout_drop_membership;
 
264
 
 
265
        mngr->cm_assocs[i].ca_cache = cache;
 
266
        mngr->cm_assocs[i].ca_change = cb;
 
267
        mngr->cm_assocs[i].ca_change_data = data;
 
268
 
 
269
        if (mngr->cm_flags & NL_AUTO_PROVIDE)
 
270
                nl_cache_mngt_provide(cache);
 
271
 
 
272
        NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
 
273
               cache, nl_cache_name(cache), mngr);
 
274
 
 
275
        *result = cache;
 
276
        return 0;
 
277
 
 
278
errout_drop_membership:
 
279
        for (grp = ops->co_groups; grp->ag_group; grp++)
 
280
                nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
 
281
errout_free_cache:
 
282
        nl_cache_free(cache);
 
283
 
 
284
        return err;
 
285
}
 
286
 
 
287
/**
 
288
 * Get file descriptor
 
289
 * @arg mngr            Cache Manager
 
290
 *
 
291
 * Get the file descriptor of the socket associated to the manager.
 
292
 * This can be used to change socket options or monitor activity
 
293
 * using poll()/select().
 
294
 */
 
295
int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
 
296
{
 
297
        return nl_socket_get_fd(mngr->cm_handle);
 
298
}
 
299
 
 
300
/**
 
301
 * Check for event notifications
 
302
 * @arg mngr            Cache Manager
 
303
 * @arg timeout         Upper limit poll() will block, in milliseconds.
 
304
 *
 
305
 * Causes poll() to be called to check for new event notifications
 
306
 * being available. Automatically receives and handles available
 
307
 * notifications.
 
308
 *
 
309
 * This functionally is ideally called regularly during an idle
 
310
 * period.
 
311
 *
 
312
 * @return A positive value if at least one update was handled, 0
 
313
 *         for none, or a  negative error code.
 
314
 */
 
315
int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
 
316
{
 
317
        int ret;
 
318
        struct pollfd fds = {
 
319
                .fd = nl_socket_get_fd(mngr->cm_handle),
 
320
                .events = POLLIN,
 
321
        };
 
322
 
 
323
        NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
 
324
        ret = poll(&fds, 1, timeout);
 
325
        NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
 
326
        if (ret < 0)
 
327
                return -nl_syserr2nlerr(errno);
 
328
 
 
329
        if (ret == 0)
 
330
                return 0;
 
331
 
 
332
        return nl_cache_mngr_data_ready(mngr);
 
333
}
 
334
 
 
335
/**
 
336
 * Receive available event notifications
 
337
 * @arg mngr            Cache manager
 
338
 *
 
339
 * This function can be called if the socket associated to the manager
 
340
 * contains updates to be received. This function should not be used
 
341
 * if nl_cache_mngr_poll() is used.
 
342
 *
 
343
 * @return A positive value if at least one update was handled, 0
 
344
 *         for none, or a  negative error code.
 
345
 */
 
346
int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
 
347
{
 
348
        int err;
 
349
 
 
350
        err = nl_recvmsgs_default(mngr->cm_handle);
 
351
        if (err < 0)
 
352
                return err;
 
353
 
 
354
        return 1;
 
355
}
 
356
 
 
357
/**
 
358
 * Free cache manager and all caches.
 
359
 * @arg mngr            Cache manager.
 
360
 *
 
361
 * Release all resources after usage of a cache manager.
 
362
 */
 
363
void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
 
364
{
 
365
        int i;
 
366
 
 
367
        if (!mngr)
 
368
                return;
 
369
 
 
370
        if (mngr->cm_handle)
 
371
                nl_close(mngr->cm_handle);
 
372
 
 
373
        for (i = 0; i < mngr->cm_nassocs; i++)
 
374
                if (mngr->cm_assocs[i].ca_cache)
 
375
                        nl_cache_free(mngr->cm_assocs[i].ca_cache);
 
376
 
 
377
        free(mngr->cm_assocs);
 
378
        free(mngr);
 
379
 
 
380
        NL_DBG(1, "Cache manager %p freed\n", mngr);
 
381
}
 
382
 
 
383
/** @} */