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