Whamcloud - gitweb
b=16098
[fs/lustre-release.git] / lnet / utils / l_ioctl.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
5  *
6  *   This file is part of Portals, http://www.sf.net/projects/lustre/
7  *
8  *   Portals is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Portals is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Portals; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #define __USE_FILE_OFFSET64
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <sys/mman.h>
32 #include <sys/ioctl.h>
33 #include <errno.h>
34 #include <unistd.h>
35
36 #include <lnet/api-support.h>
37 #include <lnet/lnetctl.h>
38 #include <libcfs/portals_utils.h>
39
40
41 static ioc_handler_t  do_ioctl;                 /* forward ref */
42 static ioc_handler_t *current_ioc_handler = &do_ioctl;
43
44 struct ioc_dev {
45         const char * dev_name;
46         int dev_fd;
47         int dev_major;
48         int dev_minor;
49 };
50
51 static struct ioc_dev ioc_dev_list[10];
52
53 struct dump_hdr {
54         int magic;
55         int dev_id;
56         unsigned int opc;
57 };
58
59 char *dump_filename;
60
61 void
62 set_ioc_handler (ioc_handler_t *handler)
63 {
64         if (handler == NULL)
65                 current_ioc_handler = do_ioctl;
66         else
67                 current_ioc_handler = handler;
68 }
69
70 /* Catamount has no <linux/kdev_t.h>, so just define it here */
71 #ifndef MKDEV
72 # define MKDEV(a,b) (((a) << 8) | (b))
73 #endif
74
75 static int
76 open_ioc_dev(int dev_id) 
77 {
78         const char * dev_name;
79
80         if (dev_id < 0 || 
81             dev_id >= sizeof(ioc_dev_list) / sizeof(ioc_dev_list[0]))
82                 return -EINVAL;
83
84         dev_name = ioc_dev_list[dev_id].dev_name;
85         if (dev_name == NULL) {
86                 fprintf(stderr, "unknown device id: %d\n", dev_id);
87                 return -EINVAL;
88         }
89
90         if (ioc_dev_list[dev_id].dev_fd < 0) {
91                 int fd = open(dev_name, O_RDWR);
92
93                 /* Make the /dev/ node if we need to */
94                 if (fd < 0 && errno == ENOENT) {
95                         if (mknod(dev_name, 
96                                   S_IFCHR|S_IWUSR|S_IRUSR,
97                                   MKDEV(ioc_dev_list[dev_id].dev_major,
98                                         ioc_dev_list[dev_id].dev_minor)) == 0)
99                                 fd = open(dev_name, O_RDWR);
100                         else
101                                 fprintf(stderr, "mknod %s failed: %s\n",
102                                         dev_name, strerror(errno));
103                 }
104
105                 if (fd < 0) {
106                         fprintf(stderr, "opening %s failed: %s\n"
107                                 "hint: the kernel modules may not be loaded\n",
108                                 dev_name, strerror(errno));
109                         return fd;
110                 }
111                 ioc_dev_list[dev_id].dev_fd = fd;
112         }
113
114         return ioc_dev_list[dev_id].dev_fd;
115 }
116
117
118 static int 
119 do_ioctl(int dev_id, unsigned int opc, void *buf)
120 {
121         int fd, rc;
122         
123         fd = open_ioc_dev(dev_id);
124         if (fd < 0) 
125                 return fd;
126
127         rc = ioctl(fd, opc, buf);
128         return rc;
129         
130 }
131
132 static FILE *
133 get_dump_file() 
134 {
135         FILE *fp = NULL;
136         
137         if (!dump_filename) {
138                 fprintf(stderr, "no dump filename\n");
139         } else 
140                 fp = fopen(dump_filename, "a");
141         return fp;
142 }
143
144 /*
145  * The dump file should start with a description of which devices are
146  * used, but for now it will assumed whatever app reads the file will
147  * know what to do. */
148 int 
149 dump(int dev_id, unsigned int opc, void *buf)
150 {
151         FILE *fp;
152         struct dump_hdr dump_hdr;
153         struct libcfs_ioctl_hdr * ioc_hdr = (struct  libcfs_ioctl_hdr *) buf;
154         int rc;
155         
156         printf("dumping opc %x to %s\n", opc, dump_filename);
157         
158
159         dump_hdr.magic = 0xdeadbeef;
160         dump_hdr.dev_id = dev_id;
161         dump_hdr.opc = opc;
162
163         fp = get_dump_file();
164         if (fp == NULL) {
165                 fprintf(stderr, "%s: %s\n", dump_filename, 
166                         strerror(errno));
167                 return -EINVAL;
168         }
169         
170         rc = fwrite(&dump_hdr, sizeof(dump_hdr), 1, fp);
171         if (rc == 1)
172                 rc = fwrite(buf, ioc_hdr->ioc_len, 1, fp);
173         fclose(fp);
174         if (rc != 1) {
175                 fprintf(stderr, "%s: %s\n", dump_filename,
176                         strerror(errno));
177                 return -EINVAL;
178         }
179
180         return 0;
181 }
182
183 /* register a device to send ioctls to.  */
184 int 
185 register_ioc_dev(int dev_id, const char * dev_name, int major, int minor) 
186 {
187
188         if (dev_id < 0 || 
189             dev_id >= sizeof(ioc_dev_list) / sizeof(ioc_dev_list[0]))
190                 return -EINVAL;
191
192         unregister_ioc_dev(dev_id);
193
194         ioc_dev_list[dev_id].dev_name = dev_name;
195         ioc_dev_list[dev_id].dev_fd = -1;
196         ioc_dev_list[dev_id].dev_major = major;
197         ioc_dev_list[dev_id].dev_minor = minor;
198  
199         return dev_id;
200 }
201
202 void
203 unregister_ioc_dev(int dev_id) 
204 {
205
206         if (dev_id < 0 || 
207             dev_id >= sizeof(ioc_dev_list) / sizeof(ioc_dev_list[0]))
208                 return;
209         if (ioc_dev_list[dev_id].dev_name != NULL &&
210             ioc_dev_list[dev_id].dev_fd >= 0) 
211                 close(ioc_dev_list[dev_id].dev_fd);
212
213         ioc_dev_list[dev_id].dev_name = NULL;
214         ioc_dev_list[dev_id].dev_fd = -1;
215 }
216
217 /* If this file is set, then all ioctl buffers will be 
218    appended to the file. */
219 int
220 set_ioctl_dump(char * file)
221 {
222         if (dump_filename)
223                 free(dump_filename);
224         
225         dump_filename = strdup(file);
226         if (dump_filename == NULL)
227                 abort();
228
229         set_ioc_handler(&dump);
230         return 0;
231 }
232
233 int
234 l_ioctl(int dev_id, unsigned int opc, void *buf)
235 {
236         return current_ioc_handler(dev_id, opc, buf);
237 }
238
239 /* Read an ioctl dump file, and call the ioc_func for each ioctl buffer
240  * in the file.  For example:
241  *
242  * parse_dump("lctl.dump", l_ioctl);
243  *
244  * Note: if using l_ioctl, then you also need to register_ioc_dev() for 
245  * each device used in the dump.
246  */
247 int 
248 parse_dump(char * dump_file, ioc_handler_t ioc_func)
249 {
250         int line =0;
251         struct stat st;
252         char *start, *buf, *end;
253 #ifndef __CYGWIN__
254         int fd;
255 #else
256         HANDLE fd, hmap;
257         DWORD size;
258 #endif
259         
260 #ifndef __CYGWIN__
261         fd = syscall(SYS_open, dump_file, O_RDONLY);
262         if (fd < 0) {
263                 fprintf(stderr, "couldn't open %s: %s\n", dump_file, 
264                         strerror(errno));
265                 exit(1);
266         }
267
268         if (fstat(fd, &st)) { 
269                 perror("stat fails");
270                 exit(1);
271         }
272
273         if (st.st_size < 1) {
274                 fprintf(stderr, "KML is empty\n");
275                 exit(1);
276         }
277
278         start = buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE , fd, 0);
279         end = start + st.st_size;
280         close(fd);
281         if (start == MAP_FAILED) {
282                 fprintf(stderr, "can't create file mapping\n");
283                 exit(1);
284         }
285 #else
286         fd = CreateFile(dump_file, GENERIC_READ, FILE_SHARE_READ, NULL,
287                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
288         size = GetFileSize(fd, NULL);
289         if (size < 1) {
290                 fprintf(stderr, "KML is empty\n");
291                 exit(1);
292         }
293
294         hmap = CreateFileMapping(fd, NULL, PAGE_READONLY, 0,0, NULL);
295         start = buf = MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0);
296         end = buf + size;
297         CloseHandle(fd);
298         if (start == NULL) {
299                 fprintf(stderr, "can't create file mapping\n");
300                 exit(1);
301         }
302 #endif /* __CYGWIN__ */
303
304         while (buf < end) {
305                 struct dump_hdr *dump_hdr = (struct dump_hdr *) buf;
306                 struct libcfs_ioctl_hdr * data;
307                 char tmp[8096];
308                 int rc;
309
310                 line++;
311
312                 data = (struct libcfs_ioctl_hdr *) (buf + sizeof(*dump_hdr));
313                 if (buf + data->ioc_len > end ) {
314                         fprintf(stderr, "dump file overflow, %p + %d > %p\n", buf,
315                                 data->ioc_len, end);
316                         return -1;
317                 }
318 #if 0
319                 printf ("dump_hdr: %lx data: %lx\n",
320                         (unsigned long)dump_hdr - (unsigned long)buf, (unsigned long)data - (unsigned long)buf);
321
322                 printf("%d: opcode %x len: %d  ver: %x ", line, dump_hdr->opc,
323                        data->ioc_len, data->ioc_version);
324 #endif
325
326                 memcpy(tmp, data, data->ioc_len);
327
328                 rc = ioc_func(dump_hdr->dev_id, dump_hdr->opc, tmp);
329                 if (rc) {
330                         printf("failed: %d\n", rc);
331                         exit(1);
332                 }
333
334                 buf += data->ioc_len + sizeof(*dump_hdr);
335         }
336
337 #ifndef __CYGWIN__
338         munmap(start, end - start);
339 #else
340         UnmapViewOfFile(start);
341         CloseHandle(hmap);
342 #endif
343
344         return 0;
345 }
346
347 int 
348 jt_ioc_dump(int argc, char **argv)
349 {
350         if (argc > 2) {
351                 fprintf(stderr, "usage: %s [hostname]\n", argv[0]);
352                 return 0;
353         }
354         printf("setting dumpfile to: %s\n", argv[1]);
355         
356         set_ioctl_dump(argv[1]);
357         return 0;
358 }