1 /* 2 * atalk_proc.c - proc support for Appletalk 3 * 4 * Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation, version 2. 9 */ 10 11 #include <linux/config.h> 12 #include <linux/init.h> 13 #include <linux/proc_fs.h> 14 #include <linux/seq_file.h> 15 #include <net/sock.h> 16 #include <linux/atalk.h> 17 18 19 static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) 20 { 21 struct atalk_iface *i; 22 23 for (i = atalk_interfaces; pos && i; i = i->next) 24 --pos; 25 26 return i; 27 } 28 29 static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) 30 { 31 loff_t l = *pos; 32 33 read_lock_bh(&atalk_interfaces_lock); 34 return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; 35 } 36 37 static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) 38 { 39 struct atalk_iface *i; 40 41 ++*pos; 42 if (v == SEQ_START_TOKEN) { 43 i = NULL; 44 if (atalk_interfaces) 45 i = atalk_interfaces; 46 goto out; 47 } 48 i = v; 49 i = i->next; 50 out: 51 return i; 52 } 53 54 static void atalk_seq_interface_stop(struct seq_file *seq, void *v) 55 { 56 read_unlock_bh(&atalk_interfaces_lock); 57 } 58 59 static int atalk_seq_interface_show(struct seq_file *seq, void *v) 60 { 61 struct atalk_iface *iface; 62 63 if (v == SEQ_START_TOKEN) { 64 seq_puts(seq, "Interface Address Networks " 65 "Status\n"); 66 goto out; 67 } 68 69 iface = v; 70 seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", 71 iface->dev->name, ntohs(iface->address.s_net), 72 iface->address.s_node, ntohs(iface->nets.nr_firstnet), 73 ntohs(iface->nets.nr_lastnet), iface->status); 74 out: 75 return 0; 76 } 77 78 static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) 79 { 80 struct atalk_route *r; 81 82 for (r = atalk_routes; pos && r; r = r->next) 83 --pos; 84 85 return r; 86 } 87 88 static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) 89 { 90 loff_t l = *pos; 91 92 read_lock_bh(&atalk_routes_lock); 93 return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; 94 } 95 96 static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) 97 { 98 struct atalk_route *r; 99 100 ++*pos; 101 if (v == SEQ_START_TOKEN) { 102 r = NULL; 103 if (atalk_routes) 104 r = atalk_routes; 105 goto out; 106 } 107 r = v; 108 r = r->next; 109 out: 110 return r; 111 } 112 113 static void atalk_seq_route_stop(struct seq_file *seq, void *v) 114 { 115 read_unlock_bh(&atalk_routes_lock); 116 } 117 118 static int atalk_seq_route_show(struct seq_file *seq, void *v) 119 { 120 struct atalk_route *rt; 121 122 if (v == SEQ_START_TOKEN) { 123 seq_puts(seq, "Target Router Flags Dev\n"); 124 goto out; 125 } 126 127 if (atrtr_default.dev) { 128 rt = &atrtr_default; 129 seq_printf(seq, "Default %04X:%02X %-4d %s\n", 130 ntohs(rt->gateway.s_net), rt->gateway.s_node, 131 rt->flags, rt->dev->name); 132 } 133 134 rt = v; 135 seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", 136 ntohs(rt->target.s_net), rt->target.s_node, 137 ntohs(rt->gateway.s_net), rt->gateway.s_node, 138 rt->flags, rt->dev->name); 139 out: 140 return 0; 141 } 142 143 static __inline__ struct sock *atalk_get_socket_idx(loff_t pos) 144 { 145 struct sock *s; 146 struct hlist_node *node; 147 148 sk_for_each(s, node, &atalk_sockets) 149 if (!pos--) 150 goto found; 151 s = NULL; 152 found: 153 return s; 154 } 155 156 static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) 157 { 158 loff_t l = *pos; 159 160 read_lock_bh(&atalk_sockets_lock); 161 return l ? atalk_get_socket_idx(--l) : SEQ_START_TOKEN; 162 } 163 164 static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) 165 { 166 struct sock *i; 167 168 ++*pos; 169 if (v == SEQ_START_TOKEN) { 170 i = sk_head(&atalk_sockets); 171 goto out; 172 } 173 i = sk_next(v); 174 out: 175 return i; 176 } 177 178 static void atalk_seq_socket_stop(struct seq_file *seq, void *v) 179 { 180 read_unlock_bh(&atalk_sockets_lock); 181 } 182 183 static int atalk_seq_socket_show(struct seq_file *seq, void *v) 184 { 185 struct sock *s; 186 struct atalk_sock *at; 187 188 if (v == SEQ_START_TOKEN) { 189 seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " 190 "Rx_queue St UID\n"); 191 goto out; 192 } 193 194 s = v; 195 at = at_sk(s); 196 197 seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " 198 "%02X %d\n", 199 s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, 200 ntohs(at->dest_net), at->dest_node, at->dest_port, 201 atomic_read(&s->sk_wmem_alloc), 202 atomic_read(&s->sk_rmem_alloc), 203 s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); 204 out: 205 return 0; 206 } 207 208 static struct seq_operations atalk_seq_interface_ops = { 209 .start = atalk_seq_interface_start, 210 .next = atalk_seq_interface_next, 211 .stop = atalk_seq_interface_stop, 212 .show = atalk_seq_interface_show, 213 }; 214 215 static struct seq_operations atalk_seq_route_ops = { 216 .start = atalk_seq_route_start, 217 .next = atalk_seq_route_next, 218 .stop = atalk_seq_route_stop, 219 .show = atalk_seq_route_show, 220 }; 221 222 static struct seq_operations atalk_seq_socket_ops = { 223 .start = atalk_seq_socket_start, 224 .next = atalk_seq_socket_next, 225 .stop = atalk_seq_socket_stop, 226 .show = atalk_seq_socket_show, 227 }; 228 229 static int atalk_seq_interface_open(struct inode *inode, struct file *file) 230 { 231 return seq_open(file, &atalk_seq_interface_ops); 232 } 233 234 static int atalk_seq_route_open(struct inode *inode, struct file *file) 235 { 236 return seq_open(file, &atalk_seq_route_ops); 237 } 238 239 static int atalk_seq_socket_open(struct inode *inode, struct file *file) 240 { 241 return seq_open(file, &atalk_seq_socket_ops); 242 } 243 244 static struct file_operations atalk_seq_interface_fops = { 245 .owner = THIS_MODULE, 246 .open = atalk_seq_interface_open, 247 .read = seq_read, 248 .llseek = seq_lseek, 249 .release = seq_release, 250 }; 251 252 static struct file_operations atalk_seq_route_fops = { 253 .owner = THIS_MODULE, 254 .open = atalk_seq_route_open, 255 .read = seq_read, 256 .llseek = seq_lseek, 257 .release = seq_release, 258 }; 259 260 static struct file_operations atalk_seq_socket_fops = { 261 .owner = THIS_MODULE, 262 .open = atalk_seq_socket_open, 263 .read = seq_read, 264 .llseek = seq_lseek, 265 .release = seq_release, 266 }; 267 268 static struct proc_dir_entry *atalk_proc_dir; 269 270 int __init atalk_proc_init(void) 271 { 272 struct proc_dir_entry *p; 273 int rc = -ENOMEM; 274 275 atalk_proc_dir = proc_mkdir("atalk", proc_net); 276 if (!atalk_proc_dir) 277 goto out; 278 atalk_proc_dir->owner = THIS_MODULE; 279 280 p = create_proc_entry("interface", S_IRUGO, atalk_proc_dir); 281 if (!p) 282 goto out_interface; 283 p->proc_fops = &atalk_seq_interface_fops; 284 285 p = create_proc_entry("route", S_IRUGO, atalk_proc_dir); 286 if (!p) 287 goto out_route; 288 p->proc_fops = &atalk_seq_route_fops; 289 290 p = create_proc_entry("socket", S_IRUGO, atalk_proc_dir); 291 if (!p) 292 goto out_socket; 293 p->proc_fops = &atalk_seq_socket_fops; 294 295 p = create_proc_entry("arp", S_IRUGO, atalk_proc_dir); 296 if (!p) 297 goto out_arp; 298 p->proc_fops = &atalk_seq_arp_fops; 299 300 rc = 0; 301 out: 302 return rc; 303 out_arp: 304 remove_proc_entry("socket", atalk_proc_dir); 305 out_socket: 306 remove_proc_entry("route", atalk_proc_dir); 307 out_route: 308 remove_proc_entry("interface", atalk_proc_dir); 309 out_interface: 310 remove_proc_entry("atalk", proc_net); 311 goto out; 312 } 313 314 void __exit atalk_proc_exit(void) 315 { 316 remove_proc_entry("interface", atalk_proc_dir); 317 remove_proc_entry("route", atalk_proc_dir); 318 remove_proc_entry("socket", atalk_proc_dir); 319 remove_proc_entry("arp", atalk_proc_dir); 320 remove_proc_entry("atalk", proc_net); 321 } 322