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