//------------------------------------------------------------------- // channels.c // // This module will install a set of kernel facilities for use // by one process in posting a message to another process. It // is currently under construction. // // NOTE: This module expects our 'myexport.o' to be installed. // // programmer: ALLAN CRUSE // date begun: 07 NOV 2004 -- implemented make operation // revised on: 10 NOV 2004 -- added open, post, and pend // completion: //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_read_entry() #include // for copy_to/from_user() #include "channels.h" // for constants and functions #define N_ELTS 16 // maximum number of channels typedef struct { pid_t ownr; pid_t user; void * base; unsigned long size; wait_queue_head_t rq; wait_queue_head_t wq; } CHANNEL; static char modname[] = "channels"; static unsigned long save_old_syscall; extern unsigned long * sys_call_table; static CHANNEL channel[ N_ELTS ]; static int my_get_info( char *buf, char **start, off_t off, int count ) { int i, len; len = sprintf( buf+len, "\n%s\n", modname ); for (i = 0; i < N_ELTS; i++) { len += sprintf( buf+len, "%2d: ", i ); len += sprintf( buf+len, "base=%08X ", channel[ i ].base ); len += sprintf( buf+len, "size=%-8d ", channel[ i ].size ); len += sprintf( buf+len, "ownr=%-5d ", channel[ i ].ownr ); len += sprintf( buf+len, "user=%-5d ", channel[ i ].user ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); return len; } static asmlinkage long my_syscall( int cmd, int key, char *buf, int len ) { pid_t pid = current->pid; int idx, more; void *where; switch ( cmd ) { case CHAN_MAKE: for (idx = 0; idx < N_ELTS; idx++) if ( channel[ idx ].ownr == 0 ) break; if ( idx == N_ELTS ) return -ENOSPC; where = kmalloc( INFO_LENGTH, GFP_KERNEL ); if ( !where ) return -ENOMEM; channel[ idx ].base = where; channel[ idx ].size = 0; channel[ idx ].user = 0; channel[ idx ].ownr = pid; return idx; case CHAN_OPEN: if (( key < 0 )||(key >= N_ELTS )) return -EINVAL; if ( channel[ key ].ownr == 0 ) return -ENOTTY; if ( channel[ key ].user != 0 ) return -EBUSY; if ( channel[ key ].ownr == pid ) return -EPERM; channel[ key ].user = pid; return 0; // <-- Is this what we want? case CHAN_SHUT: break; case CHAN_POST: if (( key < 0 )||(key >= N_ELTS )) return -EINVAL; if ( channel[ key].ownr != pid ) return -EPERM; while ( channel[ key ].size != 0 ) { interruptible_sleep_on( &channel[ key ].wq ); if ( signal_pending( current ) ) return -EINTR; } if ( len > INFO_LENGTH ) len = INFO_LENGTH; where = channel[ key ].base; more = copy_from_user( where, buf, len ); if ( more ) return -EFAULT; channel[ key ].size = len; wake_up_interruptible( &channel[ key ].rq ); return len; case CHAN_PEND: if (( key < 0 )||(key >= N_ELTS )) return -EINVAL; if ( channel[ key].user != pid ) return -EPERM; while ( channel[ key ].size == 0 ) { interruptible_sleep_on( &channel[ key ].rq ); if ( signal_pending( current ) ) return -EINTR; } if ( len > channel[ key ].size ) len = channel[ key ].size; where = channel[ key ].base; more = copy_to_user( buf, where, len ); if ( more ) return -EFAULT; channel[ key ].size = 0; wake_up_interruptible( &channel[ key ].wq ); return len; case CHAN_KILL: break; } return -EINVAL; } int init_module( void ) { int i; printk( "<1>\nInstalling \'%s\' module\n", modname ); // initialize the stashes for (i = 0; i < N_ELTS; i++) { init_waitqueue_head( &channel[ i ].wq ); init_waitqueue_head( &channel[ i ].rq ); } // install the system-call save_old_syscall = sys_call_table[ OBSOLETE_ID ]; sys_call_table[ OBSOLETE_ID ] = (unsigned long)my_syscall; // create the pseudo-file create_proc_info_entry( modname, 0, NULL, my_get_info ); return 0; // SUCCESS } void cleanup_module( void ) { int i; // restore sys_call_table[] sys_call_table[ OBSOLETE_ID ] = save_old_syscall; // remove the pseudo-file remove_proc_entry( modname, NULL ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");