* ecki: added some more output if the -d option is used
[oss-qm-packages.git] / slattach.c
blob86f4194483b37c808bd1162a00838a8d307b4f26
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.
7 * Usage: slattach [-ehlmnqv] [ -k keepalive ] [ -o outfill ]
8 * [-c cmd] [-s speed] [-p protocol] tty | -
10 * Version: @(#)slattach.c 1.20 1999-05-29
12 * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
13 * Copyright 1988-1993 MicroWalt Corporation
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
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.
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <pwd.h>
41 #include <signal.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <getopt.h>
46 #include <linux/if_slip.h>
48 #if defined(__GLIBC__)
49 #if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
50 # include <termbits.h>
51 #else
52 # include <termios.h>
53 #endif
54 #endif
56 #include "pathnames.h"
57 #include "net-support.h"
58 #include "version.h"
59 #include "config.h"
60 #include "intl.h"
62 #ifndef _PATH_LOCKD
63 #define _PATH_LOCKD "/var/lock" /* lock files */
64 #endif
65 #ifndef _UID_UUCP
66 #define _UID_UUCP "uucp" /* owns locks */
67 #endif
70 #define DEF_PROTO "cslip"
73 char *Release = RELEASE,
74 *Version = "@(#) slattach 1.20 (1999-05-29)",
75 *Signature = "net-tools, Fred N. van Kempen et al.";
78 struct {
79 char *speed;
80 int code;
81 } tty_speeds[] = { /* table of usable baud rates */
82 { "50", B50 }, { "75", B75 },
83 { "110", B110 }, { "300", B300 },
84 { "600", B600 }, { "1200", B1200 },
85 { "2400", B2400 }, { "4800", B4800 },
86 { "9600", B9600 },
87 #ifdef B14400
88 { "14400", B14400 },
89 #endif
90 #ifdef B19200
91 { "19200", B19200 },
92 #endif
93 #ifdef B38400
94 { "38400", B38400 },
95 #endif
96 #ifdef B57600
97 { "57600", B57600 },
98 #endif
99 #ifdef B115200
100 { "115200", B115200 },
101 #endif
102 { NULL, 0 }
104 struct termios tty_saved, /* saved TTY device state */
105 tty_current; /* current TTY device state */
106 int tty_sdisc, /* saved TTY line discipline */
107 tty_ldisc, /* current TTY line discipline */
108 tty_fd = -1; /* TTY file descriptor */
109 int opt_c = 0; /* "command" to run at exit */
110 int opt_e = 0; /* "activate only" flag */
111 int opt_h = 0; /* "hangup" on carrier loss */
112 #ifdef SIOCSKEEPALIVE
113 int opt_k = 0; /* "keepalive" value */
114 #endif
115 int opt_l = 0; /* "lock it" flag */
116 int opt_L = 0; /* clocal flag */
117 int opt_m = 0; /* "set RAW mode" flag */
118 int opt_n = 0; /* "set No Mesg" flag */
119 #ifdef SIOCSOUTFILL
120 int opt_o = 0; /* "outfill" value */
121 #endif
122 int opt_q = 0; /* "quiet" flag */
123 int opt_d = 0; /* debug flag */
124 int opt_v = 0; /* Verbose flag */
126 /* Disable any messages to the input channel of this process. */
127 static int
128 tty_nomesg(int fd)
130 if (opt_n == 0) return(0);
131 return(fchmod(fd, 0600));
134 /* Check for an existing lock file on our device */
135 static int
136 tty_already_locked(char *nam)
138 int i = 0, pid = 0;
139 FILE *fd = (FILE *)0;
141 /* Does the lock file on our device exist? */
142 if ((fd = fopen(nam, "r")) == (FILE *)0)
143 return(0); /* No, return perm to continue */
145 /* Yes, the lock is there. Now let's make sure */
146 /* at least there's no active process that owns */
147 /* that lock. */
148 i = fscanf(fd, "%d", &pid);
149 (void) fclose(fd);
151 if (i != 1) /* Lock file format's wrong! Kill't */
152 return(0);
154 /* We got the pid, check if the process's alive */
155 if (kill(pid, 0) == 0) /* it found process */
156 return(1); /* Yup, it's running... */
158 /* Dead, we can proceed locking this device... */
159 return(0);
162 /* Lock or unlock a terminal line. */
163 static int
164 tty_lock(char *path, int mode)
166 static char saved_path[PATH_MAX];
167 static int saved_lock = 0;
168 struct passwd *pw;
169 int fd;
170 char apid[16];
172 /* We do not lock standard input. */
173 if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
175 if (mode == 1) { /* lock */
176 sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
177 if (tty_already_locked(saved_path)) {
178 fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
179 return(-1);
181 if ((fd = creat(saved_path, 0644)) < 0) {
182 if (errno != EEXIST)
183 if (opt_q == 0) fprintf(stderr,
184 _("slattach: tty_lock: (%s): %s\n"),
185 saved_path, strerror(errno));
186 return(-1);
188 sprintf(apid, "%10d\n", getpid());
189 if (write(fd, apid, strlen(apid)) != strlen(apid)) {
190 fprintf(stderr, _("slattach: cannot write PID file\n"));
191 close(fd);
192 unlink(saved_path);
193 return(-1);
196 (void) close(fd);
198 /* Make sure UUCP owns the lockfile. Required by some packages. */
199 if ((pw = getpwnam(_UID_UUCP)) == NULL) {
200 if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
201 _UID_UUCP);
202 return(0); /* keep the lock anyway */
204 (void) chown(saved_path, pw->pw_uid, pw->pw_gid);
205 saved_lock = 1;
206 } else { /* unlock */
207 if (saved_lock != 1) return(0);
208 if (unlink(saved_path) < 0) {
209 if (opt_q == 0) fprintf(stderr,
210 "slattach: tty_unlock: (%s): %s\n", saved_path,
211 strerror(errno));
212 return(-1);
214 saved_lock = 0;
217 return(0);
221 /* Find a serial speed code in the table. */
222 static int
223 tty_find_speed(char *speed)
225 int i;
227 i = 0;
228 while (tty_speeds[i].speed != NULL) {
229 if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
230 i++;
232 return(-EINVAL);
236 /* Set the number of stop bits. */
237 static int
238 tty_set_stopbits(struct termios *tty, char *stopbits)
240 if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
241 switch(*stopbits) {
242 case '1':
243 tty->c_cflag &= ~CSTOPB;
244 break;
246 case '2':
247 tty->c_cflag |= CSTOPB;
248 break;
250 default:
251 return(-EINVAL);
253 return(0);
257 /* Set the number of data bits. */
258 static int
259 tty_set_databits(struct termios *tty, char *databits)
261 if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
262 tty->c_cflag &= ~CSIZE;
263 switch(*databits) {
264 case '5':
265 tty->c_cflag |= CS5;
266 break;
268 case '6':
269 tty->c_cflag |= CS6;
270 break;
272 case '7':
273 tty->c_cflag |= CS7;
274 break;
276 case '8':
277 tty->c_cflag |= CS8;
278 break;
280 default:
281 return(-EINVAL);
283 return(0);
287 /* Set the type of parity encoding. */
288 static int
289 tty_set_parity(struct termios *tty, char *parity)
291 if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
292 switch(toupper(*parity)) {
293 case 'N':
294 tty->c_cflag &= ~(PARENB | PARODD);
295 break;
297 case 'O':
298 tty->c_cflag &= ~(PARENB | PARODD);
299 tty->c_cflag |= (PARENB | PARODD);
300 break;
302 case 'E':
303 tty->c_cflag &= ~(PARENB | PARODD);
304 tty->c_cflag |= (PARENB);
305 break;
307 default:
308 return(-EINVAL);
310 return(0);
314 /* Set the line speed of a terminal line. */
315 static int
316 tty_set_speed(struct termios *tty, char *speed)
318 int code;
320 if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
321 if ((code = tty_find_speed(speed)) < 0) return(code);
322 tty->c_cflag &= ~CBAUD;
323 tty->c_cflag |= code;
324 return(0);
328 /* Put a terminal line in a transparent state. */
329 static int
330 tty_set_raw(struct termios *tty)
332 int i;
333 int speed;
335 for(i = 0; i < NCCS; i++)
336 tty->c_cc[i] = '\0'; /* no spec chr */
337 tty->c_cc[VMIN] = 1;
338 tty->c_cc[VTIME] = 0;
339 tty->c_iflag = (IGNBRK | IGNPAR); /* input flags */
340 tty->c_oflag = (0); /* output flags */
341 tty->c_lflag = (0); /* local flags */
342 speed = (tty->c_cflag & CBAUD); /* save current speed */
343 tty->c_cflag = (CRTSCTS | HUPCL | CREAD); /* UART flags */
344 if (opt_L)
345 tty->c_cflag |= CLOCAL;
346 tty->c_cflag |= speed; /* restore speed */
347 return(0);
351 /* Fetch the state of a terminal. */
352 static int
353 tty_get_state(struct termios *tty)
355 if (ioctl(tty_fd, TCGETS, tty) < 0) {
356 if (opt_q == 0) fprintf(stderr,
357 "slattach: tty_get_state: %s\n", strerror(errno));
358 return(-errno);
360 return(0);
364 /* Set the state of a terminal. */
365 static int
366 tty_set_state(struct termios *tty)
368 if (ioctl(tty_fd, TCSETS, tty) < 0) {
369 if (opt_q == 0) fprintf(stderr,
370 "slattach: tty_set_state: %s\n", strerror(errno));
371 return(-errno);
373 return(0);
377 /* Get the line discipline of a terminal line. */
378 static int
379 tty_get_disc(int *disc)
381 if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
382 if (opt_q == 0) fprintf(stderr,
383 "slattach: tty_get_disc: %s\n", strerror(errno));
384 return(-errno);
386 return(0);
390 /* Set the line discipline of a terminal line. */
391 static int
392 tty_set_disc(int disc)
394 if (disc == -1) disc = tty_sdisc;
396 if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
397 if (opt_q == 0) fprintf(stderr,
398 "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
399 disc, strerror(errno));
400 return(-errno);
402 return(0);
406 /* Fetch the name of the network interface attached to this terminal. */
407 static int
408 tty_get_name(char *name)
410 if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
411 if (opt_q == 0) fprintf(stderr,
412 "slattach: tty_get_name: %s\n", strerror(errno));
413 return(-errno);
415 return(0);
419 /* Hangup the line. */
420 static int
421 tty_hangup(void)
423 struct termios tty;
425 tty = tty_current;
426 (void) tty_set_speed(&tty, "0");
427 if (tty_set_state(&tty) < 0) {
428 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
429 return(-errno);
432 (void) sleep(1);
434 if (tty_set_state(&tty_current) < 0) {
435 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
436 return(-errno);
438 return(0);
442 /* Close down a terminal line. */
443 static int
444 tty_close(void)
446 (void) tty_set_disc(tty_sdisc);
447 (void) tty_hangup();
448 (void) tty_lock(NULL, 0);
449 return(0);
453 /* Open and initialize a terminal line. */
454 static int
455 tty_open(char *name, char *speed)
457 char path[PATH_MAX];
458 register char *sp;
459 int fd;
461 /* Try opening the TTY device. */
462 if (name != NULL) {
463 if ((sp = strrchr(name, '/')) != (char *)NULL) *sp++ = '\0';
464 else sp = name;
465 sprintf(path, "/dev/%s", sp);
466 if (opt_d) printf("slattach: tty_open: looking for lock\n");
467 if (tty_lock(sp, 1)) return(-1); /* can we lock the device? */
468 if (opt_d) printf("slattach: tty_open: trying to open %s\n", path);
469 if ((fd = open(path, O_RDWR|O_NDELAY)) < 0) {
470 if (opt_q == 0) fprintf(stderr,
471 "slattach: tty_open(%s, RW): %s\n",
472 path, strerror(errno));
473 return(-errno);
475 tty_fd = fd;
476 if (opt_d) printf("slattach: tty_open: %s (fd=%d) ", path, fd);
477 } else {
478 tty_fd = 0;
479 sp = (char *)NULL;
482 /* Fetch the current state of the terminal. */
483 if (tty_get_state(&tty_saved) < 0) {
484 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
485 return(-errno);
487 tty_current = tty_saved;
489 /* Fetch the current line discipline of this terminal. */
490 if (tty_get_disc(&tty_sdisc) < 0) {
491 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
492 return(-errno);
494 tty_ldisc = tty_sdisc;
496 /* Put this terminal line in a 8-bit transparent mode. */
497 if (opt_m == 0) {
498 if (tty_set_raw(&tty_current) < 0) {
499 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
500 return(-errno);
503 /* Set the default speed if we need to. */
504 if (speed != NULL) {
505 if (tty_set_speed(&tty_current, speed) != 0) {
506 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
507 speed);
508 return(-errno);
512 /* Set up a completely 8-bit clean line. */
513 if (tty_set_databits(&tty_current, "8") ||
514 tty_set_stopbits(&tty_current, "1") ||
515 tty_set_parity(&tty_current, "N")) {
516 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
517 return(-errno);
520 /* Set the new line mode. */
521 if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
524 /* OK, line is open. Do we need to "silence" it? */
525 (void) tty_nomesg(tty_fd);
527 return(0);
531 /* Catch any signals. */
532 static void
533 sig_catch(int sig)
535 /* (void) signal(sig, sig_catch); */
536 tty_close();
537 exit(0);
541 static void
542 usage(void)
544 char *usage_msg = "Usage: slattach [-ehlLmnqv] "
545 #ifdef SIOCSKEEPALIVE
546 "[-k keepalive] "
547 #endif
548 #ifdef SIOCSOUTFILL
549 "[-o outfill] "
550 #endif
551 "[-c cmd] [-s speed] [-p protocol] tty | -\n"
552 " slattach -V | --version\n";
554 fprintf(stderr, usage_msg);
555 exit(1);
559 static void
560 version(void)
562 printf("%s\n%s\n%s\n", Release, Version, Signature);
563 exit(E_VERSION);
568 main(int argc, char *argv[])
570 char path[128];
571 char buff[128];
572 char *speed = NULL;
573 char *proto = DEF_PROTO;
574 char *extcmd = (char *)0;
575 struct hwtype *ht;
576 char *sp;
577 int s;
578 static struct option longopts[] = {
579 { "version", 0, NULL, 'V' },
580 { NULL, 0, NULL, 0 }
583 strcpy(path, "");
585 /* Scan command line for any arguments. */
586 opterr = 0;
587 while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
588 case 'c':
589 extcmd = optarg;
590 break;
592 case 'e':
593 opt_e = 1 - opt_e;
594 break;
596 case 'h':
597 opt_h = 1 - opt_h;
598 break;
600 #ifdef SIOCSKEEPALIVE
601 case 'k':
602 opt_k = atoi(optarg);
603 break;
604 #endif
606 case 'L':
607 opt_L = 1 - opt_L;
608 break;
610 case 'l':
611 opt_l = 1 - opt_l;
612 break;
614 case 'm':
615 opt_m = 1 - opt_m;
616 break;
618 case 'n':
619 opt_n = 1 - opt_n;
620 break;
622 #ifdef SIOCSOUTFILL
623 case 'o':
624 opt_o = atoi(optarg);
625 break;
626 #endif
628 case 'p':
629 proto = optarg;
630 break;
632 case 'q':
633 opt_q = 1 - opt_q;
634 break;
636 case 's':
637 speed = optarg;
638 break;
640 case 'd':
641 opt_d = 1 - opt_d;
642 break;
644 case 'v':
645 opt_v = 1 - opt_v;
646 break;
648 case 'V':
649 version();
650 /*NOTREACHED*/
652 default:
653 usage();
654 /*NOTREACHED*/
657 activate_init();
659 /* Check the protocol. */
660 if ((ht = get_hwtype(proto)) == NULL && strcmp(proto, "tty")) {
661 if (opt_q == 0) fprintf(stderr, _("slattach: unsupported protocol %s\n"), proto);
662 return(2);
664 if (ht == NULL) opt_m++;
666 /* Is a terminal given? */
667 if (optind != (argc - 1)) usage();
668 strncpy(path, argv[optind], 128);
669 if (!strcmp(path, "-")) {
670 opt_e = 1;
671 sp = NULL;
672 if (tty_open(NULL, speed) < 0) { return(3); }
673 } else {
674 if ((sp = strrchr(path, '/')) != NULL) *sp++ = '\0';
675 else sp = path;
676 if (tty_open(sp, speed) < 0) { return(3); }
679 /* Start the correct protocol. */
680 if (ht == NULL) {
681 tty_sdisc = N_TTY;
682 tty_close();
683 return(0);
685 (*ht->activate)(tty_fd);
686 if ((opt_v == 1) || (opt_d == 1)) {
687 tty_get_name(buff);
688 printf(_("%s started"), proto);
689 if (sp != NULL) printf(_(" on %s"), sp);
690 printf(_(" interface %s\n"), buff);
693 /* Configure keepalive and outfill. */
694 #ifdef SIOCSKEEPALIVE
695 if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
696 fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
697 #endif
698 #ifdef SIOCSOUTFILL
699 if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
700 fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
701 #endif
703 (void) signal(SIGHUP, sig_catch);
704 (void) signal(SIGINT, sig_catch);
705 (void) signal(SIGQUIT, sig_catch);
706 (void) signal(SIGTERM, sig_catch);
708 /* Wait until we get killed if hanging on a terminal. */
709 if (opt_e == 0) {
710 while(1) {
711 if(opt_h == 1) { /* hangup on carrier loss */
712 int n = 0;
714 ioctl(tty_fd, TIOCMGET, &n);
715 if(!(n & TIOCM_CAR))
716 break;
717 sleep(15);
719 else
720 sleep(60);
723 tty_close();
724 if(extcmd!=(char *)0) /* external command on exit */
725 system(extcmd);
727 exit(0);