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