busybox/miscutils/rx.c

282 lines
6.3 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
/*
2003-12-20 13:00:35 +05:30
* Copyright: Copyright (C) 2001, Hewlett-Packard Company
* Author: Christopher Hoover <ch@hpl.hp.com>
* Description: xmodem functionality for uploading of kernels
2003-12-20 13:00:35 +05:30
* and the like
* Created at: Thu Dec 20 01:58:08 PST 2001
*
* xmodem functionality for uploading of kernels and the like
2003-12-20 13:00:35 +05:30
*
* Copyright (C) 2001 Hewlett-Packard Laboratories
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
2003-12-20 13:00:35 +05:30
*
* This was originally written for blob and then adapted for busybox.
*/
//config:config RX
//config: bool "rx (2.9 kb)"
//config: default y
//config: help
//config: Receive files using the Xmodem protocol.
2003-12-20 13:00:35 +05:30
//applet:IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_RX) += rx.o
//usage:#define rx_trivial_usage
//usage: "FILE"
//usage:#define rx_full_usage "\n\n"
//usage: "Receive a file using the xmodem protocol"
//usage:
//usage:#define rx_example_usage
//usage: "$ rx /tmp/foo\n"
#include "libbb.h"
2003-12-20 13:00:35 +05:30
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define BS 0x08
#define PAD 0x1A
2003-12-20 13:00:35 +05:30
/*
Cf:
http://www.textfiles.com/apple/xmodem
http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
*/
#define TIMEOUT 1
#define TIMEOUT_LONG 10
#define MAXERRORS 10
#define read_fd STDIN_FILENO
#define write_fd STDOUT_FILENO
static int read_byte(unsigned timeout)
2007-04-17 04:02:04 +05:30
{
unsigned char buf;
2003-12-20 13:00:35 +05:30
int n;
alarm(timeout);
/* NOT safe_read! We want ALRM to interrupt us */
n = read(read_fd, &buf, 1);
2003-12-20 13:00:35 +05:30
alarm(0);
if (n == 1)
return buf;
return -1;
2003-12-20 13:00:35 +05:30
}
static int receive(/*int read_fd, */int file_fd)
2003-12-20 13:00:35 +05:30
{
unsigned char blockBuf[1024];
unsigned blockLength = 0;
unsigned errors = 0;
unsigned wantBlockNo = 1;
unsigned length = 0;
int do_crc = 1;
char reply_char;
unsigned timeout = TIMEOUT_LONG;
2003-12-20 13:00:35 +05:30
/* Flush pending input */
tcflush(read_fd, TCIFLUSH);
2003-12-20 13:00:35 +05:30
/* Ask for CRC; if we get errors, we will go with checksum */
reply_char = 'C';
full_write(write_fd, &reply_char, 1);
2003-12-20 13:00:35 +05:30
for (;;) {
int blockBegin;
int blockNo, blockNoOnesCompl;
int cksum_or_crc;
unsigned expected;
int i, j;
2003-12-20 13:00:35 +05:30
blockBegin = read_byte(timeout);
2003-12-20 13:00:35 +05:30
if (blockBegin < 0)
goto timeout;
/* If last block, remove padding */
if (blockBegin == EOT) {
/* Data blocks can be padded with ^Z characters */
/* This code tries to detect and remove them */
if (blockLength >= 3
&& blockBuf[blockLength - 1] == PAD
&& blockBuf[blockLength - 2] == PAD
&& blockBuf[blockLength - 3] == PAD
) {
while (blockLength
&& blockBuf[blockLength - 1] == PAD
) {
blockLength--;
}
}
}
/* Write previously received block */
errno = 0;
if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_perror_msg(bb_msg_write_error);
goto fatal;
}
2003-12-20 13:00:35 +05:30
timeout = TIMEOUT;
reply_char = NAK;
2003-12-20 13:00:35 +05:30
switch (blockBegin) {
case SOH:
case STX:
break;
case EOT:
reply_char = ACK;
full_write(write_fd, &reply_char, 1);
return length;
2003-12-20 13:00:35 +05:30
default:
goto error;
}
/* Block no */
blockNo = read_byte(TIMEOUT);
2003-12-20 13:00:35 +05:30
if (blockNo < 0)
goto timeout;
/* Block no, in one's complement form */
blockNoOnesCompl = read_byte(TIMEOUT);
2003-12-20 13:00:35 +05:30
if (blockNoOnesCompl < 0)
goto timeout;
if (blockNo != (255 - blockNoOnesCompl)) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg("bad block ones compl");
2003-12-20 13:00:35 +05:30
goto error;
}
blockLength = (blockBegin == SOH) ? 128 : 1024;
for (i = 0; i < blockLength; i++) {
int cc = read_byte(TIMEOUT);
if (cc < 0)
goto timeout;
blockBuf[i] = cc;
2003-12-20 13:00:35 +05:30
}
cksum_or_crc = read_byte(TIMEOUT);
if (cksum_or_crc < 0)
goto timeout;
if (do_crc) {
cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
if (cksum_or_crc < 0)
2003-12-20 13:00:35 +05:30
goto timeout;
}
if (blockNo == ((wantBlockNo - 1) & 0xff)) {
/* a repeat of the last block is ok, just ignore it. */
/* this also ignores the initial block 0 which is */
/* meta data. */
blockLength = 0;
2003-12-20 13:00:35 +05:30
goto next;
}
if (blockNo != (wantBlockNo & 0xff)) {
bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
2003-12-20 13:00:35 +05:30
goto error;
}
expected = 0;
if (do_crc) {
2003-12-20 13:00:35 +05:30
for (i = 0; i < blockLength; i++) {
expected = expected ^ blockBuf[i] << 8;
for (j = 0; j < 8; j++) {
if (expected & 0x8000)
expected = (expected << 1) ^ 0x1021;
2003-12-20 13:00:35 +05:30
else
expected = (expected << 1);
}
2003-12-20 13:00:35 +05:30
}
expected &= 0xffff;
2003-12-20 13:00:35 +05:30
} else {
for (i = 0; i < blockLength; i++)
expected += blockBuf[i];
expected &= 0xff;
}
if (cksum_or_crc != expected) {
bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
: "checksum error, expected 0x%02x, got 0x%02x",
expected, cksum_or_crc);
goto error;
2003-12-20 13:00:35 +05:30
}
wantBlockNo++;
length += blockLength;
next:
2003-12-20 13:00:35 +05:30
errors = 0;
reply_char = ACK;
full_write(write_fd, &reply_char, 1);
2003-12-20 13:00:35 +05:30
continue;
error:
timeout:
blockLength = 0;
2003-12-20 13:00:35 +05:30
errors++;
if (errors == MAXERRORS) {
/* Abort */
/* If were asking for crc, try again w/o crc */
if (reply_char == 'C') {
reply_char = NAK;
2003-12-20 13:00:35 +05:30
errors = 0;
do_crc = 0;
2003-12-20 13:00:35 +05:30
goto timeout;
}
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg("too many errors; giving up");
fatal:
/* 5 CAN followed by 5 BS. Don't try too hard... */
safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
2003-12-20 13:00:35 +05:30
return -1;
}
/* Flush pending input */
tcflush(read_fd, TCIFLUSH);
2003-12-20 13:00:35 +05:30
full_write(write_fd, &reply_char, 1);
} /* for (;;) */
2003-12-20 13:00:35 +05:30
}
2008-07-05 14:48:54 +05:30
static void sigalrm_handler(int UNUSED_PARAM signum)
2003-12-20 13:00:35 +05:30
{
}
int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int rx_main(int argc UNUSED_PARAM, char **argv)
2003-12-20 13:00:35 +05:30
{
struct termios tty, orig_tty;
int termios_err;
int file_fd;
2003-12-20 13:00:35 +05:30
int n;
/* Disabled by vda:
* why we can't receive from stdin? Why we *require*
* controlling tty?? */
/*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
file_fd = xopen(single_argv(argv), O_RDWR|O_CREAT|O_TRUNC);
termios_err = tcgetattr(read_fd, &tty);
if (termios_err == 0) {
//TODO: use set_termios_to_raw()
orig_tty = tty;
cfmakeraw(&tty);
tcsetattr(read_fd, TCSAFLUSH, &tty);
}
/* No SA_RESTART: we want ALRM to interrupt read() */
signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
2003-12-20 13:00:35 +05:30
n = receive(file_fd);
2003-12-20 13:00:35 +05:30
if (termios_err == 0)
tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
if (ENABLE_FEATURE_CLEAN_UP)
close(file_fd);
fflush_stdout_and_exit(n >= 0);
2003-12-20 13:00:35 +05:30
}