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