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