0df716b6028b992ccb12c836ee9d556403716e27
[xscreensaver] / hacks / sonar.c
1 /* sonar.c --- Simulate a sonar screen.
2  *
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. 
9  *
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.
14  *
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.
18  *
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.
23  *
24  * It should be easy to extend this code to support other sorts of sensors.
25  * Some ideas:
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.
30  *
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 
39  * implied warranty.
40  *
41  * $Revision: 1.24 $
42  *
43  * Version 1.0 April 27, 1998.
44  * - Initial version
45  * - Submitted to RedHat Screensaver Contest
46  * 
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
52  * 
53  * Version 1.2
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
60  *   collection.
61  *
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
69  *   collection.
70  *
71  * Version 1.4 November 18, 1998.
72  * - More ping portability fixes.
73  *
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.
77  */
78
79 /* These are computed by configure now:
80    #define HAVE_ICMP
81    #define HAVE_ICMPHDR
82  */
83
84
85 /* Include Files */
86
87 #include <stdlib.h>
88 #include <stdio.h>
89 #include <math.h>
90 #include <sys/stat.h>
91
92 #include "screenhack.h"
93 #include "colors.h"
94 #include "hsv.h"
95
96 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
97 # include <unistd.h>
98 # include <limits.h>
99 # include <signal.h>
100 # include <fcntl.h>
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>
112 # include <netdb.h>
113 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
114
115
116 /* Defines */
117
118 #undef MY_MIN
119 #define MY_MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
120
121 #ifndef LINE_MAX
122 # define LINE_MAX 2048
123 #endif
124
125 /* Frigging icmp */
126
127 #if defined(HAVE_ICMP)
128 # define HAVE_PING
129 # define ICMP             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)
136 # define HAVE_PING
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
143 #else
144 # undef HAVE_PING
145 #endif
146
147
148 #ifdef HAVE_PING
149 # if defined(__DECC) || defined(_IP_VHL)
150    /* This is how you do it on DEC C, and possibly some BSD systems. */
151 #  define IP_HDRLEN(ip)   ((ip)->ip_vhl & 0x0F)
152 # else
153    /* This is how you do it on everything else. */
154 #  define IP_HDRLEN(ip)   ((ip)->ip_hl)
155 # endif
156 #endif /* HAVE_PING */
157
158
159 /* Forward References */
160
161 #ifdef HAVE_PING
162 static u_short checksum(u_short *, int);
163 #endif
164 static long delta(struct timeval *, struct timeval *);
165
166
167 /* Data Structures */
168
169 /*
170  * The Bogie.
171  *
172  * This represents an object that is visible on the scope.
173  */
174
175 typedef struct Bogie {
176     char *name;                 /* The name of the thing being displayed */
177     int distance;               /* The distance to this thing (0 - 100) */
178     int tick;                   /* The tick that it was found on */
179     int ttl;                    /* The time to live */
180     int age;                    /* How long it's been around */
181     struct Bogie *next;         /* The next one in the list */
182 } Bogie;
183
184 /*
185  * Sonar Information.
186  *
187  * This contains all of the runtime information about the sonar scope.
188  */
189
190 typedef struct {
191     Display *dpy;               /* The X display */
192     Window win;                 /* The window */
193     GC hi,                      /* The leading edge of the sweep */
194         lo,                     /* The trailing part of the sweep */
195         erase,                  /* Used to erase things */
196         grid,                   /* Used to draw the grid */
197         text;                   /* Used to draw text */
198     Colormap cmap;              /* The colormap */
199     XFontStruct *font;          /* The font to use for the labels */
200     int text_steps;             /* How many steps to fade text. */
201     XColor *text_colors;        /* Pixel values used to fade text */
202     int sweep_degrees;          /* How much of the circle the sweep uses */
203     int sweep_segs;             /* How many gradients in the sweep. */
204     XColor *sweep_colors;        /* The sweep pixel values */
205     int width, height;          /* Window dimensions */
206     int minx, miny, maxx, maxy, /* Bounds of the scope */
207         centrex, centrey, radius; /* Parts of the scope circle */
208     Bogie *visible;             /* List of visible objects */
209     int current;                /* Current position of sweep */
210     int sweepnum;               /* The current id of the sweep */
211     int delay;                  /* how long between each frame of the anim */
212
213     int TTL;                    /* The number of ticks that bogies are visible
214                                    on the screen before they fade away. */
215 } sonar_info;
216
217 static Bool debug_p = False;
218
219
220 /* 
221  * Variables to support the differnt Sonar modes.
222  */
223
224 Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
225 void *sensor_info;                      /* Information about the sensor */
226
227 /*
228  * A list of targets to ping.
229  */
230
231 typedef struct ping_target {
232     char *name;                 /* The name of the target */
233 #ifdef HAVE_PING
234     struct sockaddr address;    /* The address of the target */
235 #endif /* HAVE_PING */
236     struct ping_target *next;   /* The next one in the list */
237 } ping_target;
238
239
240 #ifdef HAVE_PING
241 /*
242  * Ping Information.
243  *
244  * This contains the information for the ping sensor.
245  */
246
247 typedef struct {
248     int icmpsock;               /* Socket for sending pings */
249     int pid;                    /* Our process ID */
250     int seq;                    /* Packet sequence number */
251     int timeout;                /* Timeout value for pings */
252     ping_target *targets;       /* List of targets to ping */
253     int numtargets;             /* The number of targets to ping */
254 } ping_info;
255
256 /* Flag to indicate that the timer has expired on us */
257
258 static int timer_expired;
259
260 #endif /* HAVE_PING */
261
262 /*
263  * A list of targets for the simulator
264  */
265
266 typedef struct sim_target {
267     char *name;                 /* The name of the target */
268     int nexttick;               /* The next tick that this will be seen */
269     int nextdist;               /* The distance on that tick */
270     int movedonsweep;           /* The number of the sweep this last moved */
271 } sim_target;
272
273 /*
274  * Simulator Information.
275  *
276  * This contains the information for the simulator mode.
277  */
278
279 typedef struct {
280     sim_target *teamA;          /* The bogies for the A team */
281     int numA;                   /* The number of bogies in team A */
282     char *teamAID;              /* The identifier for bogies in team A */
283     sim_target *teamB;          /* The bogies for the B team */
284     int numB;                   /* The number of bogies in team B */
285     char *teamBID;              /* The identifier for bogies in team B */
286 } sim_info;
287
288 /* Name of the Screensaver hack */
289
290 char *progclass="sonar";
291
292 /* Application Defaults */
293
294 char *defaults [] = {
295     ".background:      #000000",
296     ".sweepColor:      #00FF00",
297     "*delay:           100000",
298     "*scopeColor:      #003300",
299     "*gridColor:       #00AA00",
300     "*textColor:       #FFFF00",
301     "*ttl:             90",
302     "*mode:            default",
303     "*font:            fixed",
304     "*sweepDegrees:    30",
305
306     "*textSteps:       80",     /* npixels */
307     "*sweepSegments:   80",     /* npixels */
308
309     "*pingTimeout:     3000",
310
311     "*teamAName:       F18",
312     "*teamBName:       MIG",
313     "*teamACount:      4",
314     "*teamBCount:      4",
315
316     "*ping:            default",
317     ".debug:           false",
318     0
319 };
320
321 /* Options passed to this program */
322
323 XrmOptionDescRec options [] = {
324     {"-background",   ".background",   XrmoptionSepArg, 0 },
325     {"-sweep-color",  ".sweepColor",   XrmoptionSepArg, 0 },
326     {"-scope-color",  ".scopeColor",   XrmoptionSepArg, 0 },
327     {"-grid-color",   ".gridColor",    XrmoptionSepArg, 0 },
328     {"-text-color",   ".textColor",    XrmoptionSepArg, 0 },
329     {"-ttl",          ".ttl",          XrmoptionSepArg, 0 },
330     {"-font",         ".font",         XrmoptionSepArg, 0 },
331 #ifdef HAVE_PING
332     {"-ping-timeout", ".pingTimeout",  XrmoptionSepArg, 0 },
333 #endif /* HAVE_PING */
334     {"-team-a-name",   ".teamAName",   XrmoptionSepArg, 0 },
335     {"-team-b-name",   ".teamBName",   XrmoptionSepArg, 0 },
336     {"-team-a-count",  ".teamACount",  XrmoptionSepArg, 0 },
337     {"-team-b-count",  ".teamBCount",  XrmoptionSepArg, 0 },
338
339     {"-ping",          ".ping",        XrmoptionSepArg, 0 },
340     {"-debug",         ".debug",       XrmoptionNoArg, "True" },
341     { 0, 0, 0, 0 }
342 };
343
344 /*
345  * Create a new Bogie and set some initial values.
346  *
347  * Args:
348  *    name     - The name of the bogie.
349  *    distance - The distance value.
350  *    tick     - The tick value.
351  *    ttl      - The time to live value.
352  *
353  * Returns:
354  *    The newly allocated bogie or null if a memory problem occured.
355  */
356
357 static Bogie *
358 newBogie(char *name, int distance, int tick, int ttl) 
359 {
360
361     /* Local Variables */
362
363     Bogie *new;
364
365     distance *= 1000;
366     /* Allocate a bogie and initialize it */
367
368     if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
369         fprintf(stderr, "%s: Out of Memory\n", progname);
370         return NULL;
371     }
372     new->name = name;
373     new->distance = distance;
374     new->tick = tick;
375     new->ttl = ttl;
376     new->age = 0;
377     new->next = (Bogie *) 0;
378     return new;
379 }
380
381 /*
382  * Free a Bogie.
383  *
384  * Args:
385  *    b - The bogie to free.
386  */
387
388
389 static void
390 freeBogie(Bogie *b) 
391 {
392     if (b->name != (char *) 0)
393         free(b->name);
394     free(b);
395 }
396
397 /*
398  * Find a bogie by name in a list.
399  *
400  * This does a simple linear search of the list for a given name.
401  *
402  * Args:
403  *    bl   - The Bogie list to search.
404  *    name - The name to look for.
405  *
406  * Returns:
407  *    The requested Bogie or null if it wasn't found.
408  */
409
410 static Bogie *
411 findNode(Bogie *bl, char *name) 
412 {
413
414     /* Local Variables */
415
416     Bogie *p;
417
418     /* Abort if the list is empty or no name is given */
419
420     if ((name == NULL) || (bl == NULL))
421         return NULL;
422
423     /* Search the list for the desired name */
424
425     p = bl;
426     while (p != NULL) {
427         if (strcmp(p->name, name) == 0)
428             return p;
429         p = p->next;
430     }
431
432     /* Not found */
433
434     return NULL;
435 }
436
437 #ifdef HAVE_PING
438
439 /*
440  * Lookup the address for a ping target;
441  *
442  * Args:
443  *    target - The ping_target fill in the address for.
444  *
445  * Returns:
446  *    1 if the host was successfully resolved, 0 otherwise.
447  */
448
449 static int
450 lookupHost(ping_target *target) 
451 {
452   struct hostent *hent;
453   struct sockaddr_in *iaddr;
454
455   int iip[4];
456   char c;
457
458   iaddr = (struct sockaddr_in *) &(target->address);
459   iaddr->sin_family = AF_INET;
460
461   if (4 == sscanf(target->name, "%d.%d.%d.%d%c",
462                   &iip[0], &iip[1], &iip[2], &iip[3], &c))
463     {
464       /* It's an IP address.
465        */
466       unsigned char ip[4];
467
468       ip[0] = iip[0];
469       ip[1] = iip[1];
470       ip[2] = iip[2];
471       ip[3] = iip[3];
472
473       if (ip[3] == 0)
474         {
475           if (debug_p > 1)
476             fprintf (stderr, "%s:   ignoring bogus IP %s\n",
477                      progname, target->name);
478           return 0;
479         }
480
481       iaddr->sin_addr.s_addr = ((ip[3] << 24) |
482                                 (ip[2] << 16) |
483                                 (ip[1] <<  8) |
484                                 (ip[0]));
485       hent = gethostbyaddr (ip, 4, AF_INET);
486
487       if (debug_p > 1)
488         fprintf (stderr, "%s:   %s => %s\n",
489                  progname, target->name,
490                  ((hent && hent->h_name && *hent->h_name)
491                   ? hent->h_name : "<unknown>"));
492
493       if (hent && hent->h_name && *hent->h_name)
494         target->name = strdup (hent->h_name);
495     }
496   else
497     {
498       /* It's a host name.
499        */
500       hent = gethostbyname (target->name);
501       if (!hent)
502         {
503           fprintf (stderr, "%s: could not resolve host:  %s\n",
504                    progname, target->name);
505           return 0;
506         }
507
508       memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
509               sizeof(iaddr->sin_addr));
510
511       if (debug_p > 1)
512         fprintf (stderr, "%s:   %s => %d.%d.%d.%d\n",
513                  progname, target->name,
514                  iaddr->sin_addr.s_addr       & 255,
515                  iaddr->sin_addr.s_addr >>  8 & 255,
516                  iaddr->sin_addr.s_addr >> 16 & 255,
517                  iaddr->sin_addr.s_addr >> 24 & 255);
518     }
519   return 1;
520 }
521
522
523 static void
524 print_host (FILE *out, unsigned long ip, const char *name)
525 {
526   char ips[50];
527   sprintf (ips, "%lu.%lu.%lu.%lu",
528            (ip)       & 255,
529            (ip >>  8) & 255,
530            (ip >> 16) & 255,
531            (ip >> 24) & 255);
532   if (!name || !*name) name = "<unknown>";
533   fprintf (out, "%-16s %s\n", ips, name);
534 }
535
536
537 /*
538  * Create a target for a host.
539  *
540  * Args:
541  *    name - The name of the host.
542  *
543  * Returns:
544  *    A newly allocated target or null if the host could not be resolved.
545  */
546
547 static ping_target *
548 newHost(char *name) 
549 {
550
551     /* Local Variables */
552
553     ping_target *target = NULL;
554
555     /* Create the target */
556
557     if ((target = calloc(1, sizeof(ping_target))) == NULL) {
558         fprintf(stderr, "%s: Out of Memory\n", progname);
559         goto target_init_error;
560     }
561     if ((target->name = strdup(name)) == NULL) {
562         fprintf(stderr, "%s: Out of Memory\n", progname);
563         goto target_init_error;
564     }
565
566     /* Lookup the host */
567
568     if (! lookupHost(target))
569         goto target_init_error;
570
571     /* Don't ever use loopback (127.0.0) hosts */
572     {
573       struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
574       unsigned long ip = iaddr->sin_addr.s_addr;
575       if ((ip         & 255) == 127 &&
576           ((ip >>  8) & 255) == 0 &&
577           ((ip >> 16) & 255) == 0)
578         {
579           if (debug_p)
580             fprintf (stderr, "%s:   ignoring loopback host %s\n",
581                      progname, target->name);
582           goto target_init_error;
583         }
584     }
585
586     /* Done */
587
588     if (debug_p)
589       {
590         struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
591         unsigned long ip = iaddr->sin_addr.s_addr;
592         fprintf (stderr, "%s:   added ", progname);
593         print_host (stderr, ip, target->name);
594       }
595
596     return target;
597
598     /* Handle errors here */
599
600 target_init_error:
601     if (target != NULL)
602         free(target);
603     return NULL;
604 }
605
606 /*
607  * Generate a list of ping targets from the entries in a file.
608  *
609  * Args:
610  *    fname - The name of the file. This file is expected to be in the same
611  *            format as /etc/hosts.
612  *
613  * Returns:
614  *    A list of targets to ping or null if an error occured.
615  */
616
617 static ping_target *
618 readPingHostsFile(char *fname) 
619 {
620     /* Local Variables */
621
622     FILE *fp;
623     char buf[LINE_MAX];
624     char *p;
625     ping_target *list = NULL;
626     char *addr, *name;
627     ping_target *new;
628
629     /* Make sure we in fact have a file to process */
630
631     if ((fname == NULL) || (fname[0] == '\0')) {
632         fprintf(stderr, "%s: invalid ping host file name\n", progname);
633         return NULL;
634     }
635
636     /* Open the file */
637
638     if ((fp = fopen(fname, "r")) == NULL) {
639         char msg[1024];
640         sprintf(msg, "%s: unable to open host file %s", progname, fname);
641         perror(msg);
642         return NULL;
643     }
644
645     if (debug_p)
646       fprintf (stderr, "%s:  reading file %s\n", progname, fname);
647
648     /* Read the file line by line */
649
650     while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
651
652         /*
653          * Parse the line skipping those that start with '#'.
654          * The rest of the lines in the file should be in the same
655          * format as a /etc/hosts file. We are only concerned with
656          * the first two field, the IP address and the name
657          */
658
659         while ((*p == ' ') || (*p == '\t'))
660             p++;
661         if (*p == '#')
662             continue;
663
664         /* Get the name and address */
665
666         name = addr = NULL;
667         if ((addr = strtok(buf, " ,;\t\n")) != NULL)
668             name = strtok(NULL, " ,;\t\n");
669         else
670             continue;
671
672         /* Check to see if the addr looks like an addr.  If not, assume
673            the addr is a name and there is no addr.  This way, we can
674            handle files whose lines have "xx.xx.xx.xx hostname" as their
675            first two tokens, and also files that have a hostname as their
676            first token (like .ssh/known_hosts and .rhosts.)
677          */
678         {
679           int i; char c;
680           if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
681             {
682               name = addr;
683               addr = NULL;
684             }
685         }
686
687         /* If the name is all digits, it's not a name. */
688         if (name)
689           {
690             const char *s;
691             for (s = name; *s; s++)
692               if (*s < '0' || *s > '9')
693                 break;
694             if (! *s)
695               {
696                 if (debug_p > 1)
697                   fprintf (stderr, "%s:  skipping bogus name \"%s\" (%s)\n",
698                            progname, name, addr);
699                 name = NULL;
700               }
701           }
702
703         /* Create a new target using first the name then the address */
704
705         new = NULL;
706         if (name != NULL)
707             new = newHost(name);
708         if (new == NULL && addr != NULL)
709             new = newHost(addr);
710
711         /* Add it to the list if we got one */
712
713         if (new != NULL) {
714             new->next = list;
715             list = new;
716         }
717     }
718
719     /* Close the file and return the list */
720
721     fclose(fp);
722     return list;
723 }
724
725
726 static ping_target *
727 delete_duplicate_hosts (ping_target *list)
728 {
729   ping_target *head = list;
730   ping_target *rest;
731
732   for (rest = head; rest; rest = rest->next)
733     {
734       struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address);
735       unsigned long ip1 = i1->sin_addr.s_addr;
736
737       static ping_target *rest2;
738       for (rest2 = rest; rest2; rest2 = rest2->next)
739         {
740           if (rest2 && rest2->next)
741             {
742               struct sockaddr_in *i2 = (struct sockaddr_in *)
743                 &(rest2->next->address);
744               unsigned long ip2 = i2->sin_addr.s_addr;
745
746               if (ip1 == ip2)
747                 {
748                   if (debug_p)
749                     {
750                       fprintf (stderr, "%s: deleted duplicate: ", progname);
751                       print_host (stderr, ip2, rest2->next->name);
752                     }
753                   rest2->next = rest2->next->next;
754                 }
755             }
756         }
757     }
758
759   return head;
760 }
761
762
763
764
765 /*
766  * Generate a list ping targets consisting of all of the entries on
767  * the same subnet.
768  *
769  * Returns:
770  *    A list of all of the hosts on this net.
771  */
772
773 static ping_target *
774 subnetHostsList(int base, int subnet_width) 
775 {
776     unsigned long mask;
777
778     /* Local Variables */
779
780     char hostname[BUFSIZ];
781     char address[BUFSIZ];
782     struct hostent *hent;
783     char *p;
784     int i;
785     ping_target *new;
786     ping_target *list = NULL;
787
788     if (subnet_width < 24)
789       {
790         fprintf (stderr,
791    "%s: pinging %lu hosts is a bad idea; please use a subnet mask of 24 bits\n"
792                  "       or more (255 hosts max.)\n",
793                  progname, (unsigned long) (1L << (32 - subnet_width)) - 1);
794         exit (1);
795       }
796     else if (subnet_width > 30)
797       {
798         fprintf (stderr, "%s: a subnet of %d bits doesn't make sense:"
799                  " try \"subnet/24\" or \"subnet/29\".\n",
800                  progname, subnet_width);
801         exit (1);
802       }
803
804
805     if (debug_p)
806       fprintf (stderr, "%s:   adding %d-bit subnet\n", progname, subnet_width);
807
808     /* Get our hostname */
809
810     if (gethostname(hostname, BUFSIZ)) {
811         fprintf(stderr, "%s: unable to get local hostname\n", progname);
812         return NULL;
813     }
814
815     /* Get our IP address and convert it to a string */
816
817     if ((hent = gethostbyname(hostname)) == NULL) {
818         fprintf(stderr, "%s: unable to lookup our IP address\n", progname);
819         return NULL;
820     }
821     strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
822
823     /* Construct targets for all addresses in this subnet */
824
825     mask = 0;
826     for (i = 0; i < subnet_width; i++)
827       mask |= (1L << (31-i));
828
829     /* If no base IP specified, assume localhost. */
830     if (base == 0)
831       base = ((((unsigned char) hent->h_addr_list[0][0]) << 24) |
832               (((unsigned char) hent->h_addr_list[0][1]) << 16) |
833               (((unsigned char) hent->h_addr_list[0][2]) <<  8) |
834               (((unsigned char) hent->h_addr_list[0][3])));
835
836     if (base == ((127 << 24) | 1))
837       {
838         fprintf (stderr,
839                  "%s: unable to determine local subnet address: \"%s\"\n"
840                  "       resolves to loopback address %d.%d.%d.%d.\n",
841                  progname, hostname,
842                  (base >> 24) & 255, (base >> 16) & 255,
843                  (base >>  8) & 255, (base      ) & 255);
844         return NULL;
845       }
846
847     for (i = 255; i >= 0; i--) {
848         int ip = (base & 0xFFFFFF00) | i;
849       
850         if ((ip & mask) != (base & mask))   /* not in the mask range at all */
851           continue;
852         if ((ip & ~mask) == 0)              /* broadcast address */
853           continue;
854         if ((ip & ~mask) == ~mask)          /* broadcast address */
855           continue;
856
857         sprintf (address, "%d.%d.%d.%d", 
858                  (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, (ip)&255);
859
860         if (debug_p > 1)
861           fprintf(stderr, "%s:  subnet: %s (%d.%d.%d.%d & %d.%d.%d.%d / %d)\n",
862                   progname,
863                   address,
864                   (int) (base>>24)&255,
865                   (int) (base>>16)&255,
866                   (int) (base>> 8)&255,
867                   (int) (base&mask&255),
868                   (int) (mask>>24)&255,
869                   (int) (mask>>16)&255,
870                   (int) (mask>> 8)&255,
871                   (int) (mask&255),
872                   (int) subnet_width);
873
874         p = address + strlen(address) + 1;
875         sprintf(p, "%d", i);
876
877         new = newHost(address);
878         if (new != NULL) {
879             new->next = list;
880             list = new;
881         }
882     }
883
884     /* Done */
885
886     return list;
887 }
888
889 /*
890  * Initialize the ping sensor.
891  *
892  * Returns:
893  *    A newly allocated ping_info structure or null if an error occured.
894  */
895
896 static ping_target *parse_mode (Bool ping_works_p);
897
898 static ping_info *
899 init_ping(void) 
900 {
901
902   Bool socket_initted_p = False;
903
904     /* Local Variables */
905
906     ping_info *pi = NULL;               /* The new ping_info struct */
907     ping_target *pt;                    /* Used to count the targets */
908
909     /* Create the ping info structure */
910
911     if ((pi = (ping_info *) calloc(1, sizeof(ping_info))) == NULL) {
912         fprintf(stderr, "%s: Out of memory\n", progname);
913         goto ping_init_error;
914     }
915
916     /* Create the ICMP socket */
917
918     if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
919       socket_initted_p = True;
920     }
921
922     /* Disavow privs */
923
924     setuid(getuid());
925
926
927     pi->pid = getpid() & 0xFFFF;
928     pi->seq = 0;
929     pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
930
931     /* Generate a list of targets */
932
933     pi->targets = parse_mode (socket_initted_p);
934     pi->targets = delete_duplicate_hosts (pi->targets);
935
936
937     if (debug_p)
938       {
939         ping_target *t;
940         fprintf (stderr, "%s: Target list:\n", progname);
941         for (t = pi->targets; t; t = t->next)
942           {
943             struct sockaddr_in *iaddr = (struct sockaddr_in *) &(t->address);
944             unsigned long ip = iaddr->sin_addr.s_addr;
945             fprintf (stderr, "%s:   ", progname);
946             print_host (stderr, ip, t->name);
947           }
948       }
949
950     /* Make sure there is something to ping */
951
952     if (pi->targets == NULL) {
953       goto ping_init_error;
954     }
955
956     /* Count the targets */
957
958     pt = pi->targets;
959     pi->numtargets = 0;
960     while (pt != NULL) {
961         pi->numtargets++;
962         pt = pt->next;
963     }
964
965     /* Done */
966
967     return pi;
968
969     /* Handle initialization errors here */
970
971 ping_init_error:
972     if (pi != NULL)
973         free(pi);
974     return NULL;
975 }
976
977
978 /*
979  * Ping a host.
980  *
981  * Args:
982  *    pi   - The ping information strcuture.
983  *    host - The name or IP address of the host to ping (in ascii).
984  */
985
986 static void
987 sendping(ping_info *pi, ping_target *pt) 
988 {
989
990     /* Local Variables */
991
992     u_char *packet;
993     struct ICMP *icmph;
994     int result;
995
996     /*
997      * Note, we will send the character name of the host that we are
998      * pinging in the packet so that we don't have to keep track of the
999      * name or do an address lookup when it comes back.
1000      */
1001
1002     int pcktsiz = sizeof(struct ICMP) + sizeof(struct timeval) +
1003         strlen(pt->name) + 1;
1004
1005     /* Create the ICMP packet */
1006
1007     if ((packet = (u_char *) malloc(pcktsiz)) == (void *) 0)
1008         return;  /* Out of memory */
1009     icmph = (struct ICMP *) packet;
1010     ICMP_TYPE(icmph) = ICMP_ECHO;
1011     ICMP_CODE(icmph) = 0;
1012     ICMP_CHECKSUM(icmph) = 0;
1013     ICMP_ID(icmph) = pi->pid;
1014     ICMP_SEQ(icmph) = pi->seq++;
1015     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
1016                  (struct timezone *) 0);
1017     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
1018            pt->name);
1019     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
1020
1021     /* Send it */
1022
1023     if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0, 
1024                          &pt->address, sizeof(pt->address))) !=  pcktsiz) {
1025 #if 0
1026         char errbuf[BUFSIZ];
1027         sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
1028         perror(errbuf);
1029 #endif
1030     }
1031 }
1032
1033 /*
1034  * Catch a signal and do nothing.
1035  *
1036  * Args:
1037  *    sig - The signal that was caught.
1038  */
1039
1040 static void
1041 sigcatcher(int sig)
1042 {
1043     timer_expired = 1;
1044 }
1045
1046 /*
1047  * Compute the checksum on a ping packet.
1048  *
1049  * Args:
1050  *    packet - A pointer to the packet to compute the checksum for.
1051  *    size   - The size of the packet.
1052  *
1053  * Returns:
1054  *    The computed checksum
1055  *    
1056  */
1057
1058 static u_short
1059 checksum(u_short *packet, int size) 
1060 {
1061
1062     /* Local Variables */
1063
1064     register int nleft = size;
1065     register u_short *w = packet;
1066     register int sum = 0;
1067     u_short answer = 0;
1068
1069     /*
1070      * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1071      * sequential 16 bit words to it, and at the end, fold back all the
1072      * carry bits from the top 16 bits into the lower 16 bits.
1073      */
1074
1075     while (nleft > 1)  {
1076         sum += *w++;
1077         nleft -= 2;
1078     }
1079
1080     /* mop up an odd byte, if necessary */
1081
1082     if (nleft == 1) {
1083         *(u_char *)(&answer) = *(u_char *)w ;
1084         *(1 + (u_char *)(&answer)) = 0;
1085         sum += answer;
1086     }
1087
1088     /* add back carry outs from top 16 bits to low 16 bits */
1089
1090     sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
1091     sum += (sum >> 16);                     /* add carry */
1092     answer = ~sum;                          /* truncate to 16 bits */
1093
1094     /* Done */
1095
1096     return(answer);
1097 }
1098
1099 /*
1100  * Look for ping replies.
1101  *
1102  * Retrieve all outstanding ping replies.
1103  *
1104  * Args:
1105  *    si - Information about the sonar.
1106  *    pi - Ping information.
1107  *    ttl - The time each bogie is to live on the screen
1108  *
1109  * Returns:
1110  *    A Bogie list of all the machines that replied.
1111  */
1112
1113 static Bogie *
1114 getping(sonar_info *si, ping_info *pi) 
1115 {
1116
1117     /* Local Variables */
1118
1119     struct sockaddr from;
1120     int fromlen;
1121     int result;
1122     u_char packet[1024];
1123     struct timeval now;
1124     struct timeval *then;
1125     struct ip *ip;
1126     int iphdrlen;
1127     struct ICMP *icmph;
1128     Bogie *bl = NULL;
1129     Bogie *new;
1130     char *name;
1131     struct sigaction sa;
1132     struct itimerval it;
1133     fd_set rfds;
1134     struct timeval tv;
1135
1136     /* Set up a signal to interupt our wait for a packet */
1137
1138     sigemptyset(&sa.sa_mask);
1139     sa.sa_flags = 0;
1140     sa.sa_handler = sigcatcher;
1141     if (sigaction(SIGALRM, &sa, 0) == -1) {
1142         char msg[1024];
1143         sprintf(msg, "%s: unable to trap SIGALRM", progname);
1144         perror(msg);
1145         exit(1);
1146     }
1147
1148     /* Set up a timer to interupt us if we don't get a packet */
1149
1150     it.it_interval.tv_sec = 0;
1151     it.it_interval.tv_usec = 0;
1152     it.it_value.tv_sec = 0;
1153     it.it_value.tv_usec = pi->timeout;
1154     timer_expired = 0;
1155     setitimer(ITIMER_REAL, &it, NULL);
1156
1157     /* Wait for a result packet */
1158
1159     fromlen = sizeof(from);
1160     while (! timer_expired) {
1161       tv.tv_usec=pi->timeout;
1162       tv.tv_sec=0;
1163 #if 0
1164       /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
1165       FD_ZERO(&rfds);
1166 #else
1167       memset (&rfds, 0, sizeof(rfds));
1168 #endif
1169       FD_SET(pi->icmpsock,&rfds);
1170       /* only wait a little while, in case we raced with the timer expiration.
1171          From Valentijn Sessink <valentyn@openoffice.nl> */
1172       if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) {
1173         result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1174                       0, &from, &fromlen);
1175
1176         /* Check the packet */
1177
1178         gettimeofday(&now, (struct timezone *) 0);
1179         ip = (struct ip *) packet;
1180         iphdrlen = IP_HDRLEN(ip) << 2;
1181         icmph = (struct ICMP *) &packet[iphdrlen];
1182
1183         /* Was the packet a reply?? */
1184
1185         if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1186             /* Ignore anything but ICMP Replies */
1187             continue; /* Nope */
1188         }
1189
1190         /* Was it for us? */
1191
1192         if (ICMP_ID(icmph) != pi->pid) {
1193             /* Ignore packets not set from us */
1194             continue; /* Nope */
1195         }
1196
1197         /* Copy the name of the bogie */
1198
1199         if ((name =
1200              strdup((char *) &packet[iphdrlen + 
1201                                     + sizeof(struct ICMP)
1202                                     + sizeof(struct timeval)])) == NULL) {
1203             fprintf(stderr, "%s: Out of memory\n", progname);
1204             return bl;
1205         }
1206
1207         /* If the name is an IP addr, try to resolve it. */
1208         {
1209           int iip[4];
1210           char c;
1211           if (4 == sscanf(name, " %d.%d.%d.%d %c",
1212                           &iip[0], &iip[1], &iip[2], &iip[3], &c))
1213             {
1214               unsigned char ip[4];
1215               struct hostent *h;
1216               ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
1217               h = gethostbyaddr ((char *) ip, 4, AF_INET);
1218               if (h && h->h_name && *h->h_name)
1219                 {
1220                   free (name);
1221                   name = strdup (h->h_name);
1222                 }
1223             }
1224         }
1225
1226         /* Create the new Bogie and add it to the list we are building */
1227
1228         if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
1229             return bl;
1230         new->next = bl;
1231         bl = new;
1232
1233         /* Compute the round trip time */
1234
1235         then =  (struct timeval *) &packet[iphdrlen +
1236                                           sizeof(struct ICMP)];
1237         new->distance = delta(then, &now) / 100;
1238         if (new->distance == 0)
1239                 new->distance = 2; /* HACK */
1240       }
1241     }
1242
1243     /* Done */
1244
1245     return bl;
1246 }
1247
1248 /*
1249  * Ping hosts.
1250  *
1251  * Args:
1252  *    si - Sonar Information.
1253  *    pi - Ping Information.
1254  *
1255  * Returns:
1256  *    A list of hosts that replied to pings or null if there were none.
1257  */
1258
1259 static Bogie *
1260 ping(sonar_info *si, void *vpi) 
1261 {
1262
1263     /*
1264      * This tries to distribute the targets evely around the field of the
1265      * sonar.
1266      */
1267
1268     ping_info *pi = (ping_info *) vpi;
1269     static ping_target *ptr = NULL;
1270
1271     int tick = si->current * -1 + 1;
1272     if ((ptr == NULL) && (tick == 1))
1273         ptr = pi->targets;
1274
1275     if (pi->numtargets <= 90) {
1276         int xdrant = 90 / pi->numtargets;
1277         if ((tick % xdrant) == 0) {
1278             if (ptr != (ping_target *) 0) {
1279                 sendping(pi, ptr);
1280                 ptr = ptr->next;
1281             }
1282         }
1283
1284     } else if (pi->numtargets > 90) {
1285         if (ptr != (ping_target *) 0) {
1286             sendping(pi, ptr);
1287             ptr = ptr->next;
1288         }
1289     }
1290
1291     /* Get the results */
1292
1293     return getping(si, pi);
1294 }
1295
1296 #endif /* HAVE_PING */
1297
1298 /*
1299  * Calculate the difference between two timevals in microseconds.
1300  *
1301  * Args:
1302  *    then - The older timeval.
1303  *    now  - The newer timeval.
1304  *
1305  * Returns:
1306  *   The difference between the two in microseconds.
1307  */
1308
1309 static long
1310 delta(struct timeval *then, struct timeval *now) 
1311 {
1312     return (((now->tv_sec - then->tv_sec) * 1000000) + 
1313                (now->tv_usec - then->tv_usec));  
1314 }
1315
1316 /*
1317  * Initialize the simulation mode.
1318  */
1319
1320 static sim_info *
1321 init_sim(void) 
1322 {
1323
1324     /* Local Variables */
1325
1326     sim_info *si;
1327     int i;
1328
1329     /* Create the simulation info structure */
1330
1331     if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1332         fprintf(stderr, "%s: Out of memory\n", progname);
1333         return NULL;
1334     }
1335
1336     /* Team A */
1337
1338     si->numA = get_integer_resource("teamACount", "TeamACount");
1339     if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1340         == NULL) {
1341         free(si);
1342         fprintf(stderr, "%s: Out of Memory\n", progname);
1343         return NULL;
1344     }
1345     si->teamAID = get_string_resource("teamAName", "TeamAName");
1346     for (i = 0; i < si->numA; i++) {
1347         if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1348             == NULL) {
1349             free(si);
1350             fprintf(stderr, "%s: Out of Memory\n", progname);
1351             return NULL;
1352         }
1353         sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1354         si->teamA[i].nexttick = random() % 90;
1355         si->teamA[i].nextdist = random() % 100;
1356         si->teamA[i].movedonsweep = -1;
1357     }
1358
1359     /* Team B */
1360
1361     si->numB = get_integer_resource("teamBCount", "TeamBCount");
1362     if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1363         == NULL) {
1364         free(si);
1365         fprintf(stderr, "%s: Out of Memory\n", progname);
1366         return NULL;
1367     }
1368     si->teamBID = get_string_resource("teamBName", "TeamBName");
1369     for (i = 0; i < si->numB; i++) {
1370         if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1371             == NULL) {
1372             free(si);
1373             fprintf(stderr, "%s: Out of Memory\n", progname);
1374             return NULL;
1375         }
1376         sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1377         si->teamB[i].nexttick = random() % 90;
1378         si->teamB[i].nextdist = random() % 100;
1379         si->teamB[i].movedonsweep = -1;
1380     }
1381
1382     /* Done */
1383
1384     return si;
1385 }
1386
1387 /*
1388  * Creates and returns a drawing mask for the scope:
1389  * mask out anything outside of the disc.
1390  */
1391 static Pixmap
1392 scope_mask (Display *dpy, Window win, sonar_info *si)
1393 {
1394   XGCValues gcv;
1395   Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
1396   GC gc = XCreateGC (dpy, mask, 0, &gcv);
1397   XSetFunction (dpy, gc, GXclear);
1398   XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
1399   XSetFunction (dpy, gc, GXset);
1400   XFillArc(dpy, mask, gc, si->minx, si->miny, 
1401            si->maxx - si->minx, si->maxy - si->miny,
1402            0, 360 * 64);
1403   return mask;
1404 }
1405
1406
1407 /*
1408  * Initialize the Sonar.
1409  *
1410  * Args:
1411  *    dpy - The X display.
1412  *    win - The X window;
1413  *
1414  * Returns:
1415  *   A sonar_info strcuture or null if memory allocation problems occur.
1416  */
1417
1418 static sonar_info *
1419 init_sonar(Display *dpy, Window win) 
1420 {
1421
1422     /* Local Variables */
1423
1424     XGCValues gcv;
1425     XWindowAttributes xwa;
1426     sonar_info *si;
1427     XColor start, end;
1428     int h1, h2;
1429     double s1, s2, v1, v2;
1430
1431     /* Create the Sonar information structure */
1432
1433     if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1434         fprintf(stderr, "%s: Out of memory\n", progname);
1435         return NULL;
1436     }
1437
1438     /* Initialize the structure for the current environment */
1439
1440     si->dpy = dpy;
1441     si->win = win;
1442     si->visible = NULL;
1443     XGetWindowAttributes(dpy, win, &xwa);
1444     si->cmap = xwa.colormap;
1445     si->width = xwa.width;
1446     si->height = xwa.height;
1447     si->centrex = si->width / 2;
1448     si->centrey = si->height / 2;
1449     si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1450     si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1451     si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1452     si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1453     si->radius = si->maxx - si->centrex;
1454     si->current = 0;
1455     si->sweepnum = 0;
1456
1457     /* Get the font */
1458
1459     if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1460          == NULL) &&
1461         ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1462         fprintf(stderr, "%s: can't load an appropriate font\n", progname);
1463         return NULL;
1464     }
1465
1466     /* Get the delay between animation frames */
1467
1468     si->delay = get_integer_resource ("delay", "Integer");
1469
1470     if (si->delay < 0) si->delay = 0;
1471     si->TTL = get_integer_resource("ttl", "TTL");
1472
1473     /* Create the Graphics Contexts that will be used to draw things */
1474
1475     gcv.foreground = 
1476         get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
1477     si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
1478     gcv.font = si->font->fid;
1479     si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
1480     gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
1481                                         dpy, si->cmap);
1482     si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1483     gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1484                                         dpy, si->cmap);
1485     si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1486
1487     /* Install the clip mask... */
1488     {
1489       Pixmap mask = scope_mask (dpy, win, si);
1490       XSetClipMask(dpy, si->text, mask);
1491       XSetClipMask(dpy, si->erase, mask);
1492       XFreePixmap (dpy, mask); /* it's been copied into the GCs */
1493     }
1494
1495     /* Compute pixel values for fading text on the display */
1496
1497     XParseColor(dpy, si->cmap, 
1498                 get_string_resource("textColor", "TextColor"), &start);
1499     XParseColor(dpy, si->cmap, 
1500                 get_string_resource("scopeColor", "ScopeColor"), &end);
1501
1502     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1503     rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
1504
1505     si->text_steps = get_integer_resource("textSteps", "TextSteps");
1506     if (si->text_steps < 0 || si->text_steps > 255)
1507       si->text_steps = 10;
1508
1509     si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1510     make_color_ramp (dpy, si->cmap,
1511                      h1, s1, v1,
1512                      h2, s2, v2,
1513                      si->text_colors, &si->text_steps,
1514                      False, True, False);
1515
1516     /* Compute the pixel values for the fading sweep */
1517
1518     XParseColor(dpy, si->cmap, 
1519                 get_string_resource("sweepColor", "SweepColor"), &start);
1520
1521     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1522
1523     si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
1524     if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
1525     if (si->sweep_degrees > 350) si->sweep_degrees = 350;
1526
1527     si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1528     if (si->sweep_segs < 1 || si->sweep_segs > 255)
1529       si->sweep_segs = 255;
1530
1531     si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1532     make_color_ramp (dpy, si->cmap,
1533                      h1, s1, v1,
1534                      h2, s2, v2,
1535                      si->sweep_colors, &si->sweep_segs,
1536                      False, True, False);
1537
1538     if (si->sweep_segs <= 0)
1539       si->sweep_segs = 1;
1540
1541     /* Done */
1542
1543     return si;
1544 }
1545
1546 /*
1547  * Update the location of a simulated bogie.
1548  */
1549
1550 static void
1551 updateLocation(sim_target *t) 
1552 {
1553
1554     int xdist, xtick;
1555
1556     xtick = (int) (random() %  3) - 1;
1557     xdist = (int) (random() % 11) - 5;
1558     if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1559         t->nexttick += xtick;
1560     else
1561         t->nexttick -= xtick;
1562     if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1563         t->nextdist += xdist;
1564     else
1565         t->nextdist -= xdist;
1566 }
1567
1568 /*
1569  * The simulator. This uses information in the sim_info to simulate a bunch
1570  * of bogies flying around on the screen.
1571  */
1572
1573 /*
1574  * TODO: It would be cool to have the two teams chase each other around and
1575  *       shoot it out.
1576  */
1577
1578 static Bogie *
1579 simulator(sonar_info *si, void *vinfo) 
1580 {
1581
1582     /* Local Variables */
1583
1584     int i;
1585     Bogie *list = NULL;
1586     Bogie *new;
1587     sim_target *t;
1588     sim_info *info = (sim_info *) vinfo;
1589
1590     /* Check team A */
1591
1592     for (i = 0; i < info->numA; i++) {
1593         t = &info->teamA[i];
1594         if ((t->movedonsweep != si->sweepnum) &&
1595             (t->nexttick == (si->current * -1))) {
1596             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1597             if (list != NULL)
1598                 new->next = list;
1599             list = new;
1600             updateLocation(t);
1601             t->movedonsweep = si->sweepnum;
1602         }
1603     }
1604
1605     /* Team B */
1606
1607     for (i = 0; i < info->numB; i++) {
1608         t = &info->teamB[i];
1609         if ((t->movedonsweep != si->sweepnum) &&
1610             (t->nexttick == (si->current * -1))) {
1611             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1612             if (list != NULL)
1613                 new->next = list;
1614             list = new;
1615             updateLocation(t);
1616             t->movedonsweep = si->sweepnum;
1617         }
1618     }
1619
1620     /* Done */
1621
1622     return list;
1623 }
1624
1625 /*
1626  * Compute the X coordinate of the label.
1627  *
1628  * Args:
1629  *    si - The sonar info block.
1630  *    label - The label that will be drawn.
1631  *    x - The x coordinate of the bogie.
1632  *
1633  * Returns:
1634  *    The x coordinate of the start of the label.
1635  */
1636
1637 static int
1638 computeStringX(sonar_info *si, char *label, int x) 
1639 {
1640
1641     int width = XTextWidth(si->font, label, strlen(label));
1642     return x - (width / 2);
1643 }
1644
1645 /*
1646  * Compute the Y coordinate of the label.
1647  *
1648  * Args:
1649  *    si - The sonar information.
1650  *    y - The y coordinate of the bogie.
1651  *
1652  * Returns:
1653  *    The y coordinate of the start of the label.
1654  */
1655
1656 /* TODO: Add smarts to keep label in sonar screen */
1657
1658 static int
1659 computeStringY(sonar_info *si, int y) 
1660 {
1661
1662     int fheight = si->font->ascent + si->font->descent;
1663     return y + 5 + fheight;
1664 }
1665
1666 /*
1667  * Draw a Bogie on the radar screen.
1668  *
1669  * Args:
1670  *    si       - Sonar Information.
1671  *    draw     - A flag to indicate if the bogie should be drawn or erased.
1672  *    name     - The name of the bogie.
1673  *    degrees  - The number of degrees that it should apprear at.
1674  *    distance - The distance the object is from the centre.
1675  *    ttl      - The time this bogie has to live.
1676  *    age      - The time this bogie has been around.
1677  */
1678
1679 static void
1680 DrawBogie(sonar_info *si, int draw, char *name, int degrees, 
1681           int distance, int ttl, int age) 
1682 {
1683
1684     /* Local Variables */
1685
1686     int x, y;
1687     GC gc;
1688     int ox = si->centrex;
1689     int oy = si->centrey;
1690     int index, delta;
1691
1692     /* Compute the coordinates of the object */
1693
1694     if (distance != 0)
1695       distance = (log((double) distance) / 10.0) * si->radius;
1696     x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1697     y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1698
1699     /* Set up the graphics context */
1700
1701     if (draw) {
1702
1703         /* Here we attempt to compute the distance into the total life of
1704          * object that we currently are. This distance is used against
1705          * the total lifetime to compute a fraction which is the index of
1706          * the color to draw the bogie.
1707          */
1708
1709         if (si->current <= degrees)
1710             delta = (si->current - degrees) * -1;
1711         else
1712             delta = 90 + (degrees - si->current);
1713         delta += (age * 90);
1714         index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1715         gc = si->text;
1716         XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1717
1718     } else
1719         gc = si->erase;
1720
1721   /* Draw (or erase) the Bogie */
1722
1723     XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1724     XDrawString(si->dpy, si->win, gc,
1725                 computeStringX(si, name, x),
1726                 computeStringY(si, y), name, strlen(name));
1727 }
1728
1729
1730 /*
1731  * Draw the sonar grid.
1732  *
1733  * Args:
1734  *    si - Sonar information block.
1735  */
1736
1737 static void
1738 drawGrid(sonar_info *si) 
1739 {
1740
1741     /* Local Variables */
1742
1743     int i;
1744     int width = si->maxx - si->minx;
1745     int height = si->maxy - si->miny;
1746   
1747     /* Draw the circles */
1748
1749     XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10, 
1750              width + 20, height + 20,  0, (360 * 64));
1751
1752     XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny, 
1753              width, height,  0, (360 * 64));
1754
1755     XDrawArc(si->dpy, si->win, si->grid, 
1756              (int) (si->minx + (.166 * width)), 
1757              (int) (si->miny + (.166 * height)), 
1758              (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1759              0, (360 * 64));
1760
1761     XDrawArc(si->dpy, si->win, si->grid, 
1762              (int) (si->minx + (.333 * width)),
1763              (int) (si->miny + (.333 * height)), 
1764              (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1765              0, (360 * 64));
1766
1767     /* Draw the radial lines */
1768
1769     for (i = 0; i < 360; i += 10)
1770         if (i % 30 == 0)
1771             XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1772                       (int) (si->centrex +
1773                       (si->radius + 10) * (cos((double) i / 57.29578))),
1774                       (int) (si->centrey -
1775                       (si->radius + 10)*(sin((double) i / 57.29578))));
1776         else
1777             XDrawLine(si->dpy, si->win, si->grid, 
1778                       (int) (si->centrex + si->radius *
1779                              (cos((double) i / 57.29578))),
1780                       (int) (si->centrey - si->radius *
1781                              (sin((double) i / 57.29578))),
1782                       (int) (si->centrex +
1783                       (si->radius + 10) * (cos((double) i / 57.29578))),
1784                       (int) (si->centrey - 
1785                       (si->radius + 10) * (sin((double) i / 57.29578))));
1786 }
1787
1788 /*
1789  * Update the Sonar scope.
1790  *
1791  * Args:
1792  *    si - The Sonar information.
1793  *    bl - A list  of bogies to add to the scope.
1794  */
1795
1796 static void
1797 Sonar(sonar_info *si, Bogie *bl) 
1798 {
1799
1800     /* Local Variables */
1801
1802     Bogie *bp, *prev;
1803     int i;
1804
1805     /* Check for expired tagets and remove them from the visible list */
1806
1807     prev = NULL;
1808     for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) {
1809
1810         /*
1811          * Remove it from the visible list if it's expired or we have
1812          * a new target with the same name.
1813          */
1814
1815         bp->age ++;
1816
1817         if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1818             (findNode(bl, bp->name) != NULL)) {
1819             DrawBogie(si, 0, bp->name, bp->tick,
1820                       bp->distance, bp->ttl, bp->age);
1821             if (prev == NULL)
1822                 si->visible = bp->next;
1823             else
1824                 prev->next = bp->next;
1825             freeBogie(bp);
1826             bp = prev;
1827         } else
1828             prev = bp;
1829     }
1830
1831     /* Draw the sweep */
1832
1833     {
1834       int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
1835       int start_deg = si->current * 4 * 64;
1836       if (seg_deg <= 0) seg_deg = 1;
1837       for (i = 0; i < si->sweep_segs; i++) {
1838         XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
1839         XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny, 
1840                  si->maxx - si->minx, si->maxy - si->miny,
1841                  start_deg + (i * seg_deg),
1842                  seg_deg);
1843       }
1844
1845       /* Remove the trailing wedge the sonar */
1846       XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny, 
1847                si->maxx - si->minx, si->maxy - si->miny, 
1848                start_deg + (i * seg_deg),
1849                (4 * 64));
1850     }
1851
1852     /* Move the new targets to the visible list */
1853
1854     for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1855         bl = bl->next;
1856         bp->next = si->visible;
1857         si->visible = bp;
1858     }
1859
1860     /* Draw the visible targets */
1861
1862     for (bp = si->visible; bp != NULL; bp = bp->next) {
1863         if (bp->age < bp->ttl)          /* grins */
1864            DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
1865     }
1866
1867     /* Redraw the grid */
1868
1869     drawGrid(si);
1870 }
1871
1872
1873 static ping_target *
1874 parse_mode (Bool ping_works_p)
1875 {
1876   char *source = get_string_resource ("ping", "Ping");
1877   char *token, *end;
1878   char dummy;
1879
1880   ping_target *hostlist = 0;
1881
1882   if (!source) source = strdup("");
1883
1884   if (!*source || !strcmp (source, "default"))
1885     {
1886 # ifdef HAVE_PING
1887       if (ping_works_p)         /* if root or setuid, ping will work. */
1888         source = strdup("subnet/29,/etc/hosts");
1889       else
1890 # endif
1891         source = strdup("simulation");
1892     }
1893
1894   token = source;
1895   end = source + strlen(source);
1896   while (token < end)
1897     {
1898       char *next;
1899 # ifdef HAVE_PING
1900       ping_target *new;
1901       struct stat st;
1902       unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1903       char d;
1904 # endif /* HAVE_PING */
1905
1906       for (next = token;
1907            *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1908            next++)
1909         ;
1910       *next = 0;
1911
1912
1913       if (debug_p)
1914         fprintf (stderr, "%s: parsing %s\n", progname, token);
1915
1916       if (!strcmp (token, "simulation"))
1917         return 0;
1918
1919       if (!ping_works_p)
1920         {
1921           fprintf(stderr,
1922            "%s: this program must be setuid to root for `ping mode' to work.\n"
1923              "       Running in `simulation mode' instead.\n",
1924                   progname);
1925           return 0;
1926         }
1927
1928 #ifdef HAVE_PING
1929       if ((4 == sscanf (token, "%d.%d.%d/%d %c",    &n0,&n1,&n2,    &m,&d)) ||
1930           (5 == sscanf (token, "%d.%d.%d.%d/%d %c", &n0,&n1,&n2,&n3,&m,&d)))
1931         {
1932           /* subnet: A.B.C.D/M
1933              subnet: A.B.C/M
1934            */
1935           unsigned long ip = (n0 << 24) | (n1 << 16) | (n2 << 8) | n3;
1936           new = subnetHostsList(ip, m);
1937         }
1938       else if (4 == sscanf (token, "%d.%d.%d.%d %c", &n0, &n1, &n2, &n3, &d))
1939         {
1940           /* IP: A.B.C.D
1941            */
1942           new = newHost (token);
1943         }
1944       else if (!strcmp (token, "subnet"))
1945         {
1946           new = subnetHostsList(0, 24);
1947         }
1948       else if (1 == sscanf (token, "subnet/%d %c", &m, &dummy))
1949         {
1950           new = subnetHostsList(0, m);
1951         }
1952       else if (*token == '.' || *token == '/' || !stat (token, &st))
1953         {
1954           /* file name
1955            */
1956           new = readPingHostsFile (token);
1957         }
1958       else
1959         {
1960           /* not an existant file - must be a host name
1961            */
1962           new = newHost (token);
1963         }
1964
1965       if (new)
1966         {
1967           ping_target *nn = new;
1968           while (nn && nn->next)
1969             nn = nn->next;
1970           nn->next = hostlist;
1971           hostlist = new;
1972
1973           sensor = ping;
1974         }
1975 #endif /* HAVE_PING */
1976
1977       token = next + 1;
1978       while (token < end &&
1979              (*token == ',' || *token == ' ' ||
1980               *token == '\t' || *token == '\n'))
1981         token++;
1982     }
1983
1984   return hostlist;
1985 }
1986
1987
1988
1989 /*
1990  * Main screen saver hack.
1991  *
1992  * Args:
1993  *    dpy - The X display.
1994  *    win - The X window.
1995  */
1996
1997 void 
1998 screenhack(Display *dpy, Window win) 
1999 {
2000
2001     /* Local Variables */
2002
2003     sonar_info *si;
2004     struct timeval start, finish;
2005     Bogie *bl;
2006     long sleeptime;
2007
2008     debug_p = get_boolean_resource ("debug", "Debug");
2009
2010     sensor = 0;
2011 # ifdef HAVE_PING
2012     sensor_info = (void *) init_ping();
2013 # else  /* !HAVE_PING */
2014     sensor_info = 0;
2015     parse_mode (0);  /* just to check argument syntax */
2016 # endif /* !HAVE_PING */
2017
2018     if (sensor == 0)
2019       {
2020         sensor = simulator;
2021         if ((sensor_info = (void *) init_sim()) == NULL)
2022           exit(1);
2023       }
2024
2025     if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
2026         exit(1);
2027
2028
2029     /* Sonar loop */
2030
2031     while (1) {
2032
2033         /* Call the sensor and display the results */
2034
2035         gettimeofday(&start, (struct timezone *) 0);
2036         bl = sensor(si, sensor_info);
2037         Sonar(si, bl);
2038
2039         /* Set up and sleep for the next one */
2040
2041         si->current = (si->current - 1) % 90;
2042         if (si->current == 0)
2043           si->sweepnum++;
2044         XSync (dpy, False);
2045         gettimeofday(&finish, (struct timezone *) 0);
2046         sleeptime = si->delay - delta(&start, &finish);
2047         screenhack_handle_events (dpy);
2048         if (sleeptime > 0L)
2049             usleep(sleeptime);
2050
2051     }
2052 }