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