Add support for multiple network interfaces

Signed-off-by: Roland Stigge <stigge@antcom.de>

diff -Naur tleds-1.05b-orig/Changes tleds-1.05b/Changes
--- tleds-1.05b-orig/Changes	Thu Apr  2 13:13:38 1998
+++ tleds-1.05b/Changes	Thu Mar  7 22:44:04 2002
@@ -1,4 +1,6 @@
 VERSION	DATE		WHAT WAS FIXED OR WHAT WAS/IS NEW
+1.05b11 7 Mar 2002	Support for multiple interfaces
+
 1.05b7	2 Apr 1998	Keeps LEDs deattached also after VT is reset if run
 (beta)			with -c option.
 			eth* default delay changed to 50 ms.
diff -Naur tleds-1.05b-orig/tleds.1 tleds-1.05b/tleds.1
--- tleds-1.05b-orig/tleds.1	Sun Mar 22 21:01:00 1998
+++ tleds-1.05b/tleds.1	Thu Mar  7 23:14:40 2002
@@ -1,4 +1,4 @@
-.TH TLEDS 1 "1998 May 22"
+.TH TLEDS 1 "2002 Mar 07"
 .SH NAME
 tleds, xtleds \- Blinks keyboard LEDs indicating incoming and outgoing network packets.
 .SH SYNOPSIS
@@ -8,7 +8,7 @@
 .RB [ \-d 
 .B N
 ]
-.B interface_name
+.B interface_name ...
 .br
 .B xtleds
 .RB [ \-bchkqv
@@ -16,7 +16,7 @@
 .RB [ \-d 
 .B N
 ]
-.B interface_name
+.B interface_name ...
 .SH DESCRIPTION
 These programs help you monitor network traffic. They blink Scroll-Lock LED
 (Light Emitting Diode)
@@ -62,6 +62,19 @@
 .br
 .B cat
 .B /proc/net/dev
+.br
+It is possible to specify multiple interfaces and to append
+.B ,1
+(for NumLockLED) or
+.B ,2
+(for ScrollLockLED) directly after the respective interface. If this
+is specified, the whole (RX/TX) traffic will be reported on this LED.
+If you specify another appended
+.B ,1
+or
+.B ,2
+the meaning is slightly different: The first number specifies the receive
+LED, the latter the transmit LED. See examples.
 .SH OPTIONS
 .TP
 -b
@@ -122,6 +135,17 @@
 .B -c
 flag, and keeps LEDs detached even over VT resets, IF started as EUID root.
 .TP
+tleds ippp0 eth0 eth1
+Observes the three specified interfaces
+.TP
+tleds eth0,1 eth1,2
+All the traffic of eth0 (RX/TX) will be monitored with NumLockLED and all the
+traffic of eth1 will be monitored with ScrollLockLED.
+.TP
+tleds ippp0,2,1
+The incoming traffic of the ISDN interface ippp0 will be monitored with the
+ScrollLockLED (2) and the outgoing traffic will be monitored with the NumLockLED (1).
+.TP
 xtleds -vbd 200 lo
 Loopback device (lo) and shows version information, delay 200 ms.
 Runs using X-Windows if started from xterm, and VT (ioctl) if started in VT.
@@ -150,6 +174,7 @@
 /etc/X11/XF86Config
 .SH AUTHOR
 tleds and xtleds was made by Jouni Lohikoski <Jouni.Lohikoski@iki.fi>.
+Support for multiple devices by Roland Stigge <roland.stigge@epost.de>.
 .SH COPYRIGHT
 Copyrighted and released under GNU General Public License (GPL).
 .SH URL
diff -Naur tleds-1.05b-orig/tleds.c tleds-1.05b/tleds.c
--- tleds-1.05b-orig/tleds.c	Mon Oct  5 22:32:56 1998
+++ tleds-1.05b/tleds.c	Thu Mar  7 22:36:12 2002
@@ -8,6 +8,8 @@
 <URL: http://www.iki.fi/Jouni.Lohikoski/tleds.html> for more info and for
 the latest version.
 
+* Multiple interfaces support by Roland Stigge <stigge@epost.de>
+
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2 of the License.
@@ -38,7 +40,7 @@
 XkbDisable 	# Needed when EUID non root and Xfree v3.2 or v3.3
 Xleds 2 3	# This line is a must.
 */
-#define VERSION	"1.05beta10"
+#define VERSION	"1.05beta11"
 #define MYNAME	"tleds"
 
 /* Supported kernel version */
@@ -106,6 +108,15 @@
 #else
 #define FIELDCOUNT	11	/* 17 really, in v2.1.97 +/- N */
 #endif
+#define INTPARAMS       3
+
+/* Structs */
+struct interfaceList {
+  struct interfaceList* next;   /* it's a chain            */
+  char* interfaceName;          /* e.g. "eth0"             */
+  int rxled, txled;             /* 1 = NUM | 2 = SCROLL   */
+  int row;                      /* position in device file */
+};
 
 /* Function prototypes */
 void		check_sanity ();
@@ -114,11 +125,14 @@
 void		create_pid_file (pid_t pid, const char* name);
 void		detach_all_vt_leds (int wantDetach);
 ulong		detach_vt_leds (int tty, int wantDetach);
-char*		find_device_line (char* buffer, char* netDeviceName);
+void            sort_interfaces(struct interfaceList** interfaces);
+FILE*           open_device();
+char*		find_device_line (char* buffer, FILE* devFile, char* netDeviceName);
+int		find_device_row (char* netDeviceName);
 inline int	find_max_VT ();
 pid_t		get_old_pid ();
-int		get_sleeptime (int isDefinedByUser, char* interfaceName);
-void		handle_my_argvs (char** interfaceName, int* sleeptime,
+int		get_sleeptime (int isDefinedByUser, struct interfaceList* interfaces);
+void		handle_my_argvs (struct interfaceList** interfaces, int* sleeptime,
 			int argc, char** argv);
 inline int	is_on_X (int ttyFd);
 int		kill_old_process ();
@@ -127,8 +141,9 @@
 void		my_signal_handler (int);
 inline void	my_sleep (struct timeval sleeptimeval);
 void		parent_wants_me_dead (int);
-void 		report_traffic (char** list);
+void 		report_traffic (long received, long transmitted);
 char**		split_on_blank (char* line);
+char**		split_on_comma (char* line);
 inline void	clear_led (int what) { led(what, CLEAR, NOW); }
 inline void	set_led (int what) { led(what, SET, NOW); }
 inline void	toggle_led (int what) { led(what, TOGGLE, NOW); }
@@ -154,7 +169,11 @@
 /* The code */
 int	main (int argc, char* argv [])
 {
-char*	interfaceName;
+struct interfaceList* interfaces;
+struct interfaceList* tempInterfaces;
+char* message;
+long received, transmitted, numlock, scrolllock;
+FILE* devFile;
 char	buffer [MAXLEN];
 ulong	ledVal;	
 char*	tmpPointer;
@@ -164,17 +183,19 @@
 int	wasInDeepSleep;
 struct timeval sleeptimeval;
 
-interfaceName = NULL;
+interfaces = NULL;
 sleeptime = 0;
 check_kernel_version(); /* May die here */
-handle_my_argvs(&interfaceName, &sleeptime, argc, argv);
+handle_my_argvs(&interfaces, &sleeptime, argc, argv);
 check_sanity();	/* Checks and maybe changes the option flags. */
 #ifdef DEBUG
 opt_b = TRUE;	/* We are debugging so don't go to the background */
 #endif
 if (opt_v && !opt_q) {
 	printf(
-	    "%s version %s, GNU GPL (c) 1998 Jouni.Lohikoski@iki.fi\n",
+	    "%s version %s\n"
+	    "GNU GPL (c) 1998 Jouni.Lohikoski@iki.fi\n"
+	    "            2002 roland.stigge@epost.de\n",
 	    MYNAME, VERSION);
 	printf("<URL: http://www.iki.fi/Jouni.Lohikoski/tleds.html>\n");
 }
@@ -192,18 +213,31 @@
 	return 0;
 }
 if (! opt_q) {
-	printf("Setting keyboard LEDs based on %s %s %s %s\n",
-	    "changes of Receive/Transmit\npackets of", interfaceName,
-	    "in", devFileName);
+	printf("Setting keyboard LEDs based on "
+	       "changes of Receive/Transmit\npackets of");
+	tempInterfaces = interfaces;
+	while (tempInterfaces) {
+	  printf(" %s", tempInterfaces->interfaceName);
+	  tempInterfaces = tempInterfaces->next;
+	}
+	printf(" %s %s\n", "in", devFileName);
 	printf("Delay between updates is %d milliseconds.\n",
 	    sleeptime);
 }
-if (! find_device_line(buffer, interfaceName) && !opt_q) {
-	printf(
-	    "There is currently no such interface as %s in %s.\n%s\n",
-	    interfaceName, devFileName,
-	    "Maybe later there will be. Kill me (-k) if ya want.");
+
+sort_interfaces(&interfaces); /* prepare to start right now */
+
+tempInterfaces = interfaces;
+message = "";
+while (tempInterfaces) {
+  if (! tempInterfaces->row && ! opt_q) {
+    printf("There is currently no such interface as %s in %s.\n",
+	   tempInterfaces->interfaceName, devFileName);
+    message = "Maybe later there will be. Kill me (-k) if ya want.\n";
+  }
+  tempInterfaces = tempInterfaces->next;
 }
+printf(message);
 
 if(! opt_b) {
 	if (-1 == (pid = fork())) {
@@ -266,61 +300,148 @@
 
 /* The main loop */
 while (1) {
-	if ((tmpPointer = find_device_line(buffer, interfaceName))) {
-		if (wasInDeepSleep) {
-			wasInDeepSleep = FALSE;
-			detach_all_vt_leds(TRUE);
-		}
-		list = split_on_blank(tmpPointer);
-		report_traffic(list);
-		my_sleep(sleeptimeval);
-	} else {
-		if (! wasInDeepSleep) {
-			wasInDeepSleep = TRUE;
-			detach_all_vt_leds(FALSE);
-			previousActive = (ushort)(MAXVT + 1);
-		}
-		sleep(DEEPSLEEP);
-	}
+  tempInterfaces = interfaces;
+  numlock = 0;
+  scrolllock = 0;
+  devFile = open_device();
+  do {
+    if ((tmpPointer = find_device_line(buffer, devFile, tempInterfaces->interfaceName))) {
+      list = split_on_blank(tmpPointer);
+#if KERNEL2_0
+      received = atol(list[1]);
+      transmitted = atol(list[6]);
+#else
+      received = atol(list[2]);
+      transmitted = atol(list[10]);	/* Kernel v2.1.119 */
+#endif
+      if (tempInterfaces->rxled & 1) numlock += received;
+      if (tempInterfaces->rxled & 2) scrolllock += received;
+      if (tempInterfaces->txled & 1) numlock += transmitted;
+      if (tempInterfaces->txled & 2) scrolllock += transmitted;;
+    }
+    tempInterfaces = tempInterfaces->next;
+  } while (tempInterfaces && tmpPointer);
+  fclose(devFile);
+  if (tmpPointer) {
+    if (wasInDeepSleep) {
+      wasInDeepSleep = FALSE;
+      detach_all_vt_leds(TRUE);
+    }
+    report_traffic(numlock, scrolllock);
+    my_sleep(sleeptimeval);
+  } else {
+    if (! wasInDeepSleep) {
+      wasInDeepSleep = TRUE;
+      detach_all_vt_leds(FALSE);
+      previousActive = (ushort)(MAXVT + 1);
+    }
+    sleep(DEEPSLEEP);
+    sort_interfaces(&interfaces); /* maybe this time it works */
+  }
 }
 return 0;	/* Yeah right, never gets this far. */
 }
 
-char*	find_device_line (char* buffer, char* netDeviceName)
-{
-static long	fileOffset = 0L; 
-register FILE*	devFile;
+/*
+ * find interfaces in device file
+ * and make the list ordered like the device file
+ */
+void sort_interfaces(struct interfaceList** interfaces) {
+  struct interfaceList** interfaces2;
+  struct interfaceList* tempInterfaces;
+  int neighbours;
+
+  /* find row numbers for interfaces */
+  tempInterfaces = *interfaces;
+  while (tempInterfaces) {
+    tempInterfaces->row = find_device_row(tempInterfaces->interfaceName);
+    tempInterfaces = tempInterfaces->next;
+  }
+
+  /* sort in ascending order */
+  while (*interfaces) {
+    interfaces2 = interfaces;
+    while (*interfaces2) {
+      if ((*interfaces)->row > (*interfaces2)->row) { /* swap */
+	if ((*interfaces)->next == *interfaces2)
+	  neighbours = 1;
+	else
+	  neighbours = 0;
+	tempInterfaces = *interfaces;
+	*interfaces = *interfaces2;
+	*interfaces2 = tempInterfaces;
+	tempInterfaces = (*interfaces)->next;
+	(*interfaces)->next = (*interfaces2)->next;
+	(*interfaces2)->next = tempInterfaces;
+	if (neighbours) interfaces2 = &(*interfaces)->next;
+      }
+      interfaces2 = &(*interfaces2)->next;
+    }
+    interfaces = &(*interfaces)->next;
+  }
+}
+
+/* prepare reading statistical lines from device file */
+FILE* open_device() {
+  char	buffer [MAXLEN];
+  static long	fileOffset = 0L; 
+  FILE* devFile;
+  
+  if (! (devFile = fopen(devFileName, "r")) ) {
+    perror(devFileName);
+    exit(1);
+  }
 
-if (! (devFile = fopen(devFileName, "r")) ) {
-	perror(devFileName);
-	exit(1);
-}
-/* Skip two lines. (the header) */
-/* Two choices how to do this. Didn't find any differences in speed. */
+  /* Skip two lines. (the header) */
+  /* Two choices how to do this. Didn't find any differences in speed. */
 #if 0
-fgets(buffer, MAXLEN, devFile);
-fgets(buffer, MAXLEN, devFile);
+  fgets(buffer, MAXLEN, devFile);
+  fgets(buffer, MAXLEN, devFile);
 #else
-if (fileOffset) {
-	fseek(devFile, fileOffset, SEEK_SET);
-} else {
-	fgets(buffer, MAXLEN, devFile);
-	fileOffset += (long)strlen(buffer);
-	fgets(buffer, MAXLEN, devFile);
-	fileOffset += (long)strlen(buffer);
-}
+  if (fileOffset) {
+    fseek(devFile, fileOffset, SEEK_SET);
+  } else {
+    fgets(buffer, MAXLEN, devFile);
+    fileOffset += (long)strlen(buffer);
+    fgets(buffer, MAXLEN, devFile);
+    fileOffset += (long)strlen(buffer);
+  }
 #endif
 
+  return devFile;
+}
+
+char*	find_device_line (char* buffer, FILE* devFile, char* netDeviceName)
+{
+
+while ( fgets(buffer, MAXLEN, devFile) ) {
+	while(isblank(*buffer))
+		buffer++;
+	if (buffer == strstr(buffer, netDeviceName))
+		return buffer;
+}
+return NULL;
+}
+
+int	find_device_row (char* netDeviceName)
+{
+char	tempbuffer [MAXLEN];
+char*	buffer = tempbuffer;
+FILE* devFile;
+int returnValue = 0;
+
+devFile = open_device();
 while ( fgets(buffer, MAXLEN, devFile) ) {
+	returnValue++;
 	while(isblank(*buffer))
 		buffer++;
 	if (buffer == strstr(buffer, netDeviceName)) {
 		fclose(devFile);
-		return buffer;
+		return returnValue;
 	}
 }
 fclose(devFile);
-return NULL;
+return 0;
 }
 
 void	my_sleep (struct timeval sleeptimeval)
@@ -356,30 +477,34 @@
 return list;
 }
 
-void	report_traffic (char** list)
+char** split_on_comma (char* line)
 {
-static long	formerReceived = 0L;
-static long	formerTransmitted = 0L;
-register long	received, transmitted;
+  int i;
+  static char* list [INTPARAMS];
 
-#if KERNEL2_0
-received = atol(list[1]);
-transmitted = atol(list[6]);
-#else
-received = atol(list[2]);
-transmitted = atol(list[10]);	/* Kernel v2.1.119 */
-#endif
+  for (i = 0; i < INTPARAMS; i++) {
+    list[i] = line;
+    while (*line != ',' && *line != '\0') line++;
+    if (*line != '\0') *(line++) = '\0';
+  }
+  return list;
+}
 
-if (received != formerReceived) {
+void	report_traffic (long numlock, long scrolllock)
+{
+static long	formerNumlock = 0L;
+static long	formerScrolllock = 0L;
+
+if (numlock != formerNumlock) {
 	led(NUMLOCKLED, SET, DELAYED);
-	formerReceived = received;
+	formerNumlock = numlock;
 } else {
 	led(NUMLOCKLED, CLEAR, DELAYED);
 }
 
-if (transmitted != formerTransmitted) {
+if (scrolllock != formerScrolllock) {
 	led(SCROLLLOCKLED, SET, FINISH);
-	formerTransmitted = transmitted;
+	formerScrolllock = scrolllock;
 } else {
 	led(SCROLLLOCKLED, CLEAR, FINISH);
 }
@@ -705,10 +830,12 @@
 return (pid_t)returnValue;
 }
 
-void	handle_my_argvs (char** interfaceName, int* sleeptime,
+void	handle_my_argvs (struct interfaceList** interfaces, int* sleeptime,
 		int argc, char* argv [])
 {
 int	c;
+struct interfaceList** tempInterfaces = interfaces;
+char** list;
 
 while(EOF != (c = getopt(argc, argv, "bcd:hkqvV"))) {
 	switch (c) {
@@ -743,16 +870,35 @@
 			/* assert(0); */
 	}
 }
-*interfaceName = argv[optind];
-if (! *interfaceName || ! (*interfaceName)[0]) {
-	opt_h = TRUE; /* We may also have opt_k so we won't get h. */
-	return;
-}
+/* build interface list */
+do { /* new member */
+  if (! argv[optind] || ! (argv[optind])[0]) { /* bad argv */
+	  opt_h = TRUE; /* We may also have opt_k so we won't get h. */
+	  return;
+  }
+  *tempInterfaces = (struct interfaceList*)malloc(sizeof(struct interfaceList));
+  list = split_on_comma(argv[optind]);
+  (*tempInterfaces)->interfaceName = list[0];
+  if (*list[1]) { /* RX LED parameter */
+    (*tempInterfaces)->rxled = atoi(list[1]);
+    if (*list[2]) /* TX LED parameter */
+      (*tempInterfaces)->txled = atoi(list[2]);
+    else
+      (*tempInterfaces)->txled = atoi(list[1]);
+  } else {
+    (*tempInterfaces)->rxled = 1;
+    (*tempInterfaces)->txled = 2;
+  }
+  (*tempInterfaces)->row = 0;
+  (*tempInterfaces)->next = NULL;
+  tempInterfaces = &((*tempInterfaces)->next);
+  optind++;
+} while (optind < argc);
 if (opt_V)
 	printf("Marjo Helena Salmela on %svalehtelija ja paskiainen.\n",
 		"minua kohtaan ollut "); /* Don't ask. */
 if (! *sleeptime)
-	*sleeptime = get_sleeptime(FALSE, *interfaceName);
+	*sleeptime = get_sleeptime(FALSE, *interfaces);
 }
 
 void	check_sanity ()
@@ -765,7 +911,7 @@
 }
 }
 
-int	get_sleeptime (int isDefinedByUser, char* interfaceName)
+int	get_sleeptime (int isDefinedByUser, struct interfaceList* interfaces)
 {
 int	returnValue;
 
@@ -778,11 +924,17 @@
 	return returnValue;
 } else {
 /* Ok, we have to figure ourselves what would be good update delay. */
-	if (interfaceName == strstr(interfaceName, "eth"))
-		returnValue = DEFETHDELAY;
-	else
-		returnValue = DEFPPPDELAY;
-	return returnValue;
+  returnValue = 0;
+  while (interfaces) {
+    if (interfaces->interfaceName == strstr(interfaces->interfaceName, "eth")) {
+      /* Return minimum */
+      if (!returnValue || returnValue > DEFETHDELAY) returnValue = DEFETHDELAY;
+    } else {
+      if (!returnValue || returnValue > DEFPPPDELAY) returnValue = DEFPPPDELAY;
+    }
+    interfaces = interfaces->next;
+  }
+  return returnValue;
 }
 }
 
@@ -812,9 +964,14 @@
 
 void	usage (char* name)
 {
-printf("Usage: %s [-bchkqv] [-d <update_delay>] <interface_name>\n",
+printf("Usage: %s [-bchkqv] [-d <update_delay>] [<interface_name>] ...\n",
     name);
-printf("Example: %s -d 300 ppp0\n", name);
+printf("Example: %s -d 300 ppp0 eth0,1 eth1,2\n", name);
+printf("<interface_name>:\n"
+       "\tinterface (e.g. eth0)\n"
+       "\tOR interface,led (e.g. eth1,1)\n"
+       "\tOR interface,rx_led,tx_led (e.g. eth2,2,1)\n"
+       "led:\n\t1\tNUM LOCK LED\n\t2\tSCROLL LOCK LED\n");
 printf("Options:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
 	"\t-b\tDon't go to the background.",
 	"\t-c\tFix the CapsLED in VTs. Only for EUID root.",
