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