commands over unix socket
[pandora_liveinfo.git] / main.c
diff --git a/main.c b/main.c
index 75082eb..010f63c 100644 (file)
--- a/main.c
+++ b/main.c
@@ -7,6 +7,7 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
 #include <pthread.h>
 static struct fb_var_screeninfo g_vi;
 static uint16_t *g_screen_base, *g_screen;
 static unsigned int g_old_mem;
-static int g_fd, g_exit;
+static int g_flip_id;
+static int g_exit, g_hide;
+static pthread_cond_t g_cond;
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 #define IS_START(buf, str) \
   !strncmp(buf, str, sizeof(str) - 1)
 
-static int setup_layer(void)
+static void clear_bytes(void *mem, int bytes);
+
+static int setup_layer(int fd)
 {
-  static const char fbname[] = "/dev/fb2";
   struct omapfb_plane_info pi;
   struct omapfb_mem_info mi;
   struct omapfb_color_key key;
   int ret;
 
-  g_fd = open(fbname, O_RDWR);
-  if (g_fd == -1) {
-    fprintf(stderr, "open %s: ", fbname);
-    perror(NULL);
-    return 1;
-  }
-
-  ret = ioctl(g_fd, OMAPFB_QUERY_PLANE, &pi);
+  ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
   if (ret < 0) {
     perror("ioctl OMAPFB_QUERY_PLANE");
     return 1;
   }
 
-  ret = ioctl(g_fd, OMAPFB_QUERY_MEM, &mi);
+  ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
   if (ret < 0) {
     perror("ioctl OMAPFB_QUERY_MEM");
     return 1;
@@ -72,7 +70,7 @@ static int setup_layer(void)
   /* must disable when changing stuff */
   if (pi.enabled) {
     pi.enabled = 0;
-    ret = ioctl(g_fd, OMAPFB_SETUP_PLANE, &pi);
+    ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
     if (ret < 0) {
       perror("ioctl OMAPFB_SETUP_PLANE (d)");
       return 1;
@@ -82,7 +80,7 @@ static int setup_layer(void)
   g_old_mem = mi.size;
   if (mi.size < MEM_SIZE) {
     mi.size = MEM_SIZE;
-    ret = ioctl(g_fd, OMAPFB_SETUP_MEM, &mi);
+    ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
     if (ret < 0) {
       perror("ioctl SETUP_MEM");
       return 1;
@@ -93,13 +91,13 @@ static int setup_layer(void)
   pi.pos_y = 0;
   pi.out_width = WIDTH;
   pi.out_height = HEIGHT;
-  ret = ioctl(g_fd, OMAPFB_SETUP_PLANE, &pi);
+  ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
   if (ret < 0) {
     perror("ioctl OMAPFB_SETUP_PLANE (e1)");
     return 1;
   }
 
-  ret = ioctl(g_fd, FBIOGET_VSCREENINFO, &g_vi);
+  ret = ioctl(fd, FBIOGET_VSCREENINFO, &g_vi);
   if (ret < 0) {
     perror("ioctl FBIOGET_VSCREENINFO");
     return 1;
@@ -110,14 +108,14 @@ static int setup_layer(void)
   g_vi.yres_virtual = HEIGHT * 2;
   g_vi.bits_per_pixel = 16;
 
-  ret = ioctl(g_fd, FBIOPUT_VSCREENINFO, &g_vi);
+  ret = ioctl(fd, FBIOPUT_VSCREENINFO, &g_vi);
   if (ret < 0) {
     perror("ioctl FBIOPUT_VSCREENINFO");
     return 1;
   }
 
   pi.enabled = 1;
-  ret = ioctl(g_fd, OMAPFB_SETUP_PLANE, &pi);
+  ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
   if (ret < 0) {
     perror("ioctl OMAPFB_SETUP_PLANE (e2)");
     return 1;
@@ -127,21 +125,32 @@ static int setup_layer(void)
   key.key_type = OMAPFB_COLOR_KEY_VID_SRC;
   key.trans_key = 0x07e0;
 
-       ret = ioctl(g_fd, OMAPFB_SET_COLOR_KEY, &key);
-       if (ret < 0) {
-               perror("ioctl OMAPFB_SET_COLOR_KEY");
+  ret = ioctl(fd, OMAPFB_SET_COLOR_KEY, &key);
+  if (ret < 0) {
+    perror("ioctl OMAPFB_SET_COLOR_KEY");
+    return 1;
+  }
+
+  g_screen_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
+                  MAP_SHARED, fd, 0);
+  if (g_screen_base == MAP_FAILED) {
+    perror("mmap");
+    g_screen = g_screen_base = NULL;
     return 1;
-       }
+  }
+  clear_bytes(g_screen_base, MEM_SIZE);
+  g_screen = g_screen_base;
+  g_flip_id = 0;
 
   return 0;
 }
 
-static int check_layer(void)
+static int check_layer(int fd)
 {
   struct omapfb_plane_info pi;
   int ret;
 
-  ret = ioctl(g_fd, OMAPFB_QUERY_PLANE, &pi);
+  ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
   if (ret < 0) {
     perror("ioctl OMAPFB_QUERY_PLANE");
     return 1;
@@ -155,19 +164,24 @@ static int check_layer(void)
   return 0;
 }
 
-static void remove_layer(void)
+static void remove_layer(int fd)
 {
   struct omapfb_plane_info pi;
   struct omapfb_mem_info mi;
   int ret;
 
+  if (g_screen_base != NULL) {
+    munmap(g_screen_base, MEM_SIZE);
+    g_screen = g_screen_base = NULL;
+  }
+
   memset(&pi, 0, sizeof(pi));
   pi.enabled = 0;
-  ret = ioctl(g_fd, OMAPFB_SETUP_PLANE, &pi);
+  ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
   if (ret < 0)
     perror("ioctl OMAPFB_SETUP_PLANE (c)");
 
-  ret = ioctl(g_fd, OMAPFB_QUERY_MEM, &mi);
+  ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
   if (ret < 0) {
     perror("ioctl OMAPFB_QUERY_MEM");
     goto out;
@@ -175,31 +189,98 @@ static void remove_layer(void)
 
   if (mi.size != g_old_mem) {
     mi.size = g_old_mem;
-    ret = ioctl(g_fd, OMAPFB_SETUP_MEM, &mi);
+    ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
     if (ret < 0) {
       perror("ioctl SETUP_MEM");
       goto out;
     }
   }
 
-out:
-  close(g_fd);
-  g_fd = -1;
+out:;
+}
+
+static void flip_fb(int fd)
+{
+  g_vi.yoffset = g_flip_id * HEIGHT;
+  ioctl(fd, FBIOPAN_DISPLAY, &g_vi);
+  g_flip_id ^= 1;
+  g_screen = g_screen_base + g_flip_id * WIDTH * HEIGHT;
 }
 
-static void handle_signal(int num)
+static void handle_term(int num)
 {
   g_exit = 1;
+  pthread_cond_signal(&g_cond);
+}
+
+static void handle_usr1(int num)
+{
+  g_hide = !g_hide;
+  pthread_cond_signal(&g_cond);
 }
 
-static void flip_fb(void)
+static struct {
+  char text[16];
+  int idle;
+} user_msgs[16];
+
+static int handle_socket(int sock)
 {
-  static int id;
+  char buf[256], *p;
+  int i, ret;
+
+  ret = recv(sock, buf, sizeof(buf) - 1, 0);
+  if (ret < 0) {
+    perror("recv");
+    return ret;
+  }
+  if (ret == 0)
+    return 0;
+
+  buf[ret] = 0;
+  p = strchr(buf, ':');
+  if (p != NULL) {
+    for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
+      if (user_msgs[i].text[0] == 0)
+        break;
+      if (!strncmp(user_msgs[i].text, buf, p - buf + 1))
+        break;
+    }
+    if (i == ARRAY_SIZE(user_msgs)) {
+      printf("out of user_msg slots\n");
+      return 0;
+    }
+    memcpy(user_msgs[i].text, buf, sizeof(user_msgs[i].text) - 1);
+    user_msgs[i].text[sizeof(user_msgs[i].text) - 1] = 0;
+    user_msgs[i].idle = 0;
+  }
+  else if (!strcmp(buf, "poke")) {
+    // if hidden, show up
+    // if visible, exit
+    if (g_hide) {
+      g_hide = 0;
+      return 1;
+    }
+    g_exit = 1;
+    return 1;
+  }
+  else if (!strcmp(buf, "hide")) {
+    g_hide = 1;
+    return 1;
+  }
+  else if (!strcmp(buf, "show")) {
+    g_hide = 0;
+    return 1;
+  }
+  else if (!strcmp(buf, "quit")) {
+    g_exit = 1;
+    return 1;
+  }
+  else {
+    printf("unknown command: '%s'\n", buf);
+  }
 
-       g_vi.yoffset = id * HEIGHT;
-       ioctl(g_fd, FBIOPAN_DISPLAY, &g_vi);
-  id ^= 1;
-  g_screen = g_screen_base + id * WIDTH * HEIGHT;
+  return 0;
 }
 
 #define s_printf(x, y, fmt, ...) \
@@ -222,10 +303,10 @@ static int read_int_file(const char *fn, int *val)
   return 0;
 }
 
-static void clear(int h)
+static void clear_bytes(void *mem, int bytes)
 {
-  int *p = (int *)g_screen;
-  int l = WIDTH * h / 2;
+  int *p = mem;
+  int l = bytes / 4;
 
   for (; l >= 4; l -= 4, p += 4)
     p[0] = p[1] = p[2] = p[3] = 0x07e007e0;
@@ -233,6 +314,11 @@ static void clear(int h)
     *p = 0x07e007e0;
 }
 
+static void clear(int h)
+{
+  clear_bytes(g_screen, WIDTH * h * 2);
+}
+
 struct proc_stat {
   unsigned long idlesum;
   unsigned long sys;
@@ -645,19 +731,22 @@ static void get_netsgnl(int x, int y)
 
 int main(int argc, char *argv[])
 {
-  static const char socket_name[] = "\0livestats";
+  static const char socket_name[] = "\0liveinfo";
+  static const char fbname[] = "/dev/fb2";
   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-  pthread_cond_t cond;
   pthread_condattr_t condattr;
   struct sockaddr_un sun;
   struct timespec ts;
   struct pollfd pfd;
+  const char *p;
+  int is_hidden = 0;
   int y = 0, y_max = 0;
+  int fd = -1;
   int sock;
-  int ret;
+  int i, ret;
 
   // look for other instance
-  sock = socket(PF_UNIX, SOCK_STREAM, 0);
+  sock = socket(PF_UNIX, SOCK_DGRAM, 0);
   if (sock == -1) {
     perror("socket PF_UNIX");
     return 1;
@@ -669,15 +758,35 @@ int main(int argc, char *argv[])
 
   ret = connect(sock, (struct sockaddr *)&sun, sizeof(sun));
   if (ret == 0) {
-    printf("other instance detected, sending quit command\n");
-    ret = send(sock, "quit", 4, 0);
+    printf("other instance detected, sending poke command\n");
+    ret = send(sock, "poke", 4, 0);
     if (ret != 4)
       perror("send");
     close(sock);
-    return 0;
+    return ret == 4 ? 0 : 1;
+  }
+
+  fd = open(fbname, O_RDWR);
+  if (fd == -1) {
+    fprintf(stderr, "open %s: ", fbname);
+    perror(NULL);
+    return 1;
+  }
+
+  nice(-1);
+
+  // no need for privileges any more
+  p = getenv("SUDO_UID");
+  if (p != NULL) {
+    ret = atoi(p);
+    if (ret != 0) {
+      ret = setuid(ret);
+      if (ret)
+        perror("setuid");
+    }
   }
 
-  // bind/listen for quit command
+  // bind/listen for commands
   ret = bind(sock, (struct sockaddr *)&sun, sizeof(sun));
   if (ret != 0) {
     perror("bind");
@@ -685,43 +794,35 @@ int main(int argc, char *argv[])
     return 1;
   }
 
+#if 0
   ret = listen(sock, 1);
   if (ret != 0) {
     perror("listen");
     close(sock);
     return 1;
   }
+#endif
 
   pfd.fd = sock;
   pfd.events = POLLIN | POLLPRI;
   pfd.revents = 0;
 
-  // clean up on kill too
-  signal(SIGTERM, handle_signal);
-
   ret  = pthread_condattr_init(&condattr);
   ret |= pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
-  ret |= pthread_cond_init(&cond, &condattr);
+  ret |= pthread_cond_init(&g_cond, &condattr);
   pthread_condattr_destroy(&condattr);
   if (ret != 0) {
     fprintf(stderr, "cond init failed\n");
     return 1;
   }
 
-  ret = setup_layer();
+  ret = setup_layer(fd);
   if (ret)
     return ret;
 
-  g_screen_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
-                  MAP_SHARED, g_fd, 0);
-  if (g_screen_base == MAP_FAILED) {
-    perror("mmap");
-    return 1;
-  }
-  g_screen = g_screen_base;
-  clear(HEIGHT);
-  flip_fb();
-  clear(HEIGHT);
+  // clean up on kill too
+  signal(SIGTERM, handle_term);
+  signal(SIGUSR1, handle_usr1);
 
   pthread_mutex_lock(&mutex);
   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -730,10 +831,41 @@ int main(int argc, char *argv[])
     return 1;
   }
 
-  nice(-1);
   init_stats();
 
-  while (!g_exit) {
+  while (!g_exit)
+  {
+    // anything on unix socket?
+    ret = poll(&pfd, 1, 0);
+    if (ret < 0) {
+      perror("poll");
+      break;
+    }
+    if (ret > 0) {
+      ret = handle_socket(sock);
+      if (ret < 0)
+        break;
+      continue;
+    }
+
+    // handle hiding
+    if (g_hide) {
+      if (!is_hidden) {
+        remove_layer(fd);
+        is_hidden = 1;
+      }
+      goto do_sleep;
+    }
+    else if (is_hidden) {
+      ret = setup_layer(fd);
+      if (ret)
+        break;
+      is_hidden = 0;
+    }
+
+    if (check_layer(fd) != 0)
+      break;
+
     collect_stats();
 
     y += Y_STEP;
@@ -774,22 +906,34 @@ int main(int argc, char *argv[])
     get_bwatts (0, y += Y_STEP);
     get_btemp  (0, y += Y_STEP);
 
-    flip_fb();
-
-    if (check_layer() != 0)
-      break;
-    ret = poll(&pfd, 1, 0);
-    if (ret != 0) {
-      printf("poll returned %d\n", ret);
-      break;
+    // print user messages
+    y += Y_STEP;
+    for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
+      if (user_msgs[i].text[0] == 0)
+        break;
+      user_msgs[i].idle++;
+      if (user_msgs[i].idle > 6) {
+        // drop old entry, shift others up
+        memmove(&user_msgs[i], &user_msgs[i + 1],
+          (ARRAY_SIZE(user_msgs) - i - 1) * sizeof(user_msgs[0]));
+        user_msgs[ARRAY_SIZE(user_msgs) - 1].text[0] = 0;
+        // reprocess
+        i--;
+        continue;
+      }
+
+      s_printf(0, y += Y_STEP, user_msgs[i].text);
     }
 
+    flip_fb(fd);
+
+do_sleep:
     ts.tv_sec++;
-    pthread_cond_timedwait(&cond, &mutex, &ts);
+    pthread_cond_timedwait(&g_cond, &mutex, &ts);
   }
 
-  munmap(g_screen_base, MEM_SIZE);
-  remove_layer();
+  remove_layer(fd);
+  close(fd);
   close(sock);
 
   return 0;