Imported Upstream version 0.20.12
[profile/ivi/GUPnP.git] / libgupnp / gupnp-linux-context-manager.c
1 /*
2  * Copyright (C) 2011 Jens Georg
3  *
4  * Author: Jens Georg <mail@jensge.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:gupnp-linux-context-manager
24  * @short_description: Linux-specific implementation of #GUPnPContextManager
25  *
26  * This is a Linux-specific context manager which uses <ulink
27  * url="http://www.linuxfoundation.org/collaborate/workgroups/networking/netlink">Netlink</ulink>
28  * to detect the changes in network interface configurations, such as
29  * added or removed interfaces, network addresses, ...
30  *
31  * The context manager works in two phase.
32  *
33  * Phase one is the "bootstrapping" phase where we query all currently
34  * configured interfaces and addresses.
35  *
36  * Phase two is the "listening" phase where we just listen to the netlink
37  * messages that are happening and create or destroy #GUPnPContext<!-- -->s
38  * accordingly.
39  */
40
41 #include <config.h>
42
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <linux/netlink.h>
46 #include <linux/rtnetlink.h>
47 #ifdef HAVE_LINUX_WIRELESS_H
48 #include <linux/wireless.h>
49 #endif
50 #include <sys/ioctl.h>
51 #include <errno.h>
52 #include <string.h>
53
54 #include <glib.h>
55 #include <gio/gio.h>
56
57 #include "gupnp-linux-context-manager.h"
58 #include "gupnp-context.h"
59
60 G_DEFINE_TYPE (GUPnPLinuxContextManager,
61                gupnp_linux_context_manager,
62                GUPNP_TYPE_CONTEXT_MANAGER);
63
64 struct _GUPnPLinuxContextManagerPrivate {
65         /* Socket used for IOCTL calls */
66         int fd;
67
68         /* Netlink sequence number; nl_seq > 1 means bootstrapping done */
69         int nl_seq;
70
71         /* Socket used to do netlink communication */
72         GSocket *netlink_socket;
73
74         /* Socket source used for normal netlink communication */
75         GSource *netlink_socket_source;
76
77         /* Idle source used during bootstrap */
78         GSource *bootstrap_source;
79
80         /* A hash table mapping system interface indices to a NetworkInterface
81          * structure */
82         GHashTable *interfaces;
83 };
84
85 typedef enum {
86         /* Interface is up */
87         NETWORK_INTERFACE_UP = 1 << 0,
88
89         /* Interface doesn't support multicast or is P-t-P */
90         NETWORK_INTERFACE_IGNORE = 1 << 1,
91
92         /* Interface is down but has an address set */
93         NETWORK_INTERFACE_PRECONFIGURED = 1 << 2
94 } NetworkInterfaceFlags;
95
96 /* struct representing a network interface */
97 struct _NetworkInterface {
98         /* Weak pointer to context manager associated with this interface */
99         GUPnPLinuxContextManager *manager;
100
101         /* Name of the interface (eth0 etc.) */
102         char *name;
103
104         /* ESSID for wireless interfaces */
105         char *essid;
106
107         /* States of the interface */
108         NetworkInterfaceFlags flags;
109
110         /* UPnP contexts associated with this interface. Can be more than one
111          * with alias addresses like eth0:1 etc. */
112         GHashTable *contexts;
113 };
114
115 typedef struct _NetworkInterface NetworkInterface;
116
117 /* Create a new network interface struct and query the device name */
118 static NetworkInterface *
119 network_device_new (GUPnPLinuxContextManager *manager,
120                     int                       index)
121 {
122         NetworkInterface *device;
123         struct ifreq ifr;
124         int ret;
125
126         /* Query interface name */
127         memset (&ifr, 0, sizeof (struct ifreq));
128         ifr.ifr_ifindex = index;
129         ret = ioctl (manager->priv->fd, SIOCGIFNAME, &ifr);
130
131         if (ret == -1) {
132                 g_warning ("Could not get interface name for index %d",
133                            index);
134
135                 return NULL;
136         }
137
138         device = g_slice_new0 (NetworkInterface);
139         device->manager = manager;
140         device->name = g_strdup (ifr.ifr_name);
141
142         device->contexts = g_hash_table_new_full (g_str_hash,
143                                                   g_str_equal,
144                                                   g_free,
145                                                   g_object_unref);
146
147         return device;
148 }
149
150 /* Try to update the ESSID of a network interface. */
151 static void
152 network_device_update_essid (NetworkInterface *device)
153 {
154         char *old_essid = device->essid;
155 #ifdef HAVE_LINUX_WIRELESS_H
156         char essid[IW_ESSID_MAX_SIZE + 1];
157         struct iwreq iwr;
158         int ret;
159
160         /* Query essid */
161         memset (&iwr, 0, sizeof (struct iwreq));
162         memset (essid, 0, IW_ESSID_MAX_SIZE + 1);
163         strncpy (iwr.ifr_name, device->name, IFNAMSIZ);
164         iwr.u.essid.pointer = (caddr_t) essid;
165         iwr.u.essid.length = IW_ESSID_MAX_SIZE;
166         ret = ioctl (device->manager->priv->fd, SIOCGIWESSID, &iwr);
167
168         if ((ret == 0 && essid[0] != '\0') &&
169             (!device->essid || strcmp (device->essid, essid)))
170                 device->essid = g_strdup (essid);
171         else
172                 old_essid = NULL;
173 #endif
174         g_free (old_essid);
175 }
176
177 static void
178 network_device_create_context (NetworkInterface *device, const char *label)
179 {
180         guint port;
181         GError *error = NULL;
182         GUPnPContext *context;
183
184         /* We cannot create a context yet. But it may be that there will not
185          * be a RTM_NEWADDR message for this device if the IP address does not
186          * change so we mark this device as preconfigured and will create the
187          * context if the device comes up. If the address changes, we'll get a
188          * RTM_DELADDR before the next RTM_NEWADDR. */
189         if (!device->flags & NETWORK_INTERFACE_UP) {
190                 device->flags |= NETWORK_INTERFACE_PRECONFIGURED;
191
192                 return;
193         }
194
195         device->flags &= ~NETWORK_INTERFACE_PRECONFIGURED;
196
197         g_object_get (device->manager,
198                       "port", &port,
199                       NULL);
200
201         network_device_update_essid (device);
202         context = g_initable_new (GUPNP_TYPE_CONTEXT,
203                                   NULL,
204                                   &error,
205                                   "interface", label,
206                                   "network", device->essid,
207                                   "port", port,
208                                   NULL);
209
210         if (error) {
211                 g_warning ("Error creating GUPnP context: %s",
212                            error->message);
213                 g_error_free (error);
214
215                 return;
216         }
217         g_hash_table_insert (device->contexts, g_strdup (label), context);
218
219         g_signal_emit_by_name (device->manager,
220                                "context-available",
221                                context);
222 }
223
224 static void
225 context_signal_up (G_GNUC_UNUSED gpointer key,
226                    gpointer               value,
227                    gpointer               user_data)
228 {
229         g_signal_emit_by_name (user_data, "context-available", value);
230 }
231
232 static void
233 context_signal_down (G_GNUC_UNUSED gpointer key,
234                      gpointer               value,
235                      gpointer               user_data)
236 {
237         g_signal_emit_by_name (user_data, "context-unavailable", value);
238 }
239
240 static void
241 network_device_up (NetworkInterface *device)
242 {
243         if (device->flags & NETWORK_INTERFACE_UP)
244                 return;
245
246         device->flags |= NETWORK_INTERFACE_UP;
247
248         if (g_hash_table_size (device->contexts) > 0)
249                 g_hash_table_foreach (device->contexts,
250                                       context_signal_up,
251                                       device->manager);
252         else if (device->flags & NETWORK_INTERFACE_PRECONFIGURED)
253                 network_device_create_context (device, device->name);
254 }
255
256 static void
257 network_device_down (NetworkInterface *device)
258 {
259         if (!device->flags & NETWORK_INTERFACE_UP)
260                 return;
261
262         device->flags &= ~NETWORK_INTERFACE_UP;
263
264         if (device->contexts)
265                 g_hash_table_foreach (device->contexts,
266                                       context_signal_down,
267                                       device->manager);
268 }
269
270 static void
271 network_device_free (NetworkInterface *device)
272 {
273         g_free (device->name);
274         g_free (device->essid);
275
276         if (device->contexts != NULL) {
277                 GHashTableIter iter;
278                 char *key;
279                 GUPnPContext *value;
280
281                 g_hash_table_iter_init (&iter, device->contexts);
282                 while (g_hash_table_iter_next (&iter,
283                                                (gpointer *) &key,
284                                                (gpointer *) &value)) {
285                         g_signal_emit_by_name (device->manager,
286                                                "context-unavailable",
287                                                value);
288                         g_hash_table_iter_remove (&iter);
289                 }
290         }
291
292         g_hash_table_unref (device->contexts);
293         device->contexts = NULL;
294
295         g_slice_free (NetworkInterface, device);
296 }
297
298
299 static void query_all_network_interfaces (GUPnPLinuxContextManager *self);
300 static void query_all_addresses (GUPnPLinuxContextManager *self);
301 static void receive_netlink_message (GUPnPLinuxContextManager  *self,
302                                      GError                   **error);
303 static void create_context (GUPnPLinuxContextManager *self,
304                             const char               *label,
305                             struct ifaddrmsg         *ifa);
306 static void remove_context (GUPnPLinuxContextManager *self,
307                             const char               *label,
308                             struct ifaddrmsg         *ifa);
309
310 static gboolean
311 on_netlink_message_available (G_GNUC_UNUSED GSocket     *socket,
312                               G_GNUC_UNUSED GIOCondition condition,
313                               gpointer                   user_data)
314 {
315         GUPnPLinuxContextManager *self;
316
317         self = GUPNP_LINUX_CONTEXT_MANAGER (user_data);
318
319         receive_netlink_message (self, NULL);
320
321         return TRUE;
322 }
323
324 #define RT_ATTR_OK(a,l) \
325         ((l > 0) && RTA_OK (a, l))
326
327 static void
328 extract_info (struct nlmsghdr *header, char **label)
329 {
330         int rt_attr_len;
331         struct rtattr *rt_attr;
332
333         rt_attr = IFLA_RTA (NLMSG_DATA (header));
334         rt_attr_len = IFLA_PAYLOAD (header);
335         while (RT_ATTR_OK (rt_attr, rt_attr_len)) {
336                 if (rt_attr->rta_type == IFA_LABEL) {
337                         *label = g_strdup ((char *) RTA_DATA (rt_attr));
338
339                         break;
340                 }
341                 rt_attr = RTA_NEXT (rt_attr, rt_attr_len);
342         }
343 }
344
345 static gboolean
346 is_wireless_status_message (struct nlmsghdr *header)
347 {
348         int rt_attr_len;
349         struct rtattr *rt_attr;
350
351         rt_attr = IFLA_RTA (NLMSG_DATA (header));
352         rt_attr_len = IFLA_PAYLOAD (header);
353         while (RT_ATTR_OK (rt_attr, rt_attr_len)) {
354                 if (rt_attr->rta_type == IFLA_WIRELESS)
355                         return TRUE;
356
357                 rt_attr = RTA_NEXT (rt_attr, rt_attr_len);
358         }
359
360         return FALSE;
361 }
362
363 static void
364 create_context (GUPnPLinuxContextManager *self,
365                 const char               *label,
366                 struct ifaddrmsg         *ifa)
367 {
368         NetworkInterface *device;
369
370         remove_context (self, label, ifa);
371
372         device = g_hash_table_lookup (self->priv->interfaces,
373                                       GINT_TO_POINTER (ifa->ifa_index));
374
375         if (!device) {
376                 g_warning ("Got new address for device %d but device is"
377                            " not active",
378                            ifa->ifa_index);
379
380                 return;
381         }
382
383         /* If device isn't one we consider, silently skip address */
384         if (device->flags & NETWORK_INTERFACE_IGNORE)
385                 return;
386
387         network_device_create_context (device, label);
388 }
389
390 static void
391 remove_context (GUPnPLinuxContextManager *self,
392                 const char               *label,
393                 struct ifaddrmsg         *ifa)
394 {
395         NetworkInterface *device;
396         GUPnPContext *context;
397
398         device = g_hash_table_lookup (self->priv->interfaces,
399                                       GINT_TO_POINTER (ifa->ifa_index));
400
401         if (!device)
402                 return;
403
404         context = g_hash_table_lookup (device->contexts, label);
405         if (context) {
406                 if (device->flags & NETWORK_INTERFACE_UP) {
407                         g_signal_emit_by_name (self,
408                                                "context-unavailable",
409                                                context);
410                 }
411                 g_hash_table_remove (device->contexts, label);
412         }
413
414         if (g_hash_table_size (device->contexts) == 0)
415                 device->flags &= ~NETWORK_INTERFACE_PRECONFIGURED;
416 }
417
418 /* Idle-handler for initial interface and address bootstrapping.
419  *
420  * We cannot send the RTM_GETADDR message until we processed all packets of
421  * the RTM_GETLINK message. So on the first call this idle handler processes
422  * all answers of RTM_GETLINK on the second call all answers of RTM_GETADDR
423  * and on the third call it creates the regular socket source for listening on
424  * the netlink socket, detaching itself from the idle source afterwards.
425  */
426 static gboolean
427 on_bootstrap (GUPnPLinuxContextManager *self)
428 {
429         if (self->priv->nl_seq == 0) {
430                 query_all_network_interfaces (self);
431
432                 return TRUE;
433         } else if (self->priv->nl_seq == 1) {
434                 query_all_addresses (self);
435
436                 return TRUE;
437         } else {
438                 self->priv->netlink_socket_source = g_socket_create_source
439                                                 (self->priv->netlink_socket,
440                                                  G_IO_IN | G_IO_PRI,
441                                                  NULL);
442
443                 g_source_attach (self->priv->netlink_socket_source,
444                                  g_main_context_get_thread_default ());
445
446                 g_source_set_callback (self->priv->netlink_socket_source,
447                                        (GSourceFunc)
448                                        on_netlink_message_available,
449                                        self,
450                                        NULL);
451         }
452
453         return FALSE;
454 }
455
456 struct nl_req_s {
457         struct nlmsghdr hdr;
458         struct rtgenmsg gen;
459 };
460
461 static void
462 send_netlink_request (GUPnPLinuxContextManager *self,
463                       guint netlink_message,
464                       guint flags)
465 {
466         struct nl_req_s req;
467         struct sockaddr_nl dest;
468         struct msghdr msg;
469         struct iovec io;
470         int fd;
471
472         memset (&req, 0, sizeof (req));
473         memset (&dest, 0, sizeof (dest));
474         memset (&msg, 0, sizeof (msg));
475
476         dest.nl_family = AF_NETLINK;
477         req.hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtgenmsg));
478         req.hdr.nlmsg_seq = self->priv->nl_seq++;
479         req.hdr.nlmsg_type = netlink_message;
480         req.hdr.nlmsg_flags = NLM_F_REQUEST | flags;
481         req.gen.rtgen_family = AF_INET;
482
483         io.iov_base = &req;
484         io.iov_len = req.hdr.nlmsg_len;
485
486         msg.msg_iov = &io;
487         msg.msg_iovlen = 1;
488         msg.msg_name = &dest;
489         msg.msg_namelen = sizeof (dest);
490
491         fd = g_socket_get_fd (self->priv->netlink_socket);
492         if (sendmsg (fd, (struct msghdr *) &msg, 0) < 0)
493                 g_warning ("Could not send netlink message: %s",
494                            strerror (errno));
495 }
496
497 /* Query all available interfaces and immediately process all answers. We need
498  * to do this to be able to send RTM_GETADDR in the next step */
499 static void
500 query_all_network_interfaces (GUPnPLinuxContextManager *self)
501 {
502         GError *error = NULL;
503
504         send_netlink_request (self, RTM_GETLINK, NLM_F_DUMP);
505         do {
506                 receive_netlink_message (self, &error);
507         } while (error == NULL);
508
509         g_error_free (error);
510 }
511
512 /* Start query of all currenly available network addresses. The answer will be
513  * processed by the normal netlink socket source call-back. */
514 static void
515 query_all_addresses (GUPnPLinuxContextManager *self)
516 {
517         send_netlink_request (self,
518                               RTM_GETADDR,
519                               NLM_F_ROOT | NLM_F_MATCH | NLM_F_ACK);
520 }
521
522 /* Ignore non-multicast device, except loop-back and P-t-P devices */
523 #define INTERFACE_IS_VALID(ifi) \
524         (((ifi)->ifi_flags & (IFF_MULTICAST | IFF_LOOPBACK)) && \
525          !((ifi)->ifi_flags & IFF_POINTOPOINT))
526
527 /* Handle status changes (up, down, new address, ...) on network interfaces */
528 static void
529 handle_device_status_change (GUPnPLinuxContextManager *self,
530                              struct ifinfomsg         *ifi)
531 {
532         gpointer key;
533         NetworkInterface *device;
534
535         key = GINT_TO_POINTER (ifi->ifi_index);
536         device = g_hash_table_lookup (self->priv->interfaces,
537                                       key);
538
539         if (device != NULL) {
540                 if (ifi->ifi_flags & IFF_UP)
541                         network_device_up (device);
542                 else
543                         network_device_down (device);
544
545                 return;
546         }
547
548         device = network_device_new (self, ifi->ifi_index);
549         if (device) {
550                 if (!INTERFACE_IS_VALID (ifi))
551                         device->flags |= NETWORK_INTERFACE_IGNORE;
552                 if (ifi->ifi_flags & IFF_UP)
553                         device->flags |= NETWORK_INTERFACE_UP;
554
555                 g_hash_table_insert (self->priv->interfaces,
556                                      key,
557                                      device);
558         }
559 }
560
561 static void
562 remove_device (GUPnPLinuxContextManager *self,
563                struct ifinfomsg         *ifi)
564 {
565         g_hash_table_remove (self->priv->interfaces,
566                              GINT_TO_POINTER (ifi->ifi_index));
567 }
568
569 #define NLMSG_IS_VALID(msg,len) \
570         (NLMSG_OK(msg,len) && (msg->nlmsg_type != NLMSG_DONE))
571
572 /* Process the raw netlink message and dispatch to helper functions
573  * accordingly */
574 static void
575 receive_netlink_message (GUPnPLinuxContextManager *self, GError **error)
576 {
577         static char buf[4096];
578         static const int bufsize = 4096;
579
580         gssize len;
581         GError *inner_error = NULL;
582         struct nlmsghdr *header = (struct nlmsghdr *) buf;
583         struct ifinfomsg *ifi;
584         struct ifaddrmsg *ifa;
585
586
587         len = g_socket_receive (self->priv->netlink_socket,
588                                 buf,
589                                 bufsize,
590                                 NULL,
591                                 &inner_error);
592         if (len == -1) {
593                 if (inner_error->code != G_IO_ERROR_WOULD_BLOCK)
594                         g_warning ("Error receiving netlink message: %s",
595                                    inner_error->message);
596                 g_propagate_error (error, inner_error);
597
598                 return;
599         }
600
601         for (;NLMSG_IS_VALID (header, len); header = NLMSG_NEXT (header,len)) {
602                 switch (header->nlmsg_type) {
603                         /* RTM_NEWADDR and RTM_DELADDR are sent on real address
604                          * changes.
605                          * RTM_NEWLINK is sent on varous occations:
606                          *  - Creation of a new device
607                          *  - Device goes up/down
608                          *  - Wireless status changes
609                          * RTM_DELLINK is sent only if device is removed, like
610                          * openvpn --rmtun /dev/tun0, NOT on ifconfig down. */
611                         case RTM_NEWADDR:
612                             {
613                                 char *label = NULL;
614
615                                 ifa = NLMSG_DATA (header);
616                                 extract_info (header, &label);
617                                 create_context (self, label, ifa);
618                                 g_free (label);
619                             }
620                             break;
621                         case RTM_DELADDR:
622                             {
623                                 char *label = NULL;
624
625                                 ifa = NLMSG_DATA (header);
626                                 extract_info (header, &label);
627                                 remove_context (self, label, ifa);
628                                 g_free (label);
629                             }
630                             break;
631                         case RTM_NEWLINK:
632                                 ifi = NLMSG_DATA (header);
633
634                                 /* Check if wireless is up for chit-chat */
635                                 if (is_wireless_status_message (header))
636                                         continue;
637                                 handle_device_status_change (self, ifi);
638                                 break;
639                         case RTM_DELLINK:
640                                 ifi = NLMSG_DATA (header);
641                                 remove_device (self, ifi);
642                                 break;
643                         case NLMSG_ERROR:
644                                 break;
645                         default:
646                                 break;
647                 }
648         }
649 }
650
651 /* Create INET socket used for SIOCGIFNAME and SIOCGIWESSID ioctl
652  * calls */
653 static gboolean
654 create_ioctl_socket (GUPnPLinuxContextManager *self, GError **error)
655 {
656         self->priv->fd = socket (AF_INET, SOCK_DGRAM, 0);
657
658         if (self->priv->fd < 0) {
659                 self->priv->fd = 0;
660
661                 g_set_error_literal (error,
662                                      G_IO_ERROR,
663                                      g_io_error_from_errno (errno),
664                                      "Failed to setup socket for ioctl");
665
666                 return FALSE;
667         }
668
669         return TRUE;
670 }
671
672 /* Create a netlink socket, bind to it and wrap it in a GSocket */
673 static gboolean
674 create_netlink_socket (GUPnPLinuxContextManager *self, GError **error)
675 {
676         struct sockaddr_nl sa;
677         int fd, status;
678         GSocket *sock;
679         GError *inner_error;
680
681         inner_error = NULL;
682
683         fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
684         if (fd == -1) {
685                 g_set_error_literal (error,
686                                      G_IO_ERROR,
687                                      g_io_error_from_errno (errno),
688                                      "Failed to bind to netlink socket");
689                 return FALSE;
690         }
691
692         memset (&sa, 0, sizeof (sa));
693         sa.nl_family = AF_NETLINK;
694         /* Listen for interface changes and IP address changes */
695         sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
696
697         status = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
698         if (status == -1) {
699                 g_set_error_literal (error,
700                                      G_IO_ERROR,
701                                      g_io_error_from_errno (errno),
702                                      "Failed to bind to netlink socket");
703                 close (fd);
704
705                 return FALSE;
706         }
707
708         sock = g_socket_new_from_fd (fd, &inner_error);
709         if (sock == NULL) {
710                 close (fd);
711                 g_propagate_prefixed_error (error,
712                                             inner_error,
713                                             "Failed to create GSocket from "
714                                             "netlink socket");
715
716                 return FALSE;
717         }
718
719         g_socket_set_blocking (sock, FALSE);
720
721         self->priv->netlink_socket = sock;
722
723         return TRUE;
724 }
725
726 /* public helper function to determine runtime-fallback depending on netlink
727  * availability. */
728 gboolean
729 gupnp_linux_context_manager_is_available (void)
730 {
731         int fd = -1;
732
733         fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
734
735         if (fd == -1)
736                 return FALSE;
737
738         close (fd);
739
740         return TRUE;
741 }
742
743 /* GObject virtual functions */
744
745 static void
746 gupnp_linux_context_manager_init (GUPnPLinuxContextManager *self)
747 {
748         self->priv =
749                 G_TYPE_INSTANCE_GET_PRIVATE (self,
750                                              GUPNP_TYPE_LINUX_CONTEXT_MANAGER,
751                                              GUPnPLinuxContextManagerPrivate);
752
753         self->priv->nl_seq = 0;
754
755         self->priv->interfaces =
756                 g_hash_table_new_full (g_direct_hash,
757                                        g_direct_equal,
758                                        NULL,
759                                        (GDestroyNotify) network_device_free);
760 }
761
762 /* Constructor, kicks off bootstrapping */
763 static void
764 gupnp_linux_context_manager_constructed (GObject *object)
765 {
766         GObjectClass *parent_class;
767         GUPnPLinuxContextManager *self;
768         GError *error = NULL;
769
770         self = GUPNP_LINUX_CONTEXT_MANAGER (object);
771
772         if (!create_ioctl_socket (self, &error))
773                 goto cleanup;
774
775         if (!create_netlink_socket (self, &error))
776                 goto cleanup;
777
778         self->priv->bootstrap_source =
779                                 g_idle_source_new ();
780         g_source_attach (self->priv->bootstrap_source,
781                          g_main_context_get_thread_default ());
782         g_source_set_callback (self->priv->bootstrap_source,
783                                (GSourceFunc) on_bootstrap,
784                                self,
785                                NULL);
786 cleanup:
787         if (error) {
788                 if (self->priv->fd > 0)
789                         close (self->priv->fd);
790
791                 g_warning ("Failed to setup Linux context manager: %s",
792                            error->message);
793
794                 g_error_free (error);
795         }
796
797         /* Chain-up */
798         parent_class = G_OBJECT_CLASS (gupnp_linux_context_manager_parent_class);
799         if (parent_class->constructed)
800                 parent_class->constructed (object);
801 }
802
803 static void
804 gupnp_linux_context_manager_dispose (GObject *object)
805 {
806         GUPnPLinuxContextManager *self;
807         GObjectClass *parent_class;
808
809         self = GUPNP_LINUX_CONTEXT_MANAGER (object);
810
811         if (self->priv->bootstrap_source != NULL) {
812                 g_source_destroy (self->priv->bootstrap_source);
813                 g_source_unref (self->priv->bootstrap_source);
814                 self->priv->bootstrap_source = NULL;
815         }
816
817         if (self->priv->netlink_socket_source != NULL) {
818                g_source_destroy (self->priv->netlink_socket_source);
819                g_source_unref (self->priv->netlink_socket_source);
820                self->priv->netlink_socket_source = NULL;
821         }
822
823         if (self->priv->netlink_socket != NULL) {
824                 g_object_unref (self->priv->netlink_socket);
825                 self->priv->netlink_socket = NULL;
826         }
827
828         if (self->priv->fd != 0) {
829                 close (self->priv->fd);
830                 self->priv->fd = 0;
831         }
832
833         if (self->priv->interfaces) {
834                 g_hash_table_destroy (self->priv->interfaces);
835                 self->priv->interfaces = NULL;
836         }
837
838         /* Chain-up */
839         parent_class = G_OBJECT_CLASS (gupnp_linux_context_manager_parent_class);
840         parent_class->dispose (object);
841 }
842
843 static void
844 gupnp_linux_context_manager_class_init (GUPnPLinuxContextManagerClass *klass)
845 {
846         GObjectClass *object_class;
847
848         object_class = G_OBJECT_CLASS (klass);
849
850         object_class->constructed = gupnp_linux_context_manager_constructed;
851         object_class->dispose     = gupnp_linux_context_manager_dispose;
852
853         g_type_class_add_private (klass,
854                                   sizeof (GUPnPLinuxContextManagerPrivate));
855 }