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