Imported Upstream version 2.0.22
[platform/upstream/acpid.git] / netlink.c
1 /*
2  *  netlink.c - Kernel ACPI Event Netlink Interface
3  *
4  *  Handles the details of getting kernel ACPI events from netlink.
5  *
6  *  Inspired by (and in some cases blatantly lifted from) Zhang Rui's
7  *  acpi_genl and Alexey Kuznetsov's libnetlink.  Thanks also to Yi Yang
8  *  at intel.
9  *
10  *  Copyright (C) 2008, Ted Felix (www.tedfelix.com)
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  *
26  *  (tabs at 4)
27  */
28
29 /* system */
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 /* local */
37 #include "acpid.h"
38 #include "log.h"
39 #include "event.h"
40
41 #include "libnetlink.h"
42 #include "genetlink.h"
43 #include "acpi_genetlink.h"
44
45 #include "acpi_ids.h"
46 #include "connection_list.h"
47
48 #include "netlink.h"
49
50 static void
51 format_netlink(struct nlmsghdr *msg)
52 {
53         struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
54         struct genlmsghdr *ghdr = NLMSG_DATA(msg);
55         int len;
56         struct rtattr *attrs;
57         
58         len = msg->nlmsg_len;
59         
60         /* if this message doesn't have the proper family ID, drop it */
61         if (msg->nlmsg_type != acpi_ids_getfamily()) {
62                 if (logevents) {
63                         acpid_log(LOG_INFO, "wrong netlink family ID.");
64                 }
65                 return;
66         }
67
68         len -= NLMSG_LENGTH(GENL_HDRLEN);
69
70         if (len < 0) {
71                 acpid_log(LOG_WARNING,
72                         "wrong netlink controller message len: %d", len);
73                 return;
74         }
75
76         attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
77         /* parse the attributes in this message */
78         parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len);
79
80         /* if there's an ACPI event attribute... */
81         if (tb[ACPI_GENL_ATTR_EVENT]) {
82                 /* get the actual event struct */
83                 struct acpi_genl_event *event =
84                                 RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]);
85                 char buf[64];
86
87                 /* format it */
88                 snprintf(buf, sizeof(buf), "%s %s %08x %08x",
89                         event->device_class, event->bus_id, event->type, event->data);
90
91                 /* if we're locked, don't process the event */
92                 if (locked()) {
93                         if (logevents) {
94                                 acpid_log(LOG_INFO,
95                                         "lockfile present, not processing "
96                                         "netlink event \"%s\"", buf);
97                         }
98                         return;
99                 }
100
101                 if (logevents)
102                         acpid_log(LOG_INFO,
103                                 "received netlink event \"%s\"", buf);
104
105                 /* send the event off to the handler */
106                 acpid_handle_event(buf);
107
108                 if (logevents)
109                         acpid_log(LOG_INFO,
110                                 "completed netlink event \"%s\"", buf);
111         }
112 }
113
114 /* (based on rtnl_listen() in libnetlink.c) */
115 static void
116 process_netlink(int fd)
117 {
118         int status;
119         struct nlmsghdr *h;
120         /* the address for recvmsg() */
121         struct sockaddr_nl nladdr;
122         /* the io vector for recvmsg() */
123         struct iovec iov;
124         /* recvmsg() parameters */
125         struct msghdr msg = {
126                 .msg_name = &nladdr,
127                 .msg_namelen = sizeof(nladdr),
128                 .msg_iov = &iov,
129                 .msg_iovlen = 1,
130         };
131         /* buffer for the incoming data */
132         char buf[8192];
133         static int nerrs;
134
135         /* set up the netlink address */
136         memset(&nladdr, 0, sizeof(nladdr));
137         nladdr.nl_family = AF_NETLINK;
138         nladdr.nl_pid = 0;
139         nladdr.nl_groups = 0;
140
141         /* set up the I/O vector */
142         iov.iov_base = buf;
143         iov.iov_len = sizeof(buf);
144         
145         /* read the data into the buffer */
146         status = TEMP_FAILURE_RETRY ( recvmsg(fd, &msg, MSG_CMSG_CLOEXEC) );
147
148         /* if there was a problem, print a message and keep trying */
149         if (status < 0) {
150                 acpid_log(LOG_ERR, "netlink read error: %s (%d)",
151                         strerror(errno), errno);
152                 if (++nerrs >= ACPID_MAX_ERRS) {
153                         acpid_log(LOG_ERR,
154                                 "too many errors reading via "
155                                 "netlink - aborting");
156                         exit(EXIT_FAILURE);
157                 }
158                 return;
159         }
160         /* if an orderly shutdown has occurred, we're done */
161         if (status == 0) {
162                 acpid_log(LOG_WARNING, "netlink connection closed");
163                 exit(EXIT_FAILURE);
164         }
165         /* check to see if the address length has changed */
166         if (msg.msg_namelen != sizeof(nladdr)) {
167                 acpid_log(LOG_WARNING, "netlink unexpected length: "
168                         "%d   expected: %zd", msg.msg_namelen, sizeof(nladdr));
169                 return;
170         }
171         
172         /* for each message received */
173         for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
174                 int len = h->nlmsg_len;
175                 int l = len - sizeof(*h);
176
177                 if (l < 0  ||  len > status) {
178                         if (msg.msg_flags & MSG_TRUNC) {
179                                 acpid_log(LOG_WARNING, "netlink msg truncated (1)");
180                                 return;
181                         }
182                         acpid_log(LOG_WARNING,
183                                 "malformed netlink msg, length %d", len);
184                         return;
185                 }
186
187                 /* format the message */
188                 format_netlink(h);
189
190                 status -= NLMSG_ALIGN(len);
191                 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
192         }
193         if (msg.msg_flags & MSG_TRUNC) {
194                 acpid_log(LOG_WARNING, "netlink msg truncated (2)");
195                 return;
196         }
197         if (status) {
198                 acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
199                 return;
200         }
201
202         return;
203 }
204
205 /* convert the netlink multicast group number into a bit map */
206 /* (e.g. 4 => 16, 5 => 32) */
207 static __u32
208 nl_mgrp(__u32 group)
209 {
210         if (group > 31) {
211                 acpid_log(LOG_ERR, "Unexpected group number %d", group);
212                 return 0;
213         }
214         return group ? (1 << (group - 1)) : 0;
215 }
216
217 void open_netlink(void)
218 {
219         struct rtnl_handle rth;
220         struct connection c;
221
222         /* open the appropriate netlink socket for input */
223         if (rtnl_open_byproto(
224                 &rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) {
225                 acpid_log(LOG_ERR, "cannot open generic netlink socket");
226                 return;
227         }
228
229         acpid_log(LOG_DEBUG, "netlink opened successfully");
230
231         /* add a connection to the list */
232         c.fd = rth.fd;
233         c.process = process_netlink;
234         c.pathname = NULL;
235         c.kybd = 0;
236
237         if (add_connection(&c) < 0) {
238                 rtnl_close(&rth);
239                 acpid_log(LOG_ERR,
240                         "can't add connection for generic netlink socket");
241                 return;
242         }
243 }
244