Error handling for CLI

Continue, do not exit on read/expand errors
Accept -1 error without clicon_err in callbacks / expand
C-API: Three-value return of clicon_cliread
This commit is contained in:
Olof hagsand 2022-11-19 09:22:44 +01:00
parent f82ce896a9
commit caef594dbe
4 changed files with 58 additions and 35 deletions

View file

@ -156,7 +156,6 @@ cli_history_save(clicon_handle h)
return retval; return retval;
} }
/*! Clean and close all state of cli process (but dont exit). /*! Clean and close all state of cli process (but dont exit).
* Cannot use h after this * Cannot use h after this
* @param[in] h Clixon handle * @param[in] h Clixon handle
@ -236,17 +235,22 @@ cli_interactive(clicon_handle h)
char *cmd; char *cmd;
char *new_mode; char *new_mode;
cligen_result result; cligen_result result;
int ret;
/* Loop through all commands */ /* Loop through all commands */
while(!cligen_exiting(cli_cligen(h))) { while(!cligen_exiting(cli_cligen(h))) {
new_mode = cli_syntax_mode(h); new_mode = cli_syntax_mode(h);
cmd = NULL; cmd = NULL;
if (clicon_cliread(h, &cmd) < 0) /* Read a CLI string, including expand handling */
if ((ret = clicon_cliread(h, &cmd)) < 0)
goto done; goto done;
if (ret == 0)
continue;
if (cmd == NULL) { /* EOF */ if (cmd == NULL) { /* EOF */
cligen_exiting_set(cli_cligen(h), 1); cligen_exiting_set(cli_cligen(h), 1);
continue; continue;
} }
/* Here errors are handled */
if (clicon_parse(h, cmd, &new_mode, &result, NULL) < 0) if (clicon_parse(h, cmd, &new_mode, &result, NULL) < 0)
goto done; goto done;
/* Why not check result? */ /* Why not check result? */
@ -815,7 +819,6 @@ main(int argc,
goto done; goto done;
} }
/* Go into event-loop unless -1 command-line */ /* Go into event-loop unless -1 command-line */
if (!once){ if (!once){
retval = cli_interactive(h); retval = cli_interactive(h);

View file

@ -516,15 +516,17 @@ cli_plugin_finish(clicon_handle h)
int int
cli_handler_err(FILE *f) cli_handler_err(FILE *f)
{ {
if (clicon_errno && if (clicon_errno){
(clicon_get_logflags() & CLICON_LOG_STDERR) == 0){ /* Check if error is already logged on stderr */
fprintf(f, "%s: %s", clicon_strerror(clicon_errno), clicon_err_reason); if ((clicon_get_logflags() & CLICON_LOG_STDERR) == 0){
if (clicon_suberrno) fprintf(f, "%s: %s", clicon_strerror(clicon_errno), clicon_err_reason);
fprintf(f, ": %s", strerror(clicon_suberrno)); if (clicon_suberrno)
fprintf(f, "\n"); fprintf(f, ": %s", strerror(clicon_suberrno));
fprintf(f, "\n");
}
else
fprintf(f, "CLI command error\n");
} }
else
fprintf(f, "CLI command error\n");
return 0; return 0;
} }
@ -596,7 +598,7 @@ clicon_parse(clicon_handle h,
switch (*result) { switch (*result) {
case CG_EOF: /* eof */ case CG_EOF: /* eof */
case CG_ERROR: case CG_ERROR:
fprintf(f, "CLI parse error: %s\n", cmd); fprintf(f, "CLI parse error: %s\n", cmd); // In practice never happens
break; break;
case CG_NOMATCH: /* no match */ case CG_NOMATCH: /* no match */
fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, reason); fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, reason);
@ -741,7 +743,8 @@ cli_prompt_get(clicon_handle h,
/*! Read command from CLIgen's cliread() using current syntax mode. /*! Read command from CLIgen's cliread() using current syntax mode.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[out] stringp Pointer to command buffer or NULL on EOF * @param[out] stringp Pointer to command buffer or NULL on EOF
* @retval 0 OK * @retval 1 OK
* @retval 0 Fail but continue
* @retval -1 Error * @retval -1 Error
*/ */
int int
@ -776,15 +779,22 @@ clicon_cliread(clicon_handle h,
} }
cligen_ph_active_set_byname(cli_cligen(h), mode->csm_name); cligen_ph_active_set_byname(cli_cligen(h), mode->csm_name);
clicon_err_reset();
if (cliread(cli_cligen(h), stringp) < 0){ if (cliread(cli_cligen(h), stringp) < 0){
clicon_err(OE_FATAL, errno, "CLIgen"); cli_handler_err(stdout);
goto done; if (clicon_suberrno == ESHUTDOWN)
goto done;
goto fail;
} }
retval = 0;
retval = 1;
done: done:
if (pfmt) if (pfmt)
free(pfmt); free(pfmt);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/* /*

View file

@ -101,7 +101,7 @@ extern char clicon_err_reason[ERR_STRLEN];
/* /*
* Macros * Macros
*/ */
#define clicon_err(e,s,_fmt, args...) clicon_err_fn(__FUNCTION__, __LINE__, (e), (s), _fmt , ##args) #define clicon_err(e,s,_fmt, args...) clicon_err_fn(__FUNCTION__, __LINE__, (e), (s), _fmt , ##args)
/* /*
* Prototypes * Prototypes

View file

@ -97,8 +97,8 @@ static clixon_err_cats *_err_cat_list = NULL;
/* /*
* Variables * Variables
*/ */
int clicon_errno = 0; /* See enum clicon_err XXX: hide this and change to err_category */ int clicon_errno = 0; /* See enum clicon_err XXX: hide this and change to err_category */
int clicon_suberrno = 0; /* Corresponds to errno.h XXX: change to errno */ int clicon_suberrno = 0; /* Corresponds to errno.h XXX: change to errno */
char clicon_err_reason[ERR_STRLEN] = {0, }; char clicon_err_reason[ERR_STRLEN] = {0, };
/* /*
@ -155,6 +155,10 @@ clicon_err_reset(void)
} }
/*! Find error category struct given name /*! Find error category struct given name
*
* @param[in] category Error category
* @retval cec error category callback structs
* @retval NULL Not found
*/ */
static struct clixon_err_cats * static struct clixon_err_cats *
find_category(int category) find_category(int category)
@ -181,6 +185,13 @@ find_category(int category)
* - Logs to syslog with LOG_ERR * - Logs to syslog with LOG_ERR
* - Set global error variable name clicon_errno * - Set global error variable name clicon_errno
* - Set global reason string clicon_err_reason * - Set global reason string clicon_err_reason
* Typically a call to clicon_err() is followed by a return -1 (or NULL) that signals
* a fatal error that fails early and loud.
* However there are some cases where such an error does not cause an exit. This includes
* CLI operations of callbacks and expand functions. The reason is that user-defined errors
* should just signal an error and not terminate. To override this one can set a suberr to
* ESHUTDOWN.
*
* @note: err direction (syslog and/or stderr) controlled by clicon_log_init() * @note: err direction (syslog and/or stderr) controlled by clicon_log_init()
* *
* @param[in] fn Inline function name (when called from clicon_err() macro) * @param[in] fn Inline function name (when called from clicon_err() macro)
@ -227,7 +238,6 @@ clicon_err_fn(const char *fn,
} }
va_end(args); va_end(args);
strncpy(clicon_err_reason, msg, ERR_STRLEN-1); strncpy(clicon_err_reason, msg, ERR_STRLEN-1);
/* Check category callbacks as defined in clixon_err_cat_reg */ /* Check category callbacks as defined in clixon_err_cat_reg */
if ((cec = find_category(category)) != NULL && if ((cec = find_category(category)) != NULL &&
cec->cec_logfn){ cec->cec_logfn){
@ -282,7 +292,7 @@ clicon_err_fn(const char *fn,
msg); msg);
} }
retval = 0; retval = 0;
done: done:
if (msg) if (msg)
free(msg); free(msg);
return retval; return retval;