source: trunk/third/xscreensaver/hacks/sonar.c @ 20158

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