OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / net-tools / 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__) || defined(__UC_LIBC__)
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 pathbuf[PATH_MAX];
460   register char *path_open, *path_lock;
461   int fd;
462
463   /* Try opening the TTY device. */
464   if (name != NULL) {
465         if (name[0] != '/') {
466                 if (strlen(name + 6) > sizeof(pathbuf)) {
467                         if (opt_q == 0) fprintf(stderr, 
468                                 _("slattach: tty name too long\n"));
469                         return (-1);
470                 }
471                 sprintf(pathbuf, "/dev/%s", name);
472                 path_open = pathbuf;
473                 path_lock = name;
474         } else if (!strncmp(name, "/dev/", 5)) {
475                 path_open = name;
476                 path_lock = name + 5;
477         } else {
478                 path_open = name;
479                 path_lock = name;
480         }
481         if (opt_d) printf("slattach: tty_open: looking for lock\n");
482         if (tty_lock(path_lock, 1)) return(-1); /* can we lock the device? */
483         if (opt_d) printf("slattach: tty_open: trying to open %s\n", path_open);
484         if ((fd = open(path_open, O_RDWR|O_NDELAY)) < 0) {
485                 if (opt_q == 0) fprintf(stderr,
486                         "slattach: tty_open(%s, RW): %s\n",
487                                         path_open, strerror(errno));
488                 return(-errno);
489         }
490         tty_fd = fd;
491         if (opt_d) printf("slattach: tty_open: %s (fd=%d) ", path_open, fd);
492   } else {
493         tty_fd = 0;
494   }
495
496   /* Fetch the current state of the terminal. */
497   if (tty_get_state(&tty_saved) < 0) {
498         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
499         return(-errno);
500   }
501   tty_current = tty_saved;
502
503   /* Fetch the current line discipline of this terminal. */
504   if (tty_get_disc(&tty_sdisc) < 0) {
505         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
506         return(-errno);
507   } 
508   tty_ldisc = tty_sdisc;
509
510   /* Put this terminal line in a 8-bit transparent mode. */
511   if (opt_m == 0) {
512         if (tty_set_raw(&tty_current) < 0) {
513                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
514                 return(-errno);
515         }
516
517         /* Set the default speed if we need to. */
518         if (speed != NULL) {
519                 if (tty_set_speed(&tty_current, speed) != 0) {
520                         if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
521                                                 speed);
522                         return(-errno);
523                 }
524         }
525
526         /* Set up a completely 8-bit clean line. */
527         if (tty_set_databits(&tty_current, "8") ||
528             tty_set_stopbits(&tty_current, "1") ||
529             tty_set_parity(&tty_current, "N")) {
530                 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
531                 return(-errno);
532         }
533
534         /* Set the new line mode. */
535         if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
536   }
537
538   /* OK, line is open.  Do we need to "silence" it? */
539   (void) tty_nomesg(tty_fd);
540
541   return(0);
542 }
543
544
545 /* Catch any signals. */
546 static void
547 sig_catch(int sig)
548 {
549 /*  (void) signal(sig, sig_catch); */
550   tty_close();
551   exit(0);
552 }
553
554
555 static void
556 usage(void)
557 {
558   char *usage_msg = "Usage: slattach [-ehlLmnqv] "
559 #ifdef SIOCSKEEPALIVE
560           "[-k keepalive] "
561 #endif
562 #ifdef SIOCSOUTFILL
563           "[-o outfill] "
564 #endif
565           "[-c cmd] [-s speed] [-p protocol] tty | -\n"
566           "       slattach -V | --version\n";
567
568   fprintf(stderr, usage_msg);
569   exit(1);
570 }
571
572
573 static void 
574 version(void)
575 {
576     printf("%s\n%s\n%s\n", Release, Version, Signature);
577     exit(E_VERSION);
578 }
579
580
581 int
582 main(int argc, char *argv[])
583 {
584   char path_buf[128];
585   char *path_dev;
586   char buff[128];
587   const char *speed = NULL;
588   const char *proto = DEF_PROTO;
589   const char *extcmd = NULL;
590   int s;
591   static struct option longopts[] = {
592     { "version", 0, NULL, 'V' },
593     { NULL, 0, NULL, 0 }
594   };
595
596   strcpy(path_buf, "");
597   path_dev = path_buf;
598
599   /* Scan command line for any arguments. */
600   opterr = 0;
601   while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
602         case 'c':
603                 extcmd = optarg;
604                 break;
605
606         case 'e':
607                 opt_e = 1 - opt_e;
608                 break;
609
610         case 'h':
611                 opt_h = 1 - opt_h;
612                 break;
613
614 #ifdef SIOCSKEEPALIVE
615         case 'k':
616                 opt_k = atoi(optarg);
617                 break;
618 #endif
619
620         case 'L':
621                 opt_L = 1 - opt_L;
622                 break;
623
624         case 'l':
625                 opt_l = 1 - opt_l;
626                 break;
627
628         case 'm':
629                 opt_m = 1 - opt_m;
630                 break;
631
632         case 'n':
633                 opt_n = 1 - opt_n;
634                 break;
635
636 #ifdef SIOCSOUTFILL
637         case 'o':
638                 opt_o = atoi(optarg);
639                 break;
640 #endif
641
642         case 'p':
643                 proto = optarg;
644                 break;
645
646         case 'q':
647                 opt_q = 1 - opt_q;
648                 break;
649
650         case 's':
651                 speed = optarg;
652                 break;
653
654         case 'd':
655                 opt_d = 1 - opt_d;
656                 break;
657
658         case 'v':
659                 opt_v = 1 - opt_v;
660                 break;
661
662         case 'V':
663                 version();
664                 /*NOTREACHED*/
665
666         default:
667                 usage();
668                 /*NOTREACHED*/
669   }
670   
671   if (setvbuf(stdout,0,_IOLBF,0)) {
672         if (opt_q == 0) fprintf(stderr, _("slattach: setvbuf(stdout,0,_IOLBF,0) : %s\n"),
673                                 strerror(errno));
674         exit(1);
675   }
676
677   activate_init();
678
679   if (!strcmp(proto, "tty"))
680        opt_m++;
681
682   /* Is a terminal given? */
683   if (optind != (argc - 1)) usage();
684   safe_strncpy(path_buf, argv[optind], sizeof(path_buf));
685   if (!strcmp(path_buf, "-")) {
686         opt_e = 1;
687         path_dev = NULL;
688         if (tty_open(NULL, speed) < 0) { return(3); }
689   } else {
690         path_dev = path_buf;
691         if (tty_open(path_dev, speed) < 0) { return(3); }
692   }
693
694   /* Start the correct protocol. */
695   if (!strcmp(proto, "tty")) {
696         tty_sdisc = N_TTY;
697         tty_close();
698         return(0);
699   }
700   if (activate_ld(proto, tty_fd))
701         return(1);
702   if ((opt_v == 1) || (opt_d == 1)) {
703         if (tty_get_name(buff)) { return(3); }
704         printf(_("%s started"), proto);
705         if (path_dev != NULL) printf(_(" on %s"), path_dev);
706         printf(_(" interface %s\n"), buff);
707   }
708
709   /* Configure keepalive and outfill. */
710 #ifdef SIOCSKEEPALIVE
711   if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
712           fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
713 #endif
714 #ifdef SIOCSOUTFILL
715   if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
716           fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
717 #endif
718
719   (void) signal(SIGHUP, sig_catch);
720   (void) signal(SIGINT, sig_catch);
721   (void) signal(SIGQUIT, sig_catch);
722   (void) signal(SIGTERM, sig_catch);
723
724   /* Wait until we get killed if hanging on a terminal. */
725   if (opt_e == 0) {
726         while(1) {
727                 if(opt_h == 1) { /* hangup on carrier loss */
728                         int n = 0;
729
730                         ioctl(tty_fd, TIOCMGET, &n);
731                         if(!(n & TIOCM_CAR))
732                                 break;
733                         sleep(15);
734                 }
735                 else
736                         sleep(60);
737         };
738
739         tty_close();
740         if(extcmd)      /* external command on exit */
741                 system(extcmd);
742   }
743   exit(0);
744 }