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