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
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/param.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
47 #include <linux/if_slip.h>
49 #if defined(__GLIBC__)
50 #if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
51 # include <termbits.h>
57 #include "pathnames.h"
58 #include "net-support.h"
65 #define _PATH_LOCKD "/var/lock" /* lock files */
68 #define _UID_UUCP "uucp" /* owns locks */
71 #define _PATH_DEVPTMX "/dev/ptmx" /* pseudo-terminal master */
75 #define DEF_PROTO "cslip"
78 const char *Release = RELEASE,
79 *Version = "$Id: slattach.c,v 1.12 2009/09/06 22:59:43 vapier Exp $",
80 *Signature = "net-tools, Fred N. van Kempen et al.";
86 } tty_speeds[] = { /* table of usable baud rates */
87 { "50", B50 }, { "75", B75 },
88 { "110", B110 }, { "300", B300 },
89 { "600", B600 }, { "1200", B1200 },
90 { "2400", B2400 }, { "4800", B4800 },
105 { "115200", B115200 },
109 struct termios tty_saved, /* saved TTY device state */
110 tty_current; /* current TTY device state */
111 int tty_sdisc, /* saved TTY line discipline */
112 tty_ldisc, /* current TTY line discipline */
113 tty_fd = -1; /* TTY file descriptor */
114 char * path_pts = NULL; /* slave pseudo-terminal device */
115 int opt_c = 0; /* "command" to run at exit */
116 int opt_e = 0; /* "activate only" flag */
117 int opt_h = 0; /* "hangup" on carrier loss */
118 #ifdef SIOCSKEEPALIVE
119 int opt_k = 0; /* "keepalive" value */
121 int opt_l = 0; /* "lock it" flag */
122 int opt_L = 0; /* 3-wire mode flag */
123 int opt_m = 0; /* "set RAW mode" flag */
124 int opt_n = 0; /* "set No Mesg" flag */
126 int opt_o = 0; /* "outfill" value */
128 int opt_q = 0; /* "quiet" flag */
129 int opt_d = 0; /* debug flag */
130 int opt_v = 0; /* Verbose flag */
132 /* Disable any messages to the input channel of this process. */
136 if (opt_n == 0) return(0);
137 return(fchmod(fd, 0600));
140 /* Check for an existing lock file on our device */
142 tty_already_locked(char *nam)
145 FILE *fd = (FILE *)0;
147 /* Does the lock file on our device exist? */
148 if ((fd = fopen(nam, "r")) == (FILE *)0)
149 return(0); /* No, return perm to continue */
151 /* Yes, the lock is there. Now let's make sure */
152 /* at least there's no active process that owns */
154 i = fscanf(fd, "%d", &pid);
157 if (i != 1) /* Lock file format's wrong! Kill't */
160 /* We got the pid, check if the process's alive */
161 if (kill(pid, 0) == 0) /* it found process */
162 return(1); /* Yup, it's running... */
164 /* Dead, we can proceed locking this device... */
168 /* Lock or unlock a terminal line. */
170 tty_lock(char *path, int mode)
172 static char saved_path[PATH_MAX];
173 static int saved_lock = 0;
178 /* We do not lock standard input. */
179 if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
181 if (mode == 1) { /* lock */
182 sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
183 if (tty_already_locked(saved_path)) {
184 fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
187 if ((fd = creat(saved_path, 0644)) < 0) {
189 if (opt_q == 0) fprintf(stderr,
190 _("slattach: tty_lock: (%s): %s\n"),
191 saved_path, strerror(errno));
194 sprintf(apid, "%10d\n", getpid());
195 if (write(fd, apid, strlen(apid)) != strlen(apid)) {
196 fprintf(stderr, _("slattach: cannot write PID file\n"));
202 /* Make sure UUCP owns the lockfile. Required by some packages. */
203 if ((pw = getpwnam(_UID_UUCP)) == NULL) {
204 if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
207 return(0); /* keep the lock anyway */
209 (void) fchown(fd, pw->pw_uid, pw->pw_gid);
214 } else { /* unlock */
215 if (saved_lock != 1) return(0);
216 if (unlink(saved_path) < 0) {
217 if (opt_q == 0) fprintf(stderr,
218 "slattach: tty_unlock: (%s): %s\n", saved_path,
229 /* Find a serial speed code in the table. */
231 tty_find_speed(const char *speed)
236 while (tty_speeds[i].speed != NULL) {
237 if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
244 /* Set the number of stop bits. */
246 tty_set_stopbits(struct termios *tty, char *stopbits)
248 if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
251 tty->c_cflag &= ~CSTOPB;
255 tty->c_cflag |= CSTOPB;
265 /* Set the number of data bits. */
267 tty_set_databits(struct termios *tty, char *databits)
269 if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
270 tty->c_cflag &= ~CSIZE;
295 /* Set the type of parity encoding. */
297 tty_set_parity(struct termios *tty, char *parity)
299 if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
300 switch(toupper(*parity)) {
302 tty->c_cflag &= ~(PARENB | PARODD);
306 tty->c_cflag &= ~(PARENB | PARODD);
307 tty->c_cflag |= (PARENB | PARODD);
311 tty->c_cflag &= ~(PARENB | PARODD);
312 tty->c_cflag |= (PARENB);
322 /* Set the line speed of a terminal line. */
324 tty_set_speed(struct termios *tty, const char *speed)
328 if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
329 if ((code = tty_find_speed(speed)) < 0) return(code);
330 tty->c_cflag &= ~CBAUD;
331 tty->c_cflag |= code;
336 /* Put a terminal line in a transparent state. */
338 tty_set_raw(struct termios *tty)
343 for(i = 0; i < NCCS; i++)
344 tty->c_cc[i] = '\0'; /* no spec chr */
346 tty->c_cc[VTIME] = 0;
347 tty->c_iflag = (IGNBRK | IGNPAR); /* input flags */
348 tty->c_oflag = (0); /* output flags */
349 tty->c_lflag = (0); /* local flags */
350 speed = (tty->c_cflag & CBAUD); /* save current speed */
351 tty->c_cflag = (HUPCL | CREAD); /* UART flags */
353 tty->c_cflag |= CLOCAL;
355 tty->c_cflag |= CRTSCTS;
356 tty->c_cflag |= speed; /* restore speed */
361 /* Fetch the state of a terminal. */
363 tty_get_state(struct termios *tty)
365 if (ioctl(tty_fd, TCGETS, tty) < 0) {
366 if (opt_q == 0) fprintf(stderr,
367 "slattach: tty_get_state: %s\n", strerror(errno));
374 /* Set the state of a terminal. */
376 tty_set_state(struct termios *tty)
378 if (ioctl(tty_fd, TCSETS, tty) < 0) {
379 if (opt_q == 0) fprintf(stderr,
380 "slattach: tty_set_state: %s\n", strerror(errno));
387 /* Get the line discipline of a terminal line. */
389 tty_get_disc(int *disc)
391 if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
392 if (opt_q == 0) fprintf(stderr,
393 "slattach: tty_get_disc: %s\n", strerror(errno));
400 /* Set the line discipline of a terminal line. */
402 tty_set_disc(int disc)
404 if (disc == -1) disc = tty_sdisc;
406 if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
407 if (opt_q == 0) fprintf(stderr,
408 "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
409 disc, strerror(errno));
416 /* Fetch the name of the network interface attached to this terminal. */
418 tty_get_name(char *name)
420 if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
422 perror("tty_get_name");
429 /* Hangup the line. */
436 (void) tty_set_speed(&tty, "0");
437 if (tty_set_state(&tty) < 0) {
438 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
444 if (tty_set_state(&tty_current) < 0) {
445 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
452 /* Close down a terminal line. */
456 (void) tty_set_disc(tty_sdisc);
458 (void) tty_lock(NULL, 0);
463 /* Open and initialize a terminal line. */
465 tty_open(char *name, const char *speed)
467 char pathbuf[PATH_MAX];
468 register char *path_open, *path_lock;
471 /* Try opening the TTY device. */
473 if (name[0] != '/') {
474 if (strlen(name + 6) > sizeof(pathbuf)) {
475 if (opt_q == 0) fprintf(stderr,
476 _("slattach: tty name too long\n"));
479 sprintf(pathbuf, "/dev/%s", name);
482 } else if (!strncmp(name, "/dev/", 5)) {
484 path_lock = name + 5;
489 if (opt_d) printf("slattach: tty_open: looking for lock\n");
490 if (tty_lock(path_lock, 1)) return(-1); /* can we lock the device? */
491 if (opt_d) printf("slattach: tty_open: trying to open %s\n", path_open);
492 if ((fd = open(path_open, O_RDWR|O_NDELAY)) < 0) {
493 if (opt_q == 0) fprintf(stderr,
494 "slattach: tty_open(%s, RW): %s\n",
495 path_open, strerror(errno));
499 if (opt_d) printf("slattach: tty_open: %s (fd=%d)\n", path_open, fd);
500 if (!strcmp(path_open, _PATH_DEVPTMX)) {
501 if (opt_d) printf("slattach: tty_open: trying to grantpt and unlockpt\n");
502 if (grantpt(fd) < 0) {
503 if (opt_q == 0) fprintf(stderr,
504 "slattach: tty_open: grantpt: %s\n", strerror(errno));
507 if (unlockpt(fd) < 0) {
508 if (opt_q == 0) fprintf(stderr,
509 "slattach: tty_open: unlockpt: %s\n", strerror(errno));
512 path_pts = ptsname(fd);
513 if (path_pts == NULL) {
514 if (opt_q == 0) fprintf(stderr,
515 "slattach: tty_open: ptsname: %s\n", strerror(errno));
518 if (opt_d) printf("slattach: tty_open: %s: slave pseudo-terminal is %s\n",
519 path_open, path_pts);
525 /* Fetch the current state of the terminal. */
526 if (tty_get_state(&tty_saved) < 0) {
527 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
530 tty_current = tty_saved;
532 /* Fetch the current line discipline of this terminal. */
533 if (tty_get_disc(&tty_sdisc) < 0) {
534 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
537 tty_ldisc = tty_sdisc;
539 /* Put this terminal line in a 8-bit transparent mode. */
541 if (tty_set_raw(&tty_current) < 0) {
542 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
546 /* Set the default speed if we need to. */
548 if (tty_set_speed(&tty_current, speed) != 0) {
549 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
555 /* Set up a completely 8-bit clean line. */
556 if (tty_set_databits(&tty_current, "8") ||
557 tty_set_stopbits(&tty_current, "1") ||
558 tty_set_parity(&tty_current, "N")) {
559 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
563 /* Set the new line mode. */
564 if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
567 /* OK, line is open. Do we need to "silence" it? */
568 (void) tty_nomesg(tty_fd);
574 /* Catch any signals. */
578 /* (void) signal(sig, sig_catch); */
587 char *usage_msg = "Usage: slattach [-ehlLmnqv] "
588 #ifdef SIOCSKEEPALIVE
594 "[-c cmd] [-s speed] [-p protocol] tty | -\n"
595 " slattach -V | --version\n";
597 fputs(usage_msg, stderr);
605 printf("%s\n%s\n%s\n", Release, Version, Signature);
611 main(int argc, char *argv[])
616 const char *speed = NULL;
617 const char *proto = DEF_PROTO;
618 const char *extcmd = NULL;
620 static struct option longopts[] = {
621 { "version", 0, NULL, 'V' },
625 strcpy(path_buf, "");
628 /* Scan command line for any arguments. */
630 while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
643 #ifdef SIOCSKEEPALIVE
645 opt_k = atoi(optarg);
667 opt_o = atoi(optarg);
700 if (setvbuf(stdout,0,_IOLBF,0)) {
701 if (opt_q == 0) fprintf(stderr, _("slattach: setvbuf(stdout,0,_IOLBF,0) : %s\n"),
708 if (!strcmp(proto, "tty"))
711 /* Is a terminal given? */
712 if (optind != (argc - 1)) usage();
713 safe_strncpy(path_buf, argv[optind], sizeof(path_buf));
714 if (!strcmp(path_buf, "-")) {
717 if (tty_open(NULL, speed) < 0) { return(3); }
720 if (tty_open(path_dev, speed) < 0) { return(3); }
723 /* Start the correct protocol. */
724 if (!strcmp(proto, "tty")) {
729 if (activate_ld(proto, tty_fd))
731 if ((opt_v == 1) || (opt_d == 1)) {
732 if (tty_get_name(buff)) { return(3); }
733 printf(_("%s started"), proto);
734 if (path_dev != NULL) printf(_(" on %s"), path_dev);
735 if (path_pts != NULL) printf(_(" ptsname %s"), path_pts);
736 printf(_(" interface %s\n"), buff);
739 /* Configure keepalive and outfill. */
740 #ifdef SIOCSKEEPALIVE
741 if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
742 fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
745 if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
746 fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
749 (void) signal(SIGHUP, sig_catch);
750 (void) signal(SIGINT, sig_catch);
751 (void) signal(SIGQUIT, sig_catch);
752 (void) signal(SIGTERM, sig_catch);
754 /* Wait until we get killed if hanging on a terminal. */
757 if(opt_h == 1) { /* hangup on carrier loss */
760 ioctl(tty_fd, TIOCMGET, &n);
770 if(extcmd) /* external command on exit */