summaryrefslogtreecommitdiffstats
path: root/lib/command_queue.c
blob: b6aab5805c052d8b463a37a3fd9e5442e9a29950 (plain)
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
/* 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 <zebra.h>

#include "mqueue.h"
#include "qpnexus.h"
#include "memory.h"
#include "command_queue.h"

/*------------------------------------------------------------------------------
 * Form of message passed with command to be executed
 */

struct cq_command_args
{
  qpn_nexus ret_nexus ;

  struct cmd_element *cmd ;

  enum node_type  cnode ;               /* vty->node before execution   */
  enum node_type  onode ;               /* vty->node before "do"        */

  short int       do_shortcut ;         /* true => is "do" command      */

  short int       argc ;                /* count of arguments           */
  short int       ret ;                 /* return code                  */
} ;
MQB_ARGS_SIZE_OK(cq_command_args) ;

/*------------------------------------------------------------------------------
 * Prototypes
 */
static void cq_action(mqueue_block mqb, mqb_flag_t flag);
static void cq_return(mqueue_block mqb, mqb_flag_t flag);

/*------------------------------------------------------------------------------
 * Enqueue vty and argv[] for execution in given nexus.
 */
void
cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
                                                       qpn_nexus from_nexus)
{
  int i;
  struct cq_command_args* args ;

  mqueue_block mqb = mqb_init_new(NULL, cq_action, vty) ;
  args = mqb_get_args(mqb) ;

  args->cmd         = parsed->cmd ;
  args->cnode       = parsed->cnode ;
  args->onode       = parsed->onode ;
  args->do_shortcut = parsed->do_shortcut ;
  args->argc        = parsed->argc ;

  args->ret_nexus  = from_nexus ;
  args->ret        = CMD_SUCCESS ;

  for (i = 0; i < parsed->argc; ++i)
    mqb_push_argv_p(mqb, XSTRDUP(MTYPE_MARSHAL, parsed->argv[i]));

  mqueue_enqueue(to_nexus->queue, mqb, 0) ;
}

/*------------------------------------------------------------------------------
 * Dispatch a command from the message queue block
 *
 * When done (or revoked/deleted) return the message, so that the sender knows
 * that the command has been dealt with (one way or another).
 */
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
{
  struct vty *vty;
  struct cq_command_args* args ;

  vty  = mqb_get_arg0(mqb);
  args = mqb_get_args(mqb) ;

  if (flag == mqb_action)
    {
      const char** argv = mqb_get_argv(mqb) ;

      args->ret = (args->cmd->func)(args->cmd, vty, args->argc, argv) ;
    }
  else
    args->ret = CMD_QUEUED ;

  mqb_set_action(mqb, cq_return) ;
  mqueue_enqueue(args->ret_nexus->queue, mqb, 0) ;
} ;

/*------------------------------------------------------------------------------
 * Accept return from command executed in another thread.
 *
 * The command line processing for the vty may be stalled (with read mode
 * disabled) waiting for the return from the command.
 *
 * If the message is being revoked/deleted the state of the vty is still
 * updated (to show that the command has completed) BUT nothing is kicked.
 * It is up to the revoke/delete function to deal with any possibility of the
 * vty remaining stalled.
 */
static void
cq_return(mqueue_block mqb, mqb_flag_t flag)
{
  struct vty *vty ;
  struct cq_command_args* args ;
  int    i ;
  void** argv ;
  struct cmd_parsed parsed ;

  vty  = mqb_get_arg0(mqb) ;
  args = mqb_get_args(mqb) ;

  /* clean up                                                           */
  argv = mqb_get_argv(mqb) ;

  for (i = 0; i < args->argc; ++i)
    XFREE(MTYPE_MARSHAL, argv[i]);

  /* signal end of command -- passing the action state                  */
  parsed.cmd         = args->cmd ;
  parsed.cnode       = args->cnode ;
  parsed.onode       = args->onode ;
  parsed.do_shortcut = args->do_shortcut ;
  parsed.argc        = 0 ;
  cmd_post_command(vty, &parsed, args->ret, (flag == mqb_action)) ;

  /* update the state of the vty -- passing the "action" state          */
  vty_queued_result(vty, args->ret, (flag == mqb_action));

  if (qpthreads_enabled)
    qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);

  mqb_free(mqb);
}