1 /* sonar.c --- Simulate a sonar screen.
3 * This is an implementation of a general purpose reporting tool in the
4 * format of a Sonar display. It is designed such that a sensor is read
5 * on every movement of a sweep arm and the results of that sensor are
6 * displayed on the screen. The location of the display points (targets) on the
7 * screen are determined by the current localtion of the sweep and a distance
8 * value associated with the target.
10 * Currently the only two sensors that are implemented are the simulator
11 * (the default) and the ping sensor. The simulator randomly creates a set
12 * of bogies that move around on the scope while the ping sensor can be
13 * used to display hosts on your network.
15 * The ping code is only compiled in if you define HAVE_ICMP or HAVE_ICMPHDR,
16 * because, unfortunately, different systems have different ways of creating
17 * these sorts of packets.
19 * Also: creating an ICMP socket is a privileged operation, so the program
20 * needs to be installed SUID root if you want to use the ping mode. If you
21 * check the code you will see that this privilige is given up immediately
22 * after the socket is created.
24 * It should be easy to extend this code to support other sorts of sensors.
26 * - search the output of "netstat" for the list of hosts to ping;
27 * - plot the contents of /proc/interrupts;
28 * - plot the process table, by process size, cpu usage, or total time;
29 * - plot the logged on users by idle time or cpu usage.
31 * Copyright (C) 1998, 2001
32 * by Stephen Martin (smartin@vanderfleet-martin.net).
33 * Permission to use, copy, modify, distribute, and sell this software and its
34 * documentation for any purpose is hereby granted without fee, provided that
35 * the above copyright notice appear in all copies and that both that
36 * copyright notice and this permission notice appear in supporting
37 * documentation. No representations are made about the suitability of this
38 * software for any purpose. It is provided "as is" without express or
43 * Version 1.0 April 27, 1998.
45 * - Submitted to RedHat Screensaver Contest
47 * Version 1.1 November 3, 1998.
48 * - Added simulation mode.
49 * - Added enhancements by Thomas Bahls <thommy@cs.tu-berlin.de>
50 * - Fixed huge memory leak.
51 * - Submitted to xscreensavers
54 * - All ping code is now ifdef-ed by the compile time symbol HAVE_PING;
55 * use -DHAVE_PING to include it when you compile.
56 * - Sweep now uses gradients.
57 * - Fixed portability problems with icmphdr on some systems.
58 * - removed lowColor option/resource.
59 * - changed copyright notice so that it could be included in the xscreensavers
62 * Version 1.3 November 16, 1998.
63 * - All ping code is now ifdef-ed by the compile time symbol PING use -DPING
64 * to include it when you compile.
65 * - Sweep now uses gradients.
66 * - Fixed portability problems with icmphdr on some systems.
67 * - removed lowcolour option/resource.
68 * - changed copyright notice so that it could be included in the xscreensavers
71 * Version 1.4 November 18, 1998.
72 * - More ping portability fixes.
74 * Version 1.5 November 19, 1998.
75 * - Synced up with jwz's changes.
76 * - Now need to define HAVE_PING to compile in the ping stuff.
79 /* These are computed by configure now:
92 #include "screenhack.h"
96 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
101 # include <sys/types.h>
102 # include <sys/time.h>
103 # include <sys/ipc.h>
104 # include <sys/shm.h>
105 # include <sys/socket.h>
106 # include <netinet/in_systm.h>
107 # include <netinet/in.h>
108 # include <netinet/ip.h>
109 # include <netinet/ip_icmp.h>
110 # include <netinet/udp.h>
111 # include <arpa/inet.h>
113 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
119 #define MY_MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
122 # define LINE_MAX 2048
127 #if defined(HAVE_ICMP)
130 # define ICMP_TYPE(p) (p)->icmp_type
131 # define ICMP_CODE(p) (p)->icmp_code
132 # define ICMP_CHECKSUM(p) (p)->icmp_cksum
133 # define ICMP_ID(p) (p)->icmp_id
134 # define ICMP_SEQ(p) (p)->icmp_seq
135 #elif defined(HAVE_ICMPHDR)
137 # define ICMP icmphdr
138 # define ICMP_TYPE(p) (p)->type
139 # define ICMP_CODE(p) (p)->code
140 # define ICMP_CHECKSUM(p) (p)->checksum
141 # define ICMP_ID(p) (p)->un.echo.id
142 # define ICMP_SEQ(p) (p)->un.echo.sequence
147 /* Forward References */
150 static u_short checksum(u_short *, int);
152 static long delta(struct timeval *, struct timeval *);
155 /* Data Structures */
160 * This represents an object that is visable on the scope.
163 typedef struct Bogie {
164 char *name; /* The name of the thing being displayed */
165 int distance; /* The distance to this thing (0 - 100) */
166 int tick; /* The tick that it was found on */
167 int ttl; /* The time to live */
168 int age; /* How long it's been around */
169 struct Bogie *next; /* The next one in the list */
175 * This contains all of the runtime information about the sonar scope.
179 Display *dpy; /* The X display */
180 Window win; /* The window */
181 GC hi, /* The leading edge of the sweep */
182 lo, /* The trailing part of the sweep */
183 erase, /* Used to erase things */
184 grid, /* Used to draw the grid */
185 text; /* Used to draw text */
186 Colormap cmap; /* The colormap */
187 XFontStruct *font; /* The font to use for the labels */
188 int text_steps; /* How many steps to fade text. */
189 XColor *text_colors; /* Pixel values used to fade text */
190 int sweep_degrees; /* How much of the circle the sweep uses */
191 int sweep_segs; /* How many gradients in the sweep. */
192 XColor *sweep_colors; /* The sweep pixel values */
193 int width, height; /* Window dimensions */
194 int minx, miny, maxx, maxy, /* Bounds of the scope */
195 centrex, centrey, radius; /* Parts of the scope circle */
196 Bogie *visable; /* List of visable objects */
197 int current; /* Current position of sweep */
198 int sweepnum; /* The current id of the sweep */
199 int delay; /* how long between each frame of the anim */
201 int TTL; /* The number of ticks that bogies are visible
202 on the screen before they fade away. */
205 static Bool debug_p = False;
209 * Variables to support the differnt Sonar modes.
212 Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
213 void *sensor_info; /* Information about the sensor */
216 * A list of targets to ping.
219 typedef struct ping_target {
220 char *name; /* The name of the target */
221 struct sockaddr address; /* The address of the target */
222 struct ping_target *next; /* The next one in the list */
230 * This contains the information for the ping sensor.
234 int icmpsock; /* Socket for sending pings */
235 int pid; /* Our process ID */
236 int seq; /* Packet sequence number */
237 int timeout; /* Timeout value for pings */
238 ping_target *targets; /* List of targets to ping */
239 int numtargets; /* The number of targets to ping */
242 /* Flag to indicate that the timer has expired on us */
244 static int timer_expired;
246 #endif /* HAVE_PING */
249 * A list of targets for the simulator
252 typedef struct sim_target {
253 char *name; /* The name of the target */
254 int nexttick; /* The next tick that this will be seen */
255 int nextdist; /* The distance on that tick */
256 int movedonsweep; /* The number of the sweep this last moved */
260 * Simulator Information.
262 * This contains the information for the simulator mode.
266 sim_target *teamA; /* The bogies for the A team */
267 int numA; /* The number of bogies in team A */
268 char *teamAID; /* The identifier for bogies in team A */
269 sim_target *teamB; /* The bogies for the B team */
270 int numB; /* The number of bogies in team B */
271 char *teamBID; /* The identifier for bogies in team B */
274 /* Name of the Screensaver hack */
276 char *progclass="sonar";
278 /* Application Defaults */
280 char *defaults [] = {
281 ".background: #000000",
282 ".sweepColor: #00FF00",
284 "*scopeColor: #003300",
285 "*gridColor: #00AA00",
286 "*textColor: #FFFF00",
292 "*textSteps: 80", /* npixels */
293 "*sweepSegments: 80", /* npixels */
295 "*pingTimeout: 3000",
307 /* Options passed to this program */
309 XrmOptionDescRec options [] = {
310 {"-background", ".background", XrmoptionSepArg, 0 },
311 {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 },
312 {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 },
313 {"-grid-color", ".gridColor", XrmoptionSepArg, 0 },
314 {"-text-color", ".textColor", XrmoptionSepArg, 0 },
315 {"-ttl", ".ttl", XrmoptionSepArg, 0 },
316 {"-font", ".font", XrmoptionSepArg, 0 },
318 {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
319 #endif /* HAVE_PING */
320 {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
321 {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
322 {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
323 {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
325 {"-ping", ".ping", XrmoptionSepArg, 0 },
326 {"-debug", ".debug", XrmoptionNoArg, "True" },
331 * Create a new Bogie and set some initial values.
334 * name - The name of the bogie.
335 * distance - The distance value.
336 * tick - The tick value.
337 * ttl - The time to live value.
340 * The newly allocated bogie or null if a memory problem occured.
344 newBogie(char *name, int distance, int tick, int ttl)
347 /* Local Variables */
351 /* Allocate a bogie and initialize it */
353 if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
354 fprintf(stderr, "%s: Out of Memory\n", progname);
358 new->distance = distance;
362 new->next = (Bogie *) 0;
370 * b - The bogie to free.
377 if (b->name != (char *) 0)
383 * Find a bogie by name in a list.
385 * This does a simple linear search of the list for a given name.
388 * bl - The Bogie list to search.
389 * name - The name to look for.
392 * The requested Bogie or null if it wasn't found.
396 findNode(Bogie *bl, char *name)
399 /* Local Variables */
403 /* Abort if the list is empty or no name is given */
405 if ((name == NULL) || (bl == NULL))
408 /* Search the list for the desired name */
412 if (strcmp(p->name, name) == 0)
425 * Lookup the address for a ping target;
428 * target - The ping_target fill in the address for.
431 * 1 if the host was successfully resolved, 0 otherwise.
435 lookupHost(ping_target *target)
438 struct hostent *hent;
440 /* Local Variables */
442 struct sockaddr_in *iaddr;
444 /* Set up the target address we first assume that the name is the
445 IP address as a string */
447 iaddr = (struct sockaddr_in *) &(target->address);
448 iaddr->sin_family = AF_INET;
449 if ((iaddr->sin_addr.s_addr = inet_addr(target->name)) >= 0) {
451 ip[3] = iaddr->sin_addr.s_addr >> 24 & 255;
452 ip[2] = iaddr->sin_addr.s_addr >> 16 & 255;
453 ip[1] = iaddr->sin_addr.s_addr >> 8 & 255;
454 ip[0] = iaddr->sin_addr.s_addr & 255;
455 hent = gethostbyaddr (ip, 4, AF_INET);
456 if (hent && hent->h_name && *hent->h_name) {
457 target->name = strdup (hent->h_name);
462 /* Conversion of IP address failed, try to look the host up by name */
464 hent = gethostbyname(target->name);
466 fprintf(stderr, "%s: could not resolve host %s\n",
467 progname, target->name);
470 memcpy(&iaddr->sin_addr, hent->h_addr_list[0],
471 sizeof(iaddr->sin_addr));
479 * Create a target for a host.
482 * name - The name of the host.
485 * A newly allocated target or null if the host could not be resolved.
492 /* Local Variables */
494 ping_target *target = NULL;
496 /* Create the target */
498 if ((target = calloc(1, sizeof(ping_target))) == NULL) {
499 fprintf(stderr, "%s: Out of Memory\n", progname);
500 goto target_init_error;
502 if ((target->name = strdup(name)) == NULL) {
503 fprintf(stderr, "%s: Out of Memory\n", progname);
504 goto target_init_error;
507 /* Lookup the host */
509 if (! lookupHost(target))
510 goto target_init_error;
516 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
517 unsigned long ip = iaddr->sin_addr.s_addr;
518 fprintf (stderr, "%s: added host %d.%d.%d.%d (%s)\n", progname,
519 ip & 255, ip >> 8 & 255, ip >> 16 & 255, ip >> 24 & 255,
525 /* Handle errors here */
534 * Generate a list of ping targets from the entries in a file.
537 * fname - The name of the file. This file is expected to be in the same
538 * format as /etc/hosts.
541 * A list of targets to ping or null if an error occured.
545 readPingHostsFile(char *fname)
547 /* Local Variables */
552 ping_target *list = NULL;
556 /* Make sure we in fact have a file to process */
558 if ((fname == NULL) || (fname[0] == '\0')) {
559 fprintf(stderr, "%s: invalid ping host file name\n", progname);
565 if ((fp = fopen(fname, "r")) == NULL) {
567 sprintf(msg, "%s: unable to open host file %s", progname, fname);
573 fprintf (stderr, "%s: reading file %s\n", progname, fname);
575 /* Read the file line by line */
577 while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
580 * Parse the line skipping those that start with '#'.
581 * The rest of the lines in the file should be in the same
582 * format as a /etc/hosts file. We are only concerned with
583 * the first two field, the IP address and the name
586 while ((*p == ' ') || (*p == '\t'))
591 /* Get the name and address */
594 if ((addr = strtok(buf, " \t\n")) != NULL)
595 name = strtok(NULL, " \t\n");
599 /* Check to see if the addr looks like an addr. If not, assume
600 the addr is a name and there is no addr. This way, we can
601 handle files whose lines have "xx.xx.xx.xx hostname" as their
602 first two tokens, and also files that have a hostname as their
603 first token (like .ssh/known_hosts and .rhosts.)
607 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
613 /*printf ("\"%s\" \"%s\"\n", name, addr);*/
615 /* Create a new target using first the name then the address */
620 if (new == NULL && addr != NULL)
623 /* Add it to the list if we got one */
631 /* Close the file and return the list */
639 delete_duplicate_hosts (ping_target *list)
641 ping_target *head = list;
644 for (rest = head; rest; rest = rest->next)
646 struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address);
647 unsigned long ip1 = i1->sin_addr.s_addr;
649 static ping_target *rest2;
650 for (rest2 = rest; rest2; rest2 = rest2->next)
652 if (rest2 && rest2->next)
654 struct sockaddr_in *i2 = (struct sockaddr_in *)
655 &(rest2->next->address);
656 unsigned long ip2 = i2->sin_addr.s_addr;
661 fprintf (stderr, "%s: deleted duplicate: %s\n",
662 progname, rest2->next->name);
663 rest2->next = rest2->next->next;
676 * Generate a list ping targets consisting of all of the entries on
680 * A list of all of the hosts on this net.
684 subnetHostsList(int base, int subnet_width)
688 /* Local Variables */
690 char hostname[BUFSIZ];
691 char address[BUFSIZ];
692 struct hostent *hent;
696 ping_target *list = NULL;
698 if (subnet_width < 24)
701 "%s: pinging %u hosts is a bad idea; please use a subnet mask of 24 bits\n"
702 " or more (255 hosts max.)\n",
703 progname, (1L << (32 - subnet_width)) - 1);
706 else if (subnet_width > 30)
708 fprintf (stderr, "%s: a subnet of %d bits doesn't make sense:"
709 " try \"subnet/24\" or \"subnet/29\".\n",
710 progname, subnet_width);
716 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
718 /* Get our hostname */
720 if (gethostname(hostname, BUFSIZ)) {
721 fprintf(stderr, "%s: unable to get local hostname\n", progname);
725 /* Get our IP address and convert it to a string */
727 if ((hent = gethostbyname(hostname)) == NULL) {
728 fprintf(stderr, "%s: unable to lookup our IP address\n", progname);
731 strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
733 /* Construct targets for all addresses in this subnet */
736 for (i = 0; i < subnet_width; i++)
737 mask |= (1L << (31-i));
739 /* If no base IP specified, assume localhost. */
741 base = ((((unsigned char) hent->h_addr_list[0][0]) << 24) |
742 (((unsigned char) hent->h_addr_list[0][1]) << 16) |
743 (((unsigned char) hent->h_addr_list[0][2]) << 8) |
744 (((unsigned char) hent->h_addr_list[0][3])));
746 for (i = 255; i >= 0; i--) {
747 int ip = (base & 0xFFFFFF00) | i;
749 if ((ip & mask) != (base & mask)) /* not in the mask range at all */
751 if ((ip & ~mask) == 0) /* broadcast address */
753 if ((ip & ~mask) == ~mask) /* broadcast address */
756 sprintf (address, "%d.%d.%d.%d",
757 (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, (ip)&255);
760 fprintf(stderr, "%s: subnet: %s (%d.%d.%d.%d & %d.%d.%d.%d / %d)\n",
763 (base>>24)&255, (base>>16)&255, (base>>8)&255, base&mask&255,
764 (mask>>24)&255, (mask>>16)&255, (mask>>8)&255, mask&255,
767 p = address + strlen(address) + 1;
770 new = newHost(address);
783 * Initialize the ping sensor.
786 * A newly allocated ping_info structure or null if an error occured.
789 static ping_target *parse_mode (Bool ping_works_p);
795 Bool socket_initted_p = False;
797 /* Local Variables */
799 ping_info *pi = NULL; /* The new ping_info struct */
800 ping_target *pt; /* Used to count the targets */
802 /* Create the ping info structure */
804 if ((pi = (ping_info *) calloc(1, sizeof(ping_info))) == NULL) {
805 fprintf(stderr, "%s: Out of memory\n", progname);
806 goto ping_init_error;
809 /* Create the ICMP socket */
811 if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
812 socket_initted_p = True;
820 pi->pid = getpid() & 0xFFFF;
822 pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
824 /* Generate a list of targets */
826 pi->targets = parse_mode (socket_initted_p);
827 pi->targets = delete_duplicate_hosts (pi->targets);
830 /* Make sure there is something to ping */
832 if (pi->targets == NULL) {
833 goto ping_init_error;
836 /* Count the targets */
849 /* Handle initialization errors here */
862 * pi - The ping information strcuture.
863 * host - The name or IP address of the host to ping (in ascii).
867 sendping(ping_info *pi, ping_target *pt)
870 /* Local Variables */
877 * Note, we will send the character name of the host that we are
878 * pinging in the packet so that we don't have to keep track of the
879 * name or do an address lookup when it comes back.
882 int pcktsiz = sizeof(struct ICMP) + sizeof(struct timeval) +
883 strlen(pt->name) + 1;
885 /* Create the ICMP packet */
887 if ((packet = (u_char *) malloc(pcktsiz)) == (void *) 0)
888 return; /* Out of memory */
889 icmph = (struct ICMP *) packet;
890 ICMP_TYPE(icmph) = ICMP_ECHO;
891 ICMP_CODE(icmph) = 0;
892 ICMP_CHECKSUM(icmph) = 0;
893 ICMP_ID(icmph) = pi->pid;
894 ICMP_SEQ(icmph) = pi->seq++;
895 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
896 (struct timezone *) 0);
897 strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
899 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
903 if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0,
904 &pt->address, sizeof(pt->address))) != pcktsiz) {
907 sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
914 * Catch a signal and do nothing.
917 * sig - The signal that was caught.
927 * Compute the checksum on a ping packet.
930 * packet - A pointer to the packet to compute the checksum for.
931 * size - The size of the packet.
934 * The computed checksum
939 checksum(u_short *packet, int size)
942 /* Local Variables */
944 register int nleft = size;
945 register u_short *w = packet;
946 register int sum = 0;
950 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
951 * sequential 16 bit words to it, and at the end, fold back all the
952 * carry bits from the top 16 bits into the lower 16 bits.
960 /* mop up an odd byte, if necessary */
963 *(u_char *)(&answer) = *(u_char *)w ;
964 *(1 + (u_char *)(&answer)) = 0;
968 /* add back carry outs from top 16 bits to low 16 bits */
970 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
971 sum += (sum >> 16); /* add carry */
972 answer = ~sum; /* truncate to 16 bits */
980 * Look for ping replies.
982 * Retrieve all outstanding ping replies.
985 * si - Information about the sonar.
986 * pi - Ping information.
987 * ttl - The time each bogie is to live on the screen
990 * A Bogie list of all the machines that replied.
994 getping(sonar_info *si, ping_info *pi)
997 /* Local Variables */
999 struct sockaddr from;
1002 u_char packet[1024];
1004 struct timeval *then;
1011 struct sigaction sa;
1012 struct itimerval it;
1014 /* Set up a signal to interupt our wait for a packet */
1016 sigemptyset(&sa.sa_mask);
1018 sa.sa_handler = sigcatcher;
1019 if (sigaction(SIGALRM, &sa, 0) == -1) {
1021 sprintf(msg, "%s: unable to trap SIGALRM", progname);
1026 /* Set up a timer to interupt us if we don't get a packet */
1028 it.it_interval.tv_sec = 0;
1029 it.it_interval.tv_usec = 0;
1030 it.it_value.tv_sec = 0;
1031 it.it_value.tv_usec = pi->timeout;
1033 setitimer(ITIMER_REAL, &it, NULL);
1035 /* Wait for a result packet */
1037 fromlen = sizeof(from);
1038 while (! timer_expired &&
1039 (result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1040 0, &from, &fromlen)) > 0) {
1042 /* Check the packet */
1044 gettimeofday(&now, (struct timezone *) 0);
1045 ip = (struct ip *) packet;
1047 iphdrlen = ip->ip_hl << 2;
1048 /* On DEC OSF1 4.0, the preceeding line needs to be
1049 iphdrlen = (ip->ip_vhl & 0x0F) << 2;
1050 but I don't know how to do this portably. -- jwz.
1053 icmph = (struct ICMP *) &packet[iphdrlen];
1055 /* Was the packet a reply?? */
1057 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1058 /* Ignore anything but ICMP Replies */
1059 continue; /* Nope */
1062 /* Was it for us? */
1064 if (ICMP_ID(icmph) != pi->pid) {
1065 /* Ignore packets not set from us */
1066 continue; /* Nope */
1069 /* Copy the name of the bogie */
1072 strdup((char *) &packet[iphdrlen +
1073 + sizeof(struct ICMP)
1074 + sizeof(struct timeval)])) == NULL) {
1075 fprintf(stderr, "%s: Out of memory\n", progname);
1079 /* If the name is an IP addr, try to resolve it. */
1083 if (4 == sscanf(name, " %d.%d.%d.%d %c",
1084 &iip[0], &iip[1], &iip[2], &iip[3], &c))
1086 unsigned char ip[4];
1088 ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
1089 h = gethostbyaddr ((char *) ip, 4, AF_INET);
1090 if (h && h->h_name && *h->h_name)
1093 name = strdup (h->h_name);
1098 /* Create the new Bogie and add it to the list we are building */
1100 if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
1105 /* Compute the round trip time */
1107 then = (struct timeval *) &packet[iphdrlen +
1108 sizeof(struct ICMP)];
1109 new->distance = delta(then, &now) / 100;
1110 if (new->distance == 0)
1111 new->distance = 2; /* HACK */
1123 * si - Sonar Information.
1124 * pi - Ping Information.
1127 * A list of hosts that replied to pings or null if there were none.
1131 ping(sonar_info *si, void *vpi)
1135 * This tries to distribute the targets evely around the field of the
1139 ping_info *pi = (ping_info *) vpi;
1140 static ping_target *ptr = NULL;
1142 int tick = si->current * -1 + 1;
1143 if ((ptr == NULL) && (tick == 1))
1146 if (pi->numtargets <= 90) {
1147 int xdrant = 90 / pi->numtargets;
1148 if ((tick % xdrant) == 0) {
1149 if (ptr != (ping_target *) 0) {
1155 } else if (pi->numtargets > 90) {
1156 if (ptr != (ping_target *) 0) {
1162 /* Get the results */
1164 return getping(si, pi);
1167 #endif /* HAVE_PING */
1170 * Calculate the difference between two timevals in microseconds.
1173 * then - The older timeval.
1174 * now - The newer timeval.
1177 * The difference between the two in microseconds.
1181 delta(struct timeval *then, struct timeval *now)
1183 return (((now->tv_sec - then->tv_sec) * 1000000) +
1184 (now->tv_usec - then->tv_usec));
1188 * Initialize the simulation mode.
1195 /* Local Variables */
1200 /* Create the simulation info structure */
1202 if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1203 fprintf(stderr, "%s: Out of memory\n", progname);
1209 si->numA = get_integer_resource("teamACount", "TeamACount");
1210 if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1213 fprintf(stderr, "%s: Out of Memory\n", progname);
1216 si->teamAID = get_string_resource("teamAName", "TeamAName");
1217 for (i = 0; i < si->numA; i++) {
1218 if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1221 fprintf(stderr, "%s: Out of Memory\n", progname);
1224 sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1225 si->teamA[i].nexttick = (int) (90.0 * random() / RAND_MAX);
1226 si->teamA[i].nextdist = (int) (100.0 * random() / RAND_MAX);
1227 si->teamA[i].movedonsweep = -1;
1232 si->numB = get_integer_resource("teamBCount", "TeamBCount");
1233 if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1236 fprintf(stderr, "%s: Out of Memory\n", progname);
1239 si->teamBID = get_string_resource("teamBName", "TeamBName");
1240 for (i = 0; i < si->numB; i++) {
1241 if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1244 fprintf(stderr, "%s: Out of Memory\n", progname);
1247 sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1248 si->teamB[i].nexttick = (int) (90.0 * random() / RAND_MAX);
1249 si->teamB[i].nextdist = (int) (100.0 * random() / RAND_MAX);
1250 si->teamB[i].movedonsweep = -1;
1259 * Initialize the Sonar.
1262 * dpy - The X display.
1263 * win - The X window;
1266 * A sonar_info strcuture or null if memory allocation problems occur.
1270 init_sonar(Display *dpy, Window win)
1273 /* Local Variables */
1276 XWindowAttributes xwa;
1280 double s1, s2, v1, v2;
1282 /* Create the Sonar information structure */
1284 if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1285 fprintf(stderr, "%s: Out of memory\n", progname);
1289 /* Initialize the structure for the current environment */
1294 XGetWindowAttributes(dpy, win, &xwa);
1295 si->cmap = xwa.colormap;
1296 si->width = xwa.width;
1297 si->height = xwa.height;
1298 si->centrex = si->width / 2;
1299 si->centrey = si->height / 2;
1300 si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1301 si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1302 si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1303 si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1304 si->radius = si->maxx - si->centrex;
1310 if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1312 ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1313 fprintf(stderr, "%s: can't load an appropriate font\n", progname);
1317 /* Get the delay between animation frames */
1319 si->delay = get_integer_resource ("delay", "Integer");
1321 if (si->delay < 0) si->delay = 0;
1322 si->TTL = get_integer_resource("ttl", "TTL");
1324 /* Create the Graphics Contexts that will be used to draw things */
1327 get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
1328 si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
1329 gcv.font = si->font->fid;
1330 si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
1331 gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
1333 si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1334 gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1336 si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1338 /* Compute pixel values for fading text on the display */
1340 XParseColor(dpy, si->cmap,
1341 get_string_resource("textColor", "TextColor"), &start);
1342 XParseColor(dpy, si->cmap,
1343 get_string_resource("scopeColor", "ScopeColor"), &end);
1345 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1346 rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
1348 si->text_steps = get_integer_resource("textSteps", "TextSteps");
1349 if (si->text_steps < 0 || si->text_steps > 255)
1350 si->text_steps = 10;
1352 si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1353 make_color_ramp (dpy, si->cmap,
1356 si->text_colors, &si->text_steps,
1357 False, True, False);
1359 /* Compute the pixel values for the fading sweep */
1361 XParseColor(dpy, si->cmap,
1362 get_string_resource("sweepColor", "SweepColor"), &start);
1364 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1366 si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
1367 if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
1368 if (si->sweep_degrees > 350) si->sweep_degrees = 350;
1370 si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1371 if (si->sweep_segs < 1 || si->sweep_segs > 255)
1372 si->sweep_segs = 255;
1374 si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1375 make_color_ramp (dpy, si->cmap,
1378 si->sweep_colors, &si->sweep_segs,
1379 False, True, False);
1387 * Update the location of a simulated bogie.
1391 updateLocation(sim_target *t)
1396 xtick = (int) (3.0 * random() / RAND_MAX) - 1;
1397 xdist = (int) (11.0 * random() / RAND_MAX) - 5;
1398 if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1399 t->nexttick += xtick;
1401 t->nexttick -= xtick;
1402 if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1403 t->nextdist += xdist;
1405 t->nextdist -= xdist;
1409 * The simulator. This uses information in the sim_info to simulate a bunch
1410 * of bogies flying around on the screen.
1414 * TODO: It would be cool to have the two teams chase each other around and
1419 simulator(sonar_info *si, void *vinfo)
1422 /* Local Variables */
1428 sim_info *info = (sim_info *) vinfo;
1432 for (i = 0; i < info->numA; i++) {
1433 t = &info->teamA[i];
1434 if ((t->movedonsweep != si->sweepnum) &&
1435 (t->nexttick == (si->current * -1))) {
1436 new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1441 t->movedonsweep = si->sweepnum;
1447 for (i = 0; i < info->numB; i++) {
1448 t = &info->teamB[i];
1449 if ((t->movedonsweep != si->sweepnum) &&
1450 (t->nexttick == (si->current * -1))) {
1451 new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1456 t->movedonsweep = si->sweepnum;
1466 * Compute the X coordinate of the label.
1469 * si - The sonar info block.
1470 * label - The label that will be drawn.
1471 * x - The x coordinate of the bogie.
1474 * The x coordinate of the start of the label.
1478 computeStringX(sonar_info *si, char *label, int x)
1481 int width = XTextWidth(si->font, label, strlen(label));
1482 return x - (width / 2);
1486 * Compute the Y coordinate of the label.
1489 * si - The sonar information.
1490 * y - The y coordinate of the bogie.
1493 * The y coordinate of the start of the label.
1496 /* TODO: Add smarts to keep label in sonar screen */
1499 computeStringY(sonar_info *si, int y)
1502 int fheight = si->font->ascent + si->font->descent;
1503 return y + 5 + fheight;
1507 * Draw a Bogie on the radar screen.
1510 * si - Sonar Information.
1511 * draw - A flag to indicate if the bogie should be drawn or erased.
1512 * name - The name of the bogie.
1513 * degrees - The number of degrees that it should apprear at.
1514 * distance - The distance the object is from the centre.
1515 * ttl - The time this bogie has to live.
1516 * age - The time this bogie has been around.
1520 DrawBogie(sonar_info *si, int draw, char *name, int degrees,
1521 int distance, int ttl, int age)
1524 /* Local Variables */
1528 int ox = si->centrex;
1529 int oy = si->centrey;
1532 /* Compute the coordinates of the object */
1535 distance = (log((double) distance) / 10.0) * si->radius;
1536 x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1537 y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1539 /* Set up the graphics context */
1543 /* Here we attempt to compute the distance into the total life of
1544 * object that we currently are. This distance is used against
1545 * the total lifetime to compute a fraction which is the index of
1546 * the color to draw the bogie.
1549 if (si->current <= degrees)
1550 delta = (si->current - degrees) * -1;
1552 delta = 90 + (degrees - si->current);
1553 delta += (age * 90);
1554 index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1556 XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1561 /* Draw (or erase) the Bogie */
1563 XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1564 XDrawString(si->dpy, si->win, gc,
1565 computeStringX(si, name, x),
1566 computeStringY(si, y), name, strlen(name));
1571 * Draw the sonar grid.
1574 * si - Sonar information block.
1578 drawGrid(sonar_info *si)
1581 /* Local Variables */
1584 int width = si->maxx - si->minx;
1585 int height = si->maxy - si->miny;
1587 /* Draw the circles */
1589 XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10,
1590 width + 20, height + 20, 0, (360 * 64));
1592 XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny,
1593 width, height, 0, (360 * 64));
1595 XDrawArc(si->dpy, si->win, si->grid,
1596 (int) (si->minx + (.166 * width)),
1597 (int) (si->miny + (.166 * height)),
1598 (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1601 XDrawArc(si->dpy, si->win, si->grid,
1602 (int) (si->minx + (.333 * width)),
1603 (int) (si->miny + (.333 * height)),
1604 (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1607 /* Draw the radial lines */
1609 for (i = 0; i < 360; i += 10)
1611 XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1612 (int) (si->centrex +
1613 (si->radius + 10) * (cos((double) i / 57.29578))),
1614 (int) (si->centrey -
1615 (si->radius + 10)*(sin((double) i / 57.29578))));
1617 XDrawLine(si->dpy, si->win, si->grid,
1618 (int) (si->centrex + si->radius *
1619 (cos((double) i / 57.29578))),
1620 (int) (si->centrey - si->radius *
1621 (sin((double) i / 57.29578))),
1622 (int) (si->centrex +
1623 (si->radius + 10) * (cos((double) i / 57.29578))),
1624 (int) (si->centrey -
1625 (si->radius + 10) * (sin((double) i / 57.29578))));
1629 * Update the Sonar scope.
1632 * si - The Sonar information.
1633 * bl - A list of bogies to add to the scope.
1637 Sonar(sonar_info *si, Bogie *bl)
1640 /* Local Variables */
1645 /* Check for expired tagets and remove them from the visable list */
1648 for (bp = si->visable; bp != NULL; bp = bp->next) {
1651 * Remove it from the visable list if it's expired or we have
1652 * a new target with the same name.
1657 if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1658 (findNode(bl, bp->name) != NULL)) {
1659 DrawBogie(si, 0, bp->name, bp->tick,
1660 bp->distance, bp->ttl, bp->age);
1662 si->visable = bp->next;
1664 prev->next = bp->next;
1671 /* Draw the sweep */
1674 int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
1675 int start_deg = si->current * 4 * 64;
1676 if (seg_deg <= 0) seg_deg = 1;
1677 for (i = 0; i < si->sweep_segs; i++) {
1678 XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
1679 XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny,
1680 si->maxx - si->minx, si->maxy - si->miny,
1681 start_deg + (i * seg_deg),
1685 /* Remove the trailing wedge the sonar */
1686 XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny,
1687 si->maxx - si->minx, si->maxy - si->miny,
1688 start_deg + (i * seg_deg),
1692 /* Move the new targets to the visable list */
1694 for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1696 bp->next = si->visable;
1700 /* Draw the visable targets */
1702 for (bp = si->visable; bp != NULL; bp = bp->next) {
1703 if (bp->age < bp->ttl) /* grins */
1704 DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
1707 /* Redraw the grid */
1713 static ping_target *
1714 parse_mode (Bool ping_works_p)
1716 char *source = get_string_resource ("ping", "Ping");
1719 ping_target *hostlist = 0;
1721 if (!source) source = strdup("");
1723 if (!*source || !strcmp (source, "default"))
1726 if (ping_works_p) /* if root or setuid, ping will work. */
1727 source = strdup("subnet/29,/etc/hosts");
1730 source = strdup("simulation");
1734 end = source + strlen(source);
1740 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1744 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1751 fprintf (stderr, "%s: parsing %s\n", progname, token);
1753 if (!strcmp (token, "simulation"))
1759 "%s: this program must be setuid to root for `ping mode' to work.\n"
1760 " Running in `simulation mode' instead.\n",
1765 if ((4 == sscanf (token, "%d.%d.%d/%d %c", &n0,&n1,&n2, &m,&d)) ||
1766 (5 == sscanf (token, "%d.%d.%d.%d/%d %c", &n0,&n1,&n2,&n3,&m,&d)))
1768 /* subnet: A.B.C.D/M
1771 unsigned long ip = (n0 << 24) | (n1 << 16) | (n2 << 8) | n3;
1772 new = subnetHostsList(ip, m);
1774 else if (4 == sscanf (token, "%d.%d.%d.%d %c", &n0, &n1, &n2, &n3, &d))
1778 new = newHost (token);
1780 else if (!strcmp (token, "subnet"))
1782 new = subnetHostsList(0, 24);
1784 else if (1 == sscanf (token, "subnet/%d %c", &m))
1786 new = subnetHostsList(0, m);
1788 else if (*token == '.' || *token == '/' || !stat (token, &st))
1792 new = readPingHostsFile (token);
1796 /* not an existant file - must be a host name
1798 new = newHost (token);
1803 ping_target *nn = new;
1804 while (nn && nn->next)
1806 nn->next = hostlist;
1813 while (token < end &&
1814 (*token == ',' || *token == ' ' ||
1815 *token == '\t' || *token == '\n'))
1825 * Main screen saver hack.
1828 * dpy - The X display.
1829 * win - The X window.
1833 screenhack(Display *dpy, Window win)
1836 /* Local Variables */
1839 struct timeval start, finish;
1843 debug_p = get_boolean_resource ("debug", "Debug");
1846 sensor_info = (void *) init_ping();
1851 if ((sensor_info = (void *) init_sim()) == NULL)
1855 if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
1863 /* Call the sensor and display the results */
1865 gettimeofday(&start, (struct timezone *) 0);
1866 bl = sensor(si, sensor_info);
1869 /* Set up and sleep for the next one */
1871 si->current = (si->current - 1) % 90;
1872 if (si->current == 0)
1875 gettimeofday(&finish, (struct timezone *) 0);
1876 sleeptime = si->delay - delta(&start, &finish);
1877 screenhack_handle_events (dpy);