diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b67c510..999d767 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,9 +11,6 @@ find_package(Libev REQUIRED) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/buffer ${LIBEV_INCLUDE_DIR}) set(EXTRA_LIBS ${LIBEV_LIBRARY} util crypt m) -add_definitions(-DRTTY_FILE_UNIX_SOCKET_S="/var/run/rttyfile.sock") -add_definitions(-DRTTY_FILE_UNIX_SOCKET_C="/var/run/rttyfile_c.sock") - set(RTTY_SSL_SUPPORT_CONFIG 1) option(RTTY_SSL_SUPPORT "SSL support" ON) @@ -95,7 +92,7 @@ else() endif() add_executable(rtty main.c utils.c buffer/buffer.c log.c net.c net.h rtty.c rtty.h command.c - file.c upfile.c upfile.h downfile.c downfile.h ssl.c ssl.h web.c) + file.c filectl.c ssl.c ssl.h web.c) target_link_libraries(rtty ${EXTRA_LIBS}) # configure a header file to pass some of the CMake settings to the source code diff --git a/src/downfile.c b/src/downfile.c deleted file mode 100644 index c3383df..0000000 --- a/src/downfile.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 Jianhui Zhao - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "file.h" - -static char abspath[PATH_MAX]; -static ev_tstamp start_time; -static struct buffer b; -static int sock; - -static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) -{ - cancel_file_operation(loop, sock); -} - -static void on_socket_read(struct ev_loop *loop, struct ev_io *w, int revents) -{ - int type = read_file_msg(w->fd, &b); - - switch (type) { - case RTTY_FILE_MSG_REQUEST_ACCEPT: - buffer_put_u8(&b, RTTY_FILE_MSG_SAVE_PATH); - buffer_put_string(&b, abspath); - buffer_put_zero(&b, 1); - buffer_pull_to_fd(&b, w->fd, -1); - break; - case RTTY_FILE_MSG_INFO: - printf("Transferring '%s'...\n", (char *)buffer_data(&b)); - buffer_pull(&b, NULL, buffer_length(&b)); - start_time = ev_now(loop); - break; - case RTTY_FILE_MSG_PROGRESS: - update_progress(loop, start_time, &b); - break; - case RTTY_FILE_MSG_BUSY: - fprintf(stderr, "\033[31mRtty is busy\033[0m\n"); - ev_break(loop, EVBREAK_ALL); - break; - case RTTY_FILE_MSG_CANCELED: - puts(""); - ev_break(loop, EVBREAK_ALL); - break; - case RTTY_FILE_MSG_NO_SPACE: - fprintf(stderr, "\033[31mNo enough space\033[0m\n"); - ev_break(loop, EVBREAK_ALL); - break; - default: - break; - } -} - -void download_file() -{ - struct ev_loop *loop = EV_DEFAULT; - static struct ev_signal sw; - struct ev_io ior; - - if (getuid() > 0) { - fprintf(stderr, "Operation not permitted, must be run as root\n"); - return; - } - - if (!realpath(".", abspath)) - return; - - sock = connect_rtty_file_service(); - if (sock < 0) - return; - - request_transfer_file(); - - ev_io_init(&ior, on_socket_read, sock, EV_READ); - ev_io_start(loop, &ior); - - ev_signal_init(&sw, signal_cb, SIGINT); - ev_signal_start(loop, &sw); - - printf("Waiting to receive. Press Ctrl+C to cancel\n"); - - ev_run(loop, 0); - - buffer_free(&b); - close(sock); -} diff --git a/src/downfile.h b/src/downfile.h deleted file mode 100644 index 917090b..0000000 --- a/src/downfile.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 Jianhui Zhao - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RTTY_DOWNFILE_H_ -#define RTTY_DOWNFILE_H_ - -void download_file(); - -#endif diff --git a/src/file.c b/src/file.c index aa526ce..194fe7d 100644 --- a/src/file.c +++ b/src/file.c @@ -27,10 +27,10 @@ #include #include #include -#include -#include -#include #include +#include +#include +#include #include "log.h" #include "file.h" @@ -40,23 +40,56 @@ static uint8_t RTTY_FILE_MAGIC[] = {0xb6, 0xbc, 0xbd}; static char abspath[PATH_MAX]; -static struct buffer b; -static void notify_progress(struct file_context *ctx) +static int send_file_control_msg(int fd, struct file_control_msg *msg) +{ + if (write(fd, msg, sizeof(struct file_control_msg)) < 0) { + log_err("write fifo: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static void file_context_reset(struct file_context *ctx) +{ + if (ctx->fd > 0) { + close(ctx->fd); + ctx->fd = -1; + } + + close(ctx->ctlfd); + + ctx->busy = false; +} + +static void notify_user_canceled(struct rtty *rtty) +{ + buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); + buffer_put_u16be(&rtty->wb, 2); + buffer_put_u8(&rtty->wb, rtty->file_context.sid); + buffer_put_u8(&rtty->wb, RTTY_FILE_MSG_CANCELED); + ev_io_start(rtty->loop, &rtty->iow); +} + +static int notify_progress(struct file_context *ctx) { struct rtty *rtty = container_of(ctx, struct rtty, file_context); + struct file_control_msg msg = { + .type = RTTY_FILE_MSG_PROGRESS + }; ev_tstamp now = ev_now(rtty->loop); if (ctx->remain_size > 0 && now - ctx->last_notify_progress < 0.1) - return; + return 0; ctx->last_notify_progress = now; - buffer_truncate(&b, 0); - buffer_put_u8(&b, RTTY_FILE_MSG_PROGRESS); - buffer_put_u32(&b, ctx->remain_size); - buffer_put_u32(&b, ctx->total_size); - sendto(ctx->sock, buffer_data(&b), buffer_length(&b), 0, - (struct sockaddr *)&ctx->peer_sun, sizeof(struct sockaddr_un)); + memcpy(msg.buf, &ctx->remain_size, 4); + + if (send_file_control_msg(ctx->ctlfd, &msg) < 0) + return -1; + + return 0; } static void on_file_read(struct ev_loop *loop, struct ev_io *w, int revents) @@ -70,6 +103,15 @@ static void on_file_read(struct ev_loop *loop, struct ev_io *w, int revents) return; ret = read(w->fd, buf, sizeof(buf)); + if (ret < 0) { + struct file_control_msg msg = { + .type = RTTY_FILE_MSG_ERR + }; + + send_file_control_msg(ctx->ctlfd, &msg); + goto err; + } + ctx->remain_size -= ret; buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); @@ -79,30 +121,38 @@ static void on_file_read(struct ev_loop *loop, struct ev_io *w, int revents) buffer_put_data(&rtty->wb, buf, ret); ev_io_start(rtty->loop, &rtty->iow); - notify_progress(ctx); - if (ret == 0) { - ctx->busy = false; ev_io_stop(loop, w); - close(ctx->fd); - ctx->fd = -1; + file_context_reset(ctx); + return; } + + if (notify_progress(ctx) < 0) + goto err; + + return; + +err: + ev_io_stop(loop, w); + notify_user_canceled(rtty); + file_context_reset(ctx); } -static void start_upload_file(struct file_context *ctx, struct buffer *info) +static int start_upload_file(struct file_context *ctx, const char *path) { struct rtty *rtty = container_of(ctx, struct rtty, file_context); - uint32_t size = buffer_pull_u32(info); - const char *path = buffer_data(info); const char *name = basename(path); + struct stat st; int fd; fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) { log_err("open '%s' fail\n", path, strerror(errno)); - return; + return -1; } + fstat(fd, &st); + buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); buffer_put_u16be(&rtty->wb, 2 + strlen(name)); buffer_put_u8(&rtty->wb, ctx->sid); @@ -114,24 +164,105 @@ static void start_upload_file(struct file_context *ctx, struct buffer *info) ev_io_start(rtty->loop, &ctx->iof); ctx->fd = fd; - ctx->total_size = size; - ctx->remain_size = size; + ctx->total_size = st.st_size; + ctx->remain_size = st.st_size; - log_info("upload file: %s\n", path); + log_info("upload file: %s, size: %" PRIu64 "\n", path, (uint64_t)st.st_size); + + return 0; } -static void send_canceled_msg(struct rtty *rtty) +bool detect_file_operation(uint8_t *buf, int len, int sid, struct file_context *ctx) { - buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); - buffer_put_u16be(&rtty->wb, 2); - buffer_put_u8(&rtty->wb, rtty->file_context.sid); - buffer_put_u8(&rtty->wb, RTTY_FILE_MSG_CANCELED); - ev_io_start(rtty->loop, &rtty->iow); + struct rtty *rtty = container_of(ctx, struct rtty, file_context); + struct file_control_msg msg = {}; + char fifo_name[128]; + pid_t pid; + int ctlfd; + + if (len != 12) + return false; + + if (memcmp(buf, RTTY_FILE_MAGIC, 3)) + return false; + + memcpy(&pid, buf + 4, 4); + + memset(abspath, 0, sizeof(abspath)); + getcwd_pid(pid, abspath, sizeof(abspath) - 1); + + sprintf(fifo_name, "/tmp/rtty-file-%d.fifo", pid); + + ctlfd = open(fifo_name, O_WRONLY); + if (ctlfd < 0) { + log_err("Could not open fifo %s\n", fifo_name); + kill(pid, SIGTERM); + return true; + } + + if (ctx->busy) { + msg.type = RTTY_FILE_MSG_BUSY; + + send_file_control_msg(ctlfd, &msg); + close(ctlfd); + + return true; + } + + if (buf[3] == 'R') { + msg.type = RTTY_FILE_MSG_REQUEST_ACCEPT; + + buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); + buffer_put_u16be(&rtty->wb, 2); + buffer_put_u8(&rtty->wb, ctx->sid); + buffer_put_u8(&rtty->wb, RTTY_FILE_MSG_START_DOWNLOAD); + ev_io_start(rtty->loop, &rtty->iow); + + send_file_control_msg(ctlfd, &msg); + } else { + char path[PATH_MAX] = ""; + char link[128]; + int fd; + + memcpy(&fd, buf + 8, 4); + + sprintf(link, "/proc/%d/fd/%d", pid, fd); + + if (readlink(link, path, sizeof(path) - 1) < 0) { + log_err("readlink: %s\n", strerror(errno)); + + msg.type = RTTY_FILE_MSG_ERR; + + send_file_control_msg(ctlfd, &msg); + close(ctlfd); + + return true; + } + + msg.type = RTTY_FILE_MSG_REQUEST_ACCEPT; + send_file_control_msg(ctlfd, &msg); + + if (start_upload_file(ctx, path) < 0) { + msg.type = RTTY_FILE_MSG_ERR; + + send_file_control_msg(ctlfd, &msg); + close(ctlfd); + + return true; + } + } + + ctx->sid = sid; + ctx->ctlfd = ctlfd; + ctx->busy = true; + + return true; } static void start_download_file(struct file_context *ctx, struct buffer *info, int len) { struct rtty *rtty = container_of(ctx, struct rtty, file_context); + struct file_control_msg msg = {}; struct mntent *ment; struct statvfs sfs; char *name; @@ -144,15 +275,18 @@ static void start_download_file(struct file_context *ctx, struct buffer *info, i ment = find_mount_point(abspath); if (ment) { if (statvfs(ment->mnt_dir, &sfs) == 0 && ctx->total_size > sfs.f_bavail * sfs.f_frsize) { - uint8_t type = RTTY_FILE_MSG_NO_SPACE; + msg.type = RTTY_FILE_MSG_NO_SPACE; + + notify_user_canceled(rtty); + + send_file_control_msg(ctx->ctlfd, &msg); - send_canceled_msg(rtty); - sendto(ctx->sock, &type, 1, 0, (struct sockaddr *) &ctx->peer_sun, sizeof(struct sockaddr_un)); log_err("download file fail: no enough space\n"); goto free_name; } } + strcat(abspath, "/"); strcat(abspath, name); fd = open(abspath, O_WRONLY | O_TRUNC | O_CREAT, 0644); @@ -161,248 +295,68 @@ static void start_download_file(struct file_context *ctx, struct buffer *info, i goto free_name; } - ctx->fd = fd; - log_info("download file: %s, size: %u\n", abspath, ctx->total_size); - buffer_truncate(&b, 0); - buffer_put_u8(&b, RTTY_FILE_MSG_INFO); - buffer_put_string(&b, name); - buffer_put_zero(&b, 1); - sendto(ctx->sock, buffer_data(&b), buffer_length(&b), 0, - (struct sockaddr *)&ctx->peer_sun, sizeof(struct sockaddr_un)); + if (ctx->total_size == 0) + close(fd); + else + ctx->fd = fd; + + msg.type = RTTY_FILE_MSG_INFO; + memcpy(msg.buf, &ctx->total_size, 4); + strcpy((char *)msg.buf + 4, name); + + send_file_control_msg(ctx->ctlfd, &msg); free_name: free(name); } -static void notify_busy(struct file_context *ctx) -{ - uint8_t type = RTTY_FILE_MSG_BUSY; - - log_err("upload file is busy\n"); - sendto(ctx->sock, &type, 1, 0, - (struct sockaddr *)&ctx->peer_sun, sizeof(struct sockaddr_un)); -} - -static void on_socket_read(struct ev_loop *loop, struct ev_io *w, int revents) -{ - struct file_context *ctx = container_of(w, struct file_context, ios); - struct rtty *rtty = container_of(ctx, struct rtty, file_context); - int type = read_file_msg(w->fd, &b); - - switch (type) { - case RTTY_FILE_MSG_INFO: - start_upload_file(ctx, &b); - break; - case RTTY_FILE_MSG_CANCELED: - if (ctx->fd > -1) { - close(ctx->fd); - ctx->fd = -1; - ev_io_stop(loop, &ctx->iof); - } - - ctx->busy = false; - send_canceled_msg(rtty); - break; - case RTTY_FILE_MSG_SAVE_PATH: - strcpy(abspath, buffer_data(&b)); - strcat(abspath, "/"); - buffer_put_u8(&rtty->wb, MSG_TYPE_FILE); - buffer_put_u16be(&rtty->wb, 2); - buffer_put_u8(&rtty->wb, ctx->sid); - buffer_put_u8(&rtty->wb, RTTY_FILE_MSG_START_DOWNLOAD); - ev_io_start(loop, &rtty->iow); - break; - default: - break; - } -} - -int start_file_service(struct file_context *ctx) +void parse_file_msg(struct file_context *ctx, struct buffer *data, int len) { struct rtty *rtty = container_of(ctx, struct rtty, file_context); - struct sockaddr_un sun = { - .sun_family = AF_UNIX - }; - int sock; + struct file_control_msg msg = {}; + int type = buffer_pull_u8(data); - if (strlen(RTTY_FILE_UNIX_SOCKET_S) >= sizeof(sun.sun_path)) { - log_err("unix socket path too long\n"); - return -1; - } + len--; - sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); - if (sock < 0) { - log_err("create socket fail: %s\n", strerror(errno)); - return -1; - } - - strcpy(sun.sun_path, RTTY_FILE_UNIX_SOCKET_S); - - unlink(RTTY_FILE_UNIX_SOCKET_S); - - if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - log_err("bind socket fail: %s\n", strerror(errno)); - goto err; - } - - ev_io_init(&ctx->ios, on_socket_read, sock, EV_READ); - ev_io_start(rtty->loop, &ctx->ios); - - ctx->fd = -1; - ctx->sock = sock; - ctx->peer_sun.sun_family = AF_UNIX; - strcpy(ctx->peer_sun.sun_path, RTTY_FILE_UNIX_SOCKET_C); - - return sock; - -err: - if (sock > -1) - close(sock); - return -1; -} - -void parse_file_msg(struct file_context *ctx, uint8_t type, struct buffer *data, int len) -{ switch (type) { case RTTY_FILE_MSG_INFO: start_download_file(ctx, data, len); break; - case RTTY_FILE_MSG_DATA: - if (ctx->fd > -1) { - buffer_pull_to_fd(data, ctx->fd, len); - ctx->remain_size -= len; - notify_progress(ctx); - } else { - buffer_pull(data, NULL, len); - } - if (len == 0 && ctx->fd > -1) { + case RTTY_FILE_MSG_DATA: + if (len > 0) { + if (ctx->fd > -1) { + buffer_pull_to_fd(data, ctx->fd, len); + ctx->remain_size -= len; + + if (notify_progress(ctx) < 0) { + notify_user_canceled(rtty); + file_context_reset(ctx); + } + } else { + buffer_pull(data, NULL, len); + } + } else { + file_context_reset(ctx); + } + break; + + case RTTY_FILE_MSG_CANCELED: + if (ctx->fd > -1) { close(ctx->fd); ctx->fd = -1; - ctx->busy = false; } - break; - case RTTY_FILE_MSG_CANCELED: - if (ctx->fd > -1) - close(ctx->fd); - ctx->fd = -1; + + msg.type = RTTY_FILE_MSG_CANCELED; + send_file_control_msg(ctx->ctlfd, &msg); + close(ctx->ctlfd); + ctx->busy = false; - sendto(ctx->sock, &type, 1, 0, (struct sockaddr *) &ctx->peer_sun, sizeof(struct sockaddr_un)); break; + default: break; } } - -int connect_rtty_file_service() -{ - struct sockaddr_un sun = { .sun_family = AF_UNIX }; - int sock = -1; - - sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); - if (sock < 0) { - fprintf(stderr, "create socket fail: %s\n", strerror(errno)); - return -1; - } - - strcpy(sun.sun_path, RTTY_FILE_UNIX_SOCKET_C); - - unlink(RTTY_FILE_UNIX_SOCKET_C); - - if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - log_err("bind socket fail: %s\n", strerror(errno)); - goto err; - } - - strcpy(sun.sun_path, RTTY_FILE_UNIX_SOCKET_S); - - if (connect(sock, (struct sockaddr *) &sun, sizeof(sun)) < 0) { - fprintf(stderr, "connect to rtty fail: %s\n", strerror(errno)); - goto err; - } - - return sock; - -err: - if (sock > -1) - close(sock); - return -1; -} - -void request_transfer_file() -{ - fwrite(RTTY_FILE_MAGIC, sizeof(RTTY_FILE_MAGIC), 1, stdout); - fflush(stdout); - usleep(10000); -} - -static void accept_file_request(struct file_context *ctx) -{ - uint8_t type = RTTY_FILE_MSG_REQUEST_ACCEPT; - - ctx->fd = -1; - ctx->busy = true; - ctx->last_notify_progress = 0; - sendto(ctx->sock, &type, 1, 0, - (struct sockaddr *)&ctx->peer_sun, sizeof(struct sockaddr_un)); -} - -bool detect_file_operation(uint8_t *buf, int len, int sid, struct file_context *ctx) -{ - if (len != 3) - return false; - - if (memcmp(buf, RTTY_FILE_MAGIC, sizeof(RTTY_FILE_MAGIC))) - return false; - - ctx->sid = sid; - - if (ctx->busy) { - notify_busy(ctx); - return true; - } - - accept_file_request(ctx); - - return true; -} - -void update_progress(struct ev_loop *loop, ev_tstamp start_time, struct buffer *info) -{ - uint32_t remain = buffer_pull_u32(info); - uint32_t total = buffer_pull_u32(info); - - if (total == 0) - goto done; - - printf("%100c\r", ' '); - printf(" %lu%% %s %.3lfs\r", (total - remain) * 100UL / total, - format_size(total - remain), ev_now(loop) - start_time); - fflush(stdout); - - if (remain > 0) - return; - -done: - ev_break(loop, EVBREAK_ALL); - puts(""); -} - -void cancel_file_operation(struct ev_loop *loop, int sock) -{ - uint8_t type = RTTY_FILE_MSG_CANCELED; - send(sock, &type, 1, 0);; - ev_break(loop, EVBREAK_ALL); - puts(""); -} - -int read_file_msg(int sock, struct buffer *out) -{ - uint8_t buf[1024]; - int ret = read(sock, buf, sizeof(buf)); - buffer_truncate(out, 0); - buffer_put_data(out, buf, ret); - return buffer_pull_u8(out); -} diff --git a/src/file.h b/src/file.h index 18c0771..ecf8548 100644 --- a/src/file.h +++ b/src/file.h @@ -26,7 +26,6 @@ #define RTTY_FILE_H #include -#include #include "buffer.h" @@ -38,37 +37,31 @@ enum { RTTY_FILE_MSG_BUSY, RTTY_FILE_MSG_PROGRESS, RTTY_FILE_MSG_REQUEST_ACCEPT, - RTTY_FILE_MSG_SAVE_PATH, - RTTY_FILE_MSG_NO_SPACE + RTTY_FILE_MSG_NO_SPACE, + RTTY_FILE_MSG_ERR +}; + +struct file_control_msg { + int type; + uint8_t buf[128]; }; struct file_context { int sid; int fd; - int sock; bool busy; + int ctlfd; uint32_t total_size; uint32_t remain_size; - struct sockaddr_un peer_sun; - struct ev_io ios; /* used for unix socket */ struct ev_io iof; /* used for upload file */ ev_tstamp last_notify_progress; }; -int start_file_service(struct file_context *ctx); +void request_transfer_file(char type, const char *path); -void parse_file_msg(struct file_context *ctx, uint8_t type, struct buffer *data, int len); - -void update_progress(struct ev_loop *loop, ev_tstamp start_time, struct buffer *info); - -void cancel_file_operation(struct ev_loop *loop, int sock); - -int read_file_msg(int sock, struct buffer *out); - -int connect_rtty_file_service(); - -void request_transfer_file(); bool detect_file_operation(uint8_t *buf, int len, int sid, struct file_context *ctx); +void parse_file_msg(struct file_context *ctx, struct buffer *data, int len); + #endif diff --git a/src/filectl.c b/src/filectl.c new file mode 100644 index 0000000..234bb1c --- /dev/null +++ b/src/filectl.c @@ -0,0 +1,213 @@ +/* + * MIT License + * + * Copyright (c) 2019 Jianhui Zhao + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "file.h" + +static uint8_t RTTY_FILE_MAGIC[12] = {0xb6, 0xbc, 0xbd}; + +static struct timeval start_time; + +static uint32_t total_size; + +static char fifo_name[128]; + +static void clear_fifo() +{ + unlink(fifo_name); +} + +static void signal_handler(int sig) +{ + puts(""); + exit(0); +} + +static u_int32_t update_progress(uint8_t *buf) +{ + struct timeval now; + uint32_t remain; + + gettimeofday(&now, NULL); + + memcpy(&remain, buf, 4); + + printf("%100c\r", ' '); + printf(" %lu%% %s %.3fs\r", (total_size - remain) * 100UL / total_size, + format_size(total_size - remain), + (now.tv_sec + now.tv_usec / 1000.0 / 1000) - (start_time.tv_sec + start_time.tv_usec / 1000.0 / 1000)); + fflush(stdout); + + return remain; +} + +static void handle_file_control_msg(int fd, int sfd) +{ + struct file_control_msg msg; + struct buffer b = {}; + + while (true) { + if (buffer_put_fd(&b, fd, -1, NULL) < 0) + break; + + if (buffer_length(&b) < sizeof(msg)) + continue; + + buffer_pull(&b, &msg, sizeof(msg)); + + switch (msg.type) { + case RTTY_FILE_MSG_REQUEST_ACCEPT: + if (sfd > -1) + close(sfd); + else + printf("Waiting to receive. Press Ctrl+C to cancel\n"); + break; + + case RTTY_FILE_MSG_INFO: + memcpy(&total_size, msg.buf, 4); + + printf("Transferring '%s'...\n", (char *)(msg.buf + 4)); + + if (total_size == 0) { + printf(" 100%% 0 B 0s\n"); + goto done; + } + + gettimeofday(&start_time, NULL); + + break; + + case RTTY_FILE_MSG_PROGRESS: + if (update_progress(msg.buf) == 0) { + puts(""); + goto done; + } + break; + + case RTTY_FILE_MSG_CANCELED: + puts(""); + goto done; + + case RTTY_FILE_MSG_BUSY: + printf("\033[31mRtty is busy to transfer file\033[0m\n"); + goto done; + + case RTTY_FILE_MSG_NO_SPACE: + printf("\033[31mNo enough space\033[0m\n"); + goto done; + + default: + goto done; + } + } + +done: + buffer_free(&b); +} + +void request_transfer_file(char type, const char *path) +{ + pid_t pid = getpid(); + struct stat st; + int sfd = -1; + int ctlfd; + + sprintf(fifo_name, "/tmp/rtty-file-%d.fifo", pid); + + if (mkfifo(fifo_name, 0644) < 0) { + fprintf(stderr, "Could not create fifo %s\n", fifo_name); + exit(EXIT_FAILURE); + } + + signal(SIGINT, signal_handler); + + atexit(clear_fifo); + + usleep(10000); + + RTTY_FILE_MAGIC[3] = type; + + memcpy(RTTY_FILE_MAGIC + 4, &pid, 4); + + if (type == 'S') { + sfd = open(path, O_RDONLY); + if (sfd < 0) { + printf("open '%s' failed: ", path); + if (errno == ENOENT) + printf("No such file\n"); + else + printf("%s\n", strerror(errno)); + return; + } + + fstat(sfd, &st); + if (!(st.st_mode & S_IFREG)) { + printf("'%s' is not a regular file\n", path); + close(sfd); + return; + } + + total_size = st.st_size; + + memcpy(RTTY_FILE_MAGIC + 8, &sfd, 4); + } + + fwrite(RTTY_FILE_MAGIC, sizeof(RTTY_FILE_MAGIC), 1, stdout); + fflush(stdout); + + ctlfd = open(fifo_name, O_RDONLY | O_NONBLOCK); + if (ctlfd < 0) { + fprintf(stderr, "Could not open fifo %s\n", fifo_name); + exit(EXIT_FAILURE); + } + + if (type == 'S') { + usleep(1000 * 10); + printf("Transferring '%s'...Press Ctrl+C to cancel\n", basename(path)); + + if (total_size == 0) { + printf(" 100%% 0 B 0s\n"); + + close(sfd); + + goto done; + } + + gettimeofday(&start_time, NULL); + } + + handle_file_control_msg(ctlfd, sfd); + +done: + close(ctlfd); +} diff --git a/src/main.c b/src/main.c index 7545614..5bb9f60 100644 --- a/src/main.c +++ b/src/main.c @@ -30,8 +30,6 @@ #include "log.h" #include "rtty.h" #include "config.h" -#include "upfile.h" -#include "downfile.h" enum { LONG_OPT_HELP = 1 @@ -132,10 +130,10 @@ int main(int argc, char **argv) rtty.username = optarg; break; case 'R': - download_file(); + request_transfer_file('R', NULL); return 0; case 'S': - upload_file(optarg); + request_transfer_file('S', optarg); return 0; case 'v': verbose = true; @@ -152,6 +150,8 @@ int main(int argc, char **argv) } } + signal(SIGPIPE, SIG_IGN); + if (background && daemon(0, 0)) log_err("Can't run in the background: %s\n", strerror(errno)); diff --git a/src/rtty.c b/src/rtty.c index 188ee39..551eb0e 100644 --- a/src/rtty.c +++ b/src/rtty.c @@ -335,7 +335,7 @@ static int parse_msg(struct rtty *rtty) break; case MSG_TYPE_FILE: - parse_file_msg(&rtty->file_context, buffer_pull_u8(rb), rb, msglen - 1); + parse_file_msg(&rtty->file_context, rb, msglen); break; case MSG_TYPE_WEB: @@ -504,7 +504,7 @@ int rtty_start(struct rtty *rtty) && !rtty->reconnect) return -1; - start_file_service(&rtty->file_context); + rtty->file_context.fd = -1; INIT_LIST_HEAD(&rtty->web_reqs); diff --git a/src/upfile.c b/src/upfile.c deleted file mode 100644 index cb8719a..0000000 --- a/src/upfile.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 Jianhui Zhao - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#include "file.h" - -static ev_tstamp start_time; -static char abspath[PATH_MAX]; -static uint32_t file_size; -static struct buffer b; -static int sock; - -static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) -{ - cancel_file_operation(loop, sock); -} - -static void on_socket_read(struct ev_loop *loop, struct ev_io *w, int revents) -{ - int type = read_file_msg(w->fd, &b); - - switch (type) { - case RTTY_FILE_MSG_REQUEST_ACCEPT: - buffer_put_u8(&b, RTTY_FILE_MSG_INFO); - buffer_put_u32(&b, file_size); - buffer_put_string(&b, abspath); - buffer_put_zero(&b, 1); - buffer_pull_to_fd(&b, w->fd, -1); - start_time = ev_now(loop); - break; - case RTTY_FILE_MSG_PROGRESS: - update_progress(loop, start_time, &b); - break; - case RTTY_FILE_MSG_BUSY: - fprintf(stderr, "\033[31mRtty is busy\033[0m\n"); - ev_break(loop, EVBREAK_ALL); - break; - default: - break; - } -} - -void upload_file(const char *path) -{ - struct ev_loop *loop = EV_DEFAULT; - struct ev_signal sw; - struct ev_io ior; - struct stat st; - int fd; - - if (getuid() > 0) { - fprintf(stderr, "Operation not permitted, must be run as root\n"); - return; - } - - fd = open(path, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "open '%s' failed: ", path); - if (errno == ENOENT) - fprintf(stderr, "No such file\n"); - else - fprintf(stderr, "%s\n", strerror(errno)); - return; - } - - fstat(fd, &st); - if (!(st.st_mode & S_IFREG)) { - fprintf(stderr, "'%s' is not a regular file\n", path); - close(fd); - return; - } - close(fd); - - file_size = st.st_size; - - sock = connect_rtty_file_service(); - if (sock < 0) - return; - - if (!realpath(path, abspath)) - return; - - request_transfer_file(); - - ev_io_init(&ior, on_socket_read, sock, EV_READ); - ev_io_start(loop, &ior); - - ev_signal_init(&sw, signal_cb, SIGINT); - ev_signal_start(loop, &sw); - - printf("Transferring '%s'...Press Ctrl+C to cancel\n", basename(path)); - - ev_run(loop, 0); - - buffer_free(&b); - close(sock); -} diff --git a/src/upfile.h b/src/upfile.h deleted file mode 100644 index 41fe761..0000000 --- a/src/upfile.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 Jianhui Zhao - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RTTY_UPFILE_H_ -#define RTTY_UPFILE_H_ - -void upload_file(const char *path); - -#endif diff --git a/src/utils.c b/src/utils.c index 95f8682..e8c90b6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "utils.h" @@ -154,3 +155,16 @@ struct mntent *find_mount_point(const char *name) return ment; } + +/* + * getcwd_pid does not append a null byte to buf. It will (silently) truncate the contents (to + * a length of bufsiz characters), in case the buffer is too small to hold all of the contents. + */ +ssize_t getcwd_pid(pid_t pid, char *buf, size_t bufsiz) +{ + char link[128]; + + sprintf(link, "/proc/%d/cwd", pid); + + return readlink(link, buf, bufsiz); +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index a3b6ed5..c7c14db 100644 --- a/src/utils.h +++ b/src/utils.h @@ -38,4 +38,6 @@ const char *format_size(size_t size); struct mntent *find_mount_point(const char *name); +ssize_t getcwd_pid(pid_t pid, char *buf, size_t bufsiz); + #endif