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"
51 format_netlink(struct nlmsghdr *msg)
53 struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
54 struct genlmsghdr *ghdr = NLMSG_DATA(msg);
60 /* if this message doesn't have the proper family ID, drop it */
61 if (msg->nlmsg_type != acpi_ids_getfamily()) {
63 acpid_log(LOG_INFO, "wrong netlink family ID.");
68 len -= NLMSG_LENGTH(GENL_HDRLEN);
71 acpid_log(LOG_WARNING,
72 "wrong netlink controller message len: %d", len);
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);
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]);
88 snprintf(buf, sizeof(buf), "%s %s %08x %08x",
89 event->device_class, event->bus_id, event->type, event->data);
91 /* if we're locked, don't process the event */
95 "lockfile present, not processing "
96 "netlink event \"%s\"", buf);
103 "received netlink event \"%s\"", buf);
105 /* send the event off to the handler */
106 acpid_handle_event(buf);
110 "completed netlink event \"%s\"", buf);
114 /* (based on rtnl_listen() in libnetlink.c) */
116 process_netlink(int fd)
120 /* the address for recvmsg() */
121 struct sockaddr_nl nladdr;
122 /* the io vector for recvmsg() */
124 /* recvmsg() parameters */
125 struct msghdr msg = {
127 .msg_namelen = sizeof(nladdr),
131 /* buffer for the incoming data */
135 /* set up the netlink address */
136 memset(&nladdr, 0, sizeof(nladdr));
137 nladdr.nl_family = AF_NETLINK;
139 nladdr.nl_groups = 0;
141 /* set up the I/O vector */
143 iov.iov_len = sizeof(buf);
145 /* read the data into the buffer */
146 status = TEMP_FAILURE_RETRY ( recvmsg(fd, &msg, MSG_CMSG_CLOEXEC) );
148 /* if there was a problem, print a message and keep trying */
150 acpid_log(LOG_ERR, "netlink read error: %s (%d)",
151 strerror(errno), errno);
152 if (++nerrs >= ACPID_MAX_ERRS) {
154 "too many errors reading via "
155 "netlink - aborting");
160 /* if an orderly shutdown has occurred, we're done */
162 acpid_log(LOG_WARNING, "netlink connection closed");
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));
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);
177 if (l < 0 || len > status) {
178 if (msg.msg_flags & MSG_TRUNC) {
179 acpid_log(LOG_WARNING, "netlink msg truncated (1)");
182 acpid_log(LOG_WARNING,
183 "malformed netlink msg, length %d", len);
187 /* format the message */
190 status -= NLMSG_ALIGN(len);
191 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
193 if (msg.msg_flags & MSG_TRUNC) {
194 acpid_log(LOG_WARNING, "netlink msg truncated (2)");
198 acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
205 /* convert the netlink multicast group number into a bit map */
206 /* (e.g. 4 => 16, 5 => 32) */
211 acpid_log(LOG_ERR, "Unexpected group number %d", group);
214 return group ? (1 << (group - 1)) : 0;
217 void open_netlink(void)
219 struct rtnl_handle rth;
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");
229 acpid_log(LOG_DEBUG, "netlink opened successfully");
231 /* add a connection to the list */
233 c.process = process_netlink;
237 if (add_connection(&c) < 0) {
240 "can't add connection for generic netlink socket");