minor fix to return E_USAGE on -V instead of exit(0);
[oss-qm-packages.git] / slattach.c
blobcedae1b9c99d6cc0ca05af473780d43f5717b6af
1 /*
2 * slattach A program for handling dialup IP connecions.
3 * This program forces a TTY line to go into a special
4 * terminal line discipline, so that it can be used for
5 * network traffic instead of the regular terminal I/O.
7 * Usage: slattach [-ehlmnqv] [ -k keepalive ] [ -o outfill ]
8 * [-c cmd] [-s speed] [-p protocol] tty | -
10 * Version: @(#)slattach.c 1.20 1999-05-29
12 * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
13 * Copyright 1988-1993 MicroWalt Corporation
15 * Modified:
16 * Alan Cox, <A.Cox@swansea.ac.uk> , July 16 1994
17 * Miquel van Smoorenburg, <miquels@drinkel.ow.org>, October 1994
18 * George Shearer, <gshearer@one.net>, January 3, 1995
19 * Yossi Gottlieb, <yogo@math.tau.ac.il>, February 11, 1995
20 * Peter Tobias, <tobias@et-inf.fho-emden.de>, July 30 1995
21 * Bernd Eckenfels <net-tools@lina.inka.de>, May 29, 1999
22 * added some more printf's for debug and NOBLOCK to open
23 * this should be enough to support 2.2 ttyS-style locks
25 * This program is free software; you can redistribute it
26 * and/or modify it under the terms of the GNU General
27 * Public License as published by the Free Software
28 * Foundation; either version 2 of the License, or (at
29 * your option) any later version.
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <pwd.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <getopt.h>
47 #include <linux/if_slip.h>
49 #if defined(__GLIBC__)
50 #if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
51 # include <termbits.h>
52 #else
53 # include <termios.h>
54 #endif
55 #endif
57 #include "pathnames.h"
58 #include "net-support.h"
59 #include "version.h"
60 #include "config.h"
61 #include "intl.h"
62 #include "util.h"
64 #ifndef _PATH_LOCKD
65 #define _PATH_LOCKD "/var/lock" /* lock files */
66 #endif
67 #ifndef _UID_UUCP
68 #define _UID_UUCP "uucp" /* owns locks */
69 #endif
72 #define DEF_PROTO "cslip"
75 const char *Release = RELEASE,
76 *Version = "@(#) slattach 1.21 (1999-11-21)",
77 *Signature = "net-tools, Fred N. van Kempen et al.";
80 struct {
81 const char *speed;
82 int code;
83 } tty_speeds[] = { /* table of usable baud rates */
84 { "50", B50 }, { "75", B75 },
85 { "110", B110 }, { "300", B300 },
86 { "600", B600 }, { "1200", B1200 },
87 { "2400", B2400 }, { "4800", B4800 },
88 { "9600", B9600 },
89 #ifdef B14400
90 { "14400", B14400 },
91 #endif
92 #ifdef B19200
93 { "19200", B19200 },
94 #endif
95 #ifdef B38400
96 { "38400", B38400 },
97 #endif
98 #ifdef B57600
99 { "57600", B57600 },
100 #endif
101 #ifdef B115200
102 { "115200", B115200 },
103 #endif
104 { NULL, 0 }
106 struct termios tty_saved, /* saved TTY device state */
107 tty_current; /* current TTY device state */
108 int tty_sdisc, /* saved TTY line discipline */
109 tty_ldisc, /* current TTY line discipline */
110 tty_fd = -1; /* TTY file descriptor */
111 int opt_c = 0; /* "command" to run at exit */
112 int opt_e = 0; /* "activate only" flag */
113 int opt_h = 0; /* "hangup" on carrier loss */
114 #ifdef SIOCSKEEPALIVE
115 int opt_k = 0; /* "keepalive" value */
116 #endif
117 int opt_l = 0; /* "lock it" flag */
118 int opt_L = 0; /* clocal flag */
119 int opt_m = 0; /* "set RAW mode" flag */
120 int opt_n = 0; /* "set No Mesg" flag */
121 #ifdef SIOCSOUTFILL
122 int opt_o = 0; /* "outfill" value */
123 #endif
124 int opt_q = 0; /* "quiet" flag */
125 int opt_d = 0; /* debug flag */
126 int opt_v = 0; /* Verbose flag */
128 /* Disable any messages to the input channel of this process. */
129 static int
130 tty_nomesg(int fd)
132 if (opt_n == 0) return(0);
133 return(fchmod(fd, 0600));
136 /* Check for an existing lock file on our device */
137 static int
138 tty_already_locked(char *nam)
140 int i = 0, pid = 0;
141 FILE *fd = (FILE *)0;
143 /* Does the lock file on our device exist? */
144 if ((fd = fopen(nam, "r")) == (FILE *)0)
145 return(0); /* No, return perm to continue */
147 /* Yes, the lock is there. Now let's make sure */
148 /* at least there's no active process that owns */
149 /* that lock. */
150 i = fscanf(fd, "%d", &pid);
151 (void) fclose(fd);
153 if (i != 1) /* Lock file format's wrong! Kill't */
154 return(0);
156 /* We got the pid, check if the process's alive */
157 if (kill(pid, 0) == 0) /* it found process */
158 return(1); /* Yup, it's running... */
160 /* Dead, we can proceed locking this device... */
161 return(0);
164 /* Lock or unlock a terminal line. */
165 static int
166 tty_lock(char *path, int mode)
168 static char saved_path[PATH_MAX];
169 static int saved_lock = 0;
170 struct passwd *pw;
171 int fd;
172 char apid[16];
174 /* We do not lock standard input. */
175 if ((opt_l == 0) || ((path == NULL) && (saved_lock == 0))) return(0);
177 if (mode == 1) { /* lock */
178 sprintf(saved_path, "%s/LCK..%s", _PATH_LOCKD, path);
179 if (tty_already_locked(saved_path)) {
180 fprintf(stderr, _("slattach: /dev/%s already locked!\n"), path);
181 return(-1);
183 if ((fd = creat(saved_path, 0644)) < 0) {
184 if (errno != EEXIST)
185 if (opt_q == 0) fprintf(stderr,
186 _("slattach: tty_lock: (%s): %s\n"),
187 saved_path, strerror(errno));
188 return(-1);
190 sprintf(apid, "%10d\n", getpid());
191 if (write(fd, apid, strlen(apid)) != strlen(apid)) {
192 fprintf(stderr, _("slattach: cannot write PID file\n"));
193 close(fd);
194 unlink(saved_path);
195 return(-1);
198 (void) close(fd);
200 /* Make sure UUCP owns the lockfile. Required by some packages. */
201 if ((pw = getpwnam(_UID_UUCP)) == NULL) {
202 if (opt_q == 0) fprintf(stderr, _("slattach: tty_lock: UUCP user %s unknown!\n"),
203 _UID_UUCP);
204 return(0); /* keep the lock anyway */
206 (void) chown(saved_path, pw->pw_uid, pw->pw_gid);
207 saved_lock = 1;
208 } else { /* unlock */
209 if (saved_lock != 1) return(0);
210 if (unlink(saved_path) < 0) {
211 if (opt_q == 0) fprintf(stderr,
212 "slattach: tty_unlock: (%s): %s\n", saved_path,
213 strerror(errno));
214 return(-1);
216 saved_lock = 0;
219 return(0);
223 /* Find a serial speed code in the table. */
224 static int
225 tty_find_speed(const char *speed)
227 int i;
229 i = 0;
230 while (tty_speeds[i].speed != NULL) {
231 if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
232 i++;
234 return(-EINVAL);
238 /* Set the number of stop bits. */
239 static int
240 tty_set_stopbits(struct termios *tty, char *stopbits)
242 if (opt_d) printf("slattach: tty_set_stopbits: %c\n", *stopbits);
243 switch(*stopbits) {
244 case '1':
245 tty->c_cflag &= ~CSTOPB;
246 break;
248 case '2':
249 tty->c_cflag |= CSTOPB;
250 break;
252 default:
253 return(-EINVAL);
255 return(0);
259 /* Set the number of data bits. */
260 static int
261 tty_set_databits(struct termios *tty, char *databits)
263 if (opt_d) printf("slattach: tty_set_databits: %c\n", *databits);
264 tty->c_cflag &= ~CSIZE;
265 switch(*databits) {
266 case '5':
267 tty->c_cflag |= CS5;
268 break;
270 case '6':
271 tty->c_cflag |= CS6;
272 break;
274 case '7':
275 tty->c_cflag |= CS7;
276 break;
278 case '8':
279 tty->c_cflag |= CS8;
280 break;
282 default:
283 return(-EINVAL);
285 return(0);
289 /* Set the type of parity encoding. */
290 static int
291 tty_set_parity(struct termios *tty, char *parity)
293 if (opt_d) printf("slattach: tty_set_parity: %c\n", *parity);
294 switch(toupper(*parity)) {
295 case 'N':
296 tty->c_cflag &= ~(PARENB | PARODD);
297 break;
299 case 'O':
300 tty->c_cflag &= ~(PARENB | PARODD);
301 tty->c_cflag |= (PARENB | PARODD);
302 break;
304 case 'E':
305 tty->c_cflag &= ~(PARENB | PARODD);
306 tty->c_cflag |= (PARENB);
307 break;
309 default:
310 return(-EINVAL);
312 return(0);
316 /* Set the line speed of a terminal line. */
317 static int
318 tty_set_speed(struct termios *tty, const char *speed)
320 int code;
322 if (opt_d) printf("slattach: tty_set_speed: %s\n", speed);
323 if ((code = tty_find_speed(speed)) < 0) return(code);
324 tty->c_cflag &= ~CBAUD;
325 tty->c_cflag |= code;
326 return(0);
330 /* Put a terminal line in a transparent state. */
331 static int
332 tty_set_raw(struct termios *tty)
334 int i;
335 int speed;
337 for(i = 0; i < NCCS; i++)
338 tty->c_cc[i] = '\0'; /* no spec chr */
339 tty->c_cc[VMIN] = 1;
340 tty->c_cc[VTIME] = 0;
341 tty->c_iflag = (IGNBRK | IGNPAR); /* input flags */
342 tty->c_oflag = (0); /* output flags */
343 tty->c_lflag = (0); /* local flags */
344 speed = (tty->c_cflag & CBAUD); /* save current speed */
345 tty->c_cflag = (CRTSCTS | HUPCL | CREAD); /* UART flags */
346 if (opt_L)
347 tty->c_cflag |= CLOCAL;
348 tty->c_cflag |= speed; /* restore speed */
349 return(0);
353 /* Fetch the state of a terminal. */
354 static int
355 tty_get_state(struct termios *tty)
357 if (ioctl(tty_fd, TCGETS, tty) < 0) {
358 if (opt_q == 0) fprintf(stderr,
359 "slattach: tty_get_state: %s\n", strerror(errno));
360 return(-errno);
362 return(0);
366 /* Set the state of a terminal. */
367 static int
368 tty_set_state(struct termios *tty)
370 if (ioctl(tty_fd, TCSETS, tty) < 0) {
371 if (opt_q == 0) fprintf(stderr,
372 "slattach: tty_set_state: %s\n", strerror(errno));
373 return(-errno);
375 return(0);
379 /* Get the line discipline of a terminal line. */
380 static int
381 tty_get_disc(int *disc)
383 if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
384 if (opt_q == 0) fprintf(stderr,
385 "slattach: tty_get_disc: %s\n", strerror(errno));
386 return(-errno);
388 return(0);
392 /* Set the line discipline of a terminal line. */
393 static int
394 tty_set_disc(int disc)
396 if (disc == -1) disc = tty_sdisc;
398 if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
399 if (opt_q == 0) fprintf(stderr,
400 "slattach: tty_set_disc(%d, %d): %s\n", tty_fd,
401 disc, strerror(errno));
402 return(-errno);
404 return(0);
408 /* Fetch the name of the network interface attached to this terminal. */
409 static int
410 tty_get_name(char *name)
412 if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
413 if (opt_q == 0)
414 perror("tty_get_name");
415 return(-errno);
417 return(0);
421 /* Hangup the line. */
422 static int
423 tty_hangup(void)
425 struct termios tty;
427 tty = tty_current;
428 (void) tty_set_speed(&tty, "0");
429 if (tty_set_state(&tty) < 0) {
430 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(DROP): %s\n"), strerror(errno));
431 return(-errno);
434 (void) sleep(1);
436 if (tty_set_state(&tty_current) < 0) {
437 if (opt_q == 0) fprintf(stderr, _("slattach: tty_hangup(RAISE): %s\n"), strerror(errno));
438 return(-errno);
440 return(0);
444 /* Close down a terminal line. */
445 static int
446 tty_close(void)
448 (void) tty_set_disc(tty_sdisc);
449 (void) tty_hangup();
450 (void) tty_lock(NULL, 0);
451 return(0);
455 /* Open and initialize a terminal line. */
456 static int
457 tty_open(char *name, const char *speed)
459 char pathbuf[PATH_MAX];
460 register char *path_open, *path_lock;
461 int fd;
463 /* Try opening the TTY device. */
464 if (name != NULL) {
465 if (name[0] != '/') {
466 if (strlen(name + 6) > sizeof(pathbuf)) {
467 if (opt_q == 0) fprintf(stderr,
468 _("slattach: tty name too long\n"));
469 return (-1);
471 sprintf(pathbuf, "/dev/%s", name);
472 path_open = pathbuf;
473 path_lock = name;
474 } else if (!strncmp(name, "/dev/", 5)) {
475 path_open = name;
476 path_lock = name + 5;
477 } else {
478 path_open = name;
479 path_lock = name;
481 if (opt_d) printf("slattach: tty_open: looking for lock\n");
482 if (tty_lock(path_lock, 1)) return(-1); /* can we lock the device? */
483 if (opt_d) printf("slattach: tty_open: trying to open %s\n", path_open);
484 if ((fd = open(path_open, O_RDWR|O_NDELAY)) < 0) {
485 if (opt_q == 0) fprintf(stderr,
486 "slattach: tty_open(%s, RW): %s\n",
487 path_open, strerror(errno));
488 return(-errno);
490 tty_fd = fd;
491 if (opt_d) printf("slattach: tty_open: %s (fd=%d) ", path_open, fd);
492 } else {
493 tty_fd = 0;
496 /* Fetch the current state of the terminal. */
497 if (tty_get_state(&tty_saved) < 0) {
498 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current state!\n"));
499 return(-errno);
501 tty_current = tty_saved;
503 /* Fetch the current line discipline of this terminal. */
504 if (tty_get_disc(&tty_sdisc) < 0) {
505 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot get current line disc!\n"));
506 return(-errno);
508 tty_ldisc = tty_sdisc;
510 /* Put this terminal line in a 8-bit transparent mode. */
511 if (opt_m == 0) {
512 if (tty_set_raw(&tty_current) < 0) {
513 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set RAW mode!\n"));
514 return(-errno);
517 /* Set the default speed if we need to. */
518 if (speed != NULL) {
519 if (tty_set_speed(&tty_current, speed) != 0) {
520 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set %s bps!\n"),
521 speed);
522 return(-errno);
526 /* Set up a completely 8-bit clean line. */
527 if (tty_set_databits(&tty_current, "8") ||
528 tty_set_stopbits(&tty_current, "1") ||
529 tty_set_parity(&tty_current, "N")) {
530 if (opt_q == 0) fprintf(stderr, _("slattach: tty_open: cannot set 8N1 mode!\n"));
531 return(-errno);
534 /* Set the new line mode. */
535 if ((fd = tty_set_state(&tty_current)) < 0) return(fd);
538 /* OK, line is open. Do we need to "silence" it? */
539 (void) tty_nomesg(tty_fd);
541 return(0);
545 /* Catch any signals. */
546 static void
547 sig_catch(int sig)
549 /* (void) signal(sig, sig_catch); */
550 tty_close();
551 exit(0);
555 static void
556 usage(void)
558 char *usage_msg = "Usage: slattach [-ehlLmnqv] "
559 #ifdef SIOCSKEEPALIVE
560 "[-k keepalive] "
561 #endif
562 #ifdef SIOCSOUTFILL
563 "[-o outfill] "
564 #endif
565 "[-c cmd] [-s speed] [-p protocol] tty | -\n"
566 " slattach -V | --version\n";
568 fprintf(stderr, usage_msg);
569 exit(1);
573 static void
574 version(void)
576 printf("%s\n%s\n%s\n", Release, Version, Signature);
577 exit(E_VERSION);
582 main(int argc, char *argv[])
584 char path_buf[128];
585 char *path_dev;
586 char buff[128];
587 const char *speed = NULL;
588 const char *proto = DEF_PROTO;
589 const char *extcmd = NULL;
590 int s;
591 static struct option longopts[] = {
592 { "version", 0, NULL, 'V' },
593 { NULL, 0, NULL, 0 }
596 strcpy(path_buf, "");
597 path_dev = path_buf;
599 /* Scan command line for any arguments. */
600 opterr = 0;
601 while ((s = getopt_long(argc, argv, "c:ehlLmnp:qs:vdVk:o:", longopts, NULL)) != EOF) switch(s) {
602 case 'c':
603 extcmd = optarg;
604 break;
606 case 'e':
607 opt_e = 1 - opt_e;
608 break;
610 case 'h':
611 opt_h = 1 - opt_h;
612 break;
614 #ifdef SIOCSKEEPALIVE
615 case 'k':
616 opt_k = atoi(optarg);
617 break;
618 #endif
620 case 'L':
621 opt_L = 1 - opt_L;
622 break;
624 case 'l':
625 opt_l = 1 - opt_l;
626 break;
628 case 'm':
629 opt_m = 1 - opt_m;
630 break;
632 case 'n':
633 opt_n = 1 - opt_n;
634 break;
636 #ifdef SIOCSOUTFILL
637 case 'o':
638 opt_o = atoi(optarg);
639 break;
640 #endif
642 case 'p':
643 proto = optarg;
644 break;
646 case 'q':
647 opt_q = 1 - opt_q;
648 break;
650 case 's':
651 speed = optarg;
652 break;
654 case 'd':
655 opt_d = 1 - opt_d;
656 break;
658 case 'v':
659 opt_v = 1 - opt_v;
660 break;
662 case 'V':
663 version();
664 /*NOTREACHED*/
666 default:
667 usage();
668 /*NOTREACHED*/
671 if (setvbuf(stdout,0,_IOLBF,0)) {
672 if (opt_q == 0) fprintf(stderr, _("slattach: setvbuf(stdout,0,_IOLBF,0) : %s\n"),
673 strerror(errno));
674 exit(1);
677 activate_init();
679 if (!strcmp(proto, "tty"))
680 opt_m++;
682 /* Is a terminal given? */
683 if (optind != (argc - 1)) usage();
684 safe_strncpy(path_buf, argv[optind], sizeof(path_buf));
685 if (!strcmp(path_buf, "-")) {
686 opt_e = 1;
687 path_dev = NULL;
688 if (tty_open(NULL, speed) < 0) { return(3); }
689 } else {
690 path_dev = path_buf;
691 if (tty_open(path_dev, speed) < 0) { return(3); }
694 /* Start the correct protocol. */
695 if (!strcmp(proto, "tty")) {
696 tty_sdisc = N_TTY;
697 tty_close();
698 return(0);
700 if (activate_ld(proto, tty_fd))
701 return(1);
702 if ((opt_v == 1) || (opt_d == 1)) {
703 if (tty_get_name(buff)) { return(3); }
704 printf(_("%s started"), proto);
705 if (path_dev != NULL) printf(_(" on %s"), path_dev);
706 printf(_(" interface %s\n"), buff);
709 /* Configure keepalive and outfill. */
710 #ifdef SIOCSKEEPALIVE
711 if (opt_k && (ioctl(tty_fd, SIOCSKEEPALIVE, &opt_k) < 0))
712 fprintf(stderr, "slattach: ioctl(SIOCSKEEPALIVE): %s\n", strerror(errno));
713 #endif
714 #ifdef SIOCSOUTFILL
715 if (opt_o && (ioctl(tty_fd, SIOCSOUTFILL, &opt_o) < 0))
716 fprintf(stderr, "slattach: ioctl(SIOCSOUTFILL): %s\n", strerror(errno));
717 #endif
719 (void) signal(SIGHUP, sig_catch);
720 (void) signal(SIGINT, sig_catch);
721 (void) signal(SIGQUIT, sig_catch);
722 (void) signal(SIGTERM, sig_catch);
724 /* Wait until we get killed if hanging on a terminal. */
725 if (opt_e == 0) {
726 while(1) {
727 if(opt_h == 1) { /* hangup on carrier loss */
728 int n = 0;
730 ioctl(tty_fd, TIOCMGET, &n);
731 if(!(n & TIOCM_CAR))
732 break;
733 sleep(15);
735 else
736 sleep(60);
739 tty_close();
740 if(extcmd) /* external command on exit */
741 system(extcmd);
743 exit(0);