From 1e8d11c459ae93a4d8d7976a8530ae5198206a26 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Mon, 10 Mar 2008 14:20:26 +0100 Subject: [PATCH] ldattach: new command Add an ldattach(8) utility program similar to the one in OpenBSD. Signed-off-by: Tilman Schmidt --- sys-utils/Makefile.am | 4 +- sys-utils/ldattach.8 | 130 +++++++++++++++++ sys-utils/ldattach.c | 327 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 459 insertions(+), 2 deletions(-) create mode 100644 sys-utils/ldattach.8 create mode 100644 sys-utils/ldattach.c diff --git a/sys-utils/Makefile.am b/sys-utils/Makefile.am index 96583a45..4129c201 100644 --- a/sys-utils/Makefile.am +++ b/sys-utils/Makefile.am @@ -16,7 +16,7 @@ if LINUX sbin_PROGRAMS = ctrlaltdel endif -usrsbinexec_PROGRAMS = readprofile +usrsbinexec_PROGRAMS = ldattach readprofile if LINUX usrsbinexec_PROGRAMS += tunelp rtcwake endif @@ -24,7 +24,7 @@ endif tunelp_SOURCES = tunelp.c lp.h dist_man_MANS = flock.1 readprofile.1 \ - ctrlaltdel.8 cytune.8 dmesg.1 ipcrm.1 ipcs.1 renice.1 \ + ctrlaltdel.8 cytune.8 dmesg.1 ipcrm.1 ipcs.1 ldattach.8 renice.1 \ setsid.1 tunelp.8 setarch.8 rtcwake.8 info_TEXINFOS = ipc.texi diff --git a/sys-utils/ldattach.8 b/sys-utils/ldattach.8 new file mode 100644 index 00000000..4ee62293 --- /dev/null +++ b/sys-utils/ldattach.8 @@ -0,0 +1,130 @@ +.\" Copyright 2008 Tilman Schmidt (tilman@imap.cc) +.\" May be distributed under the GNU General Public License version 2 or later +.TH LDATTACH 8 "14 January 2008" "Linux 2.6" "Linux Programmer's Manual" +.SH NAME +ldattach \- attach a line discipline to a serial line +.SH SYNOPSIS +.nf +.BI "ldattach [ \-dhV78neo12 ] [ \-s " speed " ] " "ldisc device" +.fi +.SH DESCRIPTION +The +.B ldattach +daemon opens the specified +.I device +file +(which should refer to a serial device) +and attaches the line discipline +.B ldisc +to it for processing of the sent and/or received data. +It then goes into the background keeping the device open so that the +line discipline stays loaded. + +The line discipline +.B ldisc +may be specified either by name +or by number. + +In order to detach the line discipline, +.BR kill (1) +the +.B ldattach +process. + +With no arguments, +.B ldattach +prints usage information. +.SH LINE DISCIPLINES +As of kernel release 2.6.21, the following line disciplines are supported: +.TP +.BR TTY ( 0 ) +The default line discipline, +providing transparent operation (raw mode) +as well as the habitual terminal line editing capabilities (cooked mode). +.TP +.BR SLIP ( 1 ) +Serial Line IP (SLIP) protocol processor +for transmitting TCP/IP packets over serial lines. +.TP +.BR MOUSE ( 2 ) +Device driver for RS232 connected pointing devices (serial mice). +.TP +.BR PPP ( 3 ) +Point to Point Protocol (PPP) processor +for transmitting network packets over serial lines. +.TP +.BR STRIP ( 4 ) +.TP +.BR AX25 ( 5 ) +.TP +.BR X25 ( 6 ) +Line driver for transmitting X.25 packets over asynchronous serial lines. +.TP +.BR 6PACK ( 7 ) +.TP +.BR R3964 ( 9 ) +Driver for Simatic R3964 module. +.TP +.BR IRDA ( 11 ) +Linux IrDa (infrared data transmission) driver - +see http://irda.sourceforge.net/ +.TP +.BR HDLC ( 13 ) +Synchronous HDLC driver. +.TP +.BR SYNC_PPP ( 14 ) +Synchronous PPP driver. +.TP +.BR HCI ( 15 ) +Bluetooth HCI UART driver. +.TP +.BR GIGASET_M101 ( 16 ) +Driver for Siemens Gigaset M101 serial DECT adapter. +.SH OPTIONS +.TP +\fB-d\fP | \fB--debug\fP +Causes +.B ldattach +to stay in the foreground so that it can be interrupted or debugged, +and to print verbose messages about its progress to the standard error output. +.TP +\fB-h\fP | \fB--help\fP +Prints a usage message and exits. +.TP +\fB-V\fP | \fB--version\fP +Prints the program version. +.TP +\fB-s\fP \fIvalue\fP | \fB--speed\fP \fIvalue\fP +Set the speed of the serial line to the specified value. +.TP +\fB-7\fP | \fB--sevenbits\fP +Sets the character size of the serial line to 7 bits. +.TP +\fB-8\fP | \fB--eightbits\fP +Sets the character size of the serial line to 8 bits. +.TP +\fB-n\fP | \fB--noparity\fP +Sets the parity of the serial line to none. +.TP +\fB-e\fP | \fB--evenparity\fP +Sets the parity of the serial line to even. +.TP +\fB-o\fP | \fB--oddparity\fP +Sets the parity of the serial line to odd. +.TP +\fB-1\fP | \fB--onestopbit\fP +Sets the number of stop bits of the serial line to one. +.TP +\fB-2\fP | \fB--twostopbits\fP +Sets the number of stop bits of the serial line to two. +.SH "SEE ALSO" +.BR inputattach (1), +.BR ttys (4) +.SH AUTHOR +.nf +Tilman Schmidt (tilman@imap.cc) +.fi +.SH AVAILABILITY +The ldattach command is part of the util-linux-ng package +and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/. diff --git a/sys-utils/ldattach.c b/sys-utils/ldattach.c new file mode 100644 index 00000000..99037d62 --- /dev/null +++ b/sys-utils/ldattach.c @@ -0,0 +1,327 @@ +/* line discipline loading daemon + * open a serial device and attach a line discipline on it + * + * Usage: + * ldattach GIGASET_M101 /dev/ttyS0 + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * ===================================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define dbg(format, arg...) \ + do { if (debug) fprintf(stderr , "%s:" format "\n" , progname , ## arg); } while (0) + +#ifndef N_GIGASET_M101 +#define N_GIGASET_M101 16 +#endif + +#ifndef PACKAGE_STRING +#define PACKAGE_STRING "me" +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +static const char *progname; +static int debug = 0; + +/* currently supported line disciplines, plus some aliases */ +static const struct ld_entry { const char *s; int v; } +ld_table[] = { + { "TTY", N_TTY }, + { "SLIP", N_SLIP }, + { "MOUSE", N_MOUSE }, + { "PPP", N_PPP }, + { "STRIP", N_STRIP }, + { "AX25", N_AX25 }, + { "X25", N_X25 }, + { "6PACK", N_6PACK }, + { "R3964", N_R3964 }, + { "IRDA", N_IRDA }, + { "HDLC", N_HDLC }, + { "SYNC_PPP", N_SYNC_PPP }, + { "SYNCPPP", N_SYNC_PPP }, + { "HCI", N_HCI }, + { "GIGASET_M101", N_GIGASET_M101 }, + { "GIGASET", N_GIGASET_M101 }, + { "M101", N_GIGASET_M101 } +}; + +/* look up line discipline code */ +static int lookup_ld(const char *s) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(ld_table); i++) + if (!strcasecmp(ld_table[i].s, s)) + return ld_table[i].v; + return -1; +} + +/* replacement for tcsetattr(3) and friends supporting arbitrary speed values */ + +/* some archs don't have separate struct termios2 */ +#ifndef TCGETS2 +#define termios2 termios +#define TCGETS2 TCGETS +#define TCSETS2 TCSETS +#define TCSETSW2 TCSETSW +#define TCSETSF2 TCSETSF +#endif + +static int tcgetattr2(int fd, struct termios2 *pts) +{ + return ioctl(fd, TCGETS2, pts); +} + +static int tcsetattr2(int fd, int option, const struct termios2 *pts) +{ + int request; + + switch (option) { + case TCSANOW: + request = TCSETS2; + break; + case TCSADRAIN: + request = TCSETSW2; + break; + case TCSAFLUSH: + request = TCSETSF2; + break; + default: + errno = -EINVAL; + return -1; + } + return ioctl(fd, request, pts); +} + +static void cfmakeraw2(struct termios2 *pts) +{ + pts->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + pts->c_oflag &= ~OPOST; + pts->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + pts->c_cflag &= ~(CSIZE|PARENB); + pts->c_cflag |= CS8; +} + +/* table of standard line speeds */ +static const struct speed_entry { int s; speed_t v; } +speed_table[] = { + { 50, B50 }, + { 75, B75 }, + { 110, B110 }, + { 134, B134 }, + { 150, B150 }, + { 200, B200 }, + { 300, B300 }, + { 600, B600 }, + { 1200, B1200 }, + { 1800, B1800 }, + { 2400, B2400 }, + { 4800, B4800 }, + { 9600, B9600 }, + { 19200, B19200 }, + { 38400, B38400 } +#ifdef B57600 + ,{ 57600, B57600 } +#endif +#ifdef B115200 + ,{ 115200, B115200 } +#endif +#ifdef B230400 + ,{ 230400, B230400 } +#endif +}; + +static int cfsetspeed2(struct termios2 *pts, int speed) +{ + size_t i; + + /* try POSIX method first */ + for (i = 0; i < ARRAY_SIZE(speed_table); i++) + if (speed_table[i].s == speed) { + pts->c_cflag &= ~CBAUD; + pts->c_cflag |= speed_table[i].v; + return 0; + } + +#ifdef BOTHER + /* new method available */ + pts->c_ospeed = pts->c_ispeed = speed; + pts->c_cflag &= ~CBAUD; + pts->c_cflag |= BOTHER; + return 0; +#else + /* new method not available */ + return -1; +#endif +} + +static void __attribute__((__noreturn__)) usage(int exitcode) +{ + size_t i; + + fprintf(stderr, + "\nUsage: %s [ -dhV78neo12 ] [ -s ] \n", + progname); + fprintf(stderr, "\nKnown names:\n"); + for (i = 0; i < ARRAY_SIZE(ld_table); i++) + fprintf(stderr, " %s\n", ld_table[i].s); + exit(exitcode); +} + +int main(int argc, char **argv) +{ + int tty_fd; + struct termios2 ts; + int speed = 0, bits = '-', parity = '-', stop = '-'; + int ldisc; + int optc; + char *end; + char *dev; + static const struct option opttbl[] = { + {"speed", 1, 0, 's'}, + {"sevenbits", 0, 0, '7'}, + {"eightbits", 0, 0, '8'}, + {"noparity", 0, 0, 'n'}, + {"evenparity", 0, 0, 'e'}, + {"oddparity", 0, 0, 'o'}, + {"onestopbit", 0, 0, '1'}, + {"twostopbits", 0, 0, '2'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {"debug", 0, 0, 'd'}, + {0, 0, 0, 0} + }; + + /* parse options */ + progname = argv[0]; + if (argc == 0) + usage(EXIT_SUCCESS); + while ((optc = getopt_long(argc, argv, "dhV78neo12s:", opttbl, NULL)) >= 0) { + switch (optc) { + case 'd': + debug++; + break; + case '1': + case '2': + stop = optc; + break; + case '7': + case '8': + bits = optc; + break; + case 'n': + case 'e': + case 'o': + parity = optc; + break; + case 's': + speed = strtol(optarg, &end, 10); + if (*end || speed <= 0) + errx(EXIT_FAILURE, "invalid speed: %s", optarg); + break; + case 'V': + printf("ldattach from %s\n", PACKAGE_STRING); + break; + case 'h': + usage(EXIT_SUCCESS); + default: + warnx("invalid option"); + usage(EXIT_FAILURE); + } + } + + if (argc - optind != 2) + usage(EXIT_FAILURE); + + /* parse line discipline specification */ + if ((ldisc = lookup_ld(argv[optind])) < 0) { + ldisc = strtol(argv[optind], &end, 0); + if (*end || ldisc < 0) + errx(EXIT_FAILURE, "invalid line discipline: %s", argv[optind]); + } + + /* open device */ + dev = argv[optind+1]; + if ((tty_fd = open(dev, O_RDWR|O_NOCTTY)) < 0) + err(EXIT_FAILURE, "cannot open %s", dev); + if (!isatty(tty_fd)) + errx(EXIT_FAILURE, "%s is not a serial line", dev); + + dbg("opened %s", dev); + + /* set line speed and format */ + if (tcgetattr2(tty_fd, &ts) < 0) + err(EXIT_FAILURE, "cannot get terminal attributes for %s", dev); + cfmakeraw2(&ts); + if (speed && cfsetspeed2(&ts, speed) < 0) + errx(EXIT_FAILURE, "speed %d unsupported", speed); + switch (stop) { + case '1': + ts.c_cflag &= ~CSTOPB; + break; + case '2': + ts.c_cflag |= CSTOPB; + break; + } + switch (bits) { + case '7': + ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7; + break; + case '8': + ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8; + break; + } + switch (parity) { + case 'n': + ts.c_cflag &= ~(PARENB|PARODD); + break; + case 'e': + ts.c_cflag |= PARENB; + ts.c_cflag &= ~PARODD; + break; + case 'o': + ts.c_cflag |= (PARENB|PARODD); + break; + } + ts.c_cflag |= CREAD; /* just to be on the safe side */ + if (tcsetattr2(tty_fd, TCSAFLUSH, &ts) < 0) + err(EXIT_FAILURE, "cannot set terminal attributes for %s", dev); + + dbg("set to raw %d %c%c%c: cflag=0x%x", + speed, bits, parity, stop, ts.c_cflag); + + /* Attach the line discpline. */ + if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0) + err(EXIT_FAILURE, "cannot set line discipline"); + + dbg("line discipline set to %d", ldisc); + + /* Go into background if not in debug mode. */ + if (!debug && daemon(0, 0) < 0) + err(EXIT_FAILURE, "cannot daemonize"); + + /* Sleep to keep the line discipline active. */ + pause(); + + exit(EXIT_SUCCESS); +} -- 2.39.5