aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-10-04 15:29:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-11 00:52:55 -0400
commit46a971913611a23478283931460a95be962ce329 (patch)
tree7452d0f07ee9f1f5270a8da6c1387f35c439843d /tools
parent715a4801e734ea9c8e528265ce3ff6aead85bce1 (diff)
Staging: hv: move hyperv code out of staging directory
After many years wandering the desert, it is finally time for the Microsoft HyperV code to move out of the staging directory. Or at least the core hyperv bus code, and the utility driver, the rest still have some review to get through by the various subsystem maintainers. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/hv/hv_kvp_daemon.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
new file mode 100644
index 000000000000..11224eddcdc2
--- /dev/null
+++ b/tools/hv/hv_kvp_daemon.c
@@ -0,0 +1,500 @@
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/netlink.h>
38#include <ifaddrs.h>
39#include <netdb.h>
40#include <syslog.h>
41
42/*
43 * KYS: TODO. Need to register these in the kernel.
44 *
45 * The following definitions are shared with the in-kernel component; do not
46 * change any of this without making the corresponding changes in
47 * the KVP kernel component.
48 */
49#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */
50#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */
51#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */
52
53/*
54 * KVP protocol: The user mode component first registers with the
55 * the kernel component. Subsequently, the kernel component requests, data
56 * for the specified keys. In response to this message the user mode component
57 * fills in the value corresponding to the specified key. We overload the
58 * sequence field in the cn_msg header to define our KVP message types.
59 *
60 * We use this infrastructure for also supporting queries from user mode
61 * application for state that may be maintained in the KVP kernel component.
62 *
63 * XXXKYS: Have a shared header file between the user and kernel (TODO)
64 */
65
66enum kvp_op {
67 KVP_REGISTER = 0, /* Register the user mode component*/
68 KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/
69 KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/
70 KVP_USER_GET, /*User is requesting the value for the specified key*/
71 KVP_USER_SET /*User is providing the value for the specified key*/
72};
73
74#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512
75#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048
76
77struct hv_ku_msg {
78 __u32 kvp_index;
79 __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
80 __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */
81};
82
83enum key_index {
84 FullyQualifiedDomainName = 0,
85 IntegrationServicesVersion, /*This key is serviced in the kernel*/
86 NetworkAddressIPv4,
87 NetworkAddressIPv6,
88 OSBuildNumber,
89 OSName,
90 OSMajorVersion,
91 OSMinorVersion,
92 OSVersion,
93 ProcessorArchitecture
94};
95
96/*
97 * End of shared definitions.
98 */
99
100static char kvp_send_buffer[4096];
101static char kvp_recv_buffer[4096];
102static struct sockaddr_nl addr;
103
104static char *os_name = "";
105static char *os_major = "";
106static char *os_minor = "";
107static char *processor_arch;
108static char *os_build;
109static char *lic_version;
110static struct utsname uts_buf;
111
112void kvp_get_os_info(void)
113{
114 FILE *file;
115 char *p, buf[512];
116
117 uname(&uts_buf);
118 os_build = uts_buf.release;
119 processor_arch = uts_buf.machine;
120
121 /*
122 * The current windows host (win7) expects the build
123 * string to be of the form: x.y.z
124 * Strip additional information we may have.
125 */
126 p = strchr(os_build, '-');
127 if (p)
128 *p = '\0';
129
130 file = fopen("/etc/SuSE-release", "r");
131 if (file != NULL)
132 goto kvp_osinfo_found;
133 file = fopen("/etc/redhat-release", "r");
134 if (file != NULL)
135 goto kvp_osinfo_found;
136 /*
137 * Add code for other supported platforms.
138 */
139
140 /*
141 * We don't have information about the os.
142 */
143 os_name = uts_buf.sysname;
144 return;
145
146kvp_osinfo_found:
147 /* up to three lines */
148 p = fgets(buf, sizeof(buf), file);
149 if (p) {
150 p = strchr(buf, '\n');
151 if (p)
152 *p = '\0';
153 p = strdup(buf);
154 if (!p)
155 goto done;
156 os_name = p;
157
158 /* second line */
159 p = fgets(buf, sizeof(buf), file);
160 if (p) {
161 p = strchr(buf, '\n');
162 if (p)
163 *p = '\0';
164 p = strdup(buf);
165 if (!p)
166 goto done;
167 os_major = p;
168
169 /* third line */
170 p = fgets(buf, sizeof(buf), file);
171 if (p) {
172 p = strchr(buf, '\n');
173 if (p)
174 *p = '\0';
175 p = strdup(buf);
176 if (p)
177 os_minor = p;
178 }
179 }
180 }
181
182done:
183 fclose(file);
184 return;
185}
186
187static int
188kvp_get_ip_address(int family, char *buffer, int length)
189{
190 struct ifaddrs *ifap;
191 struct ifaddrs *curp;
192 int ipv4_len = strlen("255.255.255.255") + 1;
193 int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
194 int offset = 0;
195 const char *str;
196 char tmp[50];
197 int error = 0;
198
199 /*
200 * On entry into this function, the buffer is capable of holding the
201 * maximum key value (2048 bytes).
202 */
203
204 if (getifaddrs(&ifap)) {
205 strcpy(buffer, "getifaddrs failed\n");
206 return 1;
207 }
208
209 curp = ifap;
210 while (curp != NULL) {
211 if ((curp->ifa_addr != NULL) &&
212 (curp->ifa_addr->sa_family == family)) {
213 if (family == AF_INET) {
214 struct sockaddr_in *addr =
215 (struct sockaddr_in *) curp->ifa_addr;
216
217 str = inet_ntop(family, &addr->sin_addr,
218 tmp, 50);
219 if (str == NULL) {
220 strcpy(buffer, "inet_ntop failed\n");
221 error = 1;
222 goto getaddr_done;
223 }
224 if (offset == 0)
225 strcpy(buffer, tmp);
226 else
227 strcat(buffer, tmp);
228 strcat(buffer, ";");
229
230 offset += strlen(str) + 1;
231 if ((length - offset) < (ipv4_len + 1))
232 goto getaddr_done;
233
234 } else {
235
236 /*
237 * We only support AF_INET and AF_INET6
238 * and the list of addresses is separated by a ";".
239 */
240 struct sockaddr_in6 *addr =
241 (struct sockaddr_in6 *) curp->ifa_addr;
242
243 str = inet_ntop(family,
244 &addr->sin6_addr.s6_addr,
245 tmp, 50);
246 if (str == NULL) {
247 strcpy(buffer, "inet_ntop failed\n");
248 error = 1;
249 goto getaddr_done;
250 }
251 if (offset == 0)
252 strcpy(buffer, tmp);
253 else
254 strcat(buffer, tmp);
255 strcat(buffer, ";");
256 offset += strlen(str) + 1;
257 if ((length - offset) < (ipv6_len + 1))
258 goto getaddr_done;
259
260 }
261
262 }
263 curp = curp->ifa_next;
264 }
265
266getaddr_done:
267 freeifaddrs(ifap);
268 return error;
269}
270
271
272static int
273kvp_get_domain_name(char *buffer, int length)
274{
275 struct addrinfo hints, *info ;
276 int error = 0;
277
278 gethostname(buffer, length);
279 memset(&hints, 0, sizeof(hints));
280 hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
281 hints.ai_socktype = SOCK_STREAM;
282 hints.ai_flags = AI_CANONNAME;
283
284 error = getaddrinfo(buffer, NULL, &hints, &info);
285 if (error != 0) {
286 strcpy(buffer, "getaddrinfo failed\n");
287 return error;
288 }
289 strcpy(buffer, info->ai_canonname);
290 freeaddrinfo(info);
291 return error;
292}
293
294static int
295netlink_send(int fd, struct cn_msg *msg)
296{
297 struct nlmsghdr *nlh;
298 unsigned int size;
299 struct msghdr message;
300 char buffer[64];
301 struct iovec iov[2];
302
303 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
304
305 nlh = (struct nlmsghdr *)buffer;
306 nlh->nlmsg_seq = 0;
307 nlh->nlmsg_pid = getpid();
308 nlh->nlmsg_type = NLMSG_DONE;
309 nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
310 nlh->nlmsg_flags = 0;
311
312 iov[0].iov_base = nlh;
313 iov[0].iov_len = sizeof(*nlh);
314
315 iov[1].iov_base = msg;
316 iov[1].iov_len = size;
317
318 memset(&message, 0, sizeof(message));
319 message.msg_name = &addr;
320 message.msg_namelen = sizeof(addr);
321 message.msg_iov = iov;
322 message.msg_iovlen = 2;
323
324 return sendmsg(fd, &message, 0);
325}
326
327int main(void)
328{
329 int fd, len, sock_opt;
330 int error;
331 struct cn_msg *message;
332 struct pollfd pfd;
333 struct nlmsghdr *incoming_msg;
334 struct cn_msg *incoming_cn_msg;
335 struct hv_ku_msg *hv_msg;
336 char *p;
337 char *key_value;
338 char *key_name;
339
340 daemon(1, 0);
341 openlog("KVP", 0, LOG_USER);
342 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
343 /*
344 * Retrieve OS release information.
345 */
346 kvp_get_os_info();
347
348 fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
349 if (fd < 0) {
350 syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
351 exit(-1);
352 }
353 addr.nl_family = AF_NETLINK;
354 addr.nl_pad = 0;
355 addr.nl_pid = 0;
356 addr.nl_groups = CN_KVP_IDX;
357
358
359 error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
360 if (error < 0) {
361 syslog(LOG_ERR, "bind failed; error:%d", error);
362 close(fd);
363 exit(-1);
364 }
365 sock_opt = addr.nl_groups;
366 setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
367 /*
368 * Register ourselves with the kernel.
369 */
370 message = (struct cn_msg *)kvp_send_buffer;
371 message->id.idx = CN_KVP_IDX;
372 message->id.val = CN_KVP_VAL;
373 message->seq = KVP_REGISTER;
374 message->ack = 0;
375 message->len = 0;
376
377 len = netlink_send(fd, message);
378 if (len < 0) {
379 syslog(LOG_ERR, "netlink_send failed; error:%d", len);
380 close(fd);
381 exit(-1);
382 }
383
384 pfd.fd = fd;
385
386 while (1) {
387 pfd.events = POLLIN;
388 pfd.revents = 0;
389 poll(&pfd, 1, -1);
390
391 len = recv(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0);
392
393 if (len < 0) {
394 syslog(LOG_ERR, "recv failed; error:%d", len);
395 close(fd);
396 return -1;
397 }
398
399 incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
400 incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
401
402 switch (incoming_cn_msg->seq) {
403 case KVP_REGISTER:
404 /*
405 * Driver is registering with us; stash away the version
406 * information.
407 */
408 p = (char *)incoming_cn_msg->data;
409 lic_version = malloc(strlen(p) + 1);
410 if (lic_version) {
411 strcpy(lic_version, p);
412 syslog(LOG_INFO, "KVP LIC Version: %s",
413 lic_version);
414 } else {
415 syslog(LOG_ERR, "malloc failed");
416 }
417 continue;
418
419 case KVP_KERNEL_GET:
420 break;
421 default:
422 continue;
423 }
424
425 hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data;
426 key_name = (char *)hv_msg->kvp_key;
427 key_value = (char *)hv_msg->kvp_value;
428
429 switch (hv_msg->kvp_index) {
430 case FullyQualifiedDomainName:
431 kvp_get_domain_name(key_value,
432 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
433 strcpy(key_name, "FullyQualifiedDomainName");
434 break;
435 case IntegrationServicesVersion:
436 strcpy(key_name, "IntegrationServicesVersion");
437 strcpy(key_value, lic_version);
438 break;
439 case NetworkAddressIPv4:
440 kvp_get_ip_address(AF_INET, key_value,
441 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
442 strcpy(key_name, "NetworkAddressIPv4");
443 break;
444 case NetworkAddressIPv6:
445 kvp_get_ip_address(AF_INET6, key_value,
446 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
447 strcpy(key_name, "NetworkAddressIPv6");
448 break;
449 case OSBuildNumber:
450 strcpy(key_value, os_build);
451 strcpy(key_name, "OSBuildNumber");
452 break;
453 case OSName:
454 strcpy(key_value, os_name);
455 strcpy(key_name, "OSName");
456 break;
457 case OSMajorVersion:
458 strcpy(key_value, os_major);
459 strcpy(key_name, "OSMajorVersion");
460 break;
461 case OSMinorVersion:
462 strcpy(key_value, os_minor);
463 strcpy(key_name, "OSMinorVersion");
464 break;
465 case OSVersion:
466 strcpy(key_value, os_build);
467 strcpy(key_name, "OSVersion");
468 break;
469 case ProcessorArchitecture:
470 strcpy(key_value, processor_arch);
471 strcpy(key_name, "ProcessorArchitecture");
472 break;
473 default:
474 strcpy(key_value, "Unknown Key");
475 /*
476 * We use a null key name to terminate enumeration.
477 */
478 strcpy(key_name, "");
479 break;
480 }
481 /*
482 * Send the value back to the kernel. The response is
483 * already in the receive buffer. Update the cn_msg header to
484 * reflect the key value that has been added to the message
485 */
486
487 incoming_cn_msg->id.idx = CN_KVP_IDX;
488 incoming_cn_msg->id.val = CN_KVP_VAL;
489 incoming_cn_msg->seq = KVP_USER_SET;
490 incoming_cn_msg->ack = 0;
491 incoming_cn_msg->len = sizeof(struct hv_ku_msg);
492
493 len = netlink_send(fd, incoming_cn_msg);
494 if (len < 0) {
495 syslog(LOG_ERR, "net_link send failed; error:%d", len);
496 exit(-1);
497 }
498 }
499
500}