fix systemd unit install path
[external/acpid.git] / acpi_ids.c
1 /*
2  *  acpi_ids.c - ACPI Netlink Group and Family IDs
3  *
4  *  Copyright (C) 2008 Ted Felix (www.tedfelix.com)
5  *  Portions from acpi_genl Copyright (C) Zhang Rui <rui.zhang@intel.com>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdio.h>
23 /* needed by netlink.h, should be in there */
24 #include <arpa/inet.h>
25 #include <linux/types.h>
26 #include <string.h>
27
28 #include "genetlink.h"
29 #include "libnetlink.h"
30
31 #include "acpid.h"
32
33 #define GENL_MAX_FAM_GRPS       256
34 #define ACPI_EVENT_FAMILY_NAME          "acpi_event"
35 #define ACPI_EVENT_MCAST_GROUP_NAME     "acpi_mc_group"
36
37 static int initialized = 0;
38 static __u16 acpi_event_family_id = 0;
39 static __u32 acpi_event_mcast_group_id = 0;
40
41 /*
42  *  A CTRL_CMD_GETFAMILY message returns an attribute table that looks
43  *    like this:
44  *
45  *  CTRL_ATTR_FAMILY_ID         Use this to make sure we get the proper msgs
46  *  CTRL_ATTR_MCAST_GROUPS
47  *    CTRL_ATTR_MCAST_GRP_NAME
48  *    CTRL_ATTR_MCAST_GRP_ID    Need this for the group mask
49  *    ...
50  */
51
52 static int
53 get_ctrl_grp_id(struct rtattr *arg)
54 {
55         struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
56         char *name;
57
58         if (arg == NULL)
59                 return -1;
60
61         /* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the  */
62         /* group name and ID  */
63         parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
64
65         /* if either of the entries needed cannot be found, bail */
66         if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
67                 return -1;
68
69         /* get the name of this multicast group we've found */
70         name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
71
72         /* if it does not match the ACPI event multicast group name, bail */
73         if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
74                 return -1;
75
76         /* At this point, we've found what we were looking for.  We now  */
77         /* have the multicast group ID for ACPI events over generic netlink. */
78         acpi_event_mcast_group_id =
79                 *((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
80
81         return 0;
82 }
83
84 /* n = the response to a CTRL_CMD_GETFAMILY message */
85 static int
86 genl_get_mcast_group_id(struct nlmsghdr *n)
87 {
88         /*
89          *  Attribute table.  Note the type name "rtattr" which means "route
90          *  attribute".  This is a vestige of one of netlink's main uses:
91          *  routing.
92          */
93         struct rtattr *tb[CTRL_ATTR_MAX + 1];
94         /* place for the generic netlink header in the incoming message */
95         struct genlmsghdr ghdr;
96         /* length of the attribute and payload */
97         int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
98         /* Pointer to the attribute portion of the message */
99         struct rtattr *attrs;
100
101         if (len < 0) {
102                 fprintf(stderr, "%s: netlink CTRL_CMD_GETFAMILY response, "
103                         "wrong controller message len: %d\n", progname, len);
104                 return -1;
105         }
106
107         if (n->nlmsg_type != GENL_ID_CTRL) {
108                 fprintf(stderr, "%s: not a netlink controller message, "
109                         "nlmsg_len=%d nlmsg_type=0x%x\n", 
110                         progname, n->nlmsg_len, n->nlmsg_type);
111                 return 0;
112         }
113
114         /* copy generic netlink header into structure */
115         memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
116
117         if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
118             ghdr.cmd != CTRL_CMD_DELFAMILY &&
119             ghdr.cmd != CTRL_CMD_NEWFAMILY &&
120             ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
121             ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
122                 fprintf(stderr, "%s: unknown netlink controller command %d\n",
123                         progname, ghdr.cmd);
124                 return 0;
125         }
126
127         /* set attrs to point to the attribute */
128         attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
129         /* Read the table from the message into "tb".  This actually just  */
130         /* places pointers into the message into tb[].  */
131         parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
132
133         /* if a family ID attribute is present, get it */
134         if (tb[CTRL_ATTR_FAMILY_ID])
135         {
136                 acpi_event_family_id =
137                         *((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
138         }
139
140         /* if a "multicast groups" attribute is present... */
141         if (tb[CTRL_ATTR_MCAST_GROUPS]) {
142                 struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
143                 int i;
144
145                 /* get the group table within this attribute  */
146                 parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
147                         tb[CTRL_ATTR_MCAST_GROUPS]);
148
149                 /* for each group */
150                 for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
151                         /* if this group is valid */
152                         if (tb2[i])
153                                 /* Parse the ID.  If successful, we're done. */
154                                 if (!get_ctrl_grp_id(tb2[i]))
155                                         return 0;
156         }
157
158         return -1;
159 }
160
161 static int
162 genl_get_ids(char *family_name)
163 {
164         /* handle to the netlink connection */
165         struct rtnl_handle rth;
166         /* holds the request we are going to send and the reply */
167         struct {
168                 struct nlmsghdr n;
169                 char buf[4096];    /* ??? Is this big enough for all cases? */
170         } req;
171         /* pointer to the nlmsghdr in req */
172         struct nlmsghdr *nlh;
173         /* place for the generic netlink header before copied into req */
174         struct genlmsghdr ghdr;
175         /* return value */
176         int ret = -1;
177
178         /* clear out the request */
179         memset(&req, 0, sizeof(req));
180
181         /* set up nlh to point to the netlink header in req */
182         nlh = &req.n;
183         /* set up the netlink header */
184         nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
185         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
186         nlh->nlmsg_type = GENL_ID_CTRL;
187
188         /* clear out the generic netlink message header */
189         memset(&ghdr, 0, sizeof(struct genlmsghdr));
190         /* set the command we want to run: "GETFAMILY" */
191         ghdr.cmd = CTRL_CMD_GETFAMILY;
192         /* copy it into req */
193         memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
194
195         /* the message payload is the family name */
196         addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
197                           family_name, strlen(family_name) + 1);
198
199         /* open a generic netlink connection */
200         if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
201                 fprintf(stderr, "%s: cannot open generic netlink socket\n", 
202                         progname);
203                 return -1;
204         }
205
206         /*
207          *  Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
208          *  netlink controller.  Reply will be in nlh upon return.
209          */
210         if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
211                 fprintf(stderr, "%s: error talking to the kernel via netlink\n",
212                         progname);
213                 goto ctrl_done;
214         }
215
216         /* process the response */
217         if (genl_get_mcast_group_id(nlh) < 0) {
218                 fprintf(stderr, "%s: failed to get acpi_event netlink "
219                         "multicast group\n", progname);
220                 goto ctrl_done;
221         }
222
223         ret = 0;
224
225 ctrl_done:
226         rtnl_close(&rth);
227         return ret;
228 }
229
230 /* initialize the ACPI IDs */
231 static void
232 acpi_ids_init()
233 {
234         genl_get_ids(ACPI_EVENT_FAMILY_NAME);
235
236         initialized = 1;
237 }
238
239 /* returns the netlink family ID for ACPI event messages */
240 __u16
241 acpi_ids_getfamily()
242 {
243         /* if the IDs haven't been initialized, initialize them */
244         if (initialized == 0)
245                 acpi_ids_init();
246
247         return acpi_event_family_id;
248 }
249
250 /* returns the netlink multicast group ID for ACPI event messages */
251 __u32
252 acpi_ids_getgroup()
253 {
254         /* if the IDs haven't been initialized, initialize them */
255         if (initialized == 0)
256                 acpi_ids_init();
257
258         return acpi_event_mcast_group_id;
259 }