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