b60ae34eb95742f588401dc542708faff706a080
[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  * @(#) 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.3 2003/09/28 14:39:22 hmp 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 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 __P((void));
99 void down __P((int));
100 int getline __P((char *, int, int, time_t));
101 static void usage __P((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(pidfile, sizeof(pidfile), PIDFILE, _PATH_VARRUN, dvname) >= sizeof(pidfile))
207                 usage();
208
209         if ((pfd = fopen(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(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(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 }