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