Search

Creating a proc read entry

After understanding the basics of proc entries and their creation in the post "Creating a proc Entry" In this post we will see how to write a module to create a read only entry under the /proc file system.

To create a proc entry we need to include the file proc_fs.h



d Let us create a function create_new_proc_entry  in which we will call the function create_read_proc_entry as shown below


Note: The following module will not work for kernel versions 3.10 and above. Please refer to the post Creating read only proc entry in kernel versions above 3.10 for the new method of proc entry creation

This should create a read only entry under /proc with the name "hello" having default permissions.
We want to create the entry under /proc directly and not in any sub-directory in /proc hence the third argument is NULL .
The function that would get executed when the proc entry is read is "read_proc" .
We are not passing any data to the function, hence the last argument is NULL


The next step is to implement the read_proc function. The read function's prototype is




Arguments:
*buf : The kernel allocates a page of memory to any process that attempts to read a proc entry. The page pointer points to that buffer of memory into which the data is written.
**start: This pointer is used when the reading of the proc file should not start from the beginning of the file but from a certain offset. For small reads this is generally set to NULL.
off : The offset from the beginning of the file where the file pointer currently points to
count : The number of bytes of data to be read
data : The data passed from the create_read_proc_entry function call.
eof: is set to 1 to indicate end of file



The read_proc function returns an integer value which basically gives the number of bytes of data placed in the page buffer.

In our code we will just put a print statement in the read_proc function.



Note the printing in proc entries is using the command sprintf.

The proc entry should get created as soon as we do insertion of the module. The init function is the one that gets called when the module gets inserted so we add a call to the create_new_proc_entry in the init function of the module.


The proc entry need to be removed once we remove the module, hence add a call to remove_proc_entry in the cleanup function.

In our case it would be


Combining the above mentioned snippets into a module, the code will look as follows.

code: proc_read.c



Makefile :



Using the same makefile as before we can compile this into a .ko file.
Once compiled insert the module using the insmod command




If the insmod is successful then run the command



You should see the output as what ever you have given as print in the read_proc function.

Creating a proc entry - 1

In the past few posts we learnt how to write a small character driver that was capable of reading and writing from an array.
As a user if we want to look into what the kernel is up to, we would need our application to read the data in kernel space. But applications running in the user space can not access the data in the kernel space, in such situations the proc file system comes to rescue.
The proc file system is an interface between the kernel and the userspace. Every entry in the proc file system provides some information from the kernel.
For eg:
 The entry "meminfo"  gives the details of the memory being used in the system.
To read the data in this entry just run
cat /proc/meminfo


Similarly the "modules" entry gives details of all the modules that are currently a part of the kernel.
cat /proc/modules

The proc file system is also very useful when we want to debug a kernel module. While debugging we might want to know the values of various variables in the module or may be the data that module is handling. In such situations we can create a proc entry for our selves and dump what ever data we want to look into in the entry.
We will be using the same example character driver that we created in the previous post to create the proc entry.

The proc entry can also be used to pass data to the kernel by writing into the kernel, so there can be two kinds of proc entries
An entry that only reads data from the kernel space
An entry that reads as well as writes data into and from kernel space.

We will start by creating a proc entry for only reading first and then move to a a proc entry for write.
Creating proc entry:

Kernel provides the following functions to create a proc entry

create_proc_read_entry()
create_proc_entry()

Both of these functions are defined in the file linux/proc_fs.h. 


The create_proc_entry is a generic function that allows to create both read as well as write entries.
create_proc_read_entry is a function specific to create only read entries.

Its possible that most of the proc entries are created to read data from the kernel space that is why the kernel developers have provided a direct function to create a read proc entry.

We will first use the create_proc_read_entry to create our entry and then look into create_proc_entry. 


The proc entries are basically based on the structure "proc_dir_entry". Meaning every proc entry that gets created is represented by a structure of the kind
"proc_dir_entry"  by the kernel.
The structure "proc_dir_entry"   looks as follows in 2.6.33-21


struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
/*
* NULL ->proc_fops means "PDE is going away RSN" or
* "PDE is just created". In either case, e.g. ->read_proc won't be
* called because it's too late or too early, respectively.
*
* If you're allocating ->proc_fops dynamically, save a pointer
* somewhere.
*/
const struct file_operations *proc_fops;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int pde_users; /* number of callers into module in progress */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
};

All the fields might not be clear right now, we will get into them as and when we need.

The prototype of the function create_proc_read_entry is


struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base, 
read_proc_t *read_proc, void * data)

name :  This the name by which entry will be created under the proc file system.
            for eg: if we pass "first_proc_entry"  , then it will create a file by the name
            first_proc_entry  under the /proc directory.
mode  :  mode sets the permissions for the entry created, if 0 is passed it takes    
                system default settings
base : Base is the base directory in which the entry should be created, this is
          useful when you want to create a proc entry under a sub folder in /proc. If
          you pass NULL it will create it under /proc by default
read_proc:  read_proc is a pointer to the function that should be called every time
                the proc entry is read. The function should be implemented in the
                driver. It is this functions which we will use to display what ever data        
                we  want to display to the user.
data :  This is not used by the kernel, but is passed as it is to the read_proc
            function. The data is used by the driver writer to pass any private data
            that has to be passed to the read_proc function.  It can be passed as NULL
             if no data has to be passed.


Once a module gets registered with the kernel, the corresponding proc entry should also be created. So the call to the function create_read_proc_entry is included in the "init" function of the driver.

Removing a proc entry: 

When a module is removed from the kernel, it should also remove any proc entries it created. The function that enables the removal of proc entry is "remove_proc_entry" which has the following prototype
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);


name: Name of the proc entry that has to be removed.
parent: In case the proc entry lies in a subdirectory under the proc filesystem, we
              can pass the subdirectories here.

With this introduction to proc filesystem and the corresponding function calls that will allow us to create the entries we can start writing the code to create our first proc entry.



Writing an Example Driver From Scratch: Chapter -5 User access.

Now that we have our driver ready ,we need user application to talk to our device using the driver that we have written.

As the device only support read and write operations, we will write a simple C code that will just open the device and let the user either write into the device or read from the device.

The device node /dev/char_arr that we created by default would allow only root to do read and write. To let any user not having root privileges to read write we will have to change the permissions of the file, i.e of /dev/char_arr, which can be done by

sudo chmod 777 /dev/char_arr

Now to write our user application, we will use the following system calls.

open ( "file name" , "read or write access")

read ("File handle returned by open", "memory buffer to read the data into", "number of bytes of data to be read" )

write("File handle returned by open", "memory buffer to write data into", "number of bytes of data to be written" )

In our case we want to open the file "/dev/char_arr" and we want to do bot read as well as write operations on it, so the second argument will be O_RDWR

open returns a integer value which will act as a file handle to access the file for the rest of the program.

int fd;
fd = open("/dev/char_arr",O_RDWR);

The value of "fd" obtained above will be used to read and write into the file.

To read from the device file the system call would be

read(fd,read_buf,sizeof(read_buf))

Where read_buf is an array of characters, in which we will store the data that we read from the device.

To write into the device file the system call would be

write(fd,write_buf,sizeof(write_buf))

Where write_buf is an array of characters into which we will store the data that we want tor write into the device.

The header files that would be needed to have access to these system calls is fcntl.h

Using the above system calls provide switch case statement where the user can choose to read or write and then call the corresponding system call.

Once you write the "c" code using the above functions, compile it using gcc compiler, assuming the name of the file having the "c" code is user_app.c .

An example code for the user application is given below

user_app.c



Compile it as follows

cc user_app.c -o user_app

This will generate an executable of the name user_app. Run the this executable.

$ ./user_app

Press r to read from device or w to write the device

(Enter w first time)

Enter the data to be written into device

(Enter any data)

Now run the application again

$ ./user_app

Press r to read from device or w to write the device

(Enter r the second time)

The data in the device is (What ever data you entered in the previous run)



From the above example we can see that the user application was able to store data into the device and retrieve from it when the corresponding calls were made.

Your first sample driver and the user application are ready you can try experimenting with them, and explore further.

Note : It would be safer if you did kernel programming in a virtual machine until you are confident about that you are code will not affect the system in any way.

glibc Error: suffix or operands invalid for `fnstsw' change int to short

If you hit the error "glibc Error: suffix or operands invalid for `fnstsw' chnge int to short" while compliing glibc 2.6 or any software that depends on it, the solution is as follows. 

Open the file which is throwing the error and go to the corresponding line no. There will be a call to fnstsw, and a variable would have been passed along with it some thing like 
__asm__(fnstsw, ..... , var); 
The "var" would be type int, change it to "short". 

That should take care of the error. 

Writing an Example Driver From Scratch: Chapter -4 Inserting into the kernel

Here is the full code of the driver that we have pieced together in the previous three chapters

Note: The code has been tested and found working on version 3.5.4



The above code being a kernel module, it needs a Makefile for compilation.

Makefile:



Note: The above makefile is taken from the Device Drivers book from Alessandro Rubini .

The make file checks if the KERNELRELEASE is set, because we need to know the location where the kernel Makefile is located, as it is that Makefile which we will use to compile our code.

If the KERNELRELEASE is not set then we set the KERNELDIR to the default location of the kernel tree, where we will find the Makefile.

After setting this we call make in the script, but this time we pass the "-c" option to indicate where the kernel makefile is located. The "M" option tells the make file where is the source code of the module that we want to compile into a kernel module. In our case the source code is present in the same folder so we pass value of present working directory.

Note that the name of the object code against "obj-m" in the makefile should be the name of your "c" file for the module, just replace the ".c" with ".o" .

Once you have created the file save it as Makefile (Note the capital "M"), in the same folder where your module is stored.

Open a terminal, and go to the file where you have the module and the Makefile, run the command



If the command does not throw any errors, then your code has been compiled successfully. Run "ls" and see that you should have a file by same name as your "c" code only with the ".ko" as the extension.

To insert the module into the kernel we will use the command "insmod", you will need superuser privileges to run the command.



If there are no errors your module is a part of the kernel now, as we have added a "printk" to print the major number in our init function, run the command "dmesg" and you will find the line printed, with the major number, for eg :



Make a note of the number, we will need it again.

Once we have inserted the module, we need to have a device which will use the driver or the module. So we can create an entry like a device in /dev using the command "mknod"

Run the command



eg:



If there are no errors we have a device ready that we can talk to using our driver.

NOTE: This code will not work with command "cat" , please use the application code in the next chapter. The updated code which will work with "cat" will be the chapter-6.

Next Chapter

User Access

Writing an Example Driver From Scratch: Chapter -3 fops implementatoin

From what we have learnt in the last two chapters, our code should look some thing like this



The next step now is to define the file operations that we wish to implement in our driver. For this we would need a structure of the kind file_operation. The structure is defined in linux/fs.h. The structure is basically a set of function pointers, and a driver can choose to implement what ever set of functions it wants to implement and declare them in the driver. For our case we had planned to support, read,write,open and release operations which can be done as follows.

struct file_operations fops = { read: read, write: write, open: open, release: release };

Please note that the format of the structure is very different from what you would come across in normal "c" structures. This format is specific to the kernel modules.

The column on the left is the name of the operation that we want to support and, the value on the right is the name of the function that will implement the operation. Which means "read" operation will be implemented by a function called "read", "write" operation will be implemented by a function called "write". The function names could be any thing of your choice as long as you use the same name in the structure as well as the actual implementation of the function.

By specifying the function names in this fashion using the file_operations structure, we are informing the kernel which function to call for which operation.

So when kernel wants to read from your device, it will get the pointer to function that will do the read operation from the file_operation structure.

Now that we have declared the operations supported by the driver, we have to actually implement the function in the driver.

Let us start from the "open" operation.

Open:

The open calls takes two arguments, the inode and the file pointer to the file being opened and should return 1 or 0 depending on failure or success of the function, respectively.

int open(struct inode *inode, struct file *filp)

For our device, there is nothing much we need to do in the open as we just have small array as a device.

We will just hold the semaphore so that only one process is allowed to open the device at a given time.

The function that will allow us to hold the semaphore is down_interruptible(struct *semaphore).

So for our device it would be

down_interruptible(&char_arr.sem)

If we fail to hold the semaphore we need to fail gracefully so we a put this call into a "if" condition.

if(down_interruptible(&char_arr.sem)) {
printk(KERN_INFO " could not hold semaphore");
return -1;
}

If any other process is holding the device, we would print the relevant message and fail.

In case there is no other process holding the device, we need to indicate success of the open call by a return 0

Once we have the device open, the next step would be either "read" data from the device or "write" data in to the device.

Let us look at the read function first.

Read :

The read function takes as arguments

A pointer to the struct file

A pointer to a char, which is a buffer into which the data from the device will be read into
A variable of the type size_t, a count of number bytes of data to be transfered into the buffer
A pointer of type loff_t, which is a pointer to the current position or the offset being read in the file.

The read function returns "ssize_t" which is the number of bytes read .

In our implementation of read, we need to put the data in our device, i.e. the array, which is the kernel space to the user space

. So we will make use of the function "copy_to_user" which takes the following arguments

A pointer to the location where data has to be copied , i.e. in our case the buffer that was passed to the read function
A Pointer the location from where the data has to be copied, i.e. in our case the array
A count of the number of bytes to be copied.


The function returns the number of bytes it could not read from the "count" number of bytes requested for. In case of successful read, it returns 0.

Therefore in our driver the call to "copy_to_user" would be

ret = copy_to_user(buff, char_arr.array, count);

Where "ret" is an unsigned long variable.

We will return the "ret" value as a return value of the read function. Thats is all that is required of our simple read function.

Write:

The write function is the opposite of read, we will have to write data into the device, i.e. in our case fill data into the array.

The write function also takes similar arguments as the read

A file pointer, to the file we are writing into.
A char buffer from which the data will be copied to the file or device.
A size_t variable to hold the number of bytes to be written
A pointer of type loff_t which will point to the current location of the file pointer in the file.


Therefore in our case the write function declaration would be

ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp)

In the case of "write" we have to copy data from the user space to the kernel space. To achieve this we will use "copy_from_user" which will transfer the requested number of bytes of data from the user space to the kernel space.

copy_from_user take the similar arguments as the copy_to_user

A pointer to the location where data has to be copied , i.e. in our case this would be the array, char_arr.array.
A Pointer the location from where the data has to be copied, i.e. in our case this would be the pointer to the buffer area.
A count of the number of bytes to be copied


The return value of copy_from_user is the amount of data that was written, in case all the data was written successfully 0 is returned.

So the copy from user function call would be

ret = copy_from_user(char_arr.array, buff, count);

We will have to return the "ret" to indicate the amount of data that was not written successfully.

The only file operation left to be implemented is release.

Release

In our device there is nothing that we have to do in release as the device is only a simple array. We had held a semaphore when we had done the open call, now we will release the semaphore.

A semaphore is released using the function call up(semaphore *)

Therefore we will have to call

up(&char_arr.sem);

Now that we have seen all the four operations that our device supports add them to our driver code. We will see the full code of our example module in the next chapter and also read and write from the device using a user application.

Next Chapter

Inserting into the kernel

Writing an Example Driver From Scratch: Chapter- 2 The init function

This is how our driver code looks as of now. We have the header files and the dummy device array.



The next step is to start with the init function.

The new kernel versions use a cdev structure to keep track of all the character devices, hence we would need a cdev structure for our device too.

struct cdev *kernel_cdev;

Let us call our cdev as kernel_cdev as declared above.
We will use dynamic allocation of major number for our device as that is the safest way to assign a major number to your device.
The function that will help us allocate a major number dynamically is

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);

The arguments being passed to the function are
dev -> The dev_t variable type,which will get the major number that the kernel allocates.
firstminor -> The first minor number in case you are looking for a series of minor numbers for your driver.
count -> The number of contiguous set of major numbers that you want to be allocated.
name -> Name of your device that should be associated with the major numbers. The same name will appear in /proc/devices.

The dev_t variable to be passed to the above function can be declared as follows.

dev_t devno

We will allocate minor numbers starting from 0, which is a common practice.
We need only one device number allocated as it is only a single driver for one device so count=1.
Name = char_arr_dev
you can select any name of your comfort. so our call to alloc_chrdev_region would look like this



Where ret is an "int" variable. If alloc_chrdev_region returns a value less than 0, then the alloc_chrdev_region failed to allocate a major number, and your init function should exit with a error message.
Hence we add a check to confirm the allocation



If the alloc_chrdev_region succeeds then the dev_no variable will have the combination major number that the kernel has allocated to your driver and the first minor number that we had selected.
To extract the major number from the dev_no we can use the macro MAJOR. MAJOR takes as input a dev_t variable and returns the major number in intiger format.



So that after doing an "insmod" of your driver you run the command dmesg and see the major number allocated which is printed by the printk.
If you were allocating the major number statically, then we would need to convert the integer number to dev_t format by combining it with the corresponding minor number which is done by the macro MKDEV.

MKDEV takes two integer numbers as input, Major number and minor number, and converts them into one dev_t type number.

dev_no = MKDEV(Major,0)

Where "dev_no" is a variable of type dev_t. This "dev" can be used for registering the driver.

Once we have the "dev_no" number we can create create our cdev structure.

Allocation:



This will initialize the stand alone "kernel_cdev" structure at runtime.
Next step is allocating the fops to the cdev structure. (We will cover fops in the next chapter).



These two assignments sets up the structure as required for us. Now we can inform the kernel about this cdev structure using the cdev_add call.

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

Where
dev: is the cdev structure that we have setup num: is the dev_t number having the major and minor number in it, the first number the device responds to . count: The number of number of device numbers that are associated with this cdev structure.

In our case the call would as follows



Again, if the "ret" value is less than 0 then it means the kernel could not add the cdev structure and hence we need to exit the init module.



If the cdev_add happens with out any errors we are almost done with the init function. Only thing left is initializing the device semaphore.



Cleanup Function:

Cleanup is pretty simple.We just need to delete the cdev structure using the call cdev_del
void cdev_del(struct cdev *dev);

Which in our case would be



Then unregister the device using

void unregister_chrdev_region(dev_t first, unsigned int count);
Where
first: first dev_t number of a contagious set of drivers that we want to unregister.
count: The number of drivers to be unregistered.

In our case it would be



Now we need to inform the kernel which are our init and cleanup functions
So we make use of the functions module_init and module_exit



Using the knowledge gained above try writing the complete init and cleanup functions, we will look at the code in the beginning of the next chapter.

Next Chapter fops Implementation

libtermcap.so.2 no such file or directory

If you are using a new version of ubuntu, i think 9.04 or above, and you get the error(might work with other distros too)



Here is the work around. libtermacp.so.2 is no longer a part of ubuntu, its obsolete and instead of that libncurses can be used. So run the following commands on your terminal




you will see a set of libncurse.so. files for eg :

/lib/libncurses.so.5
/lib/libncurses.so.5.7 etc


If you do not see such files then you will have to install libncurses, which you can do using the synaptic package manager or




(You can use yum in case you are using redhat).

once you find the libnucrses.so.5.7 file run the following command



Then retry the operation that gave you the libtermcap error, you should not see the error any more.

Writing an Example Driver From Scratch: Chapter-1 Header files

Chapter-1 Header files 

The series of posts explains writing a small example driver right from the scratch. There is no physical device, but we will take an array and consider it as our device for which the driver will be written.
The code is inspired by the example codes provided with the book "The Device Drivers" by Alessandro Rubini
Prerequisites
Basic knowledge of kernel programming and c Programming.

The driver being described is for 2.6.x kernels.

We will call the device  char_dev , which will be a structure as follows

struct device {
        char array[100];
        struct semaphore sem;
}char_dev;

So our device can hold a maximum of 100 characters and its concurrent access can be controlled using the semaphore "sem".

In this driver we will implement the following 4 operations for the device
Open.
Read.
Write.
Close.

As this is a basic driver we will not be implementing any control over the data being accepted from the user.

To begin with let us look at the possible header files we might need to write the driver.
As we are going to write a kernel program and it is going to be a module we would need the following two header files

linux/module.h
linux/kernel.h

Next to do the read and write we will make use of the functions copy__to_user and copy_from_user, for these operations we will need the header file

asm/uaccess.h

To make use of the semaphores as and when needed, we will need

linux/semaphore.h

And as we are going to write a character driver, we will need the following file

linux/cdev.h


So we can begin writing our module by including the required above files




#include < linux/module.h >
#include < linux/kernel.h >
#include < linux/fs.h>
#include < asm/uaccess.h >
#include < linux/semaphore.h >
#include < linux/cdev.h >


The next step is to figure out how do we write the init function. Init function that gets called the moment we insert the module into kernel.


Next Chapter
The init function


Reference books


Linux Kernel Programming (3rd Edition)

Linux Device Drivers, 3rd Edition

Ubuntu 10.04 does not add the windows entry to grub menu

If you installed the Ubuntu 10.04 over your windows and did not see the grub menu list the windows entry here is a work around.

First hold the shift key or press "esc" while booting to view the grub menu and make sure that windows is not listed as some times the grub screen might move too fast to even view what is listed.

If windows is not listed then boot into the ubutnu.
Open a terminal and type
       sudo fdisk -l
It should output something like

  Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          13      102400    7  HPFS/NTFS
Partition 1 does not end on cylinder boundary.
/dev/sda2              13        6375    51097600    7  HPFS/NTFS
/dev/sda3            6375       12749    51200000    7  HPFS/NTFS
/dev/sda4           12749       19458    53888001    5  Extended
/dev/sda5           12749       19070    50777088   83  Linux
/dev/sda6           19071       19458     3109888   82  Linux swap / Solaris

The entries that have HPFS/NTFS  in the system column are your windows entries.
You can access these drives by clicking on "Places" on the top menubar and the then on the partition you want to mount.

You can download the bootinfo script from

http://sourceforge.net/projects/bootinfoscript/files/bootinfoscript/0.55/boot_info_script055.sh/download?use_mirror=biznetnetworks

Run this script and look at the output, it would tell you in which /dev/sdaX is the windows bootmgr installed. Note that number X down.

In the command prompt type
       gksudo gedit /etc/grub.d/40_custom
This should open a text editor with a few lines of text in it.  At the end of this file add the following lines


 menuentry "Windows" {
root='(hd0,X)   {replace X by the number we found above}
        chainloader +1
}

Save the file by clicking on save option on the top and close the file.

Now run the following command on the terminal
   sudo update-grub

Once the command runs successfully restart your system.
Hold the shift key or press "esc" while the system boots to view the grub menu.
You should see windows listed in the options now.
Move the cursor to windows and press "enter" to boot windows.

If you see the erroe "BOOT MGR MISSING" then press cntrl+alt+delte and come to grub menu again.
Move the cursor to windows option and press "e".
you should see a line that has "root='(hd0,1)'" that we entered previously.
Now press "c" which will take you to "grub>" prompt.

At the "grub>" prompt type "ls"
It will list out all the disks that grub is able to recognize for eg.
(hd0) (hd0,1) )(hd0,2) etc...

Type
ls (hd0,1)
and see what the output is, if it does not display as a NTFS  partition or displays the name of your other disks for eg: one of you other windows drives.
try out
ls (hd0,2)
and similarly for all the hard disks present and figure out which ones are your windows drives.
Now type exit which will take you back tou your grub menu.
Again go to windows option press "e"
and change the root=(hd0,X) to which ever new number you have found and press cntrl+X to boot.
This should boot your windows.

Hope this helps.

You tube videos watch and download tool

Minitube is a cool utility that lets you watch as well as down load you tube videos.
When you run this tool you are presented with a search window where you can enter the topic on which you want to watch the videos.
The tool will pull out all the videos related to the search topic and start playing them one after the other and if you want to download any of the videos, just click on the download option and it will be downloaded on to your system.

Sure is a very useful tool.

Installation instruction for debian systems


sudo add-apt-repository ppa:ferramroberto/minitube
sudo apt-get update && sudo apt-get install minitube

You can find out more at 




dash...

If you are running ubuntu 6.06 or higher try the following

ls -l /bin/sh

The output would be some thing like this

lrwxrwxrwx 1 root root 4 2010-07-22 01:20 /bin/sh -> dash

If you look at it, you will realize that /bin/sh is actually a symbolic link to dash. 
Now if you are familiar with shells then the first thought that would come to your mind is, shouldn't it be bash ?. 
Well  no its not a typo by ubuntu, it is actually dash which actually stands for Debian Almquist Shell. 
This change was done not because the default bash does not have the features required, but because the bash is too big and the number of shell instances that are started during the boot process makes the size of the bash as a disadvantage. 
To counters this and make the booting quicker a quicker lighter  version was made and called dash. 
Now to change all the sh to dash in all the boot scripts would not have been very advisable so instead the usual /bin/sh was changed to dash by placing a symbolic link to dash. 

Grub Error 17

 Note: Most of the steps mentioned here are for grub Legacy not for grub2. There are multiple reasons for Grub error 17 and workaround given might or might not work for you. 

If you are encountering Grub error 17 while booting into a dual boot system, there are various causes for it.
If you have single hard disk which you have partitioned to install dual operating systems the simplest solution is to reinstall grub from a live CD.
1.Boot the system from a live cd
2. open a terminal
3. type the following commands
      sudo grub
      find /boot/grub/stage1
           you will see output similar to (hd?,?), the ? replaced by some numbers
     root (hd?,?)  same numbers are you got in the above output
     setup (hd0)
     quit
4. Reboot

You should have your grub up and working if not then you will have to explore the other possible reasons for the failure
The other possibility for the error is if you have multiple hard disks, with each hard disk for one OS, there are chances that grub is looking at the wrong hard disk while booting.

 Check your bios and see the order in which the hard disks are listed and make sure that the one that has Linux installed is the first one.

If that does not work, you will have to boot the system using a live CD.
 Mount the partition that has linux on it .
 Look at the contents of /boot/grub/device.map in that partition and compare the contents with the output of the command "sudo fdisk -l"
The entries in the device map basically map you hard disks between the bios and the  grub.
If the output of fdisk -l shows that linux is installed in /dev/sd?, where ? could be any alphabet.
The order in which the disks are listed in the device.map is the order in which the bios sees it with the number starting from 0 .
Where are the /dev/sd? is the way grub or linux sees it where the ? will start from letter "a".
Check entries are the same else edit them to the proper numbers. (you can use the size to figure out which /dev/sd? should be which hd? )
To edit the device.map file you will need root privileges, you can follow the following steps
1. sudo -i
2. mount /dev/sd? /mnt (replace ? with your drive letter)
3. chroot /mnt
4. cd /boot/grub
5. Edit the device.map to its correct values
6. run the command
        grub -- device.map=device.map
7. Run the command
       root (hd?,0)  ( where hd? is the hard disk on which linux is installed. you can                    
         also  try getting this info using the find command as explained above )
8. setup(hd?)
9. open the file menu.lst and if there are references to the wrong (hd?) as in the device.map then change them too
10. reboot

Hopefully you should see the grub menu back .
  

Creating Bootable USB using Ubuntu

If you have a iso image of the installation of any OS, you  need not write the image onto a cd to do the installation, here is way to easily turn your external hard disk or your pendrive into a bootable install device.

1. Plug in the usb device
2. In you ubuntu go to system->administation->startup Disk Creator.
3. This will open up a window with two fields
      Source Disc image(.iso) or CD
      Using the button "other" just below this option point to the .iso file that you have.
 4. The second option is the "Disk to use". By default your pendrive or the hard disk will be shown there, just click on the write one and click on "Make start up disk"


Once the above steps are done successfully, you can plug your pendrive or harddisk to any system that supports booting from external usb devices and run the installation.
The same steps can also be used to copy an installation "cd" to a pen drive and make the pen derive work as the installation media. For this you just have to put your "cd" into the "cd" drive and in the first step point to the cd drive.

Download Manager for Ubuntu

A download manager was some thing I always wanted in my Linux system and here it is, a nice cool download manager for ubutnu. 
Allowing you to pause resume downloads, search for them etc, in short manage your downloads effectively. 
Info and installation steps at 

Insmod explained

If you are interested in kernel programming for Linux or writing device drivers for linux  "insmod"  would be a command that you would use most often. 
insmod which can be expanded to insert module is a command that is used to insert modules into the linux kernel at runtime. 
A module can be defined any piece of code that is inserted into the kernel at runtime. 


What exactly happens when  "insmod" is used to insert a module into the kernel. 


Lets compare it with a real life scenario.  Assume your system is like a hotel and the memory like the rooms in the hotel,the kernel is the hotel manager and you are a module who wants a room in the hotel. 
The flow chart below gives the standard steps that would be followed if you ask for a room and relates it with what happens on executing the insmod command. 






Note : This is a very generic way of looking at insmod. There a lot of other things that also take place for eg the version dependency is matched, the module format is matched, the symbols used in the module should be available in the kernel. 









Commands to change the color of text and background in a terminal

Here are few commands to change the color of your terminal  background or the text

open a termimal and type the following command

printf '\e[1;34m'

this will make the text in the terminal to appear in blue.

To take it back to default

printf '\e[0;30m'

You can also change the backgound using

printf '\e[1;40m'

This will make the backgound black.

The effects are not permanent so if you close the terminal and open, you will get back your default terminal.

To know more about the working of these commands you can check out

http://bashscript.blogspot.com/2010/01/shell-colors-colorizing-shell-scripts.html

Configuring NTLMAPS on fedora

If you have an internet connection that runs behind a proxy , you could face problems installing packages using yum.
To work around this problem you can use ntlmaps
You can download the ntlmaps rpm from

http://sourceforge.net/projects/ntlmaps/

After downloading open a terminal and go to the directory where you have placed the file and run the command

tar -xzvf  ntlmaps-0.9.9.0.1.tar.gz 


cd ntlmaps-0.9.9.0.1  (or what ever folder got created after untar)


Open a file by the name server.cfg using the "vi" or"gedit" 


Update the following fields as required for your network. 


PARENT_PROXY:


PARENT_PROXY_PORT: 


NT_DOMAIN:


USER:


PASSWORD: 




Save the file and quit. 


In the folder ntlmaps-0.9.9.0.1 run the command 


python main.py &


You should see something like :


NTLM authorization Proxy Server v0.9.9
.....................
Now listening at ....... on port 5865




Press enter to get back your prompt. 


To start using yum run the following commands 


export http_proxy=http://127.0.0.1:5865
export ftp_proxy=ftp://127.0.0.1:5865


If every thing has worked fine, try 


yum update 




It should be able to get the updates. 


Note: If you use the above method, the ntlmaps will run only in the terminal where   you have run the export commands and the "python main.py " command. 



Broadcom Opens its Drivers

For all those Linux lovers who struggled to get their Broadcom cards to work with Linux here is a great news, the struggle is over as Broadcom has decided to open source their drivers.
With it the driver support available in the main line kernel will go up further making Linux even more user friendly :-).
Read the full article at
http://www.linux.com/news/featured-blogs/158:jim-zemlin/361472:good-news-for-linux-users-from-broadcom?utm_source=twitterfeed&utm_medium=twitter

Installing Directfb On Fedora

After much googling,  I have directfb up and running on Fedora 9 and 13.

After installing the fedora from DVD, ( DVD is preferable cause it has gcc and other development packages preinstalled)

Open a terminal and type

yum update.

If you get an error saying it could not connect to the repository try one of the following
1. If you are behind a proxy that needs authentication then download and run the ntlmaps. Set the respective proxies in the terminal using the "export" command

2. In the files /etc/yum.repos.d/fedora-updates.repo and /etc/yum.repos.d/fedora.repo  you will find lines starting with "mirrorlist" commented and the lines starting with the word "baseurl" uncommented. Reverse this, that is remove the "#" from the beginning of the lines that start with "baseurl" and add a "#" to the lines that start with the word "mirriorlist"

3. Check if the site that mentioned in the baseurl is working by putting it in a browser, if it is not get a new url closer to you geographically from http://mirrors.fedoraproject.org/publiclist/Fedora/13/i386/
and replace it in the two files mentioned above.

Once you have yum update working run the following

yum install libpng
yum install libjpeg
yum install freetype
yum install libpng-devel
yum install libjpeg-devel
yum install xorg-x11-xtrans-devel
yum install libsysfs-devel
yum install zlib


If libsysfs and zlib are not available download them from   

libsysfs: http://www.rpmfind.net/linux/rpm2html/search.php?query=libsysfs
zlib: http://www.zlib.net/


yum install libxext-dev
yum install kernel-headers
yum install kernel-devel-"your kernel version"
yum install directfb

If you can not find the directfb package in the repos you can download the rpm for your version from  http://rpm.pbone.net/ and run


rpm -ivh "rpm file name" 




Once the above steps work with out problems open the file  /etc/grub.conf
Note: You will have to have root privileges to edit this file so use sudo

In this file look at the kernel version that you want to work with and under that title go to the line that starts with word"kernel". At the end of this line add  "vga=791".
791 is basically to set the framebuffer resolution to 1024X768.

Now reboot your system.

Log in and run the command as root
/usr/sbin/fbset.

copy the ouput of this command and paste it at the begining of the file
/etc/fb.modes.
Note: you will need root privileges 

Now run the command "dfbdump"  if you do not see any errors your directfb is up and running.

To modify the resolution of the framebuffer you can do the following.
While the system boots press "esc" during the grub loading stage,
Press "e" after highligting the kernel version you want to boot.
Move to the line starting with "kernel"
press "e"
at the end of this line add "vga=ask"
press "b"

The booting will stop and ask you to press enter to view all the possible resolutions,  you can choose the number which ever you like and if it works, set it permanently in the file /etc/grub.conf. Remember to convert the hex number that you see during boot time to decimal.


Have fun with the directfb :-)

Networking Disabled.....

Ok this one is really trivial, but I actually spent aroud half an hour running various commands and then had to google to realize the trouble, so I guess its worth blogging it.

If your networking was wroking all fine and one fine moment you see that the networking symbol on the panel is showing networking disabled.
Then right click on the networking icon , and see if "Enable networking is ticked" ,if no then click on it enable the networking.

Uberstudent is here

For all the ubutnu fans out there, here comes one more reason to fall in love all over again. Ubuntu based distro,  UberStudent, a student oriented version for linux, designed and compiled with only students in mind has been released. (Thanks to anonymous commenter for correction)

http://www.uberstudent.org/mod/resource/view.php?id=4

Some features that seemed interesting on the first read were,
The interface to all the materials available on the cloud over the net.
Tools for writing research papers.
Nice tools to create good presentations.
Skype and pidgin for communication with every one.

Have put the full version for download, Will surely try it out soon, guess you too should grab one :-).

Move the ubuntu 10.04 window button

tasklist_lock, tasklist_unlock are not exported by the kernel any more

If you are running a kernel module that was written before the kernel 2.6.17 was released, you might see code using the tasklist
rcu(tasklist_lock) , rcu(tasklist_unlock).

The codes that do use there symbols will not compile in the kernels after 2.6.17 cause, tasklist is not longer exported by the kernel . So  the modules do not have access to the symbols tasklist_lock and tasklist_unlock

png_info has no member by the name trans

If you get the error

png_info has no member called trans

while compiling directfb the following workaround might help

The name of the values "trans" and the "trans_values" has changed in the structure png_info defined in png.h

Open the file that is generating the error and change the values of

trans to trans_alpha
trans_values to trans_color

this should take care of the errors.

Your session lasted for less than 10 seconds

After doing lots of google and various trials , I was finally able to get the reason for the above problem.

Note: The same error can occur for various reasons, so my work around might or might not work for you.

The problem was with the permissions for the /tmp folder.

The permissions for the /tmp folder should be
rwxrwxrwxt
some one had changed this to
rwx------- 
So other than root no other user was able to start the Xserver session.
once I changed the permissions using :

sudo chmod a+rwxt /tmp

every thing is working fine.

Scripting - Looping

Looping in Scripts 

Let us say that we want to create 100 files, by the name file1,file2,file3...,file100. 
We could  execute the command "touch" with the file name a 100 times manually, which is a very time consuming and inefficient way of doing it. 
In scripting we have a work around for such repetitive job, loops. 

Loops are use when we want to execute a specific set of commands repeatedly a certain number of times or until some condition is not satisfied. 

Bash scripting provides us with the following kinds of loops 

for 
while
until

for : 

The syntax of the "for" loop is 

for  "var" in "list
do

{ block of commands to be executed } 
done 

e.g.: 

#!/bin/bash

for i in 1 2 3
do 
echo $i
done. 

Save the file as first.sh,give execute permission to the script and run it. 
The above example would give the output 
1
2
3

In the "for" loop we pass a list after the "in" keyword. The list can be list of any kind, each time one value of the list would be assigned to the variable and the loop will be executed. 
In the above example shown above, the first time the list was executed "1" was assigned to the variable "i", second time "2" was assigned and so on. 
The length of the list is not fixed, it can be as long as you want. 

e.g.:

for i in *
do 
echo $i
done 

Save the file as for2.sh, give execute permission and run it. 

In the above example we have used "*" in place of the list. 
What this does is repeatedly  assign the name of the files and folders in the present working directory to the variable "i". Thus when the loop executes, the script would act like the "ls" command and list out the contents of the present working directory. 

Bash also provides a special kind of "for" loop, which you would be familiar if you have done "c" programming. 

The syntax of this "for" loop is 

for(("initialise variable";"some conditional statement";"incrementing the variable"))
do
{Block of commands to be executed} 
done

e.g.: 

#!/bin/bash
for((i=0;i<10;i=i+1))
do
echo $i
done

Save the file as for3.sh, Give execute permission and run it.   

In the above format of the for loop, note that we need two "(".  
The first expression is initialising a variable to a value from which we want to start counting. In the above example we have initialised "i" to 0. 

The second expression is a conditional statement, the "for" loop  will keep executing as long as this statement is true. In the above example we have used the condition "i<10", which means keep executing the loop as long as the value of "i" is less than 10. 

The third expression is the value by which the variable should change each time a new iteration of the loop is started. In the above example we have used "i=i+1", which means we are increasing the value of "i" by one each time the for loop begins. 

So the above script will run the command "echo" 10 times, each time incrementing the value of "i" by 1. 

Note that the above format of the for loop is specific to bash, so if you want to run it with out giving the execute permission you will have to use "bash for3.sh" and not "sh for3.sh". 

While loop:

Syntax for while loop. 

while("condition statement")
do

{block of commands to be executed}
done 

The while loop is used when do not know the exact count of number of times we want to
execute the loop,but the loop execution is dependent on certain condition. 
As long as the condition being tested in the conditional statement is true, the loop will keep executing. 
The condition statement is implemented using the "test" command,as shown in the article  "Conditional Statements"

 eg: 

#!/bin/bash 
i=0
while[ $i -lt 5 ]
do 
echo $((i++)) 
done
echo "End of Loop"

Save the above script as while.sh, give execute permission and run it. 
The script should produce the output 
0
1
2
3
4
End of Loop

Note that the increment operator works only in the bash and not in simple bourne shell. 

The loop keeps executing till the expression in the conditional statement of the while loop is true. "i" starts with a value of "0" and each time it enters the loop the value gets incremented. Once the value of "i" becomes 5 the expression "i -lt 5" turns out to be false the script stops entering the loop and jumps to the statement after the loop, i.e. the statement after the "done" statement. 


Until loop: 

The until loop works like opposite of the while loop.
Syntax: 

until [ "conditional statement" ]
do 

{"block of commands'} 
done 

The until loop will keep running as long as the expression in the conditional statement is false, once the expression returns true the loop will exit. 
e.g.: 


#!/bin/bash
i=0
until [ $i -gt 5 ]
do 
echo $((i++)) 
done
echo "End of Loop"

Save the above script as until.sh, give it execute permissions and run it. 
It should produce the following output
0
1
2
3
4
5
End of Loop 

The "i" starts from the value "0", the expression in the until checks if the value of "i" is greater than false, which is false and hence it enters the loop. Each time the loop is executed the value of "i" is incremented by 1. As soon as the value of "i" becomes six, the expression "$i -gt 5" becomes true. Thus the until loop exits. 

Break: 

The break statement in useful when you want to stop a loop from continuing execution. 

 e.g.: 

#!/bin/bash 
i=0
while [ 1 ]
do
echo $((i++))
if [ $i -eq 5 ]
then
break
fi
done

Save the above script as break.sh,give it execute permission and run it. The script should produce the output 

0
1
2
3
4

In the script we have used a "1" in the while loop condition,which means it will always be true, and will keep executing the loop for ever. The break statement comes handy in such situations, when you want to stop a infinite loop is certain condition is met.  
In the script above the value of "i" is compared in the if statement with the "5", and when the value of i becomes 5, the script enters the if block where the break is executed. This causes the script to jump out of the loop it is currently executing, thus ending the script. 

Continue: 

"Continue" is useful when you want to skip to begin of the loop with out executing some of the statements. 
 eg: 

#!/bin/bash 
i=0
while [ $((i++)) -lt 5 ]
do
if [ $i -eq 2 ]
then
continue
fi
echo $i
done

Save the above script as continue.sh, give it execute permission and run it. You should see the output 
1
3
4
5

The script keeps comparing the value of "i" with 2, and when the value becomes "2" it enters the if block where the "continue" gets executed which causes the script to skip to the beginning whit out executing the remaining statements in the script. That is why the value "2" does not get printed in the output while rest of them do get printed.  


Try it out: 
1. Write a script that will list out only the files in your current folder. (Hint use the "test" and the "for loop")
2. Write a script that will create a back up of every file in your directory in a subdirectory by the name "backup". 
3. Write a script that will list only the first 5 latest modified files in your current directory. 
(Hint: Use "for i in `ls -t`" along with "break") 
4. Write a script that will prompt the user for a file name, and search for the same in the current directory. If the file exists echo "File present" else echo  "File not found". 
5. Write a script that will give execute permission to all the files that end with the extension .sh. (Hint: use "for i in *.sh" )