1 /* sonar, Copyright (c) 1998-2012 Jamie Zawinski and Stephen Martin
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * This implements the "ping" sensor for sonar.
14 #include "screenhackI.h"
18 #undef usleep /* conflicts with unistd.h on OSX */
21 /* Note: to get this to compile for iPhone, you need to fix Xcode!
22 The icmp headers exist for the simulator build environment, but
23 not for the real-device build environment. This appears to
24 just be an Apple bug, not intentional.
26 xc=/Applications/Xcode.app/Contents
27 for path in /Developer/Platforms/iPhone*?/Developer/SDKs/?* \
28 $xc/Developer/Platforms/iPhone*?/Developer/SDKs/?* ; do
30 /usr/include/netinet/ip.h \
31 /usr/include/netinet/in_systm.h \
32 /usr/include/netinet/ip_icmp.h \
33 /usr/include/netinet/ip_var.h \
34 /usr/include/netinet/udp.h
36 ln -s "$file" "$path$file"
42 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
44 # include <sys/stat.h>
48 # include <sys/types.h>
49 # include <sys/time.h>
52 # include <sys/socket.h>
53 # include <netinet/in_systm.h>
54 # include <netinet/in.h>
55 # include <netinet/ip.h>
56 # include <netinet/ip_icmp.h>
57 # include <netinet/udp.h>
58 # include <arpa/inet.h>
60 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
62 #if defined(HAVE_ICMP)
65 # define ICMP_TYPE(p) (p)->icmp_type
66 # define ICMP_CODE(p) (p)->icmp_code
67 # define ICMP_CHECKSUM(p) (p)->icmp_cksum
68 # define ICMP_ID(p) (p)->icmp_id
69 # define ICMP_SEQ(p) (p)->icmp_seq
70 #elif defined(HAVE_ICMPHDR)
73 # define ICMP_TYPE(p) (p)->type
74 # define ICMP_CODE(p) (p)->code
75 # define ICMP_CHECKSUM(p) (p)->checksum
76 # define ICMP_ID(p) (p)->un.echo.id
77 # define ICMP_SEQ(p) (p)->un.echo.sequence
85 init_ping (Display *dpy, char **error_ret, const char *subnet, int timeout,
86 Bool resolve_p, Bool times_p, Bool debug_p)
88 if (! (!subnet || !*subnet || !strcmp(subnet, "default")))
89 fprintf (stderr, "%s: not compiled with support for pinging hosts.\n",
94 #else /* HAVE_PING -- whole file */
97 #if defined(__DECC) || defined(_IP_VHL)
98 /* This is how you do it on DEC C, and possibly some BSD systems. */
99 # define IP_HDRLEN(ip) ((ip)->ip_vhl & 0x0F)
101 /* This is how you do it on everything else. */
102 # define IP_HDRLEN(ip) ((ip)->ip_hl)
105 /* yes, there is only one, even when multiple savers are running in the
106 same address space - since we can only open this socket before dropping
109 static int global_icmpsock = 0;
111 /* Set by a signal handler. */
112 static int timer_expired;
116 static u_short checksum(u_short *, int);
117 static long delta(struct timeval *, struct timeval *);
121 char *version; /* short version number of xscreensaver */
122 int icmpsock; /* socket for sending pings */
123 int pid; /* our process ID */
124 int seq; /* packet sequence number */
125 int timeout; /* packet timeout */
128 sonar_bogie *targets; /* the hosts we will ping;
129 those that pong end up on ssd->pending. */
130 sonar_bogie *last_pinged; /* pointer into 'targets' list */
131 double last_ping_time;
140 struct sockaddr address; /* ip address */
145 /* Packs an IP address quad into bigendian network order. */
147 pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
149 unsigned long i = (((a & 255) << 24) |
156 /* Unpacks an IP address quad from bigendian network order. */
158 unpack_addr (unsigned long addr,
159 unsigned int *a, unsigned int *b,
160 unsigned int *c, unsigned int *d)
163 *a = (addr >> 24) & 255;
164 *b = (addr >> 16) & 255;
165 *c = (addr >> 8) & 255;
172 /* If resolves the bogie's name (either a hostname or ip address string)
173 to a hostent. Returns 1 if successful, 0 if it failed to resolve.
176 resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
178 ping_bogie *pb = (ping_bogie *) sb->closure;
179 struct hostent *hent;
180 struct sockaddr_in *iaddr;
185 iaddr = (struct sockaddr_in *) &(pb->address);
186 iaddr->sin_family = AF_INET;
188 if (4 == sscanf (sb->name, " %u.%u.%u.%u %c",
189 &ip[0], &ip[1], &ip[2], &ip[3], &c))
191 /* It's an IP address.
196 fprintf (stderr, "%s: ignoring bogus IP %s\n",
201 iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
203 hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr,
204 sizeof(iaddr->sin_addr.s_addr),
210 fprintf (stderr, "%s: %s => %s\n",
212 ((hent && hent->h_name && *hent->h_name)
213 ? hent->h_name : "<unknown>"));
215 if (hent && hent->h_name && *hent->h_name)
216 sb->name = strdup (hent->h_name);
220 /* It's a host name. */
222 /* don't waste time being confused by non-hostname tokens
223 in .ssh/known_hosts */
224 if (!strcmp (sb->name, "ssh-rsa") ||
225 !strcmp (sb->name, "ssh-dsa") ||
226 !strcmp (sb->name, "ssh-dss") ||
227 strlen (sb->name) >= 80)
230 /* .ssh/known_hosts sometimes contains weirdness like "[host]:port".
232 if (strchr (sb->name, '['))
235 fprintf (stderr, "%s: ignoring bogus address \"%s\"\n",
240 /* If the name contains a colon, it's probably IPv6. */
241 if (strchr (sb->name, ':'))
244 fprintf (stderr, "%s: ignoring ipv6 address \"%s\"\n",
249 hent = gethostbyname (sb->name);
253 fprintf (stderr, "%s: could not resolve host: %s\n",
258 memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
259 sizeof(iaddr->sin_addr));
263 unsigned int a, b, c, d;
264 unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
265 fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
266 progname, sb->name, a, b, c, d);
274 print_host (FILE *out, unsigned long ip, const char *name)
277 unsigned int a, b, c, d;
278 unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */
279 sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
280 if (!name || !*name) name = "<unknown>";
281 fprintf (out, "%-16s %s\n", ips, name);
285 /* Create a sonar_bogie a host name or ip address string.
286 Returns NULL if the name could not be resolved.
289 bogie_for_host (sonar_sensor_data *ssd, const char *name, Bool resolve_p)
291 ping_data *pd = (ping_data *) ssd->closure;
292 sonar_bogie *b = (sonar_bogie *) calloc (1, sizeof(*b));
293 ping_bogie *pb = (ping_bogie *) calloc (1, sizeof(*pb));
294 struct sockaddr_in *iaddr;
297 b->name = strdup (name);
300 if (! resolve_bogie_hostname (pd, b, resolve_p))
303 iaddr = (struct sockaddr_in *) &(pb->address);
305 /* Don't ever use loopback (127.0.0.x) hosts */
306 ip = iaddr->sin_addr.s_addr;
307 if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */
310 fprintf (stderr, "%s: ignoring loopback host %s\n",
315 /* Don't ever use broadcast (255.x.x.x) hosts */
316 if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */
319 fprintf (stderr, "%s: ignoring broadcast host %s\n",
326 fprintf (stderr, "%s: added ", progname);
327 print_host (stderr, ip, b->name);
333 if (b) free_bogie (ssd, b);
338 /* Return a list of bogies read from a file.
339 The file can be like /etc/hosts or .ssh/known_hosts or probably
340 just about anything that has host names in it.
343 read_hosts_file (sonar_sensor_data *ssd, const char *filename)
345 ping_data *pd = (ping_data *) ssd->closure;
349 sonar_bogie *list = 0;
353 /* Kludge: on OSX, variables have not been expanded in the command
354 line arguments, so as a special case, allow the string to begin
355 with literal "$HOME/" or "~/".
357 This is so that the "Known Hosts" menu item in sonar.xml works.
359 if (!strncmp(filename, "~/", 2) || !strncmp(filename, "$HOME/", 6))
361 char *s = strchr (filename, '/');
362 strcpy (buf, getenv("HOME"));
367 fp = fopen(filename, "r");
371 sprintf(buf, "%s: %s", progname, filename);
373 if (pd->debug_p) /* on OSX don't syslog this */
380 fprintf (stderr, "%s: reading \"%s\"\n", progname, filename);
382 while ((p = fgets(buf, LINE_MAX, fp)))
384 while ((*p == ' ') || (*p == '\t')) /* skip whitespace */
386 if (*p == '#') /* skip comments */
389 /* Get the name and address */
391 if ((addr = strtok(buf, " ,;\t\n")))
392 name = strtok(0, " ,;\t\n");
396 /* Check to see if the addr looks like an addr. If not, assume
397 the addr is a name and there is no addr. This way, we can
398 handle files whose lines have "xx.xx.xx.xx hostname" as their
399 first two tokens, and also files that have a hostname as their
400 first token (like .ssh/known_hosts and .rhosts.)
404 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
411 /* If the name is all digits, it's not a name. */
415 for (s = name; *s; s++)
416 if (*s < '0' || *s > '9')
421 fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
422 progname, name, addr);
427 /* Create a new target using first the name then the address */
431 new = bogie_for_host (ssd, name, pd->resolve_p);
433 new = bogie_for_host (ssd, addr, pd->resolve_p);
448 delete_duplicate_hosts (sonar_sensor_data *ssd, sonar_bogie *list)
450 ping_data *pd = (ping_data *) ssd->closure;
451 sonar_bogie *head = list;
454 for (sb = head; sb; sb = sb->next)
456 ping_bogie *pb = (ping_bogie *) sb->closure;
457 struct sockaddr_in *i1 = (struct sockaddr_in *) &(pb->address);
458 unsigned long ip1 = i1->sin_addr.s_addr;
461 for (sb2 = sb; sb2; sb2 = sb2->next)
463 if (sb2 && sb2->next)
465 ping_bogie *pb2 = (ping_bogie *) sb2->next->closure;
466 struct sockaddr_in *i2 = (struct sockaddr_in *) &(pb2->address);
467 unsigned long ip2 = i2->sin_addr.s_addr;
473 fprintf (stderr, "%s: deleted duplicate: ", progname);
474 print_host (stderr, ip2, sb2->next->name);
476 sb2->next = sb2->next->next;
487 /* Generate a list of bogies consisting of all of the entries on
488 the same subnet. 'base' ip is in network order; 0 means localhost.
491 subnet_hosts (sonar_sensor_data *ssd, char **error_ret,
492 unsigned long n_base, int subnet_width)
494 ping_data *pd = (ping_data *) ssd->closure;
495 unsigned long h_mask; /* host order */
496 unsigned long h_base; /* host order */
498 /* Local Variables */
500 char hostname[BUFSIZ];
501 char address[BUFSIZ];
502 struct hostent *hent;
506 sonar_bogie *list = 0;
509 if (subnet_width < 24)
512 "Pinging %lu hosts is a bad\n"
513 "idea. Please use a subnet\n"
514 "mask of 24 bits or more.",
515 (unsigned long) (1L << (32 - subnet_width)) - 1);
516 *error_ret = strdup(buf);
519 else if (subnet_width > 30)
523 "doesn't make sense.\n"
524 "Try \"subnet/24\"\n"
525 "or \"subnet/29\".\n",
527 *error_ret = strdup(buf);
533 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
535 /* Get our hostname */
537 if (gethostname(hostname, BUFSIZ))
539 *error_ret = strdup ("Unable to determine\n"
544 /* Get our IP address and convert it to a string */
546 hent = gethostbyname(hostname);
549 strcat (hostname, ".local"); /* Necessary on iphone */
550 hent = gethostbyname(hostname);
555 /* Without being able to resolve localhost to an IP, we don't know
556 what our local subnet is. I don't know another way to find that,
557 short of running "ifconfig" and parsing the output...
560 "Unable to resolve\n"
563 *error_ret = strdup(buf);
567 strcpy (address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
569 /* Construct targets for all addresses in this subnet */
572 for (i = 0; i < subnet_width; i++)
573 h_mask |= (1L << (31-i));
575 /* If no base IP specified, assume localhost. */
577 n_base = pack_addr (hent->h_addr_list[0][0],
578 hent->h_addr_list[0][1],
579 hent->h_addr_list[0][2],
580 hent->h_addr_list[0][3]);
581 h_base = ntohl (n_base);
583 if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
585 unsigned int a, b, c, d;
586 unpack_addr (n_base, &a, &b, &c, &d);
588 "Unable to determine\n"
589 "local subnet address:\n"
594 hostname, a, b, c, d);
595 *error_ret = strdup(buf);
599 for (i = 255; i >= 0; i--) {
600 unsigned int a, b, c, d;
601 int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
603 if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
605 if ((ip & ~h_mask) == 0) /* broadcast address */
607 if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
610 unpack_addr (htonl (ip), &a, &b, &c, &d);
611 sprintf (address, "%u.%u.%u.%u", a, b, c, d);
615 unsigned int aa, ab, ac, ad;
616 unsigned int ma, mb, mc, md;
617 unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
618 unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
620 "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
627 p = address + strlen(address) + 1;
630 new = bogie_for_host (ssd, address, pd->resolve_p);
642 /* Send a ping packet.
645 send_ping (ping_data *pd, const sonar_bogie *b)
647 ping_bogie *pb = (ping_bogie *) b->closure;
650 const char *token = "org.jwz.xscreensaver.sonar";
652 int pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
653 strlen(b->name) + 1 +
655 strlen(pd->version) + 1);
657 /* Create the ICMP packet */
659 if (! (packet = (u_char *) calloc(1, pcktsiz)))
660 return; /* Out of memory */
662 icmph = (struct ICMP *) packet;
663 ICMP_TYPE(icmph) = ICMP_ECHO;
664 ICMP_CODE(icmph) = 0;
665 ICMP_CHECKSUM(icmph) = 0;
666 ICMP_ID(icmph) = pd->pid;
667 ICMP_SEQ(icmph) = pd->seq++;
668 # ifdef GETTIMEOFDAY_TWO_ARGS
669 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
670 (struct timezone *) 0);
672 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
675 /* We store the name of the host we're pinging in the packet, and parse
676 that out of the return packet later (see get_ping() for why).
677 After that, we also include the name and version of this program,
678 just to give a clue to anyone sniffing and wondering what's up.
680 sprintf ((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
682 b->name, 0, token, pd->version);
684 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
688 if (sendto(pd->icmpsock, packet, pcktsiz, 0,
689 &pb->address, sizeof(pb->address))
694 sprintf(buf, "%s: pinging %s", progname, b->name);
708 /* Compute the checksum on a ping packet.
711 checksum (u_short *packet, int size)
713 register int nleft = size;
714 register u_short *w = packet;
715 register int sum = 0;
718 /* Using a 32 bit accumulator (sum), we add sequential 16 bit words
719 to it, and at the end, fold back all the carry bits from the
720 top 16 bits into the lower 16 bits.
728 /* mop up an odd byte, if necessary */
732 *(u_char *)(&answer) = *(u_char *)w ;
733 *(1 + (u_char *)(&answer)) = 0;
737 /* add back carry outs from top 16 bits to low 16 bits */
739 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
740 sum += (sum >> 16); /* add carry */
741 answer = ~sum; /* truncate to 16 bits */
747 /* Copies the sonar_bogie and the underlying ping_bogie.
750 copy_ping_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
752 sonar_bogie *b2 = copy_bogie (ssd, b);
755 ping_bogie *pb = (ping_bogie *) b->closure;
756 ping_bogie *pb2 = (ping_bogie *) calloc (1, sizeof(*pb));
757 pb2->address = pb->address;
764 /* Look for all outstanding ping replies.
767 get_ping (sonar_sensor_data *ssd)
769 ping_data *pd = (ping_data *) ssd->closure;
770 struct sockaddr from;
771 unsigned int fromlen; /* Posix says socklen_t, but that's not portable */
775 struct timeval *then;
780 sonar_bogie *new = 0;
786 /* Set up a signal to interrupt our wait for a packet */
788 sigemptyset(&sa.sa_mask);
790 sa.sa_handler = sigcatcher;
791 if (sigaction(SIGALRM, &sa, 0) == -1)
794 sprintf(msg, "%s: unable to trap SIGALRM", progname);
799 /* Set up a timer to interupt us if we don't get a packet */
801 it.it_interval.tv_sec = 0;
802 it.it_interval.tv_usec = 0;
803 it.it_value.tv_sec = 0;
804 it.it_value.tv_usec = pd->timeout;
806 setitimer(ITIMER_REAL, &it, 0);
808 /* Wait for a result packet */
810 fromlen = sizeof(from);
811 while (! timer_expired)
813 tv.tv_usec = pd->timeout;
816 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
819 memset (&rfds, 0, sizeof(rfds));
821 FD_SET(pd->icmpsock, &rfds);
822 /* only wait a little while, in case we raced with the timer expiration.
823 From Valentijn Sessink <valentyn@openoffice.nl> */
824 if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
826 result = recvfrom (pd->icmpsock, packet, sizeof(packet),
829 /* Check the packet */
831 # ifdef GETTIMEOFDAY_TWO_ARGS
832 gettimeofday(&now, (struct timezone *) 0);
836 ip = (struct ip *) packet;
837 iphdrlen = IP_HDRLEN(ip) << 2;
838 icmph = (struct ICMP *) &packet[iphdrlen];
839 then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
842 /* Ignore anything but ICMP Replies */
843 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY)
846 /* Ignore packets not set from us */
847 if (ICMP_ID(icmph) != pd->pid)
850 /* Find the bogie in 'targets' that corresponds to this packet
851 and copy it, so that this bogie stays in the same spot (th)
852 on the screen, and so that we don't have to resolve it again.
854 We could find the bogie by comparing ip->ip_src.s_addr to
855 pb->address, but it is possible that, in certain weird router
856 or NAT situations, that the reply will come back from a
857 different address than the one we sent it to. So instead,
858 we parse the name out of the reply packet payload.
861 const char *name = (char *) &packet[iphdrlen +
862 sizeof(struct ICMP) +
863 sizeof(struct timeval)];
865 for (b = pd->targets; b; b = b->next)
866 if (!strcmp (name, b->name))
868 new = copy_ping_bogie (ssd, b);
873 if (! new) /* not in targets? */
875 unsigned int a, b, c, d;
876 unpack_addr (ip->ip_src.s_addr, &a, &b, &c, &d);
878 "%s: UNEXPECTED PING REPLY! "
879 "%4d bytes, icmp_seq=%-4d from %d.%d.%d.%d\n",
880 progname, result, ICMP_SEQ(icmph), a, b, c, d);
888 double msec = delta(then, &now) / 1000.0;
892 if (new->desc) free (new->desc);
893 new->desc = (char *) malloc (30);
894 if (msec > 99) sprintf (new->desc, "%.0f ms", msec);
895 else if (msec > 9) sprintf (new->desc, "%.1f ms", msec);
896 else if (msec > 1) sprintf (new->desc, "%.2f ms", msec);
897 else sprintf (new->desc, "%.3f ms", msec);
900 if (pd->debug_p && pd->times_p) /* ping-like stdout log */
902 char *s = strdup(new->name);
906 s2 = s + strlen(s) - 28;
907 strncpy (s2, "...", 3);
910 "%3d bytes from %28s: icmp_seq=%-4d time=%s\n",
911 result, s2, ICMP_SEQ(icmph), new->desc);
916 /* The radius must be between 0.0 and 1.0.
917 We want to display ping times on a logarithmic scale,
918 with the three rings being 2.5, 70 and 2,000 milliseconds.
920 if (msec <= 0) msec = 0.001;
921 new->r = log (msec * 10) / log (20000);
923 /* Don't put anyone *too* close to the center of the screen. */
924 if (new->r < 0) new->r = 0;
925 if (new->r < 0.1) new->r += 0.1;
934 /* difference between the two times in microseconds.
937 delta (struct timeval *then, struct timeval *now)
939 return (((now->tv_sec - then->tv_sec) * 1000000) +
940 (now->tv_usec - then->tv_usec));
945 ping_free_data (sonar_sensor_data *ssd, void *closure)
947 ping_data *pd = (ping_data *) closure;
948 sonar_bogie *b = pd->targets;
951 sonar_bogie *b2 = b->next;
959 ping_free_bogie_data (sonar_sensor_data *sd, void *closure)
965 /* Returns the current time in seconds as a double.
971 # ifdef GETTIMEOFDAY_TWO_ARGS
973 gettimeofday(&now, &tzp);
978 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
982 /* If a bogie is provided, pings it.
983 Then, returns all outstanding ping replies.
986 ping_scan (sonar_sensor_data *ssd)
988 ping_data *pd = (ping_data *) ssd->closure;
989 double now = double_time();
990 double ping_cycle = 10; /* re-ping a given host every 10 seconds */
991 double ping_interval = ping_cycle / pd->target_count;
993 if (now > pd->last_ping_time + ping_interval) /* time to ping someone */
996 pd->last_pinged = pd->last_pinged->next;
997 if (! pd->last_pinged)
998 pd->last_pinged = pd->targets;
999 send_ping (pd, pd->last_pinged);
1000 pd->last_ping_time = now;
1003 return get_ping (ssd);
1007 /* Returns a list of hosts to ping based on the "-ping" argument.
1009 static sonar_bogie *
1010 parse_mode (sonar_sensor_data *ssd, char **error_ret,
1011 const char *ping_arg, Bool ping_works_p)
1013 ping_data *pd = (ping_data *) ssd->closure;
1014 char *source, *token, *end, dummy;
1015 sonar_bogie *hostlist = 0;
1017 if (!ping_arg || !*ping_arg || !strcmp (ping_arg, "default"))
1018 source = strdup("subnet/28");
1020 source = strdup(ping_arg);
1023 end = source + strlen(source);
1029 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1034 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1041 fprintf (stderr, "%s: parsing %s\n", progname, token);
1045 *error_ret = strdup ("Sonar must be setuid to ping!\n"
1046 "Running simulation instead.");
1050 if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) ||
1051 (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1053 /* subnet: A.B.C.D/M
1056 unsigned long ip = pack_addr (n0, n1, n2, n3);
1057 new = subnet_hosts (ssd, error_ret, ip, m);
1059 else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1063 new = bogie_for_host (ssd, token, pd->resolve_p);
1065 else if (!strcmp (token, "subnet"))
1067 new = subnet_hosts (ssd, error_ret, 0, 24);
1069 else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1071 new = subnet_hosts (ssd, error_ret, 0, m);
1073 else if (*token == '.' || *token == '/' ||
1074 *token == '$' || *token == '~' ||
1079 new = read_hosts_file (ssd, token);
1083 /* not an existant file - must be a host name
1085 new = bogie_for_host (ssd, token, pd->resolve_p);
1090 sonar_bogie *nn = new;
1093 nn->next = hostlist;
1098 while (token < end &&
1099 (*token == ',' || *token == ' ' ||
1100 *token == '\t' || *token == '\n'))
1110 init_ping (Display *dpy, char **error_ret,
1111 const char *subnet, int timeout,
1112 Bool resolve_p, Bool times_p, Bool debug_p)
1114 sonar_sensor_data *ssd = (sonar_sensor_data *) calloc (1, sizeof(*ssd));
1115 ping_data *pd = (ping_data *) calloc (1, sizeof(*pd));
1120 Bool socket_initted_p = False;
1121 Bool socket_raw_p = False;
1123 pd->resolve_p = resolve_p;
1124 pd->times_p = times_p;
1125 pd->debug_p = debug_p;
1128 ssd->scan_cb = ping_scan;
1129 ssd->free_data_cb = ping_free_data;
1130 ssd->free_bogie_cb = ping_free_bogie_data;
1132 /* Get short version number. */
1133 s = strchr (screensaver_id, ' ');
1134 pd->version = strdup (s+1);
1135 s = strchr (pd->version, ' ');
1139 /* Create the ICMP socket. Do this before dropping privs.
1141 Raw sockets can only be opened by root (or setuid root), so we
1142 only try to do this when the effective uid is 0.
1144 We used to just always try, and notice the failure. But apparently
1145 that causes "SELinux" to log spurious warnings when running with the
1146 "strict" policy. So to avoid that, we just don't try unless we
1149 On MacOS X, we can avoid the whole problem by using a
1150 non-privileged datagram instead of a raw socket.
1152 if (global_icmpsock)
1154 pd->icmpsock = global_icmpsock;
1155 socket_initted_p = True;
1157 fprintf (stderr, "%s: re-using icmp socket\n", progname);
1160 else if ((pd->icmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0)
1162 socket_initted_p = True;
1164 else if (geteuid() == 0 &&
1165 (pd->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
1167 socket_initted_p = True;
1168 socket_raw_p = True;
1171 if (socket_initted_p)
1173 global_icmpsock = pd->icmpsock;
1174 socket_initted_p = True;
1176 fprintf (stderr, "%s: opened %s icmp socket\n", progname,
1177 (socket_raw_p ? "raw" : "dgram"));
1180 fprintf (stderr, "%s: unable to open icmp socket\n", progname);
1185 pd->pid = getpid() & 0xFFFF;
1187 pd->timeout = timeout;
1189 /* Generate a list of targets */
1191 pd->targets = parse_mode (ssd, error_ret, subnet, socket_initted_p);
1192 pd->targets = delete_duplicate_hosts (ssd, pd->targets);
1196 fprintf (stderr, "%s: Target list:\n", progname);
1197 for (b = pd->targets; b; b = b->next)
1199 ping_bogie *pb = (ping_bogie *) b->closure;
1200 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
1201 unsigned long ip = iaddr->sin_addr.s_addr;
1202 fprintf (stderr, "%s: ", progname);
1203 print_host (stderr, ip, b->name);
1207 /* Make sure there is something to ping */
1209 pd->target_count = 0;
1210 for (b = pd->targets; b; b = b->next)
1213 if (pd->target_count == 0)
1216 *error_ret = strdup ("No hosts to ping!\n"
1217 "Simulating instead.");
1218 if (pd) ping_free_data (ssd, pd);
1219 if (ssd) free (ssd);
1223 /* Distribute them evenly around the display field.
1225 div = pd->target_count;
1226 if (div > 90) div = 90; /* no closer together than 4 degrees */
1227 for (i = 0, b = pd->targets; b; b = b->next, i++)
1228 b->th = M_PI * 2 * ((div - i) % div) / div;
1233 #endif /* HAVE_PING -- whole file */