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