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