+// SPDX-License-Identifier: GPL-2.0-or-later
/* Virtual terminal interface shell.
* Copyright (C) 2000 Kunihiro Ishiguro
- *
- * 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 this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
/* Flag to indicate if in user/unprivileged mode. */
int user_mode;
-/* For sigsetjmp() & siglongjmp(). */
-static sigjmp_buf jmpbuf;
-
-/* Flag for avoid recursive siglongjmp() call. */
-static int jmpflag = 0;
-
-/* A static variable for holding the line. */
-static char *line_read;
-
/* Master of threads. */
struct thread_master *master;
/* Command logging */
FILE *logfile;
+static void vtysh_rl_callback(char *line_read)
+{
+ HIST_ENTRY *last;
+
+ rl_callback_handler_remove();
+
+ if (!line_read) {
+ vtysh_loop_exited = true;
+ return;
+ }
+
+ /* If the line has any text in it, save it on the history. But only if
+ * last command in history isn't the same one.
+ */
+ if (*line_read) {
+ using_history();
+ last = previous_history();
+ if (!last || strcmp(last->line, line_read) != 0) {
+ add_history(line_read);
+ append_history(1, history_file);
+ }
+ }
+
+ vtysh_execute(line_read);
+
+ if (!vtysh_loop_exited)
+ rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
+
+ free(line_read);
+}
+
/* SIGTSTP handler. This function care user's ^Z input. */
static void sigtstp(int sig)
{
+ rl_callback_handler_remove();
+
/* Execute "end" command. */
vtysh_execute("end");
+ if (!vtysh_loop_exited)
+ rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
+
/* Initialize readline. */
rl_initialize();
printf("\n");
-
- /* Check jmpflag for duplicate siglongjmp(). */
- if (!jmpflag)
- return;
-
- jmpflag = 0;
-
- /* Back to main command loop. */
- siglongjmp(jmpbuf, 1);
+ rl_forced_update_display();
}
/* SIGINT handler. This function care user's ^Z input. */
{"timestamp", no_argument, NULL, 't'},
{0}};
+bool vtysh_loop_exited;
+
+static struct thread *vtysh_rl_read_thread;
+
+static void vtysh_rl_read(struct thread *thread)
+{
+ thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO,
+ &vtysh_rl_read_thread);
+ rl_callback_read_char();
+}
+
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
-static char *vtysh_rl_gets(void)
+static void vtysh_rl_run(void)
{
- HIST_ENTRY *last;
- /* If the buffer has already been allocated, return the memory
- * to the free pool. */
- if (line_read) {
- free(line_read);
- line_read = NULL;
- }
+ struct thread thread;
- /* Get a line from the user. Change prompt according to node. XXX. */
- line_read = readline(vtysh_prompt());
+ master = thread_master_create(NULL);
- /* If the line has any text in it, save it on the history. But only if
- * last command in history isn't the same one. */
- if (line_read && *line_read) {
- using_history();
- last = previous_history();
- if (!last || strcmp(last->line, line_read) != 0) {
- add_history(line_read);
- append_history(1, history_file);
- }
- }
+ rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
+ thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO,
+ &vtysh_rl_read_thread);
+
+ while (!vtysh_loop_exited && thread_fetch(master, &thread))
+ thread_call(&thread);
- return (line_read);
+ if (!vtysh_loop_exited)
+ rl_callback_handler_remove();
+
+ thread_master_free(master);
}
static void log_it(const char *line)
const char *pathspace_arg = NULL;
char pathspace[MAXPATHLEN] = "";
const char *histfile = NULL;
+ const char *histfile_env = getenv("VTYSH_HISTFILE");
/* SUID: drop down to calling user & go back up when needed */
elevuid = geteuid();
}
/* Initialize user input buffer. */
- line_read = NULL;
setlinebuf(stdout);
/* Signal and others. */
* Setup history file for use by both -c and regular input
* If we can't find the home directory, then don't store
* the history information.
- * VTYSH_HISTFILE is prefered over command line
+ * VTYSH_HISTFILE is preferred over command line
* argument (-H/--histfile).
*/
- if (getenv("VTYSH_HISTFILE")) {
- const char *file = getenv("VTYSH_HISTFILE");
-
- strlcpy(history_file, file, sizeof(history_file));
+ if (histfile_env) {
+ strlcpy(history_file, histfile_env, sizeof(history_file));
} else if (histfile) {
strlcpy(history_file, histfile, sizeof(history_file));
} else {
vtysh_add_timestamp = ts_flag;
- /* Preparation for longjmp() in sigtstp(). */
- sigsetjmp(jmpbuf, 1);
- jmpflag = 1;
-
/* Main command loop. */
- while (vtysh_rl_gets())
- vtysh_execute(line_read);
+ vtysh_rl_run();
vtysh_uninit();