X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fsonar.c;h=d6c4aec9e7111c34e718b6cb61d806a9fffb27ef;hb=49f5b54f312fe4ac2e9bc47581a72451bd0e8439;hp=c4169f663dbd362d0a02dc8d492f40fb951d38c9;hpb=72c1f4c1dc6ab07fe121a327ff1c30bf51ef74c1;p=xscreensaver diff --git a/hacks/sonar.c b/hacks/sonar.c index c4169f66..d6c4aec9 100644 --- a/hacks/sonar.c +++ b/hacks/sonar.c @@ -1,4 +1,12 @@ /* sonar.c --- Simulate a sonar screen. + * Copyright (C) 1998-2006 by Stephen Martin and Jamie Zawinski + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. * * This is an implementation of a general purpose reporting tool in the * format of a Sonar display. It is designed such that a sensor is read @@ -16,10 +24,16 @@ * because, unfortunately, different systems have different ways of creating * these sorts of packets. * - * Also: creating an ICMP socket is a privileged operation, so the program - * needs to be installed SUID root if you want to use the ping mode. If you - * check the code you will see that this privilige is given up immediately - * after the socket is created. + * In order to use the ping sensor on most systems, this program must be + * installed as setuid root, so that it can create an ICMP RAW socket. Root + * privileges are disavowed shortly after startup (just after connecting to + * the X server and reading the resource database) so this is *believed* to + * be a safe thing to do, but it is usually recommended that you have as few + * setuid programs around as possible, on general principles. + * + * It is not necessary to make it setuid on MacOS systems, because on those + * systems, unprivileged programs can ping by using ICMP DGRAM sockets + * instead of ICMP RAW. * * It should be easy to extend this code to support other sorts of sensors. * Some ideas: @@ -28,19 +42,10 @@ * - plot the process table, by process size, cpu usage, or total time; * - plot the logged on users by idle time or cpu usage. * - * Copyright (C) 1998 by Stephen Martin (smartin@vanderfleet-martin.net). - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * $Revision: 1.14 $ + * $Revision: 1.57 $ * * Version 1.0 April 27, 1998. - * - Initial version + * - Initial version, by Stephen Martin * - Submitted to RedHat Screensaver Contest * * Version 1.1 November 3, 1998. @@ -75,6 +80,7 @@ * - Now need to define HAVE_PING to compile in the ping stuff. */ + /* These are computed by configure now: #define HAVE_ICMP #define HAVE_ICMPHDR @@ -86,11 +92,14 @@ #include #include #include +#include #include "screenhack.h" #include "colors.h" #include "hsv.h" +#undef usleep /* conflicts with unistd.h on OSX */ + #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR) # include # include @@ -142,6 +151,18 @@ # undef HAVE_PING #endif + +#ifdef HAVE_PING +# if defined(__DECC) || defined(_IP_VHL) + /* This is how you do it on DEC C, and possibly some BSD systems. */ +# define IP_HDRLEN(ip) ((ip)->ip_vhl & 0x0F) +# else + /* This is how you do it on everything else. */ +# define IP_HDRLEN(ip) ((ip)->ip_hl) +# endif +#endif /* HAVE_PING */ + + /* Forward References */ #ifdef HAVE_PING @@ -149,16 +170,18 @@ static u_short checksum(u_short *, int); #endif static long delta(struct timeval *, struct timeval *); + /* Data Structures */ /* * The Bogie. * - * This represents an object that is visable on the scope. + * This represents an object that is visible on the scope. */ typedef struct Bogie { char *name; /* The name of the thing being displayed */ + char *desc; /* Beneath the name (e.g., ping time) */ int distance; /* The distance to this thing (0 - 100) */ int tick; /* The tick that it was found on */ int ttl; /* The time to live */ @@ -172,7 +195,10 @@ typedef struct Bogie { * This contains all of the runtime information about the sonar scope. */ -typedef struct { +typedef struct ping_target ping_target; + +typedef struct sonar_info sonar_info; +struct sonar_info { Display *dpy; /* The X display */ Window win; /* The window */ GC hi, /* The leading edge of the sweep */ @@ -190,31 +216,40 @@ typedef struct { int width, height; /* Window dimensions */ int minx, miny, maxx, maxy, /* Bounds of the scope */ centrex, centrey, radius; /* Parts of the scope circle */ - Bogie *visable; /* List of visable objects */ + Bogie *visible; /* List of visible objects */ int current; /* Current position of sweep */ int sweepnum; /* The current id of the sweep */ int delay; /* how long between each frame of the anim */ -} sonar_info; + int TTL; /* The number of ticks that bogies are visible + on the screen before they fade away. */ -/* - * Variables to support the differnt Sonar modes. - */ + ping_target *last_ptr; + + Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */ + void *sensor_info; /* Information about the sensor */ + +}; + +static Bool debug_p = False; +static Bool resolve_p = True; +static Bool times_p = True; -Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */ -void *sensor_info; /* Information about the sensor */ /* * A list of targets to ping. */ -#ifdef HAVE_PING -typedef struct ping_target { +struct ping_target { char *name; /* The name of the target */ +#ifdef HAVE_PING struct sockaddr address; /* The address of the target */ +#endif /* HAVE_PING */ struct ping_target *next; /* The next one in the list */ -} ping_target; +}; + +#ifdef HAVE_PING /* * Ping Information. * @@ -262,70 +297,7 @@ typedef struct { char *teamBID; /* The identifier for bogies in team B */ } sim_info; -/* Name of the Screensaver hack */ - -char *progclass="sonar"; - -/* Application Defaults */ - -char *defaults [] = { - ".background: #000000", - ".sweepColor: #00FF00", - "*delay: 100000", - "*scopeColor: #003300", - "*gridColor: #00AA00", - "*textColor: #FFFF00", - "*ttl: 90", - "*mode: default", - "*font: fixed", - "*sweepDegrees: 30", - - "*textSteps: 80", /* npixels */ - "*sweepSegments: 80", /* npixels */ - -#ifdef HAVE_PING - "*pingTimeout: 3000", - "*pingSource: file", - "*pingFile: /etc/hosts", - "*pingList: localhost", -#endif /* HAVE_PING */ - "*teamAName: F18", - "*teamBName: MIG", - "*teamACount: 4", - "*teamBCount: 4", - 0 -}; - -/* Options passed to this program */ -XrmOptionDescRec options [] = { - {"-background", ".background", XrmoptionSepArg, 0 }, - {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 }, - {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 }, - {"-grid-color", ".gridColor", XrmoptionSepArg, 0 }, - {"-text-color", ".textColor", XrmoptionSepArg, 0 }, - {"-ttl", ".ttl", XrmoptionSepArg, 0 }, - {"-mode", ".mode", XrmoptionSepArg, 0 }, - {"-font", ".font", XrmoptionSepArg, 0 }, -#ifdef HAVE_PING - {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 }, - {"-ping-source", ".pingSource", XrmoptionSepArg, 0 }, - {"-ping-file", ".pingFile", XrmoptionSepArg, 0 }, - {"-ping-list", ".pingList", XrmoptionSepArg, 0 }, -#endif /* HAVE_PING */ - {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 }, - {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 }, - {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 }, - {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 }, - { 0, 0, 0, 0 } -}; - -/* - * The number of ticks that bogies are visable on the screen before they - * fade away. - */ - -static int TTL; /* * Create a new Bogie and set some initial values. @@ -348,6 +320,7 @@ newBogie(char *name, int distance, int tick, int ttl) Bogie *new; + distance *= 1000; /* Allocate a bogie and initialize it */ if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) { @@ -370,6 +343,7 @@ newBogie(char *name, int distance, int tick, int ttl) * b - The bogie to free. */ + static void freeBogie(Bogie *b) { @@ -420,6 +394,31 @@ findNode(Bogie *bl, char *name) #ifdef HAVE_PING +/* Packs an IP address quad into bigendian network order. */ +static unsigned long +pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d) +{ + unsigned long i = (((a & 255) << 24) | + ((b & 255) << 16) | + ((c & 255) << 8) | + ((d & 255) )); + return htonl (i); +} + +/* Unpacks an IP address quad from bigendian network order. */ +static void +unpack_addr (unsigned long addr, + unsigned int *a, unsigned int *b, + unsigned int *c, unsigned int *d) +{ + addr = ntohl (addr); + *a = (addr >> 24) & 255; + *b = (addr >> 16) & 255; + *c = (addr >> 8) & 255; + *d = (addr ) & 255; +} + + /* * Lookup the address for a ping target; * @@ -433,35 +432,95 @@ findNode(Bogie *bl, char *name) static int lookupHost(ping_target *target) { + struct hostent *hent; + struct sockaddr_in *iaddr; - /* Local Variables */ + unsigned int ip[4]; + char c; + + iaddr = (struct sockaddr_in *) &(target->address); + iaddr->sin_family = AF_INET; + + if (4 == sscanf (target->name, " %u.%u.%u.%u %c", + &ip[0], &ip[1], &ip[2], &ip[3], &c)) + { + /* It's an IP address. + */ + if (ip[3] == 0) + { + if (debug_p > 1) + fprintf (stderr, "%s: ignoring bogus IP %s\n", + progname, target->name); + return 0; + } - struct sockaddr_in *iaddr; + iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]); + if (resolve_p) + hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr, + sizeof(iaddr->sin_addr.s_addr), + AF_INET); + else + hent = 0; + + if (debug_p > 1) + fprintf (stderr, "%s: %s => %s\n", + progname, target->name, + ((hent && hent->h_name && *hent->h_name) + ? hent->h_name : "")); + + if (hent && hent->h_name && *hent->h_name) + target->name = strdup (hent->h_name); + } + else + { + /* It's a host name. + */ - /* Set up the target address we first assume that the name is the - IP address as a string */ - iaddr = (struct sockaddr_in *) &(target->address); - iaddr->sin_family = AF_INET; - if ((iaddr->sin_addr.s_addr = inet_addr(target->name)) == -1) { + /* don't waste time being confused by non-hostname tokens + in .ssh/known_hosts */ + if (!strcmp (target->name, "ssh-rsa") || + !strcmp (target->name, "ssh-dsa") || + !strcmp (target->name, "ssh-dss") || + strlen (target->name) >= 80) + return 0; - /* Conversion of IP address failed, try to look the host up by name */ + hent = gethostbyname (target->name); + if (!hent) + { + if (debug_p) + fprintf (stderr, "%s: could not resolve host: %s\n", + progname, target->name); + return 0; + } - struct hostent *hent = gethostbyname(target->name); - if (hent == NULL) { - fprintf(stderr, "%s: could not resolve host %s\n", - progname, target->name); - return 0; - } - memcpy(&iaddr->sin_addr, hent->h_addr_list[0], - sizeof(iaddr->sin_addr)); + memcpy (&iaddr->sin_addr, hent->h_addr_list[0], + sizeof(iaddr->sin_addr)); + + if (debug_p > 1) + { + unsigned int a, b, c, d; + unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d); + fprintf (stderr, "%s: %s => %d.%d.%d.%d\n", + progname, target->name, a, b, c, d); + } } + return 1; +} - /* Done */ - return 1; +static void +print_host (FILE *out, unsigned long ip, const char *name) +{ + char ips[50]; + unsigned int a, b, c, d; + unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */ + sprintf (ips, "%u.%u.%u.%u", a, b, c, d); + if (!name || !*name) name = ""; + fprintf (out, "%-16s %s\n", ips, name); } + /* * Create a target for a host. * @@ -496,8 +555,43 @@ newHost(char *name) if (! lookupHost(target)) goto target_init_error; + /* Don't ever use loopback (127.0.0.x) hosts */ + { + struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address); + unsigned long ip = iaddr->sin_addr.s_addr; + + if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */ + { + if (debug_p) + fprintf (stderr, "%s: ignoring loopback host %s\n", + progname, target->name); + goto target_init_error; + } + } + + /* Don't ever use broadcast (255.x.x.x) hosts */ + { + struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address); + unsigned long ip = iaddr->sin_addr.s_addr; + if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */ + { + if (debug_p) + fprintf (stderr, "%s: ignoring broadcast host %s\n", + progname, target->name); + goto target_init_error; + } + } + /* Done */ + if (debug_p) + { + struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address); + unsigned long ip = iaddr->sin_addr.s_addr; + fprintf (stderr, "%s: added ", progname); + print_host (stderr, ip, target->name); + } + return target; /* Handle errors here */ @@ -522,7 +616,6 @@ target_init_error: static ping_target * readPingHostsFile(char *fname) { - /* Local Variables */ FILE *fp; @@ -539,15 +632,34 @@ readPingHostsFile(char *fname) return NULL; } + /* Kludge: on OSX, variables have not been expanded in the command + line arguments, so as a special case, allow the string to begin + with literal "$HOME/" or "~/". + + This is so that the "Known Hosts" menu item in sonar.xml works. + */ + if (!strncmp(fname, "~/", 2) || !strncmp(fname, "$HOME/", 6)) { + char *s = strchr (fname, '/'); + strcpy (buf, getenv("HOME")); + strcat (buf, s); + fname = buf; + } + /* Open the file */ if ((fp = fopen(fname, "r")) == NULL) { char msg[1024]; - sprintf(msg, "%s: unable to open host file %s", progname, fname); - perror(msg); + sprintf(msg, "%s: unable to open host file %s", progname, fname); +#ifdef HAVE_COCOA + if (debug_p) /* on OSX don't syslog this */ +#endif + perror(msg); return NULL; } + if (debug_p) + fprintf (stderr, "%s: reading file %s\n", progname, fname); + /* Read the file line by line */ while ((p = fgets(buf, LINE_MAX, fp)) != NULL) { @@ -567,8 +679,8 @@ readPingHostsFile(char *fname) /* Get the name and address */ name = addr = NULL; - if ((addr = strtok(buf, " \t\n")) != NULL) - name = strtok(NULL, " \t\n"); + if ((addr = strtok(buf, " ,;\t\n")) != NULL) + name = strtok(NULL, " ,;\t\n"); else continue; @@ -586,7 +698,22 @@ readPingHostsFile(char *fname) addr = NULL; } } - /*printf ("\"%s\" \"%s\"\n", name, addr);*/ + + /* If the name is all digits, it's not a name. */ + if (name) + { + const char *s; + for (s = name; *s; s++) + if (*s < '0' || *s > '9') + break; + if (! *s) + { + if (debug_p > 1) + fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n", + progname, name, addr); + name = NULL; + } + } /* Create a new target using first the name then the address */ @@ -610,59 +737,59 @@ readPingHostsFile(char *fname) return list; } -/* - * Generate a list of ping targets from the entries in a string. - * - * Args: - * list - A list of comma separated host names. - * - * Returns: - * A list of targets to ping or null if an error occured. - */ static ping_target * -readPingHostsList(char *list) +delete_duplicate_hosts (ping_target *list) { + ping_target *head = list; + ping_target *rest; - /* Local Variables */ - - char *host; - ping_target *hostlist = NULL; - ping_target *new; + for (rest = head; rest; rest = rest->next) + { + struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address); + unsigned long ip1 = i1->sin_addr.s_addr; - /* Check that there is a list */ + ping_target *rest2; + for (rest2 = rest; rest2; rest2 = rest2->next) + { + if (rest2 && rest2->next) + { + struct sockaddr_in *i2 = (struct sockaddr_in *) + &(rest2->next->address); + unsigned long ip2 = i2->sin_addr.s_addr; - if ((list == NULL) || (list[0] == '\0')) - return NULL; + if (ip1 == ip2) + { + if (debug_p) + { + fprintf (stderr, "%s: deleted duplicate: ", progname); + print_host (stderr, ip2, rest2->next->name); + } + rest2->next = rest2->next->next; + } + } + } + } - /* Loop through the hosts and add them to the list to return */ + return head; +} - host = strtok(list, ","); - while (host != NULL) { - new = newHost(host); - if (new != NULL) { - new->next = hostlist; - hostlist = new; - } - host = strtok(NULL, ","); - } - /* Done */ - return hostlist; -} /* * Generate a list ping targets consisting of all of the entries on - * the same subnet. + * the same subnet. 'base' ip is in network order; 0 means localhost. * * Returns: * A list of all of the hosts on this net. */ static ping_target * -subnetHostsList(void) +subnetHostsList(unsigned long n_base, int subnet_width) { + unsigned long h_mask; /* host order */ + unsigned long h_base; /* host order */ /* Local Variables */ @@ -674,6 +801,26 @@ subnetHostsList(void) ping_target *new; ping_target *list = NULL; + if (subnet_width < 24) + { + fprintf (stderr, + "%s: pinging %lu hosts is a bad idea; please use a subnet mask of 24 bits\n" + " or more (255 hosts max.)\n", + progname, (unsigned long) (1L << (32 - subnet_width)) - 1); + exit (1); + } + else if (subnet_width > 30) + { + fprintf (stderr, "%s: a subnet of %d bits doesn't make sense:" + " try \"subnet/24\" or \"subnet/29\".\n", + progname, subnet_width); + exit (1); + } + + + if (debug_p) + fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width); + /* Get our hostname */ if (gethostname(hostname, BUFSIZ)) { @@ -689,29 +836,69 @@ subnetHostsList(void) } strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0]))); - /* Get a pointer to the last "." in the string */ + /* Construct targets for all addresses in this subnet */ - if ((p = strrchr(address, '.')) == NULL) { - fprintf(stderr, "%s: can't parse IP address %s\n", progname, address); - return NULL; - } - p++; + h_mask = 0; + for (i = 0; i < subnet_width; i++) + h_mask |= (1L << (31-i)); - /* Construct targets for all addresses in this subnet */ + /* If no base IP specified, assume localhost. */ + if (n_base == 0) + n_base = pack_addr (hent->h_addr_list[0][0], + hent->h_addr_list[0][1], + hent->h_addr_list[0][2], + hent->h_addr_list[0][3]); + h_base = ntohl (n_base); - /* #### jwz: actually, this is wrong, since it assumes a - netmask of 255.255.255.0. But I'm not sure how to find - the local netmask. - */ - for (i = 254; i > 0; i--) { + if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */ + { + unsigned int a, b, c, d; + unpack_addr (n_base, &a, &b, &c, &d); + fprintf (stderr, + "%s: unable to determine local subnet address: \"%s\"\n" + " resolves to loopback address %u.%u.%u.%u.\n", + progname, hostname, a, b, c, d); + return NULL; + } + + for (i = 255; i >= 0; i--) { + unsigned int a, b, c, d; + int ip = (h_base & 0xFFFFFF00L) | i; /* host order */ + + if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */ + continue; + if ((ip & ~h_mask) == 0) /* broadcast address */ + continue; + if ((ip & ~h_mask) == ~h_mask) /* broadcast address */ + continue; + + unpack_addr (htonl (ip), &a, &b, &c, &d); + sprintf (address, "%u.%u.%u.%u", a, b, c, d); + + if (debug_p > 1) + { + unsigned int aa, ab, ac, ad; + unsigned int ma, mb, mc, md; + unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad); + unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md); + fprintf (stderr, + "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n", + progname, address, + aa, ab, ac, ad, + ma, mb, mc, md, + subnet_width); + } + + p = address + strlen(address) + 1; sprintf(p, "%d", i); + new = newHost(address); if (new != NULL) { new->next = list; list = new; } } - + /* Done */ return list; @@ -724,14 +911,24 @@ subnetHostsList(void) * A newly allocated ping_info structure or null if an error occured. */ +static ping_target *parse_mode (sonar_info *, Bool ping_works_p); + +/* yes, there is only one, even when multiple savers are running in the + same address space - since we can only open this socket before dropping + privs. + */ +static int global_icmpsock = 0; + static ping_info * -init_ping(void) +init_ping(sonar_info *si) { + Bool socket_initted_p = False; + Bool socket_raw_p = False; + /* Local Variables */ ping_info *pi = NULL; /* The new ping_info struct */ - char *src; /* The source of the ping hosts */ ping_target *pt; /* Used to count the targets */ /* Create the ping info structure */ @@ -741,61 +938,76 @@ init_ping(void) goto ping_init_error; } - /* Create the ICMP socket and turn off SUID */ - - if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { - char msg[1024]; - sprintf(msg, "%s: can't create ICMP socket", progname); - perror(msg); - fprintf(stderr, - "%s: this program must be setuid to root for `ping mode' to work.\n", - progname); - goto ping_init_error; - } - setuid(getuid()); - pi->pid = getpid() & 0xFFFF; - pi->seq = 0; - pi->timeout = get_integer_resource("pingTimeout", "PingTimeout"); + /* Create the ICMP socket. Do this before dropping privs. - /* Generate a list of targets */ + Raw sockets can only be opened by root (or setuid root), so we + only try to do this when the effective uid is 0. - src = get_string_resource("pingSource", "PingSource"); - if (strcmp(src, "file") == 0) { + We used to just always try, and notice the failure. But apparently + that causes "SELinux" to log spurious warnings when running with the + "strict" policy. So to avoid that, we just don't try unless we + know it will work. - /* - * The list of ping targets is to come from a file in - * /etc/hosts format - */ + On MacOS X, we can avoid the whole problem by using a + non-privileged datagram instead of a raw socket. + */ + if (global_icmpsock) { + pi->icmpsock = global_icmpsock; + socket_initted_p = True; + if (debug_p) + fprintf (stderr, "%s: re-using icmp socket\n", progname); + + } else if ((pi->icmpsock = + socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0) { + socket_initted_p = True; + + } else if (geteuid() == 0 && + (pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) { + socket_initted_p = True; + socket_raw_p = True; + } - pi->targets = readPingHostsFile(get_string_resource("pingFile", - "PingFile")); + if (socket_initted_p) { + global_icmpsock = pi->icmpsock; + socket_initted_p = True; + if (debug_p) + fprintf (stderr, "%s: opened %s icmp socket\n", progname, + (socket_raw_p ? "raw" : "dgram")); + } else if (debug_p) + fprintf (stderr, "%s: unable to open icmp socket\n", progname); - } else if (strcmp(src, "list") == 0) { + /* Disavow privs */ - /* The list of hosts is to come from the pinghostlist resource */ + setuid(getuid()); - pi->targets = readPingHostsList(get_string_resource("pingList", - "PingList")); - } else if (strcmp(src, "subnet") == 0) { + pi->pid = getpid() & 0xFFFF; + pi->seq = 0; + pi->timeout = get_integer_resource(si->dpy, "pingTimeout", "PingTimeout"); - pi->targets = subnetHostsList(); + /* Generate a list of targets */ - } else { + pi->targets = parse_mode (si, socket_initted_p); + pi->targets = delete_duplicate_hosts (pi->targets); - /* Unknown source */ - fprintf(stderr, - "%s: pingSource must be `file', `list', or `subnet', not: %s\n", - progname, src); - exit (1); - } + if (debug_p) + { + ping_target *t; + fprintf (stderr, "%s: Target list:\n", progname); + for (t = pi->targets; t; t = t->next) + { + struct sockaddr_in *iaddr = (struct sockaddr_in *) &(t->address); + unsigned long ip = iaddr->sin_addr.s_addr; + fprintf (stderr, "%s: ", progname); + print_host (stderr, ip, t->name); + } + } /* Make sure there is something to ping */ if (pi->targets == NULL) { - fprintf(stderr, "%s: nothing to ping", progname); - goto ping_init_error; + goto ping_init_error; } /* Count the targets */ @@ -819,6 +1031,7 @@ ping_init_error: return NULL; } + /* * Ping a host. * @@ -856,8 +1069,13 @@ sendping(ping_info *pi, ping_target *pt) ICMP_CHECKSUM(icmph) = 0; ICMP_ID(icmph) = pi->pid; ICMP_SEQ(icmph) = pi->seq++; +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)], (struct timezone *) 0); +# else + gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]); +# endif + strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)], pt->name); ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz); @@ -955,13 +1173,13 @@ checksum(u_short *packet, int size) */ static Bogie * -getping(sonar_info *si, ping_info *pi, int ttl) +getping(sonar_info *si, ping_info *pi) { /* Local Variables */ struct sockaddr from; - int fromlen; + unsigned int fromlen; /* Posix says socklen_t, but that's not portable */ int result; u_char packet[1024]; struct timeval now; @@ -974,6 +1192,8 @@ getping(sonar_info *si, ping_info *pi, int ttl) char *name; struct sigaction sa; struct itimerval it; + fd_set rfds; + struct timeval tv; /* Set up a signal to interupt our wait for a packet */ @@ -999,22 +1219,34 @@ getping(sonar_info *si, ping_info *pi, int ttl) /* Wait for a result packet */ fromlen = sizeof(from); - while (! timer_expired && - (result = recvfrom(pi->icmpsock, packet, sizeof(packet), - 0, &from, &fromlen)) > 0) { + while (! timer_expired) { + tv.tv_usec=pi->timeout; + tv.tv_sec=0; +#if 0 + /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */ + FD_ZERO(&rfds); +#else + memset (&rfds, 0, sizeof(rfds)); +#endif + FD_SET(pi->icmpsock,&rfds); + /* only wait a little while, in case we raced with the timer expiration. + From Valentijn Sessink */ + if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) { + result = recvfrom(pi->icmpsock, packet, sizeof(packet), + 0, &from, &fromlen); /* Check the packet */ +# ifdef GETTIMEOFDAY_TWO_ARGS gettimeofday(&now, (struct timezone *) 0); +# else + gettimeofday(&now); +# endif ip = (struct ip *) packet; - - iphdrlen = ip->ip_hl << 2; - /* On DEC OSF1 4.0, the preceeding line needs to be - iphdrlen = (ip->ip_vhl & 0x0F) << 2; - but I don't know how to do this portably. -- jwz. - */ - + iphdrlen = IP_HDRLEN(ip) << 2; icmph = (struct ICMP *) &packet[iphdrlen]; + then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)]; + /* Was the packet a reply?? */ @@ -1040,6 +1272,12 @@ getping(sonar_info *si, ping_info *pi, int ttl) return bl; } +# if 0 /* Don't need to do this -- the host names are already as + resolved as they're going to get. (We stored the resolved + name in the outgoing ping packet, so that same string just + came back to us.) + */ + /* If the name is an IP addr, try to resolve it. */ { int iip[4]; @@ -1047,10 +1285,16 @@ getping(sonar_info *si, ping_info *pi, int ttl) if (4 == sscanf(name, " %d.%d.%d.%d %c", &iip[0], &iip[1], &iip[2], &iip[3], &c)) { - unsigned char ip[4]; + struct sockaddr_in iaddr; struct hostent *h; - ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3]; - h = gethostbyaddr ((char *) ip, 4, AF_INET); + iaddr.sin_addr.s_addr = pack_addr (iip[0],iip[1],iip[2],iip[3]); + if (resolve_p) + h = gethostbyaddr ((const char *) &iaddr.sin_addr.s_addr, + sizeof(iaddr.sin_addr.s_addr), + AF_INET); + else + h = 0; + if (h && h->h_name && *h->h_name) { free (name); @@ -1058,21 +1302,58 @@ getping(sonar_info *si, ping_info *pi, int ttl) } } } +# endif /* 0 */ /* Create the new Bogie and add it to the list we are building */ - if ((new = newBogie(name, 0, si->current, ttl)) == NULL) + if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL) return bl; new->next = bl; bl = new; - /* Compute the round trip time */ + { + float msec = delta(then, &now) / 1000.0; + + if (times_p) + { + if (new->desc) free (new->desc); + new->desc = (char *) malloc (30); + if (msec > 99) sprintf (new->desc, " %.0f ms ", msec); + else if (msec > 9) sprintf (new->desc, " %.1f ms ", msec); + else if (msec > 1) sprintf (new->desc, " %.2f ms ", msec); + else sprintf (new->desc, " %.3f ms ", msec); + } + + if (debug_p && times_p) /* print ping-like stuff to stdout */ + { + struct sockaddr_in *iaddr = (struct sockaddr_in *) &from; + unsigned int a, b, c, d; + char ipstr[20]; + char *s = strdup (new->desc); + char *s2 = s, *s3 = s; + while (*s2 == ' ') s2++; + s3 = strchr (s2, ' '); + if (s3) *s3 = 0; + + unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d); + sprintf (ipstr, "%d.%d.%d.%d", a, b, c, d); + + fprintf (stdout, + "%3d bytes from %28s: " + "icmp_seq=%-4d ttl=%d time=%s ms\n", + result, + name, + /*ipstr,*/ + ICMP_SEQ(icmph), si->TTL, s2); + free (s); + } + + /* Don't put anyone *too* close to the center of the screen. */ + msec += 0.6; - then = (struct timeval *) &packet[iphdrlen + - sizeof(struct ICMP)]; - new->distance = delta(then, &now) / 100; - if (new->distance == 0) - new->distance = 2; /* HACK */ + new->distance = msec * 10; + } + } } /* Done */ @@ -1101,31 +1382,30 @@ ping(sonar_info *si, void *vpi) */ ping_info *pi = (ping_info *) vpi; - static ping_target *ptr = NULL; int tick = si->current * -1 + 1; - if ((ptr == NULL) && (tick == 1)) - ptr = pi->targets; + if ((si->last_ptr == NULL) && (tick == 1)) + si->last_ptr = pi->targets; if (pi->numtargets <= 90) { int xdrant = 90 / pi->numtargets; if ((tick % xdrant) == 0) { - if (ptr != (ping_target *) 0) { - sendping(pi, ptr); - ptr = ptr->next; + if (si->last_ptr != (ping_target *) 0) { + sendping(pi, si->last_ptr); + si->last_ptr = si->last_ptr->next; } } } else if (pi->numtargets > 90) { - if (ptr != (ping_target *) 0) { - sendping(pi, ptr); - ptr = ptr->next; + if (si->last_ptr != (ping_target *) 0) { + sendping(pi, si->last_ptr); + si->last_ptr = si->last_ptr->next; } } /* Get the results */ - return getping(si, pi, TTL); + return getping(si, pi); } #endif /* HAVE_PING */ @@ -1152,15 +1432,18 @@ delta(struct timeval *then, struct timeval *now) * Initialize the simulation mode. */ +#define BELLRAND(x) (((random()%(x)) + (random()%(x)) + (random()%(x)))/3) + static sim_info * -init_sim(void) +init_sim(Display *dpy) { - /* Local Variables */ sim_info *si; int i; + int maxdist = 20; /* larger than this is off the (log) display */ + /* Create the simulation info structure */ if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) { @@ -1170,14 +1453,14 @@ init_sim(void) /* Team A */ - si->numA = get_integer_resource("teamACount", "TeamACount"); + si->numA = get_integer_resource(dpy, "teamACount", "TeamACount"); if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target))) == NULL) { free(si); fprintf(stderr, "%s: Out of Memory\n", progname); return NULL; } - si->teamAID = get_string_resource("teamAName", "TeamAName"); + si->teamAID = get_string_resource(dpy, "teamAName", "TeamAName"); for (i = 0; i < si->numA; i++) { if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4)) == NULL) { @@ -1186,21 +1469,21 @@ init_sim(void) return NULL; } sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1); - si->teamA[i].nexttick = (int) (90.0 * random() / RAND_MAX); - si->teamA[i].nextdist = (int) (100.0 * random() / RAND_MAX); + si->teamA[i].nexttick = random() % 90; + si->teamA[i].nextdist = BELLRAND(maxdist); si->teamA[i].movedonsweep = -1; } /* Team B */ - si->numB = get_integer_resource("teamBCount", "TeamBCount"); + si->numB = get_integer_resource(dpy, "teamBCount", "TeamBCount"); if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target))) == NULL) { free(si); fprintf(stderr, "%s: Out of Memory\n", progname); return NULL; } - si->teamBID = get_string_resource("teamBName", "TeamBName"); + si->teamBID = get_string_resource(dpy, "teamBName", "TeamBName"); for (i = 0; i < si->numB; i++) { if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4)) == NULL) { @@ -1209,8 +1492,8 @@ init_sim(void) return NULL; } sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1); - si->teamB[i].nexttick = (int) (90.0 * random() / RAND_MAX); - si->teamB[i].nextdist = (int) (100.0 * random() / RAND_MAX); + si->teamB[i].nexttick = random() % 90; + si->teamB[i].nextdist = BELLRAND(maxdist); si->teamB[i].movedonsweep = -1; } @@ -1220,130 +1503,52 @@ init_sim(void) } /* - * Initialize the Sonar. - * - * Args: - * dpy - The X display. - * win - The X window; - * - * Returns: - * A sonar_info strcuture or null if memory allocation problems occur. + * Creates and returns a drawing mask for the scope: + * mask out anything outside of the disc. */ +static Pixmap +scope_mask (Display *dpy, Window win, sonar_info *si) +{ + XGCValues gcv; + Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1); + GC gc; + gcv.foreground = 0; + gc = XCreateGC (dpy, mask, GCForeground, &gcv); + XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height); + XSetForeground (dpy, gc, 1); + XFillArc(dpy, mask, gc, si->minx, si->miny, + si->maxx - si->minx, si->maxy - si->miny, + 0, 360 * 64); + XFreeGC (dpy, gc); + return mask; +} -static sonar_info * -init_sonar(Display *dpy, Window win) + +static void +reshape (sonar_info *si) { + XWindowAttributes xgwa; + Pixmap mask; + XGetWindowAttributes(si->dpy, si->win, &xgwa); + si->width = xgwa.width; + si->height = xgwa.height; + si->centrex = si->width / 2; + si->centrey = si->height / 2; + si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10; + si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10; + si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10; + si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10; + si->radius = si->maxx - si->centrex; + + /* Install the clip mask... */ + if (! debug_p) { + mask = scope_mask (si->dpy, si->win, si); + XSetClipMask(si->dpy, si->text, mask); + XSetClipMask(si->dpy, si->erase, mask); + XFreePixmap (si->dpy, mask); /* it's been copied into the GCs */ + } +} - /* Local Variables */ - - XGCValues gcv; - XWindowAttributes xwa; - sonar_info *si; - XColor start, end; - int h1, h2; - double s1, s2, v1, v2; - - /* Create the Sonar information structure */ - - if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) { - fprintf(stderr, "%s: Out of memory\n", progname); - return NULL; - } - - /* Initialize the structure for the current environment */ - - si->dpy = dpy; - si->win = win; - si->visable = NULL; - XGetWindowAttributes(dpy, win, &xwa); - si->cmap = xwa.colormap; - si->width = xwa.width; - si->height = xwa.height; - si->centrex = si->width / 2; - si->centrey = si->height / 2; - si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10; - si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10; - si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10; - si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10; - si->radius = si->maxx - si->centrex; - si->current = 0; - si->sweepnum = 0; - - /* Get the font */ - - if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font"))) - == NULL) && - ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) { - fprintf(stderr, "%s: can't load an appropriate font\n", progname); - return NULL; - } - - /* Get the delay between animation frames */ - - si->delay = get_integer_resource ("delay", "Integer"); - if (si->delay < 0) si->delay = 0; - - /* Create the Graphics Contexts that will be used to draw things */ - - gcv.foreground = - get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap); - si->hi = XCreateGC(dpy, win, GCForeground, &gcv); - gcv.font = si->font->fid; - si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv); - gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor", - dpy, si->cmap); - si->erase = XCreateGC (dpy, win, GCForeground, &gcv); - gcv.foreground = get_pixel_resource("gridColor", "GridColor", - dpy, si->cmap); - si->grid = XCreateGC (dpy, win, GCForeground, &gcv); - - /* Compute pixel values for fading text on the display */ - - XParseColor(dpy, si->cmap, - get_string_resource("textColor", "TextColor"), &start); - XParseColor(dpy, si->cmap, - get_string_resource("scopeColor", "ScopeColor"), &end); - - rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); - rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2); - - si->text_steps = get_integer_resource("textSteps", "TextSteps"); - if (si->text_steps < 0 || si->text_steps > 255) - si->text_steps = 10; - - si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor)); - make_color_ramp (dpy, si->cmap, - h1, s1, v1, - h2, s2, v2, - si->text_colors, &si->text_steps, - False, True, False); - - /* Compute the pixel values for the fading sweep */ - - XParseColor(dpy, si->cmap, - get_string_resource("sweepColor", "SweepColor"), &start); - - rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); - - si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees"); - if (si->sweep_degrees <= 0) si->sweep_degrees = 20; - if (si->sweep_degrees > 350) si->sweep_degrees = 350; - - si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments"); - if (si->sweep_segs < 1 || si->sweep_segs > 255) - si->sweep_segs = 255; - - si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor)); - make_color_ramp (dpy, si->cmap, - h1, s1, v1, - h2, s2, v2, - si->sweep_colors, &si->sweep_segs, - False, True, False); - - /* Done */ - - return si; -} /* * Update the location of a simulated bogie. @@ -1355,8 +1560,8 @@ updateLocation(sim_target *t) int xdist, xtick; - xtick = (int) (3.0 * random() / RAND_MAX) - 1; - xdist = (int) (11.0 * random() / RAND_MAX) - 5; + xtick = (int) (random() % 3) - 1; + xdist = (int) (random() % 11) - 5; if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0)) t->nexttick += xtick; else @@ -1395,7 +1600,7 @@ simulator(sonar_info *si, void *vinfo) t = &info->teamA[i]; if ((t->movedonsweep != si->sweepnum) && (t->nexttick == (si->current * -1))) { - new = newBogie(strdup(t->name), t->nextdist, si->current, TTL); + new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL); if (list != NULL) new->next = list; list = new; @@ -1410,7 +1615,7 @@ simulator(sonar_info *si, void *vinfo) t = &info->teamB[i]; if ((t->movedonsweep != si->sweepnum) && (t->nexttick == (si->current * -1))) { - new = newBogie(strdup(t->name), t->nextdist, si->current, TTL); + new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL); if (list != NULL) new->next = list; list = new; @@ -1437,7 +1642,7 @@ simulator(sonar_info *si, void *vinfo) */ static int -computeStringX(sonar_info *si, char *label, int x) +computeStringX(sonar_info *si, const char *label, int x) { int width = XTextWidth(si->font, label, strlen(label)); @@ -1455,14 +1660,12 @@ computeStringX(sonar_info *si, char *label, int x) * The y coordinate of the start of the label. */ -/* TODO: Add smarts to keep label in sonar screen */ - static int computeStringY(sonar_info *si, int y) { - int fheight = si->font->ascent + si->font->descent; - return y + 5 + fheight; + int fheight = si->font->ascent /* + si->font->descent */; + return y + fheight; } /* @@ -1479,8 +1682,8 @@ computeStringY(sonar_info *si, int y) */ static void -DrawBogie(sonar_info *si, int draw, char *name, int degrees, - int distance, int ttl, int age) +DrawBogie(sonar_info *si, int draw, const char *name, const char *desc, + int degrees, int distance, int ttl, int age) { /* Local Variables */ @@ -1523,9 +1726,21 @@ DrawBogie(sonar_info *si, int draw, char *name, int degrees, /* Draw (or erase) the Bogie */ XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64); + + x += 3; /* move away from the dot */ + y += 7; + y = computeStringY(si, y); XDrawString(si->dpy, si->win, gc, - computeStringX(si, name, x), - computeStringY(si, y), name, strlen(name)); + computeStringX(si, name, x), y, + name, strlen(name)); + + if (desc && *desc) + { + y = computeStringY(si, y); + XDrawString(si->dpy, si->win, gc, + computeStringX(si, desc, x), y, + desc, strlen(desc)); + } } @@ -1604,13 +1819,13 @@ Sonar(sonar_info *si, Bogie *bl) Bogie *bp, *prev; int i; - /* Check for expired tagets and remove them from the visable list */ + /* Check for expired tagets and remove them from the visible list */ prev = NULL; - for (bp = si->visable; bp != NULL; bp = bp->next) { + for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) { /* - * Remove it from the visable list if it's expired or we have + * Remove it from the visible list if it's expired or we have * a new target with the same name. */ @@ -1618,13 +1833,18 @@ Sonar(sonar_info *si, Bogie *bl) if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) || (findNode(bl, bp->name) != NULL)) { - DrawBogie(si, 0, bp->name, bp->tick, + +#ifndef HAVE_COCOA /* we repaint every frame: no need to erase */ + DrawBogie(si, 0, bp->name, bp->desc, bp->tick, bp->distance, bp->ttl, bp->age); +#endif /* HAVE_COCOA */ + if (prev == NULL) - si->visable = bp->next; + si->visible = bp->next; else prev->next = bp->next; freeBogie(bp); + bp = prev; } else prev = bp; } @@ -1632,37 +1852,41 @@ Sonar(sonar_info *si, Bogie *bl) /* Draw the sweep */ { - int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs; int start_deg = si->current * 4 * 64; + int end_deg = start_deg + (si->sweep_degrees * 64); + int seg_deg = (end_deg - start_deg) / si->sweep_segs; if (seg_deg <= 0) seg_deg = 1; - for (i = 0; i < si->sweep_segs; i++) { - XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel); - XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny, - si->maxx - si->minx, si->maxy - si->miny, - start_deg + (i * seg_deg), - seg_deg); - } /* Remove the trailing wedge the sonar */ XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny, si->maxx - si->minx, si->maxy - si->miny, - start_deg + (i * seg_deg), - (4 * 64)); + end_deg, + 4 * 64); + + for (i = 0; i < si->sweep_segs; i++) { + int ii = si->sweep_segs - i - 1; + XSetForeground (si->dpy, si->hi, si->sweep_colors[ii].pixel); + XFillArc (si->dpy, si->win, si->hi, si->minx, si->miny, + si->maxx - si->minx, si->maxy - si->miny, + start_deg, + seg_deg * (ii + 1)); + } } - /* Move the new targets to the visable list */ + /* Move the new targets to the visible list */ for (bp = bl; bp != (Bogie *) 0; bp = bl) { bl = bl->next; - bp->next = si->visable; - si->visable = bp; + bp->next = si->visible; + si->visible = bp; } - /* Draw the visable targets */ + /* Draw the visible targets */ - for (bp = si->visable; bp != NULL; bp = bp->next) { + for (bp = si->visible; bp != NULL; bp = bp->next) { if (bp->age < bp->ttl) /* grins */ - DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age); + DrawBogie(si, 1, bp->name, bp->desc, + bp->tick, bp->distance, bp->ttl,bp->age); } /* Redraw the grid */ @@ -1670,94 +1894,397 @@ Sonar(sonar_info *si, Bogie *bl) drawGrid(si); } + +static ping_target * +parse_mode (sonar_info *si, Bool ping_works_p) +{ + char *source = get_string_resource (si->dpy, "ping", "Ping"); + char *token, *end; +#ifdef HAVE_PING + char dummy; +#endif + + ping_target *hostlist = 0; + + if (!source) source = strdup(""); + + if (!*source || !strcmp (source, "default")) + { + free (source); +# ifdef HAVE_PING + if (ping_works_p) /* if root or setuid, ping will work. */ + source = strdup("subnet/29,/etc/hosts"); + else +# endif + source = strdup("simulation"); + } + + token = source; + end = source + strlen(source); + while (token < end) + { + char *next; +# ifdef HAVE_PING + ping_target *new; + struct stat st; + unsigned int n0=0, n1=0, n2=0, n3=0, m=0; + char d; +# endif /* HAVE_PING */ + + for (next = token; + *next && + *next != ',' && *next != ' ' && *next != '\t' && *next != '\n'; + next++) + ; + *next = 0; + + + if (debug_p) + fprintf (stderr, "%s: parsing %s\n", progname, token); + + if (!strcmp (token, "simulation")) + return 0; + + if (!ping_works_p) + { + fprintf(stderr, + "%s: this program must be setuid to root for `ping mode' to work.\n" + " Running in `simulation mode' instead.\n", + progname); + return 0; + } + +#ifdef HAVE_PING + if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) || + (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d))) + { + /* subnet: A.B.C.D/M + subnet: A.B.C/M + */ + unsigned long ip = pack_addr (n0, n1, n2, n3); + new = subnetHostsList(ip, m); + } + else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d)) + { + /* IP: A.B.C.D + */ + new = newHost (token); + } + else if (!strcmp (token, "subnet")) + { + new = subnetHostsList(0, 24); + } + else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy)) + { + new = subnetHostsList(0, m); + } + else if (*token == '.' || *token == '/' || + *token == '$' || *token == '~' || + !stat (token, &st)) + { + /* file name + */ + new = readPingHostsFile (token); + } + else + { + /* not an existant file - must be a host name + */ + new = newHost (token); + } + + if (new) + { + ping_target *nn = new; + while (nn && nn->next) + nn = nn->next; + nn->next = hostlist; + hostlist = new; + + si->sensor = ping; + } +#endif /* HAVE_PING */ + + token = next + 1; + while (token < end && + (*token == ',' || *token == ' ' || + *token == '\t' || *token == '\n')) + token++; + } + + free (source); + return hostlist; +} + + /* - * Main screen saver hack. + * Initialize the Sonar. * * Args: * dpy - The X display. - * win - The X window. + * win - The X window; + * + * Returns: + * A sonar_info strcuture or null if memory allocation problems occur. */ -void -screenhack(Display *dpy, Window win) +static void * +sonar_init (Display *dpy, Window win) { - /* Local Variables */ + XGCValues gcv; + XWindowAttributes xwa; sonar_info *si; - struct timeval start, finish; - Bogie *bl; - long sleeptime; - char *mode; - - /* - * Initialize - * Adding new sensors would involve supporting more modes other than - * ping and initiailizing the sensor in the same way. - */ + XColor start, end; + int h1, h2; + double s1, s2, v1, v2; - mode = get_string_resource("mode", "Mode"); + debug_p = get_boolean_resource (dpy, "debug", "Debug"); + resolve_p = get_boolean_resource (dpy, "resolve", "Resolve"); + times_p = get_boolean_resource (dpy, "showTimes", "ShowTimes"); - if (!mode || !*mode || !strcmp(mode, "default")) /* Pick a good default. */ - { -#ifdef HAVE_PING - if (geteuid() == 0) /* we're root or setuid -- ping will work. */ - mode = "ping"; - else -#endif - mode = "simulation"; - } + /* Create the Sonar information structure */ -#ifdef HAVE_PING - if (strcmp(mode, "ping") == 0) { - sensor = ping; - if ((sensor_info = (void *) init_ping()) == (void *) 0) - { - fprintf (stderr, "%s: running in `simulation mode' instead.\n", - progname); - goto SIM; - } - } else -#endif /* HAVE_PING */ - if (strcmp(mode, "simulation") == 0) { -#ifdef HAVE_PING - SIM: -#endif - sensor = simulator; - if ((sensor_info = (void *) init_sim()) == NULL) - exit(1); - } else { - fprintf(stderr, "%s: unsupported Sonar mode: %s\n", progname, mode); - fprintf(stderr, - "\tCurrently supported modes are `ping' and `simulation'\n"); - exit(1); + if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) { + fprintf(stderr, "%s: Out of memory\n", progname); + exit (1); } - if ((si = init_sonar(dpy, win)) == (sonar_info *) 0) - exit(1); - - /* Sonar loop */ - TTL = get_integer_resource("ttl", "TTL"); + /* Initialize the structure for the current environment */ + + si->dpy = dpy; + si->win = win; + si->visible = NULL; + + XGetWindowAttributes(dpy, win, &xwa); + si->cmap = xwa.colormap; - while (1) { + si->current = 0; + si->sweepnum = 0; - /* Call the sensor and display the results */ + /* Get the font */ - gettimeofday(&start, (struct timezone *) 0); - bl = sensor(si, sensor_info); - Sonar(si, bl); + { + char *fn = get_string_resource (dpy, "font", "Font"); + if (((si->font = XLoadQueryFont(dpy, fn)) == NULL) && + ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) { + fprintf(stderr, "%s: can't load an appropriate font\n", progname); + exit (1); + } + if (fn) free (fn); + } - /* Set up and sleep for the next one */ + /* Get the delay between animation frames */ + + si->delay = get_integer_resource (dpy, "delay", "Integer"); - si->current = (si->current - 1) % 90; - if (si->current == 0) - si->sweepnum++; - XSync (dpy, False); - gettimeofday(&finish, (struct timezone *) 0); - sleeptime = si->delay - delta(&start, &finish); - screenhack_handle_events (dpy); - if (sleeptime > 0L) - usleep(sleeptime); + if (si->delay < 0) si->delay = 0; + si->TTL = get_integer_resource(dpy, "ttl", "TTL"); + + /* Create the Graphics Contexts that will be used to draw things */ + gcv.foreground = + get_pixel_resource (dpy, si->cmap, "sweepColor", "SweepColor"); + si->hi = XCreateGC(dpy, win, GCForeground, &gcv); + gcv.font = si->font->fid; + si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv); + gcv.foreground = get_pixel_resource(dpy, si->cmap, + "scopeColor", "ScopeColor"); + si->erase = XCreateGC (dpy, win, GCForeground, &gcv); + gcv.foreground = get_pixel_resource(dpy, si->cmap, + "gridColor", "GridColor"); + si->grid = XCreateGC (dpy, win, GCForeground, &gcv); + + reshape (si); + + /* Compute pixel values for fading text on the display */ + { + char *s = get_string_resource(dpy, "textColor", "TextColor"); + XParseColor(dpy, si->cmap, s, &start); + if (s) free (s); + s = get_string_resource(dpy, "scopeColor", "ScopeColor"); + XParseColor(dpy, si->cmap, s, &end); + if (s) free (s); } + + rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); + rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2); + + si->text_steps = get_integer_resource(dpy, "textSteps", "TextSteps"); + if (si->text_steps < 0 || si->text_steps > 255) + si->text_steps = 10; + + si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor)); + make_color_ramp (dpy, si->cmap, + h1, s1, v1, + h2, s2, v2, + si->text_colors, &si->text_steps, + False, True, False); + + /* Compute the pixel values for the fading sweep */ + + { + char *s = get_string_resource(dpy, "sweepColor", "SweepColor"); + XParseColor(dpy, si->cmap, s, &start); + if (s) free (s); + } + + rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); + + si->sweep_degrees = get_integer_resource(dpy, "sweepDegrees", "Degrees"); + if (si->sweep_degrees <= 0) si->sweep_degrees = 20; + if (si->sweep_degrees > 350) si->sweep_degrees = 350; + + si->sweep_segs = get_integer_resource(dpy, "sweepSegments", "SweepSegments"); + if (si->sweep_segs < 1 || si->sweep_segs > 255) + si->sweep_segs = 255; + + si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor)); + make_color_ramp (dpy, si->cmap, + h1, s1, v1, + h2, s2, v2, + si->sweep_colors, &si->sweep_segs, + False, True, False); + + if (si->sweep_segs <= 0) + si->sweep_segs = 1; + + +# ifdef HAVE_PING + si->sensor_info = (void *) init_ping(si); +# else /* !HAVE_PING */ + parse_mode (dpy, 0); /* just to check argument syntax */ +# endif /* !HAVE_PING */ + + if (!si->sensor) + { + si->sensor = simulator; + si->sensor_info = (void *) init_sim(dpy); + if (! si->sensor_info) + exit(1); + } + + /* Done */ + + return si; } + + +static unsigned long +sonar_draw (Display *dpy, Window window, void *closure) +{ + sonar_info *si = (sonar_info *) closure; + Bogie *bl; + struct timeval start, finish; + long delay; + +# ifdef HAVE_COCOA /* repaint the whole window so that antialiasing works */ + XClearWindow (dpy,window); + XFillRectangle (dpy, window, si->erase, 0, 0, si->width, si->height); +# endif + + /* Call the sensor and display the results */ + +# ifdef GETTIMEOFDAY_TWO_ARGS + gettimeofday(&start, (struct timezone *) 0); +# else + gettimeofday(&start); +# endif + bl = si->sensor(si, si->sensor_info); + Sonar(si, bl); + + /* Set up and sleep for the next one */ + + si->current = (si->current - 1) % 90; + if (si->current == 0) + si->sweepnum++; + +# ifdef GETTIMEOFDAY_TWO_ARGS + gettimeofday(&finish, (struct timezone *) 0); +# else + gettimeofday(&finish); +# endif + + delay = si->delay - delta(&start, &finish); + if (delay < 0) delay = 0; + return delay; +} + + +static void +sonar_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + sonar_info *si = (sonar_info *) closure; + XClearWindow (si->dpy, si->win); + reshape (si); +} + +static Bool +sonar_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + return False; +} + +static void +sonar_free (Display *dpy, Window window, void *closure) +{ +} + + + +static const char *sonar_defaults [] = { + ".background: #000000", + ".sweepColor: #00FF00", + "*delay: 100000", + "*scopeColor: #003300", + "*gridColor: #00AA00", + "*textColor: #FFFF00", + "*ttl: 90", + "*mode: default", + "*font: fixed", + "*sweepDegrees: 30", + + "*textSteps: 80", /* npixels */ + "*sweepSegments: 80", /* npixels */ + + "*pingTimeout: 3000", + + "*teamAName: F18", + "*teamBName: MIG", + "*teamACount: 4", + "*teamBCount: 4", + + "*ping: default", + "*resolve: true", + "*showTimes: true", + ".debug: false", + 0 +}; + +static XrmOptionDescRec sonar_options [] = { + {"-background", ".background", XrmoptionSepArg, 0 }, + {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 }, + {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 }, + {"-grid-color", ".gridColor", XrmoptionSepArg, 0 }, + {"-text-color", ".textColor", XrmoptionSepArg, 0 }, + {"-ttl", ".ttl", XrmoptionSepArg, 0 }, + {"-font", ".font", XrmoptionSepArg, 0 }, +#ifdef HAVE_PING + {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 }, +#endif /* HAVE_PING */ + {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 }, + {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 }, + {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 }, + {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 }, + + {"-ping", ".ping", XrmoptionSepArg, 0 }, + {"-no-dns", ".resolve", XrmoptionNoArg, "False" }, + {"-no-times", ".showTimes", XrmoptionNoArg, "False" }, + {"-debug", ".debug", XrmoptionNoArg, "True" }, + { 0, 0, 0, 0 } +}; + + +XSCREENSAVER_MODULE ("Sonar", sonar)