xmodem.c revision 1.1
1/* $OpenBSD: xmodem.c,v 1.1 2012/07/10 11:42:02 nicm Exp $ */
2
3/*
4 * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <errno.h>
22#include <signal.h>
23#include <stdio.h>
24#include <string.h>
25#include <termios.h>
26#include <unistd.h>
27
28#include "cu.h"
29
30#define XMODEM_BLOCK 128
31#define XMODEM_RETRIES 10
32
33#define XMODEM_SOH '\001'
34#define XMODEM_EOT '\004'
35#define XMODEM_ACK '\006'
36#define XMODEM_NAK '\025'
37#define XMODEM_SUB '\032'
38
39volatile sig_atomic_t xmodem_stop;
40
41void
42xmodem_signal(int sig)
43{
44	xmodem_stop = 1;
45}
46
47int
48xmodem_read(char *c)
49{
50	for (;;) {
51		switch (read(line_fd, c, 1)) {
52		case -1:
53			if (errno == EINTR && !xmodem_stop)
54				continue;
55			return (-1);
56		case 0:
57			errno = EPIPE;
58			return (-1);
59		case 1:
60			return (0);
61		}
62	}
63}
64
65int
66xmodem_write(const u_char *buf, size_t len)
67{
68	ssize_t	n;
69
70	while (len > 0) {
71		n = write(line_fd, buf, len);
72		if (n == -1) {
73			if (errno == EINTR && !xmodem_stop)
74				continue;
75			return (-1);
76		}
77		buf += n;
78		len -= n;
79	}
80	return (0);
81}
82
83void
84xmodem_send(const char *file)
85{
86	FILE			*f;
87	u_char			 buf[3 + XMODEM_BLOCK + 1], c;
88	size_t			 len;
89	uint8_t			 num;
90	u_int			 i;
91	struct termios		 tio;
92	struct sigaction	 act, oact;
93
94	f = fopen(file, "r");
95	if (f == NULL) {
96		cu_warn("%s", file);
97		return;
98	}
99
100	memset(&act, 0, sizeof(act));
101	sigemptyset(&act.sa_mask);
102	act.sa_flags = 0;
103	act.sa_handler = xmodem_signal;
104	if (sigaction(SIGINT, &act, &oact) != 0)
105		cu_err(1, "sigaction");
106	xmodem_stop = 0;
107
108	if (isatty(STDIN_FILENO)) {
109		memcpy(&tio, &saved_tio, sizeof(tio));
110		tio.c_lflag &= ~ECHO;
111		if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0)
112			cu_err(1, "tcsetattr");
113	}
114
115	num = 1;
116	for (;;) {
117		len = fread(buf + 3, 1, XMODEM_BLOCK, f);
118		if (len == 0)
119			break;
120		memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len);
121
122		buf[0] = XMODEM_SOH;
123		buf[1] = num;
124		buf[2] = 255 - num;
125
126		buf[3 + XMODEM_BLOCK] = 0;
127		for (i = 0; i < 128; i++)
128			buf[3 + XMODEM_BLOCK] += buf[3 + i];
129
130		for (i = 0; i < XMODEM_RETRIES; i++) {
131			if (xmodem_stop) {
132				errno = EINTR;
133				goto fail;
134			}
135			cu_warnx("%s: sending block %u (attempt %u)", file,
136			    num, 1 + i);
137			if (xmodem_write(buf, sizeof buf) != 0)
138				goto fail;
139
140			if (xmodem_read(&c) != 0)
141				goto fail;
142			if (c == XMODEM_ACK)
143				break;
144			if (c != XMODEM_NAK) {
145				cu_warnx("%s: unexpected response \%03hho",
146				    file, c);
147			}
148		}
149		if (i == XMODEM_RETRIES) {
150			cu_warnx("%s: too many retries", file);
151			goto out;
152		};
153
154		if (len < XMODEM_BLOCK)
155			break;
156		num++;
157	}
158
159	buf[0] = XMODEM_EOT;
160	if (xmodem_write(buf, 1) != 0)
161		goto fail;
162	cu_warnx("%s: completed %u blocks", file, num);
163
164	goto out;
165
166fail:
167	cu_warn("%s", file);
168
169out:
170	set_termios();
171
172	sigaction(SIGINT, &oact, NULL);
173
174	return;
175}
176