use file version
[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 = "$Id: slattach.c,v 1.11 2005/12/04 05:15:36 ecki Exp $",
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;              /* 3-wire mode 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 = (HUPCL | CREAD);               /* UART flags           */
346   if (opt_L) 
347         tty->c_cflag |= CLOCAL;
348   else
349         tty->c_cflag |= CRTSCTS;
350   tty->c_cflag |= speed;                        /* restore speed        */
351   return(0);
352 }
353
354
355 /* Fetch the state of a terminal. */
356 static int
357 tty_get_state(struct termios *tty)
358 {
359   if (ioctl(tty_fd, TCGETS, tty) < 0) {
360         if (opt_q == 0) fprintf(stderr,
361                 "slattach: tty_get_state: %s\n", strerror(errno));
362         return(-errno);
363   }
364   return(0);
365 }
366
367
368 /* Set the state of a terminal. */
369 static int
370 tty_set_state(struct termios *tty)
371 {
372   if (ioctl(tty_fd, TCSETS, tty) < 0) {
373         if (opt_q == 0) fprintf(stderr,
374                 "slattach: tty_set_state: %s\n", strerror(errno));
375         return(-errno);
376   }
377   return(0);
378 }
379
380
381 /* Get the line discipline of a terminal line. */
382 static int
383 tty_get_disc(int *disc)
384 {
385   if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
386         if (opt_q == 0) fprintf(stderr,
387                 "slattach: tty_get_disc: %s\n", strerror(errno));
388         return(-errno);
389   }
390   return(0);
391 }
392
393
394 /* Set the line discipline of a terminal line. */
395 static int
396 tty_set_disc(int disc)
397 {
398   if (disc == -1) disc = tty_sdisc;
399
400   if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
401         if (opt_q == 0) fprintf(stderr,
402                 "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
403                         disc, strerror(errno));
404         return(-errno);
405   }
406   return(0);
407 }
408
409
410 /* Fetch the name of the network interface attached to this terminal. */
411 static int
412 tty_get_name(char *name)
413 {
414   if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
415         if (opt_q == 0) 
416             perror("tty_get_name");
417         return(-errno);
418   }
419   return(0);
420 }
421
422
423 /* Hangup the line. */
424 static int
425 tty_hangup(void)
426 {
427   struct termios tty;
428
429   tty = tty_current;
430   (void) tty_set_speed(&tty, "0");
431   if (tty_set_state(&tty) < 0) {
432         if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
433         return(-errno);
434   }
435
436   (void) sleep(1);
437
438   if (tty_set_state(&tty_current) < 0) {
439         if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
440         return(-errno);
441   }
442   return(0);
443 }
444
445
446 /* Close down a terminal line. */
447 static int
448 tty_close(void)
449 {
450   (void) tty_set_disc(tty_sdisc);
451   (void) tty_hangup();
452   (void) tty_lock(NULL, 0);
453   return(0);
454 }
455
456
457 /* Open and initialize a terminal line. */
458 static int
459 tty_open(char *name, const char *speed)
460 {
461   char pathbuf[PATH_MAX];
462   register char *path_open, *path_lock;
463   int fd;
464
465   /* Try opening the TTY device. */
466   if (name != NULL) {
467         if (name[0] != '/') {
468                 if (strlen(name + 6) > sizeof(pathbuf)) {
469                         if (opt_q == 0) fprintf(stderr, 
470                                 _("slattach: tty name too long\n"));
471                         return (-1);
472                 }
473                 sprintf(pathbuf, "/dev/%s", name);
474                 path_open = pathbuf;
475                 path_lock = name;
476         } else if (!strncmp(name, "/dev/", 5)) {
477                 path_open = name;
478                 path_lock = name + 5;
479         } else {
480                 path_open = name;
481                 path_lock = name;
482         }
483         if (opt_d) printf("slattach: tty_open: looking for lock\n");
484         if (tty_lock(path_lock, 1)) return(-1); /* can we lock the device? */
485         if (opt_d) printf("slattach: tty_open: trying to open %s\n", path_open);
486         if ((fd = open(path_open, O_RDWR|O_NDELAY)) < 0) {
487                 if (opt_q == 0) fprintf(stderr,
488                         "slattach: tty_open(%s, RW): %s\n",
489                                         path_open, strerror(errno));
490                 return(-errno);
491         }
492         tty_fd = fd;
493         if (opt_d) printf("slattach: tty_open: %s (fd=%d) ", path_open, fd);
494   } else {
495         tty_fd = 0;
496   }
497
498   /* Fetch the current state of the terminal. */
499   if (tty_get_state(&tty_saved) < 0) {
500         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
501         return(-errno);
502   }
503   tty_current = tty_saved;
504
505   /* Fetch the current line discipline of this terminal. */
506   if (tty_get_disc(&tty_sdisc) < 0) {
507         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
508         return(-errno);
509   } 
510   tty_ldisc = tty_sdisc;
511
512   /* Put this terminal line in a 8-bit transparent mode. */
513   if (opt_m == 0) {
514         if (tty_set_raw(&tty_current) < 0) {
515                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
516                 return(-errno);
517         }
518
519         /* Set the default speed if we need to. */
520         if (speed != NULL) {
521                 if (tty_set_speed(&tty_current, speed) != 0) {
522                         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
523                                                 speed);
524                         return(-errno);
525                 }
526         }
527
528         /* Set up a completely 8-bit clean line. */
529         if (tty_set_databits(&tty_current, "8") ||
530             tty_set_stopbits(&tty_current, "1") ||
531             tty_set_parity(&tty_current, "N")) {
532                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
533                 return(-errno);
534         }
535
536         /* Set the new line mode. */
537         if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
538   }
539
540   /* OK, line is open.  Do we need to "silence" it? */
541   (void) tty_nomesg(tty_fd);
542
543   return(0);
544 }
545
546
547 /* Catch any signals. */
548 static void
549 sig_catch(int sig)
550 {
551 /*  (void) signal(sig, sig_catch); */
552   tty_close();
553   exit(0);
554 }
555
556
557 static void
558 usage(void)
559 {
560   char *usage_msg = "Usage: slattach [-ehlLmnqv] "
561 #ifdef SIOCSKEEPALIVE
562           "[-k keepalive] "
563 #endif
564 #ifdef SIOCSOUTFILL
565           "[-o outfill] "
566 #endif
567           "[-c cmd] [-s speed] [-p protocol] tty | -\n"
568           "       slattach -V | --version\n";
569
570   fprintf(stderr, usage_msg);
571   exit(1);
572 }
573
574
575 static void 
576 version(void)
577 {
578     printf("%s\n%s\n%s\n", Release, Version, Signature);
579     exit(E_VERSION);
580 }
581
582
583 int
584 main(int argc, char *argv[])
585 {
586   char path_buf[128];
587   char *path_dev;
588   char buff[128];
589   const char *speed = NULL;
590   const char *proto = DEF_PROTO;
591   const char *extcmd = NULL;
592   int s;
593   static struct option longopts[] = {
594     { "version", 0, NULL, 'V' },
595     { NULL, 0, NULL, 0 }
596   };
597
598   strcpy(path_buf, "");
599   path_dev = path_buf;
600
601   /* Scan command line for any arguments. */
602   opterr = 0;
603   while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
604         case 'c':
605                 extcmd = optarg;
606                 break;
607
608         case 'e':
609                 opt_e = 1 - opt_e;
610                 break;
611
612         case 'h':
613                 opt_h = 1 - opt_h;
614                 break;
615
616 #ifdef SIOCSKEEPALIVE
617         case 'k':
618                 opt_k = atoi(optarg);
619                 break;
620 #endif
621
622         case 'L':
623                 opt_L = 1 - opt_L;
624                 break;
625
626         case 'l':
627                 opt_l = 1 - opt_l;
628                 break;
629
630         case 'm':
631                 opt_m = 1 - opt_m;
632                 break;
633
634         case 'n':
635                 opt_n = 1 - opt_n;
636                 break;
637
638 #ifdef SIOCSOUTFILL
639         case 'o':
640                 opt_o = atoi(optarg);
641                 break;
642 #endif
643
644         case 'p':
645                 proto = optarg;
646                 break;
647
648         case 'q':
649                 opt_q = 1 - opt_q;
650                 break;
651
652         case 's':
653                 speed = optarg;
654                 break;
655
656         case 'd':
657                 opt_d = 1 - opt_d;
658                 break;
659
660         case 'v':
661                 opt_v = 1 - opt_v;
662                 break;
663
664         case 'V':
665                 version();
666                 /*NOTREACHED*/
667
668         default:
669                 usage();
670                 /*NOTREACHED*/
671   }
672   
673   if (setvbuf(stdout,0,_IOLBF,0)) {
674         if (opt_q == 0) fprintf(stderr, _("slattach: setvbuf(stdout,0,_IOLBF,0) : %s\n"),
675                                 strerror(errno));
676         exit(1);
677   }
678
679   activate_init();
680
681   if (!strcmp(proto, "tty"))
682        opt_m++;
683
684   /* Is a terminal given? */
685   if (optind != (argc - 1)) usage();
686   safe_strncpy(path_buf, argv[optind], sizeof(path_buf));
687   if (!strcmp(path_buf, "-")) {
688         opt_e = 1;
689         path_dev = NULL;
690         if (tty_open(NULL, speed) < 0) { return(3); }
691   } else {
692         path_dev = path_buf;
693         if (tty_open(path_dev, speed) < 0) { return(3); }
694   }
695
696   /* Start the correct protocol. */
697   if (!strcmp(proto, "tty")) {
698         tty_sdisc = N_TTY;
699         tty_close();
700         return(0);
701   }
702   if (activate_ld(proto, tty_fd))
703         return(1);
704   if ((opt_v == 1) || (opt_d == 1)) {
705         if (tty_get_name(buff)) { return(3); }
706         printf(_("%s started"), proto);
707         if (path_dev != NULL) printf(_(" on %s"), path_dev);
708         printf(_(" interface %s\n"), buff);
709   }
710
711   /* Configure keepalive and outfill. */
712 #ifdef SIOCSKEEPALIVE
713   if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
714           fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
715 #endif
716 #ifdef SIOCSOUTFILL
717   if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
718           fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
719 #endif
720
721   (void) signal(SIGHUP, sig_catch);
722   (void) signal(SIGINT, sig_catch);
723   (void) signal(SIGQUIT, sig_catch);
724   (void) signal(SIGTERM, sig_catch);
725
726   /* Wait until we get killed if hanging on a terminal. */
727   if (opt_e == 0) {
728         while(1) {
729                 if(opt_h == 1) { /* hangup on carrier loss */
730                         int n = 0;
731
732                         ioctl(tty_fd, TIOCMGET, &n);
733                         if(!(n & TIOCM_CAR))
734                                 break;
735                         sleep(15);
736                 }
737                 else
738                         sleep(60);
739         };
740
741         tty_close();
742         if(extcmd)      /* external command on exit */
743                 system(extcmd);
744   }
745   exit(0);
746 }