Whamcloud - gitweb
Don't update your trees yet..... Brian and I are trying to get our
[fs/lustre-release.git] / lustre / utils / obdctl.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2002 Cluster File Systems, Inc.
5  *   Author: Peter J. Braam <braam@clusterfs.com>
6  *   Author: Phil Schwan <phil@clusterfs.com>
7  *
8  *   This file is part of Lustre, http://www.lustre.org.
9  *
10  *   Lustre is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Lustre is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with Lustre; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25
26 #include <stdlib.h>
27 #include <sys/ioctl.h>
28 #include <fcntl.h>
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <signal.h>
36 #define printk printf
37
38 #include <linux/lustre_lib.h>
39 #include <linux/lustre_idl.h>
40 #include <linux/lustre_dlm.h>
41
42 #include <libxml/xmlmemory.h>
43 #include <libxml/parser.h>
44
45 #include <unistd.h>
46 #include <sys/un.h>
47 #include <time.h>
48 #include <sys/time.h>
49 #include <netinet/in.h>
50 #include <errno.h>
51 #include <string.h>
52
53 #define __KERNEL__
54 #include <linux/list.h>
55 #undef __KERNEL__
56
57 #include "parser.h"
58 #include <stdio.h>
59
60 static int jt_newdev(int argc, char **argv);
61 static int jt_attach(int argc, char **argv);
62 static int jt_setup(int argc, char **argv);
63
64
65 int fd = -1;
66 uint64_t conn_addr = -1;
67 uint64_t conn_cookie;
68 char rawbuf[8192];
69 char *buf = rawbuf;
70 int max = 8192;
71 int thread;
72 int rc = 0;
73
74 #define IOCINIT(data)                                                   \
75 do {                                                                    \
76         memset(&data, 0, sizeof(data));                                 \
77         data.ioc_version = OBD_IOCTL_VERSION;                           \
78         data.ioc_addr = conn_addr;                                      \
79         data.ioc_cookie = conn_cookie;                                  \
80         data.ioc_len = sizeof(data);                                    \
81         if (fd < 0) {                                                   \
82                 fprintf(stderr, "No device open, use device\n");        \
83                 return 1;                                               \
84         }                                                               \
85 } while (0)
86
87 /*
88     pack "LL LL LL LL LL LL LL L L L L L L L L L a60 a60 L L L",
89     $obdo->{id}, 0,
90     $obdo->{gr}, 0,
91     $obdo->{atime}, 0,
92     $obdo->{mtime}, 0 ,
93     $obdo->{ctime}, 0,
94     $obdo->{size}, 0,
95     $obdo->{blocks}, 0,
96     $obdo->{blksize},
97     $obdo->{mode},
98     $obdo->{uid},
99     $obdo->{gid},
100     $obdo->{flags},
101     $obdo->{obdflags},
102     $obdo->{nlink},
103     $obdo->{generation},
104     $obdo->{valid},
105     $obdo->{inline},
106     $obdo->{obdmd},
107     0, 0, # struct list_head
108     0;  #  struct obd_ops
109 }
110
111 */
112
113 char * obdo_print(struct obdo *obd)
114 {
115         char buf[1024];
116
117         sprintf(buf, "id: %Ld\ngrp: %Ld\natime: %Ld\nmtime: %Ld\nctime: %Ld\n"
118                 "size: %Ld\nblocks: %Ld\nblksize: %d\nmode: %o\nuid: %d\n"
119                 "gid: %d\nflags: %x\nobdflags: %x\nnlink: %d,\nvalid %x\n",
120                 obd->o_id,
121                 obd->o_gr,
122                 obd->o_atime,
123                 obd->o_mtime,
124                 obd->o_ctime,
125                 obd->o_size,
126                 obd->o_blocks,
127                 obd->o_blksize,
128                 obd->o_mode,
129                 obd->o_uid,
130                 obd->o_gid,
131                 obd->o_flags,
132                 obd->o_obdflags,
133                 obd->o_nlink,
134                 obd->o_valid);
135         return strdup(buf);
136 }
137
138 static char *cmdname(char *func)
139 {
140         static char buf[512];
141
142         if (thread) {
143                 sprintf(buf, "%s-%d", func, thread);
144                 return buf;
145         }
146
147         return func;
148 }
149
150 int getfd(char *func)
151 {
152         if (fd == -1)
153                 fd = open("/dev/obd", O_RDWR);
154         if (fd == -1) {
155                 fprintf(stderr, "error: %s: opening /dev/obd: %s\n",
156                         cmdname(func), strerror(errno));
157                 return -1;
158         }
159         return 0;
160 }
161
162 #define difftime(a, b)                                          \
163         ((double)(a)->tv_sec - (b)->tv_sec +                    \
164          ((double)((a)->tv_usec - (b)->tv_usec) / 1000000))
165
166 static int be_verbose(int verbose, struct timeval *next_time,
167                       int num, int *next_num, int num_total)
168 {
169         struct timeval now;
170
171         if (!verbose)
172                 return 0;
173
174         if (next_time != NULL)
175                 gettimeofday(&now, NULL);
176
177         /* A positive verbosity means to print every X iterations */
178         if (verbose > 0 &&
179             (next_num == NULL || num >= *next_num || num >= num_total)) {
180                 *next_num += verbose;
181                 if (next_time) {
182                         next_time->tv_sec = now.tv_sec - verbose;
183                         next_time->tv_usec = now.tv_usec;
184                 }
185                 return 1;
186         }
187
188         /* A negative verbosity means to print at most each X seconds */
189         if (verbose < 0 && next_time != NULL && difftime(&now, next_time) >= 0){
190                 next_time->tv_sec = now.tv_sec - verbose;
191                 next_time->tv_usec = now.tv_usec;
192                 if (next_num)
193                         *next_num = num;
194                 return 1;
195         }
196
197         return 0;
198 }
199
200 static int get_verbose(const char *arg)
201 {
202         int verbose;
203
204         if (!arg || arg[0] == 'v')
205                 verbose = 1;
206         else if (arg[0] == 's' || arg[0] == 'q')
207                 verbose = 0;
208         else
209                 verbose = (int) strtoul(arg, NULL, 0);
210
211         if (verbose < 0)
212                 printf("Print status every %d seconds\n", -verbose);
213         else if (verbose == 1)
214                 printf("Print status every operation\n");
215         else if (verbose > 1)
216                 printf("Print status every %d operations\n", verbose);
217
218         return verbose;
219 }
220
221 static int do_disconnect(char *func, int verbose)
222 {
223         struct obd_ioctl_data data;
224
225         if (conn_addr == -1) 
226                 return 0; 
227
228         IOCINIT(data);
229
230         rc = ioctl(fd, OBD_IOC_DISCONNECT , &data);
231         if (rc < 0) {
232                 fprintf(stderr, "error: %s: %x %s\n", cmdname(func),
233                         OBD_IOC_DISCONNECT, strerror(errno));
234         } else {
235                 if (verbose)
236                         printf("%s: disconnected conn %Lx\n", cmdname(func),
237                                conn_addr);
238                 conn_addr = -1;
239         }
240
241         return rc;
242 }
243
244 extern command_t cmdlist[];
245
246 static int xml_command(char *cmd, ...) {
247         va_list args;
248         char *arg, *cmds[8];
249         int i = 1, j;
250        
251         cmds[0] = cmd; 
252         va_start(args, cmd);
253
254         while (((arg = va_arg(args, char *)) != NULL) && (i < 8)) {
255                 cmds[i] = arg;
256                 i++;
257         }
258
259         va_end(args);
260
261         printf("obdctl > ");
262         for (j = 0; j < i; j++)
263           printf("%s ", cmds[j]);
264
265         printf("\n");
266
267         return Parser_execarg(i, cmds, cmdlist);
268 }
269
270 static network_t *xml_network(xmlDocPtr doc, xmlNodePtr root) {
271         xmlNodePtr cur = root->xmlChildrenNode;
272         network_t *net;
273         
274         if ((net = (network_t *)calloc(1, sizeof(network_t))) == NULL) {
275                 printf("error: unable to malloc network_t\n");
276                 return NULL;
277         }
278         
279         net->type = xmlGetProp(root, "type");
280         if (net->type == NULL) {
281                 printf("error: type attrib required (tcp, elan, myrinet)\n");
282                 free(net);
283                 return NULL;
284         }
285
286         while (cur != NULL) {
287                 if (!xmlStrcmp(cur->name, "server"))
288                         net->server = xmlNodeGetContent(cur);
289
290                 if (!xmlStrcmp(cur->name, "port"))
291                         net->port = atoi(xmlNodeGetContent(cur));
292
293                 cur = cur->next;
294         } 
295
296         if (net->server == NULL) {
297                 printf("error: <server> tag required\n");
298                 free(net);
299                 return NULL;
300         }
301         
302         return net;
303 }
304
305 static int xml_mds(xmlDocPtr doc, xmlNodePtr root, 
306                    char *serv_id, char *serv_uuid) {
307         xmlNodePtr cur = root->xmlChildrenNode;
308         char *fstype = NULL, *device = NULL;
309         int rc;
310
311         printf("--- Setting up MDS ---\n");
312         while (cur != NULL) {
313                 if (!xmlStrcmp(cur->name, "fstype"))
314                         fstype = xmlNodeGetContent(cur);
315
316                 if (!xmlStrcmp(cur->name, "device"))
317                         device = xmlNodeGetContent(cur);
318  
319                 cur = cur->next;
320         } 
321
322         if ((fstype == NULL) || (device == NULL)) {
323                 printf("error: <fstype> and <device> tags required\n");
324                 return -1;
325         }
326
327         if ((rc = xml_command("newdev", NULL)) != 0)
328                 return rc;
329
330         if ((rc = xml_command("attach", "mds", "MDSDEV", NULL)) != 0)
331                 return rc;
332
333         if ((rc = xml_command("setup", device, fstype, NULL)) != 0)
334                 return rc;
335
336         return 0;
337 }
338         
339 static int xml_obd(xmlDocPtr doc, xmlNodePtr root, 
340                    char *serv_id, char *serv_uuid) {
341         xmlNodePtr cur = root->xmlChildrenNode;
342         char *obdtype, *format = NULL, *fstype = NULL, *device = NULL;
343         int rc;
344
345         obdtype = xmlGetProp(root, "type");
346         printf("--- Setting up OBD ---\n");
347
348         while (cur != NULL) {
349                 if (!xmlStrcmp(cur->name, "fstype"))
350                         fstype = xmlNodeGetContent(cur);
351
352                 if (!xmlStrcmp(cur->name, "device"))
353                         device = xmlNodeGetContent(cur);
354  
355                 if (!xmlStrcmp(cur->name, "autoformat"))
356                         format = xmlNodeGetContent(cur);
357
358                 cur = cur->next;
359         } 
360
361         if ((obdtype == NULL) || (fstype == NULL) || (device == NULL)) {
362                 printf("error: 'type' attrib and <fstype> and <device> tags required\n");
363                 return -1;
364         }
365
366         /* FIXME: Building and configuring loopback devices should go here
367          * but is currently unsupported.  You'll have to use the scripts
368          * for now until support is added, or specify a real device.
369          */
370         
371         if ((rc = xml_command("newdev", NULL)) != 0)
372                 return rc;
373
374         if ((rc = xml_command("attach", obdtype, "OBDDEV", NULL)) != 0)
375                 return rc;
376
377         if ((rc = xml_command("setup", device, fstype, NULL)) != 0)
378                 return rc;
379
380         return 0;
381 }
382
383 static int xml_ost(xmlDocPtr doc, xmlNodePtr root, 
384                    char *serv_id, char *serv_uuid) {
385         int rc;
386
387         printf("--- Setting up OST ---\n");
388         if ((rc = xml_command("newdev", NULL)) != 0)
389                 return rc;
390
391         if ((rc = xml_command("attach", "ost", "OSTDEV", NULL)) != 0)
392                 return rc;
393
394         if ((rc = xml_command("setup", "$OBDDEV", NULL)) != 0)
395                 return rc;
396
397         return 0;
398 }
399
400 static int xml_osc(xmlDocPtr doc, xmlNodePtr root, 
401                    char *serv_id, char *serv_uuid) {
402         xmlNodePtr cur = root->xmlChildrenNode;
403         network_t *net = NULL;
404         int rc = 0;
405
406         printf("--- Setting up OSC ---\n");
407         while (cur != NULL) {
408                 if (!xmlStrcmp(cur->name, "network")) {
409                         net = xml_network(doc, cur);
410                         if (net == NULL)
411                                 return -1;
412                 }
413                 cur = cur->next;
414         } 
415
416         if (net == NULL) {
417                 printf("error: <network> tag required\n");
418                 return -1;
419         }
420
421         if ((rc = xml_command("newdev", NULL)) != 0) {
422                 rc = -1;
423                 goto xml_osc_error;
424         }
425
426         if ((rc = xml_command("attach", "osc", "OSCDEV", NULL)) != 0) {
427                 rc = -1;
428                 goto xml_osc_error;
429         }
430
431         if ((rc = xml_command("setup", "OSTDEV", net->server, NULL)) != 0) {
432                 rc = -1;
433                 goto xml_osc_error;
434         }
435
436 xml_osc_error:
437         free(net);
438         return rc;
439 }
440
441 static int xml_mdc(xmlDocPtr doc, xmlNodePtr root, 
442                    char *serv_id, char *serv_uuid) {
443         xmlNodePtr cur = root->xmlChildrenNode;
444         network_t *net = NULL;
445         int rc = 0;
446
447         printf("--- Setting up MDC ---\n");
448         while (cur != NULL) {
449                 if (!xmlStrcmp(cur->name, "network")) {
450                         net = xml_network(doc, cur);
451                         if (net == NULL)
452                                 return -1;
453                 }
454                 cur = cur->next;
455         } 
456
457         if (net == NULL) {
458                 printf("error: <network> tag required\n");
459                 return -1;
460         }
461
462         if ((rc = xml_command("newdev", NULL)) != 0) {
463                 rc = -1;
464                 goto xml_mdc_error;
465         }
466
467         if ((rc = xml_command("attach", "mdc", "MDCDEV", NULL)) != 0) {
468                 rc = -1;
469                 goto xml_mdc_error;
470         }
471
472         if ((rc = xml_command("setup", "MDSDEV", net->server, NULL)) != 0) {
473                 rc = -1;
474                 goto xml_mdc_error;
475         }
476
477 xml_mdc_error:
478         free(net);
479         return rc;
480 }
481
482 static int xml_lov(xmlDocPtr doc, xmlNodePtr root, 
483                    char *serv_id, char *serv_uuid) {
484         printf("--- Setting up LOV ---\n");
485         return 0;
486 }
487
488 static int xml_router(xmlDocPtr doc, xmlNodePtr root, 
489                    char *serv_id, char *serv_uuid) {
490         printf("--- Setting up ROUTER ---\n");
491         return 0;
492 }
493
494 static int xml_ldlm(xmlDocPtr doc, xmlNodePtr root, 
495                    char *serv_id, char *serv_uuid) {
496         int rc;
497
498         printf("--- Setting up LDLM ---\n");
499         if ((rc = xml_command("newdev", NULL)) != 0)
500                 return rc;
501
502         if ((rc = xml_command("attach", "ldlm", "LDLMDEV", NULL)) != 0)
503                 return rc;
504
505         if ((rc = xml_command("setup", NULL)) != 0)
506                 return rc;
507
508         return 0;
509 }
510
511 static int xml_service(xmlDocPtr doc, xmlNodePtr root, 
512                        int serv_num, char *serv_id, char *serv_uuid) {
513         xmlNodePtr cur = root;
514         char *id, *uuid;
515
516         while (cur != NULL) {
517                 id = xmlGetProp(cur, "id");
518                 uuid = xmlGetProp(cur, "uuid");
519
520                 if (xmlStrcmp(id, serv_id) ||
521                     xmlStrcmp(uuid, serv_uuid)) {
522                         cur = cur->next;
523                         continue;
524                 }
525
526                 if (!xmlStrcmp(cur->name, "mds"))
527                         return xml_mds(doc, cur, serv_id, serv_uuid);
528                 else if (!xmlStrcmp(cur->name, "obd"))
529                         return xml_obd(doc, cur, serv_id, serv_uuid);
530                 else if (!xmlStrcmp(cur->name, "ost"))
531                         return xml_ost(doc, cur, serv_id, serv_uuid);
532                 else if (!xmlStrcmp(cur->name, "osc"))
533                         return xml_osc(doc, cur, serv_id, serv_uuid);
534                 else if (!xmlStrcmp(cur->name, "mdc"))
535                         return xml_mdc(doc, cur, serv_id, serv_uuid);
536                 else if (!xmlStrcmp(cur->name, "lov"))
537                         return xml_lov(doc, cur, serv_id, serv_uuid);
538                 else if (!xmlStrcmp(cur->name, "router"))
539                         return xml_router(doc, cur, serv_id, serv_uuid);
540                 else if (!xmlStrcmp(cur->name, "ldlm"))
541                         return xml_ldlm(doc, cur, serv_id, serv_uuid);
542                 else
543                         return -1;        
544
545                 cur = cur->next;
546         }
547
548         printf("error: No XML config branch for id=%s uuid=%s\n",
549                 serv_id, serv_uuid); 
550         return -1; 
551 }
552
553 static int xml_profile(xmlDocPtr doc, xmlNodePtr root, 
554                        int prof_num, char *prof_id, char *prof_uuid) {
555         xmlNodePtr parent, cur = root;
556         char *id, *uuid, *srv_id, *srv_uuid;
557         int rc = 0, num;
558
559         while (cur != NULL) {
560                 id = xmlGetProp(cur, "id");
561                 uuid = xmlGetProp(cur, "uuid");
562
563                 if (xmlStrcmp(cur->name, "profile") || 
564                     xmlStrcmp(id, prof_id)          ||
565                     xmlStrcmp(uuid, prof_uuid)) {
566                         cur = cur->next;
567                         continue;
568                 }
569
570                 /* FIXME: Doesn't understand mountpoints yet
571                  *        xml_mountpoint(doc, root, ...);
572                  */    
573
574                 /* Setup each service in turn
575                  * FIXME: Should be sorted by "num" attr, we shouldn't
576                  *        assume they're in order in the XML document.
577                  */
578                 parent = cur;
579                 cur = cur->xmlChildrenNode;
580                 while (cur != NULL) {
581                         if (!xmlStrcmp(cur->name, "service_id")) {
582                                 num = atoi(xmlGetProp(cur, "num"));
583                                 rc = xml_service(doc, root, num,
584                                         srv_id = xmlGetProp(cur, "id"),
585                                         srv_uuid = xmlGetProp(cur, "uuid"));
586                                 if (rc != 0) {
587                                         printf("error: service config\n");
588                                         return rc;
589                                 }
590                         }
591
592                         cur = cur->next;
593                 }
594
595                 cur = parent->next;
596         }
597
598         return rc; 
599 }
600
601 static int xml_node(xmlDocPtr doc, xmlNodePtr root) {
602         xmlNodePtr parent, cur = root;
603         char *id, *uuid;
604         int rc = 0, num;
605         
606         /* Walk the node tags looking for ours */
607         while (cur != NULL) {
608                 if (xmlStrcmp(cur->name, "node")) {
609                         cur = cur->next;
610                         continue;
611                 }
612
613                 id = xmlGetProp(cur, "id");
614                 if (id == NULL)
615                         return -1;
616
617                 uuid = xmlGetProp(cur, "uuid");
618                 if (uuid == NULL)
619                         return -1;
620
621                 /* FIXME: Verify our ID and UUID against /etc/lustre/id
622                  *        so we're sure we are who we think we are.
623                  */
624
625                 /* Setup each profile in turn
626                  * FIXME: Should be sorted by "num" attr, we shouldn't
627                  *        assume they're in order in the XML document.
628                  */
629                 parent = cur;
630                 cur = cur->xmlChildrenNode;
631                 while (cur != NULL) {
632                         if (!xmlStrcmp(cur->name, "profile_id")) {
633                                 num = atoi(xmlGetProp(cur, "num"));
634                                 rc = xml_profile(doc, root, num,
635                                                  xmlGetProp(cur, "id"),
636                                                  xmlGetProp(cur, "uuid"));
637                                 if (rc != 0)
638                                         return rc;
639                         }
640
641                         cur = cur->next;
642                 }
643
644                 cur = parent->next;
645         }
646
647         return rc;
648 }
649
650 static int do_xml(char *func, char *file)
651 {
652         xmlDocPtr doc;
653         xmlNodePtr cur;
654         int rc;
655
656         doc = xmlParseFile(file);
657         if (doc == NULL) {
658                 fprintf(stderr, "error: Unable to parse XML\n");
659                 return -1; 
660         }
661
662         cur = xmlDocGetRootElement(doc);
663         if (cur == NULL) {
664                 fprintf(stderr, "error: Empty XML\n");
665                 xmlFreeDoc(doc);
666                 return -1;
667         }
668         
669         if (xmlStrcmp(cur->name, (const xmlChar *)"lustre")) {
670                 fprintf(stderr, "error: Root node != <lustre>\n");
671                 xmlFreeDoc(doc);
672                 return -1;
673         }
674
675         /* FIXME: Validate the XML against the DTD here */
676     
677         /* FIXME: Merge all the text nodes under each branch and 
678          *        prune empty nodes.  Just to make the parsing more
679          *        tolerant, the exact location of nested tags isn't
680          *        critical for this.
681          */
682         
683         rc = xml_node(doc, cur->xmlChildrenNode);
684         xmlFreeDoc(doc);
685
686         return rc;
687 }
688
689 static int do_device(char *func, int dev)
690 {
691         struct obd_ioctl_data data;
692
693         memset(&data, 0, sizeof(data));
694
695         data.ioc_dev = dev;
696
697         if (getfd(func))
698                 return -1;
699
700         if (obd_ioctl_pack(&data, &buf, max)) {
701                 fprintf(stderr, "error: %s: invalid ioctl\n", cmdname(func));
702                 return -2;
703         }
704
705         return ioctl(fd, OBD_IOC_DEVICE , buf);
706 }
707
708 static int jt_device(int argc, char **argv)
709 {
710         do_disconnect(argv[0], 1);
711
712         if (argc != 2) {
713                 fprintf(stderr, "usage: %s devno\n", cmdname(argv[0]));
714                 return -1;
715         }
716
717         rc = do_device(argv[0], strtoul(argv[1], NULL, 0));
718
719         if (rc < 0)
720                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
721                         strerror(rc = errno));
722
723         return rc;
724 }
725
726 static int jt_connect(int argc, char **argv)
727 {
728         struct obd_ioctl_data data;
729
730         IOCINIT(data);
731
732         do_disconnect(argv[0], 1);
733
734         if (argc != 1) {
735                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
736                 return -1;
737         }
738
739         rc = ioctl(fd, OBD_IOC_CONNECT , &data);
740         if (rc < 0)
741                 fprintf(stderr, "error: %s: %x %s\n", cmdname(argv[0]),
742                         OBD_IOC_CONNECT, strerror(rc = errno));
743         else
744                 conn_addr = data.ioc_addr;
745                 conn_cookie = data.ioc_cookie;
746         return rc;
747 }
748
749 static int jt_disconnect(int argc, char **argv)
750 {
751         if (argc != 1) {
752                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
753                 return -1;
754         }
755
756         if (conn_addr == -1)
757                 return 0;
758
759         return do_disconnect(argv[0], 0);
760 }
761
762 static int jt__xml(int argc, char **argv)
763 {
764         if (argc < 2) {
765                 fprintf(stderr, "usage: %s <xml file> <command [args ...]>\n",
766                         cmdname(argv[0]));
767                 return -1;
768         }
769
770         return do_xml("xml", argv[1]);
771 }
772
773 static int jt__device(int argc, char **argv)
774 {
775         char *arg2[3];
776         int ret;
777
778         if (argc < 3) {
779                 fprintf(stderr, "usage: %s devno <command [args ...]>\n",
780                         cmdname(argv[0]));
781                 return -1;
782         }
783
784         rc = do_device("device", strtoul(argv[1], NULL, 0));
785
786         if (!rc) {
787                 arg2[0] = "connect";
788                 arg2[1] = NULL;
789                 rc = jt_connect(1, arg2);
790         }
791
792         if (!rc)
793                 rc = Parser_execarg(argc - 2, argv + 2, cmdlist);
794
795         ret = do_disconnect(argv[0], 0);
796         if (!rc)
797                 rc = ret;
798
799         return rc;
800 }
801
802 static int jt__threads(int argc, char **argv)
803 {
804         int threads, next_thread;
805         int verbose;
806         int i, j;
807
808         if (argc < 5) {
809                 fprintf(stderr,
810                         "usage: %s numthreads verbose devno <cmd [args ...]>\n",
811                         argv[0]);
812                 return -1;
813         }
814
815         threads = strtoul(argv[1], NULL, 0);
816
817         verbose = get_verbose(argv[2]);
818
819         printf("%s: starting %d threads on device %s running %s\n",
820                argv[0], threads, argv[3], argv[4]);
821
822         for (i = 1, next_thread = verbose; i <= threads; i++) {
823                 rc = fork();
824                 if (rc < 0) {
825                         fprintf(stderr, "error: %s: #%d - %s\n", argv[0], i,
826                                 strerror(rc = errno));
827                         break;
828                 } else if (rc == 0) {
829                         thread = i;
830                         argv[2] = "--device";
831                         return jt__device(argc - 2, argv + 2);
832                 } else if (be_verbose(verbose, NULL, i, &next_thread, threads))
833                         printf("%s: thread #%d (PID %d) started\n",
834                                argv[0], i, rc);
835                 rc = 0;
836         }
837
838         if (!thread) { /* parent process */
839                 if (!verbose)
840                         printf("%s: started %d threads\n\n", argv[0], i - 1);
841                 else
842                         printf("\n");
843
844                 for (j = 1; j < i; j++) {
845                         int status;
846                         int ret = wait(&status);
847
848                         if (ret < 0) {
849                                 fprintf(stderr, "error: %s: wait - %s\n",
850                                         argv[0], strerror(errno));
851                                 if (!rc)
852                                         rc = errno;
853                         } else {
854                                 /*
855                                  * This is a hack.  We _should_ be able to use
856                                  * WIFEXITED(status) to see if there was an
857                                  * error, but it appears to be broken and it
858                                  * always returns 1 (OK).  See wait(2).
859                                  */
860                                 int err = WEXITSTATUS(status);
861                                 if (err)
862                                         fprintf(stderr,
863                                                 "%s: PID %d had rc=%d\n",
864                                                 argv[0], ret, err);
865                                 if (!rc)
866                                         rc = err;
867                         }
868                 }
869         }
870
871         return rc;
872 }
873
874 static int jt_detach(int argc, char **argv)
875 {
876         struct obd_ioctl_data data;
877
878         IOCINIT(data);
879
880         if (argc != 1) {
881                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
882                 return -1;
883         }
884
885         if (obd_ioctl_pack(&data, &buf, max)) {
886                 fprintf(stderr, "error: %s: invalid ioctl\n", cmdname(argv[0]));
887                 return -2;
888         }
889
890         rc = ioctl(fd, OBD_IOC_DETACH , buf);
891         if (rc < 0)
892                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
893                         strerror(rc=errno));
894
895         return rc;
896 }
897
898 static int jt_cleanup(int argc, char **argv)
899 {
900         struct obd_ioctl_data data;
901
902         IOCINIT(data);
903
904         if (argc != 1) {
905                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
906                 return -1;
907         }
908
909         rc = ioctl(fd, OBD_IOC_CLEANUP , &data);
910         if (rc < 0)
911                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
912                         strerror(rc=errno));
913
914         return rc;
915 }
916
917 static int jt_newdev(int argc, char **argv)
918 {
919         struct obd_ioctl_data data;
920
921         if (getfd(argv[0]))
922                 return -1;
923
924         IOCINIT(data);
925
926         if (argc != 1) {
927                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
928                 return -1;
929         }
930
931         rc = ioctl(fd, OBD_IOC_NEWDEV , &data);
932         if (rc < 0)
933                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
934                         strerror(rc=errno));
935         else {
936                 printf("Current device set to %d\n", data.ioc_dev);
937         }
938
939         return rc;
940 }
941
942 static int jt_list(int argc, char **argv)
943 {
944         char buf[1024];
945         struct obd_ioctl_data *data = (struct obd_ioctl_data *)buf;
946
947         if (getfd(argv[0]))
948                 return -1;
949
950         memset(buf, 0, sizeof(buf));
951         data->ioc_version = OBD_IOCTL_VERSION;
952         data->ioc_addr = conn_addr;
953         data->ioc_cookie = conn_addr;
954         data->ioc_len = sizeof(buf);
955         data->ioc_inllen1 = sizeof(buf) - size_round(sizeof(*data));
956
957         if (argc != 1) {
958                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
959                 return -1;
960         }
961
962         rc = ioctl(fd, OBD_IOC_LIST , data);
963         if (rc < 0)
964                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
965                         strerror(rc=errno));
966         else {
967                 printf("%s", data->ioc_bulk);
968         }
969
970         return rc;
971 }
972
973 static int jt_attach(int argc, char **argv)
974 {
975         struct obd_ioctl_data data;
976
977         IOCINIT(data);
978
979         if (argc != 2 && argc != 3 && argc != 4) {
980                 fprintf(stderr, "usage: %s type [name [uuid]]\n",
981                         cmdname(argv[0]));
982                 return -1;
983         }
984
985         data.ioc_inllen1 =  strlen(argv[1]) + 1;
986         data.ioc_inlbuf1 = argv[1];
987         if (argc >= 3) {
988                 data.ioc_inllen2 = strlen(argv[2]) + 1;
989                 data.ioc_inlbuf2 = argv[2];
990         }
991
992         if (argc == 4) {
993                 data.ioc_inllen3 = strlen(argv[3]) + 1;
994                 data.ioc_inlbuf3 = argv[3];
995         }
996
997         if (obd_ioctl_pack(&data, &buf, max)) {
998                 fprintf(stderr, "error: %s: invalid ioctl\n", cmdname(argv[0]));
999                 return -2;
1000         }
1001
1002         rc = ioctl(fd, OBD_IOC_ATTACH , buf);
1003         if (rc < 0)
1004                 fprintf(stderr, "error: %s: %x %s\n", cmdname(argv[0]),
1005                         OBD_IOC_ATTACH, strerror(rc = errno));
1006         else if (argc == 3) {
1007                 char name[1024];
1008                 if (strlen(argv[2]) > 128) {
1009                         printf("Name too long to set environment\n");
1010                         return -EINVAL;
1011                 }
1012                 snprintf(name, 512, "LUSTRE_DEV_%s", argv[2]);
1013                 rc = setenv(name, argv[1], 1);
1014                 if (rc) {
1015                         printf("error setting env variable %s\n", name);
1016                 }
1017         }
1018
1019         return rc;
1020 }
1021
1022 #define N2D_OFF 0x100    /* So we can tell between error codes and devices */
1023
1024 static int do_name2dev(char *func, char *name)
1025 {
1026         struct obd_ioctl_data data;
1027
1028         if (getfd(func))
1029                 return -1;
1030
1031         IOCINIT(data);
1032
1033         data.ioc_inllen1 = strlen(name) + 1;
1034         data.ioc_inlbuf1 = name;
1035
1036         if (obd_ioctl_pack(&data, &buf, max)) {
1037                 fprintf(stderr, "error: %s: invalid ioctl\n", cmdname(func));
1038                 return -2;
1039         }
1040         rc = ioctl(fd, OBD_IOC_NAME2DEV , buf);
1041         if (rc < 0) {
1042                 fprintf(stderr, "error: %s: %s - %s\n", cmdname(func),
1043                         name, strerror(rc = errno));
1044                 return rc;
1045         }
1046
1047         memcpy((char *)(&data), buf, sizeof(data));
1048
1049         return data.ioc_dev + N2D_OFF;
1050 }
1051
1052 static int jt_name2dev(int argc, char **argv)
1053 {
1054         if (argc != 2) {
1055                 fprintf(stderr, "usage: %s name\n", cmdname(argv[0]));
1056                 return -1;
1057         }
1058
1059         rc = do_name2dev(argv[0], argv[1]);
1060         if (rc >= N2D_OFF) {
1061                 int dev = rc - N2D_OFF;
1062                 rc = do_device(argv[0], dev);
1063                 if (rc == 0)
1064                         printf("%d\n", dev);
1065         }
1066         return rc;
1067 }
1068
1069 static int jt_setup(int argc, char **argv)
1070 {
1071         struct obd_ioctl_data data;
1072
1073         IOCINIT(data);
1074
1075         if ( argc > 3) {
1076                 fprintf(stderr, "usage: %s [device] [fstype]\n",
1077                         cmdname(argv[0]));
1078                 return -1;
1079         }
1080
1081         data.ioc_dev = -1;
1082         if (argc > 1) {
1083                 if (argv[1][0] == '$') {
1084                         rc = do_name2dev(argv[0], argv[1] + 1);
1085                         if (rc >= N2D_OFF) {
1086                                 printf("%s is device %d\n", argv[1],
1087                                        rc - N2D_OFF);
1088                                 data.ioc_dev = rc - N2D_OFF;
1089                         }
1090                 } else
1091                         data.ioc_dev = strtoul(argv[1], NULL, 0);
1092                 data.ioc_inllen1 = strlen(argv[1]) + 1;
1093                 data.ioc_inlbuf1 = argv[1];
1094         }
1095         if ( argc == 3 ) {
1096                 data.ioc_inllen2 = strlen(argv[2]) + 1;
1097                 data.ioc_inlbuf2 = argv[2];
1098         }
1099
1100         if (obd_ioctl_pack(&data, &buf, max)) {
1101                 fprintf(stderr, "error: %s: invalid ioctl\n", cmdname(argv[0]));
1102                 return -2;
1103         }
1104         rc = ioctl(fd, OBD_IOC_SETUP , buf);
1105         if (rc < 0)
1106                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
1107                         strerror(rc = errno));
1108
1109         return rc;
1110 }
1111
1112
1113 static int jt_create(int argc, char **argv)
1114 {
1115         struct obd_ioctl_data data;
1116         struct timeval next_time;
1117         int count = 1, next_count;
1118         int verbose;
1119         int i;
1120
1121         IOCINIT(data);
1122         if (argc < 2 || argc > 4) {
1123                 fprintf(stderr, "usage: %s num [mode] [verbose]\n",
1124                         cmdname(argv[0]));
1125                 return -1;
1126         }
1127         count = strtoul(argv[1], NULL, 0);
1128
1129         if (argc > 2)
1130                 data.ioc_obdo1.o_mode = strtoul(argv[2], NULL, 0);
1131         else
1132                 data.ioc_obdo1.o_mode = 0100644;
1133         data.ioc_obdo1.o_valid = OBD_MD_FLMODE;
1134
1135         verbose = get_verbose(argv[3]);
1136
1137         printf("%s: %d obdos\n", cmdname(argv[0]), count);
1138         gettimeofday(&next_time, NULL);
1139         next_time.tv_sec -= verbose;
1140
1141         for (i = 1, next_count = verbose; i <= count ; i++) {
1142                 rc = ioctl(fd, OBD_IOC_CREATE , &data);
1143                 if (rc < 0) {
1144                         fprintf(stderr, "error: %s: #%d - %s\n",
1145                                 cmdname(argv[0]), i, strerror(rc = errno));
1146                         break;
1147                 }
1148                 if (be_verbose(verbose, &next_time, i, &next_count, count))
1149                         printf("%s: #%d is object id %Ld\n", cmdname(argv[0]),
1150                                i, data.ioc_obdo1.o_id);
1151         }
1152         return rc;
1153 }
1154
1155 static int jt_setattr(int argc, char **argv)
1156 {
1157         struct obd_ioctl_data data;
1158
1159         IOCINIT(data);
1160         if (argc != 2) {
1161                 fprintf(stderr, "usage: %s id mode\n", cmdname(argv[0]));
1162                 return -1;
1163         }
1164
1165         data.ioc_obdo1.o_id = strtoul(argv[1], NULL, 0);
1166         data.ioc_obdo1.o_mode = S_IFREG | strtoul(argv[2], NULL, 0);
1167         data.ioc_obdo1.o_valid = OBD_MD_FLMODE;
1168
1169         rc = ioctl(fd, OBD_IOC_SETATTR , &data);
1170         if (rc < 0)
1171                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
1172                         strerror(rc = errno));
1173
1174         return rc;
1175 }
1176
1177 static int jt_destroy(int argc, char **argv)
1178 {
1179         struct obd_ioctl_data data;
1180
1181         IOCINIT(data);
1182         if (argc != 2) {
1183                 fprintf(stderr, "usage: %s id\n", cmdname(argv[0]));
1184                 return -1;
1185         }
1186
1187         data.ioc_obdo1.o_id = strtoul(argv[1], NULL, 0);
1188         data.ioc_obdo1.o_mode = S_IFREG|0644;
1189
1190         rc = ioctl(fd, OBD_IOC_DESTROY , &data);
1191         if (rc < 0)
1192                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
1193                         strerror(rc = errno));
1194
1195         return rc;
1196 }
1197
1198 static int jt_getattr(int argc, char **argv)
1199 {
1200         struct obd_ioctl_data data;
1201
1202         if (argc != 2) {
1203                 fprintf(stderr, "usage: %s id\n", cmdname(argv[0]));
1204                 return -1;
1205         }
1206
1207         IOCINIT(data);
1208         data.ioc_obdo1.o_id = strtoul(argv[1], NULL, 0);
1209         /* to help obd filter */
1210         data.ioc_obdo1.o_mode = 0100644;
1211         data.ioc_obdo1.o_valid = 0xffffffff;
1212         printf("%s: object id %Ld\n", cmdname(argv[0]), data.ioc_obdo1.o_id);
1213
1214         rc = ioctl(fd, OBD_IOC_GETATTR , &data);
1215         if (rc) {
1216                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
1217                         strerror(rc=errno));
1218         } else {
1219                 printf("%s: object id %Ld, mode %o\n", cmdname(argv[0]),
1220                        data.ioc_obdo1.o_id, data.ioc_obdo1.o_mode);
1221         }
1222         return rc;
1223 }
1224
1225 static int jt_test_getattr(int argc, char **argv)
1226 {
1227         struct obd_ioctl_data data;
1228         struct timeval start, next_time;
1229         int i, count, next_count;
1230         int verbose;
1231
1232         if (argc != 2 && argc != 3) {
1233                 fprintf(stderr, "usage: %s count [verbose]\n",cmdname(argv[0]));
1234                 return -1;
1235         }
1236
1237         IOCINIT(data);
1238         count = strtoul(argv[1], NULL, 0);
1239
1240         if (argc == 3)
1241                 verbose = get_verbose(argv[2]);
1242         else
1243                 verbose = 1;
1244
1245         data.ioc_obdo1.o_valid = 0xffffffff;
1246         data.ioc_obdo1.o_id = 2;
1247         gettimeofday(&start, NULL);
1248         next_time.tv_sec = start.tv_sec - verbose;
1249         next_time.tv_usec = start.tv_usec;
1250         printf("%s: getting %d attrs (testing only): %s", cmdname(argv[0]),
1251                count, ctime(&start.tv_sec));
1252
1253         for (i = 1, next_count = verbose; i <= count; i++) {
1254                 rc = ioctl(fd, OBD_IOC_GETATTR , &data);
1255                 if (rc < 0) {
1256                         fprintf(stderr, "error: %s: #%d - %s\n",
1257                                 cmdname(argv[0]), i, strerror(rc = errno));
1258                         break;
1259                 } else {
1260                         if (be_verbose(verbose, &next_time, i,&next_count,count))
1261                         printf("%s: got attr #%d\n", cmdname(argv[0]), i);
1262                 }
1263         }
1264
1265         if (!rc) {
1266                 struct timeval end;
1267                 double diff;
1268
1269                 gettimeofday(&end, NULL);
1270
1271                 diff = difftime(&end, &start);
1272
1273                 --i;
1274                 printf("%s: %d attrs in %.4gs (%.4g attr/s): %s",
1275                        cmdname(argv[0]), i, diff, (double)i / diff,
1276                        ctime(&end.tv_sec));
1277         }
1278         return rc;
1279 }
1280
1281 static int jt_test_brw(int argc, char **argv)
1282 {
1283         struct obd_ioctl_data data;
1284         struct timeval start, next_time;
1285         char *bulk, *b;
1286         int pages = 1, obdos = 1, count, next_count;
1287         int verbose = 1, write = 0, rw;
1288         int i, o, p;
1289         int len;
1290
1291         if (argc < 2 || argc > 6) {
1292                 fprintf(stderr,
1293                         "usage: %s count [write [verbose [pages [obdos]]]]\n",
1294                         cmdname(argv[0]));
1295                 return -1;
1296         }
1297
1298         count = strtoul(argv[1], NULL, 0);
1299
1300         if (argc >= 3) {
1301                 if (argv[2][0] == 'w' || argv[2][0] == '1')
1302                         write = 1;
1303                 else if (argv[2][0] == 'r' || argv[2][0] == '0')
1304                         write = 0;
1305
1306                 verbose = get_verbose(argv[3]);
1307         }
1308
1309         if (argc >= 5)
1310                 pages = strtoul(argv[4], NULL, 0);
1311         if (argc >= 6)
1312                 obdos = strtoul(argv[5], NULL, 0);
1313
1314         if (obdos != 1 && obdos != 2) {
1315                 fprintf(stderr, "error: %s: only 1 or 2 obdos supported\n",
1316                         cmdname(argv[0]));
1317                 return -2;
1318         }
1319
1320         len = pages * PAGE_SIZE;
1321
1322         bulk = calloc(obdos, len);
1323         if (!bulk) {
1324                 fprintf(stderr, "error: %s: no memory allocating %dx%d pages\n",
1325                         cmdname(argv[0]), obdos, pages);
1326                 return -2;
1327         }
1328         IOCINIT(data);
1329         data.ioc_obdo1.o_id = 2;
1330         data.ioc_count = len;
1331         data.ioc_offset = 0;
1332         data.ioc_plen1 = len;
1333         data.ioc_pbuf1 = bulk;
1334         if (obdos > 1) {
1335                 data.ioc_obdo2.o_id = 3;
1336                 data.ioc_plen2 = len;
1337                 data.ioc_pbuf2 = bulk + len;
1338         }
1339
1340         gettimeofday(&start, NULL);
1341         next_time.tv_sec = start.tv_sec - verbose;
1342         next_time.tv_usec = start.tv_usec;
1343
1344         printf("%s: %s %d (%dx%d pages) (testing only): %s",
1345                cmdname(argv[0]), write ? "writing" : "reading",
1346                count, obdos, pages, ctime(&start.tv_sec));
1347
1348         /*
1349          * We will put in the start time (and loop count inside the loop)
1350          * at the beginning of each page so that we will be able to validate
1351          * (at some later time) whether the data actually made it or not.
1352          *
1353          * XXX we do not currently use any of this memory in OBD_IOC_BRW_*
1354          *     just to avoid the overhead of the copy_{to,from}_user.  It
1355          *     can be fixed if we ever need to send real data around.
1356          */
1357         for (o = 0, b = bulk; o < obdos; o++)
1358                 for (p = 0; p < pages; p++, b += PAGE_SIZE)
1359                         memcpy(b, &start, sizeof(start));
1360
1361         rw = write ? OBD_IOC_BRW_WRITE : OBD_IOC_BRW_READ;
1362         for (i = 1, next_count = verbose; i <= count; i++) {
1363                 if (write) {
1364                         b = bulk + sizeof(struct timeval);
1365                         for (o = 0; o < obdos; o++)
1366                                 for (p = 0; p < pages; p++, b += PAGE_SIZE)
1367                                         memcpy(b, &count, sizeof(count));
1368                 }
1369
1370                 rc = ioctl(fd, rw, &data);
1371                 if (rc) {
1372                         fprintf(stderr, "error: %s: #%d - %s on %s\n",
1373                                 cmdname(argv[0]), i, strerror(rc = errno),
1374                                 write ? "write" : "read");
1375                         break;
1376                 } else if (be_verbose(verbose, &next_time, i,&next_count,count))
1377                         printf("%s: %s number %d\n", cmdname(argv[0]),
1378                                write ? "write" : "read", i);
1379         }
1380
1381         free(bulk);
1382
1383         if (!rc) {
1384                 struct timeval end;
1385                 double diff;
1386
1387                 gettimeofday(&end, NULL);
1388
1389                 diff = difftime(&end, &start);
1390
1391                 --i;
1392                 printf("%s: %s %dx%dx%d pages in %.4gs (%.4g pg/s): %s",
1393                        cmdname(argv[0]), write ? "wrote" : "read", obdos,
1394                        pages, i, diff, (double)obdos * i * pages / diff,
1395                        ctime(&end.tv_sec));
1396         }
1397         return rc;
1398 }
1399
1400 static int jt_test_ldlm(int argc, char **argv)
1401 {
1402         struct obd_ioctl_data data;
1403
1404         IOCINIT(data);
1405         if (argc != 1) {
1406                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
1407                 return 1;
1408         }
1409
1410         rc = ioctl(fd, IOC_LDLM_TEST, &data);
1411         if (rc)
1412                 fprintf(stderr, "error: %s: test failed: %s\n",
1413                         cmdname(argv[0]), strerror(rc = errno));
1414         return rc;
1415 }
1416
1417 static int jt_newconn(int argc, char **argv)
1418 {
1419         struct obd_ioctl_data data;
1420
1421         IOCINIT(data);
1422         if (argc != 1) {
1423                 fprintf(stderr, "usage: %s\n", cmdname(argv[0]));
1424                 return -1;
1425         }
1426
1427         rc = ioctl(fd, OBD_IOC_RECOVD_NEWCONN , &data);
1428         if (rc < 0)
1429                 fprintf(stderr, "error: %s: %s\n", cmdname(argv[0]),
1430                         strerror(rc = errno));
1431
1432         return rc;
1433 }
1434
1435 static int jt_quit(int argc, char **argv)
1436 {
1437         Parser_quit(argc, argv);
1438
1439         return rc;
1440 }
1441
1442 command_t cmdlist[] = {
1443         /* Metacommands */
1444         {"--xml", jt__xml, 0, "--xml <xml file> <command [args ...]>"},
1445         {"--device", jt__device, 0, "--device <devno> <command [args ...]>"},
1446         {"--threads", jt__threads, 0,
1447                 "--threads <threads> <devno> <command [args ...]>"},
1448
1449         /* Device configuration commands */
1450         {"list", jt_list, 0, "list the devices (no args)"},
1451         {"newdev", jt_newdev, 0, "set device to a new unused obd (no args)"},
1452         {"device", jt_device, 0, "set current device (args device_no name)"},
1453         {"name2dev", jt_name2dev, 0, "set device by name (args name)"},
1454         {"attach", jt_attach, 0, "name the type of device (args: type data"},
1455         {"setup", jt_setup, 0, "setup device (args: <blkdev> [data]"},
1456         {"detach", jt_detach, 0, "detach the current device (arg: )"},
1457         {"cleanup", jt_cleanup, 0, "cleanup the current device (arg: )"},
1458
1459         /* Session commands */
1460         {"connect", jt_connect, 0, "connect - get a connection to device"},
1461         {"disconnect", jt_disconnect, 0,
1462                 "disconnect - break connection to device"},
1463
1464         /* Session operations */
1465         {"create", jt_create, 0, "create [count [mode [verbose]]]"},
1466         {"destroy", jt_destroy, 0, "destroy <id>"},
1467         {"getattr", jt_getattr, 0, "getattr <id>"},
1468         {"setattr", jt_setattr, 0, "setattr <id> <mode>"},
1469         {"newconn", jt_newconn, 0, "newconn [newuuid]"},
1470         {"test_getattr", jt_test_getattr, 0, "test_getattr <count> [verbose]"},
1471         {"test_brw", jt_test_brw, 0, "test_brw <count> [write [verbose]]"},
1472         {"test_ldlm", jt_test_ldlm, 0, "test lock manager (no args)"},
1473
1474         /* User interface commands */
1475         {"help", Parser_help, 0, "help"},
1476         {"exit", jt_quit, 0, "quit"},
1477         {"quit", jt_quit, 0, "quit"},
1478         { 0, 0, 0, NULL }
1479 };
1480
1481
1482 static void signal_server(int sig)
1483 {
1484         if (sig == SIGINT) {
1485                 do_disconnect("sigint", 1);
1486                 exit(1);
1487         } else {
1488                 fprintf(stderr, "%s: got signal %d\n", cmdname("sigint"), sig);
1489         }
1490 }
1491
1492 int main(int argc, char **argv)
1493 {
1494         struct sigaction sigact;
1495
1496         sigact.sa_handler = signal_server;
1497         sigfillset(&sigact.sa_mask);
1498         sigact.sa_flags = SA_RESTART;
1499         sigaction(SIGINT, &sigact, NULL);
1500
1501
1502         if (argc > 1) {
1503                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
1504         } else {
1505                 Parser_init("obdctl > ", cmdlist);
1506                 rc = Parser_commands();
1507         }
1508
1509         do_disconnect(argv[0], 1);
1510         return rc;
1511 }
1512