-/* sonar, Copyright (c) 1998-2008 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2018 Jamie Zawinski and Stephen Martin
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
*
* It should be easy to extend this code to support other sorts of sensors.
* Some ideas:
+ *
* - search the output of "netstat" for the list of hosts to ping;
* - plot the contents of /proc/interrupts;
* - plot the process table, by process size, cpu usage, or total time;
* - plot the logged on users by idle time or cpu usage.
- *
+ * - plot IM contacts or Facebook friends and their last-activity times.
*/
-#define DEF_FONT "-*-lucidatypewriter-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
+#define DEF_FONT "-*-courier-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
#define DEF_SPEED "1.0"
#define DEF_SWEEP_SIZE "0.3"
#define DEF_FONT_SIZE "12"
#define DEF_WOBBLE "True"
#define DEF_DEBUG "False"
+#include "thread_util.h"
+
#define DEFAULTS "*delay: 30000 \n" \
"*font: " DEF_FONT "\n" \
"*showFPS: False \n" \
"*wireframe: False \n" \
+ "*texFontCacheSize: 300 \n" \
+ THREAD_DEFAULTS_XLOCK
-# define refresh_sonar 0
+# define release_sonar 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
#ifdef USE_GL /* whole file */
+/* #define TEST_ASYNC_NETDB 1 */
+
+# if TEST_ASYNC_NETDB
+# include "async_netdb.h"
+
+# include <assert.h>
+# include <netinet/in.h>
+# include <stdio.h>
+# endif /* TEST_ASYNC_NETDB */
+
typedef struct {
double x,y,z;
} XYZ;
texture_font_data *texfont;
+ enum { MSG, RESOLVE, RUN } state;
sonar_sensor_data *ssd;
char *error;
+ char *desc;
sonar_bogie *displayed; /* on screen and fading */
sonar_bogie *pending; /* returned by sensor, not yet on screen */
+# if TEST_ASYNC_NETDB
+ async_name_from_addr_t query0;
+ async_addr_from_name_t query1;
+# endif
} sonar_configuration;
static sonar_configuration *sps = NULL;
{ "+times", ".times", XrmoptionNoArg, "False" },
{ "-wobble", ".wobble", XrmoptionNoArg, "True" },
{ "+wobble", ".wobble", XrmoptionNoArg, "False" },
+ THREAD_OPTIONS
{ "-debug", ".debug", XrmoptionNoArg, "True" },
};
if (wire && !(mesh_p || sweep_p)) return 0;
- glPushAttrib (GL_ENABLE_BIT);
glDisable (GL_TEXTURE_2D);
glFrontFace (GL_CCW);
r0 = i / (GLfloat) r_steps;
r1 = (i+1) / (GLfloat) r_steps;
+
+ if (r1 > 1) r1 = 1; /* avoid asin lossage */
+
z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
glEnd();
}
- glPopAttrib();
free (ring);
return polys;
char *string2 = strdup (string);
char *token = string2;
char *line;
- GLfloat color[4];
+
+ if (MI_WIDTH(mi) > 2560) font_scale /= 2; /* Retina displays */
if (size <= 0) /* if size not specified, draw in yellow with alpha */
{
+ GLfloat color[4];
color[0] = 1;
color[1] = 1;
color[2] = 0;
while ((line = strtok (token, "\r\n")))
{
- int w = texture_string_width (sp->texfont, line, &lh);
+ XCharStruct e;
+ int w, ascent, descent;
+ texture_string_metrics (sp->texfont, line, &e, &ascent, &descent);
+ w = e.width;
+ lh = ascent + descent;
if (w > max_w) max_w = w;
lines++;
token = 0;
token = string2;
while ((line = strtok (token, "\r\n")))
{
- int w = texture_string_width (sp->texfont, line, 0);
+ XCharStruct e;
+ int w;
+ texture_string_metrics (sp->texfont, line, &e, 0, 0);
+ w = e.width;
glPushMatrix();
- glTranslatef ((max_w-w)/2, 0, 0);
+ glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
if (wire)
{
int th_steps = 36 * 4; /* same as in draw_screen */
static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
- static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
static const GLfloat shiny = 0.0;
if (wire) return 0;
- glPushAttrib (GL_ENABLE_BIT);
glDisable (GL_TEXTURE_2D);
glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
polys++;
}
glEnd();
- glPopAttrib();
+ return polys;
+}
+
+
+static int
+draw_angles (ModeInfo *mi)
+{
+ int i;
+ int polys = 0;
+
+ static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
+ static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
+
+ glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
glTranslatef (0, 0, 0.01);
for (i = 0; i < 360; i += 10)
/* called from sonar-sim.c and sonar-icmp.c */
sonar_bogie *
-copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
+sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
{
sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
b2->name = strdup (b->name);
/* called from sonar-icmp.c */
void
-free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
+sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
{
if (b->closure)
ssd->free_bogie_cb (ssd, b->closure);
prev->next = b->next;
else
(*from_list) = b->next;
- free_bogie (ssd, b);
+ sonar_free_bogie (ssd, b);
break;
}
}
copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
sonar_bogie **to_list)
{
- sonar_bogie *ob, *prev;
+ sonar_bogie *ob, *next;
if (!b) abort();
- for (prev = 0, ob = *to_list; ob; prev = ob, ob = ob->next)
+ for (ob = *to_list, next = ob ? ob->next : 0;
+ ob;
+ ob = next, next = ob ? ob->next : 0)
{
if (ob == b) abort(); /* this will end badly */
if (!strcmp (ob->name, b->name)) /* match! */
}
}
- b = copy_bogie (ssd, b);
+ b = sonar_copy_bogie (ssd, b);
b->next = *to_list;
*to_list = b;
}
draw_startup_blurb (ModeInfo *mi)
{
sonar_configuration *sp = &sps[MI_SCREEN(mi)];
- const char *msg = (sp->error ? sp->error : "Resolving hosts...");
- static const GLfloat color[4] = {0, 1, 0, 1};
- if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
- return; /* don't bother */
+ if (sp->error)
+ {
+ const char *msg = sp->error;
+ static const GLfloat color[4] = {0, 1, 0, 1};
- glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
- glTranslatef (0, 0, 0.3);
- draw_text (mi, msg, 0, 0, 0, 30.0);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+ glTranslatef (0, 0, 0.3);
+ draw_text (mi, msg, 0, 0, 0, 30.0);
- /* only leave error message up for N seconds */
- if (sp->error &&
- sp->start_time + 4 < double_time())
- {
- free (sp->error);
- sp->error = 0;
+ /* only leave error message up for N seconds */
+ if (sp->start_time + 6 < double_time())
+ {
+ free (sp->error);
+ sp->error = 0;
+ }
}
}
{
sonar_configuration *sp = &sps[MI_SCREEN(mi)];
GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width * 9/16;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
- glViewport (0, 0, (GLint) width, (GLint) height);
+ glViewport (0, y, (GLint) width, (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
{
sonar_configuration *sp = &sps[MI_SCREEN(mi)];
- if (event->xany.type == ButtonPress &&
- event->xbutton.button == Button1)
- {
- sp->button_down_p = True;
- gltrackball_start (sp->trackball,
- event->xbutton.x, event->xbutton.y,
- MI_WIDTH (mi), MI_HEIGHT (mi));
- return True;
- }
- else if (event->xany.type == ButtonRelease &&
- event->xbutton.button == Button1)
- {
- sp->button_down_p = False;
- return True;
- }
- else if (event->xany.type == ButtonPress &&
- (event->xbutton.button == Button4 ||
- event->xbutton.button == Button5 ||
- event->xbutton.button == Button6 ||
- event->xbutton.button == Button7))
- {
- gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
- !!event->xbutton.state);
- return True;
- }
- else if (event->xany.type == MotionNotify &&
- sp->button_down_p)
- {
- gltrackball_track (sp->trackball,
- event->xmotion.x, event->xmotion.y,
- MI_WIDTH (mi), MI_HEIGHT (mi));
- return True;
- }
+ if (gltrackball_event_handler (event, sp->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &sp->button_down_p))
+ return True;
return False;
}
-
ENTRYPOINT void
init_sonar (ModeInfo *mi)
{
sonar_configuration *sp;
- int wire = MI_IS_WIREFRAME(mi);
- if (!sps) {
- sps = (sonar_configuration *)
- calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
- if (!sps) {
- fprintf(stderr, "%s: out of memory\n", progname);
- exit(1);
- }
- }
+ MI_INIT (mi, sps);
sp = &sps[MI_SCREEN(mi)];
sp->glx_context = init_GL(mi);
reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
- if (!wire)
- {
- GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
- GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
- GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
- GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
-
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_LIGHTING);
- glEnable(GL_LIGHT0);
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_NORMALIZE);
- glEnable(GL_LINE_SMOOTH);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glShadeModel(GL_SMOOTH);
-
- glLightfv(GL_LIGHT0, GL_POSITION, pos);
- glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
- glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
- glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
- }
-
- sp->trackball = gltrackball_init ();
+ sp->trackball = gltrackball_init (False);
sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
sp->start_time = double_time ();
sp->sweep_offset = random() % 60;
sp->sweep_th = -1;
+ sp->state = MSG;
+}
+
+
+# ifdef TEST_ASYNC_NETDB
+
+# include <arpa/inet.h>
+
+static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
+{
+ sa_family_t family = ((struct sockaddr *)addr)->sa_family;
+ char buf[256];
+ switch (family)
+ {
+ case AF_INET:
+ fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
+ break;
+ case AF_INET6:
+ inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+ buf, sizeof (buf));
+ fputs (buf, stream);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+static void _print_error (int gai_error, int errno_error, FILE *stream)
+{
+ fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
+}
+
+# if ASYNC_NETDB_USE_GAI
+
+static void _print_thread (pthread_t thread, FILE *stream)
+{
+# ifdef __linux__
+ fprintf (stream, "%#lx", thread);
+# elif defined __APPLE__ && defined __MACH__
+ fprintf (stream, "%p", thread);
+# else
+ putc ('?', stream);
+# endif
}
+# endif /* ASYNC_NETDB_USE_GAI */
+
+# endif /* TEST_ASYNC_NETDB */
+
static void
init_sensor (ModeInfo *mi)
if (!ping_arg || !*ping_arg ||
!strcmp(ping_arg, "default") ||
!!strcmp (ping_arg, "simulation"))
- sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
- ping_timeout, resolve_p, times_p, debug_p);
+ /* sonar_init_ping() always disavows privs, even on failure. */
+ sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
+ ping_arg, ping_timeout, resolve_p, times_p,
+ debug_p);
+ else
+ setuid(getuid()); /* Disavow privs if not pinging. */
- /* Disavow privs. This was already done in init_ping(), but
- we might not have called that at all, so do it again. */
- setuid(getuid());
+ sp->start_time = double_time (); /* for error message timing */
if (!sp->ssd)
- sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
- team_a_name, team_b_name,
- team_a_count, team_b_count,
- debug_p);
+ sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
+ team_a_name, team_b_name,
+ team_a_count, team_b_count,
+ debug_p);
if (!sp->ssd)
abort();
+
+# if TEST_ASYNC_NETDB
+ /*
+ For extremely mysterious reasons, setuid apparently causes
+ pthread_join(3) to deadlock.
+ A rough guess at the sequence of events:
+ 1. Worker thread is created.
+ 2. Worker thread exits.
+ 3. setuid(getuid()) is called.
+ 4. pthread_join is called slightly later.
+
+ This may have something to do with glibc's use of SIGSETXID.
+ */
+
+ putc ('\n', stderr);
+
+# if !ASYNC_NETDB_USE_GAI
+ fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
+# endif
+
+ {
+ static const unsigned long addresses[] =
+ {
+ INADDR_LOOPBACK,
+ 0x00010203,
+ 0x08080808
+ };
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
+
+ sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
+ sizeof(addr));
+ assert (sp->query0);
+ if (sp->query0)
+ {
+ fputs ("Looking up hostname from address: ", stderr);
+ _print_sockaddr (&addr, sizeof(addr), stderr);
+# if ASYNC_NETDB_USE_GAI
+ fputs (" @ ", stderr);
+ _print_thread (sp->query0->io.thread, stderr);
+# endif
+ putc ('\n', stderr);
+ }
+
+ if (!(random () & 3))
+ {
+ fputs ("Aborted hostname lookup (early)\n", stderr);
+ async_name_from_addr_cancel (sp->query0);
+ sp->query0 = NULL;
+ }
+ }
+
+ {
+ static const char *const hosts[] =
+ {
+ "example.com",
+ "invalid",
+ "ip6-localhost"
+ };
+ const char *host = hosts[random () % 3];
+
+ sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
+
+ assert (sp->query1);
+
+ fprintf (stderr, "Looking up address from hostname: %s", host);
+# if ASYNC_NETDB_USE_GAI
+ fputs (" @ ", stderr);
+ _print_thread (sp->query1->io.thread, stderr);
+# endif
+ putc ('\n', stderr);
+
+ if (!(random () & 3))
+ {
+ fputs ("Aborted address lookup (early)\n", stderr);
+ async_addr_from_name_cancel (sp->query1);
+ sp->query1 = NULL;
+ }
+ }
+
+ fflush (stderr);
+# endif
}
sonar_configuration *sp = &sps[MI_SCREEN(mi)];
Display *dpy = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
+ int wire = MI_IS_WIREFRAME(mi);
if (!sp->glx_context)
return;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ if (!wire)
+ {
+ GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
+ GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_NORMALIZE);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glShadeModel(GL_SMOOTH);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+ }
+
glPushMatrix ();
- { GLfloat s = 7; glScalef (s,s,s); }
+ glRotatef(current_device_rotation(), 0, 0, 1);
+
+ {
+ GLfloat s = 7;
+ if (MI_WIDTH(mi) < MI_HEIGHT(mi))
+ s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
+ glScalef (s,s,s);
+ }
gltrackball_rotate (sp->trackball);
glCallList (sp->grid_list);
mi->polygon_count += sp->screen_polys;
- if (! sp->ssd || sp->error)
+ glPushMatrix();
+ mi->polygon_count += draw_angles (mi); /* angles */
+ glPopMatrix();
+
+ if (sp->desc) /* local subnet */
+ {
+ glPushMatrix();
+ glTranslatef (0, 0, 0.00002);
+ mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
+ /* glRotatef (45, 0, 0, 1); */
+ /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
+ glPopMatrix();
+ }
+
+ if (sp->error)
+ sp->state = MSG;
+
+ switch (sp->state) {
+ case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
draw_startup_blurb(mi);
+ sp->state++;
+ break;
+ case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
+ if (! sp->ssd)
+ init_sensor (mi);
+ sp->state++;
+ break;
+ case RUN: /* Frame N: ping away */
+ sweep (sp);
+ break;
+ }
glPopMatrix ();
glXSwapBuffers(dpy, window);
- if (! sp->ssd)
- /* Just starting up. "Resolving hosts" text printed. Go stall. */
- init_sensor (mi);
- else
- sweep (sp);
+# if TEST_ASYNC_NETDB
+ if(sp->query0 && async_name_from_addr_is_done (sp->query0))
+ {
+ if (!(random () & 3))
+ {
+ fputs ("Aborted hostname lookup (late)\n", stderr);
+ async_name_from_addr_cancel (sp->query0);
+ }
+ else
+ {
+ char *hostname = NULL;
+ int errno_error;
+ int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
+ &errno_error);
+
+ if(gai_error)
+ {
+ fputs ("Couldn't get hostname: ", stderr);
+ _print_error (gai_error, errno_error, stderr);
+ putc ('\n', stderr);
+ }
+ else
+ {
+ fprintf (stderr, "Got a hostname: %s\n", hostname);
+ free (hostname);
+ }
+ }
+
+ sp->query0 = NULL;
+ }
+
+ if(sp->query1 && async_addr_from_name_is_done (sp->query1))
+ {
+ if (!(random () & 3))
+ {
+ fputs ("Aborted address lookup (late)\n", stderr);
+ async_addr_from_name_cancel (sp->query1);
+ }
+ else
+ {
+ async_netdb_sockaddr_storage_t addr;
+ socklen_t addrlen;
+ int errno_error;
+ int gai_error = async_addr_from_name_finish (sp->query1, &addr,
+ &addrlen, &errno_error);
+
+ if (gai_error)
+ {
+ fputs ("Couldn't get address: ", stderr);
+ _print_error (gai_error, errno_error, stderr);
+ putc ('\n', stderr);
+ }
+ else
+ {
+ fputs ("Got an address: ", stderr);
+ _print_sockaddr (&addr, addrlen, stderr);
+ putc ('\n', stderr);
+ }
+ }
+
+ sp->query1 = NULL;
+ }
+
+ fflush (stderr);
+# endif /* TEST_ASYNC_NETDB */
}
ENTRYPOINT void
-release_sonar (ModeInfo *mi)
+free_sonar (ModeInfo *mi)
{
#if 0
sonar_configuration *sp = &sps[MI_SCREEN(mi)];