fix systemd unit install path
[external/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 static void
49 format_netlink(struct nlmsghdr *msg)
50 {
51         struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
52         struct genlmsghdr *ghdr = NLMSG_DATA(msg);
53         int len;
54         struct rtattr *attrs;
55         
56         len = msg->nlmsg_len;
57         
58         /* if this message doesn't have the proper family ID, drop it */
59         if (msg->nlmsg_type != acpi_ids_getfamily()) {
60                 if (logevents) {
61                         acpid_log(LOG_INFO, "wrong netlink family ID.");
62                 }
63                 return;
64         }
65
66         len -= NLMSG_LENGTH(GENL_HDRLEN);
67
68         if (len < 0) {
69                 acpid_log(LOG_WARNING,
70                         "wrong netlink controller message len: %d", len);
71                 return;
72         }
73
74         attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
75         /* parse the attributes in this message */
76         parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len);
77
78         /* if there's an ACPI event attribute... */
79         if (tb[ACPI_GENL_ATTR_EVENT]) {
80                 /* get the actual event struct */
81                 struct acpi_genl_event *event =
82                                 RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]);
83                 char buf[64];
84
85                 /* format it */
86                 snprintf(buf, sizeof(buf), "%s %s %08x %08x",
87                         event->device_class, event->bus_id, event->type, event->data);
88
89                 /* if we're locked, don't process the event */
90                 if (locked()) {
91                         if (logevents) {
92                                 acpid_log(LOG_INFO,
93                                         "lockfile present, not processing "
94                                         "netlink event \"%s\"", buf);
95                         }
96                         return;
97                 }
98
99                 if (logevents)
100                         acpid_log(LOG_INFO,
101                                 "received netlink event \"%s\"", buf);
102
103                 /* send the event off to the handler */
104                 acpid_handle_event(buf);
105
106                 if (logevents)
107                         acpid_log(LOG_INFO,
108                                 "completed netlink event \"%s\"", buf);
109         }
110 }
111
112 /* (based on rtnl_listen() in libnetlink.c) */
113 void
114 process_netlink(int fd)
115 {
116         int status;
117         struct nlmsghdr *h;
118         /* the address for recvmsg() */
119         struct sockaddr_nl nladdr;
120         /* the io vector for recvmsg() */
121         struct iovec iov;
122         /* recvmsg() parameters */
123         struct msghdr msg = {
124                 .msg_name = &nladdr,
125                 .msg_namelen = sizeof(nladdr),
126                 .msg_iov = &iov,
127                 .msg_iovlen = 1,
128         };
129         /* buffer for the incoming data */
130         char buf[8192];
131         static int nerrs;
132
133         /* set up the netlink address */
134         memset(&nladdr, 0, sizeof(nladdr));
135         nladdr.nl_family = AF_NETLINK;
136         nladdr.nl_pid = 0;
137         nladdr.nl_groups = 0;
138
139         /* set up the I/O vector */
140         iov.iov_base = buf;
141         iov.iov_len = sizeof(buf);
142         
143         /* read the data into the buffer */
144         status = recvmsg(fd, &msg, 0);
145
146         /* if there was a problem, print a message and keep trying */
147         if (status < 0) {
148                 /* if we were interrupted by a signal, bail */
149                 if (errno == EINTR)
150                         return;
151                 
152                 acpid_log(LOG_ERR, "netlink read error: %s (%d)",
153                         strerror(errno), errno);
154                 if (++nerrs >= ACPID_MAX_ERRS) {
155                         acpid_log(LOG_ERR,
156                                 "too many errors reading via "
157                                 "netlink - aborting");
158                         exit(EXIT_FAILURE);
159                 }
160                 return;
161         }
162         /* if an orderly shutdown has occurred, we're done */
163         if (status == 0) {
164                 acpid_log(LOG_WARNING, "netlink connection closed");
165                 exit(EXIT_FAILURE);
166         }
167         /* check to see if the address length has changed */
168         if (msg.msg_namelen != sizeof(nladdr)) {
169                 acpid_log(LOG_WARNING, "netlink unexpected length: "
170                         "%d   expected: %d", msg.msg_namelen, sizeof(nladdr));
171                 return;
172         }
173         
174         /* for each message received */
175         for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
176                 int len = h->nlmsg_len;
177                 int l = len - sizeof(*h);
178
179                 if (l < 0  ||  len > status) {
180                         if (msg.msg_flags & MSG_TRUNC) {
181                                 acpid_log(LOG_WARNING, "netlink msg truncated (1)");
182                                 return;
183                         }
184                         acpid_log(LOG_WARNING,
185                                 "malformed netlink msg, length %d", len);
186                         return;
187                 }
188
189                 /* format the message */
190                 format_netlink(h);
191
192                 status -= NLMSG_ALIGN(len);
193                 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
194         }
195         if (msg.msg_flags & MSG_TRUNC) {
196                 acpid_log(LOG_WARNING, "netlink msg truncated (2)");
197                 return;
198         }
199         if (status) {
200                 acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
201                 return;
202         }
203
204         return;
205 }
206
207 /* convert the netlink multicast group number into a bit map */
208 /* (e.g. 4 => 16, 5 => 32) */
209 static __u32
210 nl_mgrp(__u32 group)
211 {
212         if (group > 31) {
213                 acpid_log(LOG_ERR, "Unexpected group number %d", group);
214                 return 0;
215         }
216         return group ? (1 << (group - 1)) : 0;
217 }
218
219 void open_netlink(void)
220 {
221         struct rtnl_handle rth;
222         struct connection c;
223
224         /* open the appropriate netlink socket for input */
225         if (rtnl_open_byproto(
226                 &rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) {
227                 acpid_log(LOG_ERR, "cannot open generic netlink socket");
228                 return;
229         }
230
231         acpid_log(LOG_DEBUG, "netlink opened successfully");
232
233         /* add a connection to the list */
234         c.fd = rth.fd;
235         c.process = process_netlink;
236         c.pathname = NULL;
237         c.kybd = 0;
238         add_connection(&c);
239 }
240