a98878c874be4280974121d89bcde7feb4df81d1
[linux-flexiantxendom0-3.2.10.git] / tools / hv / hv_kvp_daemon.c
1 /*
2  * An implementation of key value pair (KVP) functionality for Linux.
3  *
4  *
5  * Copyright (C) 2010, Novell, Inc.
6  * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License version 2 as published
10  * by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15  * NON INFRINGEMENT.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  */
23
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/poll.h>
28 #include <sys/utsname.h>
29 #include <linux/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <arpa/inet.h>
36 #include <linux/connector.h>
37 #include <linux/hyperv.h>
38 #include <linux/netlink.h>
39 #include <ifaddrs.h>
40 #include <netdb.h>
41 #include <syslog.h>
42
43
44 /*
45  * KVP protocol: The user mode component first registers with the
46  * the kernel component. Subsequently, the kernel component requests, data
47  * for the specified keys. In response to this message the user mode component
48  * fills in the value corresponding to the specified key. We overload the
49  * sequence field in the cn_msg header to define our KVP message types.
50  *
51  * We use this infrastructure for also supporting queries from user mode
52  * application for state that may be maintained in the KVP kernel component.
53  *
54  */
55
56
57 enum key_index {
58         FullyQualifiedDomainName = 0,
59         IntegrationServicesVersion, /*This key is serviced in the kernel*/
60         NetworkAddressIPv4,
61         NetworkAddressIPv6,
62         OSBuildNumber,
63         OSName,
64         OSMajorVersion,
65         OSMinorVersion,
66         OSVersion,
67         ProcessorArchitecture
68 };
69
70 static char kvp_send_buffer[4096];
71 static char kvp_recv_buffer[4096];
72 static struct sockaddr_nl addr;
73
74 static char *os_name = "";
75 static char *os_major = "";
76 static char *os_minor = "";
77 static char *processor_arch;
78 static char *os_build;
79 static char *lic_version;
80 static struct utsname uts_buf;
81
82 void kvp_get_os_info(void)
83 {
84         FILE    *file;
85         char    *p, buf[512];
86
87         uname(&uts_buf);
88         os_build = uts_buf.release;
89         processor_arch = uts_buf.machine;
90
91         /*
92          * The current windows host (win7) expects the build
93          * string to be of the form: x.y.z
94          * Strip additional information we may have.
95          */
96         p = strchr(os_build, '-');
97         if (p)
98                 *p = '\0';
99
100         file = fopen("/etc/SuSE-release", "r");
101         if (file != NULL)
102                 goto kvp_osinfo_found;
103         file  = fopen("/etc/redhat-release", "r");
104         if (file != NULL)
105                 goto kvp_osinfo_found;
106         /*
107          * Add code for other supported platforms.
108          */
109
110         /*
111          * We don't have information about the os.
112          */
113         os_name = uts_buf.sysname;
114         return;
115
116 kvp_osinfo_found:
117         /* up to three lines */
118         p = fgets(buf, sizeof(buf), file);
119         if (p) {
120                 p = strchr(buf, '\n');
121                 if (p)
122                         *p = '\0';
123                 p = strdup(buf);
124                 if (!p)
125                         goto done;
126                 os_name = p;
127
128                 /* second line */
129                 p = fgets(buf, sizeof(buf), file);
130                 if (p) {
131                         p = strchr(buf, '\n');
132                         if (p)
133                                 *p = '\0';
134                         p = strdup(buf);
135                         if (!p)
136                                 goto done;
137                         os_major = p;
138
139                         /* third line */
140                         p = fgets(buf, sizeof(buf), file);
141                         if (p)  {
142                                 p = strchr(buf, '\n');
143                                 if (p)
144                                         *p = '\0';
145                                 p = strdup(buf);
146                                 if (p)
147                                         os_minor = p;
148                         }
149                 }
150         }
151
152 done:
153         fclose(file);
154         return;
155 }
156
157 static int
158 kvp_get_ip_address(int family, char *buffer, int length)
159 {
160         struct ifaddrs *ifap;
161         struct ifaddrs *curp;
162         int ipv4_len = strlen("255.255.255.255") + 1;
163         int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
164         int offset = 0;
165         const char *str;
166         char tmp[50];
167         int error = 0;
168
169         /*
170          * On entry into this function, the buffer is capable of holding the
171          * maximum key value (2048 bytes).
172          */
173
174         if (getifaddrs(&ifap)) {
175                 strcpy(buffer, "getifaddrs failed\n");
176                 return 1;
177         }
178
179         curp = ifap;
180         while (curp != NULL) {
181                 if ((curp->ifa_addr != NULL) &&
182                    (curp->ifa_addr->sa_family == family)) {
183                         if (family == AF_INET) {
184                                 struct sockaddr_in *addr =
185                                 (struct sockaddr_in *) curp->ifa_addr;
186
187                                 str = inet_ntop(family, &addr->sin_addr,
188                                                 tmp, 50);
189                                 if (str == NULL) {
190                                         strcpy(buffer, "inet_ntop failed\n");
191                                         error = 1;
192                                         goto getaddr_done;
193                                 }
194                                 if (offset == 0)
195                                         strcpy(buffer, tmp);
196                                 else
197                                         strcat(buffer, tmp);
198                                 strcat(buffer, ";");
199
200                                 offset += strlen(str) + 1;
201                                 if ((length - offset) < (ipv4_len + 1))
202                                         goto getaddr_done;
203
204                         } else {
205
206                         /*
207                          * We only support AF_INET and AF_INET6
208                          * and the list of addresses is separated by a ";".
209                          */
210                                 struct sockaddr_in6 *addr =
211                                 (struct sockaddr_in6 *) curp->ifa_addr;
212
213                                 str = inet_ntop(family,
214                                         &addr->sin6_addr.s6_addr,
215                                         tmp, 50);
216                                 if (str == NULL) {
217                                         strcpy(buffer, "inet_ntop failed\n");
218                                         error = 1;
219                                         goto getaddr_done;
220                                 }
221                                 if (offset == 0)
222                                         strcpy(buffer, tmp);
223                                 else
224                                         strcat(buffer, tmp);
225                                 strcat(buffer, ";");
226                                 offset += strlen(str) + 1;
227                                 if ((length - offset) < (ipv6_len + 1))
228                                         goto getaddr_done;
229
230                         }
231
232                 }
233                 curp = curp->ifa_next;
234         }
235
236 getaddr_done:
237         freeifaddrs(ifap);
238         return error;
239 }
240
241
242 static int
243 kvp_get_domain_name(char *buffer, int length)
244 {
245         struct addrinfo hints, *info ;
246         int error = 0;
247
248         gethostname(buffer, length);
249         memset(&hints, 0, sizeof(hints));
250         hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
251         hints.ai_socktype = SOCK_STREAM;
252         hints.ai_flags = AI_CANONNAME;
253
254         error = getaddrinfo(buffer, NULL, &hints, &info);
255         if (error != 0) {
256                 strcpy(buffer, "getaddrinfo failed\n");
257                 return error;
258         }
259         strcpy(buffer, info->ai_canonname);
260         freeaddrinfo(info);
261         return error;
262 }
263
264 static int
265 netlink_send(int fd, struct cn_msg *msg)
266 {
267         struct nlmsghdr *nlh;
268         unsigned int size;
269         struct msghdr message;
270         char buffer[64];
271         struct iovec iov[2];
272
273         size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
274
275         nlh = (struct nlmsghdr *)buffer;
276         nlh->nlmsg_seq = 0;
277         nlh->nlmsg_pid = getpid();
278         nlh->nlmsg_type = NLMSG_DONE;
279         nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
280         nlh->nlmsg_flags = 0;
281
282         iov[0].iov_base = nlh;
283         iov[0].iov_len = sizeof(*nlh);
284
285         iov[1].iov_base = msg;
286         iov[1].iov_len = size;
287
288         memset(&message, 0, sizeof(message));
289         message.msg_name = &addr;
290         message.msg_namelen = sizeof(addr);
291         message.msg_iov = iov;
292         message.msg_iovlen = 2;
293
294         return sendmsg(fd, &message, 0);
295 }
296
297 int main(void)
298 {
299         int fd, len, sock_opt;
300         int error;
301         struct cn_msg *message;
302         struct pollfd pfd;
303         struct nlmsghdr *incoming_msg;
304         struct cn_msg   *incoming_cn_msg;
305         struct hv_kvp_msg *hv_msg;
306         char    *p;
307         char    *key_value;
308         char    *key_name;
309
310         daemon(1, 0);
311         openlog("KVP", 0, LOG_USER);
312         syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
313         /*
314          * Retrieve OS release information.
315          */
316         kvp_get_os_info();
317
318         fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
319         if (fd < 0) {
320                 syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
321                 exit(-1);
322         }
323         addr.nl_family = AF_NETLINK;
324         addr.nl_pad = 0;
325         addr.nl_pid = 0;
326         addr.nl_groups = CN_KVP_IDX;
327
328
329         error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
330         if (error < 0) {
331                 syslog(LOG_ERR, "bind failed; error:%d", error);
332                 close(fd);
333                 exit(-1);
334         }
335         sock_opt = addr.nl_groups;
336         setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
337         /*
338          * Register ourselves with the kernel.
339          */
340         message = (struct cn_msg *)kvp_send_buffer;
341         message->id.idx = CN_KVP_IDX;
342         message->id.val = CN_KVP_VAL;
343
344         hv_msg = (struct hv_kvp_msg *)message->data;
345         hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
346         message->ack = 0;
347         message->len = sizeof(struct hv_kvp_msg);
348
349         len = netlink_send(fd, message);
350         if (len < 0) {
351                 syslog(LOG_ERR, "netlink_send failed; error:%d", len);
352                 close(fd);
353                 exit(-1);
354         }
355
356         pfd.fd = fd;
357
358         while (1) {
359                 pfd.events = POLLIN;
360                 pfd.revents = 0;
361                 poll(&pfd, 1, -1);
362
363                 len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0);
364
365                 if (len < 0) {
366                         syslog(LOG_ERR, "recv failed; error:%d", len);
367                         close(fd);
368                         return -1;
369                 }
370
371                 incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
372                 incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
373                 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
374
375                 switch (hv_msg->kvp_hdr.operation) {
376                 case KVP_OP_REGISTER:
377                         /*
378                          * Driver is registering with us; stash away the version
379                          * information.
380                          */
381                         p = (char *)hv_msg->body.kvp_register.version;
382                         lic_version = malloc(strlen(p) + 1);
383                         if (lic_version) {
384                                 strcpy(lic_version, p);
385                                 syslog(LOG_INFO, "KVP LIC Version: %s",
386                                         lic_version);
387                         } else {
388                                 syslog(LOG_ERR, "malloc failed");
389                         }
390                         continue;
391
392                 case KVP_OP_SET:
393                 case KVP_OP_GET:
394                 case KVP_OP_DELETE:
395                 default:
396                         break;
397                 }
398
399                 if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
400                         goto kvp_done;
401
402                 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
403                 key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
404                 key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
405
406                 switch (hv_msg->body.kvp_enum_data.index) {
407                 case FullyQualifiedDomainName:
408                         kvp_get_domain_name(key_value,
409                                         HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
410                         strcpy(key_name, "FullyQualifiedDomainName");
411                         break;
412                 case IntegrationServicesVersion:
413                         strcpy(key_name, "IntegrationServicesVersion");
414                         strcpy(key_value, lic_version);
415                         break;
416                 case NetworkAddressIPv4:
417                         kvp_get_ip_address(AF_INET, key_value,
418                                         HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
419                         strcpy(key_name, "NetworkAddressIPv4");
420                         break;
421                 case NetworkAddressIPv6:
422                         kvp_get_ip_address(AF_INET6, key_value,
423                                         HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
424                         strcpy(key_name, "NetworkAddressIPv6");
425                         break;
426                 case OSBuildNumber:
427                         strcpy(key_value, os_build);
428                         strcpy(key_name, "OSBuildNumber");
429                         break;
430                 case OSName:
431                         strcpy(key_value, os_name);
432                         strcpy(key_name, "OSName");
433                         break;
434                 case OSMajorVersion:
435                         strcpy(key_value, os_major);
436                         strcpy(key_name, "OSMajorVersion");
437                         break;
438                 case OSMinorVersion:
439                         strcpy(key_value, os_minor);
440                         strcpy(key_name, "OSMinorVersion");
441                         break;
442                 case OSVersion:
443                         strcpy(key_value, os_build);
444                         strcpy(key_name, "OSVersion");
445                         break;
446                 case ProcessorArchitecture:
447                         strcpy(key_value, processor_arch);
448                         strcpy(key_name, "ProcessorArchitecture");
449                         break;
450                 default:
451                         strcpy(key_value, "Unknown Key");
452                         /*
453                          * We use a null key name to terminate enumeration.
454                          */
455                         strcpy(key_name, "");
456                         break;
457                 }
458                 /*
459                  * Send the value back to the kernel. The response is
460                  * already in the receive buffer. Update the cn_msg header to
461                  * reflect the key value that has been added to the message
462                  */
463 kvp_done:
464
465                 incoming_cn_msg->id.idx = CN_KVP_IDX;
466                 incoming_cn_msg->id.val = CN_KVP_VAL;
467                 incoming_cn_msg->ack = 0;
468                 incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
469
470                 len = netlink_send(fd, incoming_cn_msg);
471                 if (len < 0) {
472                         syslog(LOG_ERR, "net_link send failed; error:%d", len);
473                         exit(-1);
474                 }
475         }
476
477 }