1 /* sonar, Copyright (c) 1998-2008 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 */
20 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
22 # include <sys/stat.h>
26 # include <sys/types.h>
27 # include <sys/time.h>
30 # include <sys/socket.h>
31 # include <netinet/in_systm.h>
32 # include <netinet/in.h>
33 # include <netinet/ip.h>
34 # include <netinet/ip_icmp.h>
35 # include <netinet/udp.h>
36 # include <arpa/inet.h>
38 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
40 #if defined(HAVE_ICMP)
43 # define ICMP_TYPE(p) (p)->icmp_type
44 # define ICMP_CODE(p) (p)->icmp_code
45 # define ICMP_CHECKSUM(p) (p)->icmp_cksum
46 # define ICMP_ID(p) (p)->icmp_id
47 # define ICMP_SEQ(p) (p)->icmp_seq
48 #elif defined(HAVE_ICMPHDR)
51 # define ICMP_TYPE(p) (p)->type
52 # define ICMP_CODE(p) (p)->code
53 # define ICMP_CHECKSUM(p) (p)->checksum
54 # define ICMP_ID(p) (p)->un.echo.id
55 # define ICMP_SEQ(p) (p)->un.echo.sequence
63 init_ping (Display *dpy, const char *subnet, int timeout,
64 Bool resolve_p, Bool times_p, Bool debug_p)
66 if (! (!subnet || !*subnet || !strcmp(subnet, "default")))
67 fprintf (stderr, "%s: not compiled with support for pinging hosts.\n",
72 #else /* HAVE_PING -- whole file */
75 #if defined(__DECC) || defined(_IP_VHL)
76 /* This is how you do it on DEC C, and possibly some BSD systems. */
77 # define IP_HDRLEN(ip) ((ip)->ip_vhl & 0x0F)
79 /* This is how you do it on everything else. */
80 # define IP_HDRLEN(ip) ((ip)->ip_hl)
83 /* yes, there is only one, even when multiple savers are running in the
84 same address space - since we can only open this socket before dropping
87 static int global_icmpsock = 0;
89 /* Set by a signal handler. */
90 static int timer_expired;
94 static u_short checksum(u_short *, int);
95 static long delta(struct timeval *, struct timeval *);
99 char *version; /* short version number of xscreensaver */
100 int icmpsock; /* socket for sending pings */
101 int pid; /* our process ID */
102 int seq; /* packet sequence number */
103 int timeout; /* packet timeout */
106 sonar_bogie *targets; /* the hosts we will ping;
107 those that pong end up on ssd->pending. */
108 sonar_bogie *last_pinged; /* pointer into 'targets' list */
109 double last_ping_time;
118 struct sockaddr address; /* ip address */
123 /* Packs an IP address quad into bigendian network order. */
125 pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
127 unsigned long i = (((a & 255) << 24) |
134 /* Unpacks an IP address quad from bigendian network order. */
136 unpack_addr (unsigned long addr,
137 unsigned int *a, unsigned int *b,
138 unsigned int *c, unsigned int *d)
141 *a = (addr >> 24) & 255;
142 *b = (addr >> 16) & 255;
143 *c = (addr >> 8) & 255;
150 /* If resolves the bogie's name (either a hostname or ip address string)
151 to a hostent. Returns 1 if successful, 0 if it failed to resolve.
154 resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
156 ping_bogie *pb = (ping_bogie *) sb->closure;
157 struct hostent *hent;
158 struct sockaddr_in *iaddr;
163 iaddr = (struct sockaddr_in *) &(pb->address);
164 iaddr->sin_family = AF_INET;
166 if (4 == sscanf (sb->name, " %u.%u.%u.%u %c",
167 &ip[0], &ip[1], &ip[2], &ip[3], &c))
169 /* It's an IP address.
174 fprintf (stderr, "%s: ignoring bogus IP %s\n",
179 iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
181 hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr,
182 sizeof(iaddr->sin_addr.s_addr),
188 fprintf (stderr, "%s: %s => %s\n",
190 ((hent && hent->h_name && *hent->h_name)
191 ? hent->h_name : "<unknown>"));
193 if (hent && hent->h_name && *hent->h_name)
194 sb->name = strdup (hent->h_name);
198 /* It's a host name. */
200 /* don't waste time being confused by non-hostname tokens
201 in .ssh/known_hosts */
202 if (!strcmp (sb->name, "ssh-rsa") ||
203 !strcmp (sb->name, "ssh-dsa") ||
204 !strcmp (sb->name, "ssh-dss") ||
205 strlen (sb->name) >= 80)
208 hent = gethostbyname (sb->name);
212 fprintf (stderr, "%s: could not resolve host: %s\n",
217 memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
218 sizeof(iaddr->sin_addr));
222 unsigned int a, b, c, d;
223 unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
224 fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
225 progname, sb->name, a, b, c, d);
233 print_host (FILE *out, unsigned long ip, const char *name)
236 unsigned int a, b, c, d;
237 unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */
238 sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
239 if (!name || !*name) name = "<unknown>";
240 fprintf (out, "%-16s %s\n", ips, name);
244 /* Create a sonar_bogie a host name or ip address string.
245 Returns NULL if the name could not be resolved.
248 bogie_for_host (sonar_sensor_data *ssd, const char *name, Bool resolve_p)
250 ping_data *pd = (ping_data *) ssd->closure;
251 sonar_bogie *b = (sonar_bogie *) calloc (1, sizeof(*b));
252 ping_bogie *pb = (ping_bogie *) calloc (1, sizeof(*pb));
253 struct sockaddr_in *iaddr;
256 b->name = strdup (name);
259 if (! resolve_bogie_hostname (pd, b, resolve_p))
262 iaddr = (struct sockaddr_in *) &(pb->address);
264 /* Don't ever use loopback (127.0.0.x) hosts */
265 ip = iaddr->sin_addr.s_addr;
266 if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */
269 fprintf (stderr, "%s: ignoring loopback host %s\n",
274 /* Don't ever use broadcast (255.x.x.x) hosts */
275 if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */
278 fprintf (stderr, "%s: ignoring broadcast host %s\n",
285 fprintf (stderr, "%s: added ", progname);
286 print_host (stderr, ip, b->name);
292 if (b) free_bogie (ssd, b);
297 /* Return a list of bogies read from a file.
298 The file can be like /etc/hosts or .ssh/known_hosts or probably
299 just about anything that has host names in it.
302 read_hosts_file (sonar_sensor_data *ssd, const char *filename)
304 ping_data *pd = (ping_data *) ssd->closure;
308 sonar_bogie *list = 0;
312 /* Kludge: on OSX, variables have not been expanded in the command
313 line arguments, so as a special case, allow the string to begin
314 with literal "$HOME/" or "~/".
316 This is so that the "Known Hosts" menu item in sonar.xml works.
318 if (!strncmp(filename, "~/", 2) || !strncmp(filename, "$HOME/", 6))
320 char *s = strchr (filename, '/');
321 strcpy (buf, getenv("HOME"));
326 fp = fopen(filename, "r");
330 sprintf(buf, "%s: %s", progname, filename);
332 if (pd->debug_p) /* on OSX don't syslog this */
339 fprintf (stderr, "%s: reading \"%s\"\n", progname, filename);
341 while ((p = fgets(buf, LINE_MAX, fp)))
343 while ((*p == ' ') || (*p == '\t')) /* skip whitespace */
345 if (*p == '#') /* skip comments */
348 /* Get the name and address */
351 if ((addr = strtok(buf, " ,;\t\n")))
352 name = strtok(0, " ,;\t\n");
356 /* Check to see if the addr looks like an addr. If not, assume
357 the addr is a name and there is no addr. This way, we can
358 handle files whose lines have "xx.xx.xx.xx hostname" as their
359 first two tokens, and also files that have a hostname as their
360 first token (like .ssh/known_hosts and .rhosts.)
364 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
371 /* If the name is all digits, it's not a name. */
375 for (s = name; *s; s++)
376 if (*s < '0' || *s > '9')
381 fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
382 progname, name, addr);
387 /* Create a new target using first the name then the address */
391 new = bogie_for_host (ssd, name, pd->resolve_p);
393 new = bogie_for_host (ssd, addr, pd->resolve_p);
408 delete_duplicate_hosts (sonar_sensor_data *ssd, sonar_bogie *list)
410 ping_data *pd = (ping_data *) ssd->closure;
411 sonar_bogie *head = list;
414 for (sb = head; sb; sb = sb->next)
416 ping_bogie *pb = (ping_bogie *) sb->closure;
417 struct sockaddr_in *i1 = (struct sockaddr_in *) &(pb->address);
418 unsigned long ip1 = i1->sin_addr.s_addr;
421 for (sb2 = sb; sb2; sb2 = sb2->next)
423 if (sb2 && sb2->next)
425 ping_bogie *pb2 = (ping_bogie *) sb2->next->closure;
426 struct sockaddr_in *i2 = (struct sockaddr_in *) &(pb2->address);
427 unsigned long ip2 = i2->sin_addr.s_addr;
433 fprintf (stderr, "%s: deleted duplicate: ", progname);
434 print_host (stderr, ip2, sb2->next->name);
436 sb2->next = sb2->next->next;
447 /* Generate a list of bogies consisting of all of the entries on
448 the same subnet. 'base' ip is in network order; 0 means localhost.
451 subnet_hosts (sonar_sensor_data *ssd, char **error_ret,
452 unsigned long n_base, int subnet_width)
454 ping_data *pd = (ping_data *) ssd->closure;
455 unsigned long h_mask; /* host order */
456 unsigned long h_base; /* host order */
458 /* Local Variables */
460 char hostname[BUFSIZ];
461 char address[BUFSIZ];
462 struct hostent *hent;
466 sonar_bogie *list = 0;
469 if (subnet_width < 24)
472 "Pinging %lu hosts is a bad\n"
473 "idea. Please use a subnet\n"
474 "mask of 24 bits or more.",
475 (unsigned long) (1L << (32 - subnet_width)) - 1);
476 *error_ret = strdup(buf);
479 else if (subnet_width > 30)
483 "doesn't make sense.\n"
484 "Try \"subnet/24\"\n"
485 "or \"subnet/29\".\n",
487 *error_ret = strdup(buf);
493 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
495 /* Get our hostname */
497 if (gethostname(hostname, BUFSIZ))
499 *error_ret = strdup ("Unable to determine\n"
504 /* Get our IP address and convert it to a string */
506 if (! (hent = gethostbyname(hostname)))
509 "Unable to resolve\n"
512 *error_ret = strdup(buf);
515 strcpy (address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
517 /* Construct targets for all addresses in this subnet */
520 for (i = 0; i < subnet_width; i++)
521 h_mask |= (1L << (31-i));
523 /* If no base IP specified, assume localhost. */
525 n_base = pack_addr (hent->h_addr_list[0][0],
526 hent->h_addr_list[0][1],
527 hent->h_addr_list[0][2],
528 hent->h_addr_list[0][3]);
529 h_base = ntohl (n_base);
531 if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
533 unsigned int a, b, c, d;
534 unpack_addr (n_base, &a, &b, &c, &d);
536 "Unable to determine\n"
537 "local subnet address:\n"
542 hostname, a, b, c, d);
543 *error_ret = strdup(buf);
547 for (i = 255; i >= 0; i--) {
548 unsigned int a, b, c, d;
549 int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
551 if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
553 if ((ip & ~h_mask) == 0) /* broadcast address */
555 if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
558 unpack_addr (htonl (ip), &a, &b, &c, &d);
559 sprintf (address, "%u.%u.%u.%u", a, b, c, d);
563 unsigned int aa, ab, ac, ad;
564 unsigned int ma, mb, mc, md;
565 unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
566 unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
568 "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
575 p = address + strlen(address) + 1;
578 new = bogie_for_host (ssd, address, pd->resolve_p);
590 /* Send a ping packet.
593 send_ping (ping_data *pd, const sonar_bogie *b)
595 ping_bogie *pb = (ping_bogie *) b->closure;
599 const char *token = "org.jwz.xscreensaver.sonar";
601 int pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
602 strlen(b->name) + 1 +
604 strlen(pd->version) + 1);
606 /* Create the ICMP packet */
608 if (! (packet = (u_char *) calloc(1, pcktsiz)))
609 return; /* Out of memory */
611 icmph = (struct ICMP *) packet;
612 ICMP_TYPE(icmph) = ICMP_ECHO;
613 ICMP_CODE(icmph) = 0;
614 ICMP_CHECKSUM(icmph) = 0;
615 ICMP_ID(icmph) = pd->pid;
616 ICMP_SEQ(icmph) = pd->seq++;
617 # ifdef GETTIMEOFDAY_TWO_ARGS
618 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
619 (struct timezone *) 0);
621 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
624 /* We store the name of the host we're pinging in the packet, and parse
625 that out of the return packet later (see get_ping() for why).
626 After that, we also include the name and version of this program,
627 just to give a clue to anyone sniffing and wondering what's up.
629 sprintf ((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
631 b->name, 0, token, pd->version);
633 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
637 if ((result = sendto(pd->icmpsock, packet, pcktsiz, 0,
638 &pb->address, sizeof(pb->address)))
643 sprintf(buf, "%s: pinging %s", progname, b->name);
657 /* Compute the checksum on a ping packet.
660 checksum (u_short *packet, int size)
662 register int nleft = size;
663 register u_short *w = packet;
664 register int sum = 0;
667 /* Using a 32 bit accumulator (sum), we add sequential 16 bit words
668 to it, and at the end, fold back all the carry bits from the
669 top 16 bits into the lower 16 bits.
677 /* mop up an odd byte, if necessary */
681 *(u_char *)(&answer) = *(u_char *)w ;
682 *(1 + (u_char *)(&answer)) = 0;
686 /* add back carry outs from top 16 bits to low 16 bits */
688 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
689 sum += (sum >> 16); /* add carry */
690 answer = ~sum; /* truncate to 16 bits */
696 /* Copies the sonar_bogie and the underlying ping_bogie.
699 copy_ping_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
701 sonar_bogie *b2 = copy_bogie (ssd, b);
704 ping_bogie *pb = (ping_bogie *) b->closure;
705 ping_bogie *pb2 = (ping_bogie *) calloc (1, sizeof(*pb));
706 pb2->address = pb->address;
713 /* Look for all outstanding ping replies.
716 get_ping (sonar_sensor_data *ssd)
718 ping_data *pd = (ping_data *) ssd->closure;
719 struct sockaddr from;
720 unsigned int fromlen; /* Posix says socklen_t, but that's not portable */
724 struct timeval *then;
729 sonar_bogie *new = 0;
735 /* Set up a signal to interrupt our wait for a packet */
737 sigemptyset(&sa.sa_mask);
739 sa.sa_handler = sigcatcher;
740 if (sigaction(SIGALRM, &sa, 0) == -1)
743 sprintf(msg, "%s: unable to trap SIGALRM", progname);
748 /* Set up a timer to interupt us if we don't get a packet */
750 it.it_interval.tv_sec = 0;
751 it.it_interval.tv_usec = 0;
752 it.it_value.tv_sec = 0;
753 it.it_value.tv_usec = pd->timeout;
755 setitimer(ITIMER_REAL, &it, 0);
757 /* Wait for a result packet */
759 fromlen = sizeof(from);
760 while (! timer_expired)
762 tv.tv_usec = pd->timeout;
765 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
768 memset (&rfds, 0, sizeof(rfds));
770 FD_SET(pd->icmpsock, &rfds);
771 /* only wait a little while, in case we raced with the timer expiration.
772 From Valentijn Sessink <valentyn@openoffice.nl> */
773 if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
775 result = recvfrom (pd->icmpsock, packet, sizeof(packet),
778 /* Check the packet */
780 # ifdef GETTIMEOFDAY_TWO_ARGS
781 gettimeofday(&now, (struct timezone *) 0);
785 ip = (struct ip *) packet;
786 iphdrlen = IP_HDRLEN(ip) << 2;
787 icmph = (struct ICMP *) &packet[iphdrlen];
788 then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
791 /* Ignore anything but ICMP Replies */
792 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY)
795 /* Ignore packets not set from us */
796 if (ICMP_ID(icmph) != pd->pid)
799 /* Find the bogie in 'targets' that corresponds to this packet
800 and copy it, so that this bogie stays in the same spot (th)
801 on the screen, and so that we don't have to resolve it again.
803 We could find the bogie by comparing ip->ip_src.s_addr to
804 pb->address, but it is possible that, in certain weird router
805 or NAT situations, that the reply will come back from a
806 different address than the one we sent it to. So instead,
807 we parse the name out of the reply packet payload.
810 const char *name = (char *) &packet[iphdrlen +
811 sizeof(struct ICMP) +
812 sizeof(struct timeval)];
814 for (b = pd->targets; b; b = b->next)
815 if (!strcmp (name, b->name))
817 new = copy_ping_bogie (ssd, b);
822 if (! new) /* not in targets? */
824 unsigned int a, b, c, d;
825 unpack_addr (ip->ip_src.s_addr, &a, &b, &c, &d);
827 "%s: UNEXPECTED PING REPLY! "
828 "%4d bytes, icmp_seq=%-4d from %d.%d.%d.%d\n",
829 progname, result, ICMP_SEQ(icmph), a, b, c, d);
837 double msec = delta(then, &now) / 1000.0;
841 if (new->desc) free (new->desc);
842 new->desc = (char *) malloc (30);
843 if (msec > 99) sprintf (new->desc, "%.0f ms", msec);
844 else if (msec > 9) sprintf (new->desc, "%.1f ms", msec);
845 else if (msec > 1) sprintf (new->desc, "%.2f ms", msec);
846 else sprintf (new->desc, "%.3f ms", msec);
849 if (pd->debug_p && pd->times_p) /* ping-like stdout log */
851 char *s = strdup(new->name);
855 s2 = s + strlen(s) - 28;
856 strncpy (s2, "...", 3);
859 "%3d bytes from %28s: icmp_seq=%-4d time=%s\n",
860 result, s2, ICMP_SEQ(icmph), new->desc);
865 /* The radius must be between 0.0 and 1.0.
866 We want to display ping times on a logarithmic scale,
867 with the three rings being 2.5, 70 and 2,000 milliseconds.
869 if (msec <= 0) msec = 0.001;
870 new->r = log (msec * 10) / log (20000);
872 /* Don't put anyone *too* close to the center of the screen. */
873 if (new->r < 0) new->r = 0;
874 if (new->r < 0.1) new->r += 0.1;
883 /* difference between the two times in microseconds.
886 delta (struct timeval *then, struct timeval *now)
888 return (((now->tv_sec - then->tv_sec) * 1000000) +
889 (now->tv_usec - then->tv_usec));
894 ping_free_data (sonar_sensor_data *ssd, void *closure)
896 ping_data *pd = (ping_data *) closure;
897 sonar_bogie *b = pd->targets;
900 sonar_bogie *b2 = b->next;
908 ping_free_bogie_data (sonar_sensor_data *sd, void *closure)
914 /* Returns the current time in seconds as a double.
920 # ifdef GETTIMEOFDAY_TWO_ARGS
922 gettimeofday(&now, &tzp);
927 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
931 /* If a bogie is provided, pings it.
932 Then, returns all outstanding ping replies.
935 ping_scan (sonar_sensor_data *ssd)
937 ping_data *pd = (ping_data *) ssd->closure;
938 double now = double_time();
939 double ping_cycle = 10; /* re-ping a given host every 10 seconds */
940 double ping_interval = ping_cycle / pd->target_count;
942 if (now > pd->last_ping_time + ping_interval) /* time to ping someone */
945 pd->last_pinged = pd->last_pinged->next;
946 if (! pd->last_pinged)
947 pd->last_pinged = pd->targets;
948 send_ping (pd, pd->last_pinged);
949 pd->last_ping_time = now;
952 return get_ping (ssd);
956 /* Returns a list of hosts to ping based on the "-ping" argument.
959 parse_mode (sonar_sensor_data *ssd, char **error_ret,
960 const char *ping_arg, Bool ping_works_p)
962 ping_data *pd = (ping_data *) ssd->closure;
963 char *source, *token, *end, dummy;
964 sonar_bogie *hostlist = 0;
966 if (!ping_arg || !*ping_arg || !strcmp (ping_arg, "default"))
967 source = strdup("subnet/28");
969 source = strdup(ping_arg);
972 end = source + strlen(source);
978 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
983 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
990 fprintf (stderr, "%s: parsing %s\n", progname, token);
994 *error_ret = strdup ("Sonar must be setuid to ping!\n"
995 "Running simulation instead.");
999 if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) ||
1000 (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1002 /* subnet: A.B.C.D/M
1005 unsigned long ip = pack_addr (n0, n1, n2, n3);
1006 new = subnet_hosts (ssd, error_ret, ip, m);
1008 else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1012 new = bogie_for_host (ssd, token, pd->resolve_p);
1014 else if (!strcmp (token, "subnet"))
1016 new = subnet_hosts (ssd, error_ret, 0, 24);
1018 else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1020 new = subnet_hosts (ssd, error_ret, 0, m);
1022 else if (*token == '.' || *token == '/' ||
1023 *token == '$' || *token == '~' ||
1028 new = read_hosts_file (ssd, token);
1032 /* not an existant file - must be a host name
1034 new = bogie_for_host (ssd, token, pd->resolve_p);
1039 sonar_bogie *nn = new;
1040 while (nn && nn->next)
1042 nn->next = hostlist;
1047 while (token < end &&
1048 (*token == ',' || *token == ' ' ||
1049 *token == '\t' || *token == '\n'))
1059 init_ping (Display *dpy, char **error_ret,
1060 const char *subnet, int timeout,
1061 Bool resolve_p, Bool times_p, Bool debug_p)
1063 sonar_sensor_data *ssd = (sonar_sensor_data *) calloc (1, sizeof(*ssd));
1064 ping_data *pd = (ping_data *) calloc (1, sizeof(*pd));
1069 Bool socket_initted_p = False;
1070 Bool socket_raw_p = False;
1072 pd->resolve_p = resolve_p;
1073 pd->times_p = times_p;
1074 pd->debug_p = debug_p;
1077 ssd->scan_cb = ping_scan;
1078 ssd->free_data_cb = ping_free_data;
1079 ssd->free_bogie_cb = ping_free_bogie_data;
1081 /* Get short version number. */
1082 s = strchr (screensaver_id, ' ');
1083 pd->version = strdup (s+1);
1084 s = strchr (pd->version, ' ');
1088 /* Create the ICMP socket. Do this before dropping privs.
1090 Raw sockets can only be opened by root (or setuid root), so we
1091 only try to do this when the effective uid is 0.
1093 We used to just always try, and notice the failure. But apparently
1094 that causes "SELinux" to log spurious warnings when running with the
1095 "strict" policy. So to avoid that, we just don't try unless we
1098 On MacOS X, we can avoid the whole problem by using a
1099 non-privileged datagram instead of a raw socket.
1101 if (global_icmpsock)
1103 pd->icmpsock = global_icmpsock;
1104 socket_initted_p = True;
1106 fprintf (stderr, "%s: re-using icmp socket\n", progname);
1109 else if ((pd->icmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0)
1111 socket_initted_p = True;
1113 else if (geteuid() == 0 &&
1114 (pd->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
1116 socket_initted_p = True;
1117 socket_raw_p = True;
1120 if (socket_initted_p)
1122 global_icmpsock = pd->icmpsock;
1123 socket_initted_p = True;
1125 fprintf (stderr, "%s: opened %s icmp socket\n", progname,
1126 (socket_raw_p ? "raw" : "dgram"));
1129 fprintf (stderr, "%s: unable to open icmp socket\n", progname);
1134 pd->pid = getpid() & 0xFFFF;
1136 pd->timeout = timeout;
1138 /* Generate a list of targets */
1140 pd->targets = parse_mode (ssd, error_ret, subnet, socket_initted_p);
1141 pd->targets = delete_duplicate_hosts (ssd, pd->targets);
1145 fprintf (stderr, "%s: Target list:\n", progname);
1146 for (b = pd->targets; b; b = b->next)
1148 ping_bogie *pb = (ping_bogie *) b->closure;
1149 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
1150 unsigned long ip = iaddr->sin_addr.s_addr;
1151 fprintf (stderr, "%s: ", progname);
1152 print_host (stderr, ip, b->name);
1156 /* Make sure there is something to ping */
1158 pd->target_count = 0;
1159 for (b = pd->targets; b; b = b->next)
1162 if (pd->target_count == 0)
1165 *error_ret = strdup ("No hosts to ping!\n"
1166 "Simulating instead.");
1167 if (pd) ping_free_data (ssd, pd);
1168 if (ssd) free (ssd);
1172 /* Distribute them evenly around the display field.
1174 div = pd->target_count;
1175 if (div > 90) div = 90; /* no closer together than 4 degrees */
1176 for (i = 0, b = pd->targets; b; b = b->next, i++)
1177 b->th = M_PI * 2 * ((div - i) % div) / div;
1182 #endif /* HAVE_PING -- whole file */