131921Sbrian/*-
231921Sbrian * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
331921Sbrian * All rights reserved.
431921Sbrian *
531921Sbrian * Redistribution and use in source and binary forms, with or without
631921Sbrian * modification, are permitted provided that the following conditions
731921Sbrian * are met:
831921Sbrian * 1. Redistributions of source code must retain the above copyright
931921Sbrian *    notice, this list of conditions and the following disclaimer.
1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1131921Sbrian *    notice, this list of conditions and the following disclaimer in the
1231921Sbrian *    documentation and/or other materials provided with the distribution.
1331921Sbrian *
1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1731921Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2431921Sbrian * SUCH DAMAGE.
2531205Sbrian */
2631205Sbrian
27117280Scharnier#include <sys/cdefs.h>
28117280Scharnier__FBSDID("$FreeBSD$");
29117280Scharnier
3027008Sbrian#include <sys/types.h>
3131031Sbrian
3227008Sbrian#include <sys/socket.h>
3327008Sbrian#include <netinet/in.h>
3427747Sbrian#include <arpa/inet.h>
3527008Sbrian#include <sys/un.h>
3627008Sbrian#include <netdb.h>
3731031Sbrian
3831119Sbrian#include <sys/time.h>
3949376Sbrian#include <err.h>
4031204Sbrian#include <errno.h>
4131031Sbrian#include <histedit.h>
4269245Sbrian#include <semaphore.h>
4369245Sbrian#include <pthread.h>
4431204Sbrian#include <setjmp.h>
4527008Sbrian#include <signal.h>
4631031Sbrian#include <stdio.h>
4731031Sbrian#include <stdlib.h>
4827008Sbrian#include <string.h>
4931119Sbrian#include <time.h>
5027008Sbrian#include <unistd.h>
5127008Sbrian
5227008Sbrian#define LINELEN 2048
5327008Sbrian
5469245Sbrian/* Data passed to the threads we create */
5569245Sbrianstruct thread_data {
5669245Sbrian    EditLine *edit;		/* libedit stuff */
5769245Sbrian    History *hist;		/* libedit stuff */
5869245Sbrian    pthread_t trm;		/* Terminal thread (for pthread_kill()) */
5969245Sbrian    int ppp;			/* ppp descriptor */
6069245Sbrian};
6169245Sbrian
6269245Sbrian/* Flags passed to Receive() */
6369245Sbrian#define REC_PASSWD  (1)		/* Handle a password request from ppp */
6469245Sbrian#define REC_SHOW    (2)		/* Show everything except prompts */
6569245Sbrian#define REC_VERBOSE (4)		/* Show everything */
6669245Sbrian
6769245Sbrianstatic char *passwd;
6869245Sbrianstatic char *prompt;		/* Tell libedit what the current prompt is */
6969245Sbrianstatic int data = -1;		/* setjmp() has been done when data != -1 */
7069245Sbrianstatic jmp_buf pppdead;		/* Jump the Terminal thread out of el_gets() */
7169245Sbrianstatic int timetogo;		/* Tell the Monitor thread to exit */
7269245Sbrianstatic sem_t sem_select;	/* select() co-ordination between threads */
7369245Sbrianstatic int TimedOut;		/* Set if our connect() timed out */
7469245Sbrianstatic int want_sem_post;	/* Need to let the Monitor thread in ? */
7569245Sbrian
7669245Sbrian/*
7769245Sbrian * How to use pppctl...
7869245Sbrian */
7931031Sbrianstatic int
8049376Sbrianusage()
8127008Sbrian{
8249376Sbrian    fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] "
8331031Sbrian            "Port|LocalSock [command[;command]...]\n");
8431031Sbrian    fprintf(stderr, "              -v tells pppctl to output all"
8531031Sbrian            " conversation\n");
8631031Sbrian    fprintf(stderr, "              -t n specifies a timeout of n"
8731204Sbrian            " seconds when connecting (default 2)\n");
8827008Sbrian    fprintf(stderr, "              -p passwd specifies your password\n");
8949376Sbrian    exit(1);
9027008Sbrian}
9127008Sbrian
9269245Sbrian/*
9369245Sbrian * Handle the SIGALRM received due to a connect() timeout.
9469245Sbrian */
9531031Sbrianstatic void
9631031SbrianTimeout(int Sig)
9727008Sbrian{
9827008Sbrian    TimedOut = 1;
9927008Sbrian}
10027008Sbrian
10169245Sbrian/*
10269245Sbrian * A callback routine for libedit to find out what the current prompt is.
10369245Sbrian * All the work is done in Receive() below.
10469245Sbrian */
10531031Sbrianstatic char *
10631031SbrianGetPrompt(EditLine *e)
10727008Sbrian{
10831031Sbrian    if (prompt == NULL)
10931031Sbrian        prompt = "";
11031031Sbrian    return prompt;
11131031Sbrian}
11231031Sbrian
11369245Sbrian/*
11469245Sbrian * Receive data from the ppp descriptor.
11569245Sbrian * We also handle password prompts here (if asked via the `display' arg)
11669245Sbrian * and buffer what our prompt looks like (via the `prompt' global).
11769245Sbrian */
11831031Sbrianstatic int
11931204SbrianReceive(int fd, int display)
12031031Sbrian{
12169245Sbrian    static char Buffer[LINELEN];
12269245Sbrian    struct timeval t;
12327008Sbrian    int Result;
12469245Sbrian    char *last;
12569245Sbrian    fd_set f;
12627008Sbrian    int len;
127138808Sbrian    int err;
12827008Sbrian
12969245Sbrian    FD_ZERO(&f);
13069245Sbrian    FD_SET(fd, &f);
13169245Sbrian    t.tv_sec = 0;
13269245Sbrian    t.tv_usec = 100000;
13331031Sbrian    prompt = Buffer;
13427008Sbrian    len = 0;
13569245Sbrian
13627008Sbrian    while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
137138808Sbrian        if (Result == 0) {
13869245Sbrian            Result = -1;
13969245Sbrian            break;
14031204Sbrian        }
14127008Sbrian        len += Result;
14227008Sbrian        Buffer[len] = '\0';
14332020Sbrian        if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
14431079Sbrian            prompt = strrchr(Buffer, '\n');
14527008Sbrian            if (display & (REC_SHOW|REC_VERBOSE)) {
14627008Sbrian                if (display & REC_VERBOSE)
14727008Sbrian                    last = Buffer+len-1;
14827008Sbrian                else
14931079Sbrian                    last = prompt;
15027008Sbrian                if (last) {
15131031Sbrian                    last++;
15280381Ssheldonh                    write(STDOUT_FILENO, Buffer, last-Buffer);
15327008Sbrian                }
15427008Sbrian            }
15531079Sbrian            prompt = prompt == NULL ? Buffer : prompt+1;
15627008Sbrian            for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
15727008Sbrian                ;
15827008Sbrian            if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
15927008Sbrian                 /* a password is required ! */
16027008Sbrian                 if (display & REC_PASSWD) {
16127008Sbrian                    /* password time */
16227008Sbrian                    if (!passwd)
16327008Sbrian                        passwd = getpass("Password: ");
16427008Sbrian                    sprintf(Buffer, "passwd %s\n", passwd);
16531031Sbrian                    memset(passwd, '\0', strlen(passwd));
16627008Sbrian                    if (display & REC_VERBOSE)
16780381Ssheldonh                        write(STDOUT_FILENO, Buffer, strlen(Buffer));
16827008Sbrian                    write(fd, Buffer, strlen(Buffer));
16931031Sbrian                    memset(Buffer, '\0', strlen(Buffer));
17031204Sbrian                    return Receive(fd, display & ~REC_PASSWD);
17127008Sbrian                }
17227008Sbrian                Result = 1;
17327008Sbrian            } else
17427008Sbrian                Result = 0;
17527008Sbrian            break;
17669245Sbrian        } else
17769245Sbrian            prompt = "";
17832020Sbrian        if (len == sizeof Buffer - 1) {
17932020Sbrian            int flush;
18032020Sbrian            if ((last = strrchr(Buffer, '\n')) == NULL)
18132020Sbrian                /* Yeuch - this is one mother of a line ! */
18232020Sbrian                flush = sizeof Buffer / 2;
18332020Sbrian            else
18432020Sbrian                flush = last - Buffer + 1;
18580381Ssheldonh            write(STDOUT_FILENO, Buffer, flush);
18632020Sbrian            strcpy(Buffer, Buffer + flush);
18732020Sbrian            len -= flush;
18832020Sbrian        }
18969245Sbrian        if ((Result = select(fd + 1, &f, NULL, NULL, &t)) <= 0) {
190138808Sbrian            err = Result == -1 ? errno : 0;
19169245Sbrian            if (len)
19280381Ssheldonh                write(STDOUT_FILENO, Buffer, len);
193138808Sbrian            if (err == EINTR)
194138808Sbrian                continue;
19569245Sbrian            break;
19669245Sbrian        }
19727008Sbrian    }
19827008Sbrian
19927008Sbrian    return Result;
20027008Sbrian}
20127008Sbrian
20269245Sbrian/*
20369245Sbrian * Handle being told by the Monitor thread that there's data to be read
20469245Sbrian * on the ppp descriptor.
20569245Sbrian *
20669245Sbrian * Note, this is a signal handler - be careful of what we do !
20769245Sbrian */
20831117Sbrianstatic void
20969245SbrianInputHandler(int sig)
21031117Sbrian{
21169245Sbrian    static char buf[LINELEN];
21231119Sbrian    struct timeval t;
21369245Sbrian    int len;
21431119Sbrian    fd_set f;
21569245Sbrian
21669245Sbrian    if (data != -1) {
21769245Sbrian        FD_ZERO(&f);
21869245Sbrian        FD_SET(data, &f);
21969245Sbrian        t.tv_sec = t.tv_usec = 0;
22069245Sbrian
22169245Sbrian        if (select(data + 1, &f, NULL, NULL, &t) > 0) {
22269245Sbrian            len = read(data, buf, sizeof buf);
22369245Sbrian
22469245Sbrian            if (len > 0)
22580381Ssheldonh                write(STDOUT_FILENO, buf, len);
22669245Sbrian            else if (data != -1)
22769245Sbrian                longjmp(pppdead, -1);
22869245Sbrian        }
22969245Sbrian
23069245Sbrian        sem_post(&sem_select);
23169245Sbrian    } else
23269245Sbrian        /* Don't let the Monitor thread in 'till we've set ``data'' up again */
23369245Sbrian        want_sem_post = 1;
23469245Sbrian}
23569245Sbrian
23669245Sbrian/*
23769245Sbrian * This is a simple wrapper for el_gets(), allowing our SIGUSR1 signal
23869245Sbrian * handler (above) to take effect only after we've done a setjmp().
23969245Sbrian *
24069245Sbrian * We don't want it to do anything outside of here as we're going to
24169245Sbrian * service the ppp descriptor anyway.
24269245Sbrian */
24369245Sbrianstatic const char *
24469245SbrianSmartGets(EditLine *e, int *count, int fd)
24569245Sbrian{
24669245Sbrian    const char *result;
24769245Sbrian
24869245Sbrian    if (setjmp(pppdead))
24969245Sbrian        result = NULL;
25069245Sbrian    else {
25169245Sbrian        data = fd;
25269245Sbrian        if (want_sem_post)
25369245Sbrian            /* Let the Monitor thread in again */
25469245Sbrian            sem_post(&sem_select);
25569245Sbrian        result = el_gets(e, count);
25669245Sbrian    }
25769245Sbrian
25869245Sbrian    data = -1;
25969245Sbrian
26069245Sbrian    return result;
26169245Sbrian}
26269245Sbrian
26369245Sbrian/*
26469245Sbrian * The Terminal thread entry point.
26569245Sbrian *
26669245Sbrian * The bulk of the interactive work is done here.  We read the terminal,
26769245Sbrian * write the results to our ppp descriptor and read the results back.
26869245Sbrian *
26969245Sbrian * While reading the terminal (using el_gets()), it's possible to take
27069245Sbrian * a SIGUSR1 from the Monitor thread, telling us that the ppp descriptor
27169245Sbrian * has some data.  The data is read and displayed by the signal handler
27269245Sbrian * itself.
27369245Sbrian */
27469245Sbrianstatic void *
27569245SbrianTerminal(void *v)
27669245Sbrian{
27769245Sbrian    struct sigaction act, oact;
27869245Sbrian    struct thread_data *td;
27969245Sbrian    const char *l;
28031204Sbrian    int len;
28184261Sobrien#ifndef __OpenBSD__
28273558Sbrian    HistEvent hev = { 0, "" };
28373558Sbrian#endif
28431117Sbrian
28569245Sbrian    act.sa_handler = InputHandler;
28669245Sbrian    sigemptyset(&act.sa_mask);
28769245Sbrian    act.sa_flags = SA_RESTART;
28869245Sbrian    sigaction(SIGUSR1, &act, &oact);
28969245Sbrian
29069245Sbrian    td = (struct thread_data *)v;
29169245Sbrian    want_sem_post = 1;
29269245Sbrian
29369245Sbrian    while ((l = SmartGets(td->edit, &len, td->ppp))) {
29469245Sbrian        if (len > 1)
29584261Sobrien#ifdef __OpenBSD__
29684261Sobrien            history(td->hist, H_ENTER, l);
29784261Sobrien#else
29873558Sbrian            history(td->hist, &hev, H_ENTER, l);
29969245Sbrian#endif
30069245Sbrian        write(td->ppp, l, len);
30169245Sbrian        if (Receive(td->ppp, REC_SHOW) != 0)
30269245Sbrian            break;
30331204Sbrian    }
30469245Sbrian
30569245Sbrian    return NULL;
30631117Sbrian}
30731117Sbrian
30869245Sbrian/*
30969245Sbrian * The Monitor thread entry point.
31069245Sbrian *
31169245Sbrian * This thread simply monitors our ppp descriptor.  When there's something
31269245Sbrian * to read, a SIGUSR1 is sent to the Terminal thread.
31369245Sbrian *
31469245Sbrian * sem_select() is used by the Terminal thread to keep us from sending
31569245Sbrian * flurries of SIGUSR1s, and is used from the main thread to wake us up
31669245Sbrian * when it's time to exit.
31769245Sbrian */
31869245Sbrianstatic void *
31969245SbrianMonitor(void *v)
32031117Sbrian{
32169245Sbrian    struct thread_data *td;
32269245Sbrian    fd_set f;
32369245Sbrian    int ret;
32431117Sbrian
32569245Sbrian    td = (struct thread_data *)v;
32669245Sbrian    FD_ZERO(&f);
32769245Sbrian    FD_SET(td->ppp, &f);
32831117Sbrian
32969245Sbrian    sem_wait(&sem_select);
33069245Sbrian    while (!timetogo)
33169245Sbrian        if ((ret = select(td->ppp + 1, &f, NULL, NULL, NULL)) > 0) {
33269245Sbrian            pthread_kill(td->trm, SIGUSR1);
33369245Sbrian            sem_wait(&sem_select);
33469245Sbrian        }
33569245Sbrian
33669245Sbrian    return NULL;
33731117Sbrian}
33831117Sbrian
339123229Stjrstatic const char *
340123229Stjrsockaddr_ntop(const struct sockaddr *sa)
341123229Stjr{
342123229Stjr    const void *addr;
343123229Stjr    static char addrbuf[INET6_ADDRSTRLEN];
344123229Stjr
345123229Stjr    switch (sa->sa_family) {
346123229Stjr    case AF_INET:
347123229Stjr	addr = &((const struct sockaddr_in *)sa)->sin_addr;
348123229Stjr	break;
349123229Stjr    case AF_UNIX:
350123229Stjr	addr = &((const struct sockaddr_un *)sa)->sun_path;
351123229Stjr	break;
352123229Stjr    case AF_INET6:
353123229Stjr	addr = &((const struct sockaddr_in6 *)sa)->sin6_addr;
354123229Stjr	break;
355123229Stjr    default:
356123229Stjr	return NULL;
357123229Stjr    }
358123229Stjr    inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
359123229Stjr    return addrbuf;
360123229Stjr}
361123229Stjr
36269245Sbrian/*
36369245Sbrian * Connect to ppp using either a local domain socket or a tcp socket.
36469245Sbrian *
36569245Sbrian * If we're given arguments, process them and quit, otherwise create two
36669245Sbrian * threads to handle interactive mode.
36769245Sbrian */
36827008Sbrianint
36927008Sbrianmain(int argc, char **argv)
37027008Sbrian{
37127008Sbrian    struct sockaddr_un ifsun;
372123229Stjr    int n, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2;
37327008Sbrian    unsigned TimeoutVal;
37427008Sbrian    char *DoneWord = "x", *next, *start;
37527008Sbrian    struct sigaction act, oact;
37669245Sbrian    void *thread_ret;
37769245Sbrian    pthread_t mon;
37869245Sbrian    char Command[LINELEN];
37969245Sbrian    char Buffer[LINELEN];
38027008Sbrian
38127008Sbrian    verbose = 0;
38227008Sbrian    TimeoutVal = 2;
38364703Sbrian    hide1 = hide1off = hide2 = 0;
38427008Sbrian
38527008Sbrian    for (arg = 1; arg < argc; arg++)
38627008Sbrian        if (*argv[arg] == '-') {
38727008Sbrian            for (start = argv[arg] + 1; *start; start++)
38827008Sbrian                switch (*start) {
38927008Sbrian                    case 't':
39027008Sbrian                        TimeoutVal = (unsigned)atoi
39127008Sbrian                            (start[1] ? start + 1 : argv[++arg]);
39227008Sbrian                        start = DoneWord;
39327008Sbrian                        break;
39427008Sbrian
39527008Sbrian                    case 'v':
39627008Sbrian                        verbose = REC_VERBOSE;
39727008Sbrian                        break;
39827008Sbrian
39927008Sbrian                    case 'p':
40064703Sbrian                        if (start[1]) {
40164703Sbrian                          hide1 = arg;
40264703Sbrian                          hide1off = start - argv[arg];
40364703Sbrian                          passwd = start + 1;
40464703Sbrian                        } else {
40564703Sbrian                          hide1 = arg;
40664703Sbrian                          hide1off = start - argv[arg];
40764703Sbrian                          passwd = argv[++arg];
40864703Sbrian                          hide2 = arg;
40964703Sbrian                        }
41027008Sbrian                        start = DoneWord;
41127008Sbrian                        break;
41227008Sbrian
41327008Sbrian                    default:
41449376Sbrian                        usage();
41527008Sbrian                }
41627008Sbrian        }
41727008Sbrian        else
41827008Sbrian            break;
41927008Sbrian
42027008Sbrian
42131006Sbrian    if (argc < arg + 1)
42249376Sbrian        usage();
42327008Sbrian
42464703Sbrian    if (hide1) {
42564703Sbrian      char title[1024];
42664703Sbrian      int pos, harg;
42764703Sbrian
42864703Sbrian      for (harg = pos = 0; harg < argc; harg++)
42964703Sbrian        if (harg == 0 || harg != hide2) {
43064703Sbrian          if (harg == 0 || harg != hide1)
43181904Sbrian            n = snprintf(title + pos, sizeof title - pos, "%s%s",
43264703Sbrian                            harg ? " " : "", argv[harg]);
43364703Sbrian          else if (hide1off > 1)
43481904Sbrian            n = snprintf(title + pos, sizeof title - pos, " %.*s",
43564703Sbrian                            hide1off, argv[harg]);
43681904Sbrian          else
43781904Sbrian            n = 0;
43881904Sbrian          if (n < 0 || n >= sizeof title - pos)
43981904Sbrian            break;
44081904Sbrian          pos += n;
44164703Sbrian        }
44264703Sbrian#ifdef __FreeBSD__
44364703Sbrian      setproctitle("-%s", title);
44464703Sbrian#else
44564703Sbrian      setproctitle("%s", title);
44664703Sbrian#endif
44764703Sbrian    }
44864703Sbrian
44927008Sbrian    if (*argv[arg] == '/') {
45031914Sbrian        memset(&ifsun, '\0', sizeof ifsun);
45127008Sbrian        ifsun.sun_len = strlen(argv[arg]);
45227008Sbrian        if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
45349376Sbrian            warnx("%s: path too long", argv[arg]);
45427008Sbrian            return 1;
45527008Sbrian        }
45627008Sbrian        ifsun.sun_family = AF_LOCAL;
45727008Sbrian        strcpy(ifsun.sun_path, argv[arg]);
45827008Sbrian
45927008Sbrian        if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
46049376Sbrian            warnx("cannot create local domain socket");
46127008Sbrian            return 2;
46227008Sbrian        }
463123229Stjr	if (connect(fd, (struct sockaddr *)&ifsun, sizeof(ifsun)) < 0) {
464123229Stjr	    if (errno)
465123229Stjr		warn("cannot connect to socket %s", argv[arg]);
466123229Stjr	    else
467123229Stjr		warnx("cannot connect to socket %s", argv[arg]);
468123229Stjr	    close(fd);
469123229Stjr	    return 3;
470123229Stjr	}
47127008Sbrian    } else {
472123229Stjr        char *addr, *p, *port;
473123229Stjr	const char *caddr;
474123229Stjr	struct addrinfo hints, *res, *pai;
475123229Stjr        int gai;
476123229Stjr	char local[] = "localhost";
47727347Sbrian
478123229Stjr	addr = argv[arg];
479123229Stjr	if (addr[strspn(addr, "0123456789")] == '\0') {
480123229Stjr	    /* port on local machine */
481123229Stjr	    port = addr;
482123229Stjr	    addr = local;
483123229Stjr	} else if (*addr == '[') {
484123229Stjr	    /* [addr]:port */
485123229Stjr	    if ((p = strchr(addr, ']')) == NULL) {
486123229Stjr		warnx("%s: mismatched '['", addr);
487123229Stjr		return 1;
488123229Stjr	    }
489123229Stjr	    addr++;
490123229Stjr	    *p++ = '\0';
491123229Stjr	    if (*p != ':') {
492123229Stjr		warnx("%s: missing port", addr);
493123229Stjr		return 1;
494123229Stjr	    }
495123229Stjr	    port = ++p;
496123229Stjr	} else {
497123229Stjr	    /* addr:port */
498123229Stjr	    p = addr + strcspn(addr, ":");
499123229Stjr	    if (*p != ':') {
500123229Stjr		warnx("%s: missing port", addr);
501123229Stjr		return 1;
502123229Stjr	    }
503123229Stjr	    *p++ = '\0';
504123229Stjr	    port = p;
505123229Stjr	}
506123229Stjr	memset(&hints, 0, sizeof(hints));
507123229Stjr	hints.ai_socktype = SOCK_STREAM;
508123229Stjr	gai = getaddrinfo(addr, port, &hints, &res);
509123229Stjr	if (gai != 0) {
510123229Stjr	    warnx("%s: %s", addr, gai_strerror(gai));
511123229Stjr	    return 1;
512123229Stjr	}
513123229Stjr	for (pai = res; pai != NULL; pai = pai->ai_next) {
514123229Stjr	    if (fd = socket(pai->ai_family, pai->ai_socktype,
515123229Stjr		pai->ai_protocol), fd < 0) {
516123229Stjr		warnx("cannot create socket");
517123229Stjr		continue;
518123229Stjr	    }
519123229Stjr	    TimedOut = 0;
520123229Stjr	    if (TimeoutVal) {
521123229Stjr		act.sa_handler = Timeout;
522123229Stjr		sigemptyset(&act.sa_mask);
523123229Stjr		act.sa_flags = 0;
524123229Stjr		sigaction(SIGALRM, &act, &oact);
525123229Stjr		alarm(TimeoutVal);
526123229Stjr	    }
527123229Stjr	    if (connect(fd, pai->ai_addr, pai->ai_addrlen) == 0)
528123229Stjr		break;
529123229Stjr	    if (TimeoutVal) {
530123229Stjr		save_errno = errno;
531123229Stjr		alarm(0);
532123229Stjr		sigaction(SIGALRM, &oact, 0);
533123229Stjr		errno = save_errno;
534123229Stjr	    }
535123229Stjr	    caddr = sockaddr_ntop(pai->ai_addr);
536123229Stjr	    if (caddr == NULL)
537123229Stjr		caddr = argv[arg];
538123229Stjr	    if (TimedOut)
539123229Stjr		warnx("timeout: cannot connect to %s", caddr);
540123229Stjr	    else {
541123229Stjr		if (errno)
542123229Stjr		    warn("cannot connect to %s", caddr);
543123229Stjr		else
544123229Stjr		    warnx("cannot connect to %s", caddr);
545123229Stjr	    }
546123229Stjr	    close(fd);
547123229Stjr	}
548123229Stjr	freeaddrinfo(res);
549123229Stjr	if (pai == NULL)
550123229Stjr	    return 1;
551123229Stjr	if (TimeoutVal) {
552123229Stjr	    alarm(0);
553123229Stjr	    sigaction(SIGALRM, &oact, 0);
554123229Stjr	}
55527008Sbrian    }
55627008Sbrian
55727008Sbrian    len = 0;
55827008Sbrian    Command[sizeof(Command)-1] = '\0';
55927008Sbrian    for (arg++; arg < argc; arg++) {
56027008Sbrian        if (len && len < sizeof(Command)-1)
56127008Sbrian            strcpy(Command+len++, " ");
56227008Sbrian        strncpy(Command+len, argv[arg], sizeof(Command)-len-1);
56327008Sbrian        len += strlen(Command+len);
56427008Sbrian    }
56527008Sbrian
56669245Sbrian    switch (Receive(fd, verbose | REC_PASSWD)) {
56727008Sbrian        case 1:
56827008Sbrian            fprintf(stderr, "Password incorrect\n");
56927008Sbrian            break;
57027008Sbrian
57127008Sbrian        case 0:
57269245Sbrian            passwd = NULL;
57331006Sbrian            if (len == 0) {
57469245Sbrian                struct thread_data td;
57569245Sbrian                const char *env;
57631031Sbrian                int size;
57784261Sobrien#ifndef __OpenBSD__
57873558Sbrian                HistEvent hev = { 0, "" };
57973558Sbrian#endif
58031031Sbrian
58169245Sbrian                td.hist = history_init();
58231031Sbrian                if ((env = getenv("EL_SIZE"))) {
58331031Sbrian                    size = atoi(env);
58431031Sbrian                    if (size < 0)
58531031Sbrian                      size = 20;
58631031Sbrian                } else
58731031Sbrian                    size = 20;
58884261Sobrien#ifdef __OpenBSD__
58984261Sobrien                history(td.hist, H_EVENT, size);
59084261Sobrien                td.edit = el_init("pppctl", stdin, stdout);
59184261Sobrien#else
59273558Sbrian                history(td.hist, &hev, H_SETSIZE, size);
59369245Sbrian                td.edit = el_init("pppctl", stdin, stdout, stderr);
59446084Sbrian#endif
59569245Sbrian                el_source(td.edit, NULL);
59669245Sbrian                el_set(td.edit, EL_PROMPT, GetPrompt);
59734771Sbrian                if ((env = getenv("EL_EDITOR"))) {
59831031Sbrian                    if (!strcmp(env, "vi"))
59969245Sbrian                        el_set(td.edit, EL_EDITOR, "vi");
60031031Sbrian                    else if (!strcmp(env, "emacs"))
60169245Sbrian                        el_set(td.edit, EL_EDITOR, "emacs");
60234771Sbrian                }
60369245Sbrian                el_set(td.edit, EL_SIGNAL, 1);
60469245Sbrian                el_set(td.edit, EL_HIST, history, (const char *)td.hist);
60569245Sbrian
60669245Sbrian                td.ppp = fd;
60769245Sbrian                td.trm = NULL;
60869245Sbrian
60969245Sbrian                /*
61069245Sbrian                 * We create two threads.  The Terminal thread does all the
61169245Sbrian                 * work while the Monitor thread simply tells the Terminal
61269245Sbrian                 * thread when ``fd'' becomes readable.  The telling is done
61369245Sbrian                 * by sending a SIGUSR1 to the Terminal thread.  The
61469245Sbrian                 * sem_select semaphore is used to prevent the monitor
61569245Sbrian                 * thread from firing excessive signals at the Terminal
61669245Sbrian                 * thread (it's abused for exit handling too - see below).
61769245Sbrian                 *
61869245Sbrian                 * The Terminal thread never uses td.trm !
61969245Sbrian                 */
62069245Sbrian                sem_init(&sem_select, 0, 0);
62169245Sbrian
62269245Sbrian                pthread_create(&td.trm, NULL, Terminal, &td);
62369245Sbrian                pthread_create(&mon, NULL, Monitor, &td);
62469245Sbrian
62569245Sbrian                /* Wait for the terminal thread to finish */
62669245Sbrian                pthread_join(td.trm, &thread_ret);
62731204Sbrian                fprintf(stderr, "Connection closed\n");
62869245Sbrian
62969245Sbrian                /* Get rid of the monitor thread by abusing sem_select */
63069245Sbrian                timetogo = 1;
63169245Sbrian                close(fd);
63269245Sbrian                fd = -1;
63369245Sbrian                sem_post(&sem_select);
63469245Sbrian                pthread_join(mon, &thread_ret);
63569245Sbrian
63669245Sbrian                /* Restore our terminal and release resources */
63769245Sbrian                el_end(td.edit);
63869245Sbrian                history_end(td.hist);
63969245Sbrian                sem_destroy(&sem_select);
64031006Sbrian            } else {
64131006Sbrian                start = Command;
64231006Sbrian                do {
64331031Sbrian                    next = strchr(start, ';');
64431006Sbrian                    while (*start == ' ' || *start == '\t')
64531006Sbrian                        start++;
64631006Sbrian                    if (next)
64731006Sbrian                        *next = '\0';
64831006Sbrian                    strcpy(Buffer, start);
64931006Sbrian                    Buffer[sizeof(Buffer)-2] = '\0';
65031006Sbrian                    strcat(Buffer, "\n");
65131006Sbrian                    if (verbose)
65280381Ssheldonh                        write(STDOUT_FILENO, Buffer, strlen(Buffer));
65331006Sbrian                    write(fd, Buffer, strlen(Buffer));
65431204Sbrian                    if (Receive(fd, verbose | REC_SHOW) != 0) {
65531829Sbrian                        fprintf(stderr, "Connection closed\n");
65631006Sbrian                        break;
65731006Sbrian                    }
65831006Sbrian                    if (next)
65931006Sbrian                        start = ++next;
66031006Sbrian                } while (next && *next);
66127008Sbrian                if (verbose)
66280381Ssheldonh                    write(STDOUT_FILENO, "quit\n", 5);
66374793Sbrian                write(fd, "quit\n", 5);
66474793Sbrian                while (Receive(fd, verbose | REC_SHOW) == 0)
66574793Sbrian                    ;
66674793Sbrian                if (verbose)
66731006Sbrian                    puts("");
66831006Sbrian            }
66927008Sbrian            break;
67027008Sbrian
67127008Sbrian        default:
67249376Sbrian            warnx("ppp is not responding");
67327008Sbrian            break;
67427008Sbrian    }
67527008Sbrian
67669245Sbrian    if (fd != -1)
67769245Sbrian        close(fd);
67827008Sbrian
67927008Sbrian    return 0;
68027008Sbrian}
681