add .gitignore
[blinker.git] / blinker.c
1 /*
2  * Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *        notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *        notice, this list of conditions and the following disclaimer in the
12  *        documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *        may be used to endorse or promote products derived from this software
15  *        without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <sys/kbio.h>
36 #include <sys/types.h>
37 #include <sys/sysctl.h>
38 #include <sys/sysctl.h>
39 #include <sys/socket.h>
40 #include <net/if.h>
41 #include <net/if_mib.h>
42
43 #ifdef USE_X
44 #include <X11/X.h>
45 #include <X11/Xlib.h>
46 #include <X11/XKBlib.h>
47 #endif
48
49 #include <tlog.h>
50 #include <tmalloc.h>
51
52 extern char *optarg;
53
54 static void
55 usage()
56 {
57         fputs(
58                 "Copyright (c) 2009 Teodor Sigaev <teodor@sigaev.ru>. All rights reserved.\n"
59                 "blinker - blink by numlock/scroll leds accordingly with network activity\n"
60                 "./blinker\n",
61                 stdout
62         );
63         exit(1);
64 }
65
66 static
67 unsigned int
68 getifnum(void)
69 {
70     u_int   data    = 0;
71         size_t  datalen = 0;
72         static int      name[] = { 
73                                         CTL_NET,
74                                         PF_LINK,
75                                         NETLINK_GENERIC,
76                                         IFMIB_SYSTEM,
77                                         IFMIB_IFCOUNT 
78                                 };
79
80         datalen = sizeof(data);
81         if (sysctl(name, 5, &data, &datalen, NULL, 0) != 0)
82                 tlog(TL_CRIT|TL_EXIT,"sysctl failed: %s", strerror(errno));
83
84         return data;
85 }
86                                                                                                                                                                                                                                                                                                         
87 static int
88 getifmibdata(int row, struct ifmibdata *data)
89 {
90         size_t  datalen = 0;
91         static  int name[] = { 
92                                                 CTL_NET,
93                                                 PF_LINK,
94                                                 NETLINK_GENERIC,
95                                                 IFMIB_IFDATA,
96                                                 0,
97                                                 IFDATA_GENERAL 
98                                         };
99         datalen = sizeof(*data);
100         name[4] = row;
101
102         if ((sysctl(name, 6, data, &datalen, NULL, 0) != 0) && (errno != ENOENT)) {
103                 tlog(TL_WARN,"sysctl failed: %s", strerror(errno));
104                 return -1;
105         }
106
107         return 0;
108 }
109
110 #define LED_ON  0x01
111 #define LED_OFF 0x02
112
113 typedef struct Blinker {
114         void    (*exec)(struct Blinker *, int, int); 
115         int             TxLed;
116         int             RxLed;
117         int             
118                         unused1:12,
119                         RxOn:2,
120                         TxOn:2,
121                         unused2:16;
122         void    *arg;   
123 } Blinker;
124
125 static void
126 execKbd(struct Blinker * blinker, int RxAction, int TxAction) {
127         if ( !(blinker->RxOn == RxAction && blinker->TxOn == TxAction) ) {
128                 int flag = 0;
129
130                 if (ioctl(0, KDGETLED, &flag) == -1)
131                         tlog(TL_WARN,"unable to get led's statuses: %s",
132                                                 strerror(errno));
133
134                 if (RxAction)
135                         flag |= blinker->RxLed;
136                 else
137                         flag &= ~blinker->RxLed;
138                 if (TxAction)
139                         flag |= blinker->TxLed; 
140                 else
141                         flag &= ~blinker->TxLed;
142
143                 if (ioctl(0, KDSETLED, flag) == -1)
144                         tlog(TL_WARN,"unable to set NumLock led: %s",
145                                                 strerror(errno));
146
147                 blinker->RxOn = RxAction;
148                 blinker->TxOn = TxAction;
149         }
150 }
151
152 #ifdef USE_X
153 static void
154 execXKbd(struct Blinker * blinker, int RxAction, int TxAction) {
155         static XKeyboardControl value;
156
157         if ( blinker->RxOn != RxAction ) {
158                 value.led = blinker->RxLed;
159                 value.led_mode = (RxAction) ? LedModeOn : LedModeOff;
160                 XChangeKeyboardControl((Display*)blinker->arg, KBLed | KBLedMode, &value);
161         }
162
163         if ( blinker->TxOn != TxAction ) {
164                 value.led = blinker->TxLed;
165                 value.led_mode = (TxAction) ? LedModeOn : LedModeOff;
166                 XChangeKeyboardControl((Display*)blinker->arg, KBLed | KBLedMode, &value);
167         }
168
169         if ( !(blinker->RxOn == RxAction && blinker->TxOn == TxAction) )
170         {
171                 XFlush((Display*)blinker->arg);
172                 blinker->RxOn = RxAction;
173                 blinker->TxOn = TxAction;
174         }
175 }
176 #endif
177
178 typedef struct statData {
179         u_int64_t       ip;
180         u_int64_t       op;
181 } statData;
182
183 static int LetWork = 1;
184
185 static void
186 stopWork(int s) {
187         LetWork = 0;
188 }
189
190 int
191 main(int argc, char *argv[]) {
192         keyboard_info_t info;
193         int     i;
194         Blinker blinker;
195         struct ifmibdata data;
196         statData        curData = {0,0},
197                                 oldData = {0,0};
198
199         while( (i=getopt(argc, argv, "d:h"))!=-1) {
200                 switch(i) {
201                         case 'h':
202                         default:
203                                 usage();
204                 }       
205         }
206
207         opentlog(TL_OPEN_STDERR, TL_INFO, NULL);
208         signal(SIGHUP, stopWork);
209         signal(SIGSTOP, stopWork);
210         signal(SIGINT, stopWork);
211
212         memset(&blinker, 0, sizeof(blinker));
213
214         if (ioctl(0, KDGKBINFO, &info) == 0) {
215                 blinker.TxLed = LED_CAP;
216                 blinker.RxLed = LED_NUM;
217                 blinker.exec = execKbd;
218         } else {
219
220                 tlog(TL_WARN,"unable to obtain keyboard information: %s%s",
221                                                 strerror(errno),
222 #ifdef USE_X
223                                                 (errno == ENOTTY) ? "; Try X-windows interface." :
224 #endif
225                                                 "" ); /* ENOTTY */
226 #ifdef USE_X
227                 { 
228                 Display         *dpy;
229                 XkbDescPtr      xkb;
230
231                 dpy = XOpenDisplay(NULL);
232                 if (dpy == NULL)
233                         tlog(TL_CRIT|TL_EXIT, "Could not open default display");
234
235                 if ( (xkb= XkbGetMap(dpy,0,XkbUseCoreKbd)) == NULL || 
236                                 XkbGetIndicatorMap(dpy,XkbAllIndicatorsMask,xkb)!=Success ||
237                                 XkbGetNames(dpy,XkbAllNamesMask,xkb)!=Success)
238                         tlog(TL_CRIT|TL_EXIT, "Fail to get indicator's descriptions");
239
240                 for (i=0;i<XkbNumIndicators;i++) {
241                         if (xkb->indicators->phys_indicators & (1<<i)) {
242                                 char *name = XGetAtomName(dpy, xkb->names->indicators[i]);
243                                 XkbIndicatorMapPtr map= &xkb->indicators->maps[i];
244
245                                 if (name == NULL || *name == '\0')
246                                         continue;
247
248                                 if (strcmp(name, "Caps Lock") == 0)
249                                         blinker.TxLed = i + 1;
250                                 else if (strcmp(name, "Num Lock") == 0)
251                                         blinker.RxLed = i + 1;
252                                 else
253                                         continue;
254
255                                 if (map->flags & XkbIM_NoExplicit) {
256                                         /* allow control */
257                                         XkbIndicatorMapPtr      newmap = (XkbIndicatorMapPtr)tmalloc(sizeof(XkbIndicatorMapRec));
258
259                                         *newmap = *map;
260                                         newmap->flags &= ~XkbIM_NoExplicit;
261
262                                         if (XkbSetNamedIndicator(dpy, xkb->names->indicators[i], False, False, True, newmap) == False)
263                                                 tlog(TL_WARN,"XkbSetNamedIndicator fails");
264                                 }
265                         }
266                 }
267
268                 if (blinker.RxLed == 0 || blinker.TxLed == 0)
269                          tlog(TL_CRIT|TL_EXIT, "Fail to identify leds");
270
271                 blinker.arg = dpy;
272                 blinker.exec = execXKbd;
273         }
274 #endif
275         }
276
277         /*
278          * switch off leds - initial state
279          */
280         blinker.TxOn = blinker.RxOn = 1;
281         blinker.exec(&blinker, 0, 0);
282
283         while(LetWork) {
284                 oldData = curData;
285                 
286                 memset(&curData, 0, sizeof(curData));
287                 for(i=1;i<=getifnum();i++) {
288                         if (getifmibdata(i, &data) != 0)
289                                 continue;
290                         if (data.ifmd_name[0] == 'l' && data.ifmd_name[1] == 'o' )
291                                 continue;
292
293                         curData.ip += data.ifmd_data.ifi_ipackets;
294                         curData.op += data.ifmd_data.ifi_opackets;
295                 }
296
297                 blinker.exec(
298                                 &blinker, 
299                                 (curData.ip == oldData.ip) ? 0 : 1, /* Tx */
300                                 (curData.op == oldData.op) ? 0 : 1 /* Rx */
301                 );
302
303                 /*
304                  * 25 times per second
305                  */
306                 usleep(40000);
307         }
308
309         blinker.exec(&blinker, 0, 0);
310 #ifdef USE_X
311         if (blinker.arg)
312                 XCloseDisplay((Display*)blinker.arg);
313 #endif
314         return 0;
315 }