]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/ldattach.c
blkid, hwclock, ldattach: use program_invocation_short_name
[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 <errno.h>
16 #include <fcntl.h>
17 #include <getopt.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <termios.h>
25 #include <unistd.h>
26
27 #include "c.h"
28 #include "nls.h"
29 #include "strutils.h"
30 #include "closestream.h"
31
32 #ifndef N_GIGASET_M101
33 # define N_GIGASET_M101 16
34 #endif
35
36 #ifndef N_PPS
37 # define N_PPS 18
38 #endif
39
40 /* attach a line discipline ioctl */
41 #ifndef TIOCSETD
42 # define TIOCSETD 0x5423
43 #endif
44
45 static int debug = 0;
46
47 struct ld_table {
48 const char *name;
49 int value;
50 };
51
52 /* currently supported line disciplines, plus some aliases */
53 static const struct ld_table ld_discs[] = {
54 { "TTY", N_TTY },
55 { "SLIP", N_SLIP },
56 { "MOUSE", N_MOUSE },
57 { "PPP", N_PPP },
58 { "STRIP", N_STRIP },
59 { "AX25", N_AX25 },
60 { "X25", N_X25 },
61 { "6PACK", N_6PACK },
62 { "R3964", N_R3964 },
63 { "IRDA", N_IRDA },
64 { "HDLC", N_HDLC },
65 { "SYNC_PPP", N_SYNC_PPP },
66 { "SYNCPPP", N_SYNC_PPP },
67 { "HCI", N_HCI },
68 { "PPS", N_PPS },
69 { "M101", N_GIGASET_M101 },
70 { "GIGASET", N_GIGASET_M101 },
71 { "GIGASET_M101", N_GIGASET_M101 },
72 { NULL, 0 }
73 };
74
75 /* known c_iflag names */
76 static const struct ld_table ld_iflags[] =
77 {
78 { "IGNBRK", IGNBRK },
79 { "BRKINT", BRKINT },
80 { "IGNPAR", IGNPAR },
81 { "PARMRK", PARMRK },
82 { "INPCK", INPCK },
83 { "ISTRIP", ISTRIP },
84 { "INLCR", INLCR },
85 { "IGNCR", IGNCR },
86 { "ICRNL", ICRNL },
87 { "IUCLC", IUCLC },
88 { "IXON", IXON },
89 { "IXANY", IXANY },
90 { "IXOFF", IXOFF },
91 { "IMAXBEL", IMAXBEL },
92 { "IUTF8", IUTF8 },
93 { NULL, 0 }
94 };
95
96 static void dbg(char *fmt, ...)
97 {
98 va_list args;
99
100 if (debug == 0)
101 return;
102 fflush(NULL);
103 fprintf(stderr, "%s: ", program_invocation_short_name);
104 va_start(args, fmt);
105 vfprintf(stderr, fmt, args);
106 va_end(args);
107 fprintf(stderr, "\n");
108 fflush(NULL);
109 return;
110 }
111
112 static int lookup_table(const struct ld_table *tab, const char *str)
113 {
114 const struct ld_table *t;
115
116 for (t = tab; t && t->name; t++)
117 if (!strcasecmp(t->name, str))
118 return t->value;
119 return -1;
120 }
121
122 static void print_table(FILE * out, const struct ld_table *tab)
123 {
124 const struct ld_table *t;
125 int i;
126
127 for (t = tab, i = 1; t && t->name; t++, i++) {
128 fprintf(out, " %-10s", t->name);
129 if (!(i % 6))
130 fputc('\n', out);
131 }
132 }
133
134 static int parse_iflag(char *str, int *set_iflag, int *clr_iflag)
135 {
136 int iflag;
137 char *s;
138
139 for (s = strtok(str, ","); s != NULL; s = strtok(NULL, ",")) {
140 if (*s == '-')
141 s++;
142 if ((iflag = lookup_table(ld_iflags, s)) < 0)
143 iflag = strtos32_or_err(s, _("invalid iflag"));
144 if (s > str && *(s - 1) == '-')
145 *clr_iflag |= iflag;
146 else
147 *set_iflag |= iflag;
148 }
149 dbg("iflag (set/clear): %d/%d", *set_iflag, *clr_iflag);
150 return 0;
151 }
152
153
154 static void __attribute__ ((__noreturn__)) usage(int exitcode)
155 {
156 FILE *out = exitcode == EXIT_SUCCESS ? stdout : stderr;
157
158 fputs(USAGE_HEADER, out);
159 fprintf(out, _(" %s [options] <ldisc> <device>\n"), program_invocation_short_name);
160 fputs(USAGE_OPTIONS, out);
161
162 fputs(_(" -d, --debug print verbose messages to stderr\n"), out);
163 fputs(_(" -s, --speed <value> set serial line speed\n"), out);
164 fputs(_(" -7, --sevenbits set character size to 7 bits\n"), out);
165 fputs(_(" -8, --eightbits set character size to 8 bits\n"), out);
166 fputs(_(" -n, --noparity set parity to none\n"), out);
167 fputs(_(" -e, --evenparity set parity to even\n"), out);
168 fputs(_(" -o, --oddparity set parity to odd\n"), out);
169 fputs(_(" -1, --onestopbit set stop bits to one\n"), out);
170 fputs(_(" -2, --twostopbits set stop bits to two\n"), out);
171 fputs(_(" -i, --iflag [-]<iflag> set input mode flag\n"), out);
172
173 fputs(USAGE_SEPARATOR, out);
174 fputs(USAGE_HELP, out);
175 fputs(USAGE_VERSION, out);
176 fputs(_("\nKnown <ldisc> names:\n"), out);
177 print_table(out, ld_discs);
178 fputs(_("\nKnown <iflag> names:\n"), out);
179 print_table(out, ld_iflags);
180 fputc('\n', out);
181 fprintf(out, USAGE_MAN_TAIL("ldattach(8)"));
182 exit(exitcode);
183 }
184
185 static int my_cfsetspeed(struct termios *ts, int speed)
186 {
187 /* Standard speeds
188 * -- cfsetspeed() is able to translate number to Bxxx constants
189 */
190 if (cfsetspeed(ts, speed) == 0)
191 return 0;
192
193 /* Nonstandard speeds
194 * -- we have to bypass glibc and set the speed manually (because glibc
195 * checks for speed and supports Bxxx bit rates only)...
196 */
197 #ifdef _HAVE_STRUCT_TERMIOS_C_ISPEED
198 # define BOTHER 0010000 /* non standard rate */
199 dbg("using non-standard speeds");
200 ts->c_ospeed = ts->c_ispeed = speed;
201 ts->c_cflag &= ~CBAUD;
202 ts->c_cflag |= BOTHER;
203 return 0;
204 #else
205 return -1;
206 #endif
207 }
208
209 int main(int argc, char **argv)
210 {
211 int tty_fd;
212 struct termios ts;
213 int speed = 0, bits = '-', parity = '-', stop = '-';
214 int set_iflag = 0, clr_iflag = 0;
215 int ldisc;
216 int optc;
217 char *dev;
218 static const struct option opttbl[] = {
219 {"speed", required_argument, NULL, 's'},
220 {"sevenbits", no_argument, NULL, '7'},
221 {"eightbits", no_argument, NULL, '8'},
222 {"noparity", no_argument, NULL, 'n'},
223 {"evenparity", no_argument, NULL, 'e'},
224 {"oddparity", no_argument, NULL, 'o'},
225 {"onestopbit", no_argument, NULL, '1'},
226 {"twostopbits", no_argument, NULL, '2'},
227 {"iflag", required_argument, NULL, 'i'},
228 {"help", no_argument, NULL, 'h'},
229 {"version", no_argument, NULL, 'V'},
230 {"debug", no_argument, NULL, 'd'},
231 {NULL, 0, NULL, 0}
232 };
233
234 setlocale(LC_ALL, "");
235 bindtextdomain(PACKAGE, LOCALEDIR);
236 textdomain(PACKAGE);
237 atexit(close_stdout);
238
239 /* parse options */
240 if (argc == 0)
241 usage(EXIT_SUCCESS);
242 while ((optc =
243 getopt_long(argc, argv, "dhV78neo12s:i:", opttbl,
244 NULL)) >= 0) {
245 switch (optc) {
246 case 'd':
247 debug = 1;
248 break;
249 case '1':
250 case '2':
251 stop = optc;
252 break;
253 case '7':
254 case '8':
255 bits = optc;
256 break;
257 case 'n':
258 case 'e':
259 case 'o':
260 parity = optc;
261 break;
262 case 's':
263 speed = strtos32_or_err(optarg, _("invalid speed argument"));
264 break;
265 case 'i':
266 parse_iflag(optarg, &set_iflag, &clr_iflag);
267 break;
268 case 'V':
269 printf(UTIL_LINUX_VERSION);
270 return EXIT_SUCCESS;
271 case 'h':
272 usage(EXIT_SUCCESS);
273 default:
274 warnx(_("invalid option"));
275 usage(EXIT_FAILURE);
276 }
277 }
278
279 if (argc - optind != 2)
280 usage(EXIT_FAILURE);
281
282 /* parse line discipline specification */
283 ldisc = lookup_table(ld_discs, argv[optind]);
284 if (ldisc < 0)
285 ldisc = strtos32_or_err(argv[optind], _("invalid line discipline argument"));
286
287 /* open device */
288 dev = argv[optind + 1];
289 if ((tty_fd = open(dev, O_RDWR | O_NOCTTY)) < 0)
290 err(EXIT_FAILURE, _("cannot open %s"), dev);
291 if (!isatty(tty_fd))
292 errx(EXIT_FAILURE, _("%s is not a serial line"), dev);
293
294 dbg("opened %s", dev);
295
296 /* set line speed and format */
297 if (tcgetattr(tty_fd, &ts) < 0)
298 err(EXIT_FAILURE,
299 _("cannot get terminal attributes for %s"), dev);
300 cfmakeraw(&ts);
301 if (speed && my_cfsetspeed(&ts, speed) < 0)
302 errx(EXIT_FAILURE, _("speed %d unsupported"), speed);
303
304 switch (stop) {
305 case '1':
306 ts.c_cflag &= ~CSTOPB;
307 break;
308 case '2':
309 ts.c_cflag |= CSTOPB;
310 break;
311 case '-':
312 break;
313 default:
314 abort();
315 }
316 switch (bits) {
317 case '7':
318 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
319 break;
320 case '8':
321 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
322 break;
323 case '-':
324 break;
325 default:
326 abort();
327 }
328 switch (parity) {
329 case 'n':
330 ts.c_cflag &= ~(PARENB | PARODD);
331 break;
332 case 'e':
333 ts.c_cflag |= PARENB;
334 ts.c_cflag &= ~PARODD;
335 break;
336 case 'o':
337 ts.c_cflag |= (PARENB | PARODD);
338 break;
339 case '-':
340 break;
341 default:
342 abort();
343 }
344
345 ts.c_cflag |= CREAD; /* just to be on the safe side */
346 ts.c_iflag |= set_iflag;
347 ts.c_iflag &= ~clr_iflag;
348
349 if (tcsetattr(tty_fd, TCSAFLUSH, &ts) < 0)
350 err(EXIT_FAILURE,
351 _("cannot set terminal attributes for %s"), dev);
352
353 dbg("set to raw %d %c%c%c: cflag=0x%x",
354 speed, bits, parity, stop, ts.c_cflag);
355
356 /* Attach the line discpline. */
357 if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
358 err(EXIT_FAILURE, _("cannot set line discipline"));
359
360 dbg("line discipline set to %d", ldisc);
361
362 /* Go into background if not in debug mode. */
363 if (!debug && daemon(0, 0) < 0)
364 err(EXIT_FAILURE, _("cannot daemonize"));
365
366 /* Sleep to keep the line discipline active. */
367 pause();
368
369 exit(EXIT_SUCCESS);
370 }