http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[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.29 $
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 ((const char *) 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 # ifdef GETTIMEOFDAY_TWO_ARGS
1016     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
1017                  (struct timezone *) 0);
1018 # else
1019     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
1020 # endif
1021
1022     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
1023            pt->name);
1024     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
1025
1026     /* Send it */
1027
1028     if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0, 
1029                          &pt->address, sizeof(pt->address))) !=  pcktsiz) {
1030 #if 0
1031         char errbuf[BUFSIZ];
1032         sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
1033         perror(errbuf);
1034 #endif
1035     }
1036 }
1037
1038 /*
1039  * Catch a signal and do nothing.
1040  *
1041  * Args:
1042  *    sig - The signal that was caught.
1043  */
1044
1045 static void
1046 sigcatcher(int sig)
1047 {
1048     timer_expired = 1;
1049 }
1050
1051 /*
1052  * Compute the checksum on a ping packet.
1053  *
1054  * Args:
1055  *    packet - A pointer to the packet to compute the checksum for.
1056  *    size   - The size of the packet.
1057  *
1058  * Returns:
1059  *    The computed checksum
1060  *    
1061  */
1062
1063 static u_short
1064 checksum(u_short *packet, int size) 
1065 {
1066
1067     /* Local Variables */
1068
1069     register int nleft = size;
1070     register u_short *w = packet;
1071     register int sum = 0;
1072     u_short answer = 0;
1073
1074     /*
1075      * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1076      * sequential 16 bit words to it, and at the end, fold back all the
1077      * carry bits from the top 16 bits into the lower 16 bits.
1078      */
1079
1080     while (nleft > 1)  {
1081         sum += *w++;
1082         nleft -= 2;
1083     }
1084
1085     /* mop up an odd byte, if necessary */
1086
1087     if (nleft == 1) {
1088         *(u_char *)(&answer) = *(u_char *)w ;
1089         *(1 + (u_char *)(&answer)) = 0;
1090         sum += answer;
1091     }
1092
1093     /* add back carry outs from top 16 bits to low 16 bits */
1094
1095     sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
1096     sum += (sum >> 16);                     /* add carry */
1097     answer = ~sum;                          /* truncate to 16 bits */
1098
1099     /* Done */
1100
1101     return(answer);
1102 }
1103
1104 /*
1105  * Look for ping replies.
1106  *
1107  * Retrieve all outstanding ping replies.
1108  *
1109  * Args:
1110  *    si - Information about the sonar.
1111  *    pi - Ping information.
1112  *    ttl - The time each bogie is to live on the screen
1113  *
1114  * Returns:
1115  *    A Bogie list of all the machines that replied.
1116  */
1117
1118 static Bogie *
1119 getping(sonar_info *si, ping_info *pi) 
1120 {
1121
1122     /* Local Variables */
1123
1124     struct sockaddr from;
1125     unsigned int fromlen;  /* Posix says socklen_t, but that's not portable */
1126     int result;
1127     u_char packet[1024];
1128     struct timeval now;
1129     struct timeval *then;
1130     struct ip *ip;
1131     int iphdrlen;
1132     struct ICMP *icmph;
1133     Bogie *bl = NULL;
1134     Bogie *new;
1135     char *name;
1136     struct sigaction sa;
1137     struct itimerval it;
1138     fd_set rfds;
1139     struct timeval tv;
1140
1141     /* Set up a signal to interupt our wait for a packet */
1142
1143     sigemptyset(&sa.sa_mask);
1144     sa.sa_flags = 0;
1145     sa.sa_handler = sigcatcher;
1146     if (sigaction(SIGALRM, &sa, 0) == -1) {
1147         char msg[1024];
1148         sprintf(msg, "%s: unable to trap SIGALRM", progname);
1149         perror(msg);
1150         exit(1);
1151     }
1152
1153     /* Set up a timer to interupt us if we don't get a packet */
1154
1155     it.it_interval.tv_sec = 0;
1156     it.it_interval.tv_usec = 0;
1157     it.it_value.tv_sec = 0;
1158     it.it_value.tv_usec = pi->timeout;
1159     timer_expired = 0;
1160     setitimer(ITIMER_REAL, &it, NULL);
1161
1162     /* Wait for a result packet */
1163
1164     fromlen = sizeof(from);
1165     while (! timer_expired) {
1166       tv.tv_usec=pi->timeout;
1167       tv.tv_sec=0;
1168 #if 0
1169       /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
1170       FD_ZERO(&rfds);
1171 #else
1172       memset (&rfds, 0, sizeof(rfds));
1173 #endif
1174       FD_SET(pi->icmpsock,&rfds);
1175       /* only wait a little while, in case we raced with the timer expiration.
1176          From Valentijn Sessink <valentyn@openoffice.nl> */
1177       if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) {
1178         result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1179                       0, &from, &fromlen);
1180
1181         /* Check the packet */
1182
1183 # ifdef GETTIMEOFDAY_TWO_ARGS
1184         gettimeofday(&now, (struct timezone *) 0);
1185 # else
1186         gettimeofday(&now);
1187 # endif
1188         ip = (struct ip *) packet;
1189         iphdrlen = IP_HDRLEN(ip) << 2;
1190         icmph = (struct ICMP *) &packet[iphdrlen];
1191
1192         /* Was the packet a reply?? */
1193
1194         if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1195             /* Ignore anything but ICMP Replies */
1196             continue; /* Nope */
1197         }
1198
1199         /* Was it for us? */
1200
1201         if (ICMP_ID(icmph) != pi->pid) {
1202             /* Ignore packets not set from us */
1203             continue; /* Nope */
1204         }
1205
1206         /* Copy the name of the bogie */
1207
1208         if ((name =
1209              strdup((char *) &packet[iphdrlen + 
1210                                     + sizeof(struct ICMP)
1211                                     + sizeof(struct timeval)])) == NULL) {
1212             fprintf(stderr, "%s: Out of memory\n", progname);
1213             return bl;
1214         }
1215
1216         /* If the name is an IP addr, try to resolve it. */
1217         {
1218           int iip[4];
1219           char c;
1220           if (4 == sscanf(name, " %d.%d.%d.%d %c",
1221                           &iip[0], &iip[1], &iip[2], &iip[3], &c))
1222             {
1223               unsigned char ip[4];
1224               struct hostent *h;
1225               ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
1226               h = gethostbyaddr ((char *) ip, 4, AF_INET);
1227               if (h && h->h_name && *h->h_name)
1228                 {
1229                   free (name);
1230                   name = strdup (h->h_name);
1231                 }
1232             }
1233         }
1234
1235         /* Create the new Bogie and add it to the list we are building */
1236
1237         if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
1238             return bl;
1239         new->next = bl;
1240         bl = new;
1241
1242         /* Compute the round trip time */
1243
1244         then =  (struct timeval *) &packet[iphdrlen +
1245                                           sizeof(struct ICMP)];
1246         new->distance = delta(then, &now) / 100;
1247         if (new->distance == 0)
1248                 new->distance = 2; /* HACK */
1249       }
1250     }
1251
1252     /* Done */
1253
1254     return bl;
1255 }
1256
1257 /*
1258  * Ping hosts.
1259  *
1260  * Args:
1261  *    si - Sonar Information.
1262  *    pi - Ping Information.
1263  *
1264  * Returns:
1265  *    A list of hosts that replied to pings or null if there were none.
1266  */
1267
1268 static Bogie *
1269 ping(sonar_info *si, void *vpi) 
1270 {
1271
1272     /*
1273      * This tries to distribute the targets evely around the field of the
1274      * sonar.
1275      */
1276
1277     ping_info *pi = (ping_info *) vpi;
1278     static ping_target *ptr = NULL;
1279
1280     int tick = si->current * -1 + 1;
1281     if ((ptr == NULL) && (tick == 1))
1282         ptr = pi->targets;
1283
1284     if (pi->numtargets <= 90) {
1285         int xdrant = 90 / pi->numtargets;
1286         if ((tick % xdrant) == 0) {
1287             if (ptr != (ping_target *) 0) {
1288                 sendping(pi, ptr);
1289                 ptr = ptr->next;
1290             }
1291         }
1292
1293     } else if (pi->numtargets > 90) {
1294         if (ptr != (ping_target *) 0) {
1295             sendping(pi, ptr);
1296             ptr = ptr->next;
1297         }
1298     }
1299
1300     /* Get the results */
1301
1302     return getping(si, pi);
1303 }
1304
1305 #endif /* HAVE_PING */
1306
1307 /*
1308  * Calculate the difference between two timevals in microseconds.
1309  *
1310  * Args:
1311  *    then - The older timeval.
1312  *    now  - The newer timeval.
1313  *
1314  * Returns:
1315  *   The difference between the two in microseconds.
1316  */
1317
1318 static long
1319 delta(struct timeval *then, struct timeval *now) 
1320 {
1321     return (((now->tv_sec - then->tv_sec) * 1000000) + 
1322                (now->tv_usec - then->tv_usec));  
1323 }
1324
1325 /*
1326  * Initialize the simulation mode.
1327  */
1328
1329 static sim_info *
1330 init_sim(void) 
1331 {
1332
1333     /* Local Variables */
1334
1335     sim_info *si;
1336     int i;
1337
1338     /* Create the simulation info structure */
1339
1340     if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1341         fprintf(stderr, "%s: Out of memory\n", progname);
1342         return NULL;
1343     }
1344
1345     /* Team A */
1346
1347     si->numA = get_integer_resource("teamACount", "TeamACount");
1348     if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1349         == NULL) {
1350         free(si);
1351         fprintf(stderr, "%s: Out of Memory\n", progname);
1352         return NULL;
1353     }
1354     si->teamAID = get_string_resource("teamAName", "TeamAName");
1355     for (i = 0; i < si->numA; i++) {
1356         if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1357             == NULL) {
1358             free(si);
1359             fprintf(stderr, "%s: Out of Memory\n", progname);
1360             return NULL;
1361         }
1362         sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1363         si->teamA[i].nexttick = random() % 90;
1364         si->teamA[i].nextdist = random() % 100;
1365         si->teamA[i].movedonsweep = -1;
1366     }
1367
1368     /* Team B */
1369
1370     si->numB = get_integer_resource("teamBCount", "TeamBCount");
1371     if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1372         == NULL) {
1373         free(si);
1374         fprintf(stderr, "%s: Out of Memory\n", progname);
1375         return NULL;
1376     }
1377     si->teamBID = get_string_resource("teamBName", "TeamBName");
1378     for (i = 0; i < si->numB; i++) {
1379         if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1380             == NULL) {
1381             free(si);
1382             fprintf(stderr, "%s: Out of Memory\n", progname);
1383             return NULL;
1384         }
1385         sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1386         si->teamB[i].nexttick = random() % 90;
1387         si->teamB[i].nextdist = random() % 100;
1388         si->teamB[i].movedonsweep = -1;
1389     }
1390
1391     /* Done */
1392
1393     return si;
1394 }
1395
1396 /*
1397  * Creates and returns a drawing mask for the scope:
1398  * mask out anything outside of the disc.
1399  */
1400 static Pixmap
1401 scope_mask (Display *dpy, Window win, sonar_info *si)
1402 {
1403   XGCValues gcv;
1404   Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
1405   GC gc = XCreateGC (dpy, mask, 0, &gcv);
1406   XSetFunction (dpy, gc, GXclear);
1407   XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
1408   XSetFunction (dpy, gc, GXset);
1409   XFillArc(dpy, mask, gc, si->minx, si->miny, 
1410            si->maxx - si->minx, si->maxy - si->miny,
1411            0, 360 * 64);
1412   return mask;
1413 }
1414
1415
1416 /*
1417  * Initialize the Sonar.
1418  *
1419  * Args:
1420  *    dpy - The X display.
1421  *    win - The X window;
1422  *
1423  * Returns:
1424  *   A sonar_info strcuture or null if memory allocation problems occur.
1425  */
1426
1427 static sonar_info *
1428 init_sonar(Display *dpy, Window win) 
1429 {
1430
1431     /* Local Variables */
1432
1433     XGCValues gcv;
1434     XWindowAttributes xwa;
1435     sonar_info *si;
1436     XColor start, end;
1437     int h1, h2;
1438     double s1, s2, v1, v2;
1439
1440     /* Create the Sonar information structure */
1441
1442     if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1443         fprintf(stderr, "%s: Out of memory\n", progname);
1444         return NULL;
1445     }
1446
1447     /* Initialize the structure for the current environment */
1448
1449     si->dpy = dpy;
1450     si->win = win;
1451     si->visible = NULL;
1452     XGetWindowAttributes(dpy, win, &xwa);
1453     si->cmap = xwa.colormap;
1454     si->width = xwa.width;
1455     si->height = xwa.height;
1456     si->centrex = si->width / 2;
1457     si->centrey = si->height / 2;
1458     si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1459     si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1460     si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1461     si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1462     si->radius = si->maxx - si->centrex;
1463     si->current = 0;
1464     si->sweepnum = 0;
1465
1466     /* Get the font */
1467
1468     if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1469          == NULL) &&
1470         ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1471         fprintf(stderr, "%s: can't load an appropriate font\n", progname);
1472         return NULL;
1473     }
1474
1475     /* Get the delay between animation frames */
1476
1477     si->delay = get_integer_resource ("delay", "Integer");
1478
1479     if (si->delay < 0) si->delay = 0;
1480     si->TTL = get_integer_resource("ttl", "TTL");
1481
1482     /* Create the Graphics Contexts that will be used to draw things */
1483
1484     gcv.foreground = 
1485         get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
1486     si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
1487     gcv.font = si->font->fid;
1488     si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
1489     gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
1490                                         dpy, si->cmap);
1491     si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1492     gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1493                                         dpy, si->cmap);
1494     si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1495
1496     /* Install the clip mask... */
1497     {
1498       Pixmap mask = scope_mask (dpy, win, si);
1499       XSetClipMask(dpy, si->text, mask);
1500       XSetClipMask(dpy, si->erase, mask);
1501       XFreePixmap (dpy, mask); /* it's been copied into the GCs */
1502     }
1503
1504     /* Compute pixel values for fading text on the display */
1505
1506     XParseColor(dpy, si->cmap, 
1507                 get_string_resource("textColor", "TextColor"), &start);
1508     XParseColor(dpy, si->cmap, 
1509                 get_string_resource("scopeColor", "ScopeColor"), &end);
1510
1511     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1512     rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
1513
1514     si->text_steps = get_integer_resource("textSteps", "TextSteps");
1515     if (si->text_steps < 0 || si->text_steps > 255)
1516       si->text_steps = 10;
1517
1518     si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1519     make_color_ramp (dpy, si->cmap,
1520                      h1, s1, v1,
1521                      h2, s2, v2,
1522                      si->text_colors, &si->text_steps,
1523                      False, True, False);
1524
1525     /* Compute the pixel values for the fading sweep */
1526
1527     XParseColor(dpy, si->cmap, 
1528                 get_string_resource("sweepColor", "SweepColor"), &start);
1529
1530     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1531
1532     si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
1533     if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
1534     if (si->sweep_degrees > 350) si->sweep_degrees = 350;
1535
1536     si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1537     if (si->sweep_segs < 1 || si->sweep_segs > 255)
1538       si->sweep_segs = 255;
1539
1540     si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1541     make_color_ramp (dpy, si->cmap,
1542                      h1, s1, v1,
1543                      h2, s2, v2,
1544                      si->sweep_colors, &si->sweep_segs,
1545                      False, True, False);
1546
1547     if (si->sweep_segs <= 0)
1548       si->sweep_segs = 1;
1549
1550     /* Done */
1551
1552     return si;
1553 }
1554
1555 /*
1556  * Update the location of a simulated bogie.
1557  */
1558
1559 static void
1560 updateLocation(sim_target *t) 
1561 {
1562
1563     int xdist, xtick;
1564
1565     xtick = (int) (random() %  3) - 1;
1566     xdist = (int) (random() % 11) - 5;
1567     if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1568         t->nexttick += xtick;
1569     else
1570         t->nexttick -= xtick;
1571     if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1572         t->nextdist += xdist;
1573     else
1574         t->nextdist -= xdist;
1575 }
1576
1577 /*
1578  * The simulator. This uses information in the sim_info to simulate a bunch
1579  * of bogies flying around on the screen.
1580  */
1581
1582 /*
1583  * TODO: It would be cool to have the two teams chase each other around and
1584  *       shoot it out.
1585  */
1586
1587 static Bogie *
1588 simulator(sonar_info *si, void *vinfo) 
1589 {
1590
1591     /* Local Variables */
1592
1593     int i;
1594     Bogie *list = NULL;
1595     Bogie *new;
1596     sim_target *t;
1597     sim_info *info = (sim_info *) vinfo;
1598
1599     /* Check team A */
1600
1601     for (i = 0; i < info->numA; i++) {
1602         t = &info->teamA[i];
1603         if ((t->movedonsweep != si->sweepnum) &&
1604             (t->nexttick == (si->current * -1))) {
1605             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1606             if (list != NULL)
1607                 new->next = list;
1608             list = new;
1609             updateLocation(t);
1610             t->movedonsweep = si->sweepnum;
1611         }
1612     }
1613
1614     /* Team B */
1615
1616     for (i = 0; i < info->numB; i++) {
1617         t = &info->teamB[i];
1618         if ((t->movedonsweep != si->sweepnum) &&
1619             (t->nexttick == (si->current * -1))) {
1620             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1621             if (list != NULL)
1622                 new->next = list;
1623             list = new;
1624             updateLocation(t);
1625             t->movedonsweep = si->sweepnum;
1626         }
1627     }
1628
1629     /* Done */
1630
1631     return list;
1632 }
1633
1634 /*
1635  * Compute the X coordinate of the label.
1636  *
1637  * Args:
1638  *    si - The sonar info block.
1639  *    label - The label that will be drawn.
1640  *    x - The x coordinate of the bogie.
1641  *
1642  * Returns:
1643  *    The x coordinate of the start of the label.
1644  */
1645
1646 static int
1647 computeStringX(sonar_info *si, char *label, int x) 
1648 {
1649
1650     int width = XTextWidth(si->font, label, strlen(label));
1651     return x - (width / 2);
1652 }
1653
1654 /*
1655  * Compute the Y coordinate of the label.
1656  *
1657  * Args:
1658  *    si - The sonar information.
1659  *    y - The y coordinate of the bogie.
1660  *
1661  * Returns:
1662  *    The y coordinate of the start of the label.
1663  */
1664
1665 /* TODO: Add smarts to keep label in sonar screen */
1666
1667 static int
1668 computeStringY(sonar_info *si, int y) 
1669 {
1670
1671     int fheight = si->font->ascent + si->font->descent;
1672     return y + 5 + fheight;
1673 }
1674
1675 /*
1676  * Draw a Bogie on the radar screen.
1677  *
1678  * Args:
1679  *    si       - Sonar Information.
1680  *    draw     - A flag to indicate if the bogie should be drawn or erased.
1681  *    name     - The name of the bogie.
1682  *    degrees  - The number of degrees that it should apprear at.
1683  *    distance - The distance the object is from the centre.
1684  *    ttl      - The time this bogie has to live.
1685  *    age      - The time this bogie has been around.
1686  */
1687
1688 static void
1689 DrawBogie(sonar_info *si, int draw, char *name, int degrees, 
1690           int distance, int ttl, int age) 
1691 {
1692
1693     /* Local Variables */
1694
1695     int x, y;
1696     GC gc;
1697     int ox = si->centrex;
1698     int oy = si->centrey;
1699     int index, delta;
1700
1701     /* Compute the coordinates of the object */
1702
1703     if (distance != 0)
1704       distance = (log((double) distance) / 10.0) * si->radius;
1705     x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1706     y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1707
1708     /* Set up the graphics context */
1709
1710     if (draw) {
1711
1712         /* Here we attempt to compute the distance into the total life of
1713          * object that we currently are. This distance is used against
1714          * the total lifetime to compute a fraction which is the index of
1715          * the color to draw the bogie.
1716          */
1717
1718         if (si->current <= degrees)
1719             delta = (si->current - degrees) * -1;
1720         else
1721             delta = 90 + (degrees - si->current);
1722         delta += (age * 90);
1723         index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1724         gc = si->text;
1725         XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1726
1727     } else
1728         gc = si->erase;
1729
1730   /* Draw (or erase) the Bogie */
1731
1732     XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1733     XDrawString(si->dpy, si->win, gc,
1734                 computeStringX(si, name, x),
1735                 computeStringY(si, y), name, strlen(name));
1736 }
1737
1738
1739 /*
1740  * Draw the sonar grid.
1741  *
1742  * Args:
1743  *    si - Sonar information block.
1744  */
1745
1746 static void
1747 drawGrid(sonar_info *si) 
1748 {
1749
1750     /* Local Variables */
1751
1752     int i;
1753     int width = si->maxx - si->minx;
1754     int height = si->maxy - si->miny;
1755   
1756     /* Draw the circles */
1757
1758     XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10, 
1759              width + 20, height + 20,  0, (360 * 64));
1760
1761     XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny, 
1762              width, height,  0, (360 * 64));
1763
1764     XDrawArc(si->dpy, si->win, si->grid, 
1765              (int) (si->minx + (.166 * width)), 
1766              (int) (si->miny + (.166 * height)), 
1767              (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1768              0, (360 * 64));
1769
1770     XDrawArc(si->dpy, si->win, si->grid, 
1771              (int) (si->minx + (.333 * width)),
1772              (int) (si->miny + (.333 * height)), 
1773              (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1774              0, (360 * 64));
1775
1776     /* Draw the radial lines */
1777
1778     for (i = 0; i < 360; i += 10)
1779         if (i % 30 == 0)
1780             XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1781                       (int) (si->centrex +
1782                       (si->radius + 10) * (cos((double) i / 57.29578))),
1783                       (int) (si->centrey -
1784                       (si->radius + 10)*(sin((double) i / 57.29578))));
1785         else
1786             XDrawLine(si->dpy, si->win, si->grid, 
1787                       (int) (si->centrex + si->radius *
1788                              (cos((double) i / 57.29578))),
1789                       (int) (si->centrey - si->radius *
1790                              (sin((double) i / 57.29578))),
1791                       (int) (si->centrex +
1792                       (si->radius + 10) * (cos((double) i / 57.29578))),
1793                       (int) (si->centrey - 
1794                       (si->radius + 10) * (sin((double) i / 57.29578))));
1795 }
1796
1797 /*
1798  * Update the Sonar scope.
1799  *
1800  * Args:
1801  *    si - The Sonar information.
1802  *    bl - A list  of bogies to add to the scope.
1803  */
1804
1805 static void
1806 Sonar(sonar_info *si, Bogie *bl) 
1807 {
1808
1809     /* Local Variables */
1810
1811     Bogie *bp, *prev;
1812     int i;
1813
1814     /* Check for expired tagets and remove them from the visible list */
1815
1816     prev = NULL;
1817     for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) {
1818
1819         /*
1820          * Remove it from the visible list if it's expired or we have
1821          * a new target with the same name.
1822          */
1823
1824         bp->age ++;
1825
1826         if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1827             (findNode(bl, bp->name) != NULL)) {
1828             DrawBogie(si, 0, bp->name, bp->tick,
1829                       bp->distance, bp->ttl, bp->age);
1830             if (prev == NULL)
1831                 si->visible = bp->next;
1832             else
1833                 prev->next = bp->next;
1834             freeBogie(bp);
1835             bp = prev;
1836         } else
1837             prev = bp;
1838     }
1839
1840     /* Draw the sweep */
1841
1842     {
1843       int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
1844       int start_deg = si->current * 4 * 64;
1845       if (seg_deg <= 0) seg_deg = 1;
1846       for (i = 0; i < si->sweep_segs; i++) {
1847         XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
1848         XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny, 
1849                  si->maxx - si->minx, si->maxy - si->miny,
1850                  start_deg + (i * seg_deg),
1851                  seg_deg);
1852       }
1853
1854       /* Remove the trailing wedge the sonar */
1855       XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny, 
1856                si->maxx - si->minx, si->maxy - si->miny, 
1857                start_deg + (i * seg_deg),
1858                (4 * 64));
1859     }
1860
1861     /* Move the new targets to the visible list */
1862
1863     for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1864         bl = bl->next;
1865         bp->next = si->visible;
1866         si->visible = bp;
1867     }
1868
1869     /* Draw the visible targets */
1870
1871     for (bp = si->visible; bp != NULL; bp = bp->next) {
1872         if (bp->age < bp->ttl)          /* grins */
1873            DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
1874     }
1875
1876     /* Redraw the grid */
1877
1878     drawGrid(si);
1879 }
1880
1881
1882 static ping_target *
1883 parse_mode (Bool ping_works_p)
1884 {
1885   char *source = get_string_resource ("ping", "Ping");
1886   char *token, *end;
1887   char dummy;
1888
1889   ping_target *hostlist = 0;
1890
1891   if (!source) source = strdup("");
1892
1893   if (!*source || !strcmp (source, "default"))
1894     {
1895 # ifdef HAVE_PING
1896       if (ping_works_p)         /* if root or setuid, ping will work. */
1897         source = strdup("subnet/29,/etc/hosts");
1898       else
1899 # endif
1900         source = strdup("simulation");
1901     }
1902
1903   token = source;
1904   end = source + strlen(source);
1905   while (token < end)
1906     {
1907       char *next;
1908 # ifdef HAVE_PING
1909       ping_target *new;
1910       struct stat st;
1911       unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1912       char d;
1913 # endif /* HAVE_PING */
1914
1915       for (next = token;
1916            *next &&
1917            *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1918            next++)
1919         ;
1920       *next = 0;
1921
1922
1923       if (debug_p)
1924         fprintf (stderr, "%s: parsing %s\n", progname, token);
1925
1926       if (!strcmp (token, "simulation"))
1927         return 0;
1928
1929       if (!ping_works_p)
1930         {
1931           fprintf(stderr,
1932            "%s: this program must be setuid to root for `ping mode' to work.\n"
1933              "       Running in `simulation mode' instead.\n",
1934                   progname);
1935           return 0;
1936         }
1937
1938 #ifdef HAVE_PING
1939       if ((4 == sscanf (token, "%u.%u.%u/%u %c",    &n0,&n1,&n2,    &m,&d)) ||
1940           (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1941         {
1942           /* subnet: A.B.C.D/M
1943              subnet: A.B.C/M
1944            */
1945           unsigned long ip = (n0 << 24) | (n1 << 16) | (n2 << 8) | n3;
1946           new = subnetHostsList(ip, m);
1947         }
1948       else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1949         {
1950           /* IP: A.B.C.D
1951            */
1952           new = newHost (token);
1953         }
1954       else if (!strcmp (token, "subnet"))
1955         {
1956           new = subnetHostsList(0, 24);
1957         }
1958       else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1959         {
1960           new = subnetHostsList(0, m);
1961         }
1962       else if (*token == '.' || *token == '/' || !stat (token, &st))
1963         {
1964           /* file name
1965            */
1966           new = readPingHostsFile (token);
1967         }
1968       else
1969         {
1970           /* not an existant file - must be a host name
1971            */
1972           new = newHost (token);
1973         }
1974
1975       if (new)
1976         {
1977           ping_target *nn = new;
1978           while (nn && nn->next)
1979             nn = nn->next;
1980           nn->next = hostlist;
1981           hostlist = new;
1982
1983           sensor = ping;
1984         }
1985 #endif /* HAVE_PING */
1986
1987       token = next + 1;
1988       while (token < end &&
1989              (*token == ',' || *token == ' ' ||
1990               *token == '\t' || *token == '\n'))
1991         token++;
1992     }
1993
1994   return hostlist;
1995 }
1996
1997
1998
1999 /*
2000  * Main screen saver hack.
2001  *
2002  * Args:
2003  *    dpy - The X display.
2004  *    win - The X window.
2005  */
2006
2007 void 
2008 screenhack(Display *dpy, Window win) 
2009 {
2010
2011     /* Local Variables */
2012
2013     sonar_info *si;
2014     struct timeval start, finish;
2015     Bogie *bl;
2016     long sleeptime;
2017
2018     debug_p = get_boolean_resource ("debug", "Debug");
2019
2020     sensor = 0;
2021 # ifdef HAVE_PING
2022     sensor_info = (void *) init_ping();
2023 # else  /* !HAVE_PING */
2024     sensor_info = 0;
2025     parse_mode (0);  /* just to check argument syntax */
2026 # endif /* !HAVE_PING */
2027
2028     if (sensor == 0)
2029       {
2030         sensor = simulator;
2031         if ((sensor_info = (void *) init_sim()) == NULL)
2032           exit(1);
2033       }
2034
2035     if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
2036         exit(1);
2037
2038
2039     /* Sonar loop */
2040
2041     while (1) {
2042
2043         /* Call the sensor and display the results */
2044
2045 # ifdef GETTIMEOFDAY_TWO_ARGS
2046         gettimeofday(&start, (struct timezone *) 0);
2047 # else
2048         gettimeofday(&start);
2049 # endif
2050         bl = sensor(si, sensor_info);
2051         Sonar(si, bl);
2052
2053         /* Set up and sleep for the next one */
2054
2055         si->current = (si->current - 1) % 90;
2056         if (si->current == 0)
2057           si->sweepnum++;
2058         XSync (dpy, False);
2059 # ifdef GETTIMEOFDAY_TWO_ARGS
2060         gettimeofday(&finish, (struct timezone *) 0);
2061 # else
2062         gettimeofday(&finish);
2063 # endif
2064         sleeptime = si->delay - delta(&start, &finish);
2065         screenhack_handle_events (dpy);
2066         if (sleeptime > 0L)
2067             usleep(sleeptime);
2068
2069     }
2070 }