@@ -15,6 +15,7 @@ | |||||
#include "config.h" | #include "config.h" | ||||
#include "ed2k.h" | #include "ed2k.h" | ||||
#include "util.h" | #include "util.h" | ||||
#include "globals.h" | |||||
/* Needed, bcuz of custom %B format */ | /* Needed, bcuz of custom %B format */ | ||||
#pragma GCC diagnostic push | #pragma GCC diagnostic push | ||||
@@ -409,10 +410,12 @@ void api_free() | |||||
uio_error("Cannot cancel api keepalive thread"); | uio_error("Cannot cancel api keepalive thread"); | ||||
} else { | } else { | ||||
int je = pthread_join(api_ka_thread, NULL); | int je = pthread_join(api_ka_thread, NULL); | ||||
if (je != 0) { | |||||
if (je != 0) | |||||
uio_error("Cannot join api keepalive thread: %s", | uio_error("Cannot join api keepalive thread: %s", | ||||
strerror(je)); | strerror(je)); | ||||
} | |||||
else | |||||
uio_debug("Keepalive thread ended"); | |||||
if (pthread_mutex_destroy(&api_work_mx) != 0) | if (pthread_mutex_destroy(&api_work_mx) != 0) | ||||
uio_error("Cannot destroy api work mutex"); | uio_error("Cannot destroy api work mutex"); | ||||
} | } | ||||
@@ -473,21 +476,33 @@ static void api_ratelimit() | |||||
} | } | ||||
} | } | ||||
/* | |||||
* Returns the written byte count | |||||
* Or -1 on error, and -2 if errno was EINTR | |||||
*/ | |||||
static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size) | static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size) | ||||
{ | { | ||||
ssize_t read_len; | ssize_t read_len; | ||||
int en; | |||||
api_ratelimit(); | api_ratelimit(); | ||||
uio_debug("{Api}: Sending: %.*s", (int)data_len, buffer); | uio_debug("{Api}: Sending: %.*s", (int)data_len, buffer); | ||||
if (api_encryption) | if (api_encryption) | ||||
data_len = api_encrypt(buffer, data_len); | data_len = api_encrypt(buffer, data_len); | ||||
if (net_send(buffer, data_len) == -1) { | |||||
uio_error("Cannot send data: %s", strerror(errno)); | |||||
return -1; | |||||
} | |||||
en = net_send(buffer, data_len); | |||||
if (en < 0) | |||||
return en; | |||||
read_len = net_read(buffer, buf_size); | read_len = net_read(buffer, buf_size); | ||||
if (read_len < 0) { | |||||
uio_error("!!! BAD PLACE EINTR !!! report pls"); | |||||
return en; /* This could lead so some problems if we also want to | |||||
log out. If we hit this, the msg got sent, but we | |||||
couldn't read the response. That means, in the | |||||
logout call, this msg's data will be read | |||||
Let's see if this ever comes up */ | |||||
} | |||||
api_ratelimit_sent(); | api_ratelimit_sent(); | ||||
if (api_encryption) | if (api_encryption) | ||||
@@ -566,8 +581,11 @@ enum error api_cmd_version(struct api_result *res) | |||||
enum error err = NOERR; | enum error err = NOERR; | ||||
pthread_mutex_lock(&api_work_mx); | pthread_mutex_lock(&api_work_mx); | ||||
if (res_len == -1) { | |||||
err = ERR_API_COMMFAIL; | |||||
if (res_len < 0) { | |||||
if (res_len == -2 && should_exit) | |||||
err = ERR_SHOULD_EXIT; | |||||
else | |||||
err = ERR_API_COMMFAIL; | |||||
goto end; | goto end; | ||||
} | } | ||||
@@ -607,8 +625,11 @@ static enum error api_cmd_auth(const char *uname, const char *pass, | |||||
sizeof(buffer)); | sizeof(buffer)); | ||||
enum error err = NOERR; | enum error err = NOERR; | ||||
if (res_len == -1) { | |||||
err = ERR_API_COMMFAIL; | |||||
if (res_len < 0) { | |||||
if (res_len == -2 && should_exit) | |||||
err = ERR_SHOULD_EXIT; | |||||
else | |||||
err = ERR_API_COMMFAIL; | |||||
goto end; | goto end; | ||||
} | } | ||||
@@ -653,8 +674,11 @@ static enum error api_cmd_logout(struct api_result *res) | |||||
long code; | long code; | ||||
enum error err = NOERR; | enum error err = NOERR; | ||||
if (res_len == -1) { | |||||
err = ERR_API_COMMFAIL; | |||||
if (res_len < 0) { | |||||
if (res_len == -2 && should_exit) | |||||
err = ERR_SHOULD_EXIT; | |||||
else | |||||
err = ERR_API_COMMFAIL; | |||||
goto end; | goto end; | ||||
} | } | ||||
@@ -683,8 +707,11 @@ enum error api_cmd_uptime(struct api_result *res) | |||||
long code; | long code; | ||||
enum error err = NOERR; | enum error err = NOERR; | ||||
if (res_len == -1) { | |||||
err = ERR_API_COMMFAIL; | |||||
if (res_len < 0) { | |||||
if (res_len == -2 && should_exit) | |||||
err = ERR_SHOULD_EXIT; | |||||
else | |||||
err = ERR_API_COMMFAIL; | |||||
goto end; | goto end; | ||||
} | } | ||||
@@ -729,8 +756,11 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash, | |||||
api_session, size, hash_str, ml_state, watched), | api_session, size, hash_str, ml_state, watched), | ||||
sizeof(buffer)); | sizeof(buffer)); | ||||
if (res_len == -1) { | |||||
err = ERR_API_COMMFAIL; | |||||
if (res_len < 0) { | |||||
if (res_len == -2 && should_exit) | |||||
err = ERR_SHOULD_EXIT; | |||||
else | |||||
err = ERR_API_COMMFAIL; | |||||
goto end; | goto end; | ||||
} | } | ||||
@@ -1,17 +1,39 @@ | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <errno.h> | |||||
#include <string.h> | |||||
#include "config.h" | #include "config.h" | ||||
#include "error.h" | #include "error.h" | ||||
#include "uio.h" | #include "uio.h" | ||||
#include "cmd.h" | #include "cmd.h" | ||||
#include "globals.h" | |||||
bool should_exit = false; | |||||
static void signal_handler(int signum, siginfo_t *info, void *ctx) | |||||
{ | |||||
should_exit = true; | |||||
printf("\033[0GGot C-c. Press again to force exit\n"); | |||||
} | |||||
int main(int argc, char **argv) | int main(int argc, char **argv) | ||||
{ | { | ||||
int exit_code = EXIT_SUCCESS; | int exit_code = EXIT_SUCCESS; | ||||
enum error err = config_parse(argc, argv); | |||||
enum error err; | |||||
struct sigaction sact = { | |||||
.sa_flags = SA_SIGINFO | SA_RESETHAND, | |||||
//.sa_flags = SA_SIGINFO, | |||||
.sa_sigaction = signal_handler, | |||||
}; | |||||
if (sigaction(SIGINT, &sact, NULL) != 0) { | |||||
uio_error("Cannot set up signal handler: %s", strerror(errno)); | |||||
return EXIT_FAILURE; | |||||
} | |||||
err = config_parse(argc, argv); | |||||
if (err == ERR_OPT_EXIT) | if (err == ERR_OPT_EXIT) | ||||
return EXIT_SUCCESS; | return EXIT_SUCCESS; | ||||
else if (err != NOERR) | else if (err != NOERR) | ||||
@@ -20,7 +42,9 @@ int main(int argc, char **argv) | |||||
//config_dump(); | //config_dump(); | ||||
err = cmd_main(); | err = cmd_main(); | ||||
if (err != NOERR) | |||||
if (err == ERR_SHOULD_EXIT) | |||||
uio_debug("Exiting as requested orz"); | |||||
else if (err != NOERR) | |||||
exit_code = EXIT_FAILURE; | exit_code = EXIT_FAILURE; | ||||
config_free(); | config_free(); | ||||
@@ -3,10 +3,12 @@ | |||||
#include <string.h> | #include <string.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <ftw.h> | #include <ftw.h> | ||||
#include <assert.h> | |||||
#include "ed2k.h" | #include "ed2k.h" | ||||
#include "ed2k_util.h" | #include "ed2k_util.h" | ||||
#include "uio.h" | #include "uio.h" | ||||
#include "globals.h" | |||||
static struct ed2k_util_opts l_opts; | static struct ed2k_util_opts l_opts; | ||||
@@ -15,11 +17,13 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, | |||||
{ | { | ||||
unsigned char buf[blksize], hash[ED2K_HASH_SIZE]; | unsigned char buf[blksize], hash[ED2K_HASH_SIZE]; | ||||
struct ed2k_ctx ed2k; | struct ed2k_ctx ed2k; | ||||
enum error err; | |||||
FILE *f; | FILE *f; | ||||
size_t read_len; | size_t read_len; | ||||
int en; | |||||
if (l_opts.pre_hash_fn) { | if (l_opts.pre_hash_fn) { | ||||
enum error err = l_opts.pre_hash_fn(file_path, st, l_opts.data); | |||||
err = l_opts.pre_hash_fn(file_path, st, l_opts.data); | |||||
if (err == ED2KUTIL_DONTHASH) | if (err == ED2KUTIL_DONTHASH) | ||||
return NOERR; | return NOERR; | ||||
else if (err != NOERR) | else if (err != NOERR) | ||||
@@ -28,24 +32,52 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, | |||||
f = fopen(file_path, "rb"); | f = fopen(file_path, "rb"); | ||||
if (!f) { | if (!f) { | ||||
uio_error("Failed to open file: %s (%s)", file_path, strerror(errno)); | |||||
return ERR_ED2KUTIL_FS; | |||||
en = errno; | |||||
uio_error("Failed to open file: %s (%s)", file_path, strerror(en)); | |||||
if (en == EINTR && should_exit) | |||||
return ERR_SHOULD_EXIT; | |||||
else | |||||
return ERR_ED2KUTIL_FS; | |||||
} | } | ||||
ed2k_init(&ed2k); | ed2k_init(&ed2k); | ||||
read_len = fread(buf, 1, sizeof(buf), f); | read_len = fread(buf, 1, sizeof(buf), f); | ||||
while (read_len > 0) { | |||||
/* From my test, fread wont return anything special on signal interrupt */ | |||||
while (read_len > 0 && !should_exit) { | |||||
ed2k_update(&ed2k, buf, read_len); | ed2k_update(&ed2k, buf, read_len); | ||||
read_len = fread(buf, 1, sizeof(buf), f); | read_len = fread(buf, 1, sizeof(buf), f); | ||||
} | } | ||||
// TODO check if eof or error | |||||
if (should_exit) { | |||||
err = ERR_SHOULD_EXIT; | |||||
goto fail; | |||||
} | |||||
if (ferror(f)) { /* Loop stopped bcuz of error, not EOF */ | |||||
uio_error("Failure while reading file"); | |||||
err = ERR_ED2KUTIL_FS; | |||||
goto fail; | |||||
} | |||||
assert(feof(f)); | |||||
ed2k_final(&ed2k, hash); | ed2k_final(&ed2k, hash); | ||||
fclose(f); | |||||
if (fclose(f) != 0) { | |||||
en = errno; | |||||
uio_debug("Fclose failed: %s", strerror(en)); | |||||
if (en == EINTR && should_exit) | |||||
return ERR_SHOULD_EXIT; | |||||
else | |||||
return ERR_ED2KUTIL_FS; | |||||
} | |||||
if (l_opts.post_hash_fn) | if (l_opts.post_hash_fn) | ||||
return l_opts.post_hash_fn(file_path, hash, st, l_opts.data); | return l_opts.post_hash_fn(file_path, hash, st, l_opts.data); | ||||
return NOERR; | return NOERR; | ||||
fail: | |||||
if (f) /* We can't get a 2nd interrupt now */ | |||||
fclose(f); | |||||
return err; | |||||
} | } | ||||
static int ed2k_util_walk(const char *fpath, const struct stat *sb, | static int ed2k_util_walk(const char *fpath, const struct stat *sb, | ||||
@@ -45,6 +45,8 @@ | |||||
E(ERR_THRD) /* Generic pthread error */ \ | E(ERR_THRD) /* Generic pthread error */ \ | ||||
\ | \ | ||||
E(ERR_LIBEVENT) /* There are some problem with a libevent function */ \ | E(ERR_LIBEVENT) /* There are some problem with a libevent function */ \ | ||||
\ | |||||
E(ERR_SHOULD_EXIT) /* Probably got a C-c, program should exit now */ \ | |||||
E(_ERR_COUNT) \ | E(_ERR_COUNT) \ | ||||
@@ -0,0 +1,7 @@ | |||||
#ifndef _GLOBALS_H | |||||
#define _GLOBALS_H | |||||
#include <stdbool.h> | |||||
extern bool should_exit; | |||||
#endif /* _GLOBALS_H */ |
@@ -248,7 +248,11 @@ ssize_t net_send(const void *msg, size_t msg_len) | |||||
{ | { | ||||
ssize_t w_len = send(net_socket, msg, msg_len, 0); | ssize_t w_len = send(net_socket, msg, msg_len, 0); | ||||
if (w_len == -1) { | if (w_len == -1) { | ||||
uio_error("{net} Send failed: %s", strerror(errno)); | |||||
int en = errno; | |||||
uio_error("{net} Send failed: %s", strerror(en)); | |||||
if (en == EINTR) | |||||
return -2; | |||||
return -1; | return -1; | ||||
} | } | ||||
return w_len; | return w_len; | ||||
@@ -258,8 +262,14 @@ ssize_t net_read(void* out_data, size_t read_size) | |||||
{ | { | ||||
ssize_t read = recv(net_socket, out_data, read_size, 0); | ssize_t read = recv(net_socket, out_data, read_size, 0); | ||||
if (read == -1) { | if (read == -1) { | ||||
int en = errno; | |||||
uio_error("{net} Read failed: %s", strerror(errno)); | uio_error("{net} Read failed: %s", strerror(errno)); | ||||
if (en == EINTR) | |||||
return -2; | |||||
return -1; | return -1; | ||||
} | } | ||||
if (read == read_size) | |||||
uio_warning("{net} Data may have been discarded!"); | |||||
return read; | return read; | ||||
} | } |
@@ -12,6 +12,8 @@ enum error net_init(); | |||||
/* | /* | ||||
* Send and read data to and from the api | * Send and read data to and from the api | ||||
* Returns the number of bytes sent/read or -1 on error | |||||
* If the error is EINTR, it returns -2 | |||||
*/ | */ | ||||
ssize_t net_send(const void *msg, size_t msg_len); | ssize_t net_send(const void *msg, size_t msg_len); | ||||
ssize_t net_read(void *out_data, size_t read_size); | ssize_t net_read(void *out_data, size_t read_size); | ||||