/* * linux/fs/proc/net.c * * Copyright (C) 1991, 1992 Linus Torvalds * * gjh 3/'93 heim@peanuts.informatik.uni-tuebingen.de (Gerald J. Heim) * most of this file is stolen from base.c * it works, but you shouldn't use it as a guideline * for new proc-fs entries. once i'll make it better. * fvk 3/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen) * cleaned up the whole thing, moved "net" specific code to * the NET kernel layer (where it belonged in the first place). * Michael K. Johnson (johnsonm@stolaf.edu) 3/93 * Added support from my previous inet.c. Cleaned things up * quite a bit, modularized the code. * fvk 4/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen) * Renamed "route_get_info()" to "rt_get_info()" for consistency. * * proc net directory handling functions */ #include #include #include #include #include #include /* forward references */ static int proc_readnet(struct inode * inode, struct file * file, char * buf, int count); static int proc_readnetdir(struct inode *, struct file *, struct dirent *, int); static int proc_lookupnet(struct inode *,const char *,int,struct inode **); /* the get_*_info() functions are in the net code, and are configured in via the standard mechanism... */ extern int unix_get_info(char *); #ifdef CONFIG_INET extern int tcp_get_info(char *); extern int udp_get_info(char *); extern int raw_get_info(char *); extern int arp_get_info(char *); extern int dev_get_info(char *); extern int rt_get_info(char *); #endif /* CONFIG_INET */ static struct file_operations proc_net_operations = { NULL, /* lseek - default */ proc_readnet, /* read - bad */ NULL, /* write - bad */ proc_readnetdir, /* readdir */ NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* can't fsync */ }; /* * proc directories can do almost nothing.. */ struct inode_operations proc_net_inode_operations = { &proc_net_operations, /* default net directory file-ops */ NULL, /* create */ proc_lookupnet, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; static struct proc_dir_entry net_dir[] = { { 1,2,".." }, { 8,1,"." }, { 128,4,"unix" } #ifdef CONFIG_INET ,{ 129,3,"arp" }, { 130,5,"route" }, { 131,3,"dev" }, { 132,3,"raw" }, { 133,3,"tcp" }, { 134,3,"udp" } #endif /* CONFIG_INET */ }; #define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0]))) static int proc_lookupnet(struct inode * dir,const char * name, int len, struct inode ** result) { unsigned int ino; int i; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } i = NR_NET_DIRENTRY; while (i-- > 0 && !proc_match(len,name,net_dir+i)) /* nothing */; if (i < 0) { iput(dir); return -ENOENT; } ino = net_dir[i].low_ino; if (!(*result = iget(dir->i_sb,ino))) { iput(dir); return -ENOENT; } iput(dir); return 0; } static int proc_readnetdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count) { struct proc_dir_entry * de; unsigned int ino; int i,j; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; ino = inode->i_ino; if (((unsigned) filp->f_pos) < NR_NET_DIRENTRY) { de = net_dir + filp->f_pos; filp->f_pos++; i = de->namelen; ino = de->low_ino; put_fs_long(ino, &dirent->d_ino); put_fs_word(i,&dirent->d_reclen); put_fs_byte(0,i+dirent->d_name); j = i; while (i--) put_fs_byte(de->name[i], i+dirent->d_name); return j; } return 0; } static int proc_readnet(struct inode * inode, struct file * file, char * buf, int count) { char * page; int length; int end; unsigned int ino; if (count < 0) return -EINVAL; if (!(page = (char*) __get_free_page(GFP_KERNEL))) return -ENOMEM; ino = inode->i_ino; switch (ino) { #ifdef CONFIG_INET case 128: length = unix_get_info(page); break; case 129: length = arp_get_info(page); break; case 130: length = rt_get_info(page); break; case 131: length = dev_get_info(page); break; case 132: length = raw_get_info(page); break; case 133: length = tcp_get_info(page); break; case 134: length = udp_get_info(page); break; #endif /* CONFIG_INET */ default: free_page((unsigned long) page); return -EBADF; } if (file->f_pos >= length) { free_page((unsigned long) page); return 0; } if (count + file->f_pos > length) count = length - file->f_pos; end = count + file->f_pos; memcpy_tofs(buf, page + file->f_pos, count); free_page((unsigned long) page); file->f_pos = end; return count; }