Logo Search packages:      
Sourcecode: acpid version File versions  Download package

netlink.c

#include <acpid/driver/netlink.h>

/**
 *    low-level netlink functions
 */

int netlink_open(struct acpi_channel_private *private, int protocol, int groups)
{
      private->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
      if (private->fd < 0)
            return -1;

      memset(&private->local, 0, sizeof(private->local));
      private->local.nl_family = AF_NETLINK;
      private->local.nl_groups = groups;

      if (bind(private->fd, (struct sockaddr *)&private->local, sizeof(private->local)) < 0)
            goto fail;

      socklen_t len = sizeof(private->local);
      if (getsockname(private->fd, (struct sockaddr *)&private->local, &len) < 0)
            goto fail;

      private->seq = time(NULL);

      return 0;

fail:
      close(private->fd);
      return -1;
}

int netlink_send(struct acpi_channel_private *private, struct nlmsghdr *n, pid_t peer, int groups)
{
      struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = peer, .nl_groups = groups };

      n->nlmsg_seq = ++private->seq;
      return sendto(private->fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr));
}

int netlink_recv(struct acpi_channel_private *private, struct nlmsghdr *n, pid_t *peer)
{
      struct sockaddr_nl nladdr;
      socklen_t len = sizeof(nladdr);

      int ret = recvfrom(private->fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, &len);
      *peer = nladdr.nl_pid;

      return ret;
}

int netlink_wait(struct acpi_channel_private *private, struct nlmsghdr *n, pid_t peer)
{
      int len = n->nlmsg_len;

      for (;;) {
            pid_t sender;

            n->nlmsg_len = len;
            int ret = netlink_recv(private, n, &sender);
            if (ret < 0 || sender != peer)
                  continue;

            for (struct nlmsghdr * h = n; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
                  if (h->nlmsg_pid != private->local.nl_pid || h->nlmsg_seq != private->seq)
                        continue;

                  if (h->nlmsg_type == NLMSG_ERROR)
                        return -1;

                  memcpy(n, h, h->nlmsg_len);
                  return 0;
            }
      }
}

void netlink_close(struct acpi_channel_private *private)
{
      close(private->fd);
}

/**
 *    netlink message attributes
 */

int netlink_attr_attach(struct nlmsghdr *n, int max, int type, const void *data, int alen)
{
      int len = NLA_LENGTH(alen);
      struct nlattr *nla;

      if (NLMSG_ALIGN(n->nlmsg_len) + NLA_ALIGN(len) > max)
            return -1;

      nla = NLMSG_TAIL(n);
      nla->nla_type = type;
      nla->nla_len = len;
      memcpy(NLA_DATA(nla), data, alen);
      n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLA_ALIGN(len);
      return 0;
}

int netlink_attr_parse(struct nlattr *table[], int max, struct nlattr *src, int len)
{
      memset(table, 0, sizeof(struct nlattr *) * (max + 1));

      while (NLA_OK(src, len)) {
            if (src->nla_type <= max)
                  table[src->nla_type] = src;
            src = NLA_NEXT(src, len);
      }
      return 0;
}


/**
 *    acpi netlink broadcast group discovery
 */

static const char *acpi_family_name = "acpi_event";
static const char *acpi_group_name = "acpi_mc_group";

static int acapi_netlink_group(struct nlattr *attr, int *group)
{
      if (attr == NULL)
            return -1;

      struct nlattr *attrs[CTRL_ATTR_MCAST_GRP_MAX + 1];
      netlink_attr_parse(attrs, CTRL_ATTR_MCAST_GRP_MAX, NLA_DATA(attr), attr->nla_len - NLA_HDRLEN);

      const char *name = NLA_DATA(attrs[CTRL_ATTR_MCAST_GRP_NAME]);
      if (strcmp(name, acpi_group_name))
            return -1;

      *group = *(__u32 *) (NLA_DATA(attrs[CTRL_ATTR_MCAST_GRP_ID]));
      return 0;
}


static int acpi_netlink_family(struct nlmsghdr *n, int *family, int *group)
{
      if (n->nlmsg_type != GENL_ID_CTRL)
            return -1;
      struct genlmsghdr *ghdr = NLMSG_DATA(n);

      if (ghdr->cmd != CTRL_CMD_NEWFAMILY)
            return -1;

      if (n->nlmsg_len < NLMSG_LENGTH(GENL_HDRLEN))
            return -1;

      struct nlattr *attrs[CTRL_ATTR_MAX + 1];
      netlink_attr_parse(attrs, CTRL_ATTR_MAX, NLMSG_DATA(n) + GENL_HDRLEN, NLMSG_PAYLOAD(n, GENL_HDRLEN));

      if (attrs[CTRL_ATTR_FAMILY_ID])
            *family = *(__u32 *) (NLA_DATA(attrs[CTRL_ATTR_FAMILY_ID]));

      if (attrs[CTRL_ATTR_MCAST_GROUPS]) {
            struct nlattr *attrs2[GENL_MAX_FAM_GRPS + 1];
            netlink_attr_parse(attrs2, GENL_MAX_FAM_GRPS, NLA_DATA(attrs[CTRL_ATTR_MCAST_GROUPS]), attrs[CTRL_ATTR_MCAST_GROUPS]->nla_len - NLA_HDRLEN);

            for (int i = 0; i < GENL_MAX_FAM_GRPS; i++) {
                  if (acapi_netlink_group(attrs2[i], group) == 0)
                        return 0;
            }
      }

      return 0;
}

int acpi_netlink_event(struct acpi_channel *channel, struct acpi_genl_event *events, int max)
{
      struct acpi_channel_private *private = channel->private;

      char buffer[256];
      struct nlmsghdr *n = (struct nlmsghdr *) &buffer;
      n->nlmsg_len = 256;

      pid_t sender;
      int ret = netlink_recv(private, n, &sender);
      if (ret < 0)
            return -1;

      int i = 0;
      for (struct nlmsghdr * h = n; NLMSG_OK(h, ret) && i < max; h = NLMSG_NEXT(h, ret), ++i) {
            if (h->nlmsg_type == NLMSG_ERROR)
                  return -1;

            struct nlattr *attrs[ACPI_GENL_ATTR_MAX + 1];
            netlink_attr_parse(attrs, ACPI_GENL_ATTR_MAX, NLMSG_DATA(h) + GENL_HDRLEN, NLMSG_PAYLOAD(h, GENL_HDRLEN));

            if (attrs[ACPI_GENL_ATTR_EVENT]) {
                  struct acpi_genl_event *event = NLA_DATA(attrs[ACPI_GENL_ATTR_EVENT]);
                  memcpy(events + i, event, sizeof(struct acpi_genl_event));
            }
      }

      return i;
}



/**
 *    acpi netlink channel
 */

static int setup(struct acpi_channel *channel, struct acpi_channel_descriptor *cds, unsigned long num)
{
      struct acpi_channel_private *private = (struct acpi_channel_private *) channel->private;

      acpi_channel_watch(channel, cds, private->fd, POLLIN);

      return 1;
}

static int handle(struct acpi_channel *channel, lua_State *L, int fd, int events)
{
      struct acpi_channel_private *private = channel->private;

      for (;;) {
            struct acpi_genl_event e;
            int ret = acpi_netlink_event(channel, &e, 1);
            if (ret <= 0)
                  return ret;

            printf("got event: %20s %15s %08x %08x\n", e.device_class, e.bus_id, e.type, e.data);

            //*strchr(e.bus_id, ':') = 0;
            acpi_channel_event(channel, L, e.bus_id, e.type, e.data);
      }

      return 0;
}


static int create(struct acpi_channel *channel, lua_State *L)
{
      static const struct acpi_channel_ops ops = { setup, handle };
      acpi_channel_register(channel, &ops);

      lua_pushinteger(L, 1);
      lua_gettable(L, -2);
      int ret = strcmp(lua_tostring(L, -1), "acpi");
      lua_pop(L, 1);

      if (ret)
            return -1;

      channel->private = malloc(sizeof(struct acpi_channel_private));
      if (channel->private == NULL)
            return -1;

      struct acpi_channel_private *private = channel->private;
      if (netlink_open(private, NETLINK_GENERIC, 0))
            return -1;

      char buffer[256];
      struct nlmsghdr *nlmsg = (struct nlmsghdr *)&buffer;

      nlmsg->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
      nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
      nlmsg->nlmsg_type = GENL_ID_CTRL;

      struct genlmsghdr *ghdr = NLMSG_DATA(nlmsg);
      ghdr->cmd = CTRL_CMD_GETFAMILY;

      netlink_attr_attach(nlmsg, 128, CTRL_ATTR_FAMILY_NAME, acpi_family_name, strlen(acpi_family_name) + 1);

      if (netlink_send(private, nlmsg, 0, 0) < 0)
            return -1;

      nlmsg->nlmsg_len = 256;
      if (netlink_wait(private, nlmsg, 0) < 0)
            return -1;

      int family, group;
      if (acpi_netlink_family(nlmsg, &family, &group) < 0)
            return -1;

      netlink_close(private);
      if (netlink_open(private, NETLINK_GENERIC, group ? (1 << (group - 1)) : 0))
            return -1;

      int flags = fcntl(private->fd, F_GETFL, 0);
      fcntl(private->fd, F_SETFL, flags | O_NONBLOCK);

      return 0;

  ctrl_done:
      netlink_close(private);
      return -1;
}

static void destroy(struct acpi_channel *channel)
{
      struct acpi_channel_private *private = channel->private;

      close(private->fd);
      free(channel->private);
}



/*
 *    driver constuctor
 */

static __attribute__((constructor)) void constructor()
{
      static const struct acpi_driver driver = { "netlink", { create, destroy } };
      acpi_driver_register(&driver);
}


Generated by  Doxygen 1.6.0   Back to index