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