ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.22.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.38 $
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.  Do this before dropping privs.
954
955        Raw sockets can only be opened by root (or setuid root), so we
956        only try to do this when the effective uid is 0.
957
958        We used to just always try, and notice the failure.  But apparently
959        that causes "SELinux" to log spurious warnings when running with the
960        "strict" policy.  So to avoid that, we just don't try unless we
961        know it will work.
962      */
963     if (geteuid() == 0 &&
964         (pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
965       socket_initted_p = True;
966     }
967
968     /* Disavow privs */
969
970     setuid(getuid());
971
972
973     pi->pid = getpid() & 0xFFFF;
974     pi->seq = 0;
975     pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
976
977     /* Generate a list of targets */
978
979     pi->targets = parse_mode (socket_initted_p);
980     pi->targets = delete_duplicate_hosts (pi->targets);
981
982
983     if (debug_p)
984       {
985         ping_target *t;
986         fprintf (stderr, "%s: Target list:\n", progname);
987         for (t = pi->targets; t; t = t->next)
988           {
989             struct sockaddr_in *iaddr = (struct sockaddr_in *) &(t->address);
990             unsigned long ip = iaddr->sin_addr.s_addr;
991             fprintf (stderr, "%s:   ", progname);
992             print_host (stderr, ip, t->name);
993           }
994       }
995
996     /* Make sure there is something to ping */
997
998     if (pi->targets == NULL) {
999       goto ping_init_error;
1000     }
1001
1002     /* Count the targets */
1003
1004     pt = pi->targets;
1005     pi->numtargets = 0;
1006     while (pt != NULL) {
1007         pi->numtargets++;
1008         pt = pt->next;
1009     }
1010
1011     /* Done */
1012
1013     return pi;
1014
1015     /* Handle initialization errors here */
1016
1017 ping_init_error:
1018     if (pi != NULL)
1019         free(pi);
1020     return NULL;
1021 }
1022
1023
1024 /*
1025  * Ping a host.
1026  *
1027  * Args:
1028  *    pi   - The ping information strcuture.
1029  *    host - The name or IP address of the host to ping (in ascii).
1030  */
1031
1032 static void
1033 sendping(ping_info *pi, ping_target *pt) 
1034 {
1035
1036     /* Local Variables */
1037
1038     u_char *packet;
1039     struct ICMP *icmph;
1040     int result;
1041
1042     /*
1043      * Note, we will send the character name of the host that we are
1044      * pinging in the packet so that we don't have to keep track of the
1045      * name or do an address lookup when it comes back.
1046      */
1047
1048     int pcktsiz = sizeof(struct ICMP) + sizeof(struct timeval) +
1049         strlen(pt->name) + 1;
1050
1051     /* Create the ICMP packet */
1052
1053     if ((packet = (u_char *) malloc(pcktsiz)) == (void *) 0)
1054         return;  /* Out of memory */
1055     icmph = (struct ICMP *) packet;
1056     ICMP_TYPE(icmph) = ICMP_ECHO;
1057     ICMP_CODE(icmph) = 0;
1058     ICMP_CHECKSUM(icmph) = 0;
1059     ICMP_ID(icmph) = pi->pid;
1060     ICMP_SEQ(icmph) = pi->seq++;
1061 # ifdef GETTIMEOFDAY_TWO_ARGS
1062     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
1063                  (struct timezone *) 0);
1064 # else
1065     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
1066 # endif
1067
1068     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
1069            pt->name);
1070     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
1071
1072     /* Send it */
1073
1074     if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0, 
1075                          &pt->address, sizeof(pt->address))) !=  pcktsiz) {
1076 #if 0
1077         char errbuf[BUFSIZ];
1078         sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
1079         perror(errbuf);
1080 #endif
1081     }
1082 }
1083
1084 /*
1085  * Catch a signal and do nothing.
1086  *
1087  * Args:
1088  *    sig - The signal that was caught.
1089  */
1090
1091 static void
1092 sigcatcher(int sig)
1093 {
1094     timer_expired = 1;
1095 }
1096
1097 /*
1098  * Compute the checksum on a ping packet.
1099  *
1100  * Args:
1101  *    packet - A pointer to the packet to compute the checksum for.
1102  *    size   - The size of the packet.
1103  *
1104  * Returns:
1105  *    The computed checksum
1106  *    
1107  */
1108
1109 static u_short
1110 checksum(u_short *packet, int size) 
1111 {
1112
1113     /* Local Variables */
1114
1115     register int nleft = size;
1116     register u_short *w = packet;
1117     register int sum = 0;
1118     u_short answer = 0;
1119
1120     /*
1121      * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1122      * sequential 16 bit words to it, and at the end, fold back all the
1123      * carry bits from the top 16 bits into the lower 16 bits.
1124      */
1125
1126     while (nleft > 1)  {
1127         sum += *w++;
1128         nleft -= 2;
1129     }
1130
1131     /* mop up an odd byte, if necessary */
1132
1133     if (nleft == 1) {
1134         *(u_char *)(&answer) = *(u_char *)w ;
1135         *(1 + (u_char *)(&answer)) = 0;
1136         sum += answer;
1137     }
1138
1139     /* add back carry outs from top 16 bits to low 16 bits */
1140
1141     sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
1142     sum += (sum >> 16);                     /* add carry */
1143     answer = ~sum;                          /* truncate to 16 bits */
1144
1145     /* Done */
1146
1147     return(answer);
1148 }
1149
1150 /*
1151  * Look for ping replies.
1152  *
1153  * Retrieve all outstanding ping replies.
1154  *
1155  * Args:
1156  *    si - Information about the sonar.
1157  *    pi - Ping information.
1158  *    ttl - The time each bogie is to live on the screen
1159  *
1160  * Returns:
1161  *    A Bogie list of all the machines that replied.
1162  */
1163
1164 static Bogie *
1165 getping(sonar_info *si, ping_info *pi) 
1166 {
1167
1168     /* Local Variables */
1169
1170     struct sockaddr from;
1171     unsigned int fromlen;  /* Posix says socklen_t, but that's not portable */
1172     int result;
1173     u_char packet[1024];
1174     struct timeval now;
1175     struct timeval *then;
1176     struct ip *ip;
1177     int iphdrlen;
1178     struct ICMP *icmph;
1179     Bogie *bl = NULL;
1180     Bogie *new;
1181     char *name;
1182     struct sigaction sa;
1183     struct itimerval it;
1184     fd_set rfds;
1185     struct timeval tv;
1186
1187     /* Set up a signal to interupt our wait for a packet */
1188
1189     sigemptyset(&sa.sa_mask);
1190     sa.sa_flags = 0;
1191     sa.sa_handler = sigcatcher;
1192     if (sigaction(SIGALRM, &sa, 0) == -1) {
1193         char msg[1024];
1194         sprintf(msg, "%s: unable to trap SIGALRM", progname);
1195         perror(msg);
1196         exit(1);
1197     }
1198
1199     /* Set up a timer to interupt us if we don't get a packet */
1200
1201     it.it_interval.tv_sec = 0;
1202     it.it_interval.tv_usec = 0;
1203     it.it_value.tv_sec = 0;
1204     it.it_value.tv_usec = pi->timeout;
1205     timer_expired = 0;
1206     setitimer(ITIMER_REAL, &it, NULL);
1207
1208     /* Wait for a result packet */
1209
1210     fromlen = sizeof(from);
1211     while (! timer_expired) {
1212       tv.tv_usec=pi->timeout;
1213       tv.tv_sec=0;
1214 #if 0
1215       /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
1216       FD_ZERO(&rfds);
1217 #else
1218       memset (&rfds, 0, sizeof(rfds));
1219 #endif
1220       FD_SET(pi->icmpsock,&rfds);
1221       /* only wait a little while, in case we raced with the timer expiration.
1222          From Valentijn Sessink <valentyn@openoffice.nl> */
1223       if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) {
1224         result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1225                       0, &from, &fromlen);
1226
1227         /* Check the packet */
1228
1229 # ifdef GETTIMEOFDAY_TWO_ARGS
1230         gettimeofday(&now, (struct timezone *) 0);
1231 # else
1232         gettimeofday(&now);
1233 # endif
1234         ip = (struct ip *) packet;
1235         iphdrlen = IP_HDRLEN(ip) << 2;
1236         icmph = (struct ICMP *) &packet[iphdrlen];
1237         then  = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
1238
1239
1240         /* Was the packet a reply?? */
1241
1242         if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1243             /* Ignore anything but ICMP Replies */
1244             continue; /* Nope */
1245         }
1246
1247         /* Was it for us? */
1248
1249         if (ICMP_ID(icmph) != pi->pid) {
1250             /* Ignore packets not set from us */
1251             continue; /* Nope */
1252         }
1253
1254         /* Copy the name of the bogie */
1255
1256         if ((name =
1257              strdup((char *) &packet[iphdrlen + 
1258                                     + sizeof(struct ICMP)
1259                                     + sizeof(struct timeval)])) == NULL) {
1260             fprintf(stderr, "%s: Out of memory\n", progname);
1261             return bl;
1262         }
1263
1264 # if 0  /* Don't need to do this -- the host names are already as
1265            resolved as they're going to get.  (We stored the resolved
1266            name in the outgoing ping packet, so that same string just
1267            came back to us.)
1268          */
1269
1270         /* If the name is an IP addr, try to resolve it. */
1271         {
1272           int iip[4];
1273           char c;
1274           if (4 == sscanf(name, " %d.%d.%d.%d %c",
1275                           &iip[0], &iip[1], &iip[2], &iip[3], &c))
1276             {
1277               struct sockaddr_in iaddr;
1278               struct hostent *h;
1279               iaddr.sin_addr.s_addr = pack_addr (iip[0],iip[1],iip[2],iip[3]);
1280               if (resolve_p)
1281                 h = gethostbyaddr ((const char *) &iaddr.sin_addr.s_addr,
1282                                    sizeof(iaddr.sin_addr.s_addr),
1283                                    AF_INET);
1284               else
1285                 h = 0;
1286
1287               if (h && h->h_name && *h->h_name)
1288                 {
1289                   free (name);
1290                   name = strdup (h->h_name);
1291                 }
1292             }
1293         }
1294 # endif /* 0 */
1295
1296         /* Create the new Bogie and add it to the list we are building */
1297
1298         if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
1299             return bl;
1300         new->next = bl;
1301         bl = new;
1302
1303         {
1304           float msec = delta(then, &now) / 1000.0;
1305
1306           if (times_p)
1307             {
1308               if (new->desc) free (new->desc);
1309               new->desc = (char *) malloc (30);
1310               if      (msec > 99) sprintf (new->desc, "    %.0f ms   ", msec);
1311               else if (msec >  9) sprintf (new->desc, "    %.1f ms   ", msec);
1312               else if (msec >  1) sprintf (new->desc, "    %.2f ms   ", msec);
1313               else                sprintf (new->desc, "    %.3f ms   ", msec);
1314             }
1315
1316           if (debug_p && times_p)  /* print ping-like stuff to stdout */
1317             {
1318               struct sockaddr_in *iaddr = (struct sockaddr_in *) &from;
1319               unsigned int a, b, c, d;
1320               char ipstr[20];
1321               char *s = strdup (new->desc);
1322               char *s2 = s, *s3 = s;
1323               while (*s2 == ' ') s2++;
1324               s3 = strchr (s2, ' ');
1325               if (s3) *s3 = 0;
1326
1327               unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
1328               sprintf (ipstr, "%d.%d.%d.%d", a, b, c, d);
1329
1330               fprintf (stdout,
1331                        "%3d bytes from %28s: "
1332                        "icmp_seq=%-4d ttl=%d time=%s ms\n",
1333                        result,
1334                        name,
1335                        /*ipstr,*/
1336                        ICMP_SEQ(icmph), si->TTL, s2);
1337               free (s);
1338             }
1339
1340           /* Don't put anyone *too* close to the center of the screen. */
1341           msec += 0.6;
1342
1343           new->distance = msec * 10;
1344         }
1345       }
1346     }
1347
1348     /* Done */
1349
1350     return bl;
1351 }
1352
1353 /*
1354  * Ping hosts.
1355  *
1356  * Args:
1357  *    si - Sonar Information.
1358  *    pi - Ping Information.
1359  *
1360  * Returns:
1361  *    A list of hosts that replied to pings or null if there were none.
1362  */
1363
1364 static Bogie *
1365 ping(sonar_info *si, void *vpi) 
1366 {
1367
1368     /*
1369      * This tries to distribute the targets evely around the field of the
1370      * sonar.
1371      */
1372
1373     ping_info *pi = (ping_info *) vpi;
1374     static ping_target *ptr = NULL;
1375
1376     int tick = si->current * -1 + 1;
1377     if ((ptr == NULL) && (tick == 1))
1378         ptr = pi->targets;
1379
1380     if (pi->numtargets <= 90) {
1381         int xdrant = 90 / pi->numtargets;
1382         if ((tick % xdrant) == 0) {
1383             if (ptr != (ping_target *) 0) {
1384                 sendping(pi, ptr);
1385                 ptr = ptr->next;
1386             }
1387         }
1388
1389     } else if (pi->numtargets > 90) {
1390         if (ptr != (ping_target *) 0) {
1391             sendping(pi, ptr);
1392             ptr = ptr->next;
1393         }
1394     }
1395
1396     /* Get the results */
1397
1398     return getping(si, pi);
1399 }
1400
1401 #endif /* HAVE_PING */
1402
1403 /*
1404  * Calculate the difference between two timevals in microseconds.
1405  *
1406  * Args:
1407  *    then - The older timeval.
1408  *    now  - The newer timeval.
1409  *
1410  * Returns:
1411  *   The difference between the two in microseconds.
1412  */
1413
1414 static long
1415 delta(struct timeval *then, struct timeval *now) 
1416 {
1417     return (((now->tv_sec - then->tv_sec) * 1000000) + 
1418                (now->tv_usec - then->tv_usec));  
1419 }
1420
1421 /*
1422  * Initialize the simulation mode.
1423  */
1424
1425 static sim_info *
1426 init_sim(void) 
1427 {
1428
1429     /* Local Variables */
1430
1431     sim_info *si;
1432     int i;
1433
1434     /* Create the simulation info structure */
1435
1436     if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1437         fprintf(stderr, "%s: Out of memory\n", progname);
1438         return NULL;
1439     }
1440
1441     /* Team A */
1442
1443     si->numA = get_integer_resource("teamACount", "TeamACount");
1444     if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1445         == NULL) {
1446         free(si);
1447         fprintf(stderr, "%s: Out of Memory\n", progname);
1448         return NULL;
1449     }
1450     si->teamAID = get_string_resource("teamAName", "TeamAName");
1451     for (i = 0; i < si->numA; i++) {
1452         if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1453             == NULL) {
1454             free(si);
1455             fprintf(stderr, "%s: Out of Memory\n", progname);
1456             return NULL;
1457         }
1458         sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1459         si->teamA[i].nexttick = random() % 90;
1460         si->teamA[i].nextdist = random() % 100;
1461         si->teamA[i].movedonsweep = -1;
1462     }
1463
1464     /* Team B */
1465
1466     si->numB = get_integer_resource("teamBCount", "TeamBCount");
1467     if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1468         == NULL) {
1469         free(si);
1470         fprintf(stderr, "%s: Out of Memory\n", progname);
1471         return NULL;
1472     }
1473     si->teamBID = get_string_resource("teamBName", "TeamBName");
1474     for (i = 0; i < si->numB; i++) {
1475         if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1476             == NULL) {
1477             free(si);
1478             fprintf(stderr, "%s: Out of Memory\n", progname);
1479             return NULL;
1480         }
1481         sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1482         si->teamB[i].nexttick = random() % 90;
1483         si->teamB[i].nextdist = random() % 100;
1484         si->teamB[i].movedonsweep = -1;
1485     }
1486
1487     /* Done */
1488
1489     return si;
1490 }
1491
1492 /*
1493  * Creates and returns a drawing mask for the scope:
1494  * mask out anything outside of the disc.
1495  */
1496 static Pixmap
1497 scope_mask (Display *dpy, Window win, sonar_info *si)
1498 {
1499   XGCValues gcv;
1500   Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
1501   GC gc = XCreateGC (dpy, mask, 0, &gcv);
1502   XSetFunction (dpy, gc, GXclear);
1503   XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
1504   XSetFunction (dpy, gc, GXset);
1505   XFillArc(dpy, mask, gc, si->minx, si->miny, 
1506            si->maxx - si->minx, si->maxy - si->miny,
1507            0, 360 * 64);
1508   return mask;
1509 }
1510
1511
1512 static void
1513 reshape (sonar_info *si)
1514 {
1515   XWindowAttributes xgwa;
1516   Pixmap mask;
1517   XGetWindowAttributes(si->dpy, si->win, &xgwa);
1518   si->width = xgwa.width;
1519   si->height = xgwa.height;
1520   si->centrex = si->width / 2;
1521   si->centrey = si->height / 2;
1522   si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1523   si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1524   si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1525   si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1526   si->radius = si->maxx - si->centrex;
1527
1528   /* Install the clip mask... */
1529   mask = scope_mask (si->dpy, si->win, si);
1530   XSetClipMask(si->dpy, si->text, mask);
1531   XSetClipMask(si->dpy, si->erase, mask);
1532   XFreePixmap (si->dpy, mask); /* it's been copied into the GCs */
1533 }
1534
1535 /*
1536  * Initialize the Sonar.
1537  *
1538  * Args:
1539  *    dpy - The X display.
1540  *    win - The X window;
1541  *
1542  * Returns:
1543  *   A sonar_info strcuture or null if memory allocation problems occur.
1544  */
1545
1546 static sonar_info *
1547 init_sonar(Display *dpy, Window win) 
1548 {
1549
1550     /* Local Variables */
1551
1552     XGCValues gcv;
1553     XWindowAttributes xwa;
1554     sonar_info *si;
1555     XColor start, end;
1556     int h1, h2;
1557     double s1, s2, v1, v2;
1558
1559     /* Create the Sonar information structure */
1560
1561     if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1562         fprintf(stderr, "%s: Out of memory\n", progname);
1563         return NULL;
1564     }
1565
1566     /* Initialize the structure for the current environment */
1567
1568     si->dpy = dpy;
1569     si->win = win;
1570     si->visible = NULL;
1571
1572     XGetWindowAttributes(dpy, win, &xwa);
1573     si->cmap = xwa.colormap;
1574
1575     si->current = 0;
1576     si->sweepnum = 0;
1577
1578     /* Get the font */
1579
1580     if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1581          == NULL) &&
1582         ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1583         fprintf(stderr, "%s: can't load an appropriate font\n", progname);
1584         return NULL;
1585     }
1586
1587     /* Get the delay between animation frames */
1588
1589     si->delay = get_integer_resource ("delay", "Integer");
1590
1591     if (si->delay < 0) si->delay = 0;
1592     si->TTL = get_integer_resource("ttl", "TTL");
1593
1594     /* Create the Graphics Contexts that will be used to draw things */
1595
1596     gcv.foreground = 
1597         get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
1598     si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
1599     gcv.font = si->font->fid;
1600     si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
1601     gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
1602                                         dpy, si->cmap);
1603     si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1604     gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1605                                         dpy, si->cmap);
1606     si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1607
1608     reshape (si);
1609
1610     /* Compute pixel values for fading text on the display */
1611
1612     XParseColor(dpy, si->cmap, 
1613                 get_string_resource("textColor", "TextColor"), &start);
1614     XParseColor(dpy, si->cmap, 
1615                 get_string_resource("scopeColor", "ScopeColor"), &end);
1616
1617     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1618     rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
1619
1620     si->text_steps = get_integer_resource("textSteps", "TextSteps");
1621     if (si->text_steps < 0 || si->text_steps > 255)
1622       si->text_steps = 10;
1623
1624     si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1625     make_color_ramp (dpy, si->cmap,
1626                      h1, s1, v1,
1627                      h2, s2, v2,
1628                      si->text_colors, &si->text_steps,
1629                      False, True, False);
1630
1631     /* Compute the pixel values for the fading sweep */
1632
1633     XParseColor(dpy, si->cmap, 
1634                 get_string_resource("sweepColor", "SweepColor"), &start);
1635
1636     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1637
1638     si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
1639     if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
1640     if (si->sweep_degrees > 350) si->sweep_degrees = 350;
1641
1642     si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1643     if (si->sweep_segs < 1 || si->sweep_segs > 255)
1644       si->sweep_segs = 255;
1645
1646     si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1647     make_color_ramp (dpy, si->cmap,
1648                      h1, s1, v1,
1649                      h2, s2, v2,
1650                      si->sweep_colors, &si->sweep_segs,
1651                      False, True, False);
1652
1653     if (si->sweep_segs <= 0)
1654       si->sweep_segs = 1;
1655
1656     /* Done */
1657
1658     return si;
1659 }
1660
1661 /*
1662  * Update the location of a simulated bogie.
1663  */
1664
1665 static void
1666 updateLocation(sim_target *t) 
1667 {
1668
1669     int xdist, xtick;
1670
1671     xtick = (int) (random() %  3) - 1;
1672     xdist = (int) (random() % 11) - 5;
1673     if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1674         t->nexttick += xtick;
1675     else
1676         t->nexttick -= xtick;
1677     if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1678         t->nextdist += xdist;
1679     else
1680         t->nextdist -= xdist;
1681 }
1682
1683 /*
1684  * The simulator. This uses information in the sim_info to simulate a bunch
1685  * of bogies flying around on the screen.
1686  */
1687
1688 /*
1689  * TODO: It would be cool to have the two teams chase each other around and
1690  *       shoot it out.
1691  */
1692
1693 static Bogie *
1694 simulator(sonar_info *si, void *vinfo) 
1695 {
1696
1697     /* Local Variables */
1698
1699     int i;
1700     Bogie *list = NULL;
1701     Bogie *new;
1702     sim_target *t;
1703     sim_info *info = (sim_info *) vinfo;
1704
1705     /* Check team A */
1706
1707     for (i = 0; i < info->numA; i++) {
1708         t = &info->teamA[i];
1709         if ((t->movedonsweep != si->sweepnum) &&
1710             (t->nexttick == (si->current * -1))) {
1711             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1712             if (list != NULL)
1713                 new->next = list;
1714             list = new;
1715             updateLocation(t);
1716             t->movedonsweep = si->sweepnum;
1717         }
1718     }
1719
1720     /* Team B */
1721
1722     for (i = 0; i < info->numB; i++) {
1723         t = &info->teamB[i];
1724         if ((t->movedonsweep != si->sweepnum) &&
1725             (t->nexttick == (si->current * -1))) {
1726             new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1727             if (list != NULL)
1728                 new->next = list;
1729             list = new;
1730             updateLocation(t);
1731             t->movedonsweep = si->sweepnum;
1732         }
1733     }
1734
1735     /* Done */
1736
1737     return list;
1738 }
1739
1740 /*
1741  * Compute the X coordinate of the label.
1742  *
1743  * Args:
1744  *    si - The sonar info block.
1745  *    label - The label that will be drawn.
1746  *    x - The x coordinate of the bogie.
1747  *
1748  * Returns:
1749  *    The x coordinate of the start of the label.
1750  */
1751
1752 static int
1753 computeStringX(sonar_info *si, const char *label, int x) 
1754 {
1755
1756     int width = XTextWidth(si->font, label, strlen(label));
1757     return x - (width / 2);
1758 }
1759
1760 /*
1761  * Compute the Y coordinate of the label.
1762  *
1763  * Args:
1764  *    si - The sonar information.
1765  *    y - The y coordinate of the bogie.
1766  *
1767  * Returns:
1768  *    The y coordinate of the start of the label.
1769  */
1770
1771 static int
1772 computeStringY(sonar_info *si, int y) 
1773 {
1774
1775     int fheight = si->font->ascent /* + si->font->descent */;
1776     return y + fheight;
1777 }
1778
1779 /*
1780  * Draw a Bogie on the radar screen.
1781  *
1782  * Args:
1783  *    si       - Sonar Information.
1784  *    draw     - A flag to indicate if the bogie should be drawn or erased.
1785  *    name     - The name of the bogie.
1786  *    degrees  - The number of degrees that it should apprear at.
1787  *    distance - The distance the object is from the centre.
1788  *    ttl      - The time this bogie has to live.
1789  *    age      - The time this bogie has been around.
1790  */
1791
1792 static void
1793 DrawBogie(sonar_info *si, int draw, const char *name, const char *desc,
1794           int degrees, int distance, int ttl, int age) 
1795 {
1796
1797     /* Local Variables */
1798
1799     int x, y;
1800     GC gc;
1801     int ox = si->centrex;
1802     int oy = si->centrey;
1803     int index, delta;
1804
1805     /* Compute the coordinates of the object */
1806
1807     if (distance != 0)
1808       distance = (log((double) distance) / 10.0) * si->radius;
1809     x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1810     y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1811
1812     /* Set up the graphics context */
1813
1814     if (draw) {
1815
1816         /* Here we attempt to compute the distance into the total life of
1817          * object that we currently are. This distance is used against
1818          * the total lifetime to compute a fraction which is the index of
1819          * the color to draw the bogie.
1820          */
1821
1822         if (si->current <= degrees)
1823             delta = (si->current - degrees) * -1;
1824         else
1825             delta = 90 + (degrees - si->current);
1826         delta += (age * 90);
1827         index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1828         gc = si->text;
1829         XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1830
1831     } else
1832         gc = si->erase;
1833
1834   /* Draw (or erase) the Bogie */
1835
1836     XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1837
1838     x += 3;  /* move away from the dot */
1839     y += 7;
1840     y = computeStringY(si, y);
1841     XDrawString(si->dpy, si->win, gc,
1842                 computeStringX(si, name, x), y,
1843                 name, strlen(name));
1844
1845     if (desc && *desc)
1846       {
1847         y = computeStringY(si, y);
1848         XDrawString(si->dpy, si->win, gc,
1849                     computeStringX(si, desc, x), y,
1850                     desc, strlen(desc));
1851       }
1852 }
1853
1854
1855 /*
1856  * Draw the sonar grid.
1857  *
1858  * Args:
1859  *    si - Sonar information block.
1860  */
1861
1862 static void
1863 drawGrid(sonar_info *si) 
1864 {
1865
1866     /* Local Variables */
1867
1868     int i;
1869     int width = si->maxx - si->minx;
1870     int height = si->maxy - si->miny;
1871   
1872     /* Draw the circles */
1873
1874     XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10, 
1875              width + 20, height + 20,  0, (360 * 64));
1876
1877     XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny, 
1878              width, height,  0, (360 * 64));
1879
1880     XDrawArc(si->dpy, si->win, si->grid, 
1881              (int) (si->minx + (.166 * width)), 
1882              (int) (si->miny + (.166 * height)), 
1883              (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1884              0, (360 * 64));
1885
1886     XDrawArc(si->dpy, si->win, si->grid, 
1887              (int) (si->minx + (.333 * width)),
1888              (int) (si->miny + (.333 * height)), 
1889              (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1890              0, (360 * 64));
1891
1892     /* Draw the radial lines */
1893
1894     for (i = 0; i < 360; i += 10)
1895         if (i % 30 == 0)
1896             XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1897                       (int) (si->centrex +
1898                       (si->radius + 10) * (cos((double) i / 57.29578))),
1899                       (int) (si->centrey -
1900                       (si->radius + 10)*(sin((double) i / 57.29578))));
1901         else
1902             XDrawLine(si->dpy, si->win, si->grid, 
1903                       (int) (si->centrex + si->radius *
1904                              (cos((double) i / 57.29578))),
1905                       (int) (si->centrey - si->radius *
1906                              (sin((double) i / 57.29578))),
1907                       (int) (si->centrex +
1908                       (si->radius + 10) * (cos((double) i / 57.29578))),
1909                       (int) (si->centrey - 
1910                       (si->radius + 10) * (sin((double) i / 57.29578))));
1911 }
1912
1913 /*
1914  * Update the Sonar scope.
1915  *
1916  * Args:
1917  *    si - The Sonar information.
1918  *    bl - A list  of bogies to add to the scope.
1919  */
1920
1921 static void
1922 Sonar(sonar_info *si, Bogie *bl) 
1923 {
1924
1925     /* Local Variables */
1926
1927     Bogie *bp, *prev;
1928     int i;
1929
1930     /* Check for expired tagets and remove them from the visible list */
1931
1932     prev = NULL;
1933     for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) {
1934
1935         /*
1936          * Remove it from the visible list if it's expired or we have
1937          * a new target with the same name.
1938          */
1939
1940         bp->age ++;
1941
1942         if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1943             (findNode(bl, bp->name) != NULL)) {
1944             DrawBogie(si, 0, bp->name, bp->desc, bp->tick,
1945                       bp->distance, bp->ttl, bp->age);
1946             if (prev == NULL)
1947                 si->visible = bp->next;
1948             else
1949                 prev->next = bp->next;
1950             freeBogie(bp);
1951             bp = prev;
1952         } else
1953             prev = bp;
1954     }
1955
1956     /* Draw the sweep */
1957
1958     {
1959       int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
1960       int start_deg = si->current * 4 * 64;
1961       if (seg_deg <= 0) seg_deg = 1;
1962       for (i = 0; i < si->sweep_segs; i++) {
1963         XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
1964         XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny, 
1965                  si->maxx - si->minx, si->maxy - si->miny,
1966                  start_deg + (i * seg_deg),
1967                  seg_deg);
1968       }
1969
1970       /* Remove the trailing wedge the sonar */
1971       XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny, 
1972                si->maxx - si->minx, si->maxy - si->miny, 
1973                start_deg + (i * seg_deg),
1974                (4 * 64));
1975     }
1976
1977     /* Move the new targets to the visible list */
1978
1979     for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1980         bl = bl->next;
1981         bp->next = si->visible;
1982         si->visible = bp;
1983     }
1984
1985     /* Draw the visible targets */
1986
1987     for (bp = si->visible; bp != NULL; bp = bp->next) {
1988         if (bp->age < bp->ttl)          /* grins */
1989            DrawBogie(si, 1, bp->name, bp->desc,
1990                      bp->tick, bp->distance, bp->ttl,bp->age);
1991     }
1992
1993     /* Redraw the grid */
1994
1995     drawGrid(si);
1996 }
1997
1998
1999 static ping_target *
2000 parse_mode (Bool ping_works_p)
2001 {
2002   char *source = get_string_resource ("ping", "Ping");
2003   char *token, *end;
2004   char dummy;
2005
2006   ping_target *hostlist = 0;
2007
2008   if (!source) source = strdup("");
2009
2010   if (!*source || !strcmp (source, "default"))
2011     {
2012 # ifdef HAVE_PING
2013       if (ping_works_p)         /* if root or setuid, ping will work. */
2014         source = strdup("subnet/29,/etc/hosts");
2015       else
2016 # endif
2017         source = strdup("simulation");
2018     }
2019
2020   token = source;
2021   end = source + strlen(source);
2022   while (token < end)
2023     {
2024       char *next;
2025 # ifdef HAVE_PING
2026       ping_target *new;
2027       struct stat st;
2028       unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
2029       char d;
2030 # endif /* HAVE_PING */
2031
2032       for (next = token;
2033            *next &&
2034            *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
2035            next++)
2036         ;
2037       *next = 0;
2038
2039
2040       if (debug_p)
2041         fprintf (stderr, "%s: parsing %s\n", progname, token);
2042
2043       if (!strcmp (token, "simulation"))
2044         return 0;
2045
2046       if (!ping_works_p)
2047         {
2048           fprintf(stderr,
2049            "%s: this program must be setuid to root for `ping mode' to work.\n"
2050              "       Running in `simulation mode' instead.\n",
2051                   progname);
2052           return 0;
2053         }
2054
2055 #ifdef HAVE_PING
2056       if ((4 == sscanf (token, "%u.%u.%u/%u %c",    &n0,&n1,&n2,    &m,&d)) ||
2057           (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
2058         {
2059           /* subnet: A.B.C.D/M
2060              subnet: A.B.C/M
2061            */
2062           unsigned long ip = pack_addr (n0, n1, n2, n3);
2063           new = subnetHostsList(ip, m);
2064         }
2065       else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
2066         {
2067           /* IP: A.B.C.D
2068            */
2069           new = newHost (token);
2070         }
2071       else if (!strcmp (token, "subnet"))
2072         {
2073           new = subnetHostsList(0, 24);
2074         }
2075       else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
2076         {
2077           new = subnetHostsList(0, m);
2078         }
2079       else if (*token == '.' || *token == '/' || !stat (token, &st))
2080         {
2081           /* file name
2082            */
2083           new = readPingHostsFile (token);
2084         }
2085       else
2086         {
2087           /* not an existant file - must be a host name
2088            */
2089           new = newHost (token);
2090         }
2091
2092       if (new)
2093         {
2094           ping_target *nn = new;
2095           while (nn && nn->next)
2096             nn = nn->next;
2097           nn->next = hostlist;
2098           hostlist = new;
2099
2100           sensor = ping;
2101         }
2102 #endif /* HAVE_PING */
2103
2104       token = next + 1;
2105       while (token < end &&
2106              (*token == ',' || *token == ' ' ||
2107               *token == '\t' || *token == '\n'))
2108         token++;
2109     }
2110
2111   return hostlist;
2112 }
2113
2114
2115 static void
2116 handle_events (sonar_info *si)
2117 {
2118   while (XPending (si->dpy))
2119     {
2120       XEvent event;
2121       XNextEvent (si->dpy, &event);
2122
2123       if (event.xany.type == ConfigureNotify)
2124         {
2125           XClearWindow (si->dpy, si->win);
2126           reshape (si);
2127         }
2128
2129       screenhack_handle_event (si->dpy, &event);
2130     }
2131 }
2132
2133
2134
2135 /*
2136  * Main screen saver hack.
2137  *
2138  * Args:
2139  *    dpy - The X display.
2140  *    win - The X window.
2141  */
2142
2143 void 
2144 screenhack(Display *dpy, Window win) 
2145 {
2146
2147     /* Local Variables */
2148
2149     sonar_info *si;
2150     struct timeval start, finish;
2151     Bogie *bl;
2152     long sleeptime;
2153
2154     debug_p = get_boolean_resource ("debug", "Debug");
2155     resolve_p = get_boolean_resource ("resolve", "Resolve");
2156     times_p = get_boolean_resource ("showTimes", "ShowTimes");
2157
2158     sensor = 0;
2159 # ifdef HAVE_PING
2160     sensor_info = (void *) init_ping();
2161 # else  /* !HAVE_PING */
2162     sensor_info = 0;
2163     parse_mode (0);  /* just to check argument syntax */
2164 # endif /* !HAVE_PING */
2165
2166     if (sensor == 0)
2167       {
2168         sensor = simulator;
2169         if ((sensor_info = (void *) init_sim()) == NULL)
2170           exit(1);
2171       }
2172
2173     if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
2174         exit(1);
2175
2176
2177     /* Sonar loop */
2178
2179     while (1) {
2180
2181         /* Call the sensor and display the results */
2182
2183 # ifdef GETTIMEOFDAY_TWO_ARGS
2184         gettimeofday(&start, (struct timezone *) 0);
2185 # else
2186         gettimeofday(&start);
2187 # endif
2188         bl = sensor(si, sensor_info);
2189         Sonar(si, bl);
2190
2191         /* Set up and sleep for the next one */
2192
2193         si->current = (si->current - 1) % 90;
2194         if (si->current == 0)
2195           si->sweepnum++;
2196         XSync (dpy, False);
2197 # ifdef GETTIMEOFDAY_TWO_ARGS
2198         gettimeofday(&finish, (struct timezone *) 0);
2199 # else
2200         gettimeofday(&finish);
2201 # endif
2202         sleeptime = si->delay - delta(&start, &finish);
2203         handle_events (si);
2204         if (sleeptime > 0L)
2205             usleep(sleeptime);
2206
2207     }
2208 }