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