xref: /openbsd/usr.bin/cu/xmodem.c (revision 898184e3)
1 /* $OpenBSD: xmodem.c,v 1.4 2013/01/17 21:10:24 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 
39 volatile sig_atomic_t xmodem_stop;
40 
41 void
42 xmodem_signal(int sig)
43 {
44 	xmodem_stop = 1;
45 }
46 
47 int
48 xmodem_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 
65 int
66 xmodem_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 
83 void
84 xmodem_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, total;
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 	total = 1;
117 	for (;;) {
118 		len = fread(buf + 3, 1, XMODEM_BLOCK, f);
119 		if (len == 0)
120 			break;
121 		memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len);
122 
123 		buf[0] = XMODEM_SOH;
124 		buf[1] = num;
125 		buf[2] = 255 - num;
126 
127 		buf[3 + XMODEM_BLOCK] = 0;
128 		for (i = 0; i < 128; i++)
129 			buf[3 + XMODEM_BLOCK] += buf[3 + i];
130 
131 		for (i = 0; i < XMODEM_RETRIES; i++) {
132 			if (xmodem_stop) {
133 				errno = EINTR;
134 				goto fail;
135 			}
136 			cu_warnx("%s: sending block %u (attempt %u)", file,
137 			    total, 1 + i);
138 			if (xmodem_write(buf, sizeof buf) != 0)
139 				goto fail;
140 
141 			if (xmodem_read(&c) != 0)
142 				goto fail;
143 			if (c == XMODEM_ACK)
144 				break;
145 			if (c != XMODEM_NAK) {
146 				cu_warnx("%s: unexpected response \%03hho",
147 				    file, c);
148 			}
149 		}
150 		if (i == XMODEM_RETRIES) {
151 			cu_warnx("%s: too many retries", file);
152 			goto out;
153 		}
154 
155 		if (len < XMODEM_BLOCK)
156 			break;
157 		num++;
158 		total++;
159 	}
160 
161 	buf[0] = XMODEM_EOT;
162 	if (xmodem_write(buf, 1) != 0)
163 		goto fail;
164 	cu_warnx("%s: completed %u blocks", file, num);
165 
166 	goto out;
167 
168 fail:
169 	cu_warn("%s", file);
170 
171 out:
172 	set_termios();
173 
174 	sigaction(SIGINT, &oact, NULL);
175 
176 	fclose(f);
177 }
178