xref: /freebsd/usr.sbin/pppctl/pppctl.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
1c39934eaSBrian Somers /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4c39934eaSBrian Somers  * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
5c39934eaSBrian Somers  * All rights reserved.
6c39934eaSBrian Somers  *
7c39934eaSBrian Somers  * Redistribution and use in source and binary forms, with or without
8c39934eaSBrian Somers  * modification, are permitted provided that the following conditions
9c39934eaSBrian Somers  * are met:
10c39934eaSBrian Somers  * 1. Redistributions of source code must retain the above copyright
11c39934eaSBrian Somers  *    notice, this list of conditions and the following disclaimer.
12c39934eaSBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
13c39934eaSBrian Somers  *    notice, this list of conditions and the following disclaimer in the
14c39934eaSBrian Somers  *    documentation and/or other materials provided with the distribution.
15c39934eaSBrian Somers  *
16c39934eaSBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c39934eaSBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c39934eaSBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c39934eaSBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c39934eaSBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c39934eaSBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c39934eaSBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c39934eaSBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c39934eaSBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c39934eaSBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c39934eaSBrian Somers  * SUCH DAMAGE.
27f5c9fce9SBrian Somers  */
28f5c9fce9SBrian Somers 
29c957ff40SBrian Somers #include <sys/types.h>
307d0a1bacSBrian Somers 
31c957ff40SBrian Somers #include <sys/socket.h>
32c957ff40SBrian Somers #include <netinet/in.h>
33437b5af6SBrian Somers #include <arpa/inet.h>
34c957ff40SBrian Somers #include <sys/un.h>
35c957ff40SBrian Somers #include <netdb.h>
367d0a1bacSBrian Somers 
37d2bb1901SBrian Somers #include <sys/time.h>
38e1e9e152SBrian Somers #include <err.h>
39f3a51f7aSBrian Somers #include <errno.h>
407d0a1bacSBrian Somers #include <histedit.h>
418a407aadSBrian Somers #include <semaphore.h>
428a407aadSBrian Somers #include <pthread.h>
43f3a51f7aSBrian Somers #include <setjmp.h>
44c957ff40SBrian Somers #include <signal.h>
45c957ff40SBrian Somers #include <stdio.h>
46c957ff40SBrian Somers #include <stdlib.h>
477d0a1bacSBrian Somers #include <string.h>
48d2bb1901SBrian Somers #include <time.h>
497d0a1bacSBrian Somers #include <unistd.h>
50c957ff40SBrian Somers 
51c957ff40SBrian Somers #define LINELEN 2048
52c957ff40SBrian Somers 
538a407aadSBrian Somers /* Data passed to the threads we create */
548a407aadSBrian Somers struct thread_data {
558a407aadSBrian Somers     EditLine *edit;		/* libedit stuff */
568a407aadSBrian Somers     History *hist;		/* libedit stuff */
578a407aadSBrian Somers     pthread_t trm;		/* Terminal thread (for pthread_kill()) */
588a407aadSBrian Somers     int ppp;			/* ppp descriptor */
598a407aadSBrian Somers };
608a407aadSBrian Somers 
618a407aadSBrian Somers /* Flags passed to Receive() */
628a407aadSBrian Somers #define REC_PASSWD  (1)		/* Handle a password request from ppp */
638a407aadSBrian Somers #define REC_SHOW    (2)		/* Show everything except prompts */
648a407aadSBrian Somers #define REC_VERBOSE (4)		/* Show everything */
658a407aadSBrian Somers 
668a407aadSBrian Somers static char *passwd;
678a407aadSBrian Somers static char *prompt;		/* Tell libedit what the current prompt is */
688a407aadSBrian Somers static int data = -1;		/* setjmp() has been done when data != -1 */
698a407aadSBrian Somers static jmp_buf pppdead;		/* Jump the Terminal thread out of el_gets() */
708a407aadSBrian Somers static int timetogo;		/* Tell the Monitor thread to exit */
718a407aadSBrian Somers static sem_t sem_select;	/* select() co-ordination between threads */
728a407aadSBrian Somers static int TimedOut;		/* Set if our connect() timed out */
738a407aadSBrian Somers static int want_sem_post;	/* Need to let the Monitor thread in ? */
748a407aadSBrian Somers 
758a407aadSBrian Somers /*
768a407aadSBrian Somers  * How to use pppctl...
778a407aadSBrian Somers  */
787d0a1bacSBrian Somers static int
usage()79e1e9e152SBrian Somers usage()
80c957ff40SBrian Somers {
81e1e9e152SBrian Somers     fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] "
827d0a1bacSBrian Somers             "Port|LocalSock [command[;command]...]\n");
837d0a1bacSBrian Somers     fprintf(stderr, "              -v tells pppctl to output all"
847d0a1bacSBrian Somers             " conversation\n");
857d0a1bacSBrian Somers     fprintf(stderr, "              -t n specifies a timeout of n"
86f3a51f7aSBrian Somers             " seconds when connecting (default 2)\n");
87c957ff40SBrian Somers     fprintf(stderr, "              -p passwd specifies your password\n");
88e1e9e152SBrian Somers     exit(1);
89c957ff40SBrian Somers }
90c957ff40SBrian Somers 
918a407aadSBrian Somers /*
928a407aadSBrian Somers  * Handle the SIGALRM received due to a connect() timeout.
938a407aadSBrian Somers  */
947d0a1bacSBrian Somers static void
Timeout(int Sig)957d0a1bacSBrian Somers Timeout(int Sig)
96c957ff40SBrian Somers {
97c957ff40SBrian Somers     TimedOut = 1;
98c957ff40SBrian Somers }
99c957ff40SBrian Somers 
1008a407aadSBrian Somers /*
1018a407aadSBrian Somers  * A callback routine for libedit to find out what the current prompt is.
1028a407aadSBrian Somers  * All the work is done in Receive() below.
1038a407aadSBrian Somers  */
1047d0a1bacSBrian Somers static char *
GetPrompt(EditLine * e)1057d0a1bacSBrian Somers GetPrompt(EditLine *e)
1067d0a1bacSBrian Somers {
1077d0a1bacSBrian Somers     if (prompt == NULL)
1087d0a1bacSBrian Somers         prompt = "";
1097d0a1bacSBrian Somers     return prompt;
1107d0a1bacSBrian Somers }
1117d0a1bacSBrian Somers 
1128a407aadSBrian Somers /*
1138a407aadSBrian Somers  * Receive data from the ppp descriptor.
1148a407aadSBrian Somers  * We also handle password prompts here (if asked via the `display' arg)
1158a407aadSBrian Somers  * and buffer what our prompt looks like (via the `prompt' global).
1168a407aadSBrian Somers  */
1177d0a1bacSBrian Somers static int
Receive(int fd,int display)118f3a51f7aSBrian Somers Receive(int fd, int display)
119c957ff40SBrian Somers {
1208a407aadSBrian Somers     static char Buffer[LINELEN];
121d282086dSPedro F. Giffuni     char temp[sizeof(Buffer)];
1228a407aadSBrian Somers     struct timeval t;
123c957ff40SBrian Somers     int Result;
124c957ff40SBrian Somers     char *last;
1258a407aadSBrian Somers     fd_set f;
1268a407aadSBrian Somers     int len;
127cf4bdb6eSBrian Somers     int err;
128c957ff40SBrian Somers 
1298a407aadSBrian Somers     FD_ZERO(&f);
1308a407aadSBrian Somers     FD_SET(fd, &f);
1318a407aadSBrian Somers     t.tv_sec = 0;
1328a407aadSBrian Somers     t.tv_usec = 100000;
1337d0a1bacSBrian Somers     prompt = Buffer;
134c957ff40SBrian Somers     len = 0;
1358a407aadSBrian Somers 
136c957ff40SBrian Somers     while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
137cf4bdb6eSBrian Somers         if (Result == 0) {
138f3a51f7aSBrian Somers             Result = -1;
139f3a51f7aSBrian Somers             break;
140f3a51f7aSBrian Somers         }
141c957ff40SBrian Somers         len += Result;
142c957ff40SBrian Somers         Buffer[len] = '\0';
143fcabffd8SBrian Somers         if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
1442d77cf0bSBrian Somers             prompt = strrchr(Buffer, '\n');
145c957ff40SBrian Somers             if (display & (REC_SHOW|REC_VERBOSE)) {
146c957ff40SBrian Somers                 if (display & REC_VERBOSE)
147c957ff40SBrian Somers                     last = Buffer+len-1;
148c957ff40SBrian Somers                 else
1492d77cf0bSBrian Somers                     last = prompt;
150c957ff40SBrian Somers                 if (last) {
1517d0a1bacSBrian Somers                     last++;
152e1b4d8d0SSheldon Hearn                     write(STDOUT_FILENO, Buffer, last-Buffer);
153c957ff40SBrian Somers                 }
154c957ff40SBrian Somers             }
1552d77cf0bSBrian Somers             prompt = prompt == NULL ? Buffer : prompt+1;
156c957ff40SBrian Somers             for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
157c957ff40SBrian Somers                 ;
158c957ff40SBrian Somers             if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
159c957ff40SBrian Somers                  /* a password is required ! */
160c957ff40SBrian Somers                  if (display & REC_PASSWD) {
161c957ff40SBrian Somers                     /* password time */
162c957ff40SBrian Somers                     if (!passwd)
163c957ff40SBrian Somers                         passwd = getpass("Password: ");
164c957ff40SBrian Somers                     sprintf(Buffer, "passwd %s\n", passwd);
1657d0a1bacSBrian Somers                     memset(passwd, '\0', strlen(passwd));
166c957ff40SBrian Somers                     if (display & REC_VERBOSE)
167e1b4d8d0SSheldon Hearn                         write(STDOUT_FILENO, Buffer, strlen(Buffer));
168c957ff40SBrian Somers                     write(fd, Buffer, strlen(Buffer));
1697d0a1bacSBrian Somers                     memset(Buffer, '\0', strlen(Buffer));
170f3a51f7aSBrian Somers                     return Receive(fd, display & ~REC_PASSWD);
171c957ff40SBrian Somers                 }
172c957ff40SBrian Somers                 Result = 1;
173c957ff40SBrian Somers             } else
174c957ff40SBrian Somers                 Result = 0;
175c957ff40SBrian Somers             break;
1768a407aadSBrian Somers         } else
1778a407aadSBrian Somers             prompt = "";
178fcabffd8SBrian Somers         if (len == sizeof Buffer - 1) {
179fcabffd8SBrian Somers             int flush;
180fcabffd8SBrian Somers             if ((last = strrchr(Buffer, '\n')) == NULL)
181fcabffd8SBrian Somers                 /* Yeuch - this is one mother of a line ! */
182fcabffd8SBrian Somers                 flush = sizeof Buffer / 2;
183fcabffd8SBrian Somers             else
184fcabffd8SBrian Somers                 flush = last - Buffer + 1;
185e1b4d8d0SSheldon Hearn             write(STDOUT_FILENO, Buffer, flush);
186d282086dSPedro F. Giffuni 	    strcpy(temp, Buffer + flush);
187d282086dSPedro F. Giffuni 	    strcpy(Buffer, temp);
188fcabffd8SBrian Somers             len -= flush;
189fcabffd8SBrian Somers         }
1908a407aadSBrian Somers         if ((Result = select(fd + 1, &f, NULL, NULL, &t)) <= 0) {
191cf4bdb6eSBrian Somers             err = Result == -1 ? errno : 0;
1928a407aadSBrian Somers             if (len)
193e1b4d8d0SSheldon Hearn                 write(STDOUT_FILENO, Buffer, len);
194cf4bdb6eSBrian Somers             if (err == EINTR)
195cf4bdb6eSBrian Somers                 continue;
1968a407aadSBrian Somers             break;
1978a407aadSBrian Somers         }
198c957ff40SBrian Somers     }
199c957ff40SBrian Somers 
200c957ff40SBrian Somers     return Result;
201c957ff40SBrian Somers }
202c957ff40SBrian Somers 
2038a407aadSBrian Somers /*
2048a407aadSBrian Somers  * Handle being told by the Monitor thread that there's data to be read
2058a407aadSBrian Somers  * on the ppp descriptor.
2068a407aadSBrian Somers  *
2078a407aadSBrian Somers  * Note, this is a signal handler - be careful of what we do !
2088a407aadSBrian Somers  */
209f960587cSBrian Somers static void
InputHandler(int sig)2108a407aadSBrian Somers InputHandler(int sig)
211f960587cSBrian Somers {
212f960587cSBrian Somers     static char buf[LINELEN];
2138a407aadSBrian Somers     struct timeval t;
214f3a51f7aSBrian Somers     int len;
2158a407aadSBrian Somers     fd_set f;
216f960587cSBrian Somers 
2178a407aadSBrian Somers     if (data != -1) {
218d2bb1901SBrian Somers         FD_ZERO(&f);
219d2bb1901SBrian Somers         FD_SET(data, &f);
220d2bb1901SBrian Somers         t.tv_sec = t.tv_usec = 0;
2218a407aadSBrian Somers 
222f3a51f7aSBrian Somers         if (select(data + 1, &f, NULL, NULL, &t) > 0) {
223f3a51f7aSBrian Somers             len = read(data, buf, sizeof buf);
2248a407aadSBrian Somers 
225f3a51f7aSBrian Somers             if (len > 0)
226e1b4d8d0SSheldon Hearn                 write(STDOUT_FILENO, buf, len);
2278a407aadSBrian Somers             else if (data != -1)
228f3a51f7aSBrian Somers                 longjmp(pppdead, -1);
229f3a51f7aSBrian Somers         }
2308a407aadSBrian Somers 
2318a407aadSBrian Somers         sem_post(&sem_select);
2328a407aadSBrian Somers     } else
2338a407aadSBrian Somers         /* Don't let the Monitor thread in 'till we've set ``data'' up again */
2348a407aadSBrian Somers         want_sem_post = 1;
235f960587cSBrian Somers }
236f960587cSBrian Somers 
2378a407aadSBrian Somers /*
2388a407aadSBrian Somers  * This is a simple wrapper for el_gets(), allowing our SIGUSR1 signal
2398a407aadSBrian Somers  * handler (above) to take effect only after we've done a setjmp().
2408a407aadSBrian Somers  *
2418a407aadSBrian Somers  * We don't want it to do anything outside of here as we're going to
2428a407aadSBrian Somers  * service the ppp descriptor anyway.
2438a407aadSBrian Somers  */
244f960587cSBrian Somers static const char *
SmartGets(EditLine * e,int * count,int fd)2458a407aadSBrian Somers SmartGets(EditLine *e, int *count, int fd)
246f960587cSBrian Somers {
247f960587cSBrian Somers     const char *result;
248f960587cSBrian Somers 
2498a407aadSBrian Somers     if (setjmp(pppdead))
2508a407aadSBrian Somers         result = NULL;
2518a407aadSBrian Somers     else {
252f960587cSBrian Somers         data = fd;
2538a407aadSBrian Somers         if (want_sem_post)
2548a407aadSBrian Somers             /* Let the Monitor thread in again */
2558a407aadSBrian Somers             sem_post(&sem_select);
2568a407aadSBrian Somers         result = el_gets(e, count);
2578a407aadSBrian Somers     }
2588a407aadSBrian Somers 
259f960587cSBrian Somers     data = -1;
260f960587cSBrian Somers 
261f960587cSBrian Somers     return result;
262f960587cSBrian Somers }
263f960587cSBrian Somers 
2648a407aadSBrian Somers /*
2658a407aadSBrian Somers  * The Terminal thread entry point.
2668a407aadSBrian Somers  *
2678a407aadSBrian Somers  * The bulk of the interactive work is done here.  We read the terminal,
2688a407aadSBrian Somers  * write the results to our ppp descriptor and read the results back.
2698a407aadSBrian Somers  *
2708a407aadSBrian Somers  * While reading the terminal (using el_gets()), it's possible to take
2718a407aadSBrian Somers  * a SIGUSR1 from the Monitor thread, telling us that the ppp descriptor
2728a407aadSBrian Somers  * has some data.  The data is read and displayed by the signal handler
2738a407aadSBrian Somers  * itself.
2748a407aadSBrian Somers  */
2758a407aadSBrian Somers static void *
Terminal(void * v)2768a407aadSBrian Somers Terminal(void *v)
2778a407aadSBrian Somers {
2788a407aadSBrian Somers     struct sigaction act, oact;
2798a407aadSBrian Somers     struct thread_data *td;
2808a407aadSBrian Somers     const char *l;
2818a407aadSBrian Somers     int len;
282757eeda0SDavid E. O'Brien #ifndef __OpenBSD__
283af5b1096SBrian Somers     HistEvent hev = { 0, "" };
284af5b1096SBrian Somers #endif
2858a407aadSBrian Somers 
2868a407aadSBrian Somers     act.sa_handler = InputHandler;
2878a407aadSBrian Somers     sigemptyset(&act.sa_mask);
2888a407aadSBrian Somers     act.sa_flags = SA_RESTART;
2898a407aadSBrian Somers     sigaction(SIGUSR1, &act, &oact);
2908a407aadSBrian Somers 
2918a407aadSBrian Somers     td = (struct thread_data *)v;
2928a407aadSBrian Somers     want_sem_post = 1;
2938a407aadSBrian Somers 
2948a407aadSBrian Somers     while ((l = SmartGets(td->edit, &len, td->ppp))) {
2958a407aadSBrian Somers         if (len > 1)
296757eeda0SDavid E. O'Brien #ifdef __OpenBSD__
2978a407aadSBrian Somers             history(td->hist, H_ENTER, l);
298757eeda0SDavid E. O'Brien #else
299757eeda0SDavid E. O'Brien             history(td->hist, &hev, H_ENTER, l);
3008a407aadSBrian Somers #endif
3018a407aadSBrian Somers         write(td->ppp, l, len);
3028a407aadSBrian Somers         if (Receive(td->ppp, REC_SHOW) != 0)
3038a407aadSBrian Somers             break;
3048a407aadSBrian Somers     }
3058a407aadSBrian Somers 
3068a407aadSBrian Somers     return NULL;
3078a407aadSBrian Somers }
3088a407aadSBrian Somers 
3098a407aadSBrian Somers /*
3108a407aadSBrian Somers  * The Monitor thread entry point.
3118a407aadSBrian Somers  *
3128a407aadSBrian Somers  * This thread simply monitors our ppp descriptor.  When there's something
3138a407aadSBrian Somers  * to read, a SIGUSR1 is sent to the Terminal thread.
3148a407aadSBrian Somers  *
3158a407aadSBrian Somers  * sem_select() is used by the Terminal thread to keep us from sending
3168a407aadSBrian Somers  * flurries of SIGUSR1s, and is used from the main thread to wake us up
3178a407aadSBrian Somers  * when it's time to exit.
3188a407aadSBrian Somers  */
3198a407aadSBrian Somers static void *
Monitor(void * v)3208a407aadSBrian Somers Monitor(void *v)
3218a407aadSBrian Somers {
3228a407aadSBrian Somers     struct thread_data *td;
3238a407aadSBrian Somers     fd_set f;
3248a407aadSBrian Somers     int ret;
3258a407aadSBrian Somers 
3268a407aadSBrian Somers     td = (struct thread_data *)v;
3278a407aadSBrian Somers     FD_ZERO(&f);
3288a407aadSBrian Somers     FD_SET(td->ppp, &f);
3298a407aadSBrian Somers 
3308a407aadSBrian Somers     sem_wait(&sem_select);
3318a407aadSBrian Somers     while (!timetogo)
3328a407aadSBrian Somers         if ((ret = select(td->ppp + 1, &f, NULL, NULL, NULL)) > 0) {
3338a407aadSBrian Somers             pthread_kill(td->trm, SIGUSR1);
3348a407aadSBrian Somers             sem_wait(&sem_select);
3358a407aadSBrian Somers         }
3368a407aadSBrian Somers 
3378a407aadSBrian Somers     return NULL;
3388a407aadSBrian Somers }
3398a407aadSBrian Somers 
3408b455488STim J. Robbins static const char *
sockaddr_ntop(const struct sockaddr * sa)3418b455488STim J. Robbins sockaddr_ntop(const struct sockaddr *sa)
3428b455488STim J. Robbins {
3438b455488STim J. Robbins     const void *addr;
3448b455488STim J. Robbins     static char addrbuf[INET6_ADDRSTRLEN];
3458b455488STim J. Robbins 
3468b455488STim J. Robbins     switch (sa->sa_family) {
3478b455488STim J. Robbins     case AF_INET:
3488b455488STim J. Robbins 	addr = &((const struct sockaddr_in *)sa)->sin_addr;
3498b455488STim J. Robbins 	break;
3508b455488STim J. Robbins     case AF_UNIX:
3518b455488STim J. Robbins 	addr = &((const struct sockaddr_un *)sa)->sun_path;
3528b455488STim J. Robbins 	break;
3538b455488STim J. Robbins     case AF_INET6:
3548b455488STim J. Robbins 	addr = &((const struct sockaddr_in6 *)sa)->sin6_addr;
3558b455488STim J. Robbins 	break;
3568b455488STim J. Robbins     default:
3578b455488STim J. Robbins 	return NULL;
3588b455488STim J. Robbins     }
3598b455488STim J. Robbins     inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
3608b455488STim J. Robbins     return addrbuf;
3618b455488STim J. Robbins }
3628b455488STim J. Robbins 
3638a407aadSBrian Somers /*
3648a407aadSBrian Somers  * Connect to ppp using either a local domain socket or a tcp socket.
3658a407aadSBrian Somers  *
3668a407aadSBrian Somers  * If we're given arguments, process them and quit, otherwise create two
3678a407aadSBrian Somers  * threads to handle interactive mode.
3688a407aadSBrian Somers  */
369c957ff40SBrian Somers int
main(int argc,char ** argv)370c957ff40SBrian Somers main(int argc, char **argv)
371c957ff40SBrian Somers {
372c957ff40SBrian Somers     struct sockaddr_un ifsun;
3738b455488STim J. Robbins     int n, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2;
374c957ff40SBrian Somers     unsigned TimeoutVal;
375c957ff40SBrian Somers     char *DoneWord = "x", *next, *start;
376c957ff40SBrian Somers     struct sigaction act, oact;
3778a407aadSBrian Somers     void *thread_ret;
3788a407aadSBrian Somers     pthread_t mon;
3798a407aadSBrian Somers     char Command[LINELEN];
3808a407aadSBrian Somers     char Buffer[LINELEN];
381c957ff40SBrian Somers 
382c957ff40SBrian Somers     verbose = 0;
383c957ff40SBrian Somers     TimeoutVal = 2;
384087a20b8SBrian Somers     hide1 = hide1off = hide2 = 0;
385c957ff40SBrian Somers 
386c957ff40SBrian Somers     for (arg = 1; arg < argc; arg++)
387c957ff40SBrian Somers         if (*argv[arg] == '-') {
388c957ff40SBrian Somers             for (start = argv[arg] + 1; *start; start++)
389c957ff40SBrian Somers                 switch (*start) {
390c957ff40SBrian Somers                     case 't':
391c957ff40SBrian Somers                         TimeoutVal = (unsigned)atoi
392c957ff40SBrian Somers                             (start[1] ? start + 1 : argv[++arg]);
393c957ff40SBrian Somers                         start = DoneWord;
394c957ff40SBrian Somers                         break;
395c957ff40SBrian Somers 
396c957ff40SBrian Somers                     case 'v':
397c957ff40SBrian Somers                         verbose = REC_VERBOSE;
398c957ff40SBrian Somers                         break;
399c957ff40SBrian Somers 
400c957ff40SBrian Somers                     case 'p':
401087a20b8SBrian Somers                         if (start[1]) {
402087a20b8SBrian Somers                           hide1 = arg;
403087a20b8SBrian Somers                           hide1off = start - argv[arg];
404087a20b8SBrian Somers                           passwd = start + 1;
405087a20b8SBrian Somers                         } else {
406087a20b8SBrian Somers                           hide1 = arg;
407087a20b8SBrian Somers                           hide1off = start - argv[arg];
408087a20b8SBrian Somers                           passwd = argv[++arg];
409087a20b8SBrian Somers                           hide2 = arg;
410087a20b8SBrian Somers                         }
411c957ff40SBrian Somers                         start = DoneWord;
412c957ff40SBrian Somers                         break;
413c957ff40SBrian Somers 
414c957ff40SBrian Somers                     default:
415e1e9e152SBrian Somers                         usage();
416c957ff40SBrian Somers                 }
417c957ff40SBrian Somers         }
418c957ff40SBrian Somers         else
419c957ff40SBrian Somers             break;
420c957ff40SBrian Somers 
421c957ff40SBrian Somers 
422fbff0da4SBrian Somers     if (argc < arg + 1)
423e1e9e152SBrian Somers         usage();
424c957ff40SBrian Somers 
425087a20b8SBrian Somers     if (hide1) {
426087a20b8SBrian Somers       char title[1024];
427087a20b8SBrian Somers       int pos, harg;
428087a20b8SBrian Somers 
429087a20b8SBrian Somers       for (harg = pos = 0; harg < argc; harg++)
430087a20b8SBrian Somers         if (harg == 0 || harg != hide2) {
431087a20b8SBrian Somers           if (harg == 0 || harg != hide1)
432ad96855dSBrian Somers             n = snprintf(title + pos, sizeof title - pos, "%s%s",
433087a20b8SBrian Somers                             harg ? " " : "", argv[harg]);
434087a20b8SBrian Somers           else if (hide1off > 1)
435ad96855dSBrian Somers             n = snprintf(title + pos, sizeof title - pos, " %.*s",
436087a20b8SBrian Somers                             hide1off, argv[harg]);
437ad96855dSBrian Somers           else
438ad96855dSBrian Somers             n = 0;
439ad96855dSBrian Somers           if (n < 0 || n >= sizeof title - pos)
440ad96855dSBrian Somers             break;
441ad96855dSBrian Somers           pos += n;
442087a20b8SBrian Somers         }
443087a20b8SBrian Somers #ifdef __FreeBSD__
444087a20b8SBrian Somers       setproctitle("-%s", title);
445087a20b8SBrian Somers #else
446087a20b8SBrian Somers       setproctitle("%s", title);
447087a20b8SBrian Somers #endif
448087a20b8SBrian Somers     }
449087a20b8SBrian Somers 
450c957ff40SBrian Somers     if (*argv[arg] == '/') {
4512e14bb46SBrian Somers         memset(&ifsun, '\0', sizeof ifsun);
452c957ff40SBrian Somers         ifsun.sun_len = strlen(argv[arg]);
453c957ff40SBrian Somers         if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
454e1e9e152SBrian Somers             warnx("%s: path too long", argv[arg]);
455c957ff40SBrian Somers             return 1;
456c957ff40SBrian Somers         }
457c957ff40SBrian Somers         ifsun.sun_family = AF_LOCAL;
458c957ff40SBrian Somers         strcpy(ifsun.sun_path, argv[arg]);
459c957ff40SBrian Somers 
460c957ff40SBrian Somers         if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
461e1e9e152SBrian Somers             warnx("cannot create local domain socket");
462c957ff40SBrian Somers             return 2;
463c957ff40SBrian Somers         }
4648b455488STim J. Robbins 	if (connect(fd, (struct sockaddr *)&ifsun, sizeof(ifsun)) < 0) {
4658b455488STim J. Robbins 	    if (errno)
4668b455488STim J. Robbins 		warn("cannot connect to socket %s", argv[arg]);
4678b455488STim J. Robbins 	    else
4688b455488STim J. Robbins 		warnx("cannot connect to socket %s", argv[arg]);
4698b455488STim J. Robbins 	    close(fd);
4708b455488STim J. Robbins 	    return 3;
471de68f5c5SBrian Somers 	}
4728b455488STim J. Robbins     } else {
4738b455488STim J. Robbins         char *addr, *p, *port;
4748b455488STim J. Robbins 	const char *caddr;
4758b455488STim J. Robbins 	struct addrinfo hints, *res, *pai;
4768b455488STim J. Robbins         int gai;
4778b455488STim J. Robbins 	char local[] = "localhost";
478c957ff40SBrian Somers 
4798b455488STim J. Robbins 	addr = argv[arg];
4808b455488STim J. Robbins 	if (addr[strspn(addr, "0123456789")] == '\0') {
4818b455488STim J. Robbins 	    /* port on local machine */
4828b455488STim J. Robbins 	    port = addr;
4838b455488STim J. Robbins 	    addr = local;
4848b455488STim J. Robbins 	} else if (*addr == '[') {
4858b455488STim J. Robbins 	    /* [addr]:port */
4868b455488STim J. Robbins 	    if ((p = strchr(addr, ']')) == NULL) {
4878b455488STim J. Robbins 		warnx("%s: mismatched '['", addr);
488437b5af6SBrian Somers 		return 1;
489437b5af6SBrian Somers 	    }
4908b455488STim J. Robbins 	    addr++;
4918b455488STim J. Robbins 	    *p++ = '\0';
4928b455488STim J. Robbins 	    if (*p != ':') {
4938b455488STim J. Robbins 		warnx("%s: missing port", addr);
494c957ff40SBrian Somers 		return 1;
495c957ff40SBrian Somers 	    }
4968b455488STim J. Robbins 	    port = ++p;
4978b455488STim J. Robbins 	} else {
4988b455488STim J. Robbins 	    /* addr:port */
4998b455488STim J. Robbins 	    p = addr + strcspn(addr, ":");
5008b455488STim J. Robbins 	    if (*p != ':') {
5018b455488STim J. Robbins 		warnx("%s: missing port", addr);
5028b455488STim J. Robbins 		return 1;
503c957ff40SBrian Somers 	    }
5048b455488STim J. Robbins 	    *p++ = '\0';
5058b455488STim J. Robbins 	    port = p;
506c957ff40SBrian Somers 	}
5078b455488STim J. Robbins 	memset(&hints, 0, sizeof(hints));
5088b455488STim J. Robbins 	hints.ai_socktype = SOCK_STREAM;
5098b455488STim J. Robbins 	gai = getaddrinfo(addr, port, &hints, &res);
5108b455488STim J. Robbins 	if (gai != 0) {
5118b455488STim J. Robbins 	    warnx("%s: %s", addr, gai_strerror(gai));
5128b455488STim J. Robbins 	    return 1;
513c957ff40SBrian Somers 	}
5148b455488STim J. Robbins 	for (pai = res; pai != NULL; pai = pai->ai_next) {
5158b455488STim J. Robbins 	    if (fd = socket(pai->ai_family, pai->ai_socktype,
5168b455488STim J. Robbins 		pai->ai_protocol), fd < 0) {
5178b455488STim J. Robbins 		warnx("cannot create socket");
5188b455488STim J. Robbins 		continue;
5198b455488STim J. Robbins 	    }
520c957ff40SBrian Somers 	    TimedOut = 0;
521c957ff40SBrian Somers 	    if (TimeoutVal) {
522c957ff40SBrian Somers 		act.sa_handler = Timeout;
523c957ff40SBrian Somers 		sigemptyset(&act.sa_mask);
524c957ff40SBrian Somers 		act.sa_flags = 0;
525c957ff40SBrian Somers 		sigaction(SIGALRM, &act, &oact);
526c957ff40SBrian Somers 		alarm(TimeoutVal);
527c957ff40SBrian Somers 	    }
5288b455488STim J. Robbins 	    if (connect(fd, pai->ai_addr, pai->ai_addrlen) == 0)
5298b455488STim J. Robbins 		break;
530c957ff40SBrian Somers 	    if (TimeoutVal) {
531e1e9e152SBrian Somers 		save_errno = errno;
532c957ff40SBrian Somers 		alarm(0);
533c957ff40SBrian Somers 		sigaction(SIGALRM, &oact, 0);
534e1e9e152SBrian Somers 		errno = save_errno;
535c957ff40SBrian Somers 	    }
5368b455488STim J. Robbins 	    caddr = sockaddr_ntop(pai->ai_addr);
5378b455488STim J. Robbins 	    if (caddr == NULL)
5388b455488STim J. Robbins 		caddr = argv[arg];
539e1e9e152SBrian Somers 	    if (TimedOut)
5408b455488STim J. Robbins 		warnx("timeout: cannot connect to %s", caddr);
541e1e9e152SBrian Somers 	    else {
542e1e9e152SBrian Somers 		if (errno)
5438b455488STim J. Robbins 		    warn("cannot connect to %s", caddr);
544e1e9e152SBrian Somers 		else
5458b455488STim J. Robbins 		    warnx("cannot connect to %s", caddr);
546ea567911SBrian Somers 	    }
547c957ff40SBrian Somers 	    close(fd);
548c957ff40SBrian Somers 	}
5498b455488STim J. Robbins 	freeaddrinfo(res);
5508b455488STim J. Robbins 	if (pai == NULL)
5518b455488STim J. Robbins 	    return 1;
552c957ff40SBrian Somers 	if (TimeoutVal) {
553c957ff40SBrian Somers 	    alarm(0);
554c957ff40SBrian Somers 	    sigaction(SIGALRM, &oact, 0);
555c957ff40SBrian Somers 	}
5568b455488STim J. Robbins     }
557c957ff40SBrian Somers 
558c957ff40SBrian Somers     len = 0;
559c957ff40SBrian Somers     Command[sizeof(Command)-1] = '\0';
560c957ff40SBrian Somers     for (arg++; arg < argc; arg++) {
561c957ff40SBrian Somers         if (len && len < sizeof(Command)-1)
562c957ff40SBrian Somers             strcpy(Command+len++, " ");
563c957ff40SBrian Somers         strncpy(Command+len, argv[arg], sizeof(Command)-len-1);
564c957ff40SBrian Somers         len += strlen(Command+len);
565c957ff40SBrian Somers     }
566c957ff40SBrian Somers 
5678a407aadSBrian Somers     switch (Receive(fd, verbose | REC_PASSWD)) {
568c957ff40SBrian Somers         case 1:
569c957ff40SBrian Somers             fprintf(stderr, "Password incorrect\n");
570c957ff40SBrian Somers             break;
571c957ff40SBrian Somers 
572c957ff40SBrian Somers         case 0:
5738a407aadSBrian Somers             passwd = NULL;
574fbff0da4SBrian Somers             if (len == 0) {
5758a407aadSBrian Somers                 struct thread_data td;
5768a407aadSBrian Somers                 const char *env;
5777d0a1bacSBrian Somers                 int size;
578757eeda0SDavid E. O'Brien #ifndef __OpenBSD__
579af5b1096SBrian Somers                 HistEvent hev = { 0, "" };
580af5b1096SBrian Somers #endif
5817d0a1bacSBrian Somers 
5828a407aadSBrian Somers                 td.hist = history_init();
5837d0a1bacSBrian Somers                 if ((env = getenv("EL_SIZE"))) {
5847d0a1bacSBrian Somers                     size = atoi(env);
5857d0a1bacSBrian Somers                     if (size < 0)
5867d0a1bacSBrian Somers                       size = 20;
5877d0a1bacSBrian Somers                 } else
5887d0a1bacSBrian Somers                     size = 20;
589757eeda0SDavid E. O'Brien #ifdef __OpenBSD__
5908a407aadSBrian Somers                 history(td.hist, H_EVENT, size);
5918a407aadSBrian Somers                 td.edit = el_init("pppctl", stdin, stdout);
592757eeda0SDavid E. O'Brien #else
593757eeda0SDavid E. O'Brien                 history(td.hist, &hev, H_SETSIZE, size);
594757eeda0SDavid E. O'Brien                 td.edit = el_init("pppctl", stdin, stdout, stderr);
595e83358b4SBrian Somers #endif
5968a407aadSBrian Somers                 el_source(td.edit, NULL);
5978a407aadSBrian Somers                 el_set(td.edit, EL_PROMPT, GetPrompt);
59823e877a8SBrian Somers                 if ((env = getenv("EL_EDITOR"))) {
5997d0a1bacSBrian Somers                     if (!strcmp(env, "vi"))
6008a407aadSBrian Somers                         el_set(td.edit, EL_EDITOR, "vi");
6017d0a1bacSBrian Somers                     else if (!strcmp(env, "emacs"))
6028a407aadSBrian Somers                         el_set(td.edit, EL_EDITOR, "emacs");
60323e877a8SBrian Somers                 }
6048a407aadSBrian Somers                 el_set(td.edit, EL_SIGNAL, 1);
6058a407aadSBrian Somers                 el_set(td.edit, EL_HIST, history, (const char *)td.hist);
6068a407aadSBrian Somers 
6078a407aadSBrian Somers                 td.ppp = fd;
6088a407aadSBrian Somers                 td.trm = NULL;
6098a407aadSBrian Somers 
6108a407aadSBrian Somers                 /*
6118a407aadSBrian Somers                  * We create two threads.  The Terminal thread does all the
6128a407aadSBrian Somers                  * work while the Monitor thread simply tells the Terminal
6138a407aadSBrian Somers                  * thread when ``fd'' becomes readable.  The telling is done
6148a407aadSBrian Somers                  * by sending a SIGUSR1 to the Terminal thread.  The
6158a407aadSBrian Somers                  * sem_select semaphore is used to prevent the monitor
6168a407aadSBrian Somers                  * thread from firing excessive signals at the Terminal
6178a407aadSBrian Somers                  * thread (it's abused for exit handling too - see below).
6188a407aadSBrian Somers                  *
6198a407aadSBrian Somers                  * The Terminal thread never uses td.trm !
6208a407aadSBrian Somers                  */
6218a407aadSBrian Somers                 sem_init(&sem_select, 0, 0);
6228a407aadSBrian Somers 
6238a407aadSBrian Somers                 pthread_create(&td.trm, NULL, Terminal, &td);
6248a407aadSBrian Somers                 pthread_create(&mon, NULL, Monitor, &td);
6258a407aadSBrian Somers 
6268a407aadSBrian Somers                 /* Wait for the terminal thread to finish */
6278a407aadSBrian Somers                 pthread_join(td.trm, &thread_ret);
628fbff0da4SBrian Somers                 fprintf(stderr, "Connection closed\n");
6298a407aadSBrian Somers 
6308a407aadSBrian Somers                 /* Get rid of the monitor thread by abusing sem_select */
6318a407aadSBrian Somers                 timetogo = 1;
6328a407aadSBrian Somers                 close(fd);
6338a407aadSBrian Somers                 fd = -1;
6348a407aadSBrian Somers                 sem_post(&sem_select);
6358a407aadSBrian Somers                 pthread_join(mon, &thread_ret);
6368a407aadSBrian Somers 
6378a407aadSBrian Somers                 /* Restore our terminal and release resources */
6388a407aadSBrian Somers                 el_end(td.edit);
6398a407aadSBrian Somers                 history_end(td.hist);
6408a407aadSBrian Somers                 sem_destroy(&sem_select);
641fbff0da4SBrian Somers             } else {
642c957ff40SBrian Somers                 start = Command;
643c957ff40SBrian Somers                 do {
6447d0a1bacSBrian Somers                     next = strchr(start, ';');
645c957ff40SBrian Somers                     while (*start == ' ' || *start == '\t')
646c957ff40SBrian Somers                         start++;
647c957ff40SBrian Somers                     if (next)
648c957ff40SBrian Somers                         *next = '\0';
649c957ff40SBrian Somers                     strcpy(Buffer, start);
650c957ff40SBrian Somers                     Buffer[sizeof(Buffer)-2] = '\0';
651c957ff40SBrian Somers                     strcat(Buffer, "\n");
652c957ff40SBrian Somers                     if (verbose)
653e1b4d8d0SSheldon Hearn                         write(STDOUT_FILENO, Buffer, strlen(Buffer));
654c957ff40SBrian Somers                     write(fd, Buffer, strlen(Buffer));
655f3a51f7aSBrian Somers                     if (Receive(fd, verbose | REC_SHOW) != 0) {
65631746f55SBrian Somers                         fprintf(stderr, "Connection closed\n");
657c957ff40SBrian Somers                         break;
658c957ff40SBrian Somers                     }
659c957ff40SBrian Somers                     if (next)
660c957ff40SBrian Somers                         start = ++next;
661c957ff40SBrian Somers                 } while (next && *next);
662c957ff40SBrian Somers                 if (verbose)
663e1b4d8d0SSheldon Hearn                     write(STDOUT_FILENO, "quit\n", 5);
6642da5df67SBrian Somers                 write(fd, "quit\n", 5);
6652da5df67SBrian Somers                 while (Receive(fd, verbose | REC_SHOW) == 0)
6662da5df67SBrian Somers                     ;
6672da5df67SBrian Somers                 if (verbose)
668c957ff40SBrian Somers                     puts("");
669fbff0da4SBrian Somers             }
670c957ff40SBrian Somers             break;
671c957ff40SBrian Somers 
672c957ff40SBrian Somers         default:
673e1e9e152SBrian Somers             warnx("ppp is not responding");
674c957ff40SBrian Somers             break;
675c957ff40SBrian Somers     }
676c957ff40SBrian Somers 
6778a407aadSBrian Somers     if (fd != -1)
678c957ff40SBrian Somers         close(fd);
679c957ff40SBrian Somers 
680c957ff40SBrian Somers     return 0;
681c957ff40SBrian Somers }
682