Redesign file transfers

Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
This commit is contained in:
Jianhui Zhao
2021-01-29 00:28:18 +08:00
parent 707c5a2d8b
commit 1f85f1fd60
12 changed files with 454 additions and 571 deletions

View File

@@ -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

View File

@@ -1,108 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#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);
}

View File

@@ -1,30 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* 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

View File

@@ -27,10 +27,10 @@
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/statvfs.h>
#include <mntent.h>
#include <inttypes.h>
#include <sys/statvfs.h>
#include <linux/limits.h>
#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);
}

View File

@@ -26,7 +26,6 @@
#define RTTY_FILE_H
#include <ev.h>
#include <sys/un.h>
#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

213
src/filectl.c Normal file
View File

@@ -0,0 +1,213 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* 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 <sys/time.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#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);
}

View File

@@ -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));

View File

@@ -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);

View File

@@ -1,122 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* 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 <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#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);
}

View File

@@ -1,30 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
*
* 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

View File

@@ -28,6 +28,7 @@
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#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);
}

View File

@@ -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