summaryrefslogtreecommitdiffstats
path: root/sys-utils/ldattach.c
diff options
context:
space:
mode:
authorTilman Schmidt2008-03-10 14:20:26 +0100
committerKarel Zak2008-03-10 14:20:26 +0100
commit1e8d11c459ae93a4d8d7976a8530ae5198206a26 (patch)
tree8eb3cff11a2d6f6427931a68ba16217f0ba3416b /sys-utils/ldattach.c
parentmount: don't canonicalize LABEL= or UUID= spec (diff)
downloadkernel-qcow2-util-linux-1e8d11c459ae93a4d8d7976a8530ae5198206a26.tar.gz
kernel-qcow2-util-linux-1e8d11c459ae93a4d8d7976a8530ae5198206a26.tar.xz
kernel-qcow2-util-linux-1e8d11c459ae93a4d8d7976a8530ae5198206a26.zip
ldattach: new command
Add an ldattach(8) utility program similar to the one in OpenBSD. Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Diffstat (limited to 'sys-utils/ldattach.c')
-rw-r--r--sys-utils/ldattach.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/sys-utils/ldattach.c b/sys-utils/ldattach.c
new file mode 100644
index 000000000..99037d62b
--- /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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <asm/termbits.h>
+#include <unistd.h>
+#include <err.h>
+
+#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 <speed> ] <ldisc> <device>\n",
+ progname);
+ fprintf(stderr, "\nKnown <ldisc> 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);
+}