/* * fibonacci v0.2, (c) jdimpson 1997, additional code Humberto R. * Baptista 1999. * * The framework for this code came from totem v2.0.5, by lilo (c) 1996, * as well as from the kernel sources, as totem doesn't implement * a write routine for itself, but fibonacci does. * * 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; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Only works in kernel mode: force on the macros */ #ifndef MODULE # define MODULE #endif #ifndef __KERNEL__ # define __KERNEL__ #endif /* makes kernel version readable (stolen frpm ftape.h): */ #define KERNEL_VER(major,minor,sublvl) (((major)<<16)+((minor)<<8)+(sublvl)) /* * Various required header files. */ #include #include #include #include #include /* * Functions. */ int init_module(void); int do_fib(int start); #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) static int fib_read(struct inode * inode, struct file * file, char *buf, int len); static int fib_write(struct inode *, struct file *, const char *, int); #elif LINUX_VERSION_CODE >= KERNEL_VER(2,2,0) static ssize_t fib_read( struct file *, char *, size_t, loff_t *); static ssize_t fib_write(struct file *, const char *, size_t, loff_t *); #else #error "Hey: I do not know what is the type of read/write in file_operations struct in this kernel version." #endif int fib_atoi (const char * str); static int fib_indodeval = 0; static int fib_seed = 0; static int fib_boole = 0; void cleanup_module(void); /* * file operations for /proc/fib */ struct file_operations fib_operations = { NULL, /* lseek */ fib_read, /* read */ fib_write, /* write */ NULL, /* readdir */ NULL, /* select */ NULL, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* can't fsync */ }; /* * inode operations for /proc/fib */ struct inode_operations fib_inode_operations = { &fib_operations, /* file-ops */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; /* * Directory entry for /proc/fib, read-write. */ struct proc_dir_entry fib_dir = { 0, /* low_ino */ 3, /* namelen */ "fib", /* name */ S_IFREG | S_IRUGO |S_IWUGO, /* mode */ 1, /* nlink */ 0, /* uid */ 0, /* gid */ 0, /* size */ &fib_inode_operations, /* inode operations */ NULL /* get_info: NOTE! any function placed here seems to be ignored if inode operations (above) is not null (so and *_read() function needs to be defined there */ }; /* * compute fibonacci, iteratively, of course! * Yes, I know this doesn't belong in the kernel. I just wanted * something mildly interesting to be computed while I tried to figure * out how to have a read-write /proc interface. If you don't want it, * don't use it! * * Also, be careful how big a number you give to /proc/fib, as the * kernel will stop EVERYTHING else and try to solve your problem. * * Hmm, this is rather like Eddy the computer in the _Heart_of_Gold_ * spaceship from the _Hitchhiker's_Guide_to_the_Galaxy_ series, by * Douglas Adams. In it, Arthur Dent, the protagonist, passionately * requests the food replicators to make him some tea. The replicator, * which had never heard of tea, consults the central computer, Eddy, * for help. Eddy gets hung up on the problem and devotes * all ships resources to solving it, including the defence and * propulsion systems. This was rather inconvenient, as the ship was * under attack at that moment. * * Of course, they did escape. I won't tell you how, but involved * infinite improbability, a sperm whale, and and potted plant... */ int do_fib (int start) { int a = 0, b = 1, hold; if (start < 0) return 0; while (start > 0) { hold = b; b = b + a; a = hold; start--; } return a; } /* * Initialize the module. */ int init_module(void) { /* * Try to register the file in the root directory of the proc * filesystem. Dynamic registration means we won't provide a fixed * inode number, but rather the registration code will allocate one for * us. This is safer, since we never know when someone will add a new * fixed inode number to the filesystem. Possible returns are 0 * (success) and -ENOENT (failure), and since there's only one error we * won't worry unduly about messages. If successful, continue. */ #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) if (!proc_register_dynamic(&proc_root, &fib_dir)) #elif LINUX_VERSION_CODE >= KERNEL_VER(2,2,0) if (!proc_register(&proc_root, &fib_dir)) #else #error "Hey: I do not know whether to use proc_register or proc_register_dynamic in this kernel version." #endif { /* * save the inode value from the * registration call, as we will need it to unregister. */ fib_indodeval = fib_dir.low_ino; fib_seed = 0; printk("fib v" VERSION " kernel support added (/proc/fib)\n"); } else { printk("WARNING: failed to load fib v" VERSION "!!!\n"); } return 0; } /* * Remove the module. If a nonzero inode value was returned, * unregister the /proc directory entry; that's all we really need to * do, but we'll log a notice as well. */ void cleanup_module(void) { if (fib_indodeval) { proc_unregister(&proc_root, fib_indodeval); printk("fib v" VERSION " kernel support removed\n"); } else { printk("WARNING: trying to clean up fib v" VERSION " which isn't installed!!\n"); } } /* * Process a read to the file. Here we compute fibonacci of the current * value in fib_seed and return it with a newline at the end. Note * that because we are handling read() through a file_operations strcture * instead of directly through the proc_dir_entry structure, we * must be able to signify that we are done by returning 0 on the * second call so the calling process will stop read()ing. (This * assumes that this will be called at least twice every time /proc/fib * is opened (and will assure that it _only_ gets called twice by * returning 0 on the second call). I don't know that this is a safe * assumption!) */ #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) static int fib_read(struct inode * inode, struct file * file, char *buf, int len) #elif LINUX_VERSION_CODE >= KERNEL_VER(2,2,0) static int fib_read(struct file * file, char *buf, size_t len, loff_t * l) #else #error "Hey: I do not know what are the parameters to fib_read in this kernel version." #endif { #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) char num_str[11]; /* only needs to be length 6, I think */ #endif /* every other read attempt should return 0 */ fib_boole = 1 - fib_boole; if (fib_boole == 0) { return 0; } #ifdef FIB_DEBUG printk("fib reading! len == %i\n",len); #endif /* FIB_DEBUG */ #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) len = sprintf(num_str, "%i\n", do_fib(fib_seed)); memcpy_tofs(buf, num_str, len); #elif LINUX_VERSION_CODE >= KERNEL_VER(2,2,0) len = sprintf(buf, "%i\n", do_fib(fib_seed)); #else #error "Hey: I do not know whether to use memcpy_tofs or not in this kernel version." #endif return len; } /* * Roll our own atoi, as I can't find it in the kernel sources (okay, * so I didn't look too hard :). It is just as well, because when * a non-numerical string is passed to it, it will return the current * value of fib_seed, thus leaving everything as if the invalid string * were never passed to /proc/fib */ int fib_atoi (const char * str) { #define FIB_BASE 10 #ifdef FIB_DEBUG char * old_str = str; #endif /* FIB_DEBUG */ int total = 0; /* stop at nulls, newlines, or decimal points */ for(; (*str != '\0') && (*str != '\n') && (*str != '.'); str++) { if (*str < '0' || *str > '9') { /* on invalid input, return the old value * of fib_seed */ #ifdef FIB_DEBUG printk("invalid input: *str == \"%c\"\n", *str); #endif /* FIB_DEBUG */ return fib_seed; } total *= FIB_BASE; total += *str - '0'; } #ifdef FIB_DEBUG printk("converted str == %s to total == %i\n", old_str, total); #endif /* FIB_DEBUG */ #undef FIB_BASE return total; } /* * Here we process a write to /proc/fib. We take the string we were * given and convert it to an integer (isn't there a generic function * in the kernel to do this?) and then we set fib_seed to this value. * Also, set fib_dir.size to be this new seed value, so we can always * see the current seed value by looking at the size of /proc/fib */ #if LINUX_VERSION_CODE <= KERNEL_VER(2,0,99) static int fib_write(struct inode * ino, struct file * fp, const char * buf, int len) { char in[11]; memset(in,'\0',sizeof(in)); memcpy_fromfs(&in, buf, (len < 10 ? len : 10)); /* in[10] = '\0'; */ fib_seed = fib_atoi(in); #elif LINUX_VERSION_CODE >= KERNEL_VER(2,2,0) static int fib_write(struct file *file, const char * buf, size_t len, loff_t * l) { fib_seed = fib_atoi(buf); #else #error "Hey: I do not know what are the parameters to fib_write in this kernel version." #endif #ifdef FIB_DEBUG printk("fib writing! fib_seed == %i, len == %i, buff == \"%s\"\n", fib_seed, len, buff); #endif /* FIB_DEBUG */ fib_dir.size = fib_seed; /* set the file size of /proc/fib to the seed */ return len; } /* note: when writing to /proc/fib, fib_write seems to get called for every unit of buffering that the writer does -- maybe */