diff options
| author | cvs2svn <cvs2svn@FreeBSD.org> | 2005-04-20 19:11:07 +0000 |
|---|---|---|
| committer | cvs2svn <cvs2svn@FreeBSD.org> | 2005-04-20 19:11:07 +0000 |
| commit | 79c906b55063e4f0f0d3281363970da4e619cf56 (patch) | |
| tree | 09cc3ca07e7d7250dd9d9d550c76eacaf462ca91 /usr.sbin/pppctl | |
| parent | 7b100a2efc2b0855b788d000f7b2215fb73a5221 (diff) | |
| parent | 1e4e7f70c04a80eee7cfe0ecc6787272a43ba8bc (diff) | |
Notes
Diffstat (limited to 'usr.sbin/pppctl')
| -rw-r--r-- | usr.sbin/pppctl/Makefile | 9 | ||||
| -rw-r--r-- | usr.sbin/pppctl/pppctl.8 | 232 | ||||
| -rw-r--r-- | usr.sbin/pppctl/pppctl.c | 680 |
3 files changed, 921 insertions, 0 deletions
diff --git a/usr.sbin/pppctl/Makefile b/usr.sbin/pppctl/Makefile new file mode 100644 index 0000000000000..6cdcc3dc6cfb2 --- /dev/null +++ b/usr.sbin/pppctl/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PROG= pppctl +MAN= pppctl.8 + +DPADD= ${LIBPTHREAD} ${LIBEDIT} ${LIBTERMCAP} +LDADD= -lpthread -ledit -ltermcap + +.include <bsd.prog.mk> diff --git a/usr.sbin/pppctl/pppctl.8 b/usr.sbin/pppctl/pppctl.8 new file mode 100644 index 0000000000000..3d807dc0ed3c5 --- /dev/null +++ b/usr.sbin/pppctl/pppctl.8 @@ -0,0 +1,232 @@ +.\" $FreeBSD$ +.Dd June 26, 1997 +.Os +.Dt PPPCTL 8 +.Sh NAME +.Nm pppctl +.Nd PPP control program +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl t Ar n +.Op Fl p Ar passwd +.Xo Oo Ar host : Oc Ns +.Ar Port | LocalSocket +.Xc +.Oo +.Sm off +.Ar command Oo ; Ar command Oc Ar ... +.Sm on +.Oc +.Sh DESCRIPTION +This utility provides command line control of the +.Xr ppp 8 +daemon. +Its primary use is to facilitate simple scripts that +control a running daemon. +.Pp +The +.Nm +utility is passed at least one argument, specifying the socket on which +.Nm ppp +is listening. +Refer to the +.Sq set server +command of +.Nm ppp +for details. +If the socket contains a leading '/', it +is taken as an +.Dv AF_LOCAL +socket. +If it contains a colon, it is treated as a +.Ar host : Ns Ar port +pair, otherwise it is treated as a TCP port specification on the +local machine (127.0.0.1). +Both the +.Ar host +and +.Ar port +may be specified numerically if you wish to avoid a DNS lookup +or don't have an entry for the given port in +.Pa /etc/services . +.Pp +All remaining arguments are concatenated to form the +.Ar command Ns (s) +that will be sent to the +.Nm ppp +daemon. +If any semi-colon characters are found, they are treated as +.Ar command +delimiters, allowing more than one +.Ar command +in a given +.Sq session . +For example: +.Bd -literal -offset indent +pppctl 3000 set timeout 300\\; show timeout +.Ed +.Pp +Don't forget to escape or quote the ';' as it is a special character +for most shells. +.Pp +If no +.Ar command +arguments are given, +.Nm +enters interactive mode, where commands are read from standard input. +When reading commands, the +.Xr editline 3 +library is used, allowing command-line editing (with +.Xr editrc 5 +defining editing behaviour). +The history size +defaults to +.Em 20 lines . +.Pp +The following command line options are available: +.Bl -tag -width Ds +.It Fl v +Display all data sent to and received from the +.Nm ppp +daemon. +Normally, +.Nm +displays only non-prompt lines received. +This option is ignored in +interactive mode. +.It Fl t Ar n +Use a timeout of +.Ar n +instead of the default 2 seconds when connecting. +This may be required +if you wish to control a daemon over a slow (or even a dialup) link. +.It Fl p Ar passwd +Specify the password required by the +.Nm ppp +daemon. +If this switch is not used, +.Nm +will prompt for a password once it has successfully connected to +.Nm ppp . +.El +.Sh EXAMPLES +If you run +.Nm ppp +in +.Fl auto +mode, +.Nm +can be used to automate many frequent tasks (you can actually control +.Nm ppp +in any mode except interactive mode). +Use of the +.Fl p +option is discouraged (even in scripts that aren't readable by others) +as a +.Xr ps 1 +listing may reveal your secret. +.Pp +The best way to allow easy, secure +.Nm +access is to create a local server socket in +.Pa /etc/ppp/ppp.conf +(in the correct section) like this: +.Bd -literal -offset indent +set server /var/run/internet "" 0177 +.Ed +.Pp +This will instruct +.Nm ppp +to create a local domain socket, with srw------- permissions and no +password, allowing access only to the user that invoked +.Nm ppp . +Refer to the +.Xr ppp 8 +man page for further details. +.Pp +You can now create some easy-access scripts. +To connect to the internet: +.Bd -literal -offset indent +#! /bin/sh +test $# -eq 0 && time=300 || time=$1 +exec pppctl /var/run/internet set timeout $time\\; dial +.Ed +.Pp +To disconnect: +.Bd -literal -offset indent +#! /bin/sh +exec pppctl /var/run/internet set timeout 300\\; close +.Ed +.Pp +To check if the line is up: +.Bd -literal -offset indent +#! /bin/sh +pppctl -p '' -v /var/run/internet quit | grep ^PPP >/dev/null +if [ $? -eq 0 ]; then + echo Link is up +else + echo Link is down +fi +.Ed +.Pp +You can even make a generic script: +.Bd -literal -offset indent +#! /bin/sh +exec pppctl /var/run/internet "$@" +.Ed +.Pp +You could also use +.Nm +to control when dial-on-demand works. +Suppose you want +.Nm ppp +to run all the time, but you want to prevent dial-out between 8pm and 8am +each day. +However, any connections active at 8pm should continue to remain +active until they are closed or naturally time out. +.Pp +A +.Xr cron 8 +entry for 8pm which runs +.Bd -literal -offset indent +pppctl /var/run/internet set filter dial 0 deny 0 0 +.Ed +.Pp +will block all further dial requests, and the corresponding 8am entry +.Bd -literal -offset indent +pppctl /var/run/internet set filter dial -1 +.Ed +.Pp +will allow them again. +.Sh ENVIRONMENT +The following environment variables are understood by +.Nm +when in interactive mode: +.Bl -tag -width XXXXXXXXXX +.It Dv EL_SIZE +The number of history lines. +The default is 20. +.It Dv EL_EDITOR +The edit mode. +Only values of "emacs" and "vi" are accepted. +Other values +are silently ignored. +This environment variable will override the +.Ar bind -v +and +.Ar bind -e +commands in +.Pa ~/.editrc . +.El +.Sh SEE ALSO +.Xr ps 1 , +.Xr editline 3 , +.Xr editrc 5 , +.Xr services 5 , +.Xr ppp 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 2.2.5 . diff --git a/usr.sbin/pppctl/pppctl.c b/usr.sbin/pppctl/pppctl.c new file mode 100644 index 0000000000000..d8e7b42ca7fa7 --- /dev/null +++ b/usr.sbin/pppctl/pppctl.c @@ -0,0 +1,680 @@ +/*- + * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> + * All rights reserved. + * + * 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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <netdb.h> + +#include <sys/time.h> +#include <err.h> +#include <errno.h> +#include <histedit.h> +#include <semaphore.h> +#include <pthread.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#define LINELEN 2048 + +/* Data passed to the threads we create */ +struct thread_data { + EditLine *edit; /* libedit stuff */ + History *hist; /* libedit stuff */ + pthread_t trm; /* Terminal thread (for pthread_kill()) */ + int ppp; /* ppp descriptor */ +}; + +/* Flags passed to Receive() */ +#define REC_PASSWD (1) /* Handle a password request from ppp */ +#define REC_SHOW (2) /* Show everything except prompts */ +#define REC_VERBOSE (4) /* Show everything */ + +static char *passwd; +static char *prompt; /* Tell libedit what the current prompt is */ +static int data = -1; /* setjmp() has been done when data != -1 */ +static jmp_buf pppdead; /* Jump the Terminal thread out of el_gets() */ +static int timetogo; /* Tell the Monitor thread to exit */ +static sem_t sem_select; /* select() co-ordination between threads */ +static int TimedOut; /* Set if our connect() timed out */ +static int want_sem_post; /* Need to let the Monitor thread in ? */ + +/* + * How to use pppctl... + */ +static int +usage() +{ + fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] " + "Port|LocalSock [command[;command]...]\n"); + fprintf(stderr, " -v tells pppctl to output all" + " conversation\n"); + fprintf(stderr, " -t n specifies a timeout of n" + " seconds when connecting (default 2)\n"); + fprintf(stderr, " -p passwd specifies your password\n"); + exit(1); +} + +/* + * Handle the SIGALRM received due to a connect() timeout. + */ +static void +Timeout(int Sig) +{ + TimedOut = 1; +} + +/* + * A callback routine for libedit to find out what the current prompt is. + * All the work is done in Receive() below. + */ +static char * +GetPrompt(EditLine *e) +{ + if (prompt == NULL) + prompt = ""; + return prompt; +} + +/* + * Receive data from the ppp descriptor. + * We also handle password prompts here (if asked via the `display' arg) + * and buffer what our prompt looks like (via the `prompt' global). + */ +static int +Receive(int fd, int display) +{ + static char Buffer[LINELEN]; + struct timeval t; + int Result; + char *last; + fd_set f; + int len; + int err; + + FD_ZERO(&f); + FD_SET(fd, &f); + t.tv_sec = 0; + t.tv_usec = 100000; + prompt = Buffer; + len = 0; + + while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) { + if (Result == 0) { + Result = -1; + break; + } + len += Result; + Buffer[len] = '\0'; + if (len > 2 && !strcmp(Buffer+len-2, "> ")) { + prompt = strrchr(Buffer, '\n'); + if (display & (REC_SHOW|REC_VERBOSE)) { + if (display & REC_VERBOSE) + last = Buffer+len-1; + else + last = prompt; + if (last) { + last++; + write(STDOUT_FILENO, Buffer, last-Buffer); + } + } + prompt = prompt == NULL ? Buffer : prompt+1; + for (last = Buffer+len-2; last > Buffer && *last != ' '; last--) + ; + if (last > Buffer+3 && !strncmp(last-3, " on", 3)) { + /* a password is required ! */ + if (display & REC_PASSWD) { + /* password time */ + if (!passwd) + passwd = getpass("Password: "); + sprintf(Buffer, "passwd %s\n", passwd); + memset(passwd, '\0', strlen(passwd)); + if (display & REC_VERBOSE) + write(STDOUT_FILENO, Buffer, strlen(Buffer)); + write(fd, Buffer, strlen(Buffer)); + memset(Buffer, '\0', strlen(Buffer)); + return Receive(fd, display & ~REC_PASSWD); + } + Result = 1; + } else + Result = 0; + break; + } else + prompt = ""; + if (len == sizeof Buffer - 1) { + int flush; + if ((last = strrchr(Buffer, '\n')) == NULL) + /* Yeuch - this is one mother of a line ! */ + flush = sizeof Buffer / 2; + else + flush = last - Buffer + 1; + write(STDOUT_FILENO, Buffer, flush); + strcpy(Buffer, Buffer + flush); + len -= flush; + } + if ((Result = select(fd + 1, &f, NULL, NULL, &t)) <= 0) { + err = Result == -1 ? errno : 0; + if (len) + write(STDOUT_FILENO, Buffer, len); + if (err == EINTR) + continue; + break; + } + } + + return Result; +} + +/* + * Handle being told by the Monitor thread that there's data to be read + * on the ppp descriptor. + * + * Note, this is a signal handler - be careful of what we do ! + */ +static void +InputHandler(int sig) +{ + static char buf[LINELEN]; + struct timeval t; + int len; + fd_set f; + + if (data != -1) { + FD_ZERO(&f); + FD_SET(data, &f); + t.tv_sec = t.tv_usec = 0; + + if (select(data + 1, &f, NULL, NULL, &t) > 0) { + len = read(data, buf, sizeof buf); + + if (len > 0) + write(STDOUT_FILENO, buf, len); + else if (data != -1) + longjmp(pppdead, -1); + } + + sem_post(&sem_select); + } else + /* Don't let the Monitor thread in 'till we've set ``data'' up again */ + want_sem_post = 1; +} + +/* + * This is a simple wrapper for el_gets(), allowing our SIGUSR1 signal + * handler (above) to take effect only after we've done a setjmp(). + * + * We don't want it to do anything outside of here as we're going to + * service the ppp descriptor anyway. + */ +static const char * +SmartGets(EditLine *e, int *count, int fd) +{ + const char *result; + + if (setjmp(pppdead)) + result = NULL; + else { + data = fd; + if (want_sem_post) + /* Let the Monitor thread in again */ + sem_post(&sem_select); + result = el_gets(e, count); + } + + data = -1; + + return result; +} + +/* + * The Terminal thread entry point. + * + * The bulk of the interactive work is done here. We read the terminal, + * write the results to our ppp descriptor and read the results back. + * + * While reading the terminal (using el_gets()), it's possible to take + * a SIGUSR1 from the Monitor thread, telling us that the ppp descriptor + * has some data. The data is read and displayed by the signal handler + * itself. + */ +static void * +Terminal(void *v) +{ + struct sigaction act, oact; + struct thread_data *td; + const char *l; + int len; +#ifndef __OpenBSD__ + HistEvent hev = { 0, "" }; +#endif + + act.sa_handler = InputHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGUSR1, &act, &oact); + + td = (struct thread_data *)v; + want_sem_post = 1; + + while ((l = SmartGets(td->edit, &len, td->ppp))) { + if (len > 1) +#ifdef __OpenBSD__ + history(td->hist, H_ENTER, l); +#else + history(td->hist, &hev, H_ENTER, l); +#endif + write(td->ppp, l, len); + if (Receive(td->ppp, REC_SHOW) != 0) + break; + } + + return NULL; +} + +/* + * The Monitor thread entry point. + * + * This thread simply monitors our ppp descriptor. When there's something + * to read, a SIGUSR1 is sent to the Terminal thread. + * + * sem_select() is used by the Terminal thread to keep us from sending + * flurries of SIGUSR1s, and is used from the main thread to wake us up + * when it's time to exit. + */ +static void * +Monitor(void *v) +{ + struct thread_data *td; + fd_set f; + int ret; + + td = (struct thread_data *)v; + FD_ZERO(&f); + FD_SET(td->ppp, &f); + + sem_wait(&sem_select); + while (!timetogo) + if ((ret = select(td->ppp + 1, &f, NULL, NULL, NULL)) > 0) { + pthread_kill(td->trm, SIGUSR1); + sem_wait(&sem_select); + } + + return NULL; +} + +static const char * +sockaddr_ntop(const struct sockaddr *sa) +{ + const void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((const struct sockaddr_in *)sa)->sin_addr; + break; + case AF_UNIX: + addr = &((const struct sockaddr_un *)sa)->sun_path; + break; + case AF_INET6: + addr = &((const struct sockaddr_in6 *)sa)->sin6_addr; + break; + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +/* + * Connect to ppp using either a local domain socket or a tcp socket. + * + * If we're given arguments, process them and quit, otherwise create two + * threads to handle interactive mode. + */ +int +main(int argc, char **argv) +{ + struct sockaddr_un ifsun; + int n, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2; + unsigned TimeoutVal; + char *DoneWord = "x", *next, *start; + struct sigaction act, oact; + void *thread_ret; + pthread_t mon; + char Command[LINELEN]; + char Buffer[LINELEN]; + + verbose = 0; + TimeoutVal = 2; + hide1 = hide1off = hide2 = 0; + + for (arg = 1; arg < argc; arg++) + if (*argv[arg] == '-') { + for (start = argv[arg] + 1; *start; start++) + switch (*start) { + case 't': + TimeoutVal = (unsigned)atoi + (start[1] ? start + 1 : argv[++arg]); + start = DoneWord; + break; + + case 'v': + verbose = REC_VERBOSE; + break; + + case 'p': + if (start[1]) { + hide1 = arg; + hide1off = start - argv[arg]; + passwd = start + 1; + } else { + hide1 = arg; + hide1off = start - argv[arg]; + passwd = argv[++arg]; + hide2 = arg; + } + start = DoneWord; + break; + + default: + usage(); + } + } + else + break; + + + if (argc < arg + 1) + usage(); + + if (hide1) { + char title[1024]; + int pos, harg; + + for (harg = pos = 0; harg < argc; harg++) + if (harg == 0 || harg != hide2) { + if (harg == 0 || harg != hide1) + n = snprintf(title + pos, sizeof title - pos, "%s%s", + harg ? " " : "", argv[harg]); + else if (hide1off > 1) + n = snprintf(title + pos, sizeof title - pos, " %.*s", + hide1off, argv[harg]); + else + n = 0; + if (n < 0 || n >= sizeof title - pos) + break; + pos += n; + } +#ifdef __FreeBSD__ + setproctitle("-%s", title); +#else + setproctitle("%s", title); +#endif + } + + if (*argv[arg] == '/') { + memset(&ifsun, '\0', sizeof ifsun); + ifsun.sun_len = strlen(argv[arg]); + if (ifsun.sun_len > sizeof ifsun.sun_path - 1) { + warnx("%s: path too long", argv[arg]); + return 1; + } + ifsun.sun_family = AF_LOCAL; + strcpy(ifsun.sun_path, argv[arg]); + + if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) { + warnx("cannot create local domain socket"); + return 2; + } + if (connect(fd, (struct sockaddr *)&ifsun, sizeof(ifsun)) < 0) { + if (errno) + warn("cannot connect to socket %s", argv[arg]); + else + warnx("cannot connect to socket %s", argv[arg]); + close(fd); + return 3; + } + } else { + char *addr, *p, *port; + const char *caddr; + struct addrinfo hints, *res, *pai; + int gai; + char local[] = "localhost"; + + addr = argv[arg]; + if (addr[strspn(addr, "0123456789")] == '\0') { + /* port on local machine */ + port = addr; + addr = local; + } else if (*addr == '[') { + /* [addr]:port */ + if ((p = strchr(addr, ']')) == NULL) { + warnx("%s: mismatched '['", addr); + return 1; + } + addr++; + *p++ = '\0'; + if (*p != ':') { + warnx("%s: missing port", addr); + return 1; + } + port = ++p; + } else { + /* addr:port */ + p = addr + strcspn(addr, ":"); + if (*p != ':') { + warnx("%s: missing port", addr); + return 1; + } + *p++ = '\0'; + port = p; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + gai = getaddrinfo(addr, port, &hints, &res); + if (gai != 0) { + warnx("%s: %s", addr, gai_strerror(gai)); + return 1; + } + for (pai = res; pai != NULL; pai = pai->ai_next) { + if (fd = socket(pai->ai_family, pai->ai_socktype, + pai->ai_protocol), fd < 0) { + warnx("cannot create socket"); + continue; + } + TimedOut = 0; + if (TimeoutVal) { + act.sa_handler = Timeout; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, &oact); + alarm(TimeoutVal); + } + if (connect(fd, pai->ai_addr, pai->ai_addrlen) == 0) + break; + if (TimeoutVal) { + save_errno = errno; + alarm(0); + sigaction(SIGALRM, &oact, 0); + errno = save_errno; + } + caddr = sockaddr_ntop(pai->ai_addr); + if (caddr == NULL) + caddr = argv[arg]; + if (TimedOut) + warnx("timeout: cannot connect to %s", caddr); + else { + if (errno) + warn("cannot connect to %s", caddr); + else + warnx("cannot connect to %s", caddr); + } + close(fd); + } + freeaddrinfo(res); + if (pai == NULL) + return 1; + if (TimeoutVal) { + alarm(0); + sigaction(SIGALRM, &oact, 0); + } + } + + len = 0; + Command[sizeof(Command)-1] = '\0'; + for (arg++; arg < argc; arg++) { + if (len && len < sizeof(Command)-1) + strcpy(Command+len++, " "); + strncpy(Command+len, argv[arg], sizeof(Command)-len-1); + len += strlen(Command+len); + } + + switch (Receive(fd, verbose | REC_PASSWD)) { + case 1: + fprintf(stderr, "Password incorrect\n"); + break; + + case 0: + passwd = NULL; + if (len == 0) { + struct thread_data td; + const char *env; + int size; +#ifndef __OpenBSD__ + HistEvent hev = { 0, "" }; +#endif + + td.hist = history_init(); + if ((env = getenv("EL_SIZE"))) { + size = atoi(env); + if (size < 0) + size = 20; + } else + size = 20; +#ifdef __OpenBSD__ + history(td.hist, H_EVENT, size); + td.edit = el_init("pppctl", stdin, stdout); +#else + history(td.hist, &hev, H_SETSIZE, size); + td.edit = el_init("pppctl", stdin, stdout, stderr); +#endif + el_source(td.edit, NULL); + el_set(td.edit, EL_PROMPT, GetPrompt); + if ((env = getenv("EL_EDITOR"))) { + if (!strcmp(env, "vi")) + el_set(td.edit, EL_EDITOR, "vi"); + else if (!strcmp(env, "emacs")) + el_set(td.edit, EL_EDITOR, "emacs"); + } + el_set(td.edit, EL_SIGNAL, 1); + el_set(td.edit, EL_HIST, history, (const char *)td.hist); + + td.ppp = fd; + td.trm = NULL; + + /* + * We create two threads. The Terminal thread does all the + * work while the Monitor thread simply tells the Terminal + * thread when ``fd'' becomes readable. The telling is done + * by sending a SIGUSR1 to the Terminal thread. The + * sem_select semaphore is used to prevent the monitor + * thread from firing excessive signals at the Terminal + * thread (it's abused for exit handling too - see below). + * + * The Terminal thread never uses td.trm ! + */ + sem_init(&sem_select, 0, 0); + + pthread_create(&td.trm, NULL, Terminal, &td); + pthread_create(&mon, NULL, Monitor, &td); + + /* Wait for the terminal thread to finish */ + pthread_join(td.trm, &thread_ret); + fprintf(stderr, "Connection closed\n"); + + /* Get rid of the monitor thread by abusing sem_select */ + timetogo = 1; + close(fd); + fd = -1; + sem_post(&sem_select); + pthread_join(mon, &thread_ret); + + /* Restore our terminal and release resources */ + el_end(td.edit); + history_end(td.hist); + sem_destroy(&sem_select); + } else { + start = Command; + do { + next = strchr(start, ';'); + while (*start == ' ' || *start == '\t') + start++; + if (next) + *next = '\0'; + strcpy(Buffer, start); + Buffer[sizeof(Buffer)-2] = '\0'; + strcat(Buffer, "\n"); + if (verbose) + write(STDOUT_FILENO, Buffer, strlen(Buffer)); + write(fd, Buffer, strlen(Buffer)); + if (Receive(fd, verbose | REC_SHOW) != 0) { + fprintf(stderr, "Connection closed\n"); + break; + } + if (next) + start = ++next; + } while (next && *next); + if (verbose) + write(STDOUT_FILENO, "quit\n", 5); + write(fd, "quit\n", 5); + while (Receive(fd, verbose | REC_SHOW) == 0) + ; + if (verbose) + puts(""); + } + break; + + default: + warnx("ppp is not responding"); + break; + } + + if (fd != -1) + close(fd); + + return 0; +} |
