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