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