@@ -484,27 +484,34 @@ static enum error api_ratelimit() | |||||
/* | /* | ||||
* Returns the written byte count | * Returns the written byte count | ||||
* Or -1 on error, and -2 if errno was EINTR | |||||
* Or negative errno value on error | |||||
*/ | */ | ||||
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) | ||||
{ | { | ||||
pthread_mutex_lock(&api_work_mx); | |||||
ssize_t read_len; | ssize_t read_len; | ||||
int en; | int en; | ||||
if (api_ratelimit() == ERR_SHOULD_EXIT) | |||||
return -2; | |||||
if (api_ratelimit() == ERR_SHOULD_EXIT) { | |||||
read_len = -2; | |||||
goto end; | |||||
} | |||||
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); | ||||
en = net_send(buffer, data_len); | en = net_send(buffer, data_len); | ||||
if (en < 0) | |||||
return en; | |||||
if (en < 0) { | |||||
read_len = en; | |||||
goto end; | |||||
} | |||||
read_len = net_read(buffer, buf_size); | read_len = net_read(buffer, buf_size); | ||||
if (read_len < 0) { | if (read_len < 0) { | ||||
uio_error("!!! BAD PLACE EINTR !!! report pls"); | |||||
return read_len; /* This could lead so some problems if we also want to | |||||
if (read_len == -EINTR) | |||||
uio_error("!!! BAD PLACE EINTR !!! report pls"); | |||||
goto end; /* This could lead so some problems if we also want to | |||||
log out. If we hit this, the msg got sent, but we | log out. If we hit this, the msg got sent, but we | ||||
couldn't read the response. That means, in the | couldn't read the response. That means, in the | ||||
logout call, this msg's data will be read | logout call, this msg's data will be read | ||||
@@ -516,6 +523,8 @@ static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size) | |||||
read_len = api_decrypt(buffer, read_len); | read_len = api_decrypt(buffer, read_len); | ||||
uio_debug("{Api}: Reading: %.*s", (int)read_len, buffer); | uio_debug("{Api}: Reading: %.*s", (int)read_len, buffer); | ||||
end: | |||||
pthread_mutex_unlock(&api_work_mx); | |||||
return read_len; | return read_len; | ||||
} | } | ||||
@@ -686,7 +695,6 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, | |||||
enum error err = NOERR; | enum error err = NOERR; | ||||
int retry_count = 0; | int retry_count = 0; | ||||
pthread_mutex_lock(&api_work_mx); | |||||
api_g_retry_count = 0; | api_g_retry_count = 0; | ||||
while (retry_count < API_MAX_TRYAGAIN && | while (retry_count < API_MAX_TRYAGAIN && | ||||
@@ -701,7 +709,7 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, | |||||
res_len = api_send(buffer, send_len, API_BUFSIZE); | res_len = api_send(buffer, send_len, API_BUFSIZE); | ||||
if (res_len < 0) { | if (res_len < 0) { | ||||
if (res_len == -2 && should_exit) | |||||
if (res_len == -EINTR && should_exit) | |||||
err = ERR_SHOULD_EXIT; | err = ERR_SHOULD_EXIT; | ||||
else | else | ||||
err = ERR_API_COMMFAIL; | err = ERR_API_COMMFAIL; | ||||
@@ -742,10 +750,24 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, | |||||
} | } | ||||
if (err == ERR_API_NOLOGIN || err == ERR_API_INV_SESSION) { | if (err == ERR_API_NOLOGIN || err == ERR_API_INV_SESSION) { | ||||
if (api_authed == false) { | |||||
/* If this happens and we are not logged in, don't retry */ | |||||
err = ERR_API_COMMFAIL; | |||||
break; | |||||
} | |||||
api_g_retry_count++; | api_g_retry_count++; | ||||
if (api_g_retry_count < API_MAX_TRYAGAIN) { | if (api_g_retry_count < API_MAX_TRYAGAIN) { | ||||
uio_debug("Let's try loggin in agane"); | |||||
struct timespec ts; | |||||
MS_TO_TIMESPEC_L(ts, 30000); | |||||
uio_debug("Let's try loggin in agane after waiting some"); | |||||
api_authed = false; /* We got logged out probably */ | api_authed = false; /* We got logged out probably */ | ||||
if (nanosleep(&ts, NULL) == -1) { | |||||
if (errno == EINTR && should_exit) { | |||||
err = ERR_SHOULD_EXIT; | |||||
goto end; | |||||
} | |||||
} | |||||
err = api_auth(); /* -> will call this function */ | err = api_auth(); /* -> will call this function */ | ||||
if (api_g_retry_count < API_MAX_TRYAGAIN) { | if (api_g_retry_count < API_MAX_TRYAGAIN) { | ||||
memcpy(buffer, l_buff, send_len); | memcpy(buffer, l_buff, send_len); | ||||
@@ -764,7 +786,6 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, | |||||
} | } | ||||
end: | end: | ||||
pthread_mutex_unlock(&api_work_mx); | |||||
return err; | return err; | ||||
} | } | ||||
static enum error api_cmd_base(char buffer[API_BUFSIZE], struct api_result *res, | static enum error api_cmd_base(char buffer[API_BUFSIZE], struct api_result *res, | ||||
@@ -1022,4 +1043,65 @@ enum error api_cmd_mylistmod(uint64_t lid, struct api_mylistadd_opts *opts, | |||||
return err; | return err; | ||||
} | } | ||||
static enum error api_cmd_myliststats_resp_parse(const char *buffer, struct api_myliststats_result *stats) | |||||
{ | |||||
/* all int | |||||
* | |||||
* {animes}|{eps}|{files}|{size of files}|{added animes}|{added eps}| | |||||
* {added files}|{added groups}|{leech %}|{glory %}|{viewed % of db}| | |||||
* {mylist % of db}|{viewed % of mylist}|{number of viewed eps}| | |||||
* {votes}|{reviews}|{viewed length in minutes} | |||||
*/ | |||||
bool glr; | |||||
char *line; | |||||
size_t line_len; | |||||
int fc; | |||||
glr = api_get_line(buffer, 2, &line, &line_len); | |||||
if (!glr) | |||||
return ERR_API_RESP_INVALID; | |||||
fc = api_field_parse(line, | |||||
"%lu", &stats->animes, | |||||
"%lu", &stats->eps, | |||||
"%lu", &stats->files, | |||||
"%lu", &stats->size_of_files, | |||||
"%lu", &stats->added_animes, | |||||
"%lu", &stats->added_eps, | |||||
"%lu", &stats->added_files, | |||||
"%lu", &stats->added_groups, | |||||
"%lu", &stats->leech_prcnt, | |||||
"%lu", &stats->glory_prcnt, | |||||
"%lu", &stats->viewed_prcnt_of_db, | |||||
"%lu", &stats->mylist_prcnt_of_db, | |||||
"%lu", &stats->viewed_prcnt_of_mylist, | |||||
"%lu", &stats->num_of_viewed_eps, | |||||
"%lu", &stats->votes, | |||||
"%lu", &stats->reviews, | |||||
"%lu", &stats->viewed_minutes, | |||||
NULL); | |||||
if (fc != 17) { | |||||
uio_error("Scanf only parsed %d", fc); | |||||
return ERR_API_RESP_INVALID; | |||||
} | |||||
return NOERR; | |||||
} | |||||
enum error api_cmd_myliststats(struct api_result *res) | |||||
{ | |||||
char buffer[API_BUFSIZE]; | |||||
enum error err = NOERR; | |||||
err = api_cmd_base(buffer, res, "MYLISTSTATS s=%s", api_session); | |||||
//err = api_cmd_base(buffer, res, "MYLISTSTATS"); | |||||
if (err != NOERR) | |||||
return err; | |||||
if (res->code == APICODE_MYLIST_STATS) | |||||
return api_cmd_myliststats_resp_parse(buffer, &res->myliststats); | |||||
else | |||||
return ERR_API_RESP_INVALID; | |||||
} | |||||
#pragma GCC diagnostic pop | #pragma GCC diagnostic pop |
@@ -2,6 +2,7 @@ | |||||
#define _API_H | #define _API_H | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include <stdbool.h> | |||||
#include "error.h" | #include "error.h" | ||||
@@ -243,6 +244,15 @@ struct api_mylistadd_result { | |||||
struct api_mylistmod_result { | struct api_mylistmod_result { | ||||
uint32_t n_edits; | uint32_t n_edits; | ||||
}; | }; | ||||
/* The largest struct probably lul 136 bytes */ | |||||
struct api_myliststats_result { | |||||
uint64_t animes, eps, files, size_of_files; | |||||
uint64_t added_animes, added_eps, added_files; | |||||
uint64_t added_groups, leech_prcnt, glory_prcnt; | |||||
uint64_t viewed_prcnt_of_db, mylist_prcnt_of_db; | |||||
uint64_t viewed_prcnt_of_mylist, num_of_viewed_eps; | |||||
uint64_t votes, reviews, viewed_minutes; | |||||
}; | |||||
#define e(n) struct api_##n##_result n | #define e(n) struct api_##n##_result n | ||||
struct api_result { | struct api_result { | ||||
@@ -255,6 +265,7 @@ struct api_result { | |||||
e(mylistadd); | e(mylistadd); | ||||
e(encrypt); | e(encrypt); | ||||
e(mylistmod); | e(mylistmod); | ||||
e(myliststats); | |||||
}; | }; | ||||
}; | }; | ||||
#undef e | #undef e | ||||
@@ -269,5 +280,6 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash, | |||||
struct api_mylistadd_opts *opts, struct api_result *res); | struct api_mylistadd_opts *opts, struct api_result *res); | ||||
enum error api_cmd_mylistmod(uint64_t lid, struct api_mylistadd_opts *opts, | enum error api_cmd_mylistmod(uint64_t lid, struct api_mylistadd_opts *opts, | ||||
struct api_result *res); | struct api_result *res); | ||||
enum error api_cmd_myliststats(struct api_result *res); | |||||
#endif /* _API_H */ | #endif /* _API_H */ |
@@ -23,8 +23,10 @@ static const struct cmd_entry ents[] = { | |||||
{ .arg_name = "server-version", .fn = cmd_server_version, .need_api = true }, | { .arg_name = "server-version", .fn = cmd_server_version, .need_api = true }, | ||||
{ .arg_name = "uptime", .fn = cmd_server_uptime, .need_auth = true }, | { .arg_name = "uptime", .fn = cmd_server_uptime, .need_auth = true }, | ||||
{ .arg_name = "ed2k", .fn = cmd_ed2k, }, | { .arg_name = "ed2k", .fn = cmd_ed2k, }, | ||||
{ .arg_name = "add", .fn = cmd_add, .argcheck = cmd_add_argcheck, .need_auth = true, .need_cache = true, }, | { .arg_name = "add", .fn = cmd_add, .argcheck = cmd_add_argcheck, .need_auth = true, .need_cache = true, }, | ||||
{ .arg_name = "modify", .fn = cmd_modify, .argcheck = cmd_modify_argcheck, .need_auth = true, .need_cache = true, }, | { .arg_name = "modify", .fn = cmd_modify, .argcheck = cmd_modify_argcheck, .need_auth = true, .need_cache = true, }, | ||||
{ .arg_name = "stats", .fn = cmd_stats, .need_auth = true, .need_cache = false, }, | |||||
}; | }; | ||||
static const int32_t ents_len = sizeof(ents)/sizeof(*ents); | static const int32_t ents_len = sizeof(ents)/sizeof(*ents); | ||||
@@ -42,4 +42,9 @@ enum error cmd_prog_version(void *); | |||||
enum error cmd_modify(void *data); | enum error cmd_modify(void *data); | ||||
enum error cmd_modify_argcheck(); | enum error cmd_modify_argcheck(); | ||||
/* | |||||
* Request the mylist stats | |||||
*/ | |||||
enum error cmd_stats(void *data); | |||||
#endif /* _CMD_H */ | #endif /* _CMD_H */ |
@@ -0,0 +1,40 @@ | |||||
#include <stdio.h> | |||||
#include "error.h" | |||||
#include "api.h" | |||||
#include "util.h" | |||||
#include "uio.h" | |||||
enum error cmd_stats(void *data) | |||||
{ | |||||
enum error err = NOERR; | |||||
struct api_result res; | |||||
struct api_myliststats_result *stats = &res.myliststats; | |||||
err = api_cmd_myliststats(&res); | |||||
if (err != NOERR) { | |||||
uio_error("Failed to request stats from API server"); | |||||
return err; | |||||
} | |||||
printf("Mylist status:\n" | |||||
"Anime: %lu\n" | |||||
"Episodes: %lu\n" | |||||
"Files: %lu\n" | |||||
"Size of files: %lu\n" | |||||
"Added groups: %lu\n" | |||||
"leech%%: %lu\n" | |||||
"Glory%%: %lu\n" | |||||
"Viewed%%/DB: %lu\n" | |||||
"Mylist%%/DB: %lu\n" | |||||
"Viewed%%/Mylist: %lu\n" | |||||
"Viewed eps: %lu\n" | |||||
"Votes: %lu\n" | |||||
"Reviews: %lu\n" | |||||
"Viewed minutes: %lu\n", | |||||
stats->animes, stats->eps, stats->files, stats->size_of_files, stats->added_groups, | |||||
stats->leech_prcnt, stats->glory_prcnt, stats->viewed_prcnt_of_db, | |||||
stats->mylist_prcnt_of_db, stats->viewed_prcnt_of_mylist, stats->num_of_viewed_eps, | |||||
stats->votes, stats->reviews, stats->viewed_minutes); | |||||
return NOERR; | |||||
} |
@@ -171,6 +171,12 @@ static struct conf_entry options[] = { | |||||
.subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]), | .subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]), | ||||
}, | }, | ||||
{ .l_name = "stats", .s_name = UCHAR_MAX + 10, | |||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true, | |||||
.type = OTYPE_SUBCOMMAND, .handle_order = 1, | |||||
.h_desc = "Get the mylist status", | |||||
}, | |||||
/*{ .l_name = "stats", .s_name = UCHAR_MAX + 5, | /*{ .l_name = "stats", .s_name = UCHAR_MAX + 5, | ||||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true, | .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, | ||||
.type = OTYPE_B, .handle_order = 1, .value_is_set = true },*/ | .type = OTYPE_B, .handle_order = 1, .value_is_set = true },*/ | ||||
@@ -265,9 +265,7 @@ ssize_t net_read(void* out_data, size_t read_size) | |||||
int en = errno; | 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 -en; | |||||
} | } | ||||
if (read == read_size) | if (read == read_size) | ||||
uio_warning("{net} Data may have been discarded!"); | uio_warning("{net} Data may have been discarded!"); | ||||
@@ -12,9 +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 | |||||
*/ | |||||
* Returns the number of bytes sent/read or | |||||
* a negative value which is the errno */ | |||||
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); | ||||