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