aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/pppctl
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>2005-04-20 19:11:07 +0000
committercvs2svn <cvs2svn@FreeBSD.org>2005-04-20 19:11:07 +0000
commit79c906b55063e4f0f0d3281363970da4e619cf56 (patch)
tree09cc3ca07e7d7250dd9d9d550c76eacaf462ca91 /usr.sbin/pppctl
parent7b100a2efc2b0855b788d000f7b2215fb73a5221 (diff)
parent1e4e7f70c04a80eee7cfe0ecc6787272a43ba8bc (diff)
Notes
Diffstat (limited to 'usr.sbin/pppctl')
-rw-r--r--usr.sbin/pppctl/Makefile9
-rw-r--r--usr.sbin/pppctl/pppctl.8232
-rw-r--r--usr.sbin/pppctl/pppctl.c680
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;
+}