]> Git Repo - binutils.git/blob - gdb/remote-vx.c
This commit was generated by cvs2svn to track changes on a CVS vendor
[binutils.git] / gdb / remote-vx.c
1 /* Memory-access and commands for remote VxWorks processes, for GDB.
2    Copyright (C) 1990-95, 1997-98, 1999 Free Software Foundation, Inc.
3    Contributed by Wind River Systems and Cygnus Support.
4
5    This file is part of GDB.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program 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 this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 #include "defs.h"
23 #include "frame.h"
24 #include "inferior.h"
25 #include "wait.h"
26 #include "target.h"
27 #include "gdbcore.h"
28 #include "command.h"
29 #include "symtab.h"
30 #include "complaints.h"
31 #include "gdbcmd.h"
32 #include "bfd.h"                /* Required by objfiles.h.  */
33 #include "symfile.h"            /* Required by objfiles.h.  */
34 #include "objfiles.h"
35 #include "gdb-stabs.h"
36
37 #include "gdb_string.h"
38 #include <errno.h>
39 #include <signal.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #define malloc bogon_malloc     /* Sun claims "char *malloc()" not void * */
44 #define free bogon_free         /* Sun claims "int free()" not void */
45 #define realloc bogon_realloc   /* Sun claims "char *realloc()", not void * */
46 #include <rpc/rpc.h>
47 #undef malloc
48 #undef free
49 #undef realloc
50 #include <sys/time.h>           /* UTek's <rpc/rpc.h> doesn't #incl this */
51 #include <netdb.h>
52 #include "vx-share/ptrace.h"
53 #include "vx-share/xdr_ptrace.h"
54 #include "vx-share/xdr_ld.h"
55 #include "vx-share/xdr_rdb.h"
56 #include "vx-share/dbgRpcLib.h"
57
58 #include <symtab.h>
59
60 /* Maximum number of bytes to transfer in a single
61    PTRACE_{READ,WRITE}DATA request.  */
62 #define VX_MEMXFER_MAX 4096
63
64 extern void vx_read_register ();
65 extern void vx_write_register ();
66 extern void symbol_file_command ();
67 extern int stop_soon_quietly;   /* for wait_for_inferior */
68
69 static int net_step ();
70 static int net_ptrace_clnt_call ();     /* Forward decl */
71 static enum clnt_stat net_clnt_call ();         /* Forward decl */
72
73 /* Target ops structure for accessing memory and such over the net */
74
75 static struct target_ops vx_ops;
76
77 /* Target ops structure for accessing VxWorks child processes over the net */
78
79 static struct target_ops vx_run_ops;
80
81 /* Saved name of target host and called function for "info files".
82    Both malloc'd.  */
83
84 static char *vx_host;
85 static char *vx_running;        /* Called function */
86
87 /* Nonzero means target that is being debugged remotely has a floating
88    point processor.  */
89
90 int target_has_fp;
91
92 /* Default error message when the network is forking up.  */
93
94 static const char rpcerr[] = "network target debugging:  rpc error";
95
96 CLIENT *pClient;                /* client used in net debugging */
97 static int ptraceSock = RPC_ANYSOCK;
98
99 enum clnt_stat net_clnt_call ();
100 static void parse_args ();
101
102 static struct timeval rpcTimeout =
103 {10, 0};
104
105 static char *skip_white_space ();
106 static char *find_white_space ();
107
108 /* Tell the VxWorks target system to download a file.
109    The load addresses of the text, data, and bss segments are
110    stored in *pTextAddr, *pDataAddr, and *pBssAddr (respectively).
111    Returns 0 for success, -1 for failure.  */
112
113 static int
114 net_load (filename, pTextAddr, pDataAddr, pBssAddr)
115      char *filename;
116      CORE_ADDR *pTextAddr;
117      CORE_ADDR *pDataAddr;
118      CORE_ADDR *pBssAddr;
119 {
120   enum clnt_stat status;
121   struct ldfile ldstruct;
122   struct timeval load_timeout;
123
124   memset ((char *) &ldstruct, '\0', sizeof (ldstruct));
125
126   /* We invoke clnt_call () here directly, instead of through
127      net_clnt_call (), because we need to set a large timeout value.
128      The load on the target side can take quite a while, easily
129      more than 10 seconds.  The user can kill this call by typing
130      CTRL-C if there really is a problem with the load.  
131
132      Do not change the tv_sec value without checking -- select() imposes
133      a limit of 10**8 on it for no good reason that I can see...  */
134
135   load_timeout.tv_sec = 99999999;       /* A large number, effectively inf. */
136   load_timeout.tv_usec = 0;
137
138   status = clnt_call (pClient, VX_LOAD, xdr_wrapstring, &filename, xdr_ldfile,
139                       &ldstruct, load_timeout);
140
141   if (status == RPC_SUCCESS)
142     {
143       if (*ldstruct.name == 0)  /* load failed on VxWorks side */
144         return -1;
145       *pTextAddr = ldstruct.txt_addr;
146       *pDataAddr = ldstruct.data_addr;
147       *pBssAddr = ldstruct.bss_addr;
148       return 0;
149     }
150   else
151     return -1;
152 }
153
154 /* returns 0 if successful, errno if RPC failed or VxWorks complains. */
155
156 static int
157 net_break (addr, procnum)
158      int addr;
159      u_long procnum;
160 {
161   enum clnt_stat status;
162   int break_status;
163   Rptrace ptrace_in;            /* XXX This is stupid.  It doesn't need to be a ptrace
164                                    structure.  How about something smaller? */
165
166   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
167   break_status = 0;
168
169   ptrace_in.addr = addr;
170   ptrace_in.pid = inferior_pid;
171
172   status = net_clnt_call (procnum, xdr_rptrace, &ptrace_in, xdr_int,
173                           &break_status);
174
175   if (status != RPC_SUCCESS)
176     return errno;
177
178   if (break_status == -1)
179     return ENOMEM;
180   return break_status;          /* probably (FIXME) zero */
181 }
182
183 /* returns 0 if successful, errno otherwise */
184
185 static int
186 vx_insert_breakpoint (addr)
187      int addr;
188 {
189   return net_break (addr, VX_BREAK_ADD);
190 }
191
192 /* returns 0 if successful, errno otherwise */
193
194 static int
195 vx_remove_breakpoint (addr)
196      int addr;
197 {
198   return net_break (addr, VX_BREAK_DELETE);
199 }
200
201 /* Start an inferior process and sets inferior_pid to its pid.
202    EXEC_FILE is the file to run.
203    ALLARGS is a string containing the arguments to the program.
204    ENV is the environment vector to pass.
205    Returns process id.  Errors reported with error().
206    On VxWorks, we ignore exec_file.  */
207
208 static void
209 vx_create_inferior (exec_file, args, env)
210      char *exec_file;
211      char *args;
212      char **env;
213 {
214   enum clnt_stat status;
215   arg_array passArgs;
216   TASK_START taskStart;
217
218   memset ((char *) &passArgs, '\0', sizeof (passArgs));
219   memset ((char *) &taskStart, '\0', sizeof (taskStart));
220
221   /* parse arguments, put them in passArgs */
222
223   parse_args (args, &passArgs);
224
225   if (passArgs.arg_array_len == 0)
226     error ("You must specify a function name to run, and arguments if any");
227
228   status = net_clnt_call (PROCESS_START, xdr_arg_array, &passArgs,
229                           xdr_TASK_START, &taskStart);
230
231   if ((status != RPC_SUCCESS) || (taskStart.status == -1))
232     error ("Can't create process on remote target machine");
233
234   /* Save the name of the running function */
235   vx_running = savestring (passArgs.arg_array_val[0],
236                            strlen (passArgs.arg_array_val[0]));
237
238   push_target (&vx_run_ops);
239   inferior_pid = taskStart.pid;
240
241   /* We will get a trace trap after one instruction.
242      Insert breakpoints and continue.  */
243
244   init_wait_for_inferior ();
245
246   /* Set up the "saved terminal modes" of the inferior
247      based on what modes we are starting it with.  */
248   target_terminal_init ();
249
250   /* Install inferior's terminal modes.  */
251   target_terminal_inferior ();
252
253   stop_soon_quietly = 1;
254   wait_for_inferior ();         /* Get the task spawn event */
255   stop_soon_quietly = 0;
256
257   /* insert_step_breakpoint ();  FIXME, do we need this?  */
258   proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
259 }
260
261 /* Fill ARGSTRUCT in argc/argv form with the arguments from the
262    argument string ARGSTRING.  */
263
264 static void
265 parse_args (arg_string, arg_struct)
266      register char *arg_string;
267      arg_array *arg_struct;
268 {
269   register int arg_count = 0;   /* number of arguments */
270   register int arg_index = 0;
271   register char *p0;
272
273   memset ((char *) arg_struct, '\0', sizeof (arg_array));
274
275   /* first count how many arguments there are */
276
277   p0 = arg_string;
278   while (*p0 != '\0')
279     {
280       if (*(p0 = skip_white_space (p0)) == '\0')
281         break;
282       p0 = find_white_space (p0);
283       arg_count++;
284     }
285
286   arg_struct->arg_array_len = arg_count;
287   arg_struct->arg_array_val = (char **) xmalloc ((arg_count + 1)
288                                                  * sizeof (char *));
289
290   /* now copy argument strings into arg_struct.  */
291
292   while (*(arg_string = skip_white_space (arg_string)))
293     {
294       p0 = find_white_space (arg_string);
295       arg_struct->arg_array_val[arg_index++] = savestring (arg_string,
296                                                            p0 - arg_string);
297       arg_string = p0;
298     }
299
300   arg_struct->arg_array_val[arg_count] = NULL;
301 }
302
303 /* Advance a string pointer across whitespace and return a pointer
304    to the first non-white character.  */
305
306 static char *
307 skip_white_space (p)
308      register char *p;
309 {
310   while (*p == ' ' || *p == '\t')
311     p++;
312   return p;
313 }
314
315 /* Search for the first unquoted whitespace character in a string.
316    Returns a pointer to the character, or to the null terminator
317    if no whitespace is found.  */
318
319 static char *
320 find_white_space (p)
321      register char *p;
322 {
323   register int c;
324
325   while ((c = *p) != ' ' && c != '\t' && c)
326     {
327       if (c == '\'' || c == '"')
328         {
329           while (*++p != c && *p)
330             {
331               if (*p == '\\')
332                 p++;
333             }
334           if (!*p)
335             break;
336         }
337       p++;
338     }
339   return p;
340 }
341
342 /* Poll the VxWorks target system for an event related
343    to the debugged task.
344    Returns -1 if remote wait failed, task status otherwise.  */
345
346 static int
347 net_wait (pEvent)
348      RDB_EVENT *pEvent;
349 {
350   int pid;
351   enum clnt_stat status;
352
353   memset ((char *) pEvent, '\0', sizeof (RDB_EVENT));
354
355   pid = inferior_pid;
356   status = net_clnt_call (PROCESS_WAIT, xdr_int, &pid, xdr_RDB_EVENT,
357                           pEvent);
358
359   /* return (status == RPC_SUCCESS)? pEvent->status: -1; */
360   if (status == RPC_SUCCESS)
361     return ((pEvent->status) ? 1 : 0);
362   else if (status == RPC_TIMEDOUT)
363     return (1);
364   else
365     return (-1);
366 }
367
368 /* Suspend the remote task.
369    Returns -1 if suspend fails on target system, 0 otherwise.  */
370
371 static int
372 net_quit ()
373 {
374   int pid;
375   int quit_status;
376   enum clnt_stat status;
377
378   quit_status = 0;
379
380   /* don't let rdbTask suspend itself by passing a pid of 0 */
381
382   if ((pid = inferior_pid) == 0)
383     return -1;
384
385   status = net_clnt_call (VX_TASK_SUSPEND, xdr_int, &pid, xdr_int,
386                           &quit_status);
387
388   return (status == RPC_SUCCESS) ? quit_status : -1;
389 }
390
391 /* Read a register or registers from the remote system.  */
392
393 void
394 net_read_registers (reg_buf, len, procnum)
395      char *reg_buf;
396      int len;
397      u_long procnum;
398 {
399   int status;
400   Rptrace ptrace_in;
401   Ptrace_return ptrace_out;
402   C_bytes out_data;
403   char message[100];
404
405   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
406   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
407
408   /* Initialize RPC input argument structure.  */
409
410   ptrace_in.pid = inferior_pid;
411   ptrace_in.info.ttype = NOINFO;
412
413   /* Initialize RPC return value structure.  */
414
415   out_data.bytes = reg_buf;
416   out_data.len = len;
417   ptrace_out.info.more_data = (caddr_t) & out_data;
418
419   /* Call RPC; take an error exit if appropriate.  */
420
421   status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out);
422   if (status)
423     error (rpcerr);
424   if (ptrace_out.status == -1)
425     {
426       errno = ptrace_out.errno_num;
427       sprintf (message, "reading %s registers", (procnum == PTRACE_GETREGS)
428                ? "general-purpose"
429                : "floating-point");
430       perror_with_name (message);
431     }
432 }
433
434 /* Write register values to a VxWorks target.  REG_BUF points to a buffer
435    containing the raw register values, LEN is the length of REG_BUF in
436    bytes, and PROCNUM is the RPC procedure number (PTRACE_SETREGS or
437    PTRACE_SETFPREGS).  An error exit is taken if the RPC call fails or
438    if an error status is returned by the remote debug server.  This is
439    a utility routine used by vx_write_register ().  */
440
441 void
442 net_write_registers (reg_buf, len, procnum)
443      char *reg_buf;
444      int len;
445      u_long procnum;
446 {
447   int status;
448   Rptrace ptrace_in;
449   Ptrace_return ptrace_out;
450   C_bytes in_data;
451   char message[100];
452
453   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
454   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
455
456   /* Initialize RPC input argument structure.  */
457
458   in_data.bytes = reg_buf;
459   in_data.len = len;
460
461   ptrace_in.pid = inferior_pid;
462   ptrace_in.info.ttype = DATA;
463   ptrace_in.info.more_data = (caddr_t) & in_data;
464
465   /* Call RPC; take an error exit if appropriate.  */
466
467   status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out);
468   if (status)
469     error (rpcerr);
470   if (ptrace_out.status == -1)
471     {
472       errno = ptrace_out.errno_num;
473       sprintf (message, "writing %s registers", (procnum == PTRACE_SETREGS)
474                ? "general-purpose"
475                : "floating-point");
476       perror_with_name (message);
477     }
478 }
479
480 /* Prepare to store registers.  Since we will store all of them,
481    read out their current values now.  */
482
483 static void
484 vx_prepare_to_store ()
485 {
486   /* Fetch all registers, if any of them are not yet fetched.  */
487   read_register_bytes (0, NULL, REGISTER_BYTES);
488 }
489
490 /* Copy LEN bytes to or from remote inferior's memory starting at MEMADDR
491    to debugger memory starting at MYADDR.  WRITE is true if writing to the
492    inferior.
493    Result is the number of bytes written or read (zero if error).  The
494    protocol allows us to return a negative count, indicating that we can't
495    handle the current address but can handle one N bytes further, but
496    vxworks doesn't give us that information.  */
497
498 static int
499 vx_xfer_memory (memaddr, myaddr, len, write, target)
500      CORE_ADDR memaddr;
501      char *myaddr;
502      int len;
503      int write;
504      struct target_ops *target; /* ignored */
505 {
506   int status;
507   Rptrace ptrace_in;
508   Ptrace_return ptrace_out;
509   C_bytes data;
510   enum ptracereq request;
511   int nleft, nxfer;
512
513   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
514   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
515
516   ptrace_in.pid = inferior_pid; /* XXX pid unnecessary for READDATA */
517   ptrace_in.addr = (int) memaddr;       /* Where from */
518   ptrace_in.data = len;         /* How many bytes */
519
520   if (write)
521     {
522       ptrace_in.info.ttype = DATA;
523       ptrace_in.info.more_data = (caddr_t) & data;
524
525       data.bytes = (caddr_t) myaddr;    /* Where from */
526       data.len = len;           /* How many bytes (again, for XDR) */
527       request = PTRACE_WRITEDATA;
528     }
529   else
530     {
531       ptrace_out.info.more_data = (caddr_t) & data;
532       request = PTRACE_READDATA;
533     }
534   /* Loop until the entire request has been satisfied, transferring
535      at most VX_MEMXFER_MAX bytes per iteration.  Break from the loop
536      if an error status is returned by the remote debug server.  */
537
538   nleft = len;
539   status = 0;
540
541   while (nleft > 0 && status == 0)
542     {
543       nxfer = min (nleft, VX_MEMXFER_MAX);
544
545       ptrace_in.addr = (int) memaddr;
546       ptrace_in.data = nxfer;
547       data.bytes = (caddr_t) myaddr;
548       data.len = nxfer;
549
550       /* Request a block from the remote debug server; if RPC fails,
551          report an error and return to debugger command level.  */
552
553       if (net_ptrace_clnt_call (request, &ptrace_in, &ptrace_out))
554         error (rpcerr);
555
556       status = ptrace_out.status;
557       if (status == 0)
558         {
559           memaddr += nxfer;
560           myaddr += nxfer;
561           nleft -= nxfer;
562         }
563       else
564         {
565           /* A target-side error has ocurred.  Set errno to the error
566              code chosen by the target so that a later perror () will
567              say something meaningful.  */
568
569           errno = ptrace_out.errno_num;
570         }
571     }
572
573   /* Return the number of bytes transferred.  */
574
575   return (len - nleft);
576 }
577
578 static void
579 vx_files_info ()
580 {
581   printf_unfiltered ("\tAttached to host `%s'", vx_host);
582   printf_unfiltered (", which has %sfloating point", target_has_fp ? "" : "no ");
583   printf_unfiltered (".\n");
584 }
585
586 static void
587 vx_run_files_info ()
588 {
589   printf_unfiltered ("\tRunning %s VxWorks process %s",
590                      vx_running ? "child" : "attached",
591                      local_hex_string (inferior_pid));
592   if (vx_running)
593     printf_unfiltered (", function `%s'", vx_running);
594   printf_unfiltered (".\n");
595 }
596
597 static void
598 vx_resume (pid, step, siggnal)
599      int pid;
600      int step;
601      enum target_signal siggnal;
602 {
603   int status;
604   Rptrace ptrace_in;
605   Ptrace_return ptrace_out;
606   CORE_ADDR cont_addr;
607
608   if (pid == -1)
609     pid = inferior_pid;
610
611   if (siggnal != 0 && siggnal != stop_signal)
612     error ("Cannot send signals to VxWorks processes");
613
614   /* Set CONT_ADDR to the address at which we are continuing,
615      or to 1 if we are continuing from where the program stopped.
616      This conforms to traditional ptrace () usage, but at the same
617      time has special meaning for the VxWorks remote debug server.
618      If the address is not 1, the server knows that the target
619      program is jumping to a new address, which requires special
620      handling if there is a breakpoint at the new address.  */
621
622   cont_addr = read_register (PC_REGNUM);
623   if (cont_addr == stop_pc)
624     cont_addr = 1;
625
626   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
627   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
628
629   ptrace_in.pid = pid;
630   ptrace_in.addr = cont_addr;   /* Target side insists on this, or it panics.  */
631
632   if (step)
633     status = net_step ();
634   else
635     status = net_ptrace_clnt_call (PTRACE_CONT, &ptrace_in, &ptrace_out);
636
637   if (status)
638     error (rpcerr);
639   if (ptrace_out.status == -1)
640     {
641       errno = ptrace_out.errno_num;
642       perror_with_name ("Resuming remote process");
643     }
644 }
645
646 static void
647 vx_mourn_inferior ()
648 {
649   pop_target ();                /* Pop back to no-child state */
650   generic_mourn_inferior ();
651 }
652 \f
653
654 static void vx_add_symbols PARAMS ((char *, int, CORE_ADDR, CORE_ADDR,
655                                     CORE_ADDR));
656
657 struct find_sect_args
658   {
659     CORE_ADDR text_start;
660     CORE_ADDR data_start;
661     CORE_ADDR bss_start;
662   };
663
664 static void find_sect PARAMS ((bfd *, asection *, void *));
665
666 static void
667 find_sect (abfd, sect, obj)
668      bfd *abfd;
669      asection *sect;
670      PTR obj;
671 {
672   struct find_sect_args *args = (struct find_sect_args *) obj;
673
674   if (bfd_get_section_flags (abfd, sect) & (SEC_CODE & SEC_READONLY))
675     args->text_start = bfd_get_section_vma (abfd, sect);
676   else if (bfd_get_section_flags (abfd, sect) & SEC_ALLOC)
677     {
678       if (bfd_get_section_flags (abfd, sect) & SEC_LOAD)
679         {
680           /* Exclude .ctor and .dtor sections which have SEC_CODE set but not
681              SEC_DATA.  */
682           if (bfd_get_section_flags (abfd, sect) & SEC_DATA)
683             args->data_start = bfd_get_section_vma (abfd, sect);
684         }
685       else
686         args->bss_start = bfd_get_section_vma (abfd, sect);
687     }
688 }
689
690 static void
691 vx_add_symbols (name, from_tty, text_addr, data_addr, bss_addr)
692      char *name;
693      int from_tty;
694      CORE_ADDR text_addr;
695      CORE_ADDR data_addr;
696      CORE_ADDR bss_addr;
697 {
698   struct section_offsets *offs;
699   struct objfile *objfile;
700   struct find_sect_args ss;
701
702   /* It might be nice to suppress the breakpoint_re_set which happens here
703      because we are going to do one again after the objfile_relocate.  */
704   objfile = symbol_file_add (name, from_tty, 0, 0, 0, 0, 0, 0);
705
706   /* This is a (slightly cheesy) way of superceding the old symbols.  A less
707      cheesy way would be to find the objfile with the same name and
708      free_objfile it.  */
709   objfile_to_front (objfile);
710
711   offs = (struct section_offsets *)
712     alloca (sizeof (struct section_offsets)
713             + objfile->num_sections * sizeof (offs->offsets));
714   memcpy (offs, objfile->section_offsets,
715           sizeof (struct section_offsets)
716           + objfile->num_sections * sizeof (offs->offsets));
717
718   ss.text_start = 0;
719   ss.data_start = 0;
720   ss.bss_start = 0;
721   bfd_map_over_sections (objfile->obfd, find_sect, &ss);
722
723   /* Both COFF and b.out frontends use these SECT_OFF_* values.  */
724   ANOFFSET (offs, SECT_OFF_TEXT) = text_addr - ss.text_start;
725   ANOFFSET (offs, SECT_OFF_DATA) = data_addr - ss.data_start;
726   ANOFFSET (offs, SECT_OFF_BSS) = bss_addr - ss.bss_start;
727   objfile_relocate (objfile, offs);
728 }
729
730 /* This function allows the addition of incrementally linked object files.  */
731
732 static void
733 vx_load_command (arg_string, from_tty)
734      char *arg_string;
735      int from_tty;
736 {
737   CORE_ADDR text_addr;
738   CORE_ADDR data_addr;
739   CORE_ADDR bss_addr;
740
741   if (arg_string == 0)
742     error ("The load command takes a file name");
743
744   arg_string = tilde_expand (arg_string);
745   make_cleanup (free, arg_string);
746
747   dont_repeat ();
748
749   /* Refuse to load the module if a debugged task is running.  Doing so
750      can have a number of unpleasant consequences to the running task.  */
751
752   if (inferior_pid != 0 && target_has_execution)
753     {
754       if (query ("You may not load a module while the target task is running.\n\
755 Kill the target task? "))
756         target_kill ();
757       else
758         error ("Load cancelled.");
759     }
760
761   QUIT;
762   immediate_quit++;
763   if (net_load (arg_string, &text_addr, &data_addr, &bss_addr) == -1)
764     error ("Load failed on target machine");
765   immediate_quit--;
766
767   vx_add_symbols (arg_string, from_tty, text_addr, data_addr, bss_addr);
768
769   /* Getting new symbols may change our opinion about what is
770      frameless.  */
771   reinit_frame_cache ();
772 }
773
774 /* Single step the target program at the source or machine level.
775    Takes an error exit if rpc fails.
776    Returns -1 if remote single-step operation fails, else 0.  */
777
778 static int
779 net_step ()
780 {
781   enum clnt_stat status;
782   int step_status;
783   SOURCE_STEP source_step;
784
785   source_step.taskId = inferior_pid;
786
787   if (step_range_end)
788     {
789       source_step.startAddr = step_range_start;
790       source_step.endAddr = step_range_end;
791     }
792   else
793     {
794       source_step.startAddr = 0;
795       source_step.endAddr = 0;
796     }
797
798   status = net_clnt_call (VX_SOURCE_STEP, xdr_SOURCE_STEP, &source_step,
799                           xdr_int, &step_status);
800
801   if (status == RPC_SUCCESS)
802     return step_status;
803   else
804     error (rpcerr);
805 }
806
807 /* Emulate ptrace using RPC calls to the VxWorks target system.
808    Returns nonzero (-1) if RPC status to VxWorks is bad, 0 otherwise.  */
809
810 static int
811 net_ptrace_clnt_call (request, pPtraceIn, pPtraceOut)
812      enum ptracereq request;
813      Rptrace *pPtraceIn;
814      Ptrace_return *pPtraceOut;
815 {
816   enum clnt_stat status;
817
818   status = net_clnt_call (request, xdr_rptrace, pPtraceIn, xdr_ptrace_return,
819                           pPtraceOut);
820
821   if (status != RPC_SUCCESS)
822     return -1;
823
824   return 0;
825 }
826
827 /* Query the target for the name of the file from which VxWorks was
828    booted.  pBootFile is the address of a pointer to the buffer to
829    receive the file name; if the pointer pointed to by pBootFile is 
830    NULL, memory for the buffer will be allocated by XDR.
831    Returns -1 if rpc failed, 0 otherwise.  */
832
833 static int
834 net_get_boot_file (pBootFile)
835      char **pBootFile;
836 {
837   enum clnt_stat status;
838
839   status = net_clnt_call (VX_BOOT_FILE_INQ, xdr_void, (char *) 0,
840                           xdr_wrapstring, pBootFile);
841   return (status == RPC_SUCCESS) ? 0 : -1;
842 }
843
844 /* Fetch a list of loaded object modules from the VxWorks target.
845    Returns -1 if rpc failed, 0 otherwise
846    There's no way to check if the returned loadTable is correct.
847    VxWorks doesn't check it.  */
848
849 static int
850 net_get_symbols (pLoadTable)
851      ldtabl *pLoadTable;        /* return pointer to ldtabl here */
852 {
853   enum clnt_stat status;
854
855   memset ((char *) pLoadTable, '\0', sizeof (struct ldtabl));
856
857   status = net_clnt_call (VX_STATE_INQ, xdr_void, 0, xdr_ldtabl, pLoadTable);
858   return (status == RPC_SUCCESS) ? 0 : -1;
859 }
860
861 /* Look up a symbol in the VxWorks target's symbol table.
862    Returns status of symbol read on target side (0=success, -1=fail)
863    Returns -1 and complain()s if rpc fails.  */
864
865 struct complaint cant_contact_target =
866 {"Lost contact with VxWorks target", 0, 0};
867
868 static int
869 vx_lookup_symbol (name, pAddr)
870      char *name;                /* symbol name */
871      CORE_ADDR *pAddr;
872 {
873   enum clnt_stat status;
874   SYMBOL_ADDR symbolAddr;
875
876   *pAddr = 0;
877   memset ((char *) &symbolAddr, '\0', sizeof (symbolAddr));
878
879   status = net_clnt_call (VX_SYMBOL_INQ, xdr_wrapstring, &name,
880                           xdr_SYMBOL_ADDR, &symbolAddr);
881   if (status != RPC_SUCCESS)
882     {
883       complain (&cant_contact_target);
884       return -1;
885     }
886
887   *pAddr = symbolAddr.addr;
888   return symbolAddr.status;
889 }
890
891 /* Check to see if the VxWorks target has a floating point coprocessor.
892    Returns 1 if target has floating point processor, 0 otherwise.
893    Calls error() if rpc fails.  */
894
895 static int
896 net_check_for_fp ()
897 {
898   enum clnt_stat status;
899   bool_t fp = 0;                /* true if fp processor is present on target board */
900
901   status = net_clnt_call (VX_FP_INQUIRE, xdr_void, 0, xdr_bool, &fp);
902   if (status != RPC_SUCCESS)
903     error (rpcerr);
904
905   return (int) fp;
906 }
907
908 /* Establish an RPC connection with the VxWorks target system.
909    Calls error () if unable to establish connection.  */
910
911 static void
912 net_connect (host)
913      char *host;
914 {
915   struct sockaddr_in destAddr;
916   struct hostent *destHost;
917   unsigned long addr;
918
919   /* Get the internet address for the given host.  Allow a numeric
920      IP address or a hostname.  */
921
922   addr = inet_addr (host);
923   if (addr == -1)
924     {
925       destHost = (struct hostent *) gethostbyname (host);
926       if (destHost == NULL)
927         /* FIXME: Probably should include hostname here in quotes.
928            For example if the user types "target vxworks vx960 " it should
929            say "Invalid host `vx960 '." not just "Invalid hostname".  */
930         error ("Invalid hostname.  Couldn't find remote host address.");
931       addr = *(unsigned long *) destHost->h_addr;
932     }
933
934   memset (&destAddr, '\0', sizeof (destAddr));
935
936   destAddr.sin_addr.s_addr = addr;
937   destAddr.sin_family = AF_INET;
938   destAddr.sin_port = 0;        /* set to actual port that remote
939                                    ptrace is listening on.  */
940
941   /* Create a tcp client transport on which to issue
942      calls to the remote ptrace server.  */
943
944   ptraceSock = RPC_ANYSOCK;
945   pClient = clnttcp_create (&destAddr, RDBPROG, RDBVERS, &ptraceSock, 0, 0);
946   /* FIXME, here is where we deal with different version numbers of the
947      proto */
948
949   if (pClient == NULL)
950     {
951       clnt_pcreateerror ("\tnet_connect");
952       error ("Couldn't connect to remote target.");
953     }
954 }
955 \f
956 /* Sleep for the specified number of milliseconds 
957  * (assumed to be less than 1000).
958  * If select () is interrupted, returns immediately;
959  * takes an error exit if select () fails for some other reason.
960  */
961
962 static void
963 sleep_ms (ms)
964      long ms;
965 {
966   struct timeval select_timeout;
967   int status;
968
969   select_timeout.tv_sec = 0;
970   select_timeout.tv_usec = ms * 1000;
971
972   status = select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0,
973                    &select_timeout);
974
975   if (status < 0 && errno != EINTR)
976     perror_with_name ("select");
977 }
978
979 static int
980 vx_wait (pid_to_wait_for, status)
981      int pid_to_wait_for;
982      struct target_waitstatus *status;
983 {
984   register int pid;
985   RDB_EVENT rdbEvent;
986   int quit_failed;
987
988   do
989     {
990       /* If CTRL-C is hit during this loop,
991          suspend the inferior process.  */
992
993       quit_failed = 0;
994       if (quit_flag)
995         {
996           quit_failed = (net_quit () == -1);
997           quit_flag = 0;
998         }
999
1000       /* If a net_quit () or net_wait () call has failed,
1001          allow the user to break the connection with the target.
1002          We can't simply error () out of this loop, since the 
1003          data structures representing the state of the inferior
1004          are in an inconsistent state.  */
1005
1006       if (quit_failed || net_wait (&rdbEvent) == -1)
1007         {
1008           terminal_ours ();
1009           if (query ("Can't %s.  Disconnect from target system? ",
1010                      (quit_failed) ? "suspend remote task"
1011                      : "get status of remote task"))
1012             {
1013               target_mourn_inferior ();
1014               error ("Use the \"target\" command to reconnect.");
1015             }
1016           else
1017             {
1018               terminal_inferior ();
1019               continue;
1020             }
1021         }
1022
1023       pid = rdbEvent.taskId;
1024       if (pid == 0)
1025         {
1026           sleep_ms (200);       /* FIXME Don't kill the network too badly */
1027         }
1028       else if (pid != inferior_pid)
1029         fatal ("Bad pid for debugged task: %s\n",
1030                local_hex_string ((unsigned long) pid));
1031     }
1032   while (pid == 0);
1033
1034   /* The mostly likely kind.  */
1035   status->kind = TARGET_WAITKIND_STOPPED;
1036
1037   switch (rdbEvent.eventType)
1038     {
1039     case EVENT_EXIT:
1040       status->kind = TARGET_WAITKIND_EXITED;
1041       /* FIXME is it possible to distinguish between a
1042          normal vs abnormal exit in VxWorks? */
1043       status->value.integer = 0;
1044       break;
1045
1046     case EVENT_START:
1047       /* Task was just started. */
1048       status->value.sig = TARGET_SIGNAL_TRAP;
1049       break;
1050
1051     case EVENT_STOP:
1052       status->value.sig = TARGET_SIGNAL_TRAP;
1053       /* XXX was it stopped by a signal?  act accordingly */
1054       break;
1055
1056     case EVENT_BREAK:           /* Breakpoint was hit. */
1057       status->value.sig = TARGET_SIGNAL_TRAP;
1058       break;
1059
1060     case EVENT_SUSPEND: /* Task was suspended, probably by ^C. */
1061       status->value.sig = TARGET_SIGNAL_INT;
1062       break;
1063
1064     case EVENT_BUS_ERR: /* Task made evil nasty reference. */
1065       status->value.sig = TARGET_SIGNAL_BUS;
1066       break;
1067
1068     case EVENT_ZERO_DIV:        /* Division by zero */
1069       status->value.sig = TARGET_SIGNAL_FPE;
1070       break;
1071
1072     case EVENT_SIGNAL:
1073 #ifdef I80960
1074       status->value.sig = i960_fault_to_signal (rdbEvent.sigType);
1075 #else
1076       /* Back in the old days, before enum target_signal, this code used
1077          to add NSIG to the signal number and claim that PRINT_RANDOM_SIGNAL
1078          would take care of it.  But PRINT_RANDOM_SIGNAL has never been
1079          defined except on the i960, so I don't really know what we are
1080          supposed to do on other architectures.  */
1081       status->value.sig = TARGET_SIGNAL_UNKNOWN;
1082 #endif
1083       break;
1084     }                           /* switch */
1085   return pid;
1086 }
1087 \f
1088 static int
1089 symbol_stub (arg)
1090      char *arg;
1091 {
1092   symbol_file_command (arg, 0);
1093   return 1;
1094 }
1095
1096 static int
1097 add_symbol_stub (arg)
1098      char *arg;
1099 {
1100   struct ldfile *pLoadFile = (struct ldfile *) arg;
1101
1102   printf_unfiltered ("\t%s: ", pLoadFile->name);
1103   vx_add_symbols (pLoadFile->name, 0, pLoadFile->txt_addr,
1104                   pLoadFile->data_addr, pLoadFile->bss_addr);
1105   printf_unfiltered ("ok\n");
1106   return 1;
1107 }
1108 /* Target command for VxWorks target systems.
1109
1110    Used in vxgdb.  Takes the name of a remote target machine
1111    running vxWorks and connects to it to initialize remote network
1112    debugging.  */
1113
1114 static void
1115 vx_open (args, from_tty)
1116      char *args;
1117      int from_tty;
1118 {
1119   extern int close ();
1120   char *bootFile;
1121   extern char *source_path;
1122   struct ldtabl loadTable;
1123   struct ldfile *pLoadFile;
1124   int i;
1125   extern CLIENT *pClient;
1126   int symbols_added = 0;
1127
1128   if (!args)
1129     error_no_arg ("target machine name");
1130
1131   target_preopen (from_tty);
1132
1133   unpush_target (&vx_ops);
1134   printf_unfiltered ("Attaching remote machine across net...\n");
1135   gdb_flush (gdb_stdout);
1136
1137   /* Allow the user to kill the connect attempt by typing ^C.
1138      Wait until the call to target_has_fp () completes before
1139      disallowing an immediate quit, since even if net_connect ()
1140      is successful, the remote debug server might be hung.  */
1141
1142   immediate_quit++;
1143
1144   net_connect (args);
1145   target_has_fp = net_check_for_fp ();
1146   printf_filtered ("Connected to %s.\n", args);
1147
1148   immediate_quit--;
1149
1150   push_target (&vx_ops);
1151
1152   /* Save a copy of the target host's name.  */
1153   vx_host = savestring (args, strlen (args));
1154
1155   /* Find out the name of the file from which the target was booted
1156      and load its symbol table.  */
1157
1158   printf_filtered ("Looking in Unix path for all loaded modules:\n");
1159   bootFile = NULL;
1160   if (!net_get_boot_file (&bootFile))
1161     {
1162       if (*bootFile)
1163         {
1164           printf_filtered ("\t%s: ", bootFile);
1165           /* This assumes that the kernel is never relocated.  Hope that is an
1166              accurate assumption.  */
1167           if (catch_errors
1168               (symbol_stub,
1169                bootFile,
1170                "Error while reading symbols from boot file:\n",
1171                RETURN_MASK_ALL))
1172             puts_filtered ("ok\n");
1173         }
1174       else if (from_tty)
1175         printf_unfiltered ("VxWorks kernel symbols not loaded.\n");
1176     }
1177   else
1178     error ("Can't retrieve boot file name from target machine.");
1179
1180   clnt_freeres (pClient, xdr_wrapstring, &bootFile);
1181
1182   if (net_get_symbols (&loadTable) != 0)
1183     error ("Can't read loaded modules from target machine");
1184
1185   i = 0 - 1;
1186   while (++i < loadTable.tbl_size)
1187     {
1188       QUIT;                     /* FIXME, avoids clnt_freeres below:  mem leak */
1189       pLoadFile = &loadTable.tbl_ent[i];
1190 #ifdef WRS_ORIG
1191       {
1192         register int desc;
1193         struct cleanup *old_chain;
1194         char *fullname = NULL;
1195
1196         desc = openp (source_path, 0, pLoadFile->name, O_RDONLY, 0, &fullname);
1197         if (desc < 0)
1198           perror_with_name (pLoadFile->name);
1199         old_chain = make_cleanup (close, desc);
1200         add_file_at_addr (fullname, desc, pLoadFile->txt_addr, pLoadFile->data_addr,
1201                           pLoadFile->bss_addr);
1202         do_cleanups (old_chain);
1203       }
1204 #else
1205       /* FIXME: Is there something better to search than the PATH? (probably
1206          not the source path, since source might be in different directories
1207          than objects.  */
1208
1209       if (catch_errors (add_symbol_stub, (char *) pLoadFile, (char *) 0,
1210                         RETURN_MASK_ALL))
1211         symbols_added = 1;
1212 #endif
1213     }
1214   printf_filtered ("Done.\n");
1215
1216   clnt_freeres (pClient, xdr_ldtabl, &loadTable);
1217
1218   /* Getting new symbols may change our opinion about what is
1219      frameless.  */
1220   if (symbols_added)
1221     reinit_frame_cache ();
1222 }
1223 \f
1224 /* Takes a task started up outside of gdb and ``attaches'' to it.
1225    This stops it cold in its tracks and allows us to start tracing it.  */
1226
1227 static void
1228 vx_attach (args, from_tty)
1229      char *args;
1230      int from_tty;
1231 {
1232   unsigned long pid;
1233   char *cptr = 0;
1234   Rptrace ptrace_in;
1235   Ptrace_return ptrace_out;
1236   int status;
1237
1238   if (!args)
1239     error_no_arg ("process-id to attach");
1240
1241   pid = strtoul (args, &cptr, 0);
1242   if ((cptr == args) || (*cptr != '\0'))
1243     error ("Invalid process-id -- give a single number in decimal or 0xhex");
1244
1245   if (from_tty)
1246     printf_unfiltered ("Attaching pid %s.\n",
1247                        local_hex_string ((unsigned long) pid));
1248
1249   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
1250   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
1251   ptrace_in.pid = pid;
1252
1253   status = net_ptrace_clnt_call (PTRACE_ATTACH, &ptrace_in, &ptrace_out);
1254   if (status == -1)
1255     error (rpcerr);
1256   if (ptrace_out.status == -1)
1257     {
1258       errno = ptrace_out.errno_num;
1259       perror_with_name ("Attaching remote process");
1260     }
1261
1262   /* It worked... */
1263
1264   inferior_pid = pid;
1265   push_target (&vx_run_ops);
1266
1267   if (vx_running)
1268     free (vx_running);
1269   vx_running = 0;
1270 }
1271
1272 /* detach_command --
1273    takes a program previously attached to and detaches it.
1274    The program resumes execution and will no longer stop
1275    on signals, etc.  We better not have left any breakpoints
1276    in the program or it'll die when it hits one.  For this
1277    to work, it may be necessary for the process to have been
1278    previously attached.  It *might* work if the program was
1279    started via the normal ptrace (PTRACE_TRACEME).  */
1280
1281 static void
1282 vx_detach (args, from_tty)
1283      char *args;
1284      int from_tty;
1285 {
1286   Rptrace ptrace_in;
1287   Ptrace_return ptrace_out;
1288   int signal = 0;
1289   int status;
1290
1291   if (args)
1292     error ("Argument given to VxWorks \"detach\".");
1293
1294   if (from_tty)
1295     printf_unfiltered ("Detaching pid %s.\n",
1296                        local_hex_string ((unsigned long) inferior_pid));
1297
1298   if (args)                     /* FIXME, should be possible to leave suspended */
1299     signal = atoi (args);
1300
1301   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
1302   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
1303   ptrace_in.pid = inferior_pid;
1304
1305   status = net_ptrace_clnt_call (PTRACE_DETACH, &ptrace_in, &ptrace_out);
1306   if (status == -1)
1307     error (rpcerr);
1308   if (ptrace_out.status == -1)
1309     {
1310       errno = ptrace_out.errno_num;
1311       perror_with_name ("Detaching VxWorks process");
1312     }
1313
1314   inferior_pid = 0;
1315   pop_target ();                /* go back to non-executing VxWorks connection */
1316 }
1317
1318 /* vx_kill -- takes a running task and wipes it out.  */
1319
1320 static void
1321 vx_kill ()
1322 {
1323   Rptrace ptrace_in;
1324   Ptrace_return ptrace_out;
1325   int status;
1326
1327   printf_unfiltered ("Killing pid %s.\n", local_hex_string ((unsigned long) inferior_pid));
1328
1329   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
1330   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
1331   ptrace_in.pid = inferior_pid;
1332
1333   status = net_ptrace_clnt_call (PTRACE_KILL, &ptrace_in, &ptrace_out);
1334   if (status == -1)
1335     warning (rpcerr);
1336   else if (ptrace_out.status == -1)
1337     {
1338       errno = ptrace_out.errno_num;
1339       perror_with_name ("Killing VxWorks process");
1340     }
1341
1342   /* If it gives good status, the process is *gone*, no events remain.
1343      If the kill failed, assume the process is gone anyhow.  */
1344   inferior_pid = 0;
1345   pop_target ();                /* go back to non-executing VxWorks connection */
1346 }
1347
1348 /* Clean up from the VxWorks process target as it goes away.  */
1349
1350 static void
1351 vx_proc_close (quitting)
1352      int quitting;
1353 {
1354   inferior_pid = 0;             /* No longer have a process.  */
1355   if (vx_running)
1356     free (vx_running);
1357   vx_running = 0;
1358 }
1359 \f
1360 /* Make an RPC call to the VxWorks target.
1361    Returns RPC status.  */
1362
1363 static enum clnt_stat
1364 net_clnt_call (procNum, inProc, in, outProc, out)
1365      enum ptracereq procNum;
1366      xdrproc_t inProc;
1367      char *in;
1368      xdrproc_t outProc;
1369      char *out;
1370 {
1371   enum clnt_stat status;
1372
1373   status = clnt_call (pClient, procNum, inProc, in, outProc, out, rpcTimeout);
1374
1375   if (status != RPC_SUCCESS)
1376     clnt_perrno (status);
1377
1378   return status;
1379 }
1380
1381 /* Clean up before losing control.  */
1382
1383 static void
1384 vx_close (quitting)
1385      int quitting;
1386 {
1387   if (pClient)
1388     clnt_destroy (pClient);     /* The net connection */
1389   pClient = 0;
1390
1391   if (vx_host)
1392     free (vx_host);             /* The hostname */
1393   vx_host = 0;
1394 }
1395
1396 /* A vxprocess target should be started via "run" not "target".  */
1397 /*ARGSUSED */
1398 static void
1399 vx_proc_open (name, from_tty)
1400      char *name;
1401      int from_tty;
1402 {
1403   error ("Use the \"run\" command to start a VxWorks process.");
1404 }
1405
1406 static void
1407 init_vx_ops ()
1408 {
1409   vx_ops.to_shortname = "vxworks";
1410   vx_ops.to_longname = "VxWorks target memory via RPC over TCP/IP";
1411   vx_ops.to_doc = "Use VxWorks target memory.  \n\
1412 Specify the name of the machine to connect to.";
1413   vx_ops.to_open = vx_open;
1414   vx_ops.to_close = vx_close;
1415   vx_ops.to_attach = vx_attach;
1416   vx_ops.to_xfer_memory = vx_xfer_memory;
1417   vx_ops.to_files_info = vx_files_info;
1418   vx_ops.to_load = vx_load_command;
1419   vx_ops.to_lookup_symbol = vx_lookup_symbol;
1420   vx_ops.to_create_inferior = vx_create_inferior;
1421   vx_ops.to_stratum = core_stratum;
1422   vx_ops.to_has_all_memory = 1;
1423   vx_ops.to_has_memory = 1;
1424   vx_ops.to_magic = OPS_MAGIC;  /* Always the last thing */
1425 };
1426
1427 static void
1428 init_vx_run_ops ()
1429 {
1430   vx_run_ops.to_shortname = "vxprocess";
1431   vx_run_ops.to_longname = "VxWorks process";
1432   vx_run_ops.to_doc = "VxWorks process; started by the \"run\" command.";
1433   vx_run_ops.to_open = vx_proc_open;
1434   vx_run_ops.to_close = vx_proc_close;
1435   vx_run_ops.to_detach = vx_detach;
1436   vx_run_ops.to_resume = vx_resume;
1437   vx_run_ops.to_wait = vx_wait;
1438   vx_run_ops.to_fetch_registers = vx_read_register;
1439   vx_run_ops.to_store_registers = vx_write_register;
1440   vx_run_ops.to_prepare_to_store = vx_prepare_to_store;
1441   vx_run_ops.to_xfer_memory = vx_xfer_memory;
1442   vx_run_ops.to_files_info = vx_run_files_info;
1443   vx_run_ops.to_insert_breakpoint = vx_insert_breakpoint;
1444   vx_run_ops.to_remove_breakpoint = vx_remove_breakpoint;
1445   vx_run_ops.to_kill = vx_kill;
1446   vx_run_ops.to_load = vx_load_command;
1447   vx_run_ops.to_lookup_symbol = vx_lookup_symbol;
1448   vx_run_ops.to_mourn_inferior = vx_mourn_inferior;
1449   vx_run_ops.to_stratum = process_stratum;
1450   vx_run_ops.to_has_memory = 1;
1451   vx_run_ops.to_has_stack = 1;
1452   vx_run_ops.to_has_registers = 1;
1453   vx_run_ops.to_has_execution = 1;
1454   vx_run_ops.to_magic = OPS_MAGIC;
1455 }
1456 \f
1457 void
1458 _initialize_vx ()
1459 {
1460   init_vx_ops ();
1461   add_target (&vx_ops);
1462   init_vx_run_ops ();
1463   add_target (&vx_run_ops);
1464
1465   add_show_from_set
1466     (add_set_cmd ("vxworks-timeout", class_support, var_uinteger,
1467                   (char *) &rpcTimeout.tv_sec,
1468                   "Set seconds to wait for rpc calls to return.\n\
1469 Set the number of seconds to wait for rpc calls to return.", &setlist),
1470      &showlist);
1471 }
This page took 0.107927 seconds and 4 git commands to generate.