commands over unix socket
[pandora_liveinfo.git] / main.c
1 /*
2  * pandora live info
3  * (C) notaz, 2014
4  *
5  * This work is licensed under the terms of 3-clause BSD license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <pthread.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <unistd.h>
20 #include <sys/time.h>
21 #include <time.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <signal.h>
25 #include <poll.h>
26 #include <linux/fb.h>
27 #include <linux/omapfb.h>
28 #include <stdint.h>
29 #include "fonts.h"
30
31 #define SCREEN_WIDTH 800
32 #define SCREEN_HEIGHT 480
33 #define WIDTH 80
34 #define HEIGHT 480
35 #define MEM_SIZE (((WIDTH * HEIGHT * 2 * 2) + 0xfff) & ~0xfff)
36 #define Y_STEP 9
37
38 static struct fb_var_screeninfo g_vi;
39 static uint16_t *g_screen_base, *g_screen;
40 static unsigned int g_old_mem;
41 static int g_flip_id;
42 static int g_exit, g_hide;
43 static pthread_cond_t g_cond;
44
45 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
46 #define IS_START(buf, str) \
47   !strncmp(buf, str, sizeof(str) - 1)
48
49 static void clear_bytes(void *mem, int bytes);
50
51 static int setup_layer(int fd)
52 {
53   struct omapfb_plane_info pi;
54   struct omapfb_mem_info mi;
55   struct omapfb_color_key key;
56   int ret;
57
58   ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
59   if (ret < 0) {
60     perror("ioctl OMAPFB_QUERY_PLANE");
61     return 1;
62   }
63
64   ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
65   if (ret < 0) {
66     perror("ioctl OMAPFB_QUERY_MEM");
67     return 1;
68   }
69
70   /* must disable when changing stuff */
71   if (pi.enabled) {
72     pi.enabled = 0;
73     ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
74     if (ret < 0) {
75       perror("ioctl OMAPFB_SETUP_PLANE (d)");
76       return 1;
77     }
78   }
79
80   g_old_mem = mi.size;
81   if (mi.size < MEM_SIZE) {
82     mi.size = MEM_SIZE;
83     ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
84     if (ret < 0) {
85       perror("ioctl SETUP_MEM");
86       return 1;
87     }
88   }
89
90   pi.pos_x = SCREEN_WIDTH - WIDTH;
91   pi.pos_y = 0;
92   pi.out_width = WIDTH;
93   pi.out_height = HEIGHT;
94   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
95   if (ret < 0) {
96     perror("ioctl OMAPFB_SETUP_PLANE (e1)");
97     return 1;
98   }
99
100   ret = ioctl(fd, FBIOGET_VSCREENINFO, &g_vi);
101   if (ret < 0) {
102     perror("ioctl FBIOGET_VSCREENINFO");
103     return 1;
104   }
105
106   g_vi.xres = g_vi.xres_virtual = WIDTH;
107   g_vi.yres = HEIGHT;
108   g_vi.yres_virtual = HEIGHT * 2;
109   g_vi.bits_per_pixel = 16;
110
111   ret = ioctl(fd, FBIOPUT_VSCREENINFO, &g_vi);
112   if (ret < 0) {
113     perror("ioctl FBIOPUT_VSCREENINFO");
114     return 1;
115   }
116
117   pi.enabled = 1;
118   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
119   if (ret < 0) {
120     perror("ioctl OMAPFB_SETUP_PLANE (e2)");
121     return 1;
122   }
123
124   memset(&key, 0, sizeof(key));
125   key.key_type = OMAPFB_COLOR_KEY_VID_SRC;
126   key.trans_key = 0x07e0;
127
128   ret = ioctl(fd, OMAPFB_SET_COLOR_KEY, &key);
129   if (ret < 0) {
130     perror("ioctl OMAPFB_SET_COLOR_KEY");
131     return 1;
132   }
133
134   g_screen_base = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE,
135                   MAP_SHARED, fd, 0);
136   if (g_screen_base == MAP_FAILED) {
137     perror("mmap");
138     g_screen = g_screen_base = NULL;
139     return 1;
140   }
141   clear_bytes(g_screen_base, MEM_SIZE);
142   g_screen = g_screen_base;
143   g_flip_id = 0;
144
145   return 0;
146 }
147
148 static int check_layer(int fd)
149 {
150   struct omapfb_plane_info pi;
151   int ret;
152
153   ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
154   if (ret < 0) {
155     perror("ioctl OMAPFB_QUERY_PLANE");
156     return 1;
157   }
158
159   if (!pi.enabled) {
160     printf("something disabled the layer (tv-out?), quitting\n");
161     return 1;
162   }
163
164   return 0;
165 }
166
167 static void remove_layer(int fd)
168 {
169   struct omapfb_plane_info pi;
170   struct omapfb_mem_info mi;
171   int ret;
172
173   if (g_screen_base != NULL) {
174     munmap(g_screen_base, MEM_SIZE);
175     g_screen = g_screen_base = NULL;
176   }
177
178   memset(&pi, 0, sizeof(pi));
179   pi.enabled = 0;
180   ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
181   if (ret < 0)
182     perror("ioctl OMAPFB_SETUP_PLANE (c)");
183
184   ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
185   if (ret < 0) {
186     perror("ioctl OMAPFB_QUERY_MEM");
187     goto out;
188   }
189
190   if (mi.size != g_old_mem) {
191     mi.size = g_old_mem;
192     ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
193     if (ret < 0) {
194       perror("ioctl SETUP_MEM");
195       goto out;
196     }
197   }
198
199 out:;
200 }
201
202 static void flip_fb(int fd)
203 {
204   g_vi.yoffset = g_flip_id * HEIGHT;
205   ioctl(fd, FBIOPAN_DISPLAY, &g_vi);
206   g_flip_id ^= 1;
207   g_screen = g_screen_base + g_flip_id * WIDTH * HEIGHT;
208 }
209
210 static void handle_term(int num)
211 {
212   g_exit = 1;
213   pthread_cond_signal(&g_cond);
214 }
215
216 static void handle_usr1(int num)
217 {
218   g_hide = !g_hide;
219   pthread_cond_signal(&g_cond);
220 }
221
222 static struct {
223   char text[16];
224   int idle;
225 } user_msgs[16];
226
227 static int handle_socket(int sock)
228 {
229   char buf[256], *p;
230   int i, ret;
231
232   ret = recv(sock, buf, sizeof(buf) - 1, 0);
233   if (ret < 0) {
234     perror("recv");
235     return ret;
236   }
237   if (ret == 0)
238     return 0;
239
240   buf[ret] = 0;
241   p = strchr(buf, ':');
242   if (p != NULL) {
243     for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
244       if (user_msgs[i].text[0] == 0)
245         break;
246       if (!strncmp(user_msgs[i].text, buf, p - buf + 1))
247         break;
248     }
249     if (i == ARRAY_SIZE(user_msgs)) {
250       printf("out of user_msg slots\n");
251       return 0;
252     }
253     memcpy(user_msgs[i].text, buf, sizeof(user_msgs[i].text) - 1);
254     user_msgs[i].text[sizeof(user_msgs[i].text) - 1] = 0;
255     user_msgs[i].idle = 0;
256   }
257   else if (!strcmp(buf, "poke")) {
258     // if hidden, show up
259     // if visible, exit
260     if (g_hide) {
261       g_hide = 0;
262       return 1;
263     }
264     g_exit = 1;
265     return 1;
266   }
267   else if (!strcmp(buf, "hide")) {
268     g_hide = 1;
269     return 1;
270   }
271   else if (!strcmp(buf, "show")) {
272     g_hide = 0;
273     return 1;
274   }
275   else if (!strcmp(buf, "quit")) {
276     g_exit = 1;
277     return 1;
278   }
279   else {
280     printf("unknown command: '%s'\n", buf);
281   }
282
283   return 0;
284 }
285
286 #define s_printf(x, y, fmt, ...) \
287   basic_text_out16(g_screen, WIDTH, x, y, fmt, ##__VA_ARGS__);
288
289 static int read_int_file(const char *fn, int *val)
290 {
291   FILE *f;
292   int ret;
293
294   *val = 0;
295   f = fopen(fn, "r");
296   if (f == NULL)
297     return 1;
298   ret = fscanf(f, "%d", val);
299   fclose(f);
300   if (ret != 1)
301     return 1;
302
303   return 0;
304 }
305
306 static void clear_bytes(void *mem, int bytes)
307 {
308   int *p = mem;
309   int l = bytes / 4;
310
311   for (; l >= 4; l -= 4, p += 4)
312     p[0] = p[1] = p[2] = p[3] = 0x07e007e0;
313   for (; l >  0; l--, p++)
314     *p = 0x07e007e0;
315 }
316
317 static void clear(int h)
318 {
319   clear_bytes(g_screen, WIDTH * h * 2);
320 }
321
322 struct proc_stat {
323   unsigned long idlesum;
324   unsigned long sys;
325   unsigned long irqs;
326   unsigned long ctxt;
327 };
328
329 static struct {
330   struct proc_stat last_st;
331   int cpu_usage;
332   int cpu_sys;
333   int irqs;
334   int ctxt;
335   int mem_free;
336   int mem_huge;
337   int mem_free_swap;
338 } stats;
339
340 static void collect_proc_stat(void)
341 {
342   static long hz;
343   struct proc_stat st;
344   char buf[256];
345   unsigned long wait;
346   unsigned long iowait;
347   FILE *f;
348
349   if (hz == 0) {
350     hz = sysconf(_SC_CLK_TCK);
351     printf("hz: %ld\n", hz);
352   }
353
354   f = fopen("/proc/stat", "r");
355   if (f == NULL)
356     return;
357
358   if (fscanf(f, "cpu %*s %*s %lu %lu %lu",
359       &st.sys, &wait, &iowait) != 3)
360     goto out;
361   do {
362     if (fgets(buf, sizeof(buf), f) == NULL)
363       goto out;
364   } while (fscanf(f, "intr %lu", &st.irqs) != 1);
365   do {
366     if (fgets(buf, sizeof(buf), f) == NULL)
367       goto out;
368   } while (fscanf(f, "ctxt %lu", &st.ctxt) != 1);
369
370   st.idlesum = wait + iowait;
371
372   stats.cpu_usage = hz - (st.idlesum - stats.last_st.idlesum);
373   stats.cpu_sys = st.sys - stats.last_st.sys;
374   if (hz != 100) {
375     stats.cpu_usage = stats.cpu_usage * 100 / hz;
376     stats.cpu_sys = stats.cpu_sys * 100 / hz;
377   }
378   stats.irqs = st.irqs - stats.last_st.irqs;
379   stats.ctxt = st.ctxt - stats.last_st.ctxt;
380
381 out:
382   stats.last_st = st;
383   fclose(f);
384 }
385
386 static void collect_mem_stat(void)
387 {
388   static const char fn[] = "/proc/meminfo";
389   uint32_t total = 0, free = 0, buffers = 0, cached = 0;
390   uint32_t anonhuge = 0, hugetot = 0, hugefree = 0, hugepgsz = 0;
391   uint32_t free_swap = 0;
392   char buf[128];
393   FILE *f;
394
395   f = fopen(fn, "r");
396   if (f == NULL)
397     return;
398
399   while (fgets(buf, sizeof(buf), f)) {
400     if (total == 0 && sscanf(buf, "MemTotal: %u", &total) == 1)
401       continue;
402     if (free == 0 && sscanf(buf, "MemFree: %u", &free) == 1)
403       continue;
404     if (buffers == 0 && sscanf(buf, "Buffers: %u", &buffers) == 1)
405       continue;
406     if (cached == 0 && sscanf(buf, "Cached: %u", &cached) == 1)
407       continue;
408     if (free_swap == 0 && sscanf(buf, "SwapFree: %u", &free_swap) == 1)
409       continue;
410     if (anonhuge == 0 && sscanf(buf, "AnonHugePages: %u", &anonhuge) == 1)
411       continue;
412     if (hugetot == 0 && sscanf(buf, "HugePages_Total: %u", &hugetot) == 1)
413       continue;
414     if (hugefree == 0 && sscanf(buf, "HugePages_Free: %u", &hugefree) == 1)
415       continue;
416     if (hugepgsz == 0 && sscanf(buf, "Hugepagesize: %u", &hugepgsz) == 1)
417       continue;
418   }
419
420   fclose(f);
421
422   hugetot = (hugetot - hugefree) * hugepgsz;
423   free += buffers + cached;
424   stats.mem_free = free >> 10;
425   stats.mem_huge = hugetot >> 10;
426   stats.mem_free_swap = free_swap >> 10;
427 }
428
429 struct io_stat {
430   unsigned long sec_r;
431   unsigned long sec_w;
432 };
433
434 static struct {
435   struct io_stat last_st;
436   int read; // kB/s
437   int write;
438 } io_stats;
439
440 static void collect_io_stat(void)
441 {
442   unsigned long sec_r, sec_w;
443   struct io_stat st = { 0, };
444   char buf[128];
445   FILE *f;
446   int i;
447
448   for (i = 0; i < 2; i++) {
449     snprintf(buf, sizeof(buf), "/sys/block/mmcblk%d/stat", i);
450     f = fopen(buf, "r");
451     if (f == NULL)
452       continue;
453
454     if (fscanf(f, "%*s %*s %lu %*s %*s %*s %lu",
455                &sec_r, &sec_w) == 2)
456     {
457       st.sec_r += sec_r;
458       st.sec_w += sec_w;
459     }
460     fclose(f);
461   }
462
463   io_stats.read = st.sec_r - io_stats.last_st.sec_r;
464   io_stats.write = st.sec_w - io_stats.last_st.sec_w;
465   // assuming 512 byte sectors..
466   io_stats.read >>= 1;
467   io_stats.write >>= 1;
468
469   io_stats.last_st = st;
470 }
471
472 struct net_stat {
473   unsigned long rx;
474   unsigned long tx;
475 };
476
477 static struct {
478   struct net_stat last_st;
479   int down; // kB/s
480   int up;
481   int have_wlan;
482   int link;
483   int level;
484 } net_stats;
485
486 static void collect_net_stat(void)
487 {
488   struct net_stat st = { 0, };
489   unsigned long rx, tx;
490   int found = 0;
491   char buf[256];
492   FILE *f;
493
494   net_stats.down = net_stats.up =
495   net_stats.have_wlan = net_stats.link = net_stats.level = 0;
496
497   f = fopen("/proc/net/dev", "r");
498   if (f == NULL)
499     return;
500
501   while (!feof(f)) {
502     static const char fmt[] =
503       "%255s %lu %*s %*s %*s %*s %*s %*s %lu";
504     if (fscanf(f, fmt, buf, &rx, &tx) != 3)
505       continue;
506     if (IS_START(buf, "lo"))
507       continue;
508     st.rx += rx;
509     st.tx += tx;
510     found = 1;
511   }
512   fclose(f);
513
514   if (found) {
515     net_stats.down = st.rx - net_stats.last_st.rx;
516     net_stats.up = st.tx - net_stats.last_st.tx;
517   }
518   net_stats.last_st = st;
519
520   f = fopen("/proc/net/wireless", "r");
521   if (f == NULL)
522     return;
523
524   while (!feof(f)) {
525     int lnk, lvl;
526     if (fscanf(f, "%255s %*x %d. %d.", buf, &lnk, &lvl) != 3)
527       continue;
528     if (IS_START(buf, "lo"))
529       continue;
530     // first one should be enough
531     net_stats.have_wlan = 1;
532     net_stats.link = lnk;
533     net_stats.level = lvl;
534     break;
535   }
536   fclose(f);
537 }
538
539 static void collect_stats(void)
540 {
541   collect_proc_stat();
542   collect_mem_stat();
543   collect_io_stat();
544   collect_net_stat();
545 }
546
547 static char cpu_reg_path[64];
548
549 static void init_stats(void)
550 {
551   char buf[64];
552   int found;
553   FILE *f;
554   int i;
555
556   // find the CPU regulator
557   for (i = found = 0; ; i++) {
558     snprintf(buf, sizeof(buf),
559       "/sys/class/regulator/regulator.%d/name", i);
560     f = fopen(buf, "r");
561     if (f == NULL)
562       break;
563     fgets(buf, sizeof(buf), f);
564     fclose(f);
565     if (IS_START(buf, "vdd_mpu")) {
566       found = 1;
567       break;
568     }
569   }
570
571   if (found)
572     snprintf(cpu_reg_path, sizeof(cpu_reg_path),
573       "/sys/class/regulator/regulator.%d/microvolts", i);
574
575   // do collect so differential stats get sane values
576   collect_stats();
577 }
578
579 static void get_time(int x, int y)
580 {
581   struct timeval tv;
582   struct tm *t;
583   char buf[16];
584
585   gettimeofday(&tv, NULL);
586   t = localtime(&tv.tv_sec);
587   strftime(buf, sizeof(buf), "%T", t);
588   s_printf(x, y, " %s", buf);
589 }
590
591 static void get_cpu(int x, int y)
592 {
593   s_printf(x, y, "CPU:%5d%%", stats.cpu_usage);
594 }
595
596 static void get_sys(int x, int y)
597 {
598   s_printf(x, y, "SYS:%5d%%", stats.cpu_sys);
599 }
600
601 static void get_irq(int x, int y)
602 {
603   s_printf(x, y, "IRQ:%5u", stats.irqs);
604 }
605
606 static void get_cs(int x, int y)
607 {
608   s_printf(x, y, "CS:%6u", stats.ctxt);
609 }
610
611 static void get_cpuf(int x, int y)
612 {
613   static const char fn[] =
614     "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq";
615   int f;
616
617   if (read_int_file(fn, &f))
618     return;
619   s_printf(x, y, "f:%7dM", f / 1000);
620 }
621
622 static void get_cpuv(int x, int y)
623 {
624   int v = 0;
625
626   if (!cpu_reg_path[0] || read_int_file(cpu_reg_path, &v))
627     return;
628   s_printf(x, y, "V:%7.3fV", (float)v / 1000000.0f);
629 }
630
631 static void get_memfree(int x, int y)
632 {
633   s_printf(x, y, "M:%7dM", stats.mem_free);
634 }
635
636 static void get_memhuge(int x, int y)
637 {
638   s_printf(x, y, "H:%7dM", stats.mem_huge);
639 }
640
641 static void get_memswap(int x, int y)
642 {
643   s_printf(x, y, "S:%7dM", stats.mem_free_swap);
644 }
645
646 #define PS_BASE "/sys/class/power_supply/bq27500-0/"
647
648 static void get_bcap(int x, int y)
649 {
650   static const char fnc[] = PS_BASE "capacity";
651   int c;
652
653   if (read_int_file(fnc, &c))
654     return;
655   s_printf(x, y, "C:%7d%%", c);
656 }
657
658 static void get_btime(int x, int y)
659 {
660   static const char fne[] = PS_BASE "time_to_empty_now";
661   static const char fnf[] = PS_BASE "time_to_full_now";
662   int t = 0;
663
664   if (read_int_file(fne, &t) || t == 0) {
665     if (read_int_file(fnf, &t))
666       return;
667   }
668   s_printf(x, y, "t:%4d:%02d", t / 3600, t / 60 % 60);
669 }
670
671 static void get_bvolts(int x, int y)
672 {
673   static const char fnv[] = PS_BASE "voltage_now";
674   int volt = 0;
675
676   if (read_int_file(fnv, &volt))
677     return;
678
679   s_printf(x, y, "V:%7.3fV", (float)volt / 1000000.0f);
680 }
681
682 static void get_bwatts(int x, int y)
683 {
684   static const char fnc[] = PS_BASE "current_now";
685   static const char fnv[] = PS_BASE "voltage_now";
686   int w, cur = 0, volt = 0;
687
688   if (read_int_file(fnc, &cur))
689     return;
690   if (read_int_file(fnv, &volt))
691     return;
692
693   w = (cur / 1000) * (volt / 1000);
694   s_printf(x, y, "P:%7.3fW", (float)w / 1000000.0f);
695 }
696
697 static void get_btemp(int x, int y)
698 {
699   static const char fnt[] = PS_BASE "temp";
700   int t;
701
702   if (read_int_file(fnt, &t))
703     return;
704   s_printf(x, y, "T:%7.1fC", (float)t / 10.0f);
705 }
706
707 static void get_ioread(int x, int y)
708 {
709   s_printf(x, y, "R:%5.1fM/s", (float)io_stats.read / 1000.0f);
710 }
711
712 static void get_iowrite(int x, int y)
713 {
714   s_printf(x, y, "W:%5.1fM/s", (float)io_stats.write / 1000.0f);
715 }
716
717 static void get_netdown(int x, int y)
718 {
719   s_printf(x, y, "D:%5uK/s", net_stats.down / 1000);
720 }
721
722 static void get_netup(int x, int y)
723 {
724   s_printf(x, y, "U:%5uK/s", net_stats.up / 1000);
725 }
726
727 static void get_netsgnl(int x, int y)
728 {
729   s_printf(x, y, "S:%5ddBm", net_stats.level);
730 }
731
732 int main(int argc, char *argv[])
733 {
734   static const char socket_name[] = "\0liveinfo";
735   static const char fbname[] = "/dev/fb2";
736   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
737   pthread_condattr_t condattr;
738   struct sockaddr_un sun;
739   struct timespec ts;
740   struct pollfd pfd;
741   const char *p;
742   int is_hidden = 0;
743   int y = 0, y_max = 0;
744   int fd = -1;
745   int sock;
746   int i, ret;
747
748   // look for other instance
749   sock = socket(PF_UNIX, SOCK_DGRAM, 0);
750   if (sock == -1) {
751     perror("socket PF_UNIX");
752     return 1;
753   }
754
755   memset(&sun, 0, sizeof(sun));
756   sun.sun_family = AF_UNIX;
757   memcpy(sun.sun_path, socket_name, sizeof(socket_name));
758
759   ret = connect(sock, (struct sockaddr *)&sun, sizeof(sun));
760   if (ret == 0) {
761     printf("other instance detected, sending poke command\n");
762     ret = send(sock, "poke", 4, 0);
763     if (ret != 4)
764       perror("send");
765     close(sock);
766     return ret == 4 ? 0 : 1;
767   }
768
769   fd = open(fbname, O_RDWR);
770   if (fd == -1) {
771     fprintf(stderr, "open %s: ", fbname);
772     perror(NULL);
773     return 1;
774   }
775
776   nice(-1);
777
778   // no need for privileges any more
779   p = getenv("SUDO_UID");
780   if (p != NULL) {
781     ret = atoi(p);
782     if (ret != 0) {
783       ret = setuid(ret);
784       if (ret)
785         perror("setuid");
786     }
787   }
788
789   // bind/listen for commands
790   ret = bind(sock, (struct sockaddr *)&sun, sizeof(sun));
791   if (ret != 0) {
792     perror("bind");
793     close(sock);
794     return 1;
795   }
796
797 #if 0
798   ret = listen(sock, 1);
799   if (ret != 0) {
800     perror("listen");
801     close(sock);
802     return 1;
803   }
804 #endif
805
806   pfd.fd = sock;
807   pfd.events = POLLIN | POLLPRI;
808   pfd.revents = 0;
809
810   ret  = pthread_condattr_init(&condattr);
811   ret |= pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
812   ret |= pthread_cond_init(&g_cond, &condattr);
813   pthread_condattr_destroy(&condattr);
814   if (ret != 0) {
815     fprintf(stderr, "cond init failed\n");
816     return 1;
817   }
818
819   ret = setup_layer(fd);
820   if (ret)
821     return ret;
822
823   // clean up on kill too
824   signal(SIGTERM, handle_term);
825   signal(SIGUSR1, handle_usr1);
826
827   pthread_mutex_lock(&mutex);
828   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
829   if (ret != 0) {
830     perror("clock_gettime CLOCK_MONOTONIC");
831     return 1;
832   }
833
834   init_stats();
835
836   while (!g_exit)
837   {
838     // anything on unix socket?
839     ret = poll(&pfd, 1, 0);
840     if (ret < 0) {
841       perror("poll");
842       break;
843     }
844     if (ret > 0) {
845       ret = handle_socket(sock);
846       if (ret < 0)
847         break;
848       continue;
849     }
850
851     // handle hiding
852     if (g_hide) {
853       if (!is_hidden) {
854         remove_layer(fd);
855         is_hidden = 1;
856       }
857       goto do_sleep;
858     }
859     else if (is_hidden) {
860       ret = setup_layer(fd);
861       if (ret)
862         break;
863       is_hidden = 0;
864     }
865
866     if (check_layer(fd) != 0)
867       break;
868
869     collect_stats();
870
871     y += Y_STEP;
872     if (y > y_max)
873       y_max = y;
874     clear(y_max);
875
876     y = 0;
877     get_time   (0, y += Y_STEP);
878     y += Y_STEP / 2;
879     get_cpu    (0, y += Y_STEP);
880     get_sys    (0, y += Y_STEP);
881     get_irq    (0, y += Y_STEP);
882     get_cs     (0, y += Y_STEP);
883     get_cpuf   (0, y += Y_STEP);
884     get_cpuv   (0, y += Y_STEP);
885     y += Y_STEP / 2;
886     s_printf(0, y += Y_STEP, "mem");
887     get_memfree(0, y += Y_STEP);
888     get_memswap(0, y += Y_STEP);
889     if (stats.mem_huge)
890       get_memhuge(0, y += Y_STEP);
891     y += Y_STEP / 2;
892     s_printf(0, y += Y_STEP, "i/o");
893     get_ioread (0, y += Y_STEP);
894     get_iowrite(0, y += Y_STEP);
895     y += Y_STEP / 2;
896     s_printf(0, y += Y_STEP, "net");
897     get_netdown(0, y += Y_STEP);
898     get_netup  (0, y += Y_STEP);
899     if (net_stats.have_wlan)
900       get_netsgnl(0, y += Y_STEP);
901     y += Y_STEP / 2;
902     s_printf(0, y += Y_STEP, "batt");
903     get_bcap   (0, y += Y_STEP);
904     get_btime  (0, y += Y_STEP);
905     get_bvolts (0, y += Y_STEP);
906     get_bwatts (0, y += Y_STEP);
907     get_btemp  (0, y += Y_STEP);
908
909     // print user messages
910     y += Y_STEP;
911     for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
912       if (user_msgs[i].text[0] == 0)
913         break;
914       user_msgs[i].idle++;
915       if (user_msgs[i].idle > 6) {
916         // drop old entry, shift others up
917         memmove(&user_msgs[i], &user_msgs[i + 1],
918           (ARRAY_SIZE(user_msgs) - i - 1) * sizeof(user_msgs[0]));
919         user_msgs[ARRAY_SIZE(user_msgs) - 1].text[0] = 0;
920         // reprocess
921         i--;
922         continue;
923       }
924
925       s_printf(0, y += Y_STEP, user_msgs[i].text);
926     }
927
928     flip_fb(fd);
929
930 do_sleep:
931     ts.tv_sec++;
932     pthread_cond_timedwait(&g_cond, &mutex, &ts);
933   }
934
935   remove_layer(fd);
936   close(fd);
937   close(sock);
938
939   return 0;
940 }
941
942 // vim:expandtab:sw=2:ts=2