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