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