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 */
370 if ((addr = strtok(buf, " ,;\t\n")))
371 name = strtok(0, " ,;\t\n");
375 /* Check to see if the addr looks like an addr. If not, assume
376 the addr is a name and there is no addr. This way, we can
377 handle files whose lines have "xx.xx.xx.xx hostname" as their
378 first two tokens, and also files that have a hostname as their
379 first token (like .ssh/known_hosts and .rhosts.)
383 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
390 /* If the name is all digits, it's not a name. */
394 for (s = name; *s; s++)
395 if (*s < '0' || *s > '9')
400 fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
401 progname, name, addr);
406 /* Create a new target using first the name then the address */
410 new = bogie_for_host (ssd, name, pd->resolve_p);
412 new = bogie_for_host (ssd, addr, pd->resolve_p);
427 delete_duplicate_hosts (sonar_sensor_data *ssd, sonar_bogie *list)
429 ping_data *pd = (ping_data *) ssd->closure;
430 sonar_bogie *head = list;
433 for (sb = head; sb; sb = sb->next)
435 ping_bogie *pb = (ping_bogie *) sb->closure;
436 struct sockaddr_in *i1 = (struct sockaddr_in *) &(pb->address);
437 unsigned long ip1 = i1->sin_addr.s_addr;
440 for (sb2 = sb; sb2; sb2 = sb2->next)
442 if (sb2 && sb2->next)
444 ping_bogie *pb2 = (ping_bogie *) sb2->next->closure;
445 struct sockaddr_in *i2 = (struct sockaddr_in *) &(pb2->address);
446 unsigned long ip2 = i2->sin_addr.s_addr;
452 fprintf (stderr, "%s: deleted duplicate: ", progname);
453 print_host (stderr, ip2, sb2->next->name);
455 sb2->next = sb2->next->next;
466 /* Generate a list of bogies consisting of all of the entries on
467 the same subnet. 'base' ip is in network order; 0 means localhost.
470 subnet_hosts (sonar_sensor_data *ssd, char **error_ret,
471 unsigned long n_base, int subnet_width)
473 ping_data *pd = (ping_data *) ssd->closure;
474 unsigned long h_mask; /* host order */
475 unsigned long h_base; /* host order */
477 /* Local Variables */
479 char hostname[BUFSIZ];
480 char address[BUFSIZ];
481 struct hostent *hent;
485 sonar_bogie *list = 0;
488 if (subnet_width < 24)
491 "Pinging %lu hosts is a bad\n"
492 "idea. Please use a subnet\n"
493 "mask of 24 bits or more.",
494 (unsigned long) (1L << (32 - subnet_width)) - 1);
495 *error_ret = strdup(buf);
498 else if (subnet_width > 30)
502 "doesn't make sense.\n"
503 "Try \"subnet/24\"\n"
504 "or \"subnet/29\".\n",
506 *error_ret = strdup(buf);
512 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
514 /* Get our hostname */
516 if (gethostname(hostname, BUFSIZ))
518 *error_ret = strdup ("Unable to determine\n"
523 /* Get our IP address and convert it to a string */
525 if (! (hent = gethostbyname(hostname)))
528 "Unable to resolve\n"
531 *error_ret = strdup(buf);
534 strcpy (address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
536 /* Construct targets for all addresses in this subnet */
539 for (i = 0; i < subnet_width; i++)
540 h_mask |= (1L << (31-i));
542 /* If no base IP specified, assume localhost. */
544 n_base = pack_addr (hent->h_addr_list[0][0],
545 hent->h_addr_list[0][1],
546 hent->h_addr_list[0][2],
547 hent->h_addr_list[0][3]);
548 h_base = ntohl (n_base);
550 if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
552 unsigned int a, b, c, d;
553 unpack_addr (n_base, &a, &b, &c, &d);
555 "Unable to determine\n"
556 "local subnet address:\n"
561 hostname, a, b, c, d);
562 *error_ret = strdup(buf);
566 for (i = 255; i >= 0; i--) {
567 unsigned int a, b, c, d;
568 int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
570 if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
572 if ((ip & ~h_mask) == 0) /* broadcast address */
574 if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
577 unpack_addr (htonl (ip), &a, &b, &c, &d);
578 sprintf (address, "%u.%u.%u.%u", a, b, c, d);
582 unsigned int aa, ab, ac, ad;
583 unsigned int ma, mb, mc, md;
584 unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
585 unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
587 "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
594 p = address + strlen(address) + 1;
597 new = bogie_for_host (ssd, address, pd->resolve_p);
609 /* Send a ping packet.
612 send_ping (ping_data *pd, const sonar_bogie *b)
614 ping_bogie *pb = (ping_bogie *) b->closure;
618 const char *token = "org.jwz.xscreensaver.sonar";
620 int pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
621 strlen(b->name) + 1 +
623 strlen(pd->version) + 1);
625 /* Create the ICMP packet */
627 if (! (packet = (u_char *) calloc(1, pcktsiz)))
628 return; /* Out of memory */
630 icmph = (struct ICMP *) packet;
631 ICMP_TYPE(icmph) = ICMP_ECHO;
632 ICMP_CODE(icmph) = 0;
633 ICMP_CHECKSUM(icmph) = 0;
634 ICMP_ID(icmph) = pd->pid;
635 ICMP_SEQ(icmph) = pd->seq++;
636 # ifdef GETTIMEOFDAY_TWO_ARGS
637 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
638 (struct timezone *) 0);
640 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
643 /* We store the name of the host we're pinging in the packet, and parse
644 that out of the return packet later (see get_ping() for why).
645 After that, we also include the name and version of this program,
646 just to give a clue to anyone sniffing and wondering what's up.
648 sprintf ((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
650 b->name, 0, token, pd->version);
652 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
656 if ((result = sendto(pd->icmpsock, packet, pcktsiz, 0,
657 &pb->address, sizeof(pb->address)))
662 sprintf(buf, "%s: pinging %s", progname, b->name);
676 /* Compute the checksum on a ping packet.
679 checksum (u_short *packet, int size)
681 register int nleft = size;
682 register u_short *w = packet;
683 register int sum = 0;
686 /* Using a 32 bit accumulator (sum), we add sequential 16 bit words
687 to it, and at the end, fold back all the carry bits from the
688 top 16 bits into the lower 16 bits.
696 /* mop up an odd byte, if necessary */
700 *(u_char *)(&answer) = *(u_char *)w ;
701 *(1 + (u_char *)(&answer)) = 0;
705 /* add back carry outs from top 16 bits to low 16 bits */
707 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
708 sum += (sum >> 16); /* add carry */
709 answer = ~sum; /* truncate to 16 bits */
715 /* Copies the sonar_bogie and the underlying ping_bogie.
718 copy_ping_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
720 sonar_bogie *b2 = copy_bogie (ssd, b);
723 ping_bogie *pb = (ping_bogie *) b->closure;
724 ping_bogie *pb2 = (ping_bogie *) calloc (1, sizeof(*pb));
725 pb2->address = pb->address;
732 /* Look for all outstanding ping replies.
735 get_ping (sonar_sensor_data *ssd)
737 ping_data *pd = (ping_data *) ssd->closure;
738 struct sockaddr from;
739 unsigned int fromlen; /* Posix says socklen_t, but that's not portable */
743 struct timeval *then;
748 sonar_bogie *new = 0;
754 /* Set up a signal to interrupt our wait for a packet */
756 sigemptyset(&sa.sa_mask);
758 sa.sa_handler = sigcatcher;
759 if (sigaction(SIGALRM, &sa, 0) == -1)
762 sprintf(msg, "%s: unable to trap SIGALRM", progname);
767 /* Set up a timer to interupt us if we don't get a packet */
769 it.it_interval.tv_sec = 0;
770 it.it_interval.tv_usec = 0;
771 it.it_value.tv_sec = 0;
772 it.it_value.tv_usec = pd->timeout;
774 setitimer(ITIMER_REAL, &it, 0);
776 /* Wait for a result packet */
778 fromlen = sizeof(from);
779 while (! timer_expired)
781 tv.tv_usec = pd->timeout;
784 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
787 memset (&rfds, 0, sizeof(rfds));
789 FD_SET(pd->icmpsock, &rfds);
790 /* only wait a little while, in case we raced with the timer expiration.
791 From Valentijn Sessink <valentyn@openoffice.nl> */
792 if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
794 result = recvfrom (pd->icmpsock, packet, sizeof(packet),
797 /* Check the packet */
799 # ifdef GETTIMEOFDAY_TWO_ARGS
800 gettimeofday(&now, (struct timezone *) 0);
804 ip = (struct ip *) packet;
805 iphdrlen = IP_HDRLEN(ip) << 2;
806 icmph = (struct ICMP *) &packet[iphdrlen];
807 then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
810 /* Ignore anything but ICMP Replies */
811 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY)
814 /* Ignore packets not set from us */
815 if (ICMP_ID(icmph) != pd->pid)
818 /* Find the bogie in 'targets' that corresponds to this packet
819 and copy it, so that this bogie stays in the same spot (th)
820 on the screen, and so that we don't have to resolve it again.
822 We could find the bogie by comparing ip->ip_src.s_addr to
823 pb->address, but it is possible that, in certain weird router
824 or NAT situations, that the reply will come back from a
825 different address than the one we sent it to. So instead,
826 we parse the name out of the reply packet payload.
829 const char *name = (char *) &packet[iphdrlen +
830 sizeof(struct ICMP) +
831 sizeof(struct timeval)];
833 for (b = pd->targets; b; b = b->next)
834 if (!strcmp (name, b->name))
836 new = copy_ping_bogie (ssd, b);
841 if (! new) /* not in targets? */
843 unsigned int a, b, c, d;
844 unpack_addr (ip->ip_src.s_addr, &a, &b, &c, &d);
846 "%s: UNEXPECTED PING REPLY! "
847 "%4d bytes, icmp_seq=%-4d from %d.%d.%d.%d\n",
848 progname, result, ICMP_SEQ(icmph), a, b, c, d);
856 double msec = delta(then, &now) / 1000.0;
860 if (new->desc) free (new->desc);
861 new->desc = (char *) malloc (30);
862 if (msec > 99) sprintf (new->desc, "%.0f ms", msec);
863 else if (msec > 9) sprintf (new->desc, "%.1f ms", msec);
864 else if (msec > 1) sprintf (new->desc, "%.2f ms", msec);
865 else sprintf (new->desc, "%.3f ms", msec);
868 if (pd->debug_p && pd->times_p) /* ping-like stdout log */
870 char *s = strdup(new->name);
874 s2 = s + strlen(s) - 28;
875 strncpy (s2, "...", 3);
878 "%3d bytes from %28s: icmp_seq=%-4d time=%s\n",
879 result, s2, ICMP_SEQ(icmph), new->desc);
884 /* The radius must be between 0.0 and 1.0.
885 We want to display ping times on a logarithmic scale,
886 with the three rings being 2.5, 70 and 2,000 milliseconds.
888 if (msec <= 0) msec = 0.001;
889 new->r = log (msec * 10) / log (20000);
891 /* Don't put anyone *too* close to the center of the screen. */
892 if (new->r < 0) new->r = 0;
893 if (new->r < 0.1) new->r += 0.1;
902 /* difference between the two times in microseconds.
905 delta (struct timeval *then, struct timeval *now)
907 return (((now->tv_sec - then->tv_sec) * 1000000) +
908 (now->tv_usec - then->tv_usec));
913 ping_free_data (sonar_sensor_data *ssd, void *closure)
915 ping_data *pd = (ping_data *) closure;
916 sonar_bogie *b = pd->targets;
919 sonar_bogie *b2 = b->next;
927 ping_free_bogie_data (sonar_sensor_data *sd, void *closure)
933 /* Returns the current time in seconds as a double.
939 # ifdef GETTIMEOFDAY_TWO_ARGS
941 gettimeofday(&now, &tzp);
946 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
950 /* If a bogie is provided, pings it.
951 Then, returns all outstanding ping replies.
954 ping_scan (sonar_sensor_data *ssd)
956 ping_data *pd = (ping_data *) ssd->closure;
957 double now = double_time();
958 double ping_cycle = 10; /* re-ping a given host every 10 seconds */
959 double ping_interval = ping_cycle / pd->target_count;
961 if (now > pd->last_ping_time + ping_interval) /* time to ping someone */
964 pd->last_pinged = pd->last_pinged->next;
965 if (! pd->last_pinged)
966 pd->last_pinged = pd->targets;
967 send_ping (pd, pd->last_pinged);
968 pd->last_ping_time = now;
971 return get_ping (ssd);
975 /* Returns a list of hosts to ping based on the "-ping" argument.
978 parse_mode (sonar_sensor_data *ssd, char **error_ret,
979 const char *ping_arg, Bool ping_works_p)
981 ping_data *pd = (ping_data *) ssd->closure;
982 char *source, *token, *end, dummy;
983 sonar_bogie *hostlist = 0;
985 if (!ping_arg || !*ping_arg || !strcmp (ping_arg, "default"))
986 source = strdup("subnet/28");
988 source = strdup(ping_arg);
991 end = source + strlen(source);
997 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1002 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1009 fprintf (stderr, "%s: parsing %s\n", progname, token);
1013 *error_ret = strdup ("Sonar must be setuid to ping!\n"
1014 "Running simulation instead.");
1018 if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) ||
1019 (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1021 /* subnet: A.B.C.D/M
1024 unsigned long ip = pack_addr (n0, n1, n2, n3);
1025 new = subnet_hosts (ssd, error_ret, ip, m);
1027 else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1031 new = bogie_for_host (ssd, token, pd->resolve_p);
1033 else if (!strcmp (token, "subnet"))
1035 new = subnet_hosts (ssd, error_ret, 0, 24);
1037 else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1039 new = subnet_hosts (ssd, error_ret, 0, m);
1041 else if (*token == '.' || *token == '/' ||
1042 *token == '$' || *token == '~' ||
1047 new = read_hosts_file (ssd, token);
1051 /* not an existant file - must be a host name
1053 new = bogie_for_host (ssd, token, pd->resolve_p);
1058 sonar_bogie *nn = new;
1059 while (nn && nn->next)
1061 nn->next = hostlist;
1066 while (token < end &&
1067 (*token == ',' || *token == ' ' ||
1068 *token == '\t' || *token == '\n'))
1078 init_ping (Display *dpy, char **error_ret,
1079 const char *subnet, int timeout,
1080 Bool resolve_p, Bool times_p, Bool debug_p)
1082 sonar_sensor_data *ssd = (sonar_sensor_data *) calloc (1, sizeof(*ssd));
1083 ping_data *pd = (ping_data *) calloc (1, sizeof(*pd));
1088 Bool socket_initted_p = False;
1089 Bool socket_raw_p = False;
1091 pd->resolve_p = resolve_p;
1092 pd->times_p = times_p;
1093 pd->debug_p = debug_p;
1096 ssd->scan_cb = ping_scan;
1097 ssd->free_data_cb = ping_free_data;
1098 ssd->free_bogie_cb = ping_free_bogie_data;
1100 /* Get short version number. */
1101 s = strchr (screensaver_id, ' ');
1102 pd->version = strdup (s+1);
1103 s = strchr (pd->version, ' ');
1107 /* Create the ICMP socket. Do this before dropping privs.
1109 Raw sockets can only be opened by root (or setuid root), so we
1110 only try to do this when the effective uid is 0.
1112 We used to just always try, and notice the failure. But apparently
1113 that causes "SELinux" to log spurious warnings when running with the
1114 "strict" policy. So to avoid that, we just don't try unless we
1117 On MacOS X, we can avoid the whole problem by using a
1118 non-privileged datagram instead of a raw socket.
1120 if (global_icmpsock)
1122 pd->icmpsock = global_icmpsock;
1123 socket_initted_p = True;
1125 fprintf (stderr, "%s: re-using icmp socket\n", progname);
1128 else if ((pd->icmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0)
1130 socket_initted_p = True;
1132 else if (geteuid() == 0 &&
1133 (pd->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
1135 socket_initted_p = True;
1136 socket_raw_p = True;
1139 if (socket_initted_p)
1141 global_icmpsock = pd->icmpsock;
1142 socket_initted_p = True;
1144 fprintf (stderr, "%s: opened %s icmp socket\n", progname,
1145 (socket_raw_p ? "raw" : "dgram"));
1148 fprintf (stderr, "%s: unable to open icmp socket\n", progname);
1153 pd->pid = getpid() & 0xFFFF;
1155 pd->timeout = timeout;
1157 /* Generate a list of targets */
1159 pd->targets = parse_mode (ssd, error_ret, subnet, socket_initted_p);
1160 pd->targets = delete_duplicate_hosts (ssd, pd->targets);
1164 fprintf (stderr, "%s: Target list:\n", progname);
1165 for (b = pd->targets; b; b = b->next)
1167 ping_bogie *pb = (ping_bogie *) b->closure;
1168 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
1169 unsigned long ip = iaddr->sin_addr.s_addr;
1170 fprintf (stderr, "%s: ", progname);
1171 print_host (stderr, ip, b->name);
1175 /* Make sure there is something to ping */
1177 pd->target_count = 0;
1178 for (b = pd->targets; b; b = b->next)
1181 if (pd->target_count == 0)
1184 *error_ret = strdup ("No hosts to ping!\n"
1185 "Simulating instead.");
1186 if (pd) ping_free_data (ssd, pd);
1187 if (ssd) free (ssd);
1191 /* Distribute them evenly around the display field.
1193 div = pd->target_count;
1194 if (div > 90) div = 90; /* no closer together than 4 degrees */
1195 for (i = 0, b = pd->targets; b; b = b->next, i++)
1196 b->th = M_PI * 2 * ((div - i) % div) / div;
1201 #endif /* HAVE_PING -- whole file */