libnl  3.2.7
cache_mngr.c
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  struct nl_cache_ops *ops = ca->ca_cache->c_ops;
93 
94  NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
95 #ifdef NL_DEBUG
96  if (nl_debug >= 4)
97  nl_object_dump(obj, &nl_debug_dp);
98 #endif
99 
100  if (ops->co_event_filter)
101  if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK)
102  return 0;
103 
104  return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
105 }
106 
107 static int event_input(struct nl_msg *msg, void *arg)
108 {
109  struct nl_cache_mngr *mngr = arg;
110  int protocol = nlmsg_get_proto(msg);
111  int type = nlmsg_hdr(msg)->nlmsg_type;
112  struct nl_cache_ops *ops;
113  int i, n;
114  struct nl_parser_param p = {
115  .pp_cb = include_cb,
116  };
117 
118  NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
119  mngr, msg);
120 #ifdef NL_DEBUG
121  if (nl_debug >= 4)
122  nl_msg_dump(msg, stderr);
123 #endif
124 
125  if (mngr->cm_protocol != protocol)
126  BUG();
127 
128  for (i = 0; i < mngr->cm_nassocs; i++) {
129  if (mngr->cm_assocs[i].ca_cache) {
130  ops = mngr->cm_assocs[i].ca_cache->c_ops;
131  for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
132  if (ops->co_msgtypes[n].mt_id == type)
133  goto found;
134  }
135  }
136 
137  return NL_SKIP;
138 
139 found:
140  NL_DBG(2, "Associated message %p to cache %p\n",
141  msg, mngr->cm_assocs[i].ca_cache);
142  p.pp_arg = &mngr->cm_assocs[i];
143 
144  return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
145 }
146 
147 /**
148  * Allocate new cache manager
149  * @arg sk Netlink socket.
150  * @arg protocol Netlink Protocol this manager is used for
151  * @arg flags Flags
152  * @arg result Result pointer
153  *
154  * @return 0 on success or a negative error code.
155  */
156 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
157  struct nl_cache_mngr **result)
158 {
159  struct nl_cache_mngr *mngr;
160  int err = -NLE_NOMEM;
161 
162  if (sk == NULL)
163  BUG();
164 
165  mngr = calloc(1, sizeof(*mngr));
166  if (!mngr)
167  goto errout;
168 
169  mngr->cm_handle = sk;
170  mngr->cm_nassocs = 32;
171  mngr->cm_protocol = protocol;
172  mngr->cm_flags = flags;
173  mngr->cm_assocs = calloc(mngr->cm_nassocs,
174  sizeof(struct nl_cache_assoc));
175  if (!mngr->cm_assocs)
176  goto errout;
177 
178  nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
179  event_input, mngr);
180 
181  /* Required to receive async event notifications */
182  nl_socket_disable_seq_check(mngr->cm_handle);
183 
184  if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
185  goto errout;
186 
187  if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
188  goto errout;
189 
190  NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
191  mngr, protocol, mngr->cm_nassocs);
192 
193  *result = mngr;
194  return 0;
195 
196 errout:
197  nl_cache_mngr_free(mngr);
198  return err;
199 }
200 
201 /**
202  * Add cache responsibility to cache manager
203  * @arg mngr Cache manager.
204  * @arg name Name of cache to keep track of
205  * @arg cb Function to be called upon changes.
206  * @arg data Argument passed on to change callback
207  * @arg result Pointer to store added cache.
208  *
209  * Allocates a new cache of the specified type and adds it to the manager.
210  * The operation will trigger a full dump request from the kernel to
211  * initially fill the contents of the cache. The manager will subscribe
212  * to the notification group of the cache to keep track of any further
213  * changes.
214  *
215  * @return 0 on success or a negative error code.
216  */
217 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
218  change_func_t cb, void *data, struct nl_cache **result)
219 {
220  struct nl_cache_ops *ops;
221  struct nl_cache *cache;
222  struct nl_af_group *grp;
223  int err, i;
224 
225  ops = nl_cache_ops_lookup(name);
226  if (!ops)
227  return -NLE_NOCACHE;
228 
229  if (ops->co_protocol != mngr->cm_protocol)
230  return -NLE_PROTO_MISMATCH;
231 
232  if (ops->co_groups == NULL)
233  return -NLE_OPNOTSUPP;
234 
235  for (i = 0; i < mngr->cm_nassocs; i++)
236  if (mngr->cm_assocs[i].ca_cache &&
237  mngr->cm_assocs[i].ca_cache->c_ops == ops)
238  return -NLE_EXIST;
239 
240 retry:
241  for (i = 0; i < mngr->cm_nassocs; i++)
242  if (!mngr->cm_assocs[i].ca_cache)
243  break;
244 
245  if (i >= mngr->cm_nassocs) {
246  mngr->cm_nassocs += 16;
247  mngr->cm_assocs = realloc(mngr->cm_assocs,
248  mngr->cm_nassocs *
249  sizeof(struct nl_cache_assoc));
250  if (mngr->cm_assocs == NULL)
251  return -NLE_NOMEM;
252  else {
253  NL_DBG(1, "Increased capacity of cache manager %p " \
254  "to %d\n", mngr, mngr->cm_nassocs);
255  goto retry;
256  }
257  }
258 
259  cache = nl_cache_alloc(ops);
260  if (!cache)
261  return -NLE_NOMEM;
262 
263  for (grp = ops->co_groups; grp->ag_group; grp++) {
264  err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
265  if (err < 0)
266  goto errout_free_cache;
267  }
268 
269  err = nl_cache_refill(mngr->cm_handle, cache);
270  if (err < 0)
271  goto errout_drop_membership;
272 
273  mngr->cm_assocs[i].ca_cache = cache;
274  mngr->cm_assocs[i].ca_change = cb;
275  mngr->cm_assocs[i].ca_change_data = data;
276 
277  if (mngr->cm_flags & NL_AUTO_PROVIDE)
278  nl_cache_mngt_provide(cache);
279 
280  NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
281  cache, nl_cache_name(cache), mngr);
282 
283  *result = cache;
284  return 0;
285 
286 errout_drop_membership:
287  for (grp = ops->co_groups; grp->ag_group; grp++)
288  nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
289 errout_free_cache:
290  nl_cache_free(cache);
291 
292  return err;
293 }
294 
295 /**
296  * Get file descriptor
297  * @arg mngr Cache Manager
298  *
299  * Get the file descriptor of the socket associated to the manager.
300  * This can be used to change socket options or monitor activity
301  * using poll()/select().
302  */
303 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
304 {
305  return nl_socket_get_fd(mngr->cm_handle);
306 }
307 
308 /**
309  * Check for event notifications
310  * @arg mngr Cache Manager
311  * @arg timeout Upper limit poll() will block, in milliseconds.
312  *
313  * Causes poll() to be called to check for new event notifications
314  * being available. Automatically receives and handles available
315  * notifications.
316  *
317  * This functionally is ideally called regularly during an idle
318  * period.
319  *
320  * @return A positive value if at least one update was handled, 0
321  * for none, or a negative error code.
322  */
323 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
324 {
325  int ret;
326  struct pollfd fds = {
327  .fd = nl_socket_get_fd(mngr->cm_handle),
328  .events = POLLIN,
329  };
330 
331  NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
332  ret = poll(&fds, 1, timeout);
333  NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
334  if (ret < 0)
335  return -nl_syserr2nlerr(errno);
336 
337  if (ret == 0)
338  return 0;
339 
340  return nl_cache_mngr_data_ready(mngr);
341 }
342 
343 /**
344  * Receive available event notifications
345  * @arg mngr Cache manager
346  *
347  * This function can be called if the socket associated to the manager
348  * contains updates to be received. This function should not be used
349  * if nl_cache_mngr_poll() is used.
350  *
351  * @return A positive value if at least one update was handled, 0
352  * for none, or a negative error code.
353  */
354 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
355 {
356  int err;
357 
358  err = nl_recvmsgs_default(mngr->cm_handle);
359  if (err < 0)
360  return err;
361 
362  return 1;
363 }
364 
365 /**
366  * Free cache manager and all caches.
367  * @arg mngr Cache manager.
368  *
369  * Release all resources after usage of a cache manager.
370  */
371 void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
372 {
373  int i;
374 
375  if (!mngr)
376  return;
377 
378  if (mngr->cm_handle)
379  nl_close(mngr->cm_handle);
380 
381  for (i = 0; i < mngr->cm_nassocs; i++) {
382  if (mngr->cm_assocs[i].ca_cache) {
383  nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache);
384  nl_cache_free(mngr->cm_assocs[i].ca_cache);
385  }
386  }
387 
388  free(mngr->cm_assocs);
389  free(mngr);
390 
391  NL_DBG(1, "Cache manager %p freed\n", mngr);
392 }
393 
394 /** @} */