|
Packit Service |
26469c |
/*
|
|
Packit Service |
26469c |
* netlink.c - Kernel ACPI Event Netlink Interface
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* Handles the details of getting kernel ACPI events from netlink.
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* Inspired by (and in some cases blatantly lifted from) Zhang Rui's
|
|
Packit Service |
26469c |
* acpi_genl and Alexey Kuznetsov's libnetlink. Thanks also to Yi Yang
|
|
Packit Service |
26469c |
* at intel.
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* Copyright (C) 2008, Ted Felix (www.tedfelix.com)
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
26469c |
* it under the terms of the GNU General Public License as published by
|
|
Packit Service |
26469c |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit Service |
26469c |
* (at your option) any later version.
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
26469c |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
26469c |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
26469c |
* GNU General Public License for more details.
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
26469c |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
26469c |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
Packit Service |
26469c |
*
|
|
Packit Service |
26469c |
* (tabs at 4)
|
|
Packit Service |
26469c |
*/
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* system */
|
|
Packit Service |
26469c |
#include <unistd.h>
|
|
Packit Service |
26469c |
#include <stdio.h>
|
|
Packit Service |
26469c |
#include <stdlib.h>
|
|
Packit Service |
26469c |
#include <string.h>
|
|
Packit Service |
26469c |
#include <errno.h>
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* local */
|
|
Packit Service |
26469c |
#include "acpid.h"
|
|
Packit Service |
26469c |
#include "log.h"
|
|
Packit Service |
26469c |
#include "event.h"
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
#include "libnetlink.h"
|
|
Packit Service |
26469c |
#include "genetlink.h"
|
|
Packit Service |
26469c |
#include "acpi_genetlink.h"
|
|
Packit Service |
26469c |
#include "libc_compat.h"
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
#include "acpi_ids.h"
|
|
Packit Service |
26469c |
#include "connection_list.h"
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
#include "netlink.h"
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
static void
|
|
Packit Service |
26469c |
format_netlink(struct nlmsghdr *msg)
|
|
Packit Service |
26469c |
{
|
|
Packit Service |
26469c |
struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
|
|
Packit Service |
26469c |
struct genlmsghdr *ghdr = NLMSG_DATA(msg);
|
|
Packit Service |
26469c |
int len;
|
|
Packit Service |
26469c |
struct rtattr *attrs;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
len = msg->nlmsg_len;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* if this message doesn't have the proper family ID, drop it */
|
|
Packit Service |
26469c |
if (msg->nlmsg_type != acpi_ids_getfamily()) {
|
|
Packit Service |
26469c |
if (logevents) {
|
|
Packit Service |
26469c |
acpid_log(LOG_INFO, "wrong netlink family ID.");
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
if (len < 0) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING,
|
|
Packit Service |
26469c |
"wrong netlink controller message len: %d", len);
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
|
|
Packit Service |
26469c |
/* parse the attributes in this message */
|
|
Packit Service |
26469c |
parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* if there's an ACPI event attribute... */
|
|
Packit Service |
26469c |
if (tb[ACPI_GENL_ATTR_EVENT]) {
|
|
Packit Service |
26469c |
/* get the actual event struct */
|
|
Packit Service |
26469c |
struct acpi_genl_event *event =
|
|
Packit Service |
26469c |
RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]);
|
|
Packit Service |
26469c |
char buf[64];
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* format it */
|
|
Packit Service |
26469c |
snprintf(buf, sizeof(buf), "%s %s %08x %08x",
|
|
Packit Service |
26469c |
event->device_class, event->bus_id, event->type, event->data);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* if we're locked, don't process the event */
|
|
Packit Service |
26469c |
if (locked()) {
|
|
Packit Service |
26469c |
if (logevents) {
|
|
Packit Service |
26469c |
acpid_log(LOG_INFO,
|
|
Packit Service |
26469c |
"lockfile present, not processing "
|
|
Packit Service |
26469c |
"netlink event \"%s\"", buf);
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
if (logevents)
|
|
Packit Service |
26469c |
acpid_log(LOG_INFO,
|
|
Packit Service |
26469c |
"received netlink event \"%s\"", buf);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* send the event off to the handler */
|
|
Packit Service |
26469c |
acpid_handle_event(buf);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
if (logevents)
|
|
Packit Service |
26469c |
acpid_log(LOG_INFO,
|
|
Packit Service |
26469c |
"completed netlink event \"%s\"", buf);
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* (based on rtnl_listen() in libnetlink.c) */
|
|
Packit Service |
26469c |
static void
|
|
Packit Service |
26469c |
process_netlink(int fd)
|
|
Packit Service |
26469c |
{
|
|
Packit Service |
26469c |
int status;
|
|
Packit Service |
26469c |
struct nlmsghdr *h;
|
|
Packit Service |
26469c |
/* the address for recvmsg() */
|
|
Packit Service |
26469c |
struct sockaddr_nl nladdr;
|
|
Packit Service |
26469c |
/* the io vector for recvmsg() */
|
|
Packit Service |
26469c |
struct iovec iov;
|
|
Packit Service |
26469c |
/* recvmsg() parameters */
|
|
Packit Service |
26469c |
struct msghdr msg = {
|
|
Packit Service |
26469c |
.msg_name = &nladdr,
|
|
Packit Service |
26469c |
.msg_namelen = sizeof(nladdr),
|
|
Packit Service |
26469c |
.msg_iov = &iov,
|
|
Packit Service |
26469c |
.msg_iovlen = 1,
|
|
Packit Service |
26469c |
};
|
|
Packit Service |
26469c |
/* buffer for the incoming data */
|
|
Packit Service |
26469c |
char buf[8192];
|
|
Packit Service |
26469c |
static int nerrs;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* set up the netlink address */
|
|
Packit Service |
26469c |
memset(&nladdr, 0, sizeof(nladdr));
|
|
Packit Service |
26469c |
nladdr.nl_family = AF_NETLINK;
|
|
Packit Service |
26469c |
nladdr.nl_pid = 0;
|
|
Packit Service |
26469c |
nladdr.nl_groups = 0;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* set up the I/O vector */
|
|
Packit Service |
26469c |
iov.iov_base = buf;
|
|
Packit Service |
26469c |
iov.iov_len = sizeof(buf);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* read the data into the buffer */
|
|
Packit Service |
26469c |
status = TEMP_FAILURE_RETRY ( recvmsg(fd, &msg, MSG_CMSG_CLOEXEC) );
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* if there was a problem, print a message and keep trying */
|
|
Packit Service |
26469c |
if (status < 0) {
|
|
Packit Service |
26469c |
acpid_log(LOG_ERR, "netlink read error: %s (%d)",
|
|
Packit Service |
26469c |
strerror(errno), errno);
|
|
Packit Service |
26469c |
if (++nerrs >= ACPID_MAX_ERRS) {
|
|
Packit Service |
26469c |
acpid_log(LOG_ERR,
|
|
Packit Service |
26469c |
"too many errors reading via "
|
|
Packit Service |
26469c |
"netlink - aborting");
|
|
Packit Service |
26469c |
exit(EXIT_FAILURE);
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
/* if an orderly shutdown has occurred, we're done */
|
|
Packit Service |
26469c |
if (status == 0) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING, "netlink connection closed");
|
|
Packit Service |
26469c |
exit(EXIT_FAILURE);
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
/* check to see if the address length has changed */
|
|
Packit Service |
26469c |
if (msg.msg_namelen != sizeof(nladdr)) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING, "netlink unexpected length: "
|
|
Packit Service |
26469c |
"%d expected: %zd", msg.msg_namelen, sizeof(nladdr));
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* for each message received */
|
|
Packit Service |
26469c |
for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
|
|
Packit Service |
26469c |
int len = h->nlmsg_len;
|
|
Packit Service |
26469c |
int l = len - sizeof(*h);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
if (l < 0 || len > status) {
|
|
Packit Service |
26469c |
if (msg.msg_flags & MSG_TRUNC) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING, "netlink msg truncated (1)");
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING,
|
|
Packit Service |
26469c |
"malformed netlink msg, length %d", len);
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* format the message */
|
|
Packit Service |
26469c |
format_netlink(h);
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
status -= NLMSG_ALIGN(len);
|
|
Packit Service |
26469c |
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
if (msg.msg_flags & MSG_TRUNC) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING, "netlink msg truncated (2)");
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
if (status) {
|
|
Packit Service |
26469c |
acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* convert the netlink multicast group number into a bit map */
|
|
Packit Service |
26469c |
/* (e.g. 4 => 16, 5 => 32) */
|
|
Packit Service |
26469c |
static __u32
|
|
Packit Service |
26469c |
nl_mgrp(__u32 group)
|
|
Packit Service |
26469c |
{
|
|
Packit Service |
26469c |
if (group > 31) {
|
|
Packit Service |
26469c |
acpid_log(LOG_ERR, "Unexpected group number %d", group);
|
|
Packit Service |
26469c |
return 0;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
return group ? (1 << (group - 1)) : 0;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
void open_netlink(void)
|
|
Packit Service |
26469c |
{
|
|
Packit Service |
26469c |
struct rtnl_handle rth;
|
|
Packit Service |
26469c |
struct connection c;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* open the appropriate netlink socket for input */
|
|
Packit Service |
26469c |
if (rtnl_open_byproto(
|
|
Packit Service |
26469c |
&rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) {
|
|
Packit Service |
26469c |
acpid_log(LOG_ERR, "cannot open generic netlink socket");
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
acpid_log(LOG_DEBUG, "netlink opened successfully");
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
/* add a connection to the list */
|
|
Packit Service |
26469c |
c.fd = rth.fd;
|
|
Packit Service |
26469c |
c.process = process_netlink;
|
|
Packit Service |
26469c |
c.pathname = NULL;
|
|
Packit Service |
26469c |
c.kybd = 0;
|
|
Packit Service |
26469c |
|
|
Packit Service |
26469c |
if (add_connection(&c) < 0) {
|
|
Packit Service |
26469c |
rtnl_close(&rth);
|
|
Packit Service |
26469c |
acpid_log(LOG_ERR,
|
|
Packit Service |
26469c |
"can't add connection for generic netlink socket");
|
|
Packit Service |
26469c |
return;
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
}
|
|
Packit Service |
26469c |
|