]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/ldattach.c
ldattach: create a generic functions for name=value tables
[thirdparty/util-linux.git] / sys-utils / ldattach.c
1 /* line discipline loading daemon
2 * open a serial device and attach a line discipline on it
3 *
4 * Usage:
5 * ldattach GIGASET_M101 /dev/ttyS0
6 *
7 * =====================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 * =====================================================================
13 */
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include <errno.h>
24 #include <termios.h>
25 #include <unistd.h>
26 #include <err.h>
27
28 #include "c.h"
29 #include "nls.h"
30
31 #define dbg(format, arg...) \
32 do { if (debug) fprintf(stderr , "%s:" format "\n" , progname , ## arg); } while (0)
33
34 #ifndef N_GIGASET_M101
35 # define N_GIGASET_M101 16
36 #endif
37
38 #ifndef N_PPS
39 # define N_PPS 18
40 #endif
41
42 /* attach a line discipline ioctl */
43 #ifndef TIOCSETD
44 # define TIOCSETD 0x5423
45 #endif
46
47 static const char *progname;
48 static int debug = 0;
49
50 struct ld_table {
51 const char *name;
52 int value;
53 };
54
55 /* currently supported line disciplines, plus some aliases */
56 static const struct ld_table ld_discs[] =
57 {
58 { "TTY", N_TTY },
59 { "SLIP", N_SLIP },
60 { "MOUSE", N_MOUSE },
61 { "PPP", N_PPP },
62 { "STRIP", N_STRIP },
63 { "AX25", N_AX25 },
64 { "X25", N_X25 },
65 { "6PACK", N_6PACK },
66 { "R3964", N_R3964 },
67 { "IRDA", N_IRDA },
68 { "HDLC", N_HDLC },
69 { "SYNC_PPP", N_SYNC_PPP },
70 { "SYNCPPP", N_SYNC_PPP },
71 { "HCI", N_HCI },
72 { "GIGASET_M101", N_GIGASET_M101 },
73 { "GIGASET", N_GIGASET_M101 },
74 { "M101", N_GIGASET_M101 },
75 { "PPS", N_PPS },
76 { NULL, 0 }
77 };
78
79 static int lookup_table(const struct ld_table *tab, const char *str)
80 {
81 const struct ld_table *t;
82
83 for (t = tab; t && t->name; t++)
84 if (!strcasecmp(t->name, str))
85 return t->value;
86 return -1;
87 }
88
89 static void print_table(FILE *out, const struct ld_table *tab)
90 {
91 const struct ld_table *t;
92 int i;
93
94 for (t = tab, i = 1; t && t->name; t++, i++) {
95 fprintf(out, " %-10s", t->name);
96 if (!(i % 3))
97 fputc('\n', out);
98 }
99 }
100
101 static void __attribute__((__noreturn__)) usage(int exitcode)
102 {
103 fprintf(stderr,
104 _("\nUsage: %s [ -dhV78neo12 ] [ -s <speed> ] <ldisc> <device>\n"),
105 progname);
106 fputs(_("\nKnown <ldisc> names:\n"), stderr);
107 print_table(stderr, ld_discs);
108
109 exit(exitcode);
110 }
111
112 static int my_cfsetspeed(struct termios *ts, int speed)
113 {
114 /* Standard speeds
115 * -- cfsetspeed() is able to translate number to Bxxx constants
116 */
117 if (cfsetspeed(ts, speed) == 0)
118 return 0;
119
120 /* Nonstandard speeds
121 * -- we have to bypass glibc and set the speed manually (because
122 * glibc checks for speed and supports Bxxx bit rates only)...
123 */
124 #ifdef _HAVE_STRUCT_TERMIOS_C_ISPEED
125 # define BOTHER 0010000 /* non standard rate */
126 dbg("using non-standard speeds");
127 ts->c_ospeed = ts->c_ispeed = speed;
128 ts->c_cflag &= ~CBAUD;
129 ts->c_cflag |= BOTHER;
130 return 0;
131 #else
132 return -1;
133 #endif
134 }
135
136 int main(int argc, char **argv)
137 {
138 int tty_fd;
139 struct termios ts;
140 int speed = 0, bits = '-', parity = '-', stop = '-';
141 int ldisc;
142 int optc;
143 char *end;
144 char *dev;
145 static const struct option opttbl[] = {
146 {"speed", 1, 0, 's'},
147 {"sevenbits", 0, 0, '7'},
148 {"eightbits", 0, 0, '8'},
149 {"noparity", 0, 0, 'n'},
150 {"evenparity", 0, 0, 'e'},
151 {"oddparity", 0, 0, 'o'},
152 {"onestopbit", 0, 0, '1'},
153 {"twostopbits", 0, 0, '2'},
154 {"help", 0, 0, 'h'},
155 {"version", 0, 0, 'V'},
156 {"debug", 0, 0, 'd'},
157 {0, 0, 0, 0}
158 };
159
160 setlocale(LC_ALL, "");
161 bindtextdomain(PACKAGE, LOCALEDIR);
162 textdomain(PACKAGE);
163
164 /* parse options */
165 progname = program_invocation_short_name;
166
167 if (argc == 0)
168 usage(EXIT_SUCCESS);
169 while ((optc = getopt_long(argc, argv, "dhV78neo12s:", opttbl, NULL)) >= 0) {
170 switch (optc) {
171 case 'd':
172 debug++;
173 break;
174 case '1':
175 case '2':
176 stop = optc;
177 break;
178 case '7':
179 case '8':
180 bits = optc;
181 break;
182 case 'n':
183 case 'e':
184 case 'o':
185 parity = optc;
186 break;
187 case 's':
188 speed = strtol(optarg, &end, 10);
189 if (*end || speed <= 0)
190 errx(EXIT_FAILURE, _("invalid speed: %s"), optarg);
191 break;
192 case 'V':
193 printf(_("ldattach from %s\n"), PACKAGE_STRING);
194 break;
195 case 'h':
196 usage(EXIT_SUCCESS);
197 default:
198 warnx(_("invalid option"));
199 usage(EXIT_FAILURE);
200 }
201 }
202
203 if (argc - optind != 2)
204 usage(EXIT_FAILURE);
205
206 /* parse line discipline specification */
207 ldisc = lookup_table(ld_discs, argv[optind]);
208 if (ldisc < 0) {
209 ldisc = strtol(argv[optind], &end, 0);
210 if (*end || ldisc < 0)
211 errx(EXIT_FAILURE, _("invalid line discipline: %s"), argv[optind]);
212 }
213
214 /* open device */
215 dev = argv[optind+1];
216 if ((tty_fd = open(dev, O_RDWR|O_NOCTTY)) < 0)
217 err(EXIT_FAILURE, _("cannot open %s"), dev);
218 if (!isatty(tty_fd))
219 errx(EXIT_FAILURE, _("%s is not a serial line"), dev);
220
221 dbg("opened %s", dev);
222
223 /* set line speed and format */
224 if (tcgetattr(tty_fd, &ts) < 0)
225 err(EXIT_FAILURE, _("cannot get terminal attributes for %s"), dev);
226 cfmakeraw(&ts);
227 if (speed && my_cfsetspeed(&ts, speed) < 0)
228 errx(EXIT_FAILURE, _("speed %d unsupported"), speed);
229 switch (stop) {
230 case '1':
231 ts.c_cflag &= ~CSTOPB;
232 break;
233 case '2':
234 ts.c_cflag |= CSTOPB;
235 break;
236 }
237 switch (bits) {
238 case '7':
239 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
240 break;
241 case '8':
242 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
243 break;
244 }
245 switch (parity) {
246 case 'n':
247 ts.c_cflag &= ~(PARENB|PARODD);
248 break;
249 case 'e':
250 ts.c_cflag |= PARENB;
251 ts.c_cflag &= ~PARODD;
252 break;
253 case 'o':
254 ts.c_cflag |= (PARENB|PARODD);
255 break;
256 }
257 ts.c_cflag |= CREAD; /* just to be on the safe side */
258 if (tcsetattr(tty_fd, TCSAFLUSH, &ts) < 0)
259 err(EXIT_FAILURE, _("cannot set terminal attributes for %s"), dev);
260
261 dbg("set to raw %d %c%c%c: cflag=0x%x",
262 speed, bits, parity, stop, ts.c_cflag);
263
264 /* Attach the line discpline. */
265 if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
266 err(EXIT_FAILURE, _("cannot set line discipline"));
267
268 dbg("line discipline set to %d", ldisc);
269
270 /* Go into background if not in debug mode. */
271 if (!debug && daemon(0, 0) < 0)
272 err(EXIT_FAILURE, _("cannot daemonize"));
273
274 /* Sleep to keep the line discipline active. */
275 pause();
276
277 exit(EXIT_SUCCESS);
278 }