Merge branch 'vendor/TNFTP'
[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.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 }