1 /* sonar, Copyright (c) 1998-2009 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 /* .ssh/known_hosts sometimes contains weirdness like "[host]:port".
210 if (strchr (sb->name, '['))
213 fprintf (stderr, "%s: ignoring bogus address \"%s\"\n",
218 /* If the name contains a colon, it's probably IPv6. */
219 if (strchr (sb->name, ':'))
222 fprintf (stderr, "%s: ignoring ipv6 address \"%s\"\n",
227 hent = gethostbyname (sb->name);
231 fprintf (stderr, "%s: could not resolve host: %s\n",
236 memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
237 sizeof(iaddr->sin_addr));
241 unsigned int a, b, c, d;
242 unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
243 fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
244 progname, sb->name, a, b, c, d);
252 print_host (FILE *out, unsigned long ip, const char *name)
255 unsigned int a, b, c, d;
256 unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */
257 sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
258 if (!name || !*name) name = "<unknown>";
259 fprintf (out, "%-16s %s\n", ips, name);
263 /* Create a sonar_bogie a host name or ip address string.
264 Returns NULL if the name could not be resolved.
267 bogie_for_host (sonar_sensor_data *ssd, const char *name, Bool resolve_p)
269 ping_data *pd = (ping_data *) ssd->closure;
270 sonar_bogie *b = (sonar_bogie *) calloc (1, sizeof(*b));
271 ping_bogie *pb = (ping_bogie *) calloc (1, sizeof(*pb));
272 struct sockaddr_in *iaddr;
275 b->name = strdup (name);
278 if (! resolve_bogie_hostname (pd, b, resolve_p))
281 iaddr = (struct sockaddr_in *) &(pb->address);
283 /* Don't ever use loopback (127.0.0.x) hosts */
284 ip = iaddr->sin_addr.s_addr;
285 if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */
288 fprintf (stderr, "%s: ignoring loopback host %s\n",
293 /* Don't ever use broadcast (255.x.x.x) hosts */
294 if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */
297 fprintf (stderr, "%s: ignoring broadcast host %s\n",
304 fprintf (stderr, "%s: added ", progname);
305 print_host (stderr, ip, b->name);
311 if (b) free_bogie (ssd, b);
316 /* Return a list of bogies read from a file.
317 The file can be like /etc/hosts or .ssh/known_hosts or probably
318 just about anything that has host names in it.
321 read_hosts_file (sonar_sensor_data *ssd, const char *filename)
323 ping_data *pd = (ping_data *) ssd->closure;
327 sonar_bogie *list = 0;
331 /* Kludge: on OSX, variables have not been expanded in the command
332 line arguments, so as a special case, allow the string to begin
333 with literal "$HOME/" or "~/".
335 This is so that the "Known Hosts" menu item in sonar.xml works.
337 if (!strncmp(filename, "~/", 2) || !strncmp(filename, "$HOME/", 6))
339 char *s = strchr (filename, '/');
340 strcpy (buf, getenv("HOME"));
345 fp = fopen(filename, "r");
349 sprintf(buf, "%s: %s", progname, filename);
351 if (pd->debug_p) /* on OSX don't syslog this */
358 fprintf (stderr, "%s: reading \"%s\"\n", progname, filename);
360 while ((p = fgets(buf, LINE_MAX, fp)))
362 while ((*p == ' ') || (*p == '\t')) /* skip whitespace */
364 if (*p == '#') /* skip comments */
367 /* Get the name and address */
369 if ((addr = strtok(buf, " ,;\t\n")))
370 name = strtok(0, " ,;\t\n");
374 /* Check to see if the addr looks like an addr. If not, assume
375 the addr is a name and there is no addr. This way, we can
376 handle files whose lines have "xx.xx.xx.xx hostname" as their
377 first two tokens, and also files that have a hostname as their
378 first token (like .ssh/known_hosts and .rhosts.)
382 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
389 /* If the name is all digits, it's not a name. */
393 for (s = name; *s; s++)
394 if (*s < '0' || *s > '9')
399 fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
400 progname, name, addr);
405 /* Create a new target using first the name then the address */
409 new = bogie_for_host (ssd, name, pd->resolve_p);
411 new = bogie_for_host (ssd, addr, pd->resolve_p);
426 delete_duplicate_hosts (sonar_sensor_data *ssd, sonar_bogie *list)
428 ping_data *pd = (ping_data *) ssd->closure;
429 sonar_bogie *head = list;
432 for (sb = head; sb; sb = sb->next)
434 ping_bogie *pb = (ping_bogie *) sb->closure;
435 struct sockaddr_in *i1 = (struct sockaddr_in *) &(pb->address);
436 unsigned long ip1 = i1->sin_addr.s_addr;
439 for (sb2 = sb; sb2; sb2 = sb2->next)
441 if (sb2 && sb2->next)
443 ping_bogie *pb2 = (ping_bogie *) sb2->next->closure;
444 struct sockaddr_in *i2 = (struct sockaddr_in *) &(pb2->address);
445 unsigned long ip2 = i2->sin_addr.s_addr;
451 fprintf (stderr, "%s: deleted duplicate: ", progname);
452 print_host (stderr, ip2, sb2->next->name);
454 sb2->next = sb2->next->next;
465 /* Generate a list of bogies consisting of all of the entries on
466 the same subnet. 'base' ip is in network order; 0 means localhost.
469 subnet_hosts (sonar_sensor_data *ssd, char **error_ret,
470 unsigned long n_base, int subnet_width)
472 ping_data *pd = (ping_data *) ssd->closure;
473 unsigned long h_mask; /* host order */
474 unsigned long h_base; /* host order */
476 /* Local Variables */
478 char hostname[BUFSIZ];
479 char address[BUFSIZ];
480 struct hostent *hent;
484 sonar_bogie *list = 0;
487 if (subnet_width < 24)
490 "Pinging %lu hosts is a bad\n"
491 "idea. Please use a subnet\n"
492 "mask of 24 bits or more.",
493 (unsigned long) (1L << (32 - subnet_width)) - 1);
494 *error_ret = strdup(buf);
497 else if (subnet_width > 30)
501 "doesn't make sense.\n"
502 "Try \"subnet/24\"\n"
503 "or \"subnet/29\".\n",
505 *error_ret = strdup(buf);
511 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
513 /* Get our hostname */
515 if (gethostname(hostname, BUFSIZ))
517 *error_ret = strdup ("Unable to determine\n"
522 /* Get our IP address and convert it to a string */
524 if (! (hent = gethostbyname(hostname)))
527 "Unable to resolve\n"
530 *error_ret = strdup(buf);
533 strcpy (address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
535 /* Construct targets for all addresses in this subnet */
538 for (i = 0; i < subnet_width; i++)
539 h_mask |= (1L << (31-i));
541 /* If no base IP specified, assume localhost. */
543 n_base = pack_addr (hent->h_addr_list[0][0],
544 hent->h_addr_list[0][1],
545 hent->h_addr_list[0][2],
546 hent->h_addr_list[0][3]);
547 h_base = ntohl (n_base);
549 if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
551 unsigned int a, b, c, d;
552 unpack_addr (n_base, &a, &b, &c, &d);
554 "Unable to determine\n"
555 "local subnet address:\n"
560 hostname, a, b, c, d);
561 *error_ret = strdup(buf);
565 for (i = 255; i >= 0; i--) {
566 unsigned int a, b, c, d;
567 int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
569 if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
571 if ((ip & ~h_mask) == 0) /* broadcast address */
573 if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
576 unpack_addr (htonl (ip), &a, &b, &c, &d);
577 sprintf (address, "%u.%u.%u.%u", a, b, c, d);
581 unsigned int aa, ab, ac, ad;
582 unsigned int ma, mb, mc, md;
583 unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
584 unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
586 "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
593 p = address + strlen(address) + 1;
596 new = bogie_for_host (ssd, address, pd->resolve_p);
608 /* Send a ping packet.
611 send_ping (ping_data *pd, const sonar_bogie *b)
613 ping_bogie *pb = (ping_bogie *) b->closure;
616 const char *token = "org.jwz.xscreensaver.sonar";
618 int pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
619 strlen(b->name) + 1 +
621 strlen(pd->version) + 1);
623 /* Create the ICMP packet */
625 if (! (packet = (u_char *) calloc(1, pcktsiz)))
626 return; /* Out of memory */
628 icmph = (struct ICMP *) packet;
629 ICMP_TYPE(icmph) = ICMP_ECHO;
630 ICMP_CODE(icmph) = 0;
631 ICMP_CHECKSUM(icmph) = 0;
632 ICMP_ID(icmph) = pd->pid;
633 ICMP_SEQ(icmph) = pd->seq++;
634 # ifdef GETTIMEOFDAY_TWO_ARGS
635 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
636 (struct timezone *) 0);
638 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
641 /* We store the name of the host we're pinging in the packet, and parse
642 that out of the return packet later (see get_ping() for why).
643 After that, we also include the name and version of this program,
644 just to give a clue to anyone sniffing and wondering what's up.
646 sprintf ((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
648 b->name, 0, token, pd->version);
650 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
654 if (sendto(pd->icmpsock, packet, pcktsiz, 0,
655 &pb->address, sizeof(pb->address))
660 sprintf(buf, "%s: pinging %s", progname, b->name);
674 /* Compute the checksum on a ping packet.
677 checksum (u_short *packet, int size)
679 register int nleft = size;
680 register u_short *w = packet;
681 register int sum = 0;
684 /* Using a 32 bit accumulator (sum), we add sequential 16 bit words
685 to it, and at the end, fold back all the carry bits from the
686 top 16 bits into the lower 16 bits.
694 /* mop up an odd byte, if necessary */
698 *(u_char *)(&answer) = *(u_char *)w ;
699 *(1 + (u_char *)(&answer)) = 0;
703 /* add back carry outs from top 16 bits to low 16 bits */
705 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
706 sum += (sum >> 16); /* add carry */
707 answer = ~sum; /* truncate to 16 bits */
713 /* Copies the sonar_bogie and the underlying ping_bogie.
716 copy_ping_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
718 sonar_bogie *b2 = copy_bogie (ssd, b);
721 ping_bogie *pb = (ping_bogie *) b->closure;
722 ping_bogie *pb2 = (ping_bogie *) calloc (1, sizeof(*pb));
723 pb2->address = pb->address;
730 /* Look for all outstanding ping replies.
733 get_ping (sonar_sensor_data *ssd)
735 ping_data *pd = (ping_data *) ssd->closure;
736 struct sockaddr from;
737 unsigned int fromlen; /* Posix says socklen_t, but that's not portable */
741 struct timeval *then;
746 sonar_bogie *new = 0;
752 /* Set up a signal to interrupt our wait for a packet */
754 sigemptyset(&sa.sa_mask);
756 sa.sa_handler = sigcatcher;
757 if (sigaction(SIGALRM, &sa, 0) == -1)
760 sprintf(msg, "%s: unable to trap SIGALRM", progname);
765 /* Set up a timer to interupt us if we don't get a packet */
767 it.it_interval.tv_sec = 0;
768 it.it_interval.tv_usec = 0;
769 it.it_value.tv_sec = 0;
770 it.it_value.tv_usec = pd->timeout;
772 setitimer(ITIMER_REAL, &it, 0);
774 /* Wait for a result packet */
776 fromlen = sizeof(from);
777 while (! timer_expired)
779 tv.tv_usec = pd->timeout;
782 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
785 memset (&rfds, 0, sizeof(rfds));
787 FD_SET(pd->icmpsock, &rfds);
788 /* only wait a little while, in case we raced with the timer expiration.
789 From Valentijn Sessink <valentyn@openoffice.nl> */
790 if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
792 result = recvfrom (pd->icmpsock, packet, sizeof(packet),
795 /* Check the packet */
797 # ifdef GETTIMEOFDAY_TWO_ARGS
798 gettimeofday(&now, (struct timezone *) 0);
802 ip = (struct ip *) packet;
803 iphdrlen = IP_HDRLEN(ip) << 2;
804 icmph = (struct ICMP *) &packet[iphdrlen];
805 then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
808 /* Ignore anything but ICMP Replies */
809 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY)
812 /* Ignore packets not set from us */
813 if (ICMP_ID(icmph) != pd->pid)
816 /* Find the bogie in 'targets' that corresponds to this packet
817 and copy it, so that this bogie stays in the same spot (th)
818 on the screen, and so that we don't have to resolve it again.
820 We could find the bogie by comparing ip->ip_src.s_addr to
821 pb->address, but it is possible that, in certain weird router
822 or NAT situations, that the reply will come back from a
823 different address than the one we sent it to. So instead,
824 we parse the name out of the reply packet payload.
827 const char *name = (char *) &packet[iphdrlen +
828 sizeof(struct ICMP) +
829 sizeof(struct timeval)];
831 for (b = pd->targets; b; b = b->next)
832 if (!strcmp (name, b->name))
834 new = copy_ping_bogie (ssd, b);
839 if (! new) /* not in targets? */
841 unsigned int a, b, c, d;
842 unpack_addr (ip->ip_src.s_addr, &a, &b, &c, &d);
844 "%s: UNEXPECTED PING REPLY! "
845 "%4d bytes, icmp_seq=%-4d from %d.%d.%d.%d\n",
846 progname, result, ICMP_SEQ(icmph), a, b, c, d);
854 double msec = delta(then, &now) / 1000.0;
858 if (new->desc) free (new->desc);
859 new->desc = (char *) malloc (30);
860 if (msec > 99) sprintf (new->desc, "%.0f ms", msec);
861 else if (msec > 9) sprintf (new->desc, "%.1f ms", msec);
862 else if (msec > 1) sprintf (new->desc, "%.2f ms", msec);
863 else sprintf (new->desc, "%.3f ms", msec);
866 if (pd->debug_p && pd->times_p) /* ping-like stdout log */
868 char *s = strdup(new->name);
872 s2 = s + strlen(s) - 28;
873 strncpy (s2, "...", 3);
876 "%3d bytes from %28s: icmp_seq=%-4d time=%s\n",
877 result, s2, ICMP_SEQ(icmph), new->desc);
882 /* The radius must be between 0.0 and 1.0.
883 We want to display ping times on a logarithmic scale,
884 with the three rings being 2.5, 70 and 2,000 milliseconds.
886 if (msec <= 0) msec = 0.001;
887 new->r = log (msec * 10) / log (20000);
889 /* Don't put anyone *too* close to the center of the screen. */
890 if (new->r < 0) new->r = 0;
891 if (new->r < 0.1) new->r += 0.1;
900 /* difference between the two times in microseconds.
903 delta (struct timeval *then, struct timeval *now)
905 return (((now->tv_sec - then->tv_sec) * 1000000) +
906 (now->tv_usec - then->tv_usec));
911 ping_free_data (sonar_sensor_data *ssd, void *closure)
913 ping_data *pd = (ping_data *) closure;
914 sonar_bogie *b = pd->targets;
917 sonar_bogie *b2 = b->next;
925 ping_free_bogie_data (sonar_sensor_data *sd, void *closure)
931 /* Returns the current time in seconds as a double.
937 # ifdef GETTIMEOFDAY_TWO_ARGS
939 gettimeofday(&now, &tzp);
944 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
948 /* If a bogie is provided, pings it.
949 Then, returns all outstanding ping replies.
952 ping_scan (sonar_sensor_data *ssd)
954 ping_data *pd = (ping_data *) ssd->closure;
955 double now = double_time();
956 double ping_cycle = 10; /* re-ping a given host every 10 seconds */
957 double ping_interval = ping_cycle / pd->target_count;
959 if (now > pd->last_ping_time + ping_interval) /* time to ping someone */
962 pd->last_pinged = pd->last_pinged->next;
963 if (! pd->last_pinged)
964 pd->last_pinged = pd->targets;
965 send_ping (pd, pd->last_pinged);
966 pd->last_ping_time = now;
969 return get_ping (ssd);
973 /* Returns a list of hosts to ping based on the "-ping" argument.
976 parse_mode (sonar_sensor_data *ssd, char **error_ret,
977 const char *ping_arg, Bool ping_works_p)
979 ping_data *pd = (ping_data *) ssd->closure;
980 char *source, *token, *end, dummy;
981 sonar_bogie *hostlist = 0;
983 if (!ping_arg || !*ping_arg || !strcmp (ping_arg, "default"))
984 source = strdup("subnet/28");
986 source = strdup(ping_arg);
989 end = source + strlen(source);
995 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1000 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1007 fprintf (stderr, "%s: parsing %s\n", progname, token);
1011 *error_ret = strdup ("Sonar must be setuid to ping!\n"
1012 "Running simulation instead.");
1016 if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) ||
1017 (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1019 /* subnet: A.B.C.D/M
1022 unsigned long ip = pack_addr (n0, n1, n2, n3);
1023 new = subnet_hosts (ssd, error_ret, ip, m);
1025 else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1029 new = bogie_for_host (ssd, token, pd->resolve_p);
1031 else if (!strcmp (token, "subnet"))
1033 new = subnet_hosts (ssd, error_ret, 0, 24);
1035 else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1037 new = subnet_hosts (ssd, error_ret, 0, m);
1039 else if (*token == '.' || *token == '/' ||
1040 *token == '$' || *token == '~' ||
1045 new = read_hosts_file (ssd, token);
1049 /* not an existant file - must be a host name
1051 new = bogie_for_host (ssd, token, pd->resolve_p);
1056 sonar_bogie *nn = new;
1059 nn->next = hostlist;
1064 while (token < end &&
1065 (*token == ',' || *token == ' ' ||
1066 *token == '\t' || *token == '\n'))
1076 init_ping (Display *dpy, char **error_ret,
1077 const char *subnet, int timeout,
1078 Bool resolve_p, Bool times_p, Bool debug_p)
1080 sonar_sensor_data *ssd = (sonar_sensor_data *) calloc (1, sizeof(*ssd));
1081 ping_data *pd = (ping_data *) calloc (1, sizeof(*pd));
1086 Bool socket_initted_p = False;
1087 Bool socket_raw_p = False;
1089 pd->resolve_p = resolve_p;
1090 pd->times_p = times_p;
1091 pd->debug_p = debug_p;
1094 ssd->scan_cb = ping_scan;
1095 ssd->free_data_cb = ping_free_data;
1096 ssd->free_bogie_cb = ping_free_bogie_data;
1098 /* Get short version number. */
1099 s = strchr (screensaver_id, ' ');
1100 pd->version = strdup (s+1);
1101 s = strchr (pd->version, ' ');
1105 /* Create the ICMP socket. Do this before dropping privs.
1107 Raw sockets can only be opened by root (or setuid root), so we
1108 only try to do this when the effective uid is 0.
1110 We used to just always try, and notice the failure. But apparently
1111 that causes "SELinux" to log spurious warnings when running with the
1112 "strict" policy. So to avoid that, we just don't try unless we
1115 On MacOS X, we can avoid the whole problem by using a
1116 non-privileged datagram instead of a raw socket.
1118 if (global_icmpsock)
1120 pd->icmpsock = global_icmpsock;
1121 socket_initted_p = True;
1123 fprintf (stderr, "%s: re-using icmp socket\n", progname);
1126 else if ((pd->icmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0)
1128 socket_initted_p = True;
1130 else if (geteuid() == 0 &&
1131 (pd->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
1133 socket_initted_p = True;
1134 socket_raw_p = True;
1137 if (socket_initted_p)
1139 global_icmpsock = pd->icmpsock;
1140 socket_initted_p = True;
1142 fprintf (stderr, "%s: opened %s icmp socket\n", progname,
1143 (socket_raw_p ? "raw" : "dgram"));
1146 fprintf (stderr, "%s: unable to open icmp socket\n", progname);
1151 pd->pid = getpid() & 0xFFFF;
1153 pd->timeout = timeout;
1155 /* Generate a list of targets */
1157 pd->targets = parse_mode (ssd, error_ret, subnet, socket_initted_p);
1158 pd->targets = delete_duplicate_hosts (ssd, pd->targets);
1162 fprintf (stderr, "%s: Target list:\n", progname);
1163 for (b = pd->targets; b; b = b->next)
1165 ping_bogie *pb = (ping_bogie *) b->closure;
1166 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
1167 unsigned long ip = iaddr->sin_addr.s_addr;
1168 fprintf (stderr, "%s: ", progname);
1169 print_host (stderr, ip, b->name);
1173 /* Make sure there is something to ping */
1175 pd->target_count = 0;
1176 for (b = pd->targets; b; b = b->next)
1179 if (pd->target_count == 0)
1182 *error_ret = strdup ("No hosts to ping!\n"
1183 "Simulating instead.");
1184 if (pd) ping_free_data (ssd, pd);
1185 if (ssd) free (ssd);
1189 /* Distribute them evenly around the display field.
1191 div = pd->target_count;
1192 if (div > 90) div = 90; /* no closer together than 4 degrees */
1193 for (i = 0, b = pd->targets; b; b = b->next, i++)
1194 b->th = M_PI * 2 * ((div - i) % div) / div;
1199 #endif /* HAVE_PING -- whole file */