Initial revision
[blinker.git] / blinker.c
1 #include <unistd.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <sys/kbio.h>
7 #include <sys/types.h>
8 #include <sys/sysctl.h>
9 #include <sys/sysctl.h>
10 #include <sys/socket.h>
11 #include <net/if.h>
12 #include <net/if_mib.h>
13
14 #ifdef USE_X
15 #include <X11/X.h>
16 #include <X11/Xlib.h>
17 #include <X11/XKBlib.h>
18 #endif
19
20 #include <tlog.h>
21 #include <tmalloc.h>
22
23 extern char *optarg;
24
25 static void
26 usage()
27 {
28         fputs(
29                 "Copyright (c) 2009 Teodor Sigaev <teodor@sigaev.ru>. All rights reserved.\n"
30                 "blinker - blink by numlock/scroll leds accordingly with network activity\n"
31                 "./blinker\n",
32                 stdout
33         );
34         exit(1);
35 }
36
37 static
38 unsigned int
39 getifnum(void)
40 {
41     u_int   data    = 0;
42         size_t  datalen = 0;
43         static int      name[] = { 
44                                         CTL_NET,
45                                         PF_LINK,
46                                         NETLINK_GENERIC,
47                                         IFMIB_SYSTEM,
48                                         IFMIB_IFCOUNT 
49                                 };
50
51         datalen = sizeof(data);
52         if (sysctl(name, 5, &data, &datalen, NULL, 0) != 0)
53                 tlog(TL_CRIT|TL_EXIT,"sysctl failed: %s", strerror(errno));
54
55         return data;
56 }
57                                                                                                                                                                                                                                                                                                         
58 static int
59 getifmibdata(int row, struct ifmibdata *data)
60 {
61         size_t  datalen = 0;
62         static  int name[] = { 
63                                                 CTL_NET,
64                                                 PF_LINK,
65                                                 NETLINK_GENERIC,
66                                                 IFMIB_IFDATA,
67                                                 0,
68                                                 IFDATA_GENERAL 
69                                         };
70         datalen = sizeof(*data);
71         name[4] = row;
72
73         if ((sysctl(name, 6, data, &datalen, NULL, 0) != 0) && (errno != ENOENT)) {
74                 tlog(TL_WARN,"sysctl failed: %s", strerror(errno));
75                 return -1;
76         }
77
78         return 0;
79 }
80
81 #define LED_ON  0x01
82 #define LED_OFF 0x02
83
84 typedef struct Blinker {
85         void    (*exec)(struct Blinker *, int, int); 
86         int             TxLed;
87         int             RxLed;
88         int             
89                         unused1:12,
90                         RxOn:2,
91                         TxOn:2,
92                         unused2:16;
93         void    *arg;   
94 } Blinker;
95
96 static void
97 execKbd(struct Blinker * blinker, int RxAction, int TxAction) {
98         if ( !(blinker->RxOn == RxAction && blinker->TxOn == TxAction) ) {
99                 int flag = 0;
100
101                 if (ioctl(0, KDGETLED, &flag) == -1)
102                         tlog(TL_WARN,"unable to get led's statuses: %s",
103                                                 strerror(errno));
104
105                 if (RxAction)
106                         flag |= blinker->RxLed;
107                 else
108                         flag &= ~blinker->RxLed;
109                 if (TxAction)
110                         flag |= blinker->TxLed; 
111                 else
112                         flag &= ~blinker->TxLed;
113
114                 if (ioctl(0, KDSETLED, &flag) == -1)
115                         tlog(TL_WARN,"unable to set NumLock led: %s",
116                                                 strerror(errno));
117
118                 blinker->RxOn = RxAction;
119                 blinker->TxOn = TxAction;
120         }
121 }
122
123 #ifdef USE_X
124 static void
125 execXKbd(struct Blinker * blinker, int RxAction, int TxAction) {
126         static XKeyboardControl value;
127
128         if ( blinker->RxOn != RxAction ) {
129                 value.led = blinker->RxLed;
130                 value.led_mode = (RxAction) ? LedModeOn : LedModeOff;
131                 XChangeKeyboardControl((Display*)blinker->arg, KBLed | KBLedMode, &value);
132         }
133
134         if ( blinker->TxOn != TxAction ) {
135                 value.led = blinker->TxLed;
136                 value.led_mode = (TxAction) ? LedModeOn : LedModeOff;
137                 XChangeKeyboardControl((Display*)blinker->arg, KBLed | KBLedMode, &value);
138         }
139
140         if ( !(blinker->RxOn == RxAction && blinker->TxOn == TxAction) )
141         {
142                 XFlush((Display*)blinker->arg);
143                 blinker->RxOn = RxAction;
144                 blinker->TxOn = TxAction;
145         }
146 }
147 #endif
148
149 typedef struct statData {
150         u_int64_t       ip;
151         u_int64_t       op;
152 } statData;
153
154 static int LetWork = 1;
155
156 static void
157 stopWork(int s) {
158         LetWork = 0;
159 }
160
161 int
162 main(int argc, char *argv[]) {
163         keyboard_info_t info;
164         int     i;
165         Blinker blinker;
166         struct ifmibdata data;
167         statData        curData = {0,0},
168                                 oldData = {0,0};
169
170         while( (i=getopt(argc, argv, "d:h"))!=-1) {
171                 switch(i) {
172                         case 'h':
173                         default:
174                                 usage();
175                 }       
176         }
177
178         opentlog(TL_OPEN_STDERR, TL_INFO, NULL);
179         signal(SIGHUP, stopWork);
180         signal(SIGSTOP, stopWork);
181         signal(SIGINT, stopWork);
182
183         memset(&blinker, 0, sizeof(blinker));
184
185         if (ioctl(0, KDGKBINFO, &info) == 0)
186         {
187                 blinker.TxLed = LED_CAP;
188                 blinker.RxLed = LED_NUM;
189                 blinker.exec = execKbd;
190         } else {
191
192                 tlog(TL_WARN,"unable to obtain keyboard information: %s%s",
193                                                 strerror(errno),
194 #ifdef USE_X
195                                                 (errno == ENOTTY) ? "; Try X-windows interface." :
196 #endif
197                                                 "" ); /* ENOTTY */
198 #ifdef USE_X
199                 { 
200                 Display         *dpy;
201                 XkbDescPtr      xkb;
202
203                 dpy = XOpenDisplay(NULL);
204                 if (dpy == NULL)
205                         tlog(TL_CRIT|TL_EXIT, "Could not open default display");
206
207                 if ( (xkb= XkbGetMap(dpy,0,XkbUseCoreKbd)) == NULL || 
208                                 XkbGetIndicatorMap(dpy,XkbAllIndicatorsMask,xkb)!=Success ||
209                                 XkbGetNames(dpy,XkbAllNamesMask,xkb)!=Success)
210                         tlog(TL_CRIT|TL_EXIT, "Fail to get indicator's descriptions");
211
212                 for (i=0;i<XkbNumIndicators;i++) {
213                         if (xkb->indicators->phys_indicators & (1<<i)) {
214                                 char *name = XGetAtomName(dpy, xkb->names->indicators[i]);
215                                 XkbIndicatorMapPtr map= &xkb->indicators->maps[i];
216
217                                 if (name == NULL || *name == '\0')
218                                         continue;
219
220                                 if (strcmp(name, "Caps Lock") == 0)
221                                         blinker.TxLed = i + 1;
222                                 else if (strcmp(name, "Num Lock") == 0)
223                                         blinker.RxLed = i + 1;
224                                 else
225                                         continue;
226
227                                 if (map->flags & XkbIM_NoExplicit) {
228                                         /* allow control */
229                                         XkbIndicatorMapPtr      newmap = (XkbIndicatorMapPtr)tmalloc(sizeof(XkbIndicatorMapRec));
230
231                                         *newmap = *map;
232                                         newmap->flags &= ~XkbIM_NoExplicit;
233
234                                         if (XkbSetNamedIndicator(dpy, xkb->names->indicators[i], False, False, True, newmap) == False)
235                                                 tlog(TL_WARN,"XkbSetNamedIndicator fails");
236                                 }
237                         }
238                 }
239
240                 if (blinker.RxLed == 0 || blinker.TxLed == 0)
241                          tlog(TL_CRIT|TL_EXIT, "Fail to identify leds");
242
243                 blinker.arg = dpy;
244                 blinker.exec = execXKbd;
245         }
246 #endif
247         }
248
249         /*
250          * switch off leds - initial state
251          */
252         blinker.TxOn = blinker.RxOn = 1;
253         blinker.exec(&blinker, 0, 0);
254
255         while(LetWork) {
256                 oldData = curData;
257                 
258                 memset(&curData, 0, sizeof(curData));
259                 for(i=1;i<=getifnum();i++) {
260                         if (getifmibdata(i, &data) != 0)
261                                 continue;
262                         if (data.ifmd_name[0] == 'l' && data.ifmd_name[1] == 'o' )
263                                 continue;
264
265                         curData.ip += data.ifmd_data.ifi_ipackets;
266                         curData.op += data.ifmd_data.ifi_opackets;
267                 }
268
269                 blinker.exec(
270                                 &blinker, 
271                                 (curData.ip == oldData.ip) ? 0 : 1, /* Tx */
272                                 (curData.op == oldData.op) ? 0 : 1 /* Rx */
273                 );
274
275                 usleep(10000);
276         }
277
278         blinker.exec(&blinker, 0, 0);
279 #ifdef USE_X
280         if (blinker.arg)
281                 XCloseDisplay((Display*)blinker.arg);
282 #endif
283         return 0;
284 }