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