xref: /dragonfly/sbin/startslip/startslip.c (revision 52f9f0d9)
1 /*-
2  * Copyright (c) 1990, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1990, 1991, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)startslip.c	8.1 (Berkeley) 6/5/93
35  * $FreeBSD: src/sbin/startslip/startslip.c,v 1.31.2.1 2000/05/07 18:26:51 joe Exp $
36  */
37 
38 #include <sys/types.h>
39 #include <sys/time.h>
40 
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <libutil.h>
45 #include <paths.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <termios.h>
52 #include <unistd.h>
53 
54 #include <net/slip.h>
55 
56 #define DEFAULT_BAUD    B9600
57 int     speed = DEFAULT_BAUD;
58 #define	FC_NONE		0	/* flow control: none */
59 #define FC_HW           1       /* flow control: hardware (RTS/CTS) */
60 int	flowcontrol = FC_NONE;
61 int     modem_control = 1;      /* !CLOCAL+HUPCL iff we watch carrier. */
62 int     sl_unit = -1;
63 int     uucp_lock = 0;          /* uucp locking */
64 char	*annex;
65 char    *username;
66 int	hup;
67 int     terminate;
68 int     locked = 0;             /* uucp lock active */
69 int	logged_in = 0;
70 int	wait_time = 60;		/* then back off */
71 int     script_timeout = 90;    /* connect script default timeout */
72 time_t  conn_time, start_time;
73 int     MAXTRIES = 6;           /* w/60 sec and doubling, takes an hour */
74 #define PIDFILE         "%sstartslip.%s.pid"
75 
76 #define MAXDIALS 20
77 char *dials[MAXDIALS];
78 int diali, dialc;
79 
80 int fd = -1;
81 FILE *pfd;
82 char *dvname, *devicename;
83 char my_pidfile[80];
84 
85 #ifdef DEBUG
86 int	debug = 1;
87 #undef LOG_ERR
88 #undef LOG_INFO
89 #define syslog fprintf
90 #define LOG_ERR stderr
91 #define LOG_INFO stderr
92 #else
93 int	debug = 0;
94 #endif
95 #define	printd	if (debug) printf
96 
97 static int carrier(void);
98 static void down(int);
99 static int getline(char *, int, int, time_t);
100 static void usage(void);
101 static void sighup(int);
102 static void sigterm(int);
103 static void sigurg(int);
104 
105 int
106 main(int argc, char **argv)
107 {
108 	char *cp, **ap;
109 	int ch, disc;
110 	FILE *wfd = NULL;
111 	char *dialerstring = NULL, buf[BUFSIZ];
112 	int unitnum, keepal = 0, outfill = 0;
113 	char unitname[32];
114 	char *password;
115 	char *upscript = NULL, *downscript = NULL;
116 	int first = 1, tries = 0;
117 	time_t fintimeout;
118 	long lpid;
119 	pid_t pid;
120 	struct termios t;
121 	int result;
122 
123 	while ((ch = getopt(argc, argv, "dhlb:s:t:w:A:U:D:W:K:O:S:L")) != -1)
124 		switch (ch) {
125 		case 'd':
126 			debug = 1;
127 			break;
128 		case 'b':
129 			speed = atoi(optarg);
130 			break;
131 		case 's':
132 			if (diali >= MAXDIALS)
133 				errx(1, "max dial strings number (%d) exceeded", MAXDIALS);
134 			dials[diali++] = strdup(optarg);
135 			break;
136 		case 't':
137 			script_timeout = atoi(optarg);
138 			break;
139 		case 'w':
140 			wait_time = atoi(optarg);
141 			break;
142 		case 'W':
143 			MAXTRIES = atoi(optarg);
144 			break;
145 		case 'A':
146 			annex = strdup(optarg);
147 			break;
148 		case 'U':
149 			upscript = strdup(optarg);
150 			break;
151 		case 'D':
152 			downscript = strdup(optarg);
153 			break;
154 		case 'L':
155 			uucp_lock = 1;
156 			break;
157 		case 'l':
158 			modem_control = 0;
159 			break;
160 		case 'h':
161 			flowcontrol = FC_HW;
162 			break;
163 		case 'K':
164 			keepal = atoi(optarg);
165 			break;
166 		case 'O':
167 			outfill = atoi(optarg);
168 			break;
169 		case 'S':
170 			sl_unit = atoi(optarg);
171 			break;
172 		case '?':
173 		default:
174 			usage();
175 		}
176 	argc -= optind;
177 	argv += optind;
178 
179 	if (argc != 3)
180 		usage();
181 
182 	/*
183 	 * Copy these so they exist after we clobber them.
184 	 */
185 	devicename = strdup(argv[0]);
186 	username = strdup(argv[1]);
187 	password = strdup(argv[2]);
188 
189 	/*
190 	 * Security hack.  Do not want private information such as the
191 	 * password and possible phone number to be left around.
192 	 * So we clobber the arguments.
193 	 */
194 	for (ap = argv - optind + 1; ap < argv + 3; ap++)
195 		for (cp = *ap; *cp != 0; cp++)
196 			*cp = '\0';
197 
198 	openlog("startslip", LOG_PID|LOG_PERROR, LOG_DAEMON);
199 
200 	if (debug)
201 		setbuf(stdout, NULL);
202 
203 	signal(SIGTERM, sigterm);
204 	if ((dvname = strrchr(devicename, '/')) == NULL)
205 		dvname = devicename;
206 	else
207 		dvname++;
208 
209 	result = snprintf(my_pidfile, sizeof(my_pidfile),
210 			  PIDFILE, _PATH_VARRUN, dvname);
211 	if (result < 0 || (unsigned int)result >= sizeof(my_pidfile))
212 		usage();
213 
214 	if ((pfd = fopen(my_pidfile, "r")) != NULL) {
215 		if (fscanf(pfd, "%ld\n", &lpid) == 1) {
216 			pid = lpid;
217 			if (pid == lpid && pid > 0)
218 				kill(pid, SIGTERM);
219 		}
220 		fclose(pfd);
221 		pfd = NULL;     /* not remove pidfile yet */
222 		sleep(5);       /* allow down script to be completed */
223 	} else
224 restart:
225 	signal(SIGHUP, SIG_IGN);
226 	signal(SIGURG, SIG_IGN);
227 	hup = 0;
228 	if (wfd) {
229 		printd("fclose, ");
230 		fclose(wfd);
231 		conn_time = time(NULL) - start_time;
232 		if (uucp_lock)
233 			uu_unlock(dvname);
234 		locked = 0;
235 		wfd = NULL;
236 		fd = -1;
237 		sleep(5);
238 	} else if (fd >= 0) {
239 		printd("close, ");
240 		close(fd);
241 		conn_time = time(NULL) - start_time;
242 		if (uucp_lock)
243 			uu_unlock(dvname);
244 		locked = 0;
245 		fd = -1;
246 		sleep(5);
247 	}
248 	if (logged_in) {
249 		syslog(LOG_INFO, "%s: connection time elapsed: %ld secs",
250 		    username, (long)conn_time);
251 		sprintf(buf, "LINE=%d %s %s down",
252 		diali ? (dialc - 1) % diali : 0,
253 		downscript ? downscript : "/sbin/ifconfig" , unitname);
254 		system(buf);
255 		logged_in = 0;
256 	}
257 	if (terminate)
258 		down(0);
259 	tries++;
260 	if (MAXTRIES > 0 && tries > MAXTRIES) {
261 		syslog(LOG_ERR, "%s: exiting login after %d tries", username, tries);
262 		/* ???
263 		if (first)
264 		*/
265 			down(3);
266 	}
267 	if (tries > 1) {
268 		syslog(LOG_INFO, "%s: sleeping %d seconds (%d tries)",
269 			username, wait_time * (tries - 1), tries);
270 		sleep(wait_time * (tries - 1));
271 		if (terminate)
272 			goto restart;
273 	}
274 
275 	if (daemon(1, debug) < 0) {
276 		syslog(LOG_ERR, "%s: daemon: %m", username);
277 		down(2);
278 	}
279 
280 	pid = getpid();
281 	printd("restart: pid %ld: ", (long)pid);
282 	if ((pfd = fopen(my_pidfile, "w")) != NULL) {
283 		fprintf(pfd, "%ld\n", (long)pid);
284 		fclose(pfd);
285 	}
286 	printd("open");
287 	if (uucp_lock) {
288 		int res;
289 		if ((res = uu_lock(dvname)) != UU_LOCK_OK) {
290 			if (res != UU_LOCK_INUSE)
291 				syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res));
292 			syslog(LOG_ERR, "%s: can't lock %s", username, devicename);
293 			goto restart;
294 		}
295 		locked = 1;
296 	}
297 	if ((fd = open(devicename, O_RDWR | O_NONBLOCK)) < 0) {
298 		syslog(LOG_ERR, "%s: open %s: %m", username, devicename);
299 		if (first)
300 			down(1);
301 		else {
302 			if (uucp_lock)
303 				uu_unlock(dvname);
304 			locked = 0;
305 			goto restart;
306 		}
307 	}
308 	printd(" %d", fd);
309 	signal(SIGHUP, sighup);
310 	if (ioctl(fd, TIOCSCTTY, 0) < 0) {
311 		syslog(LOG_ERR, "%s: ioctl (TIOCSCTTY): %m", username);
312 		down(2);
313 	}
314 	if (tcsetpgrp(fd, getpid()) < 0) {
315 		syslog(LOG_ERR, "%s: tcsetpgrp failed: %m", username);
316 		down(2);
317 	}
318 	printd(", ioctl\n");
319 	if (tcgetattr(fd, &t) < 0) {
320 		syslog(LOG_ERR, "%s: tcgetattr(%s): %m", username, devicename);
321 		down(2);
322 	}
323 	cfmakeraw(&t);
324 	switch (flowcontrol) {
325 	case FC_HW:
326 		t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW);
327 		break;
328 	case FC_NONE:
329 		t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW);
330 		break;
331 	}
332 	if (modem_control)
333 		t.c_cflag |= HUPCL;
334 	else
335 		t.c_cflag &= ~(HUPCL);
336 	t.c_cflag |= CLOCAL;    /* until modem commands passes */
337 	cfsetispeed(&t, speed);
338 	cfsetospeed(&t, speed);
339 	if (tcsetattr(fd, TCSAFLUSH, &t) < 0) {
340 		syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
341 		down(2);
342 	}
343 	sleep(2);		/* wait for flakey line to settle */
344 	if (hup || terminate)
345 		goto restart;
346 
347 	wfd = fdopen(fd, "w+");
348 	if (wfd == NULL) {
349 		syslog(LOG_ERR, "%s: can't fdopen %s: %m", username, devicename);
350 		down(2);
351 	}
352 	setbuf(wfd, NULL);
353 
354 	if (diali > 0)
355 		dialerstring = dials[dialc++ % diali];
356 	if (dialerstring) {
357 		syslog(LOG_INFO, "%s: dialer string: %s\\r", username, dialerstring);
358 		fprintf(wfd, "%s\r", dialerstring);
359 	}
360 	printd("\n");
361 
362 	fintimeout = time(NULL) + script_timeout;
363 	if (modem_control) {
364 		printd("waiting for carrier\n");
365 		while (time(NULL) < fintimeout && !carrier()) {
366 			sleep(1);
367 			if (hup || terminate)
368 				goto restart;
369 		}
370 		if (!carrier())
371 			goto restart;
372 		t.c_cflag &= ~(CLOCAL);
373 		if (tcsetattr(fd, TCSANOW, &t) < 0) {
374 			syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename);
375 			down(2);
376 		}
377 		/* Only now we able to receive HUP on carrier drop! */
378 	}
379 
380 	/*
381 	 * Log in
382 	 */
383 	printd("look for login: ");
384 	for (;;) {
385 		if (getline(buf, BUFSIZ, fd, fintimeout) == 0 || hup || terminate)
386 			goto restart;
387 		if (annex) {
388 			if (bcmp(buf, annex, strlen(annex)) == 0) {
389 				fprintf(wfd, "slip\r");
390 				printd("Sent \"slip\"\n");
391 				continue;
392 			}
393 			if (bcmp(&buf[1], "sername:", 8) == 0) {
394 				fprintf(wfd, "%s\r", username);
395 				printd("Sent login: %s\n", username);
396 				continue;
397 			}
398 			if (bcmp(&buf[1], "assword:", 8) == 0) {
399 				fprintf(wfd, "%s\r", password);
400 				printd("Sent password: %s\n", password);
401 				break;
402 			}
403 		} else {
404 			if (strstr(&buf[1], "ogin:") != NULL) {
405 				fprintf(wfd, "%s\r", username);
406 				printd("Sent login: %s\n", username);
407 				continue;
408 			}
409 			if (strstr(&buf[1], "assword:") != NULL) {
410 				fprintf(wfd, "%s\r", password);
411 				printd("Sent password: %s\n", password);
412 				break;
413 			}
414 		}
415 	}
416 
417 	sleep(5);       /* Wait until login completed */
418 	if (hup || terminate)
419 		goto restart;
420 	start_time = time(NULL);
421 	/*
422 	 * Attach
423 	 */
424 	printd("setd");
425 	disc = SLIPDISC;
426 	if (ioctl(fd, TIOCSETD, &disc) < 0) {
427 		syslog(LOG_ERR, "%s: ioctl (%s, TIOCSETD): %m",
428 		    username, devicename);
429 		down(2);
430 	}
431 	if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) {
432 		syslog(LOG_ERR, "%s: ioctl(SLIOCSUNIT): %m", username);
433 		down(2);
434 	}
435 	if (ioctl(fd, SLIOCGUNIT, &unitnum) < 0) {
436 		syslog(LOG_ERR, "%s: ioctl(SLIOCGUNIT): %m", username);
437 		down(2);
438 	}
439 	sprintf(unitname, "sl%d", unitnum);
440 
441 	if (keepal > 0) {
442 		signal(SIGURG, sigurg);
443 		if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) {
444 			syslog(LOG_ERR, "%s: ioctl(SLIOCSKEEPAL): %m", username);
445 			down(2);
446 		}
447 	}
448 	if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) {
449 		syslog(LOG_ERR, "%s: ioctl(SLIOCSOUTFILL): %m", username);
450 		down(2);
451 	}
452 
453 	sprintf(buf, "LINE=%d %s %s up",
454 		diali ? (dialc - 1) % diali : 0,
455 		upscript ? upscript : "/sbin/ifconfig" , unitname);
456 	system(buf);
457 
458 	printd(", ready\n");
459 	if (!first)
460 		syslog(LOG_INFO, "%s: reconnected on %s (%d tries)", username, unitname, tries);
461 	else
462 		syslog(LOG_INFO, "%s: connected on %s", username, unitname);
463 	first = 0;
464 	tries = 0;
465 	logged_in = 1;
466 	while (hup == 0 && terminate == 0) {
467 		sigpause(0L);
468 		printd("sigpause return\n");
469 	}
470 	goto restart;
471 	return(0); /* not reached */
472 }
473 
474 static void
475 sighup(int signo __unused)
476 {
477 	printd("hup\n");
478 	if (hup == 0 && logged_in)
479 		syslog(LOG_INFO, "%s: got hangup signal", username);
480 	hup = 1;
481 }
482 
483 static void
484 sigurg(int signo __unused)
485 {
486 	printd("urg\n");
487 	if (hup == 0 && logged_in)
488 		syslog(LOG_INFO, "%s: got dead line signal", username);
489 	hup = 1;
490 }
491 
492 static void
493 sigterm(int signo __unused)
494 {
495 	printd("terminate\n");
496 	if (terminate == 0 && logged_in)
497 		syslog(LOG_INFO, "%s: got terminate signal", username);
498 	terminate = 1;
499 }
500 
501 static int
502 getline(char *buf, int size, int this_fd, time_t fintimeout)
503 {
504 	int i;
505 	int ret;
506 	fd_set readfds;
507 	struct timeval tv;
508 	time_t timeout;
509 
510 	size--;
511 	for (i = 0; i < size; i++) {
512 		if (hup || terminate)
513 			return (0);
514 		if ((timeout = fintimeout - time(NULL)) <= 0)
515 			goto tout;
516 		FD_ZERO(&readfds);
517 		FD_SET(fd, &readfds);
518 		tv.tv_sec = timeout;
519 		tv.tv_usec = 0;
520 		if ((ret = select(this_fd + 1, &readfds, NULL, NULL, &tv)) < 0) {
521 			if (errno != EINTR)
522 				syslog(LOG_ERR, "%s: getline: select: %m", username);
523 		} else {
524 			if (! ret) {
525 			tout:
526 				printd("getline: timed out\n");
527 				return (0);
528 			}
529 			if ((ret = read(this_fd, &buf[i], 1)) == 1) {
530 				buf[i] &= 0177;
531 				if (buf[i] == '\r' || buf[i] == '\0') {
532 					i--;
533 					continue;
534 				}
535 				if (buf[i] != '\n' && buf[i] != ':')
536 					continue;
537 				buf[i + 1] = '\0';
538 				printd("Got %d: %s", i + 1, buf);
539 				return (i+1);
540 			}
541 			if (ret <= 0) {
542 				if (ret < 0) {
543 					syslog(LOG_ERR, "%s: getline: read: %m", username);
544 				} else
545 					syslog(LOG_ERR, "%s: read returned 0", username);
546 				buf[i] = '\0';
547 				printd("returning %d after %d: %s\n", ret, i, buf);
548 				return (0);
549 			}
550 		}
551 	}
552 	return (0);
553 }
554 
555 static int
556 carrier(void)
557 {
558 	int comstate;
559 
560 	if (ioctl(fd, TIOCMGET, &comstate) < 0) {
561 		syslog(LOG_ERR, "%s: ioctl (%s, TIOCMGET): %m",
562 		    username, devicename);
563 		down(2);
564 	}
565 	return !!(comstate & TIOCM_CD);
566 }
567 
568 static void
569 down(int code)
570 {
571 	if (fd > -1)
572 		close(fd);
573 	if (pfd)
574 		unlink(my_pidfile);
575 	if (uucp_lock && locked)
576 		uu_unlock(dvname);
577 	exit(code);
578 }
579 
580 static void
581 usage(void)
582 {
583 	fprintf(stderr, "%s\n%s\n%s\n%s\n",
584 "usage: startslip [-d] [-b speed] [-s string1 [-s string2 [...]]] [-h] [-l]",
585 "                 [-L] [-A annexname] [-U upscript] [-D downscript]",
586 "                 [-t script_timeout] [-W maxtries] [-w retry_pause]",
587 "                 [-K keepalive] [-O outfill] [-S unit] device user passwd");
588 	exit(1);
589 }
590