summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ngctl
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2006-08-07 14:17:05 +0000
committerglebius <glebius@FreeBSD.org>2006-08-07 14:17:05 +0000
commitb513ad862a94b8ce8b0c5db8bf4910aad6221e63 (patch)
tree2fd49f9603f6c85e9baca7a80426824c76d60251 /usr.sbin/ngctl
parent76b03d4afeb02ad9c42338912a9932e7e4b5e74a (diff)
downloadFreeBSD-src-b513ad862a94b8ce8b0c5db8bf4910aad6221e63.zip
FreeBSD-src-b513ad862a94b8ce8b0c5db8bf4910aad6221e63.tar.gz
Add line edit and history support to ngctl(8) via editline(3).
Details: - The main thread runs editline(3) functions, that can block. - A separate thread is launched to monitor netgraph sockets. - The access to the descriptors is protected by a mutex. At runtime the monitoring thread owns the mutex. When the main thread reads a command from el_gets() it asks the monitoring thread to release a mutex and sleep until the main thread processes the command. This makes ngctl(8) depend on libedit, and libpthread. Thus, the new functionality isn't compiled in if release is being built with -DRELEASE_CRUNCH. PR: bin/87352 Reviewed by: ru, Nuno Antunes <nuno.antunes gmail.com>
Diffstat (limited to 'usr.sbin/ngctl')
-rw-r--r--usr.sbin/ngctl/Makefile11
-rw-r--r--usr.sbin/ngctl/main.c179
2 files changed, 168 insertions, 22 deletions
diff --git a/usr.sbin/ngctl/Makefile b/usr.sbin/ngctl/Makefile
index 364bbf7..22122f8 100644
--- a/usr.sbin/ngctl/Makefile
+++ b/usr.sbin/ngctl/Makefile
@@ -6,7 +6,18 @@ MAN= ngctl.8
SRCS= main.c mkpeer.c config.c connect.c dot.c name.c show.c list.c \
msg.c debug.c shutdown.c rmhook.c status.c types.c write.c
WARNS?= 3
+
+.if defined(RELEASE_CRUNCH)
+NGCTL_NO_LIBEDIT=
+.endif
+
DPADD= ${LIBNETGRAPH}
LDADD= -lnetgraph
+.if !defined(NGCTL_NO_LIBEDIT)
+CFLAGS+= -DEDITLINE
+DPADD+= ${LIBPTHREAD} ${LIBEDIT} ${LIBTERMCAP}
+LDADD+= -pthread -ledit -ltermcap
+.endif
+
.include <bsd.prog.mk>
diff --git a/usr.sbin/ngctl/main.c b/usr.sbin/ngctl/main.c
index a09a0cf1..5a11899 100644
--- a/usr.sbin/ngctl/main.c
+++ b/usr.sbin/ngctl/main.c
@@ -51,6 +51,11 @@
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
+#ifdef EDITLINE
+#include <signal.h>
+#include <histedit.h>
+#include <pthread.h>
+#endif
#include <netgraph.h>
@@ -63,6 +68,7 @@
/* Internal functions */
static int ReadFile(FILE *fp);
+static void ReadSockets(fd_set);
static int DoParseCommand(char *line);
static int DoCommand(int ac, char **av);
static int DoInteractive(void);
@@ -72,6 +78,11 @@ static void Usage(const char *msg);
static int ReadCmd(int ac, char **av);
static int HelpCmd(int ac, char **av);
static int QuitCmd(int ac, char **av);
+#ifdef EDITLINE
+static sig_atomic_t unblock;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+#endif
/* List of commands */
static const struct ngcmd *const cmds[] = {
@@ -211,8 +222,122 @@ ReadFile(FILE *fp)
return (CMDRTN_OK);
}
+#ifdef EDITLINE
+/* Signal handler for Monitor() thread. */
+static void
+Unblock(int signal)
+{
+ unblock = 1;
+}
+
+/*
+ * Thread that monitors csock and dsock while main thread
+ * can be blocked in el_gets().
+ */
+static void *
+Monitor(void *v)
+{
+ struct sigaction act;
+ const int maxfd = MAX(csock, dsock) + 1;
+
+ act.sa_handler = Unblock;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGUSR1, &act, NULL);
+
+ pthread_mutex_lock(&mutex);
+ for (;;) {
+ fd_set rfds;
+
+ /* See if any data or control messages are arriving. */
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ unblock = 0;
+ if (select(maxfd, &rfds, NULL, NULL, NULL) <= 0) {
+ if (errno == EINTR) {
+ if (unblock == 1)
+ pthread_cond_wait(&cond, &mutex);
+ continue;
+ }
+ err(EX_OSERR, "select");
+ }
+
+ ReadSockets(rfds);
+ }
+
+ return (NULL);
+}
+
+static char *
+Prompt(EditLine *el)
+{
+ return PROMPT;
+}
+
/*
- * Interactive mode
+ * Here we start a thread, that will monitor the netgraph
+ * sockets and catch any unexpected messages or data on them,
+ * that can arrive while user edits his/her commands.
+ *
+ * Whenever we expect data on netgraph sockets, we send signal
+ * to monitoring thread. The signal forces it to exit select()
+ * system call and sleep on condvar until we wake it. While
+ * monitoring thread sleeps, we can do our work with netgraph
+ * sockets.
+ */
+static int
+DoInteractive(void)
+{
+ pthread_t monitor;
+ EditLine *el;
+ History *hist;
+ HistEvent hev = { 0, "" };
+
+ (*help_cmd.func)(0, NULL);
+ pthread_create(&monitor, NULL, Monitor, NULL);
+ el = el_init(getprogname(), stdin, stdout, stderr);
+ if (el == NULL)
+ return (CMDRTN_ERROR);
+ el_set(el, EL_PROMPT, Prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_EDITOR, "emacs");
+ hist = history_init();
+ if (hist == NULL)
+ return (CMDRTN_ERROR);
+ history(hist, &hev, H_SETSIZE, 100);
+ history(hist, &hev, H_SETUNIQUE, 1);
+ el_set(el, EL_HIST, history, (const char *)hist);
+ el_source(el, NULL);
+
+ for (;;) {
+ const char *buf;
+ int count;
+
+ if ((buf = el_gets(el, &count)) == NULL) {
+ printf("\n");
+ break;
+ }
+ history(hist, &hev, H_ENTER, buf);
+ pthread_kill(monitor, SIGUSR1);
+ pthread_mutex_lock(&mutex);
+ if (DoParseCommand((char *)buf) == CMDRTN_QUIT)
+ break;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+ history_end(hist);
+ el_end(el);
+ pthread_cancel(monitor);
+
+ return (CMDRTN_QUIT);
+}
+
+#else /* !EDITLINE */
+
+/*
+ * Interactive mode w/o libedit functionality.
*/
static int
DoInteractive(void)
@@ -246,27 +371,7 @@ DoInteractive(void)
printf("\n");
}
- /* Display any incoming control message */
- if (FD_ISSET(csock, &rfds))
- MsgRead();
-
- /* Display any incoming data packet */
- if (FD_ISSET(dsock, &rfds)) {
- u_char *buf;
- char hook[NG_HOOKSIZ];
- int rl;
-
- /* Read packet from socket */
- if ((rl = NgAllocRecvData(dsock, &buf, hook)) < 0)
- err(EX_OSERR, "reading hook \"%s\"", hook);
- if (rl == 0)
- errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
-
- /* Write packet to stdout */
- printf("Rec'd data packet on hook \"%s\":\n", hook);
- DumpAscii(buf, rl);
- free(buf);
- }
+ ReadSockets(rfds);
/* Get any user input */
if (FD_ISSET(0, &rfds)) {
@@ -282,6 +387,36 @@ DoInteractive(void)
}
return (CMDRTN_QUIT);
}
+#endif /* !EDITLINE */
+
+/*
+ * Read and process data on netgraph control and data sockets.
+ */
+static void
+ReadSockets(fd_set rfds)
+{
+ /* Display any incoming control message. */
+ if (FD_ISSET(csock, &rfds))
+ MsgRead();
+
+ /* Display any incoming data packet. */
+ if (FD_ISSET(dsock, &rfds)) {
+ char hook[NG_HOOKSIZ];
+ u_char *buf;
+ int rl;
+
+ /* Read packet from socket. */
+ if ((rl = NgAllocRecvData(dsock, &buf, hook)) < 0)
+ err(EX_OSERR, "reading hook \"%s\"", hook);
+ if (rl == 0)
+ errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
+
+ /* Write packet to stdout. */
+ printf("Rec'd data packet on hook \"%s\":\n", hook);
+ DumpAscii(buf, rl);
+ free(buf);
+ }
+}
/*
* Parse a command line and execute the command
OpenPOWER on IntegriCloud