1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
|
/* Command Message Queue
* Copyright (C) 2009 Chris Hall (GMCH), Highwayman
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your
* option) any later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "misc.h"
#include "qpnexus.h"
#include "mqueue.h"
#include "thread.h"
#include "command_queue.h"
#include "command_execute.h"
#include "vty_command.h"
#include "log.h"
/*==============================================================================
* This command loop processes commands for VTY_TERMINAL and VTY_VTYSH_SERVER.
*
* The command loop is a form of co-routine, which is "called" by sending a
* message (or by legacy threads events). In the multi-pthread world, the
* command loop may run in either the vty_cli_nexus or the vty_cmd_nexus, and
* may be passed from one to the other by sending a message.
*
* This command loop is usually in one of:
*
* vcl_cq_running -- the command loop is running, doing things.
*
* In this state the vty and the vty->exec are in the hands
* of the command loop.
*
* The vty->vio is always accessed under VTY_LOCK().
*
* Note that one of the things the command loop may be
* doing is waiting for a message to be picked up (or
* a legacy thread event to be dispatched).
*
* vcl_cq_waiting -- the command loop is waiting for something to happen
* (typically some I/O), and will stay there until a
* message is sent (or a legacy threads event).
*
* To be waiting the command loop must have entered "hiatus"
* and then exited.
*
* When a command loop is or has been stopped, to goes to vcl_stopped state.
*
* There are further issues:
*
* * In the mult-pthreaded world, commands may be parsed in either pthread,
* but most are executed in the Routing thread. So may switch pthreads
* after parsing a command -- by sending a message.
*
* * opening pipes is done in the vty_cli_nexus, in case of any possible
* blocking and because the pselect() stuff is all done in the
* vty_cli_nexus.
*
* * all output is via fifo buffers -- when output is pushed, as much as
* possible is done immediately, but if necessary the pselect() process
* (in the vty_cli_nexus) is left to keep things moving.
*
* In vty_io_basic() it is possible to set read/write ready and associated
* timeouts while running in the vty_cmd_nexus, but note that this is done
* by sending messages to the CLI thread (if multi-pthreaded).
*
* The smooth running of the command handling depends on the continued
* running of the CLI thread.
*
* To close a VTY must (eventually) arrange for the state to not be
* vcl_cq_running. While a vty is vcl_cq_running, the command loop may be in
* either nexus, and may be:
*
* - on the vty_cli_nexus queue (or the combined queue)
* - executing cq_process() in the vty_cli_nexus (or the single "both" nexus)
* - on the vty_cmd_nexus queue (if different)
* - executing cq_process() in the vty_cmd_nexus (if different)
*
* where being on the queue means that there is a message waiting to be
* picked up on that queue, which will end up calling cq_process().
*
* Or, in the legacy threads world:
*
* - on the event queue -- waiting for event handler to call cq_process()
* - executing cq_process()
*
* To bring the command loop to a stop will call cq_revoke to revoke any
* pending message or pending legacy event. If that succeeds, then the
* command loop can be stopped immediately. If not, then vcl_cq_running implies
* it is executing in cq_process() in one of the nexuses.
*/
/*------------------------------------------------------------------------------
* Prototypes
*/
static cmd_exec_state_t cq_enqueue(struct vty *vty, qpn_nexus dst,
cmd_exec_state_t state, cmd_ret_t ret) ;
static void cq_action(mqueue_block mqb, mqb_flag_t flag);
static int cq_thread(struct thread* thread) ;
static cmd_exec_state_t cq_process(vty vty) ;
/*------------------------------------------------------------------------------
* Enqueue vty to enter the command loop -- must be exec_null
*
* Expects the vty start up process to have output some cheerful messages,
* which is treated as a dummy "login" command. So the loop is entered at
* "exec_cmd_complete", which will deal with the return code, push any output,
* and loop round to fetch the first command line (if required).
*
* The incoming return code may be CMD_SUCCESS, CMD_WARNING or CMD_ERROR.
*/
extern void
cq_loop_enter(vty vty, cmd_ret_t ret)
{
VTY_ASSERT_CLI_THREAD() ;
qassert(vty->exec->state == exec_null) ;
vty->exec->locus = vty_cli_nexus ;
if (vty_nexus && (vty->exec->cq.mqb == NULL))
vty->exec->cq.mqb = mqb_init_new(NULL, cq_action, vty) ;
cq_enqueue(vty, vty_cli_nexus, exec_cmd_complete, ret) ;
} ;
/*------------------------------------------------------------------------------
* Resume at hiatus in cli nexus -- must be exec_hiatus
*/
extern void
cq_loop_resume(vty vty, cmd_ret_t ret)
{
VTY_ASSERT_CLI_THREAD() ;
qassert(vty->exec->state == exec_hiatus) ;
if (vty->exec->state == exec_hiatus)
cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
} ;
/*------------------------------------------------------------------------------
* Enqueue vty for execution in given nexus or issue thread event.
*
* Will execute in the current exec->state, passing in the given return
* code.
*
* Note that *must* be vcl_cq_running.
*
* Returns: the now current exec->state
* if is exec_stopped, then vty_close()
*/
static cmd_exec_state_t
cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state, cmd_ret_t ret)
{
cmd_exec exec = vty->exec ;
qassert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
VTY_LOCK() ;
qassert(vty->vio->cl_state == vcl_cq_running) ;
exec->locus = dst ; /* VTY_LOCKED */
exec->state = state ;
exec->ret = ret ;
if (vty_nexus)
{
qassert(exec->cq.mqb != NULL) ;
/* Using mqb_priority on the vty_cmd_nexus has two effects:
*
* 1. commands being dispatched take priority over other work.
*
* 2. for single pthread, all messages related to the command queue
* itself (in particular, signals) take priority.
*
* (For single pthread vty_cmd_nexus == vty_cli_nexus.)
*/
mqueue_enqueue(dst->queue, exec->cq.mqb,
(dst == vty_cmd_nexus) ? mqb_priority
: mqb_ordinary) ;
}
else
{
qassert(vty_cli_nexus == vty_cmd_nexus) ;
exec->cq.thread = thread_add_event(master, cq_thread, vty, 0) ;
} ;
VTY_UNLOCK() ;
return state ;
} ;
/*------------------------------------------------------------------------------
* Revoke any message associated with the given vty from the command queue.
*
* Must be vcl_cq_running or vcl_cq_waiting.
*
* This is used when a VTY_TERMINAL or a VTY_VTYSH_SERVER is being closed.
*
* See cq_process below for discussion of what messages there may be. At any
* time there is at most one message in flight.
*
* In the single-threaded world (including legacy threads), messages or events
* are used to enter or resume the command loop. If a message or event can
* be revoked, then the command loop is effectively stopped.
*
* In the multi-threaded world, messages are used to enter or resume the
* command loop, and to transfer it to and from the cli and cmd threads. If
* a message can be revoked, the command loop is effectively stopped.
*
* Note that the revoke does not affect any vty_cli_nexus messages associated
* with the vty_io_basic operations.
*
* If succeeds in revoking, then sets the state to vcl_cq_waiting, in the
* cli nexus and in exec_hiatus.
*
* NB: if no command loop has ever been started for this vty, then will not
* find anything to revoke.
*
* NB: if is vcl_cq_waiting already, then should not find anything to revoke,
* but does no harm to try !
*
* Returns: true <=> the command loop is now vcl_cq_waiting
*/
static bool
cq_revoke(vty_io vio, cmd_ret_t ret)
{
vty vty ;
cmd_exec exec ;
bool waiting ;
VTY_ASSERT_CLI_THREAD_LOCKED() ;
vty = vio->vty ;
exec = vty->exec ;
qassert( (vio->cl_state == vcl_cq_running) ||
(vio->cl_state == vcl_cq_waiting) ) ;
waiting = false ;
if (exec->state == exec_null)
return false ; /* just in case */
if ((vio->cl_state != vcl_cq_running) && (vio->cl_state != vcl_cq_waiting))
return false ; /* just in case */
/* If is vcl_cq_waiting, then do not expect to find */
if (vty_nexus)
{
/* We have the VTY_LOCK(). Before mqb is placed on a queue, the
* exec->locus is set, under VTY_LOCK. So when revoking we can
* check the correct queue !
*
* Note that thanks to the magic of mqb_revoke(), if the message has
* been revoked in some other way (as part of closing down a nexus,
* or its message queue), then we will discover the revoked message.
*/
waiting = mqb_revoke(exec->cq.mqb, exec->locus->queue) ;
}
else
{
/* If succeeds in cancelling the event, the thread object is discarded.
*
* If does not succeed, then there is no thread object !
*/
if (exec->cq.thread != NULL)
{
waiting = thread_cancel_event(master, vty) != 0 ;
exec->cq.thread = NULL ;
} ;
} ;
/* If have revoked, from the perspective of the command loop, this looks a
* lot like CMD_CANCEL/CMD_STOP, so that's what we do... force the loop back
* into the hands of the CLI, as if about to resume at the hiatus with one
* of those signals.
*
* If was already vcl_cq_waiting, then can do the same.
*/
if (vio->cl_state == vcl_cq_waiting)
{
waiting = true ;
qassert(exec->locus == vty_cli_nexus) ;
qassert(exec->state = exec_hiatus) ;
} ;
if (waiting)
{
exec->locus = vty_cli_nexus ;
exec->state = exec_hiatus ;
exec->ret = ret ;
vio->cl_state = vcl_cq_waiting ;
} ;
return waiting ;
} ;
/*------------------------------------------------------------------------------
* Deal with command message -- in the qpthreads world.
*
* We do nothing on mqb_destroy. Leaves cq_revoke() to pick this up.
*/
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
{
vty vty;
assert(vty_nexus) ; /* must be running qnexus-wise */
vty = mqb_get_arg0(mqb);
qassert(vty->exec->cq.mqb == mqb) ;
if (flag == mqb_action)
{
cmd_exec_state_t state ;
state = cq_process(vty) ;
if (state == exec_stopped)
vty_close(vty) ;
} ;
} ;
/*------------------------------------------------------------------------------
* Deal with command message -- in the legacy threads world.
*/
static int
cq_thread(struct thread* thread)
{
cmd_exec_state_t state ;
vty vty = THREAD_ARG(thread) ;
vty->exec->cq.thread = NULL ;
state = cq_process(vty) ;
if (state == exec_stopped)
vty_close(vty) ;
return 0 ;
} ;
/*------------------------------------------------------------------------------
* Try to stop the cq command loop.
*
* If there is a message in transit, then revoke it, forcing the command loop
* to exec_hiatus,
*
* If the command loop is (then) vcl_cq_waiting, then enter the command loop
* directly, with CMD_STOP.
*
* It may be that message queue or legacy thread handling is about to stop.
* We pass in CMD_STOP, so the command loop should promptly start closing
* down everything, and not return until everything is done, and the vty
* has been closed, or something blocks while trying to close. By calling
* the command loop directly, we guarantee that it runs at least once !
*
* Returns: true <=> can now close the vty -- uty_close().
*/
extern bool
cq_loop_stop(vty_io vio)
{
cmd_exec_state_t state ;
VTY_ASSERT_CLI_THREAD_LOCKED() ;
qassert( (vio->cl_state == vcl_cq_running) ||
(vio->cl_state == vcl_cq_waiting) ) ;
if (cq_revoke(vio, CMD_STOP))
{
vio->cl_state = vcl_cq_running ;
state = cq_process(vio->vty) ;
}
else
state = ~exec_stopped ;
return (state == exec_stopped) ;
} ;
/*------------------------------------------------------------------------------
* Process command(s) queued from VTY_TERMINAL or from VTY_VTYSH_SERVER.
*
* This is essentially a coroutine, where the state is in the vty, noting that:
*
* vty->exec->state is the "return address"
* vty->exec->ret is the "return code"
*
* Though while we are here, those are held in variables.
*
* The command loop runs at the mqueue level in the qpnexus world, or is
* driven by "events" in the legacy threads world. The three ways into the
* loop are:
*
* cq_loop_enter() -- called once and once only to start the loop.
*
* cq_loop_resume() -- called when some I/O process has completed and
* the loop is in hiatus, waiting for I/O.
*
* cq_loop_stop() -- called when want to stop command loop, forces
* exec_hiatus, ret == CMD_STOP !
*
* The one way out of the loop is when it hits a hiatus, which is triggered
* by any operation not returning CMD_SUCCESS. In hiatus, vty_cmd_hiatus()
* is called and given the current return code to deal with. It will
* return:
*
* CMD_SUCCESS => can proceed to fetch the next command
* CMD_WAITING => must leave the command loop, waiting for I/O
* CMD_STOP => close the vty and leave command loop
* CMD_IO_ERROR => loop back and deal with the error
*
* The cq_loop_enter() and cq_loop_resume() operations send a message (to the
* cli thread, in the mult-pthreaded world), whose action routine is to call
* cq_process().
*
* In the multi-pthreaded world, opening and closing files MUST be done in the
* cli thread. Most commands MUST be executed in the cmd thread. So the
* command loop is passed between the threads in a number of places. (To keep
* life simple, switches back to the cli thread if ends up waiting for I/O.)
*
* The vty_io_basic stuff allows the multi-pthread vty_cmd_nexus to set read
* and/or write ready state -- which may generate messages to the
* vty_cli_nexus.
*
* NB: on exit from cq_process the VTY may have been closed down and released,
* so do NOT depend on its existence.
*
* Returns: the now current exec->state
* if is exec_stopped, then vty_close() or uty_close() the vty.
*/
static cmd_exec_state_t
cq_process(vty vty)
{
cmd_exec exec ;
cmd_parsed parsed ;
cmd_context context ;
cmd_exec_state_t state ;
cmd_ret_t ret ;
/* Note that we have a single parser object for execution.
*
* Also, the context object is kept up to date as pipes are pushed and
* popped -- so the pointer to it remains valid.
*/
exec = vty->exec ;
parsed = exec->parsed ;
context = exec->context ;
qassert(parsed != NULL) ;
qassert(context != NULL) ;
/* Have switch wrapped in a while(1) so that can change state by setting
* state and doing "continue".
*
* Breaking out of the switch forces the state to exec_hiatus, with the
* current ret (which will switch to the CLI thread).
*
* The exec locus is either vty_cli_nexus or vty_cmd_nexus. If these
* are equal (including both NULL, in the legacy threads world), then is
* running single threaded -- otherwise is running multi-threaded.
*/
state = exec->state ;
ret = exec->ret ;
while (1)
{
switch(state)
{
/*--------------------------------------------------------------------
* Should *not* be here in any of these states.
*
* Returns in the current state.
*
* For exec_stopped, that should trigger a vty_close(), and that will
* be the end of it.
*
* For exec_null and anything else the command loop should stall
* (because cq_loop_resume() only re-enters the loop at exec_hiatus).
*/
case exec_null:
case exec_stopped:
default:
qassert(false) ;
return exec->state = state ;
/*--------------------------------------------------------------------
* Hiatus state -- some return code to be dealt with !
*
* NB: we only reach here after breaking out of the switch, or
* on cq_loop_resume().
*/
case exec_hiatus:
qassert(exec->locus == vty_cli_nexus) ;
while (1)
{
/* Let vty_cmd_hiatus() deal with the return code and/or any
* stop/error/etc trap, and adjust the stack as required.
*/
ret = vty_cmd_hiatus(vty, ret) ;
if (ret == CMD_SUCCESS)
break ; /* back to exec_fetch */
if (ret == CMD_IO_ERROR)
continue ; /* give error back to hiatus */
/* Nothing more to be done here.
*
* The exec->ret is not really significant, but we set it for
* completeness.
*
* For CMD_WAITING, we expect to re-enter the loop at
* exec_hiatus, with the return code from a future "signal".
*
* For CMD_STOP, we expect the vty to be closed.
*
* Should not have any other return code here... but we return
* in the same way as CMD_WAITING. If nothing is ever
* signalled, the command loop will simply hang.
*/
exec->ret = ret ;
if (ret != CMD_STOP)
return exec->state = exec_hiatus ;
/* <-<- DONE, pro tem <-<-<-<-<-*/
return exec->state = exec_stopped ;
/* <-<- DONE, permanently <-<-<-*/
} ;
fall_through ;
/*--------------------------------------------------------------------
* Need another command to execute => in_pipe !
*
* If multi-threaded: may be in either thread.
*
* If vty_cmd_line_fetch() cannot, for any reason, return a command
* line, will return something other than CMD_SUCCESS -- which enters
* the exec_hiatus (transferring to vty_cli_nexus).
*/
case exec_fetch:
ret = vty_cmd_line_fetch(vty) ;
if (ret != CMD_SUCCESS)
break ;
if (context->to_do != cmd_do_command)
{
state = exec_special ;
continue ;
} ;
/* Parse command in hand
*
* If multi-threaded: may be in either thread.
*/
cmd_tokenize(parsed, context->line, context->full_lex) ;
ret = cmd_parse_command(parsed, context) ;
if (ret != CMD_SUCCESS)
break ; /* on *any* parsing issue */
if ((parsed->parts & cmd_parts_execute) == 0)
{
/* Empty lines from in_pipes appear here.
*
* Don't expect to see them otherwise, but in any case need to
* complete the command execution process.
*/
state = exec_cmd_complete ;
continue ;
} ;
/* reflect if required -- CMD_WAITING is fine, let the output side
* deal with that.
*/
if (context->reflect)
{
ret = vty_cmd_reflect_line(vty) ;
if ((ret != CMD_SUCCESS) && (ret != CMD_WAITING))
break ; /* CMD_IO_ERROR or CMD_CANCEL */
} ;
/*--------------------------------------------------------------------
* Pipe work if any
*
* If multi-threaded: must be in vty_cli_nexus to proceed.
*/
case exec_open_pipes:
if ((parsed->parts & cmd_parts_pipe) != 0)
{
/* If running pthreaded, do open pipes in vty_cli_nexus
*/
if (exec->locus != vty_cli_nexus)
return cq_enqueue(vty, vty_cli_nexus, exec_open_pipes, ret) ;
/* Now in vty_cli_nexus
*/
ret = cmd_open_pipes(vty) ;
if (ret != CMD_SUCCESS)
break ; /* quit if open fails */
/* Finish if this is pipe only
*/
if ((parsed->parts & cmd_part_command) == 0)
{
state = exec_cmd_complete ;
continue ;
} ;
} ;
fall_through ;
/*--------------------------------------------------------------------
* If multi-threaded: most commands run in the vty_cmd_nexus, some
* run in the vty_cli_nexus -- transfer to the required nexus, as
* required.
*/
qassert((parsed->parts & cmd_part_command) != 0);
if (cmd_is_direct(parsed))
{
if (exec->locus != vty_cli_nexus)
return cq_enqueue(vty, vty_cli_nexus, exec_execute, ret) ;
}
else
{
if (exec->locus != vty_cmd_nexus)
return cq_enqueue(vty, vty_cmd_nexus, exec_execute, ret) ;
}
fall_through ;
/*--------------------------------------------------------------------
* Execute command -- now that is in the required nexus.
*/
case exec_execute:
#ifdef CONSUMED_TIME_CHECK
{
RUSAGE_T before;
RUSAGE_T after;
unsigned long realtime, cputime;
if (!cmd_is_direct(parsed))
GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
ret = cmd_execute(vty) ;
#ifdef CONSUMED_TIME_CHECK
if (!cmd_is_direct(parsed))
{
GETRUSAGE(&after);
realtime = thread_consumed_time(&after, &before, &cputime) ;
if (realtime > CONSUMED_TIME_CHECK)
/* Warn about CPU hog that must be fixed. */
zlog(NULL, LOG_WARNING,
"SLOW COMMAND: command took %lums (cpu time %lums): %s",
realtime/1000, cputime/1000,
qs_string(exec->context->line)) ;
} ;
} ;
#endif /* CONSUMED_TIME_CHECK */
/*--------------------------------------------------------------------
* Command has completed somehow -- this is the loop entry point.
*
* Return code may be: CMD_SUCCESS, CMD_WARNING, CMD_ERROR, CMD_CANCEL,
* or CMD_IO_ERROR.
*
* Pushes output -- note that the push is forced, even if the output
* buffers are empty, so that the command completion is signalled to
* the output side.
*
* Will either loop back to fetch the next command line, or fall into
* the hiatus.
*
* If multi-threaded: may be in either thread:
*
* vty_cmd_complete() may set write ready -- so in vty_cmd_nexus may
* generate message to vty_cli_nexus.
*/
case exec_cmd_complete:
ret = vty_cmd_complete(vty, ret) ;
if (ret != CMD_SUCCESS)
break ;
state = exec_fetch ;
continue ;
/*--------------------------------------------------------------------
* Deal with the "special" commands
*/
case exec_special:
if (exec->locus != vty_cli_nexus)
return cq_enqueue(vty, vty_cli_nexus, exec_special, ret) ;
ret = vty_cmd_special(vty) ;
state = exec_cmd_complete ;
continue ;
} ;
/* Have broken out of the switch() => exec_hiatus
*
* Something has returned a return code that causes entry to the hiatus
* state to sort out.
*
* If we are not in the vty_cli_nexus, then must pass the problem
* to the vty_cli_nexus.
*/
if (exec->locus != vty_cli_nexus)
return cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
state = exec_hiatus ;
continue ;
} ;
} ;
|