summaryrefslogtreecommitdiff
path: root/util-linux/microcom.c
blob: f6f8e07f63f8b84bb33d1f3420aa19ac19b65b8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* microcom.c
 *
 * bare bones 'talk to modem' program - similar to 'cu -l $device'
 * inspired by mgetty's microcom
 *
 */
#include "busybox.h"

int microcom_main(int argc, char **argv);
int microcom_main(int argc, char **argv)
{
	struct pollfd pfd[2];
#define sfd (pfd[1].fd)
	char *device_lock_file = NULL;
	const char *s;
	const char *opt_s = "9600";
	unsigned speed;
	int len;
	int exitcode = 1;
	struct termios tio0, tiosfd, tio;

	getopt32(argv, "s:", &opt_s);
	argc -= optind;
	argv += optind;
	if (!argv[0])
		bb_show_usage();
	speed = xatou(opt_s);

	// try to create lock file in /var/lock
	s = bb_basename(argv[0]);
	if (!s[0]) {
		errno = ENODEV;
		bb_perror_msg_and_die("can't lock device");
	}
	device_lock_file = xasprintf("/var/lock/LCK..%s", s);
	sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
	if (sfd < 0) {
		if (ENABLE_FEATURE_CLEAN_UP)
			free(device_lock_file);
		device_lock_file = NULL;
		if (errno == EEXIST)
			bb_perror_msg_and_die("can't lock device");
		// We don't abort on other errors: /var/lock can be
		// non-writable or non-existent
	} else {
		// %4d to make mgetty happy. It treats 4-bytes lock files as binary,
		// not text, PID. Making 5+ char file. Brrr...
		s = xasprintf("%4d\n", getpid());
		write(sfd, s, strlen(s));
		if (ENABLE_FEATURE_CLEAN_UP)
			free((char*)s);
		close(sfd);
	}

	// open device
	sfd = open(argv[0], O_RDWR | O_NDELAY);
	if (sfd < 0) {
		bb_perror_msg("can't open device");
		goto unlock_and_exit;
	}
	fcntl(sfd, F_SETFL, O_RDWR); // why?

	// put stdin to "raw mode", handle one character at a time
	tcgetattr(STDIN_FILENO, &tio0);
	tio = tio0;
	tio.c_lflag &= ~(ICANON|ECHO);
	tio.c_iflag &= ~(IXON|ICRNL);
	tio.c_oflag &= ~(ONLCR);
	tio.c_cc[VMIN] = 1;
	tio.c_cc[VTIME] = 0;
	if (tcsetattr(STDIN_FILENO, TCSANOW, &tio)) {
		bb_perror_msg("can't tcsetattr for %s", "stdin");
		goto unlock_and_exit;
	}

	/* same thing for modem (plus: set baud rate) - TODO: make CLI option */
	tcgetattr(sfd, &tiosfd);
	tio = tiosfd;
	tio.c_lflag &= ~(ICANON|ECHO);
	tio.c_iflag &= ~(IXON|ICRNL);
	tio.c_oflag &= ~(ONLCR);
	tio.c_cc[VMIN] = 1;
	tio.c_cc[VTIME] = 0;
	cfsetispeed(&tio, tty_value_to_baud(speed));
	cfsetospeed(&tio, tty_value_to_baud(speed));
	if (tcsetattr(sfd, TCSANOW, &tio)) {
		bb_perror_msg("can't tcsetattr for %s", "device");
		goto unlock_and_exit;
	}

	// disable SIGINT
	signal(SIGINT, SIG_IGN);

	// drain stdin
	tcflush(STDIN_FILENO, TCIFLUSH);
	printf("connected to '%s' (%d bps), exit with ctrl-X...\r\n", argv[0], speed);

	// main loop: check with poll(), then read/write bytes across
	pfd[0].fd = STDIN_FILENO;
	pfd[0].events = POLLIN;
	/*pfd[1].fd = sfd;*/
	pfd[1].events = POLLIN;
	while (1) {
		int i;
		while (-1 == poll(pfd, 2, -1) && EINTR == errno)
			continue;
		for (i = 0; i < 2; ++i) {
			if (pfd[i].revents & POLLIN) { 
				len = read(pfd[i].fd, bb_common_bufsiz1, COMMON_BUFSIZE);
				if (len > 0) {
					if (!i && 24 == bb_common_bufsiz1[0])
						goto done; // ^X exits
					write(pfd[1-i].fd, bb_common_bufsiz1, len);
				}
			}
		}
	}
 done:
	tcsetattr(sfd, TCSANOW, &tiosfd);
	tcsetattr(STDIN_FILENO, TCSANOW, &tio0);
	tcflush(STDIN_FILENO, TCIFLUSH);

	if (ENABLE_FEATURE_CLEAN_UP)
		close(sfd);
	exitcode = 0;

 unlock_and_exit:
	// delete lock file
	if (device_lock_file) {
		unlink(device_lock_file);
		if (ENABLE_FEATURE_CLEAN_UP)
			free(device_lock_file);
	}
	return exitcode;
}