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