1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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#include <sys/stat.h>
43#include <fcntl.h>
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58enum key_index {
59 FullyQualifiedDomainName = 0,
60 IntegrationServicesVersion,
61 NetworkAddressIPv4,
62 NetworkAddressIPv6,
63 OSBuildNumber,
64 OSName,
65 OSMajorVersion,
66 OSMinorVersion,
67 OSVersion,
68 ProcessorArchitecture
69};
70
71static char kvp_send_buffer[4096];
72static char kvp_recv_buffer[4096];
73static struct sockaddr_nl addr;
74
75static char *os_name = "";
76static char *os_major = "";
77static char *os_minor = "";
78static char *processor_arch;
79static char *os_build;
80static char *lic_version;
81static struct utsname uts_buf;
82
83
84#define MAX_FILE_NAME 100
85#define ENTRIES_PER_BLOCK 50
86
87struct kvp_record {
88 __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
89 __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
90};
91
92struct kvp_file_state {
93 int fd;
94 int num_blocks;
95 struct kvp_record *records;
96 int num_records;
97 __u8 fname[MAX_FILE_NAME];
98};
99
100static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
101
102static void kvp_acquire_lock(int pool)
103{
104 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
105 fl.l_pid = getpid();
106
107 if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
108 syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
109 exit(EXIT_FAILURE);
110 }
111}
112
113static void kvp_release_lock(int pool)
114{
115 struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
116 fl.l_pid = getpid();
117
118 if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
119 perror("fcntl");
120 syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
121 exit(EXIT_FAILURE);
122 }
123}
124
125static void kvp_update_file(int pool)
126{
127 FILE *filep;
128 size_t bytes_written;
129
130
131
132
133
134 kvp_acquire_lock(pool);
135
136 filep = fopen(kvp_file_info[pool].fname, "w");
137 if (!filep) {
138 kvp_release_lock(pool);
139 syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
140 exit(EXIT_FAILURE);
141 }
142
143 bytes_written = fwrite(kvp_file_info[pool].records,
144 sizeof(struct kvp_record),
145 kvp_file_info[pool].num_records, filep);
146
147 if (ferror(filep) || fclose(filep)) {
148 kvp_release_lock(pool);
149 syslog(LOG_ERR, "Failed to write file, pool: %d", pool);
150 exit(EXIT_FAILURE);
151 }
152
153 kvp_release_lock(pool);
154}
155
156static void kvp_update_mem_state(int pool)
157{
158 FILE *filep;
159 size_t records_read = 0;
160 struct kvp_record *record = kvp_file_info[pool].records;
161 struct kvp_record *readp;
162 int num_blocks = kvp_file_info[pool].num_blocks;
163 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
164
165 kvp_acquire_lock(pool);
166
167 filep = fopen(kvp_file_info[pool].fname, "r");
168 if (!filep) {
169 kvp_release_lock(pool);
170 syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
171 exit(EXIT_FAILURE);
172 }
173 for (;;) {
174 readp = &record[records_read];
175 records_read += fread(readp, sizeof(struct kvp_record),
176 ENTRIES_PER_BLOCK * num_blocks,
177 filep);
178
179 if (ferror(filep)) {
180 syslog(LOG_ERR, "Failed to read file, pool: %d", pool);
181 exit(EXIT_FAILURE);
182 }
183
184 if (!feof(filep)) {
185
186
187
188 num_blocks++;
189 record = realloc(record, alloc_unit * num_blocks);
190
191 if (record == NULL) {
192 syslog(LOG_ERR, "malloc failed");
193 exit(EXIT_FAILURE);
194 }
195 continue;
196 }
197 break;
198 }
199
200 kvp_file_info[pool].num_blocks = num_blocks;
201 kvp_file_info[pool].records = record;
202 kvp_file_info[pool].num_records = records_read;
203
204 fclose(filep);
205 kvp_release_lock(pool);
206}
207static int kvp_file_init(void)
208{
209 int ret, fd;
210 FILE *filep;
211 size_t records_read;
212 __u8 *fname;
213 struct kvp_record *record;
214 struct kvp_record *readp;
215 int num_blocks;
216 int i;
217 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
218
219 if (access("/var/opt/hyperv", F_OK)) {
220 if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
221 syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
222 exit(EXIT_FAILURE);
223 }
224 }
225
226 for (i = 0; i < KVP_POOL_COUNT; i++) {
227 fname = kvp_file_info[i].fname;
228 records_read = 0;
229 num_blocks = 1;
230 sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
231 fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
232
233 if (fd == -1)
234 return 1;
235
236
237 filep = fopen(fname, "r");
238 if (!filep)
239 return 1;
240
241 record = malloc(alloc_unit * num_blocks);
242 if (record == NULL) {
243 fclose(filep);
244 return 1;
245 }
246 for (;;) {
247 readp = &record[records_read];
248 records_read += fread(readp, sizeof(struct kvp_record),
249 ENTRIES_PER_BLOCK,
250 filep);
251
252 if (ferror(filep)) {
253 syslog(LOG_ERR, "Failed to read file, pool: %d",
254 i);
255 exit(EXIT_FAILURE);
256 }
257
258 if (!feof(filep)) {
259
260
261
262 num_blocks++;
263 record = realloc(record, alloc_unit *
264 num_blocks);
265 if (record == NULL) {
266 fclose(filep);
267 return 1;
268 }
269 continue;
270 }
271 break;
272 }
273 kvp_file_info[i].fd = fd;
274 kvp_file_info[i].num_blocks = num_blocks;
275 kvp_file_info[i].records = record;
276 kvp_file_info[i].num_records = records_read;
277 fclose(filep);
278
279 }
280
281 return 0;
282}
283
284static int kvp_key_delete(int pool, __u8 *key, int key_size)
285{
286 int i;
287 int j, k;
288 int num_records;
289 struct kvp_record *record;
290
291
292
293
294 kvp_update_mem_state(pool);
295
296 num_records = kvp_file_info[pool].num_records;
297 record = kvp_file_info[pool].records;
298
299 for (i = 0; i < num_records; i++) {
300 if (memcmp(key, record[i].key, key_size))
301 continue;
302
303
304
305
306 if (i == num_records) {
307 kvp_file_info[pool].num_records--;
308 kvp_update_file(pool);
309 return 0;
310 }
311
312 j = i;
313 k = j + 1;
314 for (; k < num_records; k++) {
315 strcpy(record[j].key, record[k].key);
316 strcpy(record[j].value, record[k].value);
317 j++;
318 }
319
320 kvp_file_info[pool].num_records--;
321 kvp_update_file(pool);
322 return 0;
323 }
324 return 1;
325}
326
327static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
328 int value_size)
329{
330 int i;
331 int j, k;
332 int num_records;
333 struct kvp_record *record;
334 int num_blocks;
335
336 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
337 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
338 return 1;
339
340
341
342
343 kvp_update_mem_state(pool);
344
345 num_records = kvp_file_info[pool].num_records;
346 record = kvp_file_info[pool].records;
347 num_blocks = kvp_file_info[pool].num_blocks;
348
349 for (i = 0; i < num_records; i++) {
350 if (memcmp(key, record[i].key, key_size))
351 continue;
352
353
354
355
356 memcpy(record[i].value, value, value_size);
357 kvp_update_file(pool);
358 return 0;
359 }
360
361
362
363
364 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
365
366 record = realloc(record, sizeof(struct kvp_record) *
367 ENTRIES_PER_BLOCK * (num_blocks + 1));
368
369 if (record == NULL)
370 return 1;
371 kvp_file_info[pool].num_blocks++;
372
373 }
374 memcpy(record[i].value, value, value_size);
375 memcpy(record[i].key, key, key_size);
376 kvp_file_info[pool].records = record;
377 kvp_file_info[pool].num_records++;
378 kvp_update_file(pool);
379 return 0;
380}
381
382static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
383 int value_size)
384{
385 int i;
386 int num_records;
387 struct kvp_record *record;
388
389 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
390 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
391 return 1;
392
393
394
395
396 kvp_update_mem_state(pool);
397
398 num_records = kvp_file_info[pool].num_records;
399 record = kvp_file_info[pool].records;
400
401 for (i = 0; i < num_records; i++) {
402 if (memcmp(key, record[i].key, key_size))
403 continue;
404
405
406
407 memcpy(value, record[i].value, value_size);
408 return 0;
409 }
410
411 return 1;
412}
413
414static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
415 __u8 *value, int value_size)
416{
417 struct kvp_record *record;
418
419
420
421
422 kvp_update_mem_state(pool);
423 record = kvp_file_info[pool].records;
424
425 if (index >= kvp_file_info[pool].num_records) {
426
427
428
429
430 strcpy(value, "");
431 return;
432 }
433
434 memcpy(key, record[index].key, key_size);
435 memcpy(value, record[index].value, value_size);
436}
437
438
439void kvp_get_os_info(void)
440{
441 FILE *file;
442 char *p, buf[512];
443
444 uname(&uts_buf);
445 os_build = uts_buf.release;
446 processor_arch = uts_buf.machine;
447
448
449
450
451
452
453 p = strchr(os_build, '-');
454 if (p)
455 *p = '\0';
456
457 file = fopen("/etc/SuSE-release", "r");
458 if (file != NULL)
459 goto kvp_osinfo_found;
460 file = fopen("/etc/redhat-release", "r");
461 if (file != NULL)
462 goto kvp_osinfo_found;
463
464
465
466
467
468
469
470 os_name = uts_buf.sysname;
471 return;
472
473kvp_osinfo_found:
474
475 p = fgets(buf, sizeof(buf), file);
476 if (p) {
477 p = strchr(buf, '\n');
478 if (p)
479 *p = '\0';
480 p = strdup(buf);
481 if (!p)
482 goto done;
483 os_name = p;
484
485
486 p = fgets(buf, sizeof(buf), file);
487 if (p) {
488 p = strchr(buf, '\n');
489 if (p)
490 *p = '\0';
491 p = strdup(buf);
492 if (!p)
493 goto done;
494 os_major = p;
495
496
497 p = fgets(buf, sizeof(buf), file);
498 if (p) {
499 p = strchr(buf, '\n');
500 if (p)
501 *p = '\0';
502 p = strdup(buf);
503 if (p)
504 os_minor = p;
505 }
506 }
507 }
508
509done:
510 fclose(file);
511 return;
512}
513
514static int
515kvp_get_ip_address(int family, char *buffer, int length)
516{
517 struct ifaddrs *ifap;
518 struct ifaddrs *curp;
519 int ipv4_len = strlen("255.255.255.255") + 1;
520 int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
521 int offset = 0;
522 const char *str;
523 char tmp[50];
524 int error = 0;
525
526
527
528
529
530
531 if (getifaddrs(&ifap)) {
532 strcpy(buffer, "getifaddrs failed\n");
533 return 1;
534 }
535
536 curp = ifap;
537 while (curp != NULL) {
538 if ((curp->ifa_addr != NULL) &&
539 (curp->ifa_addr->sa_family == family)) {
540 if (family == AF_INET) {
541 struct sockaddr_in *addr =
542 (struct sockaddr_in *) curp->ifa_addr;
543
544 str = inet_ntop(family, &addr->sin_addr,
545 tmp, 50);
546 if (str == NULL) {
547 strcpy(buffer, "inet_ntop failed\n");
548 error = 1;
549 goto getaddr_done;
550 }
551 if (offset == 0)
552 strcpy(buffer, tmp);
553 else
554 strcat(buffer, tmp);
555 strcat(buffer, ";");
556
557 offset += strlen(str) + 1;
558 if ((length - offset) < (ipv4_len + 1))
559 goto getaddr_done;
560
561 } else {
562
563
564
565
566
567 struct sockaddr_in6 *addr =
568 (struct sockaddr_in6 *) curp->ifa_addr;
569
570 str = inet_ntop(family,
571 &addr->sin6_addr.s6_addr,
572 tmp, 50);
573 if (str == NULL) {
574 strcpy(buffer, "inet_ntop failed\n");
575 error = 1;
576 goto getaddr_done;
577 }
578 if (offset == 0)
579 strcpy(buffer, tmp);
580 else
581 strcat(buffer, tmp);
582 strcat(buffer, ";");
583 offset += strlen(str) + 1;
584 if ((length - offset) < (ipv6_len + 1))
585 goto getaddr_done;
586
587 }
588
589 }
590 curp = curp->ifa_next;
591 }
592
593getaddr_done:
594 freeifaddrs(ifap);
595 return error;
596}
597
598
599static int
600kvp_get_domain_name(char *buffer, int length)
601{
602 struct addrinfo hints, *info ;
603 int error = 0;
604
605 gethostname(buffer, length);
606 memset(&hints, 0, sizeof(hints));
607 hints.ai_family = AF_INET;
608 hints.ai_socktype = SOCK_STREAM;
609 hints.ai_flags = AI_CANONNAME;
610
611 error = getaddrinfo(buffer, NULL, &hints, &info);
612 if (error != 0) {
613 strcpy(buffer, "getaddrinfo failed\n");
614 return error;
615 }
616 strcpy(buffer, info->ai_canonname);
617 freeaddrinfo(info);
618 return error;
619}
620
621static int
622netlink_send(int fd, struct cn_msg *msg)
623{
624 struct nlmsghdr *nlh;
625 unsigned int size;
626 struct msghdr message;
627 char buffer[64];
628 struct iovec iov[2];
629
630 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
631
632 nlh = (struct nlmsghdr *)buffer;
633 nlh->nlmsg_seq = 0;
634 nlh->nlmsg_pid = getpid();
635 nlh->nlmsg_type = NLMSG_DONE;
636 nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
637 nlh->nlmsg_flags = 0;
638
639 iov[0].iov_base = nlh;
640 iov[0].iov_len = sizeof(*nlh);
641
642 iov[1].iov_base = msg;
643 iov[1].iov_len = size;
644
645 memset(&message, 0, sizeof(message));
646 message.msg_name = &addr;
647 message.msg_namelen = sizeof(addr);
648 message.msg_iov = iov;
649 message.msg_iovlen = 2;
650
651 return sendmsg(fd, &message, 0);
652}
653
654int main(void)
655{
656 int fd, len, sock_opt;
657 int error;
658 struct cn_msg *message;
659 struct pollfd pfd;
660 struct nlmsghdr *incoming_msg;
661 struct cn_msg *incoming_cn_msg;
662 struct hv_kvp_msg *hv_msg;
663 char *p;
664 char *key_value;
665 char *key_name;
666
667 daemon(1, 0);
668 openlog("KVP", 0, LOG_USER);
669 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
670
671
672
673 kvp_get_os_info();
674
675 if (kvp_file_init()) {
676 syslog(LOG_ERR, "Failed to initialize the pools");
677 exit(EXIT_FAILURE);
678 }
679
680 fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
681 if (fd < 0) {
682 syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
683 exit(EXIT_FAILURE);
684 }
685 addr.nl_family = AF_NETLINK;
686 addr.nl_pad = 0;
687 addr.nl_pid = 0;
688 addr.nl_groups = CN_KVP_IDX;
689
690
691 error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
692 if (error < 0) {
693 syslog(LOG_ERR, "bind failed; error:%d", error);
694 close(fd);
695 exit(EXIT_FAILURE);
696 }
697 sock_opt = addr.nl_groups;
698 setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
699
700
701
702 message = (struct cn_msg *)kvp_send_buffer;
703 message->id.idx = CN_KVP_IDX;
704 message->id.val = CN_KVP_VAL;
705
706 hv_msg = (struct hv_kvp_msg *)message->data;
707 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
708 message->ack = 0;
709 message->len = sizeof(struct hv_kvp_msg);
710
711 len = netlink_send(fd, message);
712 if (len < 0) {
713 syslog(LOG_ERR, "netlink_send failed; error:%d", len);
714 close(fd);
715 exit(EXIT_FAILURE);
716 }
717
718 pfd.fd = fd;
719
720 while (1) {
721 struct sockaddr *addr_p = (struct sockaddr *) &addr;
722 socklen_t addr_l = sizeof(addr);
723 pfd.events = POLLIN;
724 pfd.revents = 0;
725 poll(&pfd, 1, -1);
726
727 len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0,
728 addr_p, &addr_l);
729
730 if (len < 0 || addr.nl_pid) {
731 syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
732 addr.nl_pid, errno, strerror(errno));
733 close(fd);
734 return -1;
735 }
736
737 incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
738 incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
739 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
740
741 switch (hv_msg->kvp_hdr.operation) {
742 case KVP_OP_REGISTER:
743
744
745
746
747 p = (char *)hv_msg->body.kvp_register.version;
748 lic_version = malloc(strlen(p) + 1);
749 if (lic_version) {
750 strcpy(lic_version, p);
751 syslog(LOG_INFO, "KVP LIC Version: %s",
752 lic_version);
753 } else {
754 syslog(LOG_ERR, "malloc failed");
755 }
756 continue;
757
758
759
760
761
762
763
764
765 case KVP_OP_SET:
766 if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool,
767 hv_msg->body.kvp_set.data.key,
768 hv_msg->body.kvp_set.data.key_size,
769 hv_msg->body.kvp_set.data.value,
770 hv_msg->body.kvp_set.data.value_size))
771 strcpy(hv_msg->body.kvp_set.data.key, "");
772 break;
773
774 case KVP_OP_GET:
775 if (kvp_get_value(hv_msg->kvp_hdr.pool,
776 hv_msg->body.kvp_set.data.key,
777 hv_msg->body.kvp_set.data.key_size,
778 hv_msg->body.kvp_set.data.value,
779 hv_msg->body.kvp_set.data.value_size))
780 strcpy(hv_msg->body.kvp_set.data.key, "");
781 break;
782
783 case KVP_OP_DELETE:
784 if (kvp_key_delete(hv_msg->kvp_hdr.pool,
785 hv_msg->body.kvp_delete.key,
786 hv_msg->body.kvp_delete.key_size))
787 strcpy(hv_msg->body.kvp_delete.key, "");
788 break;
789
790 default:
791 break;
792 }
793
794 if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
795 goto kvp_done;
796
797
798
799
800
801
802 if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) {
803 kvp_pool_enumerate(hv_msg->kvp_hdr.pool,
804 hv_msg->body.kvp_enum_data.index,
805 hv_msg->body.kvp_enum_data.data.key,
806 HV_KVP_EXCHANGE_MAX_KEY_SIZE,
807 hv_msg->body.kvp_enum_data.data.value,
808 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
809 goto kvp_done;
810 }
811
812 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
813 key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
814 key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
815
816 switch (hv_msg->body.kvp_enum_data.index) {
817 case FullyQualifiedDomainName:
818 kvp_get_domain_name(key_value,
819 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
820 strcpy(key_name, "FullyQualifiedDomainName");
821 break;
822 case IntegrationServicesVersion:
823 strcpy(key_name, "IntegrationServicesVersion");
824 strcpy(key_value, lic_version);
825 break;
826 case NetworkAddressIPv4:
827 kvp_get_ip_address(AF_INET, key_value,
828 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
829 strcpy(key_name, "NetworkAddressIPv4");
830 break;
831 case NetworkAddressIPv6:
832 kvp_get_ip_address(AF_INET6, key_value,
833 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
834 strcpy(key_name, "NetworkAddressIPv6");
835 break;
836 case OSBuildNumber:
837 strcpy(key_value, os_build);
838 strcpy(key_name, "OSBuildNumber");
839 break;
840 case OSName:
841 strcpy(key_value, os_name);
842 strcpy(key_name, "OSName");
843 break;
844 case OSMajorVersion:
845 strcpy(key_value, os_major);
846 strcpy(key_name, "OSMajorVersion");
847 break;
848 case OSMinorVersion:
849 strcpy(key_value, os_minor);
850 strcpy(key_name, "OSMinorVersion");
851 break;
852 case OSVersion:
853 strcpy(key_value, os_build);
854 strcpy(key_name, "OSVersion");
855 break;
856 case ProcessorArchitecture:
857 strcpy(key_value, processor_arch);
858 strcpy(key_name, "ProcessorArchitecture");
859 break;
860 default:
861 strcpy(key_value, "Unknown Key");
862
863
864
865 strcpy(key_name, "");
866 break;
867 }
868
869
870
871
872
873kvp_done:
874
875 incoming_cn_msg->id.idx = CN_KVP_IDX;
876 incoming_cn_msg->id.val = CN_KVP_VAL;
877 incoming_cn_msg->ack = 0;
878 incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
879
880 len = netlink_send(fd, incoming_cn_msg);
881 if (len < 0) {
882 syslog(LOG_ERR, "net_link send failed; error:%d", len);
883 exit(EXIT_FAILURE);
884 }
885 }
886
887}
888