ready for net-tools 1.54...
[platform/upstream/net-tools.git] / slattach.c
1 /*
2  * slattach     A program for handling dialup IP connecions.
3  *              This program forces a TTY line to go into a special
4  *              terminal line discipline, so that it can be used for
5  *              network traffic instead of the regular terminal I/O.
6  *
7  * Usage:       slattach [-ehlmnqv] [ -k keepalive ] [ -o outfill ]
8  *                      [-c cmd] [-s speed] [-p protocol] tty | -
9  *
10  * Version:     @(#)slattach.c  1.20  1999-05-29
11  *
12  * Author:      Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
13  *              Copyright 1988-1993 MicroWalt Corporation
14  *
15  * Modified:
16  *              Alan Cox, <A.Cox@swansea.ac.uk> , July 16 1994
17  *              Miquel van Smoorenburg, <miquels@drinkel.ow.org>, October 1994
18  *              George Shearer, <gshearer@one.net>, January 3, 1995
19  *              Yossi Gottlieb, <yogo@math.tau.ac.il>, February 11, 1995
20  *              Peter Tobias, <tobias@et-inf.fho-emden.de>, July 30 1995
21  *              Bernd Eckenfels <net-tools@lina.inka.de>, May 29, 1999
22  *                      added some more printf's for debug and NOBLOCK to open
23  *                      this should be enough to support 2.2 ttyS-style locks
24  *
25  *              This program is free software; you can redistribute it
26  *              and/or  modify it under  the terms of  the GNU General
27  *              Public  License as  published  by  the  Free  Software
28  *              Foundation;  either  version 2 of the License, or  (at
29  *              your option) any later version.
30  */
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <pwd.h>
42 #include <signal.h>
43 #include <stdlib.h>          
44 #include <string.h>
45 #include <unistd.h>
46 #include <getopt.h>
47 #include <linux/if_slip.h>
48
49 #if defined(__GLIBC__)
50 #if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
51 # include <termbits.h>
52 #else
53 # include <termios.h>
54 #endif
55 #endif
56
57 #include "pathnames.h"
58 #include "net-support.h"
59 #include "version.h"
60 #include "config.h"
61 #include "intl.h"
62 #include "util.h"
63
64 #ifndef _PATH_LOCKD
65 #define _PATH_LOCKD             "/var/lock"             /* lock files   */
66 #endif
67 #ifndef _UID_UUCP
68 #define _UID_UUCP               "uucp"                  /* owns locks   */
69 #endif
70
71
72 #define DEF_PROTO       "cslip"
73
74
75 const char *Release = RELEASE,
76            *Version = "@(#) slattach 1.21 (1999-11-21)",
77            *Signature = "net-tools, Fred N. van Kempen et al.";
78
79
80 struct {
81   const char    *speed;
82   int   code;
83 } tty_speeds[] = {                      /* table of usable baud rates   */
84   { "50",       B50     }, { "75",      B75     },      
85   { "110",      B110    }, { "300",     B300    },
86   { "600",      B600    }, { "1200",    B1200   },
87   { "2400",     B2400   }, { "4800",    B4800   },
88   { "9600",     B9600   },
89 #ifdef B14400
90   { "14400",    B14400  },
91 #endif
92 #ifdef B19200
93   { "19200",    B19200  },
94 #endif
95 #ifdef B38400
96   { "38400",    B38400  },
97 #endif
98 #ifdef B57600
99   { "57600",    B57600  },
100 #endif
101 #ifdef B115200
102   { "115200",   B115200 },
103 #endif
104   { NULL,       0       }
105 };
106 struct termios  tty_saved,              /* saved TTY device state       */
107                 tty_current;            /* current TTY device state     */
108 int             tty_sdisc,              /* saved TTY line discipline    */
109                 tty_ldisc,              /* current TTY line discipline  */
110                 tty_fd = -1;            /* TTY file descriptor          */
111 int             opt_c = 0;              /* "command" to run at exit     */
112 int             opt_e = 0;              /* "activate only" flag         */
113 int             opt_h = 0;              /* "hangup" on carrier loss     */
114 #ifdef SIOCSKEEPALIVE
115 int             opt_k = 0;              /* "keepalive" value            */
116 #endif
117 int             opt_l = 0;              /* "lock it" flag               */
118 int             opt_L = 0;              /* clocal flag                  */
119 int             opt_m = 0;              /* "set RAW mode" flag          */
120 int             opt_n = 0;              /* "set No Mesg" flag           */
121 #ifdef SIOCSOUTFILL
122 int             opt_o = 0;              /* "outfill" value              */
123 #endif
124 int             opt_q = 0;              /* "quiet" flag                 */
125 int             opt_d = 0;              /* debug flag                   */
126 int             opt_v = 0;              /* Verbose flag                 */
127
128 /* Disable any messages to the input channel of this process. */
129 static int
130 tty_nomesg(int fd)
131 {
132   if (opt_n == 0) return(0);
133   return(fchmod(fd, 0600));
134 }
135
136 /* Check for an existing lock file on our device */
137 static int
138 tty_already_locked(char *nam)
139 {
140   int  i = 0, pid = 0;
141   FILE *fd = (FILE *)0;
142
143   /* Does the lock file on our device exist? */
144   if ((fd = fopen(nam, "r")) == (FILE *)0)
145     return(0); /* No, return perm to continue */
146
147   /* Yes, the lock is there.  Now let's make sure */
148   /* at least there's no active process that owns */
149   /* that lock.                                   */
150   i = fscanf(fd, "%d", &pid);
151   (void) fclose(fd);
152  
153   if (i != 1) /* Lock file format's wrong! Kill't */
154     return(0);
155
156   /* We got the pid, check if the process's alive */
157   if (kill(pid, 0) == 0)      /* it found process */
158       return(1);          /* Yup, it's running... */
159
160   /* Dead, we can proceed locking this device...  */
161   return(0);
162 }
163
164 /* Lock or unlock a terminal line. */
165 static int
166 tty_lock(char *path, int mode)
167 {
168   static char saved_path[PATH_MAX];
169   static int saved_lock = 0;
170   struct passwd *pw;
171   int fd;
172   char apid[16];
173
174   /* We do not lock standard input. */
175   if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
176
177   if (mode == 1) {      /* lock */
178         sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
179         if (tty_already_locked(saved_path)) {
180                 fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
181                 return(-1);
182         }
183         if ((fd = creat(saved_path, 0644)) < 0) {
184                 if (errno != EEXIST)
185                         if (opt_q == 0) fprintf(stderr,
186                                 _("slattach: tty_lock: (%s): %s\n"),
187                                         saved_path, strerror(errno));
188                 return(-1);
189         }
190         sprintf(apid, "%10d\n", getpid());
191         if (write(fd, apid, strlen(apid)) != strlen(apid)) {
192                 fprintf(stderr, _("slattach: cannot write PID file\n"));
193                 close(fd);
194                 unlink(saved_path);
195                 return(-1);
196         }
197
198         (void) close(fd);
199
200         /* Make sure UUCP owns the lockfile.  Required by some packages. */
201         if ((pw = getpwnam(_UID_UUCP)) == NULL) {
202                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
203                                         _UID_UUCP);
204                 return(0);      /* keep the lock anyway */
205         }
206         (void) chown(saved_path, pw->pw_uid, pw->pw_gid);
207         saved_lock = 1;
208   } else {      /* unlock */
209         if (saved_lock != 1) return(0);
210         if (unlink(saved_path) < 0) {
211                 if (opt_q == 0) fprintf(stderr,
212                         "slattach: tty_unlock: (%s): %s\n", saved_path,
213                                                         strerror(errno));
214                 return(-1);
215         }
216         saved_lock = 0;
217   }
218
219   return(0);
220 }
221
222
223 /* Find a serial speed code in the table. */
224 static int
225 tty_find_speed(const char *speed)
226 {
227   int i;
228
229   i = 0;
230   while (tty_speeds[i].speed != NULL) {
231         if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
232         i++;
233   }
234   return(-EINVAL);
235 }
236
237
238 /* Set the number of stop bits. */
239 static int
240 tty_set_stopbits(struct termios *tty, char *stopbits)
241 {
242   if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
243   switch(*stopbits) {
244         case '1':
245                 tty->c_cflag &= ~CSTOPB;
246                 break;
247
248         case '2':
249                 tty->c_cflag |= CSTOPB;
250                 break;
251
252         default:
253                 return(-EINVAL);
254   }
255   return(0);
256 }
257
258
259 /* Set the number of data bits. */
260 static int
261 tty_set_databits(struct termios *tty, char *databits)
262 {
263   if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
264   tty->c_cflag &= ~CSIZE;
265   switch(*databits) {
266         case '5':
267                 tty->c_cflag |= CS5;
268                 break;
269
270         case '6':
271                 tty->c_cflag |= CS6;
272                 break;
273
274         case '7':
275                 tty->c_cflag |= CS7;
276                 break;
277
278         case '8':
279                 tty->c_cflag |= CS8;
280                 break;
281
282         default:
283                 return(-EINVAL);
284   }
285   return(0);
286 }
287
288
289 /* Set the type of parity encoding. */
290 static int
291 tty_set_parity(struct termios *tty, char *parity)
292 {
293   if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
294   switch(toupper(*parity)) {
295         case 'N':
296                 tty->c_cflag &= ~(PARENB | PARODD);
297                 break;  
298
299         case 'O':
300                 tty->c_cflag &= ~(PARENB | PARODD);
301                 tty->c_cflag |= (PARENB | PARODD);
302                 break;
303
304         case 'E':
305                 tty->c_cflag &= ~(PARENB | PARODD);
306                 tty->c_cflag |= (PARENB);
307                 break;
308
309         default:
310                 return(-EINVAL);
311   }
312   return(0);
313 }
314
315
316 /* Set the line speed of a terminal line. */
317 static int
318 tty_set_speed(struct termios *tty, const char *speed)
319 {
320   int code;
321
322   if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
323   if ((code = tty_find_speed(speed)) < 0) return(code);
324   tty->c_cflag &= ~CBAUD;
325   tty->c_cflag |= code;
326   return(0);
327 }
328
329
330 /* Put a terminal line in a transparent state. */
331 static int
332 tty_set_raw(struct termios *tty)
333 {
334   int i;
335   int speed;
336
337   for(i = 0; i < NCCS; i++)
338                 tty->c_cc[i] = '\0';            /* no spec chr          */
339   tty->c_cc[VMIN] = 1;
340   tty->c_cc[VTIME] = 0;
341   tty->c_iflag = (IGNBRK | IGNPAR);             /* input flags          */
342   tty->c_oflag = (0);                           /* output flags         */
343   tty->c_lflag = (0);                           /* local flags          */
344   speed = (tty->c_cflag & CBAUD);               /* save current speed   */
345   tty->c_cflag = (CRTSCTS | HUPCL | CREAD);     /* UART flags           */
346   if (opt_L) 
347         tty->c_cflag |= CLOCAL;
348   tty->c_cflag |= speed;                        /* restore speed        */
349   return(0);
350 }
351
352
353 /* Fetch the state of a terminal. */
354 static int
355 tty_get_state(struct termios *tty)
356 {
357   if (ioctl(tty_fd, TCGETS, tty) < 0) {
358         if (opt_q == 0) fprintf(stderr,
359                 "slattach: tty_get_state: %s\n", strerror(errno));
360         return(-errno);
361   }
362   return(0);
363 }
364
365
366 /* Set the state of a terminal. */
367 static int
368 tty_set_state(struct termios *tty)
369 {
370   if (ioctl(tty_fd, TCSETS, tty) < 0) {
371         if (opt_q == 0) fprintf(stderr,
372                 "slattach: tty_set_state: %s\n", strerror(errno));
373         return(-errno);
374   }
375   return(0);
376 }
377
378
379 /* Get the line discipline of a terminal line. */
380 static int
381 tty_get_disc(int *disc)
382 {
383   if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
384         if (opt_q == 0) fprintf(stderr,
385                 "slattach: tty_get_disc: %s\n", strerror(errno));
386         return(-errno);
387   }
388   return(0);
389 }
390
391
392 /* Set the line discipline of a terminal line. */
393 static int
394 tty_set_disc(int disc)
395 {
396   if (disc == -1) disc = tty_sdisc;
397
398   if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
399         if (opt_q == 0) fprintf(stderr,
400                 "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
401                         disc, strerror(errno));
402         return(-errno);
403   }
404   return(0);
405 }
406
407
408 /* Fetch the name of the network interface attached to this terminal. */
409 static int
410 tty_get_name(char *name)
411 {
412   if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
413         if (opt_q == 0) 
414             perror("tty_get_name");
415         return(-errno);
416   }
417   return(0);
418 }
419
420
421 /* Hangup the line. */
422 static int
423 tty_hangup(void)
424 {
425   struct termios tty;
426
427   tty = tty_current;
428   (void) tty_set_speed(&tty, "0");
429   if (tty_set_state(&tty) < 0) {
430         if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
431         return(-errno);
432   }
433
434   (void) sleep(1);
435
436   if (tty_set_state(&tty_current) < 0) {
437         if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
438         return(-errno);
439   }
440   return(0);
441 }
442
443
444 /* Close down a terminal line. */
445 static int
446 tty_close(void)
447 {
448   (void) tty_set_disc(tty_sdisc);
449   (void) tty_hangup();
450   (void) tty_lock(NULL, 0);
451   return(0);
452 }
453
454
455 /* Open and initialize a terminal line. */
456 static int
457 tty_open(char *name, const char *speed)
458 {
459   char path[PATH_MAX];
460   register char *sp;
461   int fd;
462
463   /* Try opening the TTY device. */
464   if (name != NULL) {
465         if ((sp = strrchr(name, '/')) != (char *)NULL) *sp++ = '\0';
466           else sp = name;
467         sprintf(path, "/dev/%s", sp);
468         if (opt_d) printf("slattach: tty_open: looking for lock\n");
469         if (tty_lock(sp, 1)) return(-1); /* can we lock the device? */
470         if (opt_d) printf("slattach: tty_open: trying to open %s\n", path);
471         if ((fd = open(path, O_RDWR|O_NDELAY)) < 0) {
472                 if (opt_q == 0) fprintf(stderr,
473                         "slattach: tty_open(%s, RW): %s\n",
474                                         path, strerror(errno));
475                 return(-errno);
476         }
477         tty_fd = fd;
478         if (opt_d) printf("slattach: tty_open: %s (fd=%d) ", path, fd);
479   } else {
480         tty_fd = 0;
481         sp = (char *)NULL;
482   }
483
484   /* Fetch the current state of the terminal. */
485   if (tty_get_state(&tty_saved) < 0) {
486         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
487         return(-errno);
488   }
489   tty_current = tty_saved;
490
491   /* Fetch the current line discipline of this terminal. */
492   if (tty_get_disc(&tty_sdisc) < 0) {
493         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
494         return(-errno);
495   } 
496   tty_ldisc = tty_sdisc;
497
498   /* Put this terminal line in a 8-bit transparent mode. */
499   if (opt_m == 0) {
500         if (tty_set_raw(&tty_current) < 0) {
501                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
502                 return(-errno);
503         }
504
505         /* Set the default speed if we need to. */
506         if (speed != NULL) {
507                 if (tty_set_speed(&tty_current, speed) != 0) {
508                         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
509                                                 speed);
510                         return(-errno);
511                 }
512         }
513
514         /* Set up a completely 8-bit clean line. */
515         if (tty_set_databits(&tty_current, "8") ||
516             tty_set_stopbits(&tty_current, "1") ||
517             tty_set_parity(&tty_current, "N")) {
518                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
519                 return(-errno);
520         }
521
522         /* Set the new line mode. */
523         if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
524   }
525
526   /* OK, line is open.  Do we need to "silence" it? */
527   (void) tty_nomesg(tty_fd);
528
529   return(0);
530 }
531
532
533 /* Catch any signals. */
534 static void
535 sig_catch(int sig)
536 {
537 /*  (void) signal(sig, sig_catch); */
538   tty_close();
539   exit(0);
540 }
541
542
543 static void
544 usage(void)
545 {
546   char *usage_msg = "Usage: slattach [-ehlLmnqv] "
547 #ifdef SIOCSKEEPALIVE
548           "[-k keepalive] "
549 #endif
550 #ifdef SIOCSOUTFILL
551           "[-o outfill] "
552 #endif
553           "[-c cmd] [-s speed] [-p protocol] tty | -\n"
554           "       slattach -V | --version\n";
555
556   fprintf(stderr, usage_msg);
557   exit(1);
558 }
559
560
561 static void 
562 version(void)
563 {
564     printf("%s\n%s\n%s\n", Release, Version, Signature);
565     exit(E_VERSION);
566 }
567
568
569 int
570 main(int argc, char *argv[])
571 {
572   char path[128];
573   char buff[128];
574   const char *speed = NULL;
575   const char *proto = DEF_PROTO;
576   const char *extcmd = NULL;
577   char *sp;
578   int s;
579   static struct option longopts[] = {
580     { "version", 0, NULL, 'V' },
581     { NULL, 0, NULL, 0 }
582   };
583
584   strcpy(path, "");
585
586   /* Scan command line for any arguments. */
587   opterr = 0;
588   while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
589         case 'c':
590                 extcmd = optarg;
591                 break;
592
593         case 'e':
594                 opt_e = 1 - opt_e;
595                 break;
596
597         case 'h':
598                 opt_h = 1 - opt_h;
599                 break;
600
601 #ifdef SIOCSKEEPALIVE
602         case 'k':
603                 opt_k = atoi(optarg);
604                 break;
605 #endif
606
607         case 'L':
608                 opt_L = 1 - opt_L;
609                 break;
610
611         case 'l':
612                 opt_l = 1 - opt_l;
613                 break;
614
615         case 'm':
616                 opt_m = 1 - opt_m;
617                 break;
618
619         case 'n':
620                 opt_n = 1 - opt_n;
621                 break;
622
623 #ifdef SIOCSOUTFILL
624         case 'o':
625                 opt_o = atoi(optarg);
626                 break;
627 #endif
628
629         case 'p':
630                 proto = optarg;
631                 break;
632
633         case 'q':
634                 opt_q = 1 - opt_q;
635                 break;
636
637         case 's':
638                 speed = optarg;
639                 break;
640
641         case 'd':
642                 opt_d = 1 - opt_d;
643                 break;
644
645         case 'v':
646                 opt_v = 1 - opt_v;
647                 break;
648
649         case 'V':
650                 version();
651                 /*NOTREACHED*/
652
653         default:
654                 usage();
655                 /*NOTREACHED*/
656   }
657   
658   activate_init();
659
660   if (!strcmp(proto, "tty"))
661        opt_m++;
662
663   /* Is a terminal given? */
664   if (optind != (argc - 1)) usage();
665   safe_strncpy(path, argv[optind], sizeof(path));
666   if (!strcmp(path, "-")) {
667         opt_e = 1;
668         sp = NULL;
669         if (tty_open(NULL, speed) < 0) { return(3); }
670   } else {
671         if ((sp = strrchr(path, '/')) != NULL) *sp++ = '\0';
672           else sp = path;
673         if (tty_open(sp, speed) < 0) { return(3); }
674   }
675
676   /* Start the correct protocol. */
677   if (!strcmp(proto, "tty")) {
678         tty_sdisc = N_TTY;
679         tty_close();
680         return(0);
681   }
682   if (activate_ld(proto, tty_fd))
683         return(1);
684   if ((opt_v == 1) || (opt_d == 1)) {
685         if (tty_get_name(buff)) { return(3); }
686         printf(_("%s started"), proto);
687         if (sp != NULL) printf(_(" on %s"), sp);
688         printf(_(" interface %s\n"), buff);
689   }
690
691   /* Configure keepalive and outfill. */
692 #ifdef SIOCSKEEPALIVE
693   if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
694           fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
695 #endif
696 #ifdef SIOCSOUTFILL
697   if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
698           fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
699 #endif
700
701   (void) signal(SIGHUP, sig_catch);
702   (void) signal(SIGINT, sig_catch);
703   (void) signal(SIGQUIT, sig_catch);
704   (void) signal(SIGTERM, sig_catch);
705
706   /* Wait until we get killed if hanging on a terminal. */
707   if (opt_e == 0) {
708         while(1) {
709                 if(opt_h == 1) { /* hangup on carrier loss */
710                         int n = 0;
711
712                         ioctl(tty_fd, TIOCMGET, &n);
713                         if(!(n & TIOCM_CAR))
714                                 break;
715                         sleep(15);
716                 }
717                 else
718                         sleep(60);
719         };
720
721         tty_close();
722         if(extcmd)      /* external command on exit */
723                 system(extcmd);
724   }
725   exit(0);
726 }