]> git.meshlink.io Git - catta/blob - libavahi-core/netlink.c
92ed4367e40943c8f6a0d6d4d7ac926fdf4a5443
[catta] / libavahi-core / netlink.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29
30 #include "netlink.h"
31
32 struct _AvahiNetlink {
33     GMainContext *context;
34     gint fd;
35     guint seq;
36     GPollFD poll_fd;
37     GSource *source;
38     void (*callback) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata);
39     gpointer userdata;
40 };
41
42 gboolean avahi_netlink_work(AvahiNetlink *nl, gboolean block) {
43     g_assert(nl);
44
45     for (;;) {
46         guint8 replybuf[64*1024];
47         ssize_t bytes;
48         struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
49
50         if ((bytes = recv(nl->fd, replybuf, sizeof(replybuf), block ? 0 : MSG_DONTWAIT)) < 0) {
51
52             if (errno == EAGAIN || errno == EINTR)
53                 break;
54
55             g_warning("NETLINK: recv() failed");
56             return FALSE;
57         }
58
59         if (nl->callback) {
60             for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
61                 if (!NLMSG_OK(p, (size_t) bytes)) {
62                     g_warning("NETLINK: packet truncated");
63                     return FALSE;
64                 }
65
66                 nl->callback(nl, p, nl->userdata);
67             }
68         }
69
70         if (block)
71             break;
72     }
73
74     return TRUE;
75 }
76
77 static gboolean prepare_func(GSource *source, gint *timeout) {
78     g_assert(source);
79     g_assert(timeout);
80     
81     *timeout = -1;
82     return FALSE;
83 }
84
85 static gboolean check_func(GSource *source) {
86     AvahiNetlink* nl;
87     g_assert(source);
88
89     nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource)));
90     g_assert(nl);
91     
92     return nl->poll_fd.revents & (G_IO_IN|G_IO_HUP|G_IO_ERR);
93 }
94
95 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
96     AvahiNetlink* nl;
97     g_assert(source);
98
99     nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource)));
100     g_assert(nl);
101     
102     return avahi_netlink_work(nl, FALSE);
103 }
104
105 AvahiNetlink *avahi_netlink_new(GMainContext *context, gint priority, guint32 groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata), gpointer userdata) {
106     int fd;
107     struct sockaddr_nl addr;
108     AvahiNetlink *nl;
109
110     static GSourceFuncs source_funcs = {
111         prepare_func,
112         check_func,
113         dispatch_func,
114         NULL,
115         NULL,
116         NULL
117     };
118     
119     g_assert(context);
120     g_assert(cb);
121
122     if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
123         g_critical("NETLINK: socket(PF_NETLINK): %s", strerror(errno));
124         return NULL;
125     }
126     
127     memset(&addr, 0, sizeof(addr));
128     addr.nl_family = AF_NETLINK;
129     addr.nl_groups = groups;
130     addr.nl_pid = getpid();
131
132     if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
133         close(fd);
134         g_critical("bind(): %s", strerror(errno));
135         return NULL;
136     }
137
138     nl = g_new(AvahiNetlink, 1);
139     nl->context = context;
140     g_main_context_ref(context);
141     nl->fd = fd;
142     nl->seq = 0;
143     nl->callback = cb;
144     nl->userdata = userdata;
145
146     nl->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiNetlink*));
147     *((AvahiNetlink**) (((guint8*) nl->source) + sizeof(GSource))) = nl;
148
149     g_source_set_priority(nl->source, priority);
150     
151     memset(&nl->poll_fd, 0, sizeof(GPollFD));
152     nl->poll_fd.fd = fd;
153     nl->poll_fd.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
154     g_source_add_poll(nl->source, &nl->poll_fd);
155     
156     g_source_attach(nl->source, nl->context);
157     
158     return nl;
159 }
160
161 void avahi_netlink_free(AvahiNetlink *nl) {
162     g_assert(nl);
163     
164     g_source_destroy(nl->source);
165     g_source_unref(nl->source);
166     g_main_context_unref(nl->context);
167     close(nl->fd);
168     g_free(nl);
169 }
170
171 int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, guint *ret_seq) {
172     g_assert(nl);
173     g_assert(m);
174     
175     m->nlmsg_seq = nl->seq++;
176     m->nlmsg_flags |= NLM_F_ACK;
177
178     if (send(nl->fd, m, m->nlmsg_len, 0) < 0) {
179         g_warning("NETLINK: send(): %s\n", strerror(errno));
180         return -1;
181     }
182
183     if (ret_seq)
184         *ret_seq = m->nlmsg_seq;
185
186     return 0;
187 }