Warning: include(../header.inc) [function.include]: failed to open stream: No such file or directory in /home/home4/varun/public_html/cps196/index.php on line 19

Warning: include() [function.include]: Failed opening '../header.inc' for inclusion (include_path='/cs/prophet/httpd/web-docs/include:/cs/prophet/httpd/php/lib/php/extensions/:/cs/prophet/httpd/php/lib/php:/cs/prophet/httpd/php:/cs/prophet/httpd/php/lib/php/extensions/no-debug-non-zts-20060613:.') in /home/home4/varun/public_html/cps196/index.php on line 19

Using printk can be a pain if you have to go rooting through logs to find out if the kernel has deigned to say anything to you. I've written a function called force_printk that writes the output straight to your console. Hopefully you find it useful.
We are going to print to the screen by first constructing the string to be printed and then writing it out by sending it off to the console driver. We're not doing any buffering, you you shouldn't need to flush anything. However, this also means that each call to force_printk is pretty slow, but since this is purely a debugging tool and you're not going to be printing reams of data, that shouldn't matter.
Be sure to #include <force_printk.h> in every file that uses this function.

The force_printk.h file is reproduced here again:

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>

int force_printk(char *format, ...)
{	
  struct tty_struct *current_tty;
  va_list args;
  char out_string[1024];

  va_start(args,format);
  vsnprintf(out_string,sizeof(out_string),format,args);
  va_end(args);
  
  current_tty = current->signal->tty;

  if(current_tty == NULL)
    return -1;
  ((current_tty->driver)->write) (current_tty,out_string,strlen(out_string));
  ((current_tty->driver)->write) (current_tty,"\r\n",2);

  return 0;
}

I'll start from the top and go through it line by line. Here goes:

int force_printk(char *format, ...)
This says we're taking any number of parameters, the first of which will be a character pointer. The rest can be anything.

  va_start(args,format);
  va_end(args);
The va_start and va_end macros are defined in stdarg.h
va_start takes the argument list found on the stack beyond the second argument (format) and builds a va_list structure, which it stores in the first argument (args). We're not passing args by reference since va_start is a macro (and hence we're not really passing anything anywhere)
va_end cleans up after we're done.
All this might seem like black magic, but they're actually very simple macros - they work by looking at the current function's stack.

vsnprintf(out_string,sizeof(out_string),format,args);
vsnprintf does all the nice things that printf does, with some differences:
  1. It writes to a buffer passed as the first parameter (out_string) instead of the standard output. This is important because within the kernel we don't really have a standard output to go to.
  2. It only writes as much as will fit into the buffer, the size of which is the second parameter. This makes sure we don't overflow any buffers. I don't have to explain why buffer overflows in the kernel are a terrible thing.
  3. It takes arguments in va_list form, which we have conveniently collected before. We have to do this because we don't know in advance how many parameters we will have to deal with, and one function can't unwind the stack and look into what a different function was passed. Actually, that's not completely true, but doing so is generally a bad idea.
So when this function exits, we have a string out_string that contains the string we want to output to the screen. Now the real work begins.

  current_tty = current->signal->tty;
Every process in Linux has a structure associated with it, of type task_struct, which contains all sorts of useful information about the process. Find the definition of task_struct in include/linux/sched.h. The current pointer points to the current structure's task_struct. Think of this as the kernel equivalent of the this pointer in C++.
task_structs have a member called signal (also defined in sched.h) which in turn contains a pointer called tty to the current process's console. (Whew!) This is the value we want, so we get that and store it in current_tty. So now we have a pointer to the console we want to write to.

if(current_tty == NULL)
    return -1;
Now we do some error checking. If the console we want to write to doesn't exist, we return an error value. The console could not exist if there is no console attached to the process, such as would happen if this function was called by a daemon, but you will probably not encounter any such situation. For the purposes of the lab, you should never be returned a -1. If you do, let me know.

((current_tty->driver)->write) (current_tty,out_string,strlen(out_string));
Now things get a little more complicated. The tty we were pointing to with current_tty was itself a structure called tty_struct defined in include/linux/tty.h. This structure contains an element called driver, which is another structure (did anyone tell you messing with the kernel was simple?) called tty_driver, which in turn contains a pointer called write to the function that is capable of writing to the specified console. So we call that function and let it write our constructed buffer to the screen.
The parameters are pretty self-explanatory: the tty we're interested in writing to, the string to write, and the length of the string.

((current_tty->driver)->write) (current_tty,"\r\n",2);
Like printf and unlike printk, vsnprintf does not automatically put a newline at the end of your line. I add a newline here. Take it out if you don't want it to do that. Note that the ttys implement the strict ASCII standard, so you need both \r\n instead of being able to get away with just \n.