]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/ldattach.c
25903b4eb737b9df9c3e66bee3b724b5ca8fcfaa
[thirdparty/util-linux.git] / sys-utils / ldattach.c
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Line discipline loading daemon open a serial device and attach a line
10 * discipline on it.
11 */
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <getopt.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <termios.h>
22 #include <unistd.h>
23
24 #include "c.h"
25 #include "cctype.h"
26 #include "all-io.h"
27 #include "nls.h"
28 #include "strutils.h"
29 #include "closestream.h"
30
31 #include <signal.h>
32 #include <sys/socket.h>
33 #include <linux/if.h>
34
35 #include <linux/tty.h> /* for N_GSM0710 */
36
37 #ifdef LINUX_GSMMUX_H
38 # include <linux/gsmmux.h> /* Add by guowenxue */
39 #else
40 struct gsm_config
41 {
42 unsigned int adaption;
43 unsigned int encapsulation;
44 unsigned int initiator;
45 unsigned int t1;
46 unsigned int t2;
47 unsigned int t3;
48 unsigned int n2;
49 unsigned int mru;
50 unsigned int mtu;
51 unsigned int k;
52 unsigned int i;
53 unsigned int unused[8]; /* Padding for expansion without
54 breaking stuff */
55 };
56 # define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)
57 # define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)
58 #endif
59
60 #ifndef N_GIGASET_M101
61 # define N_GIGASET_M101 16
62 #endif
63
64 #ifndef N_PPS
65 # define N_PPS 18
66 #endif
67
68 #ifndef N_GSM0710
69 # define N_GSM0710 21
70 #endif
71
72 #define MAXINTROPARMLEN 32
73
74 /* attach a line discipline ioctl */
75 #ifndef TIOCSETD
76 # define TIOCSETD 0x5423
77 #endif
78
79 static int debug = 0;
80
81 struct ld_table {
82 const char *name;
83 int value;
84 };
85
86 /* currently supported line disciplines, plus some aliases */
87 static const struct ld_table ld_discs[] = {
88 { "TTY", N_TTY },
89 { "SLIP", N_SLIP },
90 { "MOUSE", N_MOUSE },
91 { "PPP", N_PPP },
92 { "STRIP", N_STRIP },
93 { "AX25", N_AX25 },
94 { "X25", N_X25 },
95 { "6PACK", N_6PACK },
96 { "R3964", N_R3964 },
97 { "IRDA", N_IRDA },
98 { "HDLC", N_HDLC },
99 { "SYNC_PPP", N_SYNC_PPP },
100 { "SYNCPPP", N_SYNC_PPP },
101 { "HCI", N_HCI },
102 { "GIGASET_M101", N_GIGASET_M101 },
103 { "M101", N_GIGASET_M101 },
104 { "GIGASET", N_GIGASET_M101 },
105 { "PPS", N_PPS },
106 { "GSM0710", N_GSM0710},
107 { NULL, 0 }
108 };
109
110 /* known c_iflag names */
111 static const struct ld_table ld_iflags[] =
112 {
113 { "IGNBRK", IGNBRK },
114 { "BRKINT", BRKINT },
115 { "IGNPAR", IGNPAR },
116 { "PARMRK", PARMRK },
117 { "INPCK", INPCK },
118 { "ISTRIP", ISTRIP },
119 { "INLCR", INLCR },
120 { "IGNCR", IGNCR },
121 { "ICRNL", ICRNL },
122 { "IUCLC", IUCLC },
123 { "IXON", IXON },
124 { "IXANY", IXANY },
125 { "IXOFF", IXOFF },
126 { "IMAXBEL", IMAXBEL },
127 { "IUTF8", IUTF8 },
128 { NULL, 0 }
129 };
130
131 static void __attribute__((__format__ (__printf__, 1, 2)))
132 dbg(char *fmt, ...)
133 {
134 va_list args;
135
136 if (debug == 0)
137 return;
138 fflush(NULL);
139 va_start(args, fmt);
140 #ifdef HAVE_VWARNX
141 vwarnx(fmt, args);
142 #else
143 fprintf(stderr, "%s: ", program_invocation_short_name);
144 vfprintf(stderr, fmt, args);
145 fprintf(stderr, "\n");
146 #endif
147 va_end(args);
148 fflush(NULL);
149 }
150
151 static int lookup_table(const struct ld_table *tab, const char *str)
152 {
153 const struct ld_table *t;
154
155 for (t = tab; t && t->name; t++)
156 if (!c_strcasecmp(t->name, str))
157 return t->value;
158 return -1;
159 }
160
161 static void print_table(FILE * out, const struct ld_table *tab)
162 {
163 const struct ld_table *t;
164 int i;
165
166 for (t = tab, i = 1; t && t->name; t++, i++) {
167 fprintf(out, " %-12s", t->name);
168 if (!(i % 5))
169 fputc('\n', out);
170 }
171 }
172
173 static int parse_iflag(char *str, int *set_iflag, int *clr_iflag)
174 {
175 int iflag;
176 char *s;
177
178 for (s = strtok(str, ","); s != NULL; s = strtok(NULL, ",")) {
179 if (*s == '-')
180 s++;
181 if ((iflag = lookup_table(ld_iflags, s)) < 0)
182 iflag = strtos32_or_err(s, _("invalid iflag"));
183 if (s > str && *(s - 1) == '-')
184 *clr_iflag |= iflag;
185 else
186 *set_iflag |= iflag;
187 }
188 dbg("iflag (set/clear): %d/%d", *set_iflag, *clr_iflag);
189 return 0;
190 }
191
192
193 static void __attribute__((__noreturn__)) usage(void)
194 {
195 FILE *out = stdout;
196
197 fputs(USAGE_HEADER, out);
198 fprintf(out, _(" %s [options] <ldisc> <device>\n"), program_invocation_short_name);
199
200 fputs(USAGE_SEPARATOR, out);
201 fputs(_("Attach a line discipline to a serial line.\n"), out);
202
203 fputs(USAGE_OPTIONS, out);
204 fputs(_(" -d, --debug print verbose messages to stderr\n"), out);
205 fputs(_(" -s, --speed <value> set serial line speed\n"), out);
206 fputs(_(" -c, --intro-command <string> intro sent before ldattach\n"), out);
207 fputs(_(" -p, --pause <seconds> pause between intro and ldattach\n"), out);
208 fputs(_(" -7, --sevenbits set character size to 7 bits\n"), out);
209 fputs(_(" -8, --eightbits set character size to 8 bits\n"), out);
210 fputs(_(" -n, --noparity set parity to none\n"), out);
211 fputs(_(" -e, --evenparity set parity to even\n"), out);
212 fputs(_(" -o, --oddparity set parity to odd\n"), out);
213 fputs(_(" -1, --onestopbit set stop bits to one\n"), out);
214 fputs(_(" -2, --twostopbits set stop bits to two\n"), out);
215 fputs(_(" -i, --iflag [-]<iflag> set input mode flag\n"), out);
216
217 fputs(USAGE_SEPARATOR, out);
218 fprintf(out, USAGE_HELP_OPTIONS(25));
219
220 fputs(_("\nKnown <ldisc> names:\n"), out);
221 print_table(out, ld_discs);
222 fputs(USAGE_SEPARATOR, out);
223
224 fputs(_("\nKnown <iflag> names:\n"), out);
225 print_table(out, ld_iflags);
226
227 fprintf(out, USAGE_MAN_TAIL("ldattach(8)"));
228 exit(EXIT_SUCCESS);
229 }
230
231 static int my_cfsetspeed(struct termios *ts, int speed)
232 {
233 /* Standard speeds
234 * -- cfsetspeed() is able to translate number to Bxxx constants
235 */
236 if (cfsetspeed(ts, speed) == 0)
237 return 0;
238
239 /* Nonstandard speeds
240 * -- we have to bypass glibc and set the speed manually (because glibc
241 * checks for speed and supports Bxxx bit rates only)...
242 */
243 #if _HAVE_STRUCT_TERMIOS_C_ISPEED
244 # ifndef BOTHER
245 # define BOTHER 0010000 /* non standard rate */
246 # endif
247 dbg("using non-standard speeds");
248 ts->c_ospeed = ts->c_ispeed = speed;
249 ts->c_cflag &= ~CBAUD;
250 ts->c_cflag |= BOTHER;
251 return 0;
252 #else
253 return -1;
254 #endif
255 }
256
257 static void handler(int s)
258 {
259 dbg("got SIG %i -> exiting", s);
260 _exit(EXIT_SUCCESS);
261 }
262
263 static void gsm0710_set_conf(int tty_fd)
264 {
265 struct gsm_config c;
266
267 /* Add by guowenxue */
268 /* get n_gsm configuration */
269 ioctl(tty_fd, GSMIOC_GETCONF, &c);
270 /* we are initiator and need encoding 0 (basic) */
271 c.initiator = 1;
272 c.encapsulation = 0;
273 /* our modem defaults to a maximum size of 127 bytes */
274 c.mru = 127;
275 c.mtu = 127;
276 /* set the new configuration */
277 ioctl(tty_fd, GSMIOC_SETCONF, &c);
278 /* Add by guowenxue end*/
279 }
280
281 int main(int argc, char **argv)
282 {
283 int tty_fd;
284 struct termios ts;
285 int speed = 0, bits = '-', parity = '-', stop = '-';
286 int set_iflag = 0, clr_iflag = 0;
287 int ldisc;
288 int optc;
289 char *dev;
290 int intropause = 1;
291 char *introparm = NULL;
292
293 static const struct option opttbl[] = {
294 {"speed", required_argument, NULL, 's'},
295 {"sevenbits", no_argument, NULL, '7'},
296 {"eightbits", no_argument, NULL, '8'},
297 {"noparity", no_argument, NULL, 'n'},
298 {"evenparity", no_argument, NULL, 'e'},
299 {"oddparity", no_argument, NULL, 'o'},
300 {"onestopbit", no_argument, NULL, '1'},
301 {"twostopbits", no_argument, NULL, '2'},
302 {"iflag", required_argument, NULL, 'i'},
303 {"help", no_argument, NULL, 'h'},
304 {"version", no_argument, NULL, 'V'},
305 {"debug", no_argument, NULL, 'd'},
306 {"intro-command", required_argument, NULL, 'c'},
307 {"pause", required_argument, NULL, 'p'},
308 {NULL, 0, NULL, 0}
309 };
310
311 signal(SIGKILL, handler);
312 signal(SIGINT, handler);
313
314 setlocale(LC_ALL, "");
315 bindtextdomain(PACKAGE, LOCALEDIR);
316 textdomain(PACKAGE);
317 close_stdout_atexit();
318
319 /* parse options */
320 if (argc == 0)
321 errx(EXIT_FAILURE, _("bad usage"));
322
323 while ((optc =
324 getopt_long(argc, argv, "dhV78neo12s:i:c:p:", opttbl,
325 NULL)) >= 0) {
326 switch (optc) {
327 case 'd':
328 debug = 1;
329 break;
330 case '1':
331 case '2':
332 stop = optc;
333 break;
334 case '7':
335 case '8':
336 bits = optc;
337 break;
338 case 'n':
339 case 'e':
340 case 'o':
341 parity = optc;
342 break;
343 case 's':
344 speed = strtos32_or_err(optarg, _("invalid speed argument"));
345 break;
346 case 'p':
347 intropause = strtou32_or_err(optarg, _("invalid pause argument"));
348 if (intropause > 10)
349 errx(EXIT_FAILURE, "invalid pause: %s", optarg);
350 break;
351 case 'c':
352 introparm = optarg;
353 break;
354 case 'i':
355 parse_iflag(optarg, &set_iflag, &clr_iflag);
356 break;
357
358 case 'V':
359 print_version(EXIT_SUCCESS);
360 case 'h':
361 usage();
362 default:
363 errtryhelp(EXIT_FAILURE);
364 }
365 }
366
367 if (argc - optind != 2) {
368 warnx(_("not enough arguments"));
369 errtryhelp(EXIT_FAILURE);
370 }
371 /* parse line discipline specification */
372 ldisc = lookup_table(ld_discs, argv[optind]);
373 if (ldisc < 0)
374 ldisc = strtos32_or_err(argv[optind], _("invalid line discipline argument"));
375
376 /* ldisc specific option settings */
377 if (ldisc == N_GIGASET_M101) {
378 /* device specific defaults for line speed and data format */
379 if (speed == 0)
380 speed = 115200;
381 if (bits == '-')
382 bits = '8';
383 if (parity == '-')
384 parity = 'n';
385 if (stop == '-')
386 stop = '1';
387 }
388
389 /* open device */
390 dev = argv[optind + 1];
391 if ((tty_fd = open(dev, O_RDWR | O_NOCTTY)) < 0)
392 err(EXIT_FAILURE, _("cannot open %s"), dev);
393 if (!isatty(tty_fd))
394 errx(EXIT_FAILURE, _("%s is not a serial line"), dev);
395
396 dbg("opened %s", dev);
397
398 /* set line speed and format */
399 if (tcgetattr(tty_fd, &ts) < 0)
400 err(EXIT_FAILURE,
401 _("cannot get terminal attributes for %s"), dev);
402 cfmakeraw(&ts);
403 if (speed && my_cfsetspeed(&ts, speed) < 0)
404 errx(EXIT_FAILURE, _("speed %d unsupported"), speed);
405
406 switch (stop) {
407 case '1':
408 ts.c_cflag &= ~CSTOPB;
409 break;
410 case '2':
411 ts.c_cflag |= CSTOPB;
412 break;
413 case '-':
414 break;
415 default:
416 abort();
417 }
418 switch (bits) {
419 case '7':
420 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS7;
421 break;
422 case '8':
423 ts.c_cflag = (ts.c_cflag & ~CSIZE) | CS8;
424 break;
425 case '-':
426 break;
427 default:
428 abort();
429 }
430 switch (parity) {
431 case 'n':
432 ts.c_cflag &= ~(PARENB | PARODD);
433 break;
434 case 'e':
435 ts.c_cflag |= PARENB;
436 ts.c_cflag &= ~PARODD;
437 break;
438 case 'o':
439 ts.c_cflag |= (PARENB | PARODD);
440 break;
441 case '-':
442 break;
443 default:
444 abort();
445 }
446
447 ts.c_cflag |= CREAD; /* just to be on the safe side */
448 ts.c_iflag |= set_iflag;
449 ts.c_iflag &= ~clr_iflag;
450
451 if (tcsetattr(tty_fd, TCSAFLUSH, &ts) < 0)
452 err(EXIT_FAILURE,
453 _("cannot set terminal attributes for %s"), dev);
454
455 dbg("set to raw %d %c%c%c: cflag=0x%x",
456 speed, bits, parity, stop, ts.c_cflag);
457
458 if (introparm && *introparm)
459 {
460 dbg("intro command is '%s'", introparm);
461 if (write_all(tty_fd, introparm, strlen(introparm)) != 0)
462 err(EXIT_FAILURE,
463 _("cannot write intro command to %s"), dev);
464
465 if (intropause) {
466 dbg("waiting for %d seconds", intropause);
467 sleep(intropause);
468 }
469 }
470
471 /* Attach the line discipline. */
472 if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)
473 err(EXIT_FAILURE, _("cannot set line discipline"));
474
475 dbg("line discipline set to %d", ldisc);
476
477 /* ldisc specific post-attach actions */
478 if (ldisc == N_GSM0710)
479 gsm0710_set_conf(tty_fd);
480
481 /* Go into background if not in debug mode. */
482 if (!debug && daemon(0, 0) < 0)
483 err(EXIT_FAILURE, _("cannot daemonize"));
484
485 /* Sleep to keep the line discipline active. */
486 pause();
487
488 exit(EXIT_SUCCESS);
489 }