RPi Locator and Display Services
skn_network_helpers.c
Go to the documentation of this file.
1 
5 #include "skn_network_helpers.h"
6 
7 /*
8  * Global Exit Flag -- set by signal handler
9  * atomic to survive signal handlers
10  */
11 sig_atomic_t gi_exit_flag = SKN_RUN_MODE_RUN; // will hold the signal which cause exit
12 char *gd_pch_message = NULL;
13 
14 signed int gd_i_debug = 0;
18 int gd_i_socket = -1;
19 int gd_i_display = 0;
20 int gd_i_update = 0;
28 
29 static void skn_locator_print_usage();
30 static void exit_handler(int sig);
31 
32 
33 static void * service_registry_entry_create_helper(char *key, char **name, char **ip, char **port);
35 static int service_registry_entry_create(PServiceRegistry psreg, char *name, char *ip, char *port, int *errors);
36 static int service_registry_response_parse(PServiceRegistry psreg, const char *response, int *errors);
37 
38 /*
39  * General System Information Utils */
41  return sysconf(_SC_NPROCESSORS_ONLN);
42 }
43 
44 
45 int generate_loadavg_info(char *msg) {
46  double loadavg[4];
47  int rc = 0;
48 
49  rc = getloadavg(loadavg, 3);
50 
51  if (rc != PLATFORM_ERROR) {
52  snprintf(msg, SZ_INFO_BUFF -1, "LoadAvg: 1m=%2.1f, 5m=%2.1f, 15m=%2.1F",
53  loadavg[0], loadavg[1], loadavg[2]);
54  } else {
55  snprintf(msg, SZ_INFO_BUFF -1, "Load Average: Not Available %d:%d:%s",
56  rc, errno, strerror(errno));
57  }
58 
59  return rc;
60 }
61 
62 int generate_uname_info(char *msg) {
63  struct utsname info;
64 
65  int mLen = 0;
66  char * message = "uname() api failed.";
67 
68  if (uname(&info) != 0) {
69  mLen = snprintf(msg, SZ_INFO_BUFF -1, "%s", message);
70  } else {
71  mLen = snprintf(msg, SZ_INFO_BUFF -1, "%s %s, %s %s | Cores=%ld",
72  info.sysname, info.release, info.version, info.machine,
74  }
75  return mLen;
76 }
77 
78 int generate_datetime_info(char *msg) {
79  int mLen = 0;
80  struct tm *t;
81  time_t tim;
82 
83  tim = time(NULL);
84  t = localtime(&tim);
85 
86  mLen = snprintf(msg, SZ_INFO_BUFF -1, "%02d:%02d:%04d %02d:%02d:%02d",
87  t->tm_mon + 1, t->tm_mday, t->tm_year + 1900,
88  ((t->tm_hour - TZ_ADJUST) < 0 ? (t->tm_hour - TZ_ADJUST + 12) : (t->tm_hour - TZ_ADJUST)), t->tm_min, t->tm_sec);
89 
90  return mLen;
91 }
92 
96 int skn_time_delay(double delay_time) {
97  struct timespec timeout;
98  if (delay_time == 0.0 || delay_time == 0) delay_time = 0.001;
99  timeout.tv_sec = (time_t) delay_time; // extract integer only
100  timeout.tv_nsec = (long) ((delay_time - timeout.tv_sec) * 1000000000L); // 1e+9
101  return nanosleep(&timeout, NULL);
102 }
103 
153  uid_t real_user_id = 0;
154  uid_t effective_user_id = 0;
155  struct passwd *userinfo = NULL;
156 
157  real_user_id = getuid();
158  if (gd_pch_effective_userid != NULL) {
159  userinfo = getpwnam(gd_pch_effective_userid);
160  effective_user_id = userinfo->pw_uid;
161  } else {
162  effective_user_id = geteuid();
163  userinfo = getpwuid(effective_user_id);
164  gd_pch_effective_userid = userinfo->pw_name;
165  }
166 
167  return real_user_id;
168 }
174 static void exit_handler(int sig) {
175  gi_exit_flag = sig;
176  skn_logger(SD_NOTICE, "Program Exiting, from signal=%d:%s\n", sig, strsignal(sig));
177 }
178 
179 void signals_init() {
180  signal(SIGINT, exit_handler); // Ctrl-C
181  signal(SIGQUIT, exit_handler); // Quit
182  signal(SIGTERM, exit_handler); // Normal kill command
183 }
184 
185 void signals_cleanup(int sig) {
186  signal(SIGINT, SIG_DFL);
187  signal(SIGQUIT, SIG_DFL);
188  signal(SIGTERM, SIG_DFL);
189 
190  if (gi_exit_flag > SKN_RUN_MODE_RUN) { // exit caused by some interrupt -- otherwise it would be exactly 0
191  kill(getpid(), sig);
192  }
193 }
194 
195 
196 void get_default_interface_name_and_ipv4_address(char * intf, char * ipv4) {
197  IPBroadcastArray aB;
198 
200  memcpy(intf, aB.chDefaultIntfName, SZ_CHAR_BUFF);
201  memcpy(ipv4, aB.ipAddrStr[aB.defaultIndex], SZ_CHAR_BUFF);
202  } else {
203  skn_logger(SD_ERR, "InterfaceName and Address: unable to access information.");
204  }
205 }
206 
215  struct ifaddrs * ifap;
216  struct ifaddrs * p;
217  int rc = 0;
218 
219  memset(paB, 0, sizeof(IPBroadcastArray));
220  paB->count = 0;
221  paB->defaultIndex = 0;
222  strcpy(paB->cbName, "IPBroadcastArray");
223 
225  if (rc == EXIT_FAILURE) { // Alternate method for Mac: 'route -n -A inet'
226  skn_logger(SD_ERR, "No Default Network Interfaces Found!.");
227  paB->chDefaultIntfName[0] = 0;
228  }
229  rc = getifaddrs(&ifap);
230  if (rc != 0) {
231  skn_logger(SD_ERR, "No Network Interfaces Found at All ! %d:%d:%s", rc, errno, strerror(errno) );
232  return (PLATFORM_ERROR);
233  }
234  p = ifap;
235 
236  while (p && paB->count < ARY_MAX_INTF) {
237  if (p->ifa_addr != NULL && p->ifa_addr->sa_family == AF_INET && ((p->ifa_flags & IFF_BROADCAST) > 0)) {
238 
239  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_addr)->sin_addr, paB->ipAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
240  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_netmask)->sin_addr, paB->maskAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
241  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_broadaddr)->sin_addr, paB->broadAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
242 
243  strncpy(paB->ifNameStr[paB->count], p->ifa_name, (SZ_CHAR_BUFF -1));
244 
245  /* Take match as the default */
246  if (strcmp(paB->chDefaultIntfName, p->ifa_name) == 0) {
247  paB->defaultIndex = paB->count;
248  }
249 
250  paB->count++;
251  }
252  p = p->ifa_next;
253  } // end while
254  freeifaddrs(ifap);
255 
256  return paB->count;
257 }
258 
275 int get_default_interface_name(char *pchDefaultInterfaceName) {
276  FILE *f_route;
277  char line[SZ_INFO_BUFF], *dRoute = NULL, *iName = NULL;
278 
279  f_route = fopen("/proc/net/route", "r");
280  if (f_route != NULL) {
281  while (fgets(line, SZ_INFO_BUFF - 1, f_route)) {
282  iName = strtok(line, "\t");
283  dRoute = strtok(NULL, "\t");
284 
285  if (iName != NULL && dRoute != NULL) {
286  if (strcmp(dRoute, "00000000") == 0) {
287  strncpy(pchDefaultInterfaceName, iName, (SZ_INFO_BUFF - 1));
288  break;
289  }
290  }
291  }
292  fclose(f_route);
293 
294  return EXIT_SUCCESS;
295  }
296  skn_logger(SD_ERR, "Opening ProcFs for RouteInfo Failed: %d:%s, Alternate method will be attempted.", errno, strerror(errno));
297 
298  f_route = popen("route -n get 0.0.0.0", "r"); // for linux 'route -n -A inet', with interface at line_word[7]
299  if (f_route != NULL) {
300  while (fgets(line, SZ_INFO_BUFF - 1, f_route)) {
301  dRoute = strtok(line, ":");
302  iName = strtok(NULL, "\n");
303  if (strcmp(skn_strip(dRoute), "interface") == 0) {
304  strncpy(pchDefaultInterfaceName, skn_strip(iName), (SZ_INFO_BUFF - 1));
305  break;
306  }
307  }
308  fclose(f_route);
309 
310  return EXIT_SUCCESS;
311  } else {
312  skn_logger(SD_ERR, "Alternate method to get RouteInfo Failed: %d:%s", errno, strerror(errno));
313  return EXIT_FAILURE;
314  }
315 
316 }
317 
318 /**************************************************************************
319  Function: Print Usage
320 
321  Description:
322  Output the command-line options for this daemon.
323 
324  Params:
325  @argc - Standard argument count
326  @argv - Standard argument array
327 
328  Returns:
329  returns void always
330  **************************************************************************/
331 static void skn_locator_print_usage() {
332  skn_logger(" ", "%s -- %s", gd_ch_program_name, gd_ch_program_desc);
333  skn_logger(" ", "\tSkoona Development <skoona@gmail.com>");
334  if (strcmp(gd_ch_program_name, "udp_locator_client") == 0) {
335  skn_logger(" ", "Usage:\n %s [-v] [-m 'any text msg'] [-u] [-h|--help]", gd_ch_program_name);
336  skn_logger(" ", "\nOptions:");
337  skn_logger(" ", " -u, --unique-registry\t List unique entries from all responses.");
338  skn_logger(" ", " -m, --message\tAny text to send; 'stop' cause service to terminate.");
339  } else if (strcmp(gd_ch_program_name, "udp_locator_service") == 0) {
340  skn_logger(" ", "Usage:\n %s [-s] [-v] [-m '<delimited-response-message-string>'] [-a 'my_service_name'] [-h|--help]", gd_ch_program_name);
341  skn_logger(" ", " Format: name=<service-name>,ip=<service-ipaddress>ddd.ddd.ddd.ddd,port=<service-portnumber>ddddd <line-delimiter>");
342  skn_logger(" ", " REQUIRED <line-delimiter> is one of these '|', '%', ';'");
343  skn_logger(" ", " example: -m 'name=rpi_locator_service,ip=192.168.1.15,port=48028|name=lcd_display_service, ip=192.168.1.15, port=48029|'");
344  skn_logger(" ", "\nOptions:");
345  skn_logger(" ", " -a, --alt-service-name=my_service_name");
346  skn_logger(" ", " lcd_display_service is default, use this to change name.");
347  skn_logger(" ", " -s, --include-display-service\tInclude DisplayService entry in default registry.");
348  } else if (strcmp(gd_ch_program_name, "lcd_display_client") == 0) {
349  skn_logger(" ", "Usage:\n %s [-v] [-m 'message for display'] [-n 1|300] [-a 'my_service_name'] [-h|--help]", gd_ch_program_name);
350  skn_logger(" ", "\nOptions:");
351  skn_logger(" ", " -a, --alt-service-name=my_service_name");
352  skn_logger(" ", " lcd_display_service is default, use this to change name.");
353  skn_logger(" ", " -m, --message\tRequest message to send.");
354  skn_logger(" ", " -n, --non-stop=DD\tContinue to send updates every DD seconds until ctrl-break.");
355  } else if (strcmp(gd_ch_program_name, "a2d_display_client") == 0) {
356  skn_logger(" ", "Usage:\n %s [-v] [-n 1|300] [-i ddd] [-a 'my_service_name'] [-h|--help]", gd_ch_program_name);
357  skn_logger(" ", "\nOptions:");
358  skn_logger(" ", " -a, --alt-service-name=my_service_name");
359  skn_logger(" ", " lcd_display_service is default, use this to change target.");
360  skn_logger(" ", " -i, --i2c-address=ddd\tI2C decimal address. | [0x27=39, 0x20=32]");
361  skn_logger(" ", " -n, --non-stop=DD\tContinue to send updates every DD seconds until ctrl-break.");
362  }
363  skn_logger(" ", " -v, --version\tVersion printout.");
364  skn_logger(" ", " -h, --help\t\tShow this help screen.");
365 }
366 
367 /* *****************************************************
368  * Parse out the command line options from argc,argv
369  * results go to globals
370  * EXIT_FAILURE on any error, or version and help
371  * EXIT_SUCCESS on normal completion
372  */
373 int skn_handle_locator_command_line(int argc, char **argv) {
374  int opt = 0;
375  int longindex = 0;
376  struct option longopts[] = { { "include-display-service", 0, NULL, 's' }, /* set true if present */
377  { "alt-service-name", 1, NULL, 'a' }, /* set true if present */
378  { "unique-registry", 0, NULL, 'u' }, /* set true if present */
379  { "non-stop", 1, NULL, 'n' }, /* required param if */
380  { "debug", 1, NULL, 'd' }, /* required param if */
381  { "message", 1, NULL, 'm' }, /* required param if */
382  { "i2c-address", 1, NULL, 'i' }, /* required param if */
383  { "version", 0, NULL, 'v' }, /* set true if present */
384  { "help", 0, NULL, 'h' }, /* set true if present */
385  { 0, 0, 0, 0 } };
386 
387  /*
388  * Get commandline options
389  * longindex is the current index into longopts
390  * optind is current/last index into argv
391  * optarg is value attached(-d88) or next element(-d 88) of argv
392  * opterr flags a scanning error
393  */
394  while ((opt = getopt_long(argc, argv, "d:m:n:i:a:usvh", longopts, &longindex)) != -1) {
395  switch (opt) {
396  case 'u':
398  break;
399  case 's':
400  gd_i_display = 1;
401  break;
402  case 'd':
403  if (optarg) {
404  gd_i_debug = atoi(optarg);
405  } else {
406  skn_logger(SD_WARNING, "%s: input param was invalid! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind, opterr);
407  return (EXIT_FAILURE);
408  }
409  break;
410  case 'm':
411  if (optarg) {
412  gd_pch_message = strdup(optarg);
413  } else {
414  skn_logger(SD_WARNING, "%s: input param was invalid! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind, opterr);
415  return (EXIT_FAILURE);
416  }
417  break;
418  case 'n':
419  if (optarg) {
420  gd_i_update = atoi(optarg);
421  } else {
422  skn_logger(SD_WARNING, "%s: input param was invalid! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind, opterr);
423  return (EXIT_FAILURE);
424  }
425  break;
426  case 'a':
427  if (optarg) {
428  gd_pch_service_name = strdup(optarg);
429  } else {
430  skn_logger(SD_WARNING, "%s: input param was invalid! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind, opterr);
431  return (EXIT_FAILURE);
432  }
433  break;
434  case 'i':
435  if (optarg) {
436  gd_i_i2c_address = atoi(optarg);
437  } else {
438  skn_logger(SD_ERR, "%s: input param was invalid! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind,
439  opterr);
440  return (EXIT_FAILURE);
441  }
442  break;
443  case 'v':
444  skn_logger(SD_WARNING, "\n\tProgram => %s\n\tVersion => %s\n\tSkoona Development\n\t<skoona@gmail.com>\n", gd_ch_program_name, PACKAGE_VERSION);
445  return (EXIT_FAILURE);
446  break;
447  case '?':
448  skn_logger(SD_WARNING, "%s: unknown input param! %c[%d:%d:%d]\n", gd_ch_program_name, (char) opt, longindex, optind, opterr);
450  return (EXIT_FAILURE);
451  break;
452  default: /* help and default */
454  return (EXIT_FAILURE);
455  break;
456  }
457  }
458 
459  return EXIT_SUCCESS;
460 }
461 
462 void skn_program_name_and_description_set(const char *name, const char *desc) {
463  char hostName[SZ_CHAR_BUFF];
464  char * phostName = hostName;
465 
466  strncpy(gd_ch_program_name, name, sizeof(gd_ch_program_name) - 1);
467  strncpy(gd_ch_program_desc, desc, sizeof(gd_ch_program_desc) - 1);
468 
469  gethostname(hostName, sizeof(hostName) - 1);
470  strncpy(gd_ch_hostName, hostName, sizeof(gd_ch_hostName) - 1);
471  strsep(&phostName, ".");
472  strncpy(gd_ch_hostShortName, hostName, sizeof(gd_ch_hostShortName) - 1);
473 
474 }
475 
480 char * skn_strip(char * alpha) {
481  if (alpha == NULL || strlen(alpha) < 1)
482  return alpha;
483 
484  int len = strlen(alpha);
485  int end = len - 1;
486  int start = 0;
487 
488  // use isgraph() or !isspace() vs isalnum() to allow ['|' ';' '%']
489  while (isgraph(alpha[end]) == 0 && end > 0) { // remove trailing non-alphanumeric chars
490  alpha[end--] = 0;
491  }
492 
493  len = strlen(alpha);
494  while ((isalnum(alpha[start]) == 0) && start < len) { // find first non-alpha stricter
495  start++;
496  }
497  if (start < len && start > 0) { // move in place
498  end = len - start;
499  memmove(&alpha[0], &alpha[start], end);
500  alpha[end] = 0;
501  }
502 
503  return alpha;
504 }
505 
506 int skn_logger(const char *level, const char *format, ...) {
507  va_list args;
508  char buffer[SZ_LINE_BUFF];
509  const char *logLevel = SD_NOTICE;
510 
511  va_start(args, format);
512  vsnprintf(buffer, sizeof(buffer), format, args);
513  va_end(args);
514 
515  if (level != NULL) {
516  logLevel = level;
517  }
518  return fprintf(stderr, "%s%s\n", logLevel, buffer);
519 }
520 
527 int skn_udp_host_create_regular_socket(int port, double rcvTimeout) {
528  struct sockaddr_in addr;
529  int i_socket, reuseEnable = 1;
530 
531  if ((i_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
532  skn_logger(SD_EMERG, "Create Socket error=%d, etext=%s", errno, strerror(errno));
533  return (EXIT_FAILURE);
534  }
535 
536  if ((setsockopt(i_socket, SOL_SOCKET, SO_REUSEADDR, &reuseEnable, sizeof(reuseEnable))) < 0) {
537  skn_logger(SD_EMERG, "Set Socket Reuse Option error=%d, etext=%s", errno, strerror(errno));
538  return (EXIT_FAILURE);
539  }
540 
541  struct timeval tv;
542  tv.tv_sec = rcvTimeout;
543  tv.tv_usec = (long)(rcvTimeout - tv.tv_sec) * 1000000L;
544  if ((setsockopt(i_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) < 0) {
545  skn_logger(SD_EMERG, "Set Socket RcvTimeout Option error=%d, etext=%s", errno, strerror(errno));
546  return (EXIT_FAILURE);
547  }
548 
549  /* set up local address */
550  memset(&addr, 0, sizeof(addr));
551  addr.sin_family = AF_INET;
552  addr.sin_addr.s_addr = htonl(INADDR_ANY);
553  addr.sin_port = htons(port);
554  if (bind(i_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
555  skn_logger(SD_EMERG, "Bind to local Socket error=%d, etext=%s", errno, strerror(errno));
556  return (EXIT_FAILURE);
557  }
558 
559  return i_socket;
560 }
561 
568 int skn_udp_host_create_broadcast_socket(int port, double rcvTimeout) {
569  struct sockaddr_in addr;
570  int i_socket, broadcastEnable = 1;
571 
572  if ((i_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
573  skn_logger(SD_EMERG, "Create Socket error=%d, etext=%s", errno, strerror(errno));
574  return (EXIT_FAILURE);
575  }
576  if ((setsockopt(i_socket, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable))) < 0) {
577  skn_logger(SD_EMERG, "Set Socket Broadcast Option error=%d, etext=%s", errno, strerror(errno));
578  return (EXIT_FAILURE);
579  }
580 
581  struct timeval tv;
582  tv.tv_sec = rcvTimeout;
583  tv.tv_usec = (long)(rcvTimeout - tv.tv_sec) * 1000000L;
584  skn_logger(SD_INFO, "Set Socket RcvTimeout Option set to: tv_sec=%ld, tv_usec=%ld", tv.tv_sec, tv.tv_usec);
585  if ((setsockopt(i_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) < 0) {
586  skn_logger(SD_EMERG, "Set Socket RcvTimeout Option error=%d, etext=%s", errno, strerror(errno));
587  return (EXIT_FAILURE);
588  }
589 
590  /* set up local address */
591  memset(&addr, 0, sizeof(addr));
592  addr.sin_family = AF_INET;
593  addr.sin_addr.s_addr = htonl(INADDR_ANY);
594  addr.sin_port = htons(port);
595  if (bind(i_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
596  skn_logger(SD_EMERG, "Bind to local Socket error=%d, etext=%s", errno, strerror(errno));
597  return (EXIT_FAILURE);
598  }
599 
600  return i_socket;
601 }
602 
603 PServiceRequest skn_service_request_create(PRegistryEntry pre, int host_socket, char *request) {
604  PServiceRequest psr = NULL;
605 
606  if (pre == NULL) {
607  return NULL;
608  }
609  psr = (PServiceRequest) malloc(sizeof(ServiceRequest));
610  memset(psr, 0, sizeof(ServiceRequest));
611  strcpy(psr->cbName, "PServiceRequest");
612  psr->socket = host_socket;
613  psr->pre = pre;
614  strncpy(psr->request, request, SZ_INFO_BUFF-1);
615  return psr;
616 }
617 
622 double skn_duration_in_milliseconds(struct timeval *pstart, struct timeval *pend) {
623  long secs_used = 0;
624  double total_micros_used = 0.0;
625  struct timeval end;
626 
627  if (pend == NULL) { // calc running duration
628  pend = &end;
629  gettimeofday(pend, NULL);
630  }
631 
632  secs_used=(pend->tv_sec - pstart->tv_sec); //avoid overflow by subtracting first
633  total_micros_used = secs_used * 1000.0; // sec to ms
634  total_micros_used += (pend->tv_usec - pstart->tv_usec) / 1000.0; // us to ms
635 
636  return total_micros_used / 1000.0; // express in seconds.milliseconds
637 }
638 
646  struct sockaddr_in remaddr; /* remote address */
647  socklen_t addrlen = sizeof(remaddr); /* length of addresses */
648  signed int vIndex = 0;
649  struct timeval start, end;
650 
651  memset(&remaddr, 0, sizeof(remaddr));
652  remaddr.sin_family = AF_INET;
653  remaddr.sin_addr.s_addr = inet_addr(psr->pre->ip);
654  remaddr.sin_port = htons(psr->pre->port);
655 
656  /*
657  * SEND */
658  gettimeofday(&start, NULL);
659  if (sendto(psr->socket, psr->request, strlen(psr->request), 0, (struct sockaddr *) &remaddr, addrlen) < 0) {
660  gettimeofday(&end, NULL);
661  skn_logger(SD_WARNING, "ServiceRequest: SendTo(%1.6f) Timed out; Failure code=%d, etext=%s", skn_duration_in_milliseconds(&start,&end), errno, strerror(errno));
662  return EXIT_FAILURE;
663  }
664  skn_logger(SD_NOTICE, "ServiceRequest sent to %s:%s:%d", psr->pre->name, psr->pre->ip, psr->pre->port);
665 
666  /*
667  * RECEIVE */
668  vIndex = recvfrom(psr->socket, psr->response, (SZ_INFO_BUFF - 1), 0, (struct sockaddr *) &remaddr, &addrlen);
669  if (vIndex == PLATFORM_ERROR) {
670  gettimeofday(&end, NULL);
671  skn_logger(SD_WARNING, "ServiceRequest: recvfrom(%1.6f) Failure code=%d, etext=%s", skn_duration_in_milliseconds(&start,&end), errno, strerror(errno));
672  return EXIT_FAILURE;
673  }
674  psr->response[vIndex] = 0;
675  gettimeofday(&end, NULL);
676 
677  skn_logger(SD_INFO, "Response(%1.3fs) received from [%s] %s:%d",
678  skn_duration_in_milliseconds(&start,&end),
679  psr->response,
680  inet_ntoa(remaddr.sin_addr),
681  ntohs(remaddr.sin_port)
682  );
683 
684  if (strcmp(psr->response, "QUIT!") == 0) {
685  skn_logger(SD_NOTICE, "Shutdown Requested!");
686  return EXIT_FAILURE;
687  }
688 
689  return (EXIT_SUCCESS);
690 }
691 
692 int service_registry_provider(int i_socket, char *response) {
693  struct sockaddr_in remaddr; /* remote address */
694  socklen_t addrlen = sizeof(remaddr); /* length of addresses */
695  IPBroadcastArray aB;
696  char request[SZ_INFO_BUFF];
697  char recvHostName[SZ_INFO_BUFF];
698  signed int rLen = 0, rc = 0;
699  int exit_code = EXIT_SUCCESS, i_response_len = 0;
700 
701 
702  memset(request, 0, sizeof(request));
703  memset(recvHostName, 0, sizeof(recvHostName));
704 
705  rc = get_broadcast_ip_array(&aB);
706  if (rc == PLATFORM_ERROR) {
707  return EXIT_FAILURE;
708  }
709 
710  if (gd_pch_service_name == NULL) {
711  gd_pch_service_name = "lcd_display_service";
712  }
713 
714  if (strlen(response) < 16) {
715  if (gd_i_display) {
716  i_response_len = snprintf(response, (SZ_INFO_BUFF - 1),
717  "name=rpi_locator_service,ip=%s,port=%d|"
718  "name=%s,ip=%s,port=%d|",
722  } else {
723  i_response_len = snprintf(response, (SZ_INFO_BUFF - 1),
724  "name=rpi_locator_service,ip=%s,port=%d|",
726  }
727  }
729 
730  skn_logger(SD_DEBUG, "Socket Bound to %s:%s", aB.chDefaultIntfName, aB.ipAddrStr[aB.defaultIndex]);
731 
732  while (gi_exit_flag == SKN_RUN_MODE_RUN) {
733  memset(&remaddr, 0, sizeof(remaddr));
734  remaddr.sin_family = AF_INET;
735  remaddr.sin_port = htons(SKN_FIND_RPI_PORT);
736  remaddr.sin_addr.s_addr = htonl(INADDR_ANY);
737  addrlen = sizeof(remaddr);
738 
739  if ((rLen = recvfrom(i_socket, request, (SZ_INFO_BUFF - 1), 0, (struct sockaddr *) &remaddr, &addrlen)) < 0) {
740  if (errno == EAGAIN) {
741  continue;
742  }
743  skn_logger(SD_ERR, "RcvFrom() Failure code=%d, etext=%s", errno, strerror(errno));
744  exit_code = EXIT_FAILURE;
745  break;
746  }
747  request[rLen] = 0;
748 
749  rc = getnameinfo(((struct sockaddr *) &remaddr), sizeof(struct sockaddr_in), recvHostName, (SZ_INFO_BUFF-1), NULL, 0, NI_DGRAM);
750  if (rc != 0) {
751  skn_logger(SD_ERR, "GetNameInfo() Failure code=%d, etext=%s", errno, strerror(errno));
752  exit_code = EXIT_FAILURE;
753  break;
754  }
755  skn_logger(SD_NOTICE, "Received request from %s @ %s:%d", recvHostName, inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port));
756  skn_logger(SD_NOTICE, "Request data: [%s]\n", request);
757 
758  /*
759  * Add new registry entry by command */
760  if ((strncmp("ADD ", request, sizeof("ADD")) == 0) &&
761  (service_registry_valiadate_response_format(&request[4]) == EXIT_SUCCESS)) {
762  if ((response[i_response_len-1] == '|') ||
763  (response[i_response_len-1] == '%') ||
764  (response[i_response_len-1] == ';')) {
765  strncpy(&response[i_response_len], &request[4], ((SZ_COMM_BUFF - 1) - (strlen(response) + strlen(&request[4]))) );
766  i_response_len += (rLen - 4);
767  skn_logger(SD_NOTICE, "COMMAND: Add New RegistryEntry Request Accepted!");
768  }
769  }
770 
771  if (sendto(i_socket, response, strlen(response), 0, (struct sockaddr *) &remaddr, addrlen) < 0) {
772  skn_logger(SD_EMERG, "SendTo() Failure code=%d, etext=%s", errno, strerror(errno));
773  exit_code = EXIT_FAILURE;
774  break;
775  }
776 
777  /*
778  * Shutdown by command */
779  if (strcmp("QUIT!", request) == 0) {
780  skn_logger(SD_NOTICE, "COMMAND: Shutdown Requested! exit code=%d", gi_exit_flag);
781  break;
782  }
783  }
784 
785  return exit_code;
786 }
787 
788 /*
789  * Remote Service Registry routines
790 */
798  PServiceRegistry psreg = NULL;
799 
800  psreg = (PServiceRegistry) malloc(sizeof(ServiceRegistry));
801  if (psreg != NULL) {
802  memset(psreg, 0, sizeof(ServiceRegistry));
803  strcpy(psreg->cbName, "PServiceRegistry");
804  psreg->computedMax = (sizeof(psreg->entry) / sizeof(void *));
805  psreg->count = 0;
806  }
807 
808  return psreg;
809 }
810 
817 static int service_registry_entry_create(PServiceRegistry psreg, char *name, char *ip, char *port, int *errors) {
818  PRegistryEntry prent = NULL;
819 
820  if ((psreg == NULL) || (name == NULL) || (ip == NULL) || (port == NULL)) {
821  skn_logger(SD_DEBUG, "Parse failure missing value: (%s,%s,%s)", name, ip, port);
822  if (errors != NULL)
823  (*errors)++;
824  return EXIT_FAILURE;
825  }
826 
827  if (psreg->count >= psreg->computedMax) {
828  skn_logger(SD_WARNING, "Capacity Error: Too many! New entry %d:%s exceeds maximum of %d allowed! Consider using the --unique-registry option.", psreg->count, name, psreg->computedMax);
829  if (errors != NULL)
830  (*errors)++;
831  return EXIT_FAILURE;
832  }
833 
834  /* update or create entry */
835  if (gd_i_unique_registry) {
836  prent = service_registry_find_entry(psreg, name);
837  }
838  if (prent == NULL) {
839  prent = (PRegistryEntry) malloc(sizeof(RegistryEntry));
840  if (prent != NULL) {
841  psreg->entry[psreg->count] = prent;
842  psreg->count++;
843  }
844  }
845 
846  if (prent != NULL) {
847  memset(prent, 0, sizeof(RegistryEntry));
848  strcpy(prent->cbName, "PRegistryEntry");
849  strcpy(prent->name, name);
850  strcpy(prent->ip, ip);
851  prent->port = atoi(port);
852  } else {
853  skn_logger(SD_WARNING, "Internal Memory Error: Could not allocate memory for entry %d:%s !", name, psreg->count);
854  if (errors != NULL)
855  (*errors)++;
856 
857  return EXIT_FAILURE;
858  }
859 
860  return psreg->count;
861 }
862 
870  int errors = 0; // false
871 
873  service_registry_response_parse(psr, response, &errors);
875 
876  if (errors > 0) {
877  return EXIT_FAILURE; // false
878  } else {
879  return EXIT_SUCCESS; // true
880  }
881 }
882 
890  int errors = 0;
891 
893  service_registry_response_parse(psr, response, &errors);
894  if (errors > 0) {
895  free(psr);
896  return NULL; // false
897  }
898 
899  return psr;
900 }
901 
907 void service_registry_entry_response_message_log(const char * response) {
908  char * worker = NULL, *parser = NULL, *base = strdup(response);
909  parser = base;
910  skn_logger(SD_NOTICE, "Response Message:");
911  while (( ((worker = strsep(&parser, "|")) != NULL) ||
912  ((worker = strsep(&parser, "%")) != NULL) ||
913  ((worker = strsep(&parser, ";")) != NULL)) &&
914  (strlen(worker) > 8)) {
915  skn_logger(SD_NOTICE, "[%s]", worker);
916  }
917  free(base);
918 }
919 
927  PRegistryEntry prent = NULL;
928  int index = 0;
929 
930  if (psreg == NULL)
931  return NULL;
932 
933  while (index < psreg->count) {
934  if (strcmp(serviceName, psreg->entry[index]->name) == 0) {
935  prent = psreg->entry[index];
936  break;
937  }
938  index++;
939  }
940 
941  return prent;
942 }
943 
948  return psr->count;
949 }
950 
955  int index = 0;
956 
957  skn_logger(" ", "\nServiceRegistry:");
958  for (index = 0; index < psr->count; index++) {
959  skn_logger(SD_INFO, "RegistryEntry(%02d) name=%s, ip=%s, port=%d", index, psr->entry[index]->name, psr->entry[index]->ip, psr->entry[index]->port);
960  }
961  return index;
962 }
963 
970  int index = 0;
971  void * result = NULL;
972  char * names[4] = { "name", "ip", "port", NULL };
973  int offsets[4] = { offsetof(RegistryEntry, name),
974  offsetof(RegistryEntry, ip),
975  offsetof(RegistryEntry, port),
976  0
977  };
978  for (index = 0; names[index] != NULL; index++) {
979  if (strcmp(names[index], field) == 0) {
980  result = (void *) prent + offsets[index];
981  break;
982  }
983  }
984  return result;
985 }
986 
993 static void * service_registry_entry_create_helper(char *key, char **name, char **ip, char **port) {
994  int index = 0;
995  char * guess = NULL;
996  void * result = NULL;
997  char * names[4] = { "name", "ip", "port", NULL };
998  void * offsets[] = { name, ip, port, NULL };
999 
1000  skn_strip(key); // cleanup first
1001  for (index = 0; names[index] != NULL; index++) { // find direct match
1002  if (strcmp(names[index], key) == 0) {
1003  result = offsets[index];
1004  break;
1005  }
1006  }
1007  if (result == NULL) { // try to guess what the key is
1008  if ((guess = strstr(key, "e")) != NULL) {
1009  result = offsets[0];
1010  } else if ((guess = strstr(key, "i")) != NULL) {
1011  result = offsets[1];
1012  } else if ((guess = strstr(key, "t")) != NULL) {
1013  result = offsets[2];
1014  }
1015  }
1016 
1017  return result;
1018 }
1019 
1031 static int service_registry_response_parse(PServiceRegistry psreg, const char *response, int *errors) {
1032  int control = 1;
1033  char *base = NULL, *psep = NULL, *resp = NULL, *line = NULL,
1034  *keypair = NULL, *element = NULL,
1035  *name = NULL, *ip = NULL, *pport = NULL,
1036  **meta = NULL;
1037 
1038  base = resp = strdup(response);
1039 
1040  if (strstr(response, "|")) {
1041  psep = "|";
1042  } else if (strstr(response, "%")) {
1043  psep = "%";
1044  } else if (strstr(response, ";")) {
1045  psep = ";";
1046  }
1047 
1048  while ((line = strsep(&resp, psep)) != NULL) {
1049  if (strlen(line) < 16) {
1050  continue;
1051  }
1052 
1053  pport = ip = name = NULL;
1054  while ((keypair = strsep(&line, ",")) != NULL) {
1055  if (strlen(keypair) < 1) {
1056  continue;
1057  }
1058 
1059  control = 1;
1060  element = strstr(keypair, "=");
1061  if (element != NULL) {
1062  element[0] = 0;
1063  meta = service_registry_entry_create_helper(keypair, &name, &ip, &pport);
1064  if (meta != NULL && (element[1] != 0)) {
1065  *meta = skn_strip(++element);
1066  } else {
1067  control = 0;
1068  if (errors != NULL)
1069  (*errors)++;
1070  skn_logger(SD_WARNING, "Response format failure: for name=%s, ip=%s, port=%s, first failing entry: [%s]", name, ip, pport, keypair);
1071  break;
1072  }
1073  }
1074  } // end while line
1075 
1076  if (control == 1) { // catch a breakout caused by no value
1077  service_registry_entry_create(psreg, name, ip, pport, errors);
1078  }
1079 
1080  } // end while buffer
1081 
1082  free(base);
1083  return psreg->count;
1084 }
1085 
1095  struct sockaddr_in remaddr; /* remote address */
1096  socklen_t addrlen = sizeof(remaddr); /* length of addresses */
1097  IPBroadcastArray aB;
1098  int vIndex = 0;
1099  char response[SZ_INFO_BUFF];
1100  char recvHostName[SZ_INFO_BUFF];
1101  signed int rLen = 0;
1102  struct timeval start;
1103 
1104  memset(response, 0, sizeof(response));
1105  memset(recvHostName, 0, sizeof(recvHostName));
1106 
1110 
1111  skn_logger(SD_NOTICE, "Socket Bound to %s", aB.ipAddrStr[aB.defaultIndex]);
1112 
1113  gettimeofday(&start, NULL);
1114  for (vIndex = 0; vIndex < aB.count; vIndex++) {
1115  memset(&remaddr, 0, sizeof(remaddr));
1116  remaddr.sin_family = AF_INET;
1117  remaddr.sin_addr.s_addr = inet_addr(aB.broadAddrStr[vIndex]);
1118  remaddr.sin_port = htons(SKN_FIND_RPI_PORT);
1119 
1120  if (sendto(i_socket, request, strlen(request), 0, (struct sockaddr *) &remaddr, addrlen) < 0) {
1121  skn_logger(SD_WARNING, "SendTo() Timed out; Failure code=%d, etext=%s", errno, strerror(errno));
1122  break;
1123  }
1124  skn_logger(SD_NOTICE, "Message Broadcasted on %s:%s:%d", aB.ifNameStr[vIndex], aB.broadAddrStr[vIndex], SKN_FIND_RPI_PORT);
1125  }
1126 
1128  skn_logger(SD_DEBUG, "Waiting for all responses\n");
1129  while (gi_exit_flag == SKN_RUN_MODE_RUN) { // depends on a socket timeout of 5 seconds
1130 
1131  rLen = recvfrom(i_socket, response, (SZ_INFO_BUFF - 1), 0, (struct sockaddr *) &remaddr, &addrlen);
1132  if (rLen == PLATFORM_ERROR) { // EAGAIN
1133  break;
1134  }
1135  response[rLen] = 0;
1136 
1137  rLen = getnameinfo(((struct sockaddr *) &remaddr), sizeof(struct sockaddr_in), recvHostName, (SZ_INFO_BUFF -1), NULL, 0, NI_DGRAM);
1138  if (rLen != 0) {
1139  skn_logger(SD_EMERG, "getnameinfo() failed: %s\n", gai_strerror(rLen));
1140  break;
1141  }
1142 
1143  skn_logger(SD_DEBUG, "Response(%1.3fs) received from %s @ %s:%d",
1144  skn_duration_in_milliseconds(&start, NULL),
1145  recvHostName,
1146  inet_ntoa(remaddr.sin_addr),
1147  ntohs(remaddr.sin_port)
1148  );
1149  service_registry_response_parse(psr, response, NULL);
1150  }
1151 
1152  return (psr);
1153 }
1154 
1160  int index = 0;
1161 
1162  if (psreg == NULL)
1163  return;
1164 
1165  for (index = 0; index < psreg->count; index++) {
1166  free(psreg->entry[index]);
1167  }
1168  free(psreg);
1169 }
char ifNameStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:68
static void skn_locator_print_usage()
long skn_get_number_of_cpu_cores()
int skn_udp_host_create_regular_socket(int port, double rcvTimeout)
double skn_duration_in_milliseconds(struct timeval *pstart, struct timeval *pend)
struct _serviceRequest * PServiceRequest
char * gd_pch_service_name
#define ARY_MAX_INTF
Definition: cmdDC.c:62
char ipAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:69
char gd_ch_program_desc[SZ_INFO_BUFF]
#define PLATFORM_ERROR
Definition: cmdDC.c:63
#define SD_INFO
int service_registry_entry_count(PServiceRegistry psr)
#define SD_DEBUG
void get_default_interface_name_and_ipv4_address(char *intf, char *ipv4)
char maskAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:70
int port
int service_registry_provider(int i_socket, char *response)
char gd_ch_intfName[SZ_CHAR_BUFF]
void signals_init()
int skn_time_delay(double delay_time)
#define SZ_CHAR_BUFF
Definition: cmdDC.c:57
int gd_i_update
void signals_cleanup(int sig)
int skn_handle_locator_command_line(int argc, char **argv)
char * gd_pch_message
int gd_i_i2c_address
char cbName[SZ_CHAR_BUFF]
Definition: cmdDC.c:66
char gd_ch_hostName[SZ_CHAR_BUFF]
uid_t skn_get_userids()
static void exit_handler(int sig)
#define SD_ERR
static int service_registry_response_parse(PServiceRegistry psreg, const char *response, int *errors)
#define SZ_LINE_BUFF
Definition: cmdDC.c:58
#define SZ_COMM_BUFF
Definition: cmdDC.c:59
int gd_i_unique_registry
#define SD_WARNING
static void * service_registry_entry_create_helper(char *key, char **name, char **ip, char **port)
#define SD_EMERG
void service_registry_entry_response_message_log(const char *response)
#define SZ_INFO_BUFF
Definition: cmdDC.c:56
PServiceRegistry service_registry_get_via_udp_broadcast(int i_socket, char *request)
#define SKN_RPI_DISPLAY_SERVICE_PORT
int gd_i_display
char gd_ch_program_name[SZ_INFO_BUFF]
char * skn_strip(char *alpha)
int generate_uname_info(char *msg)
int defaultIndex
Definition: cmdDC.c:72
int get_default_interface_name(char *pchDefaultInterfaceName)
char chDefaultIntfName[SZ_CHAR_BUFF]
Definition: cmdDC.c:67
char name[SZ_INFO_BUFF]
#define SKN_RUN_MODE_RUN
int service_registry_valiadate_response_format(const char *response)
signed int gd_i_debug
PServiceRegistry service_registry_valiadated_registry(const char *response)
struct _serviceRegistry * PServiceRegistry
sig_atomic_t gi_exit_flag
int service_registry_list_entries(PServiceRegistry psr)
void service_registry_destroy(PServiceRegistry psreg)
char cbName[SZ_CHAR_BUFF]
struct _serviceEntry * PRegistryEntry
char request[SZ_INFO_BUFF]
char cbName[SZ_CHAR_BUFF]
char response[SZ_INFO_BUFF]
PServiceRequest skn_service_request_create(PRegistryEntry pre, int host_socket, char *request)
char gd_ch_hostShortName[SZ_CHAR_BUFF]
char cbName[SZ_CHAR_BUFF]
#define SD_NOTICE
int get_broadcast_ip_array(PIPBroadcastArray paB)
void * service_registry_get_entry_field_ref(PRegistryEntry prent, char *field)
static PServiceRegistry service_registry_create()
int skn_udp_service_request(PServiceRequest psr)
static int service_registry_entry_create(PServiceRegistry psreg, char *name, char *ip, char *port, int *errors)
int skn_udp_host_create_broadcast_socket(int port, double rcvTimeout)
PRegistryEntry pre
char broadAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:71
char * gd_pch_effective_userid
int generate_datetime_info(char *msg)
char gd_ch_ipAddress[SZ_CHAR_BUFF]
int generate_loadavg_info(char *msg)
char ip[SZ_INFO_BUFF]
void skn_program_name_and_description_set(const char *name, const char *desc)
PRegistryEntry service_registry_find_entry(PServiceRegistry psreg, char *serviceName)
#define PACKAGE_VERSION
Definition: cmdDC.c:19
PRegistryEntry entry[ARY_MAX_REGISTRY]
#define TZ_ADJUST
#define SKN_FIND_RPI_PORT
int gd_i_socket