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