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>
This commit is contained in:
Gleb Smirnoff 2006-08-07 14:17:05 +00:00
parent 42787b76b6
commit b9124a7354
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=161044
2 changed files with 168 additions and 22 deletions

View File

@ -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>

View File

@ -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;
}
/*
* Interactive mode
* 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;
}
/*
* 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