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