Blame netlink.c

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