RPi Locator and Display Services
cmdDC.c
Go to the documentation of this file.
1 
15 /*
16  * Program Standards passed from compiler
17  */
18 #ifndef PACKAGE_VERSION
19  #define PACKAGE_VERSION "0.9.0"
20 #endif
21 #ifndef PACKAGE_NAME
22  #define PACKAGE_NAME "cmdDC"
23 #endif
24 #ifndef PACKAGE_DESCRIPTION
25  #define PACKAGE_DESCRIPTION "Send Message to Raspberry Pi's on the network."
26 #endif
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <glib.h>
32 #include <gio/gio.h>
33 #include <glib-unix.h>
34 #include <gio/gnetworking.h>
35 #include <ifaddrs.h>
36 
37 #ifndef G_OPTION_FLAG_NONE
38  #define G_OPTION_FLAG_NONE 0
39 #endif
40 
41 
42 #define UDP_DISPLAY_COMM_PORT 48029
43 #define UDP_BROADCAST_PORT 48028
44 #define UDP_REGULAR_PORT 48027
45 #define UDP_CLIENTS_PORT 48026
46 #define MS_TEN_MINUTES 600000
47 #define MSG_DELAY_INTERVAL 10
48 
49 #define SZ_TIMESTAMP_BUFF 32
50 #define SZ_PARAMS_BUFF 64
51 #define SZ_RMTADDR_BUFF 256
52 #define SZ_MESSAGE_BUFF 512
53 #define SZ_RESPONSE_BUFF 256
54 
55 #define SZ_CHAR_LABEL 48
56 #define SZ_INFO_BUFF 256
57 #define SZ_CHAR_BUFF 128
58 #define SZ_LINE_BUFF 512
59 #define SZ_COMM_BUFF 256
60 
61 #define SKN_UDP_ANY_PORT 0
62 #define ARY_MAX_INTF 8
63 #define PLATFORM_ERROR -1
64 
65 typedef struct _ipBroadcastArray {
73  int count; // index = count - 1
75 
76 typedef struct _signalData {
77  GMainLoop *loop;
78  gchar * signalName;
80 
81 typedef struct _registryData {
82  gchar ch_timestamp[SZ_TIMESTAMP_BUFF];
83  gchar ch_from[SZ_RMTADDR_BUFF];
84  gchar ch_name[SZ_RMTADDR_BUFF];
85  gchar ch_ip[SZ_PARAMS_BUFF];
86  gchar ch_port[SZ_PARAMS_BUFF];
87  gchar pch_message[SZ_MESSAGE_BUFF];
89 
90 typedef struct _controlData {
91  GMainLoop *loop;
95  GSource *gCommSource;
96  GSocket *gSock;
97  GSocket *gBroadcastSocket;
98  GSocketAddress *gsDSAddr;
99  GSocketAddress *gsAddr;
100  GResolver *resolver;
101  GList *glRegistry;
102  PIPBroadcastArray paB;
104  guint gRegCount;
105  guint gErrorCount;
106  guint gMsgDelay;
107  gboolean gReady;
108  gchar ch_intfName[SZ_RMTADDR_BUFF];
109  gchar ch_this_ip[SZ_RMTADDR_BUFF];
110  gchar ch_display_service_name[SZ_RMTADDR_BUFF];
111  gchar ch_message[SZ_MESSAGE_BUFF];
112  gchar ch_request[SZ_MESSAGE_BUFF];
113  gchar ch_response[SZ_RESPONSE_BUFF];
115 
116 
117 gchar * skn_get_timestamp();
118 gint udp_registry_find_by_name(PRegData pr, gchar *pch_name);
119 PPRegData udp_registry_response_parser(PRegData msg, gchar *response);
120 gchar * skn_gio_condition_to_string(GIOCondition condition);
121 gboolean udp_initialize_message_send(PControlData pctrl);
122 
123 static gboolean cb_unix_signal_handler(PUSignalData psig);
124 static gboolean cb_udp_comm_request_handler(PControlData pctrl);
125 static gboolean cb_udp_comm_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl);
126 static gboolean cb_udp_broadcast_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl);
127 static gboolean cb_udp_registry_select_handler(PControlData pctrl);
128 
129 PIPBroadcastArray skn_get_default_interface_name_and_ipv4_address(char * intf, char * ipv4);
130 gint skn_get_broadcast_ip_array(PIPBroadcastArray paB);
131 gint skn_get_default_interface_name(char *pchDefaultInterfaceName);
132 gboolean skn_udp_network_broadcast_all_interfaces(GSocket *gSock, PIPBroadcastArray pab);
133 gchar * skn_strip(gchar * alpha);
134 
139 gchar * skn_strip(gchar * alpha) {
140  if (alpha == NULL || strlen(alpha) < 1)
141  return(alpha);
142 
143  int len = strlen(alpha);
144  int end = len - 1;
145  int start = 0;
146 
147  // use isgraph() or !isspace() vs isalnum() to allow ['|' ';' '%']
148  while ( !g_unichar_isgraph(alpha[end]) && end > 0 ) { // remove trailing non-alphanumeric chars
149  alpha[end--] = 0;
150  }
151 
152  len = strlen(alpha);
153  while ( !g_unichar_isalnum(alpha[start]) && start < len ) { // find first non-alpha stricter
154  start++;
155  }
156  if (start < len && start > 0) { // move in place
157  end = len - start;
158  memmove(&alpha[0], &alpha[start], end);
159  alpha[end] = 0;
160  }
161 
162  return(alpha);
163 }
164 
165 PIPBroadcastArray skn_get_default_interface_name_and_ipv4_address(gchar * intf, gchar * ipv4) {
166  PIPBroadcastArray paB = g_new0(IPBroadcastArray, 1);
167 
169  g_utf8_strncpy(intf, paB->chDefaultIntfName, SZ_CHAR_BUFF);
170  g_utf8_strncpy(ipv4, paB->ipAddrStr[paB->defaultIndex], SZ_CHAR_BUFF);
171  } else {
172  g_warning("[REGISTRY] InterfaceName and Address: unable to access information.");
173  return(NULL);
174  }
175  return (paB);
176 }
177 
185 gint skn_get_broadcast_ip_array(PIPBroadcastArray paB) {
186  struct ifaddrs * ifap;
187  struct ifaddrs * p;
188  gint rc = 0;
189 
190  memset(paB, 0, sizeof(IPBroadcastArray));
191  paB->count = 0;
192  paB->defaultIndex = 0;
193  strcpy(paB->cbName, "IPBroadcastArray");
194 
196  if (rc == EXIT_FAILURE) { // Alternate method for Mac: 'route -n -A inet'
197  g_warning("[REGISTRY] No Default Network Interfaces Found!.");
198  paB->chDefaultIntfName[0] = 0;
199  }
200  rc = getifaddrs(&ifap);
201  if (rc != 0) {
202  g_warning("[REGISTRY] No Network Interfaces Found at All ! %d:%d:%s", rc, errno, strerror(errno) );
203  return (PLATFORM_ERROR);
204  }
205  p = ifap;
206 
207  while (p && (paB->count < ARY_MAX_INTF)) {
208  if (p->ifa_addr != NULL && p->ifa_addr->sa_family == AF_INET && ((p->ifa_flags & IFF_BROADCAST) > 0)) {
209 
210  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_addr)->sin_addr, paB->ipAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
211  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_netmask)->sin_addr, paB->maskAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
212  inet_ntop(p->ifa_addr->sa_family, &((struct sockaddr_in *) p->ifa_broadaddr)->sin_addr, paB->broadAddrStr[paB->count], (SZ_CHAR_BUFF - 1));
213 
214  strncpy(paB->ifNameStr[paB->count], p->ifa_name, (SZ_CHAR_BUFF -1));
215 
216  /* Take match as the default */
217  if (strcmp(paB->chDefaultIntfName, p->ifa_name) == 0) {
218  paB->defaultIndex = paB->count;
219  }
220 
221  paB->count++;
222  }
223  p = p->ifa_next;
224  } // end while
225  freeifaddrs(ifap);
226 
227  return (paB->count);
228 }
229 
246 gint skn_get_default_interface_name(char *pchDefaultInterfaceName) {
247  FILE *f_route;
248  char line[SZ_INFO_BUFF], *dRoute = NULL, *iName = NULL;
249 
250  f_route = fopen("/proc/net/route", "r");
251  if (f_route != NULL) {
252  while (fgets(line, SZ_INFO_BUFF - 1, f_route)) {
253  iName = strtok(line, "\t");
254  dRoute = strtok(NULL, "\t");
255 
256  if (iName != NULL && dRoute != NULL) {
257  if (strcmp(dRoute, "00000000") == 0) {
258  strncpy(pchDefaultInterfaceName, iName, (SZ_INFO_BUFF - 1));
259  break;
260  }
261  }
262  }
263  fclose(f_route);
264 
265  return (EXIT_SUCCESS);
266  }
267  g_print("[REGISTRY] Opening ProcFs for RouteInfo Failed: %d:%s, Alternate method will be attempted.\n", errno, strerror(errno));
268 
269  f_route = popen("route -n get 0.0.0.0", "r"); // for linux 'route -n -A inet', with interface at line_word[7]
270  if (f_route != NULL) {
271  while (fgets(line, SZ_INFO_BUFF - 1, f_route)) {
272  dRoute = strtok(line, ":");
273  iName = strtok(NULL, "\n");
274  if (strcmp(skn_strip(dRoute), "interface") == 0) {
275  strncpy(pchDefaultInterfaceName, skn_strip(iName), (SZ_INFO_BUFF - 1));
276  break;
277  }
278  }
279  fclose(f_route);
280 
281  return (EXIT_SUCCESS);
282  } else {
283  g_warning("[REGISTRY] Alternate method to get RouteInfo Failed: %d:%s", errno, strerror(errno));
284  return (EXIT_FAILURE);
285  }
286 
287 }
297 gboolean skn_udp_network_broadcast_all_interfaces(GSocket *gSock, PIPBroadcastArray paB) {
298  struct sockaddr_in remaddr; /* remote address */
299  socklen_t addrlen = sizeof(remaddr); /* length of addresses */
300  gchar *request = "urn:rpilocator - Rpi Where Are You?";
301  gint vIndex = 0;
302  gint i_socket = g_socket_get_fd(gSock);
303 
304  g_print("[REGISTRY] Socket Bound to %s\n", paB->ipAddrStr[paB->defaultIndex]);
305 
306 
307  for (vIndex = 0; vIndex < paB->count; vIndex++) {
308  memset(&remaddr, 0, sizeof(remaddr));
309  remaddr.sin_family = AF_INET;
310  remaddr.sin_addr.s_addr = inet_addr(paB->broadAddrStr[vIndex]);
311  remaddr.sin_port = htons(UDP_BROADCAST_PORT);
312 
313  if (sendto(i_socket, request, strlen(request), 0, (struct sockaddr *) &remaddr, addrlen) < 0) {
314  g_warning("SendTo() Timed out; Failure code=%d, etext=%s", errno, strerror(errno));
315  break;
316  }
317  g_print("[REGISTRY] Query Broadcasted on %s:%s:%d\n", paB->ifNameStr[vIndex], paB->broadAddrStr[vIndex], UDP_BROADCAST_PORT);
318  }
319 
320  return(TRUE);
321 }
322 
323 gchar * skn_get_timestamp() {
324  GDateTime *stamp = g_date_time_new_now_local();
325  gchar *response = g_date_time_format(stamp,"%F.%T");
326 
327  return(response);
328 }
329 
330 gchar * skn_gio_condition_to_string(GIOCondition condition) {
331  gchar *value = NULL;
332 
333  switch(condition) {
334  case G_IO_IN:
335  value = "There is data to read.";
336  break;
337  case G_IO_OUT:
338  value = "Data can be written (without blocking).";
339  break;
340  case G_IO_PRI:
341  value = "There is urgent data to read.";
342  break;
343  case G_IO_ERR:
344  value = "Error condition.";
345  break;
346  case G_IO_HUP:
347  value = "Hung up (the connection has been broken, usually for pipes and sockets).";
348  break;
349  case G_IO_NVAL:
350  value = "Invalid request. The file descriptor is not open.";
351  break;
352  default:
353  value = "Unknown GIOCondition!";
354  }
355 
356  return (value);
357 }
358 
368 PPRegData udp_registry_response_parser(PRegData msg, gchar *response) {
369  gboolean rc = FALSE;
370  gboolean final_rc = FALSE;
371  gchar ** lines = NULL;
372  gchar *current_line = NULL;
373  gchar ** entries = NULL;
374  gchar *current_entry = NULL;
375  gchar ** key_value = NULL;
376  PRegData *msgs;
377  PRegData preg = NULL;
378  gint h_index = 0;
379  gint v_index = 0;
380  gint o_index = 0;
381  gint a_count = 0;
382  gint e_count = 0;
383 
384  if (g_utf8_strchr(response, -1, '|') == NULL) { // must be a registry entry
385  return (NULL);
386  }
387 
388  lines = g_strsplit_set(response, "|;%", -1); // get whole entries
389  if ((NULL == lines) || (g_strv_length(lines) < 1)) {
390  return(NULL);
391  }
392 
393  a_count = g_strv_length(lines);
394  msgs = g_new0(PRegData, a_count);
395  for(o_index = 0; o_index < a_count; o_index += 1) {
396  msgs[o_index] = g_new0(RegData, 1);
397  memmove(msgs[o_index], msg, sizeof(RegData));
398  }
399 
400  o_index = 0;
401  current_line = lines[h_index];
402  while ((NULL != current_line) && (h_index < a_count)) { // do each entry
403  if(g_utf8_strlen(current_line, -1) < 1) {
404  current_line = lines[++h_index];
405  continue;
406  }
407 
408  v_index = 0;
409  entries = g_strsplit_set(current_line, ",", -1);
410  current_entry = entries[v_index];
411  e_count = g_strv_length(entries);
412  preg = msgs[o_index];
413  rc = FALSE;
414 
415  while((NULL != current_entry) && (v_index < e_count)) {
416  if(g_utf8_strlen(current_entry, -1) < 1) {
417  current_entry = entries[++v_index];
418  continue;
419  }
420 
421  // get name, ip, port
422 
423  key_value = g_strsplit_set(current_entry, "=", -1);
424  if((key_value != NULL) && (g_strv_length(key_value) > 0)) {
425  if(g_strrstr(key_value[0], "a") != NULL) {
426  final_rc = rc = TRUE;
427  g_utf8_strncpy(preg->ch_name, key_value[1], SZ_RMTADDR_BUFF);
428  }
429  if(g_strrstr(key_value[0], "i") != NULL) {
430  final_rc = rc = TRUE;
431  g_utf8_strncpy(preg->ch_ip, key_value[1], SZ_RMTADDR_BUFF);
432  }
433  if(g_strrstr(key_value[0], "o") != NULL) {
434  final_rc = rc = TRUE;
435  g_utf8_strncpy(preg->ch_port, key_value[1], SZ_RMTADDR_BUFF);
436  }
437  }
438  g_strfreev(key_value);
439  current_entry = entries[++v_index];
440  } // end entries
441 
442  if (rc && (g_utf8_strlen(preg->ch_ip, -1) > 6)) { // only move if used and valid
443  o_index += 1;
444  }
445  g_strfreev(entries);
446 
447  h_index +=1;
448  current_line = lines[h_index];
449  } // end lines
450  g_strfreev(lines);
451 
452  if (final_rc) {
453  while(o_index < a_count) {
454  g_free(msgs[o_index]);
455  msgs[o_index++] = NULL;
456  }
457  return(msgs);
458  } else {
459  while(o_index < a_count) {
460  g_free(msgs[o_index]);
461  msgs[o_index++] = NULL;
462  }
463  g_free(msgs);
464  return(NULL);
465  }
466 }
467 
468 static gboolean cb_udp_comm_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl) {
469  GError *error = NULL;
470  gssize gss_receive = 0;
471  gchar *stamp = skn_get_timestamp();
472  gchar * rmtHost = NULL;
473  GSocketAddress *gsRmtAddr = NULL;
474  GInetAddress *gsAddr = NULL;
475  gchar *converted = NULL;
476 
477  if ( NULL == pctrl) { /* SHUTDOWN THE MAIN LOOP */
478  g_message("DisplayClient::cb_udp_comm_response_handler(error) Invalid Pointer");
479  return ( G_SOURCE_REMOVE );
480  }
481 
482  gss_receive = g_socket_receive_from(gSock, &gsRmtAddr, pctrl->ch_request, sizeof(pctrl->ch_request), NULL, &error);
483  if (error != NULL) { // gss_receive = Number of bytes read, or 0 if the connection was closed by the peer, or -1 on error
484  g_error("g_socket_receive() => %s", error->message);
485  g_clear_error(&error);
486  }
487  if (gss_receive > 0 ) {
488  pctrl->ch_request[gss_receive] = 0;
489  if (G_IS_INET_SOCKET_ADDRESS(gsRmtAddr) ) {
490  gsAddr = g_inet_socket_address_get_address( G_INET_SOCKET_ADDRESS(gsRmtAddr) );
491  if ( G_IS_INET_ADDRESS(gsAddr) ) {
492  g_object_ref(gsAddr);
493  rmtHost = g_resolver_lookup_by_address (pctrl->resolver, gsAddr, NULL, NULL);
494  if (NULL == rmtHost) {
495  rmtHost = g_inet_address_to_string ( gsAddr);
496  }
497  }
498  }
499 
500  /*
501  * Convert to UTF8 */
502  converted = g_convert (pctrl->ch_request, gss_receive, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
503  if (NULL != converted) {
504  g_utf8_strncpy(pctrl->ch_request, converted, sizeof(pctrl->ch_request));
505  g_free(converted);
506  }
507 
508  g_snprintf(pctrl->ch_response, sizeof(pctrl->ch_response), "[%s]RESPONSE From=%s, Msg=%s", stamp, rmtHost, pctrl->ch_request);
509  pctrl->gErrorCount = 0; // reset send errors
510  } else {
511  g_snprintf(pctrl->ch_response, sizeof(pctrl->ch_response), "%s", "Error: Input not Usable!");
512  }
513 
514  g_print("%s\n", pctrl->ch_response);
515  g_free(stamp);
516 
517  if ( G_IS_INET_ADDRESS(gsAddr) )
518  g_object_unref(gsAddr);
519 
520  if ( G_IS_INET_SOCKET_ADDRESS(gsRmtAddr) )
521  g_object_unref(gsRmtAddr);
522 
523  return (G_SOURCE_CONTINUE);
524 }
525 
526 static gboolean cb_udp_comm_request_handler(PControlData pctrl) {
527  GError *error = NULL;
528 
529  if ( NULL == pctrl) { /* SHUTDOWN THE MAIN LOOP */
530  g_message("DisplayClient::cb_udp_comm_request_handler(error) Invalid Pointer");
531  return ( G_SOURCE_REMOVE );
532  }
533 
534  /*
535  * TODO
536  *
537  * Add routine to change pctrl->ch_message to one of
538  * many different status messages. The <project>/src directory
539  * contains LibC routines for LoadAvg, CPU Util, Datetime, Cpu Temps, etc.
540  *
541  */
542 
543  g_socket_send_to (pctrl->gSock, pctrl->gsDSAddr, pctrl->ch_message, strlen(pctrl->ch_message), NULL, &error);
544  if (error != NULL) { // gss_send = Number of bytes written (which may be less than size ), or -1 on error
545  g_error("g_socket_send_to() => %s", error->message);
546  g_clear_error(&error);
547  pctrl->gErrorCount += 1;
548  if(pctrl->gErrorCount > 10) {
549  g_message("DisplayClient::cb_udp_comm_request_handler(error) Display Service @ %s, Not Responding!", pctrl->ch_display_service_name);
550  g_main_loop_quit(pctrl->loop);
551  return ( G_SOURCE_REMOVE );
552  } else {
553  return (G_SOURCE_CONTINUE);
554  }
555  }
556 
557  g_print("%s\n", pctrl->ch_message); // Todo not needed
558 
559  return (G_SOURCE_CONTINUE);
560 }
561 
562 static gboolean cb_udp_broadcast_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl) {
563  GError *error = NULL;
564  GSocketAddress *gsRmtAddr = NULL;
565  GInetAddress *gsAddr = NULL;
566  PRegData message = NULL;
567  PRegData msg = NULL;
568  PRegData *msgs;
569  gchar * rmtHost = NULL;
570  gssize gss_receive = 0;
571  gchar *stamp = skn_get_timestamp();
572  gint h_index = 0;
573  gchar *converted = NULL;
574 
575 
576  if ((condition & G_IO_HUP) || (condition & G_IO_ERR) || (condition & G_IO_NVAL)) { /* SHUTDOWN THE MAIN LOOP */
577  g_message("DisplayService::cb_udp_broadcast_response_handler(error) Operational Error / Shutdown Signaled => %s\n", skn_gio_condition_to_string(condition));
578  g_main_loop_quit(pctrl->loop);
579  return ( G_SOURCE_REMOVE );
580  }
581  if (condition != G_IO_IN) {
582  g_message("DisplayService::cb_udp_broadcast_response_handler(error) NOT G_IO_IN => %s\n", skn_gio_condition_to_string(condition));
583  return (G_SOURCE_CONTINUE);
584  }
585 
586  /*
587  * Allocate a new queue message and read incoming request directly into it */
588  message = g_new0(RegData,1);
589 
590  /*
591  * If socket times out before reading data any operation will error with 'G_IO_ERROR_TIMED_OUT'.
592  * Read Request Message and get Requestor IP Address or Name
593  */
594  gss_receive = g_socket_receive_from (gSock, &gsRmtAddr, message->pch_message, sizeof(message->pch_message), NULL, &error);
595  if (error != NULL) { // gss_receive = Number of bytes read, or 0 if the connection was closed by the peer, or -1 on error
596  g_error("g_socket_receive_from() => %s", error->message);
597  g_clear_error(&error);
598  g_free(message);
599  g_free(stamp);
600  return (G_SOURCE_CONTINUE);
601  }
602  if (gss_receive > 0 ) {
603  if (G_IS_INET_SOCKET_ADDRESS(gsRmtAddr) ) {
604  gsAddr = g_inet_socket_address_get_address( G_INET_SOCKET_ADDRESS(gsRmtAddr) );
605  if ( G_IS_INET_ADDRESS(gsAddr) ) {
606  g_object_ref(gsAddr);
607  rmtHost = g_resolver_lookup_by_address (pctrl->resolver, gsAddr, NULL, NULL);
608  if (NULL == rmtHost) {
609  rmtHost = g_inet_address_to_string ( gsAddr);
610  }
611  }
612  }
613 
614  /*
615  * Convert to UTF8
616  */
617  converted = g_convert (message->pch_message, gss_receive, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
618  if (NULL != converted) {
619  g_utf8_strncpy(message->pch_message, converted, sizeof(message->pch_message));
620  g_free(converted);
621  }
622 
623  g_utf8_strncpy(message->ch_timestamp, stamp, sizeof(message->ch_timestamp));
624  g_utf8_strncpy(message->ch_from, rmtHost, sizeof(message->ch_from));
625  if ( (msgs = udp_registry_response_parser(message, message->pch_message)) != NULL ) {
626  /*
627  * Send it to be processed by a message handler */
628  g_free(message);
629  h_index = 0;
630  msg = (PRegData)msgs[h_index];
631  while ( msg != NULL ) {
632  g_print("[%s]REGISTRY From=%s, Node=%s, IP=%s, Port=%s\n", msg->ch_timestamp, msg->ch_from, msg->ch_name, msg->ch_ip, msg->ch_port);
633  if (g_strcmp0(pctrl->ch_display_service_name, msg->ch_name) == 0) {
634  pctrl->glRegistry = g_list_prepend(pctrl->glRegistry, msg);
635  } else {
636  g_free(msg);
637  }
638  msg = (PRegData)msgs[++h_index];
639  }
640  g_free(msgs);
641  } else {
642  g_free(message);
643  }
644  }
645  g_free(stamp);
646  g_free(rmtHost);
647 
648  if ( G_IS_INET_ADDRESS(gsAddr) )
649  g_object_unref(gsAddr);
650 
651  if ( G_IS_INET_SOCKET_ADDRESS(gsRmtAddr) )
652  g_object_unref(gsRmtAddr);
653 
654  return (G_SOURCE_CONTINUE);
655 }
656 
657 gboolean udp_initialize_message_send(PControlData pctrl) {
658  GError *error = NULL;
659  GInetAddress *anyAddr = NULL;
660 
661  g_return_val_if_fail((NULL != pctrl), G_SOURCE_REMOVE);
662 
663  pctrl->gSock = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error);
664  if (error != NULL) {
665  g_error("g_socket_new() => %s", error->message);
666  g_clear_error(&error);
667  return(FALSE);
668  }
669 
670  anyAddr = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
671  pctrl->gsAddr = g_inet_socket_address_new(anyAddr, SKN_UDP_ANY_PORT);
672  g_object_unref(anyAddr);
673 
674  g_socket_bind(pctrl->gSock, pctrl->gsAddr, TRUE, &error);
675  if (error != NULL) {
676  g_error("g_socket_bind() => %s", error->message);
677  g_clear_error(&error);
678  exit(FALSE);
679  }
680 
681  pctrl->gCommSource = g_socket_create_source (pctrl->gSock, G_IO_IN, NULL);
682  g_source_set_callback (pctrl->gCommSource, (GSourceFunc) cb_udp_comm_response_handler, pctrl, NULL); // its really a GSocketSourceFunc
683  g_source_attach (pctrl->gCommSource, NULL);
684 
685  /*
686  * Setup Timer to drive repeated Message to Display Service */
687  g_timeout_add ((pctrl->gMsgDelay * 1000), (GSourceFunc)cb_udp_comm_request_handler, pctrl);
688 
689 
690  return (TRUE);
691 }
692 
693 // (*GCompareFunc) 0 if found, -1 if not found
694 gint udp_registry_find_by_name(PRegData pr, gchar *pch_name) {
695  return( g_strcmp0(pr->ch_name, pch_name) );
696 }
697 
698 static gboolean cb_udp_registry_select_handler(PControlData pctrl) {
699  GList *registry = NULL;
700  PRegData preg = NULL;
701  GInetAddress *anyAddr = NULL;
702 
703  g_return_val_if_fail((NULL != pctrl), G_SOURCE_REMOVE);
704 
705  /*
706  * Find in Registry the selected service name */
707  if (g_list_length(pctrl->glRegistry) < 1) {
708  pctrl->gRegistryQueries++;
709 
710  if ((pctrl->gRegistryQueries % 15) == 0) { // every 30 seconds redo query
711  g_print("[REGISTRY] Looking for [%s] in Rpi Registry every 30 seconds. StandBy...\n", pctrl->ch_display_service_name);
713  }
714 
715  return (G_SOURCE_CONTINUE);
716  }
717 
718  if ( NULL == (registry = g_list_find_custom (pctrl->glRegistry, pctrl->ch_display_service_name, (GCompareFunc)udp_registry_find_by_name)) ) {
719  return (G_SOURCE_CONTINUE);
720  }
721 
722  preg = registry->data; // now extract ip and port
723  /* Display Service Address */
724 
725  anyAddr = g_inet_address_new_from_string(preg->ch_ip); // g_object_unref(dIP) when done
726  if (NULL == anyAddr) {
727  g_error("g_inet_address_new_from_string() Failed => %s, %s", preg->ch_name, preg->ch_ip);
728  return(G_SOURCE_CONTINUE);
729  }
730  pctrl->gsDSAddr = g_inet_socket_address_new(anyAddr, g_ascii_strtoll(preg->ch_port, NULL, 10));
731  g_object_unref(anyAddr);
732 
733  pctrl->gReady = udp_initialize_message_send(pctrl);
734 
735  if (pctrl->gReady) {
736  g_print("[REGISTRY] FOUND %s on ip %s:%s, sending your message %d seconds.\n\n", preg->ch_name, preg->ch_ip, preg->ch_port, pctrl->gMsgDelay);
737  return (G_SOURCE_REMOVE);
738  } else {
739  return (G_SOURCE_CONTINUE);
740  }
741 }
742 
743 static gboolean cb_unix_signal_handler(PUSignalData psig) {
744  g_message("DisplayClient::cb_unix_signal_handler() %s Unix Signal Received => Shutdown Initiated!\n", psig->signalName);
745  g_main_loop_quit(psig->loop);
746  return ( G_SOURCE_REMOVE );
747 }
748 
749 int main(int argc, char **argv) {
750 
751  ControlData cData;
752  GSource * gBroadSource = NULL;
753  GError *error = NULL;
754  GSocket *gSock;
755  GSocketAddress *gsAddr = NULL;
756  GInetAddress *anyAddr = NULL;
757 
758  gchar * pch_display_service_name = NULL;
759  gchar * pch_message = NULL;
760 
762  cData.gErrorCount = 0;
763  cData.gRegCount = 0;
764  cData.gRegistryQueries = 0;
765  cData.gReady = FALSE;
766 
767  cData.glRegistry = NULL;
768  cData.sigTerm.signalName = "SIGTERM";
769  cData.sigInt.signalName = "SIGINT";
770  cData.sigHup.signalName = "SIGHUP";
771  cData.gErrorCount = 0;
772  cData.resolver = g_resolver_get_default();
773 
774  GOptionContext *gOptions = NULL;
775  GOptionEntry pgmOptions[] = {
776  {"display_service_name", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &pch_display_service_name, "DisplayService Name", "gtk_display_service"},
777  {"display_message", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &pch_message, "Message to send to DisplayService", "single-quoted-string"},
778  {"message_interval_delay", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &cData.gMsgDelay, "Send one message every Interval.", "[1 to 300] seconds, default no-repeat"},
779  {NULL}
780  };
781 
782  gOptions = g_option_context_new ("UDP message display client for IOT.");
783  g_option_context_add_main_entries (gOptions, pgmOptions, NULL);
784  g_option_context_parse (gOptions, &argc, &argv, &error);
785  if (error != NULL) {
786  g_error("g_option_context_parse() => %s", error->message);
787  g_clear_error(&error);
788  exit(EXIT_FAILURE);
789  }
790  g_option_context_free(gOptions);
791 
792  if (NULL == pch_message) {
793  g_utf8_strncpy(cData.ch_message, "GTK-+3.0 Rocks with GLIB-2.0 on any platform! (cldc)", sizeof(cData.ch_message));
794  } else {
795  g_utf8_strncpy(cData.ch_message, pch_message, sizeof(cData.ch_message));
796  g_free(pch_message);
797  }
798  if (NULL == pch_display_service_name) {
799  g_utf8_strncpy(cData.ch_display_service_name, "gtk_display_service", sizeof(cData.ch_display_service_name));
800  } else {
801  g_utf8_strncpy(cData.ch_display_service_name, pch_display_service_name, sizeof(cData.ch_display_service_name));
802  g_free(pch_display_service_name);
803  }
804  if (0 == cData.gMsgDelay) {
806  }
807 
808  cData.paB = NULL;
809  cData.paB = skn_get_default_interface_name_and_ipv4_address((char *)&cData.ch_intfName, (char *)&cData.ch_this_ip);
810  if (NULL == cData.paB) {
811  g_error("skn_skn_get_default_interface_name_and_ipv4_address() => Unable to discover network interface or non-available.");
812  exit(EXIT_FAILURE);
813  }
814 
815  cData.sigHup.loop = cData.sigTerm.loop = cData.sigInt.loop = cData.loop = g_main_loop_new(NULL, FALSE);
816 
817  /*
818  * Create broadcast UDP Socket for receiving messages */
819  cData.gBroadcastSocket = gSock = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &error);
820  if (error != NULL) {
821  g_error("g_socket_new(broadcast) => %s", error->message);
822  g_clear_error(&error);
823  exit(EXIT_FAILURE);
824  }
825  g_socket_set_broadcast(gSock, TRUE);
826 
827  anyAddr = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
828  gsAddr = g_inet_socket_address_new(anyAddr, SKN_UDP_ANY_PORT);
829  g_object_unref(anyAddr);
830 
831  g_socket_bind(gSock, gsAddr, TRUE, &error);
832  if (error != NULL) {
833  g_error("g_socket_bind() => %s", error->message);
834  g_clear_error(&error);
835  exit(EXIT_FAILURE);
836  }
837 
838  gBroadSource = g_socket_create_source(gSock, G_IO_IN, NULL);
839  g_source_ref(gBroadSource);
840  g_source_set_callback (gBroadSource, (GSourceFunc) cb_udp_broadcast_response_handler, &cData, NULL); // its really a GSocketSourceFunc
841  g_source_attach (gBroadSource, NULL);
842 
843  /*
844  * Handle ctrl-break and kill signals cleanly */
845  g_unix_signal_add (SIGINT, (GSourceFunc) cb_unix_signal_handler, &cData.sigInt); // SIGINT signal (Ctrl+C)
846  g_unix_signal_add (SIGHUP, (GSourceFunc) cb_unix_signal_handler, &cData.sigHup);
847  g_unix_signal_add (SIGTERM,(GSourceFunc) cb_unix_signal_handler, &cData.sigTerm);
848 
849  /*
850  * Broadcast Registry Request: 10.100.1.255 */
851  if ( skn_udp_network_broadcast_all_interfaces(gSock, cData.paB) ) {
852  /*
853  * Setup 2secTimer to find display_service before starting rest */
854  g_timeout_add (2000, (GSourceFunc)cb_udp_registry_select_handler, &cData);
855 
856  g_print("[REGISTRY] Looking for [%s] in Rpi Registry. StandBy...\n", cData.ch_display_service_name);
857 
858  g_main_loop_run(cData.loop);
859  }
860 
861  g_main_loop_unref(cData.loop);
862 
863  g_source_unref(gBroadSource);
864  g_object_unref(gSock);
865  g_object_unref(gsAddr);
866 
867  if (cData.gReady) {
868  g_source_unref(cData.gCommSource);
869  g_object_unref(cData.gSock);
870  g_object_unref(cData.gsDSAddr);
871  g_object_unref(cData.gsAddr);
872  }
873 
874  g_free(cData.paB);
875  g_list_free_full(cData.glRegistry, (GDestroyNotify)g_free);
876 
877  g_message("cmdDC: normal shutdown...");
878 
879  exit(EXIT_SUCCESS);
880 }
gchar * skn_gio_condition_to_string(GIOCondition condition)
Definition: cmdDC.c:330
char ifNameStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:68
USignalData sigHup
Definition: cmdDC.c:94
#define ARY_MAX_INTF
Definition: cmdDC.c:62
char ipAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:69
gchar ch_request[SZ_MESSAGE_BUFF]
Definition: cmdDC.c:112
gchar * skn_strip(gchar *alpha)
Definition: cmdDC.c:139
#define PLATFORM_ERROR
Definition: cmdDC.c:63
char maskAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:70
struct _signalData USignalData
gchar ch_ip[SZ_PARAMS_BUFF]
Definition: cmdDC.c:85
gchar ch_message[SZ_MESSAGE_BUFF]
Definition: cmdDC.c:111
USignalData sigTerm
Definition: cmdDC.c:92
gchar ch_response[SZ_RESPONSE_BUFF]
Definition: cmdDC.c:113
GSocket * gBroadcastSocket
Definition: cmdDC.c:97
static gboolean cb_udp_registry_select_handler(PControlData pctrl)
Definition: cmdDC.c:698
guint gRegCount
Definition: cmdDC.c:104
#define SZ_CHAR_BUFF
Definition: cmdDC.c:57
#define FALSE
struct _signalData * PUSignalData
char cbName[SZ_CHAR_BUFF]
Definition: cmdDC.c:66
struct _ipBroadcastArray * PIPBroadcastArray
#define SZ_RESPONSE_BUFF
Definition: cmdDC.c:53
gchar ch_port[SZ_PARAMS_BUFF]
Definition: cmdDC.c:86
#define UDP_BROADCAST_PORT
Definition: cmdDC.c:43
static gboolean cb_unix_signal_handler(PUSignalData psig)
Definition: cmdDC.c:743
gchar * skn_get_timestamp()
Definition: cmdDC.c:323
#define SZ_PARAMS_BUFF
Definition: cmdDC.c:50
GSocketAddress * gsAddr
Definition: cmdDC.c:99
#define TRUE
gint skn_get_default_interface_name(char *pchDefaultInterfaceName)
Definition: cmdDC.c:246
#define SZ_RMTADDR_BUFF
Definition: cmdDC.c:51
struct _ipBroadcastArray IPBroadcastArray
gboolean skn_udp_network_broadcast_all_interfaces(GSocket *gSock, PIPBroadcastArray pab)
Definition: cmdDC.c:297
struct _registryData RegData
GSocket * gSock
Definition: cmdDC.c:96
struct _controlData * PControlData
GSocketAddress * gsDSAddr
Definition: cmdDC.c:98
#define SZ_INFO_BUFF
Definition: cmdDC.c:56
#define G_OPTION_FLAG_NONE
Definition: cmdDC.c:38
gchar pch_message[SZ_MESSAGE_BUFF]
Definition: cmdDC.c:87
GMainLoop * loop
Definition: cmdDC.c:91
static gboolean cb_udp_comm_request_handler(PControlData pctrl)
Definition: cmdDC.c:526
#define SKN_UDP_ANY_PORT
Definition: cmdDC.c:61
guint gErrorCount
Definition: cmdDC.c:105
gchar * signalName
Definition: cmdDC.c:78
gchar ch_name[SZ_RMTADDR_BUFF]
Definition: cmdDC.c:84
#define MSG_DELAY_INTERVAL
Definition: cmdDC.c:47
int defaultIndex
Definition: cmdDC.c:72
struct _registryData ** PPRegData
static gboolean cb_udp_comm_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl)
Definition: cmdDC.c:468
gchar ch_this_ip[SZ_RMTADDR_BUFF]
Definition: cmdDC.c:109
char chDefaultIntfName[SZ_CHAR_BUFF]
Definition: cmdDC.c:67
static gboolean cb_udp_broadcast_response_handler(GSocket *gSock, GIOCondition condition, PControlData pctrl)
Definition: cmdDC.c:562
PIPBroadcastArray skn_get_default_interface_name_and_ipv4_address(char *intf, char *ipv4)
struct _registryData * PRegData
PIPBroadcastArray paB
Definition: cmdDC.c:102
struct _controlData ControlData
GList * glRegistry
Definition: cmdDC.c:101
gchar ch_from[SZ_RMTADDR_BUFF]
Definition: cmdDC.c:83
USignalData sigInt
Definition: cmdDC.c:93
#define SZ_MESSAGE_BUFF
Definition: cmdDC.c:52
gint skn_get_broadcast_ip_array(PIPBroadcastArray paB)
Definition: cmdDC.c:185
#define SZ_TIMESTAMP_BUFF
Definition: cmdDC.c:49
gboolean gReady
Definition: cmdDC.c:107
guint gRegistryQueries
Definition: cmdDC.c:103
gchar ch_timestamp[SZ_TIMESTAMP_BUFF]
Definition: cmdDC.c:82
gboolean udp_initialize_message_send(PControlData pctrl)
Definition: cmdDC.c:657
GMainLoop * loop
Definition: cmdDC.c:77
char broadAddrStr[ARY_MAX_INTF][SZ_CHAR_BUFF]
Definition: cmdDC.c:71
guint gMsgDelay
Definition: cmdDC.c:106
GResolver * resolver
Definition: cmdDC.c:100
int main(int argc, char **argv)
Definition: cmdDC.c:749
PPRegData udp_registry_response_parser(PRegData msg, gchar *response)
Definition: cmdDC.c:368
gint udp_registry_find_by_name(PRegData pr, gchar *pch_name)
Definition: cmdDC.c:694
gchar ch_intfName[SZ_RMTADDR_BUFF]
Definition: cmdDC.c:108
gchar ch_display_service_name[SZ_RMTADDR_BUFF]
Definition: cmdDC.c:110
GSource * gCommSource
Definition: cmdDC.c:95