Commit e2d196dd authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Take another trip over the CLI code, this time the help function.



git-svn-id: http://www.varnish-cache.org/svn/trunk@4477 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 83c5fee7
......@@ -487,8 +487,7 @@ void CNT_Init(void);
/* cache_cli.c [CLI] */
void CLI_Init(void);
void CLI_Run(void);
enum cli_set_e {MASTER_CLI, PUBLIC_CLI, DEBUG_CLI};
void CLI_AddFuncs(enum cli_set_e which, struct cli_proto *p);
void CLI_AddFuncs(struct cli_proto *p);
extern pthread_t cli_thread;
#define ASSERT_CLI() do {assert(pthread_self() == cli_thread);} while (0)
......
......@@ -375,15 +375,11 @@ ccf_listen_address(struct cli *cli, const char * const *av, void *priv)
/*--------------------------------------------------------------------*/
static struct cli_proto vca_cmds[] = {
{ CLI_SERVER_START, ccf_start },
{ NULL }
};
static struct cli_proto vca_debug_cmds[] = {
{ CLI_SERVER_START, "i", ccf_start },
{ "debug.listen_address",
"debug.listen_address",
"Report the actual listen address\n", 0, 0,
ccf_listen_address, NULL },
"d", ccf_listen_address, NULL },
{ NULL }
};
......@@ -391,8 +387,7 @@ void
VCA_Init(void)
{
CLI_AddFuncs(MASTER_CLI, vca_cmds);
CLI_AddFuncs(DEBUG_CLI, vca_debug_cmds);
CLI_AddFuncs(vca_cmds);
}
void
......
......@@ -311,7 +311,7 @@ cli_debug_backend(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto debug_cmds[] = {
{ "debug.backend", "debug.backend",
"\tExamine Backend internals\n", 0, 0, cli_debug_backend },
"\tExamine Backend internals\n", 0, 0, "d", cli_debug_backend },
{ NULL }
};
......@@ -322,5 +322,5 @@ VBE_Init(void)
{
Lck_New(&VBE_mtx);
CLI_AddFuncs(DEBUG_CLI, debug_cmds);
CLI_AddFuncs(debug_cmds);
}
......@@ -409,7 +409,7 @@ vbp_health(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto debug_cmds[] = {
{ "debug.health", "debug.health",
"\tDump backend health stuff\n",
0, 0, vbp_health },
0, 0, "d", vbp_health },
{ NULL }
};
......@@ -494,5 +494,5 @@ void
VBP_Init(void)
{
CLI_AddFuncs(DEBUG_CLI, debug_cmds);
CLI_AddFuncs(debug_cmds);
}
......@@ -794,13 +794,13 @@ static struct cli_proto ban_cmds[] = {
* XXX: COMPAT: Retain these two entries for entire 2.x series
* XXX: COMPAT: to stay compatible with 1.x series syntax.
*/
{ CLI_HIDDEN("url.purge", 1, 1) ccf_purge_url },
{ CLI_HIDDEN("hash.purge", 0, 1) ccf_purge_hash },
{ CLI_HIDDEN("purge.hash", 0, 1) ccf_purge_hash },
{ CLI_HIDDEN("url.purge", 1, 1) "h", ccf_purge_url },
{ CLI_HIDDEN("hash.purge", 0, 1) "h", ccf_purge_hash },
{ CLI_HIDDEN("purge.hash", 0, 1) "h", ccf_purge_hash },
{ CLI_PURGE_URL, ccf_purge_url },
{ CLI_PURGE, ccf_purge },
{ CLI_PURGE_LIST, ccf_purge_list },
{ CLI_PURGE_URL, "", ccf_purge_url },
{ CLI_PURGE, "", ccf_purge },
{ CLI_PURGE_LIST, "", ccf_purge_list },
{ NULL }
};
......@@ -809,7 +809,7 @@ BAN_Init(void)
{
Lck_New(&ban_mtx);
CLI_AddFuncs(PUBLIC_CLI, ban_cmds);
CLI_AddFuncs(ban_cmds);
ban_magic = BAN_New();
AN(ban_magic);
......
......@@ -1278,9 +1278,9 @@ cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto debug_cmds[] = {
{ "debug.xid", "debug.xid",
"\tExamine or set XID\n", 0, 1, cli_debug_xid },
"\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
{ "debug.srandom", "debug.srandom",
"\tSeed the random(3) function\n", 0, 1, cli_debug_srandom },
"\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
{ NULL }
};
......@@ -1294,7 +1294,7 @@ CNT_Init(void)
srandomdev();
xids = random();
CLI_AddFuncs(DEBUG_CLI, debug_cmds);
CLI_AddFuncs(debug_cmds);
}
......@@ -58,6 +58,7 @@ SVNID("$Id$")
pthread_t cli_thread;
static struct lock cli_mtx;
static int add_check;
static struct cls *cls;
/*
* The CLI commandlist is split in three:
......@@ -67,29 +68,17 @@ static int add_check;
* - Undocumented debug commands, show in undocumented "help -d"
*/
static struct cli_proto *ccf_master_cli, *ccf_public_cli, *ccf_debug_cli;
/*--------------------------------------------------------------------
* Add CLI functions to the appropriate command set
*/
void
CLI_AddFuncs(enum cli_set_e which, struct cli_proto *p)
CLI_AddFuncs(struct cli_proto *p)
{
struct cli_proto *c, **cp;
AZ(add_check);
switch (which) {
case MASTER_CLI: cp = &ccf_master_cli; break;
case PUBLIC_CLI: cp = &ccf_public_cli; break;
case DEBUG_CLI: cp = &ccf_debug_cli; break;
default: INCOMPL();
}
Lck_Lock(&cli_mtx);
c = cli_concat(*cp, p);
AN(c);
free(*cp);
*cp = c;
AZ(CLS_AddFunc(cls, 0, p));
Lck_Unlock(&cli_mtx);
}
......@@ -113,17 +102,11 @@ cli_cb_after(const struct cli *cli)
void
CLI_Run(void)
{
struct cls *cls;
int i;
add_check = 1;
cls = CLS_New(cli_cb_before, cli_cb_after, params->cli_buffer);
AN(cls);
AN(CLS_AddFd(cls, heritage.cli_in, heritage.cli_out, NULL, NULL));
AZ(CLS_AddFunc(cls, 0, ccf_master_cli));
AZ(CLS_AddFunc(cls, 0, ccf_public_cli));
AZ(CLS_AddFunc(cls, 0, ccf_debug_cli));
do {
i = CLS_Poll(cls, -1);
......@@ -159,27 +142,6 @@ cli_debug_sizeof(struct cli *cli, const char * const *av, void *priv)
/*--------------------------------------------------------------------*/
static void
ccf_help(struct cli *cli, const char * const *av, void *priv)
{
(void)priv;
cli_func_help(cli, av, ccf_public_cli);
if (av[2] != NULL && !strcmp(av[2], "-d")) {
/* Also list undocumented commands */
cli_out(cli, "\nDebugging commands:\n");
cli_func_help(cli, av, ccf_debug_cli);
} else if (cli->result == CLIS_UNKNOWN) {
/* Otherwise, try the undocumented list */
vsb_clear(cli->sb);
cli->result = CLIS_OK;
cli_func_help(cli, av, ccf_debug_cli);
}
}
/*--------------------------------------------------------------------*/
static void
ccf_panic(struct cli *cli, const char * const *av, void *priv)
{
......@@ -193,18 +155,14 @@ ccf_panic(struct cli *cli, const char * const *av, void *priv)
/*--------------------------------------------------------------------*/
static struct cli_proto master_cmds[] = {
{ CLI_PING, cli_func_ping },
{ CLI_HELP, ccf_help, NULL },
{ NULL }
};
static struct cli_proto debug_cmds[] = {
{ CLI_PING, "i", CLS_func_ping },
{ CLI_HELP, "i", CLS_func_help },
{ "debug.sizeof", "debug.sizeof",
"\tDump sizeof various data structures\n",
0, 0, cli_debug_sizeof },
0, 0, "d", cli_debug_sizeof },
{ "debug.panic.worker", "debug.panic.worker",
"\tPanic the worker process.\n",
0, 0, ccf_panic },
0, 0, "d", ccf_panic },
{ NULL }
};
......@@ -220,7 +178,9 @@ CLI_Init(void)
Lck_New(&cli_mtx);
cli_thread = pthread_self();
CLI_AddFuncs(MASTER_CLI, master_cmds);
CLI_AddFuncs(DEBUG_CLI, debug_cmds);
cls = CLS_New(cli_cb_before, cli_cb_after, params->cli_buffer);
AN(cls);
CLI_AddFuncs(master_cmds);
}
......@@ -544,7 +544,7 @@ debug_fragfetch(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto debug_cmds[] = {
{ "debug.fragfetch", "debug.fragfetch",
"\tEnable fetch fragmentation\n", 1, 1, debug_fragfetch },
"\tEnable fetch fragmentation\n", 1, 1, "d", debug_fragfetch },
{ NULL }
};
......@@ -556,5 +556,5 @@ void
Fetch_Init(void)
{
CLI_AddFuncs(DEBUG_CLI, debug_cmds);
CLI_AddFuncs(debug_cmds);
}
......@@ -328,10 +328,10 @@ VCL_##func##_method(struct sess *sp) \
/*--------------------------------------------------------------------*/
static struct cli_proto vcl_cmds[] = {
{ CLI_VCL_LOAD, ccf_config_load },
{ CLI_VCL_LIST, ccf_config_list },
{ CLI_VCL_DISCARD, ccf_config_discard },
{ CLI_VCL_USE, ccf_config_use },
{ CLI_VCL_LOAD, "i", ccf_config_load },
{ CLI_VCL_LIST, "i", ccf_config_list },
{ CLI_VCL_DISCARD, "i", ccf_config_discard },
{ CLI_VCL_USE, "i", ccf_config_use },
{ NULL }
};
......@@ -339,6 +339,6 @@ void
VCL_Init()
{
CLI_AddFuncs(MASTER_CLI, vcl_cmds);
CLI_AddFuncs(vcl_cmds);
Lck_New(&vcl_mtx);
}
......@@ -333,7 +333,7 @@ hcb_dump(struct cli *cli, const char * const *av, void *priv)
}
static struct cli_proto hcb_cmds[] = {
{ "hcb.dump", "hcb.dump", "dump HCB tree\n", 0, 0, hcb_dump },
{ "hcb.dump", "hcb.dump", "dump HCB tree\n", 0, 0, "d", hcb_dump },
{ NULL }
};
......@@ -384,7 +384,7 @@ hcb_start(void)
pthread_t tp;
(void)oh;
CLI_AddFuncs(DEBUG_CLI, hcb_cmds);
CLI_AddFuncs(hcb_cmds);
AZ(pthread_create(&tp, NULL, hcb_cleaner, NULL));
assert(sizeof(struct hcb_y) <= sizeof(oh->u));
memset(&hcb_root, 0, sizeof hcb_root);
......
......@@ -72,9 +72,6 @@ static const char *secret_file;
#define MCF_NOAUTH 0
#define MCF_AUTH 16
static void mcf_help(struct cli *cli, const char * const *av, void *priv);
/*--------------------------------------------------------------------*/
static void
......@@ -92,19 +89,6 @@ mcf_stats(struct cli *cli, const char * const *av, void *priv)
#undef MAC_STAT
}
/*--------------------------------------------------------------------*/
static void
mcf_close(struct cli *cli, const char *const *av, void *priv)
{
(void)av;
(void)priv;
cli_out(cli, "Closing CLI connection");
cli_result(cli, CLIS_CLOSE);
}
/*--------------------------------------------------------------------*/
static void
......@@ -127,22 +111,19 @@ mcf_banner(struct cli *cli, const char *const *av, void *priv)
/* XXX: what order should this list be in ? */
static struct cli_proto cli_proto[] = {
{ CLI_BANNER, mcf_banner, NULL },
{ CLI_PING, cli_func_ping },
{ CLI_SERVER_STATUS, mcf_server_status, NULL },
{ CLI_SERVER_START, mcf_server_startstop, NULL },
{ CLI_SERVER_STOP, mcf_server_startstop, cli_proto },
{ CLI_STATS, mcf_stats, NULL },
{ CLI_VCL_LOAD, mcf_config_load, NULL },
{ CLI_VCL_INLINE, mcf_config_inline, NULL },
{ CLI_VCL_USE, mcf_config_use, NULL },
{ CLI_VCL_DISCARD, mcf_config_discard, NULL },
{ CLI_VCL_LIST, mcf_config_list, NULL },
{ CLI_VCL_SHOW, mcf_config_show, NULL },
{ CLI_PARAM_SHOW, mcf_param_show, NULL },
{ CLI_PARAM_SET, mcf_param_set, NULL },
{ CLI_QUIT, mcf_close, NULL},
{ CLI_BANNER, "", mcf_banner, NULL },
{ CLI_SERVER_STATUS, "", mcf_server_status, NULL },
{ CLI_SERVER_START, "", mcf_server_startstop, NULL },
{ CLI_SERVER_STOP, "", mcf_server_startstop, cli_proto },
{ CLI_STATS, "", mcf_stats, NULL },
{ CLI_VCL_LOAD, "", mcf_config_load, NULL },
{ CLI_VCL_INLINE, "", mcf_config_inline, NULL },
{ CLI_VCL_USE, "", mcf_config_use, NULL },
{ CLI_VCL_DISCARD, "", mcf_config_discard, NULL },
{ CLI_VCL_LIST, "", mcf_config_list, NULL },
{ CLI_VCL_SHOW, "", mcf_config_show, NULL },
{ CLI_PARAM_SHOW, "", mcf_param_show, NULL },
{ CLI_PARAM_SET, "", mcf_param_set, NULL },
{ NULL }
};
......@@ -161,7 +142,7 @@ mcf_panic(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto cli_debug[] = {
{ "debug.panic.master", "debug.panic.master",
"\tPanic the master process.\n",
0, 0, mcf_panic, NULL},
0, 0, "d", mcf_panic, NULL},
{ NULL }
};
......@@ -181,6 +162,10 @@ mcf_askchild(struct cli *cli, const char * const *av, void *priv)
* running.
*/
if (cli_o <= 0) {
if (!strcmp(av[1], "help")) {
cli_out(cli, "No help from child, (not running).\n");
return;
}
cli_result(cli, CLIS_UNKNOWN);
cli_out(cli,
"Unknown request in manager process "
......@@ -202,7 +187,7 @@ mcf_askchild(struct cli *cli, const char * const *av, void *priv)
static struct cli_proto cli_askchild[] = {
{ "*", "<wild-card-entry>", "\t<fall through to cacher>\n",
0, 9999, mcf_askchild, NULL},
0, 9999, "h*", mcf_askchild, NULL},
{ NULL }
};
......@@ -348,37 +333,13 @@ mcf_auth(struct cli *cli, const char *const *av, void *priv)
}
static struct cli_proto cli_auth[] = {
{ CLI_HELP, mcf_help, cli_auth },
{ CLI_AUTH, mcf_auth, NULL },
{ CLI_QUIT, mcf_close, NULL},
{ CLI_HELP, "", CLS_func_help, NULL },
{ CLI_PING, "", CLS_func_ping },
{ CLI_AUTH, "", mcf_auth, NULL },
{ CLI_QUIT, "", CLS_func_close, NULL},
{ NULL }
};
/*--------------------------------------------------------------------*/
static void
mcf_help(struct cli *cli, const char * const *av, void *priv)
{
unsigned u;
char *p;
(void)priv;
cli_func_help(cli, av, cli_auth);
if (cli->auth == MCF_NOAUTH)
return;
cli_func_help(cli, av, cli_proto);
if (cli_o >= 0 && (av[2] == NULL || *av[2] == '-')) {
p = NULL;
if (!mgt_cli_askchild(&u, &p,
"help %s\n", av[2] != NULL ? av[2] : "")) {
cli_out(cli, "%s", p);
cli_result(cli, u);
}
free(p);
}
}
/*--------------------------------------------------------------------*/
static void
mgt_cli_cb_before(const struct cli *cli)
......
......@@ -30,6 +30,7 @@
*/
struct vlu;
struct cls;
struct cli {
unsigned magic;
......@@ -41,6 +42,7 @@ struct cli {
char challenge[34];
char *ident;
struct vlu *vlu;
struct cls *cls;
};
int cli_writeres(int fd, const struct cli *cli);
......
......@@ -46,6 +46,7 @@ struct cli_proto {
const char *help;
unsigned minarg;
unsigned maxarg;
char flags[4];
/* Dispatch information */
cli_func_t *func;
......@@ -56,8 +57,3 @@ struct cli_proto {
void cli_out(struct cli *cli, const char *fmt, ...);
void cli_quote(struct cli *cli, const char *str);
void cli_result(struct cli *cli, unsigned r);
/* From libvarnish/cli.c */
cli_func_t cli_func_help;
cli_func_t cli_func_ping;
struct cli_proto *cli_concat(struct cli_proto *, struct cli_proto *);
......@@ -40,3 +40,8 @@ int CLS_Poll(struct cls *cs, int timeout);
int CLS_PollFd(struct cls *cs, int fd, int timeout);
void CLS_Destroy(struct cls **);
/* From libvarnish/cli.c */
cli_func_t CLS_func_close;
cli_func_t CLS_func_help;
cli_func_t CLS_func_ping;
......@@ -11,7 +11,6 @@ libvarnish_la_SOURCES = \
assert.c \
binary_heap.c \
subproc.c \
cli.c \
cli_common.c \
cli_serve.c \
flopen.c \
......
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2009 Linpro AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Stuff for handling the CLI protocol
*/
#include "config.h"
#include "svnid.h"
SVNID("$Id$")
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <cli.h>
#include <cli_priv.h>
#include <libvarnish.h>
/*
* Generic help function.
*
* priv must point to cli_proto array
*/
void
cli_func_help(struct cli *cli, const char * const *av, void *priv)
{
struct cli_proto *cp;
if (av[2] == NULL || *av[2] == '-') {
for (cp = priv; cp->request != NULL; cp++)
if (cp->syntax != NULL)
cli_out(cli, "%s\n", cp->syntax);
return;
}
for (cp = priv; cp->request != NULL; cp++) {
if (cp->syntax == NULL)
continue;
if (!strcmp(cp->request, av[2])) {
cli_out(cli, "%s\n%s\n", cp->syntax, cp->help);
return;
}
}
cli_out(cli, "Unknown request.\nType 'help' for more info.\n");
cli_result(cli, CLIS_UNKNOWN);
}
struct cli_proto *
cli_concat(struct cli_proto *c1, struct cli_proto *c2)
{
struct cli_proto *c;
int i1, i2;
i1 = 0;
for(c = c1; c != NULL && c->request != NULL; c++)
i1++;
i2 = 0;
for(c = c2; c != NULL && c->request != NULL; c++)
i2++;
c = malloc(sizeof(*c) * (1L + i1 + i2));
if (c == NULL)
return (c);
if (c1 != NULL)
memcpy(c, c1, sizeof(*c1) * i1);
if (c2 != NULL)
memcpy(c + i1, c2, sizeof(*c2) * i2);
memset(c + i1 + i2, 0, sizeof(*c));
return (c);
}
......@@ -184,16 +184,3 @@ cli_readres(int fd, unsigned *status, char **ptr, double tmo)
*ptr = p;
return (0);
}
/*--------------------------------------------------------------------*/
void
cli_func_ping(struct cli *cli, const char * const *av, void *priv)
{
time_t t;
(void)priv;
(void)av;
t = time(NULL);
cli_out(cli, "PONG %ld 1.0", t);
}
......@@ -80,6 +80,107 @@ struct cls {
unsigned maxlen;
};
/*--------------------------------------------------------------------*/
void
CLS_func_close(struct cli *cli, const char *const *av, void *priv)
{
(void)av;
(void)priv;
cli_out(cli, "Closing CLI connection");
cli_result(cli, CLIS_CLOSE);
}
/*--------------------------------------------------------------------*/
void
CLS_func_ping(struct cli *cli, const char * const *av, void *priv)
{
time_t t;
(void)priv;
(void)av;
t = time(NULL);
cli_out(cli, "PONG %ld 1.0", t);
}
/*--------------------------------------------------------------------*/
void
CLS_func_help(struct cli *cli, const char * const *av, void *priv)
{
struct cli_proto *cp;
struct cls_func *cfn;
unsigned all, debug, u, d, h, i, wc;
struct cls *cs;
(void)priv;
cs = cli->cls;
CHECK_OBJ_NOTNULL(cs, CLS_MAGIC);
if (av[2] == NULL) {
all = debug = 0;
} else if (!strcmp(av[2], "-a")) {
all = 1;
debug = 0;
} else if (!strcmp(av[2], "-d")) {
all = 0;
debug = 1;
} else {
VTAILQ_FOREACH(cfn, &cs->funcs, list) {
for (cp = cfn->clp; cp->request != NULL; cp++) {
if (!strcmp(cp->request, av[2])) {
cli_out(cli, "%s\n%s\n",
cp->syntax, cp->help);
return;
}
for (u = 0; u < sizeof cp->flags; u++) {
if (cp->flags[u] == '*') {
cp->func(cli,av,priv);
return;
}
}
}
}
cli_out(cli, "Unknown request.\nType 'help' for more info.\n");
cli_result(cli, CLIS_UNKNOWN);
return;
}
VTAILQ_FOREACH(cfn, &cs->funcs, list) {
for (cp = cfn->clp; cp->request != NULL; cp++) {
d = 0;
h = 0;
i = 0;
wc = 0;
for (u = 0; u < sizeof cp->flags; u++) {
if (cp->flags[u] == '\0')
continue;
if (cp->flags[u] == 'd')
d = 1;
if (cp->flags[u] == 'h')
h = 1;
if (cp->flags[u] == 'i')
i = 1;
if (cp->flags[u] == '*')
wc = 1;
}
if (i)
continue;
if (wc) {
cp->func(cli, av, priv);
continue;
}
if (debug != d)
continue;
if (h && !all)
continue;
if (cp->syntax != NULL)
cli_out(cli, "%s\n", cp->syntax);
}
}
}
/*--------------------------------------------------------------------
* Look for a CLI command to execute
*/
......@@ -156,6 +257,7 @@ cls_vlu(void *priv, const char *p)
return (0);
cli->cmd = p;
cli->cls = cs;
av = ParseArgv(p, 0);
AN(av);
......@@ -201,6 +303,7 @@ cls_vlu(void *priv, const char *p)
cs->after(cli);
cli->cmd = NULL;
cli->cls = NULL;
FreeArgv(av);
if (cli_writeres(cfd->fdo, cli) || cli->result == CLIS_CLOSE)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment