Merge branch 'vendor/FILE'
[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  */
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 }