LinuxQuestions.org
LinuxAnswers - the LQ Linux tutorial section.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 06-29-2013, 10:46 PM   #1
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Rep: Reputation: 88
Minimalist - Detecting key events in C ?


Hi


First, what it intended is NOT this:
Code:
#include <stdio.h>

	main()     /* program which introduces keyboard input */
	{
		int  number;

		printf("Type in a number \n");
		scanf("%d", &number);
		printf("The number you typed was %d\n", number);
	}


	Sample Program Output
	Type in a number
	23
	The number you typed was 23





For raspberry, running headless, I need to code sthg in C
that will monitor my keyboard.

No xinit, x11,... is installed.

Needing a Minimalist - Detecting key events in C ?

On the headless:
This is installed, but headless it has no meaning
$ whereis getty
getty: /sbin/getty /usr/share/man/man8/getty.8.gz

NO Tkinter - since it is intended for minimalist.
in C language, and not c++


getch is fine, -yeah, sure once you see what you type and logged. screen as well.



Here I found sthg but it is well - not so much what searched: not minimalist and linux much oriented.
http://stackoverflow.com/questions/2...ard-event-in-c


there is logkeys, but it is not minimalist:
Code:
/*
  Copyleft (ɔ) 2009 Kernc
  This program is free software. It comes with absolutely no warranty whatsoever.
  See COPYING for further information.
  
  Project homepage: http://code.google.com/p/logkeys/
*/

#include <cstdio>
#include <cerrno>
#include <cwchar>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <csignal>
#include <error.h>
#include <netdb.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <linux/input.h>

#ifdef HAVE_CONFIG_H
# include <config.h>  // include config produced from ./configure
#endif

#ifndef  PACKAGE_VERSION
# define PACKAGE_VERSION "0.1.0"  // if PACKAGE_VERSION wasn't defined in <config.h>
#endif
keyboard at:
/dev/input/by-path/*-kbd

Could you give hints to look into right direction?

Thanks

Last edited by Xeratul; 06-29-2013 at 11:25 PM.
 
Old 06-29-2013, 11:27 PM   #2
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
would it be the solution?

https://bitbucket.org/kevincox/keyst...65c/keystate.c

maybe
 
Old 06-30-2013, 12:11 AM   #3
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 1,942

Rep: Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524Reputation: 524
termios works in many unices, and it is rather simple. Example program: shkeys.c
 
Old 06-30-2013, 12:25 AM   #4
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
Quote:
Originally Posted by NevemTeve View Post
termios works in many unices, and it is rather simple. Example program: shkeys.c
Thanks but it seesm that termios uses the terminal or whatever might be into getty
or else.


I would like to directly look to teh input /dev/input...
(before I did a chmod 777 /dev/input/xx... kbd concerned for testing)

look ... I was in this direction but it outputs not what might be desired

Code:
#include <stdlib.h>
#include <stdio.h>

#include <linux/input.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sysexits.h>
#include <glob.h>

#include <linux/input.h>


// added for later
//#include <curses.h>
#include <curses.h>
#include <time.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>



int main ( int argc, char *argv[], char *env[] )
{

    
    int key;
    FILE *kbd ;
    //if ( argc != 2 )    usage(argc, argv);
    char key_map[KEY_MAX/8 + 1];    //  Create a byte array the size of the number of keys
    int ch ;

    //initscr();
    int over = 0 ; 
    int keyb ; int mask;
    int l1 = 0 ;
    int l2 = 0;
    int counter ; 
    int keyblast=0;
    char bb[5][PATH_MAX];
   int i = 0 ; 
   int j = 0 ; 

 /*
  ls /dev/input/by-path/ -1 | grep kbd
pci-0000:00:1a.0-usb-0:1.5:1.0-event-kbd
pci-0000:00:1a.0-usb-0:1.5:1.1-event-kbd
pci-0000:00:1a.0-usb-0:1.5:1.1-kbd
 */

  i = 1 ; 
  strncpy( bb[i++] , "/dev/input/by-path/pci-0000:00:1a.0-usb-0:1.5:1.0-event-kbd", PATH_MAX);
  strncpy( bb[i++] , "/dev/input/by-path/pci-0000:00:1a.0-usb-0:1.5:1.1-event-kbd", PATH_MAX);
  strncpy( bb[i++] , "/dev/input/by-path/pci-0000:00:1a.0-usb-0:1.5:1.1-kbd", PATH_MAX);
 
    while ( over == 0 ) {
       for ( i = 1 ; ( i<=3 ) ; i++){
            kbd =  fopen( bb[i] , "r");
		    memset(key_map, 0, sizeof(key_map));    //  Initate the array to zero's
		    ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map);    //  Fill the keymap with the current keyboard state


            printf( "%d - %d %d - %d %d\n" , i, keyb , mask, l1 , l2 );       
            fclose( kbd);
        }
       printf (" -------- \n");
    } 


}

Last edited by Xeratul; 06-30-2013 at 12:30 AM.
 
Old 06-30-2013, 01:07 AM   #5
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
OK, I have updated your code:


Quote:
gcc -lncurses termios2.c -o term2
term2 **device**
In sh from dtelnet, the printf all the time flows the screen is only for terminals (e.g. xterm)



So I brought a modded version herewith in attachemnt.

Two questions?
1) cannot it be more lightweight and remove more
stuffs in the include about??

2) Is it possible that the keyboard remain working?
(you take over the dev and keyboard is not working any longer) ??
i.e. to read on the keyboard, but not block all inputs?

In the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>


3) is it possible to avoid using Termios and use an even more lightweight method as I indicated above by reading directly on the /dev/input... and still minimalist?
(not blocking the keyboard)
Attached Files
File Type: txt termios2.txt (2.7 KB, 9 views)

Last edited by Xeratul; 06-30-2013 at 01:12 AM.
 
Old 06-30-2013, 05:00 AM   #6
H_TeXMeX_H
Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269Reputation: 1269
I'm not sure of the exact purpose of what you want, but I did write a keylogger some time ago using code from ventriloctrl.

Code:
#include <linux/input.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	if (argc < 2)
	{
		printf("usage: %s <device>\n", argv[0]);
		return 1;
	}
	
	// !init
	struct input_event ev;
	
	// init
	int fd = open(argv[1], O_RDONLY);
	FILE * infile = fopen ( "/var/log/keys" , "a" );
	unsigned int i = 0;

	while (read(fd, &ev, sizeof(struct input_event)))
	{
		if (ev.type == 1 && ev.value != 0)
		{
			fprintf(infile, "%i\n", ev.code);
			i++;
			if (i == 5)
			{
				i=0;
				fflush(infile);
			}
		}
	}
	return 0;
}
I don't think it can be more minimal. You'll have to add key parsing yourself.
 
Old 06-30-2013, 05:42 AM   #7
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
Quote:
Originally Posted by H_TeXMeX_H View Post
I'm not sure of the exact purpose of what you want, but I did write a keylogger some time ago using code from ventriloctrl.

Code:
#include <linux/input.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	if (argc < 2)
	{
		printf("usage: %s <device>\n", argv[0]);
		return 1;
	}
	
	// !init
	struct input_event ev;
	
	// init
	int fd = open(argv[1], O_RDONLY);
	FILE * infile = fopen ( "/var/log/keys" , "a" );
	unsigned int i = 0;

	while (read(fd, &ev, sizeof(struct input_event)))
	{
		if (ev.type == 1 && ev.value != 0)
		{
			fprintf(infile, "%i\n", ev.code);
			i++;
			if (i == 5)
			{
				i=0;
				fflush(infile);
			}
		}
	}
	return 0;
}
I don't think it can be more minimal. You'll have to add key parsing yourself.


but in your code you would need logkeys dependency ?
so that one has to do before?
sudo logkeys -s -m /home/user/keyboard.map -o /var/log/keys

I would try to avoid installing logkeys, I tried to understand teh logkey opensource, but could not make a much minimalist one, or find sthg very very light.

It must be not very difficult since the input is just here readable:
/dev/input/by-path/*kbd


FILE *fp;
In C, a simple p/f...open could make it (<-- no kinding) and fclose/pclose(fp);





this for instance is minimal and reads some data, but to understand it is another story...
Code:
#include <stdio.h>
#include <string.h>

int main(int argc, char * argv[]){
   FILE * fp_in;
   char * data;

   fp_in = fopen("/dev/input/by-path/pci-0000:00:1a.0-usb-0:1.5:1.0-event-kbd"  ,"r");
   if(fp_in == NULL){
      fprintf(stderr,"Failed to open input by id\n");
   }

   fp_in = fopen("/dev/input/by-path/pci-0000:00:1a.0-usb-0:1.5:1.0-event-kbd","r");

   if(fp_in == NULL){
      fprintf(stderr,"Failed to open input by path\n");
      return 1;
   }

  while(1){
      fscanf(fp_in,data,"%s");
      fprintf(stderr,"%s",data);
  }
  return 0;
}
In my opinion, one shall use: EVIOCGUNIQ ioctl.


So far with EVIOCGUNIQ, you can directly access the /dev/input and avoid terminals/...




This solution may work, but I need to adapt it a bit. Furthermore it is not much minimalist.

Code:
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <linux/input.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static int fd;
static int own_dev = 1;
int grab = 1;

void catch_signal(int signal)
{
  switch ( signal ) {
  case SIGINT:
    /* release device if needed */
    if ( own_dev ) { 
      ioctl(fd, EVIOCGRAB, NULL); 
      own_dev = 0;
    } else {
      ioctl(fd, EVIOCGRAB, &grab);
      own_dev = 1;
    }
    break;
  case SIGSTOP:
  case SIGQUIT:
    exit(0);
    break;
  }
}

int main(int argc, char *argv[])
{
  fd_set rfds; 
  int res;
  int version = -1, ioret = -1; 
  unsigned numevs, c;
  unsigned char read_buffer[sizeof(struct input_event)*3]; /* max 3 events per read */
  struct input_event *currev;
  char device_name[1024];

  struct sigaction sighandler;
  memset(&sighandler, 0, sizeof(sighandler));
  sighandler.sa_handler = catch_signal;
  sigaction(SIGINT, &sighandler, NULL);
  sigaction(SIGQUIT, &sighandler, NULL);

  if ( argc < 2 ) { 
    fprintf(stderr, "Device needed\n");
    return -1;
  }

  FD_ZERO(&rfds);
  fd = open(argv[1], O_RDONLY);
  if ( -1 == fd ) {
    fprintf(stderr, "unable to read from mice\n");
    return -1;
  }

  ioret = ioctl(fd, EVIOCGVERSION, &version);
  ioret = ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name);
  ioret = ioctl(fd, EVIOCGRAB, &grab);
  if ( -1 == ioret ) {
    perror("ioctl()");
  }
  fprintf(stdout, "ver: %d, ret = %d\n", version, ioret);
  printf("device name is: %s\n", device_name);

  FD_SET(fd, &rfds);
  while ( 1 ) {
    res = select(fd + 1, &rfds, NULL, NULL, NULL);
    if ( -1 == res && EINTR == errno ) {
      continue;
    }
    if ( -1 == res ) {
      perror("select() failed");
      fprintf(stderr, "failed to select, fd is %d\n", fd);
      return -1;
    }
    if ( FD_ISSET(fd, &rfds) ) {
      fprintf(stdout, "got some data\n");
      res = read(fd, read_buffer, sizeof(read_buffer));
      if ( -1 == res) {
        fprintf(stderr, "error reading data\n");
        return -1;
      }
      fprintf(stdout, "got %d bytes\n", res);
      numevs = ( res / sizeof(struct input_event) ); /* get how many input events we got */
      fprintf(stdout, "got %u events\n", numevs);
      for ( c = 0; c < numevs; c++ ) {
        currev = (struct input_event *)(read_buffer + (sizeof(struct input_event) * c));
        fprintf(stdout, "event time %ld/%ld\n", currev->time.tv_sec, currev->time.tv_usec);
        fprintf(stdout, "event type = %hd, code = %hd, value = %d\n", currev->type, currev->code, currev->value);
      }
    } else {
      fprintf(stderr, "odd ... no data and we only listen in 1 fd\n");
    }
  }
  return 0;
}

Last edited by Xeratul; 06-30-2013 at 05:58 AM.
 
Old 06-30-2013, 06:14 AM   #8
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
ALMOST SOLVED

THIS IS WHAT I DESIRED, event code is displayed but but

You can compile it with
Code:
gcc -lncurses test.c -o test
./test /dev/input/...
and then to stop it you shall HIT SPACE KEY !!! (<-- important)


The problem is that it is far far not minimalist !!


Code:
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <linux/input.h>
#include <signal.h>
#include <string.h>
#include <errno.h>


#include <curses.h>

static int fd;
static int own_dev = 1;
int grab = 1;

void catch_signal(int signal)
{
  switch ( signal ) {
  case SIGINT:
    /* release device if needed */
    if ( own_dev ) { 
      ioctl(fd, EVIOCGRAB, NULL); 
      own_dev = 0;
    } else {
      ioctl(fd, EVIOCGRAB, &grab);
      own_dev = 1;
    }
    break;
  case SIGSTOP:
  case SIGQUIT:
    exit(0);
    break;
  }
}

int main(int argc, char *argv[])
{
  fd_set rfds; 
  int res;
  int version = -1, ioret = -1; 
  unsigned numevs, c;
  unsigned char read_buffer[sizeof(struct input_event)*3]; /* max 3 events per read */
  struct input_event *currev;
  char device_name[1024];
  int posy, rows, cols ;
  
  
  struct sigaction sighandler;
  memset(&sighandler, 0, sizeof(sighandler));
  sighandler.sa_handler = catch_signal;
  sigaction(SIGINT, &sighandler, NULL);
  sigaction(SIGQUIT, &sighandler, NULL);

  if ( argc < 2 ) { 
    fprintf(stderr, "Device needed\n");
    return -1;
  }
  
  initscr();
  getmaxyx(stdscr, rows, cols); 

  
  FD_ZERO(&rfds);
  fd = open(argv[1], O_RDONLY);
  if ( -1 == fd ) {
    fprintf(stderr, "unable to read from mice\n");
    return -1;
  }

  ioret = ioctl(fd, EVIOCGVERSION, &version);
  ioret = ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name);
  ioret = ioctl(fd, EVIOCGRAB, &grab);
  if ( -1 == ioret ) {
    perror("ioctl()");
  }
  fprintf(stdout, "ver: %d, ret = %d\n", version, ioret);
  printf("device name is: %s\n", device_name);
  posy = 0 ; 
  FD_SET(fd, &rfds);
  
  erase();
  refresh();
  
  scrollok(stdscr, TRUE);
  
  // loop
  while ( 1 ) {
    res = select(fd + 1, &rfds, NULL, NULL, NULL);
    if ( -1 == res && EINTR == errno ) {
      continue;
    }
    if ( -1 == res ) {
      perror("select() failed");
      // fprintf(stderr, "failed to select, fd is %d\n", fd);
      return -1;
    }
    if ( FD_ISSET(fd, &rfds) ) {
      //fprintf(stdout, "got some data\n");
      printw(    "error reading data\n");
      
      res = read(fd, read_buffer, sizeof(read_buffer));
      if ( -1 == res) {
        printw( "error reading data\n");
        return -1;
      }
      // fprintf(stdout, "got %d bytes\n", res);
      numevs = ( res / sizeof(struct input_event) ); /* get how many input events we got */
      // fprintf(stdout, "got %u events\n", numevs);
      
      
      for ( c = 0; c < numevs; c++ ) {
        currev = (struct input_event *)(read_buffer + (sizeof(struct input_event) * c));
              
        
        printw( "event time %ld/%ld\n", currev->time.tv_sec, currev->time.tv_usec);
        printw( "event type = %hd, code = %hd, value = %d\n", currev->type, currev->code, currev->value);
     
        
        
        
        if ( currev->code == 57 ) {
			 endwin();
             exit(0);
		 }
         
      }
    } else {
      fprintf(stderr, "odd ... no data and we only listen in 1 fd\n");
    }
    
    
    // last refresh
    refresh();
  }
  return 0;
}
 
Old 06-30-2013, 07:00 AM   #9
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
would you know if this below code can be heavy for my CPU ?

top says no, but in practice?

Code:
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <linux/input.h>
#include <signal.h>
#include <string.h>
#include <errno.h>


#include <curses.h>

static int fd;
static int own_dev = 1;
int grab = 1;

void catch_signal(int signal)
{
  switch ( signal ) {
  case SIGINT:
    /* release device if needed */
    if ( own_dev ) { 
      ioctl(fd, EVIOCGRAB, NULL); 
      own_dev = 0;
    } else {
      ioctl(fd, EVIOCGRAB, &grab);
      own_dev = 1;
    }
    break;
  case SIGSTOP:
  case SIGQUIT:
    exit(0);
    break;
  }
}

int main(int argc, char *argv[])
{
  fd_set rfds; 
  int res;
  int version = -1, ioret = -1; 
  unsigned numevs, c;
  unsigned char read_buffer[sizeof(struct input_event)*3]; /* max 3 events per read */
  struct input_event *currev;
  char device_name[1024];
  int posy, rows, cols ;
  
  
  struct sigaction sighandler;
  memset(&sighandler, 0, sizeof(sighandler));
  sighandler.sa_handler = catch_signal;
  sigaction(SIGINT, &sighandler, NULL);
  sigaction(SIGQUIT, &sighandler, NULL);

  if ( argc < 2 ) { 
    fprintf(stderr, "Device needed\n");
    return -1;
  }
  
  initscr();
  getmaxyx(stdscr, rows, cols); 

  
  FD_ZERO(&rfds);
  fd = open(argv[1], O_RDONLY);
  if ( -1 == fd ) {
    fprintf(stderr, "unable to read from mice\n");
    return -1;
  }

  ioret = ioctl(fd, EVIOCGVERSION, &version);
  ioret = ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name);
  ioret = ioctl(fd, EVIOCGRAB, &grab);
  if ( -1 == ioret ) {
    perror("ioctl()");
  }
  fprintf(stdout, "ver: %d, ret = %d\n", version, ioret);
  printf("device name is: %s\n", device_name);
  posy = 0 ; 
  FD_SET(fd, &rfds);
  
  erase();
  refresh();
  
  scrollok(stdscr, TRUE);
  
  // loop
  while ( 1 ) {
    res = select(fd + 1, &rfds, NULL, NULL, NULL);
    if ( -1 == res && EINTR == errno ) {
      continue;
    }
    if ( -1 == res ) {
      perror("select() failed");
      // fprintf(stderr, "failed to select, fd is %d\n", fd);
      return -1;
    }
    if ( FD_ISSET(fd, &rfds) ) {
      //fprintf(stdout, "got some data\n");
      printw(    "error reading data\n");
      
      res = read(fd, read_buffer, sizeof(read_buffer));
      if ( -1 == res) {
        printw( "error reading data\n");
        return -1;
      }
      // fprintf(stdout, "got %d bytes\n", res);
      numevs = ( res / sizeof(struct input_event) ); /* get how many input events we got */
      // fprintf(stdout, "got %u events\n", numevs);
      
      
      for ( c = 0; c < numevs; c++ ) {
        currev = (struct input_event *)(read_buffer + (sizeof(struct input_event) * c));
              
        
        printw( "event time %ld/%ld\n", currev->time.tv_sec, currev->time.tv_usec);
        printw( "event type = %hd, code = %hd, value = %d\n", currev->type, currev->code, currev->value);
     
        
        
        
        if ( currev->code == 57 ) {
			 endwin();
             exit(0);
		 }
         
      }
    } else {
      fprintf(stderr, "odd ... no data and we only listen in 1 fd\n");
    }
    
    
    // last refresh
    refresh();
  }
  return 0;
}
 
Old 07-04-2013, 01:50 PM   #10
Xeratul
Senior Member
 
Registered: Jun 2006
Location: Debian Land
Posts: 1,389

Original Poster
Rep: Reputation: 88
After few days of testing, I would like to recommend this last code I have posted. You may further update it. I added many lines, all keydings of all keys of my mini keyboard.

It is very lightweight and reliable. Better than Logkey deb since you can implement it into your own C code.


So one use it as follows:

A allmighty Jukebox or voip, ...



Now listening radio on my jukebox intelligent PI http://www.raspberrypi.org/

It is of course headless
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How to simulate arrow keys and key up / key down events to /dev/input phishGuy22 Linux - Software 1 10-17-2012 04:56 PM
[SOLVED] How can I simulate key strokes events in other processes in linux? RonBigman Programming 2 02-06-2011 02:42 PM
VB Scripting Worklsheet and Key events delta function Programming 3 01-20-2011 10:53 AM
Filter key events MTK358 Programming 5 03-01-2010 01:12 PM
Getting Key and Create events from all window children with Xt LonelyStar Programming 4 09-27-2007 08:27 AM


All times are GMT -5. The time now is 06:28 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration