2 * netlink.c - Kernel ACPI Event Netlink Interface
4 * Handles the details of getting kernel ACPI events from netlink.
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
10 * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
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.
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.
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
41 #include "libnetlink.h"
42 #include "genetlink.h"
43 #include "acpi_genetlink.h"
46 #include "connection_list.h"
49 format_netlink(struct nlmsghdr *msg)
51 struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
52 struct genlmsghdr *ghdr = NLMSG_DATA(msg);
58 /* if this message doesn't have the proper family ID, drop it */
59 if (msg->nlmsg_type != acpi_ids_getfamily()) {
61 acpid_log(LOG_INFO, "wrong netlink family ID.");
66 len -= NLMSG_LENGTH(GENL_HDRLEN);
69 acpid_log(LOG_WARNING,
70 "wrong netlink controller message len: %d", len);
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);
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]);
86 snprintf(buf, sizeof(buf), "%s %s %08x %08x",
87 event->device_class, event->bus_id, event->type, event->data);
89 /* if we're locked, don't process the event */
93 "lockfile present, not processing "
94 "netlink event \"%s\"", buf);
101 "received netlink event \"%s\"", buf);
103 /* send the event off to the handler */
104 acpid_handle_event(buf);
108 "completed netlink event \"%s\"", buf);
112 /* (based on rtnl_listen() in libnetlink.c) */
114 process_netlink(int fd)
118 /* the address for recvmsg() */
119 struct sockaddr_nl nladdr;
120 /* the io vector for recvmsg() */
122 /* recvmsg() parameters */
123 struct msghdr msg = {
125 .msg_namelen = sizeof(nladdr),
129 /* buffer for the incoming data */
133 /* set up the netlink address */
134 memset(&nladdr, 0, sizeof(nladdr));
135 nladdr.nl_family = AF_NETLINK;
137 nladdr.nl_groups = 0;
139 /* set up the I/O vector */
141 iov.iov_len = sizeof(buf);
143 /* read the data into the buffer */
144 status = recvmsg(fd, &msg, 0);
146 /* if there was a problem, print a message and keep trying */
148 /* if we were interrupted by a signal, bail */
152 acpid_log(LOG_ERR, "netlink read error: %s (%d)",
153 strerror(errno), errno);
154 if (++nerrs >= ACPID_MAX_ERRS) {
156 "too many errors reading via "
157 "netlink - aborting");
162 /* if an orderly shutdown has occurred, we're done */
164 acpid_log(LOG_WARNING, "netlink connection closed");
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));
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);
179 if (l < 0 || len > status) {
180 if (msg.msg_flags & MSG_TRUNC) {
181 acpid_log(LOG_WARNING, "netlink msg truncated (1)");
184 acpid_log(LOG_WARNING,
185 "malformed netlink msg, length %d", len);
189 /* format the message */
192 status -= NLMSG_ALIGN(len);
193 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
195 if (msg.msg_flags & MSG_TRUNC) {
196 acpid_log(LOG_WARNING, "netlink msg truncated (2)");
200 acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
207 /* convert the netlink multicast group number into a bit map */
208 /* (e.g. 4 => 16, 5 => 32) */
213 acpid_log(LOG_ERR, "Unexpected group number %d", group);
216 return group ? (1 << (group - 1)) : 0;
219 void open_netlink(void)
221 struct rtnl_handle rth;
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");
231 acpid_log(LOG_DEBUG, "netlink opened successfully");
233 /* add a connection to the list */
235 c.process = process_netlink;