refactor(c): clarify control flow and MTP callbacks
This commit is contained in:
parent
7b769076c1
commit
7d29dc83a2
9 changed files with 484 additions and 246 deletions
|
|
@ -40,15 +40,18 @@ static const TrainlogDashboardTileLayout defaults[TRAINLOG_DASHBOARD_LAYOUT_TILE
|
|||
|
||||
static int contract_index(const char *id) {
|
||||
size_t index;
|
||||
for (index = 0U; index < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++index)
|
||||
if (strcmp(id, contracts[index].id) == 0)
|
||||
for (index = 0U; index < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++index) {
|
||||
if (strcmp(id, contracts[index].id) == 0) {
|
||||
return (int)index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void trainlog_dashboard_layout_default(TrainlogDashboardLayout *layout) {
|
||||
if (layout == NULL)
|
||||
if (layout == NULL) {
|
||||
return;
|
||||
}
|
||||
layout->revision = 0U;
|
||||
(void)memcpy(layout->tiles, defaults, sizeof(defaults));
|
||||
}
|
||||
|
|
@ -61,25 +64,30 @@ static bool overlap(const TrainlogDashboardTileLayout *a, const TrainlogDashboar
|
|||
bool trainlog_dashboard_layout_validate(const TrainlogDashboardLayout *layout) {
|
||||
bool seen[TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT] = {false};
|
||||
size_t index;
|
||||
if (layout == NULL || layout->revision > TRAINLOG_DASHBOARD_LAYOUT_MAX_REVISION)
|
||||
if (layout == NULL || layout->revision > TRAINLOG_DASHBOARD_LAYOUT_MAX_REVISION) {
|
||||
return false;
|
||||
}
|
||||
for (index = 0U; index < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++index) {
|
||||
const TrainlogDashboardTileLayout *tile = &layout->tiles[index];
|
||||
int found = contract_index(tile->id);
|
||||
const TileContract *contract;
|
||||
size_t other;
|
||||
if (found < 0 || seen[(size_t)found])
|
||||
if (found < 0 || seen[(size_t)found]) {
|
||||
return false;
|
||||
}
|
||||
seen[(size_t)found] = true;
|
||||
contract = &contracts[(size_t)found];
|
||||
if (tile->y > TRAINLOG_DASHBOARD_LAYOUT_MAX_Y || tile->width < contract->min_width ||
|
||||
tile->width > contract->max_width || tile->height < contract->min_height ||
|
||||
tile->height > contract->max_height ||
|
||||
tile->x + tile->width > TRAINLOG_DASHBOARD_LAYOUT_COLUMNS)
|
||||
tile->x + tile->width > TRAINLOG_DASHBOARD_LAYOUT_COLUMNS) {
|
||||
return false;
|
||||
for (other = index + 1U; other < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++other)
|
||||
if (overlap(tile, &layout->tiles[other]))
|
||||
}
|
||||
for (other = index + 1U; other < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++other) {
|
||||
if (overlap(tile, &layout->tiles[other])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -90,11 +98,13 @@ static bool exact_object(const yyjson_val *value, size_t expected) {
|
|||
|
||||
static bool read_u16(yyjson_val *value, uint16_t *output) {
|
||||
uint64_t number;
|
||||
if (!yyjson_is_uint(value))
|
||||
if (!yyjson_is_uint(value)) {
|
||||
return false;
|
||||
}
|
||||
number = yyjson_get_uint(value);
|
||||
if (number > UINT16_MAX)
|
||||
if (number > UINT16_MAX) {
|
||||
return false;
|
||||
}
|
||||
*output = (uint16_t)number;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -111,11 +121,13 @@ trainlog_dashboard_layout_parse(const char *json, size_t size, TrainlogDashboard
|
|||
yyjson_val *tiles;
|
||||
size_t index;
|
||||
if (json == NULL || layout == NULL || size == 0U ||
|
||||
size >= TRAINLOG_DASHBOARD_LAYOUT_JSON_CAPACITY)
|
||||
size >= TRAINLOG_DASHBOARD_LAYOUT_JSON_CAPACITY) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_INVALID;
|
||||
}
|
||||
document = yyjson_read_opts((char *)json, size, YYJSON_READ_NOFLAG, NULL, &error);
|
||||
if (document == NULL)
|
||||
if (document == NULL) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_INVALID;
|
||||
}
|
||||
root = yyjson_doc_get_root(document);
|
||||
if (!exact_object(root, 5U) ||
|
||||
!yyjson_equals_str(yyjson_obj_get(root, "format"), "trainlog-dashboard-layout") ||
|
||||
|
|
@ -162,14 +174,16 @@ bool trainlog_dashboard_layout_path(char *output, size_t capacity) {
|
|||
const char *base = getenv("XDG_CONFIG_HOME");
|
||||
const char *home;
|
||||
int length;
|
||||
if (output == NULL || capacity == 0U)
|
||||
if (output == NULL || capacity == 0U) {
|
||||
return false;
|
||||
if (base != NULL && base[0] != '\0')
|
||||
}
|
||||
if (base != NULL && base[0] != '\0') {
|
||||
length = snprintf(output, capacity, "%s/trainlog/web/dashboard-layout-v1.json", base);
|
||||
else {
|
||||
} else {
|
||||
home = getenv("HOME");
|
||||
if (home == NULL || home[0] == '\0')
|
||||
if (home == NULL || home[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
length =
|
||||
snprintf(output, capacity, "%s/.config/trainlog/web/dashboard-layout-v1.json", home);
|
||||
}
|
||||
|
|
@ -181,32 +195,40 @@ static bool ensure_private_directories(const char *path) {
|
|||
char trainlog[4096];
|
||||
char *last;
|
||||
struct stat status;
|
||||
if (path == NULL || strlen(path) >= sizeof(web))
|
||||
if (path == NULL || strlen(path) >= sizeof(web)) {
|
||||
return false;
|
||||
}
|
||||
(void)snprintf(web, sizeof(web), "%s", path);
|
||||
last = strrchr(web, '/');
|
||||
if (last == NULL)
|
||||
if (last == NULL) {
|
||||
return false;
|
||||
}
|
||||
*last = '\0';
|
||||
(void)snprintf(trainlog, sizeof(trainlog), "%s", web);
|
||||
last = strrchr(trainlog, '/');
|
||||
if (last == NULL)
|
||||
if (last == NULL) {
|
||||
return false;
|
||||
}
|
||||
*last = '\0';
|
||||
/* CONTRACT: only the two Trainlog-owned directories are created/chmodded;
|
||||
* an existing symlink or non-directory is never followed. */
|
||||
{
|
||||
if (mkdir(trainlog, 0700) != 0 && errno != EEXIST)
|
||||
if (mkdir(trainlog, 0700) != 0 && errno != EEXIST) {
|
||||
return false;
|
||||
if (lstat(trainlog, &status) != 0 || !S_ISDIR(status.st_mode))
|
||||
}
|
||||
if (lstat(trainlog, &status) != 0 || !S_ISDIR(status.st_mode)) {
|
||||
return false;
|
||||
if (chmod(trainlog, 0700) != 0)
|
||||
}
|
||||
if (chmod(trainlog, 0700) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mkdir(web, 0700) != 0 && errno != EEXIST)
|
||||
if (mkdir(web, 0700) != 0 && errno != EEXIST) {
|
||||
return false;
|
||||
if (lstat(web, &status) != 0 || !S_ISDIR(status.st_mode))
|
||||
}
|
||||
if (lstat(web, &status) != 0 || !S_ISDIR(status.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
return chmod(web, 0700) == 0;
|
||||
}
|
||||
|
||||
|
|
@ -215,19 +237,23 @@ static bool sync_parent(const char *path) {
|
|||
char *slash;
|
||||
int fd;
|
||||
bool ok;
|
||||
if (strlen(path) >= sizeof(parent))
|
||||
if (strlen(path) >= sizeof(parent)) {
|
||||
return false;
|
||||
}
|
||||
(void)snprintf(parent, sizeof(parent), "%s", path);
|
||||
slash = strrchr(parent, '/');
|
||||
if (slash == NULL)
|
||||
if (slash == NULL) {
|
||||
return false;
|
||||
}
|
||||
*slash = '\0';
|
||||
fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
ok = fsync(fd) == 0;
|
||||
if (close(fd) != 0)
|
||||
if (close(fd) != 0) {
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
@ -244,8 +270,9 @@ bool trainlog_dashboard_layout_serialize(const TrainlogDashboardLayout *layout,
|
|||
: source == TRAINLOG_DASHBOARD_LAYOUT_INVALID_PERSISTED
|
||||
? "invalid_persisted"
|
||||
: "default";
|
||||
if (!trainlog_dashboard_layout_validate(layout) || output == NULL || size == NULL)
|
||||
if (!trainlog_dashboard_layout_validate(layout) || output == NULL || size == NULL) {
|
||||
return false;
|
||||
}
|
||||
#define APPEND(...) \
|
||||
do { \
|
||||
count = snprintf(output + used, capacity - used, __VA_ARGS__); \
|
||||
|
|
@ -256,8 +283,9 @@ bool trainlog_dashboard_layout_serialize(const TrainlogDashboardLayout *layout,
|
|||
APPEND("{\"format\":\"trainlog-dashboard-layout\",\"version\":1,"
|
||||
"\"revision\":%llu,\"columns\":12",
|
||||
(unsigned long long)layout->revision);
|
||||
if (include_source)
|
||||
if (include_source) {
|
||||
APPEND(",\"source\":\"%s\"", source_name);
|
||||
}
|
||||
APPEND(",\"tiles\":[");
|
||||
for (index = 0U; index < TRAINLOG_DASHBOARD_LAYOUT_TILE_COUNT; ++index) {
|
||||
const TrainlogDashboardTileLayout *tile = &layout->tiles[index];
|
||||
|
|
@ -284,15 +312,17 @@ trainlog_dashboard_layout_load(TrainlogDashboardLayout *layout,
|
|||
ssize_t count;
|
||||
int fd;
|
||||
TrainlogDashboardLayoutResult result;
|
||||
if (layout == NULL || source == NULL || !trainlog_dashboard_layout_path(path, sizeof(path)))
|
||||
if (layout == NULL || source == NULL || !trainlog_dashboard_layout_path(path, sizeof(path))) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
}
|
||||
trainlog_dashboard_layout_default(layout);
|
||||
*source = TRAINLOG_DASHBOARD_LAYOUT_DEFAULT;
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT || errno == ELOOP) {
|
||||
if (errno == ELOOP)
|
||||
if (errno == ELOOP) {
|
||||
*source = TRAINLOG_DASHBOARD_LAYOUT_INVALID_PERSISTED;
|
||||
}
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_OK;
|
||||
}
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
|
|
@ -312,8 +342,9 @@ trainlog_dashboard_layout_load(TrainlogDashboardLayout *layout,
|
|||
if (result != TRAINLOG_DASHBOARD_LAYOUT_OK) {
|
||||
trainlog_dashboard_layout_default(layout);
|
||||
*source = TRAINLOG_DASHBOARD_LAYOUT_INVALID_PERSISTED;
|
||||
} else
|
||||
} else {
|
||||
*source = TRAINLOG_DASHBOARD_LAYOUT_PERSISTED;
|
||||
}
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_OK;
|
||||
}
|
||||
|
||||
|
|
@ -321,10 +352,12 @@ static bool write_all(int fd, const char *data, size_t size) {
|
|||
size_t used = 0U;
|
||||
while (used < size) {
|
||||
ssize_t count = write(fd, data + used, size - used);
|
||||
if (count < 0 && errno == EINTR)
|
||||
if (count < 0 && errno == EINTR) {
|
||||
continue;
|
||||
if (count <= 0)
|
||||
}
|
||||
if (count <= 0) {
|
||||
return false;
|
||||
}
|
||||
used += (size_t)count;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -341,26 +374,32 @@ TrainlogDashboardLayoutResult trainlog_dashboard_layout_save(const TrainlogDashb
|
|||
bool ok;
|
||||
if (layout == NULL || saved == NULL || !trainlog_dashboard_layout_validate(layout) ||
|
||||
layout->revision != expected_revision ||
|
||||
expected_revision >= TRAINLOG_DASHBOARD_LAYOUT_MAX_REVISION)
|
||||
expected_revision >= TRAINLOG_DASHBOARD_LAYOUT_MAX_REVISION) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_INVALID;
|
||||
if (trainlog_dashboard_layout_load(¤t, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK)
|
||||
}
|
||||
if (trainlog_dashboard_layout_load(¤t, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
if (current.revision != expected_revision)
|
||||
}
|
||||
if (current.revision != expected_revision) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_CONFLICT;
|
||||
}
|
||||
*saved = *layout;
|
||||
saved->revision = expected_revision + 1U;
|
||||
if (!trainlog_dashboard_layout_path(path, sizeof(path)) || !ensure_private_directories(path) ||
|
||||
!trainlog_dashboard_layout_serialize(
|
||||
saved, TRAINLOG_DASHBOARD_LAYOUT_PERSISTED, false, json, sizeof(json), &size) ||
|
||||
snprintf(temporary, sizeof(temporary), "%s.tmp.XXXXXX", path) >= (int)sizeof(temporary))
|
||||
snprintf(temporary, sizeof(temporary), "%s.tmp.XXXXXX", path) >= (int)sizeof(temporary)) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
}
|
||||
fd = mkstemp(temporary);
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
}
|
||||
ok = fchmod(fd, 0600) == 0 && fcntl(fd, F_SETFD, FD_CLOEXEC) == 0 &&
|
||||
write_all(fd, json, size) && fsync(fd) == 0;
|
||||
if (close(fd) != 0)
|
||||
if (close(fd) != 0) {
|
||||
ok = false;
|
||||
}
|
||||
if (!ok || rename(temporary, path) != 0) {
|
||||
(void)unlink(temporary);
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
|
|
@ -376,12 +415,15 @@ TrainlogDashboardLayoutResult trainlog_dashboard_layout_delete(uint64_t expected
|
|||
TrainlogDashboardLayout current;
|
||||
TrainlogDashboardLayoutSource source;
|
||||
char path[4096];
|
||||
if (trainlog_dashboard_layout_load(¤t, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK)
|
||||
if (trainlog_dashboard_layout_load(¤t, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
if (current.revision != expected_revision)
|
||||
}
|
||||
if (current.revision != expected_revision) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_CONFLICT;
|
||||
if (!trainlog_dashboard_layout_path(path, sizeof(path)))
|
||||
}
|
||||
if (!trainlog_dashboard_layout_path(path, sizeof(path))) {
|
||||
return TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
}
|
||||
if (unlink(path) != 0) {
|
||||
return errno == ENOENT ? TRAINLOG_DASHBOARD_LAYOUT_OK : TRAINLOG_DASHBOARD_LAYOUT_IO_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ typedef struct Selection {
|
|||
} Selection;
|
||||
|
||||
static void diag(char *output, size_t size, const char *value) {
|
||||
if (output != NULL && size > 0U)
|
||||
if (output != NULL && size > 0U) {
|
||||
(void)snprintf(output, size, "%s", value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool safe_leaf(const char *name) {
|
||||
|
|
@ -50,16 +51,18 @@ static TrainlogStatus one_named(const TrainlogGenerationMtpIo *io,
|
|||
TrainlogMtpEntry entries[TRAINLOG_GENERATION_MTP_MAX_CHILDREN];
|
||||
size_t count = 0U, index, matches = 0U;
|
||||
TrainlogStatus status = children(io, selection, parent, entries, &count);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
for (index = 0U; index < count; ++index) {
|
||||
if (entries[index].folder == folder && strcmp(entries[index].name, name) == 0) {
|
||||
*result = entries[index];
|
||||
++matches;
|
||||
}
|
||||
}
|
||||
if (matches > 1U)
|
||||
if (matches > 1U) {
|
||||
return TRAINLOG_STATUS_CONFLICT;
|
||||
}
|
||||
return matches == 1U ? TRAINLOG_STATUS_OK : TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
|
@ -71,16 +74,19 @@ static bool peer_document_matches(const char *path, const char *expected) {
|
|||
yyjson_val *root, *capabilities, *item;
|
||||
size_t index, maximum;
|
||||
bool manifest = false, acknowledgement = false, valid;
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
count = read(fd, buffer, sizeof(buffer));
|
||||
(void)close(fd);
|
||||
if (count <= 0 || (size_t)count >= sizeof(buffer))
|
||||
if (count <= 0 || (size_t)count >= sizeof(buffer)) {
|
||||
return false;
|
||||
}
|
||||
buffer[count] = '\0';
|
||||
document = yyjson_read(buffer, (size_t)count, YYJSON_READ_NOFLAG);
|
||||
if (document == NULL)
|
||||
if (document == NULL) {
|
||||
return false;
|
||||
}
|
||||
root = yyjson_doc_get_root(document);
|
||||
capabilities = yyjson_obj_get(root, "capabilities");
|
||||
valid = yyjson_is_obj(root) && yyjson_obj_size(root) == 4U &&
|
||||
|
|
@ -95,10 +101,12 @@ static bool peer_document_matches(const char *path, const char *expected) {
|
|||
valid = false;
|
||||
break;
|
||||
}
|
||||
if (yyjson_equals_str(item, "generation-manifest-v1"))
|
||||
if (yyjson_equals_str(item, "generation-manifest-v1")) {
|
||||
manifest = true;
|
||||
if (yyjson_equals_str(item, "generation-ack-v1"))
|
||||
}
|
||||
if (yyjson_equals_str(item, "generation-ack-v1")) {
|
||||
acknowledgement = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
yyjson_doc_free(document);
|
||||
|
|
@ -131,8 +139,9 @@ static TrainlogStatus select_peer(const TrainlogGenerationMtpIo *io,
|
|||
storages,
|
||||
TRAINLOG_GENERATION_MTP_MAX_STORAGES,
|
||||
&storage_count);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
continue;
|
||||
}
|
||||
for (si = 0U; si < storage_count; ++si) {
|
||||
Selection candidate = {
|
||||
devices[di].bus_number, devices[di].device_number, storages[si].storage_id, 0U};
|
||||
|
|
@ -140,18 +149,21 @@ static TrainlogStatus select_peer(const TrainlogGenerationMtpIo *io,
|
|||
if (one_named(io, &candidate, UINT32_MAX, "Documents", true, &documents) !=
|
||||
TRAINLOG_STATUS_OK ||
|
||||
one_named(io, &candidate, documents.item_id, "Trainlog", true, &root) !=
|
||||
TRAINLOG_STATUS_OK)
|
||||
TRAINLOG_STATUS_OK) {
|
||||
continue;
|
||||
}
|
||||
candidate.root = root.item_id;
|
||||
if (one_named(io, &candidate, candidate.root, "android-peer-v1.json", false, &peer) !=
|
||||
TRAINLOG_STATUS_OK)
|
||||
TRAINLOG_STATUS_OK) {
|
||||
continue;
|
||||
}
|
||||
(void)unlink(peer_path);
|
||||
if (peer.size_bytes > 16384U ||
|
||||
io->receive(candidate.bus, candidate.device, peer.item_id, peer_path) !=
|
||||
TRAINLOG_STATUS_OK ||
|
||||
!peer_document_matches(peer_path, expected))
|
||||
!peer_document_matches(peer_path, expected)) {
|
||||
continue;
|
||||
}
|
||||
*output = candidate;
|
||||
++matches;
|
||||
}
|
||||
|
|
@ -169,8 +181,9 @@ static TrainlogStatus select_peer(const TrainlogGenerationMtpIo *io,
|
|||
}
|
||||
|
||||
static TrainlogStatus mkdir_private(const char *path) {
|
||||
if (mkdir(path, 0700) == 0 || errno == EEXIST)
|
||||
if (mkdir(path, 0700) == 0 || errno == EEXIST) {
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -184,70 +197,89 @@ static TrainlogStatus pull_folder(const TrainlogGenerationMtpIo *io,
|
|||
TrainlogMtpEntry entries[TRAINLOG_GENERATION_MTP_MAX_CHILDREN];
|
||||
size_t count = 0U, index;
|
||||
TrainlogStatus status;
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH)
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
status = mkdir_private(local);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
status = children(io, selection, parent, entries, &count);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
if (*objects > TRAINLOG_GENERATION_MTP_MAX_CHILDREN - count)
|
||||
}
|
||||
if (*objects > TRAINLOG_GENERATION_MTP_MAX_CHILDREN - count) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
*objects += count;
|
||||
for (index = 0U; index < count; ++index) {
|
||||
char path[1024], temporary[1060];
|
||||
int written;
|
||||
written = snprintf(path, sizeof(path), "%s/%s", local, entries[index].name);
|
||||
if (!safe_leaf(entries[index].name) || written < 0 || (size_t)written >= sizeof(path))
|
||||
if (!safe_leaf(entries[index].name) || written < 0 || (size_t)written >= sizeof(path)) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
if (entries[index].folder) {
|
||||
status = pull_folder(
|
||||
io, selection, entries[index].item_id, path, depth + 1U, total, objects);
|
||||
} else {
|
||||
struct stat value;
|
||||
if (entries[index].size_bytes > TRAINLOG_GENERATION_MTP_MAX_TOTAL_BYTES ||
|
||||
*total > TRAINLOG_GENERATION_MTP_MAX_TOTAL_BYTES - entries[index].size_bytes)
|
||||
*total > TRAINLOG_GENERATION_MTP_MAX_TOTAL_BYTES - entries[index].size_bytes) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
*total += entries[index].size_bytes;
|
||||
written = snprintf(temporary, sizeof(temporary), "%s.part.%ld", path, (long)getpid());
|
||||
if (written < 0 || (size_t)written >= sizeof(temporary))
|
||||
if (written < 0 || (size_t)written >= sizeof(temporary)) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
(void)unlink(temporary);
|
||||
status =
|
||||
io->receive(selection->bus, selection->device, entries[index].item_id, temporary);
|
||||
if (status == TRAINLOG_STATUS_OK &&
|
||||
(stat(temporary, &value) != 0 || value.st_size < 0 ||
|
||||
(uint64_t)value.st_size != entries[index].size_bytes ||
|
||||
rename(temporary, path) != 0))
|
||||
rename(temporary, path) != 0)) {
|
||||
status = TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
(void)unlink(temporary);
|
||||
}
|
||||
}
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
|
||||
static bool same_files(const char *left, const char *right) {
|
||||
int a = open(left, O_RDONLY | O_CLOEXEC), b = open(right, O_RDONLY | O_CLOEXEC);
|
||||
char ab[8192], bb[8192];
|
||||
ssize_t ac = 0, bc;
|
||||
int left_fd = open(left, O_RDONLY | O_CLOEXEC);
|
||||
int right_fd = open(right, O_RDONLY | O_CLOEXEC);
|
||||
char left_buffer[8192];
|
||||
char right_buffer[8192];
|
||||
ssize_t left_count = 0;
|
||||
ssize_t right_count;
|
||||
bool same = true;
|
||||
if (a < 0 || b < 0)
|
||||
if (left_fd < 0 || right_fd < 0) {
|
||||
same = false;
|
||||
while (same && (ac = read(a, ab, sizeof(ab))) > 0) {
|
||||
bc = read(b, bb, (size_t)ac);
|
||||
if (bc != ac || memcmp(ab, bb, (size_t)ac) != 0)
|
||||
same = false;
|
||||
}
|
||||
if (same && (ac < 0 || read(b, bb, 1U) != 0))
|
||||
while (same && (left_count = read(left_fd, left_buffer, sizeof(left_buffer))) > 0) {
|
||||
right_count = read(right_fd, right_buffer, (size_t)left_count);
|
||||
if (right_count != left_count ||
|
||||
memcmp(left_buffer, right_buffer, (size_t)left_count) != 0) {
|
||||
same = false;
|
||||
}
|
||||
}
|
||||
if (same && (left_count < 0 || read(right_fd, right_buffer, 1U) != 0)) {
|
||||
same = false;
|
||||
if (a >= 0)
|
||||
(void)close(a);
|
||||
if (b >= 0)
|
||||
(void)close(b);
|
||||
}
|
||||
if (left_fd >= 0) {
|
||||
(void)close(left_fd);
|
||||
}
|
||||
if (right_fd >= 0) {
|
||||
(void)close(right_fd);
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
||||
|
|
@ -265,8 +297,9 @@ static TrainlogStatus push_file(const TrainlogGenerationMtpIo *io,
|
|||
(void)snprintf(comparison, sizeof(comparison), "%s.compare.%ld", local, (long)getpid());
|
||||
(void)unlink(comparison);
|
||||
status = io->receive(selection->bus, selection->device, existing.item_id, comparison);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
if (same_files(local, comparison)) {
|
||||
(void)unlink(comparison);
|
||||
return TRAINLOG_STATUS_OK;
|
||||
|
|
@ -281,8 +314,9 @@ static TrainlogStatus push_file(const TrainlogGenerationMtpIo *io,
|
|||
local,
|
||||
temporary_name,
|
||||
&uploaded);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
(void)snprintf(comparison, sizeof(comparison), "%s.verify.%ld", local, (long)getpid());
|
||||
(void)unlink(comparison);
|
||||
status = io->receive(selection->bus, selection->device, uploaded, comparison);
|
||||
|
|
@ -293,16 +327,19 @@ static TrainlogStatus push_file(const TrainlogGenerationMtpIo *io,
|
|||
}
|
||||
(void)unlink(comparison);
|
||||
status = io->remove(selection->bus, selection->device, existing.item_id);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = io->rename(selection->bus, selection->device, uploaded, name);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
if (status != TRAINLOG_STATUS_NOT_FOUND)
|
||||
if (status != TRAINLOG_STATUS_NOT_FOUND) {
|
||||
return status;
|
||||
}
|
||||
status = io->send(
|
||||
selection->bus, selection->device, selection->storage, parent, local, name, &uploaded);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
(void)snprintf(comparison, sizeof(comparison), "%s.verify.%ld", local, (long)getpid());
|
||||
(void)unlink(comparison);
|
||||
status = io->receive(selection->bus, selection->device, uploaded, comparison);
|
||||
|
|
@ -324,17 +361,20 @@ static TrainlogStatus push_folder(const TrainlogGenerationMtpIo *io,
|
|||
DIR *directory;
|
||||
struct dirent *entry;
|
||||
TrainlogStatus status = TRAINLOG_STATUS_OK;
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH || (directory = opendir(local)) == NULL)
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH || (directory = opendir(local)) == NULL) {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
while ((entry = readdir(directory)) != NULL && status == TRAINLOG_STATUS_OK) {
|
||||
char path[1024];
|
||||
struct stat value;
|
||||
uint32_t child;
|
||||
bool created;
|
||||
if (!safe_leaf(entry->d_name) || entry->d_name[0] == '.')
|
||||
if (!safe_leaf(entry->d_name) || entry->d_name[0] == '.') {
|
||||
continue;
|
||||
if ((strcmp(entry->d_name, "manifest.json") == 0) != manifests)
|
||||
}
|
||||
if ((strcmp(entry->d_name, "manifest.json") == 0) != manifests) {
|
||||
continue;
|
||||
}
|
||||
int written = snprintf(path, sizeof(path), "%s/%s", local, entry->d_name);
|
||||
if (written < 0 || (size_t)written >= sizeof(path) || lstat(path, &value) != 0 ||
|
||||
S_ISLNK(value.st_mode)) {
|
||||
|
|
@ -342,8 +382,9 @@ static TrainlogStatus push_folder(const TrainlogGenerationMtpIo *io,
|
|||
break;
|
||||
}
|
||||
if (S_ISDIR(value.st_mode)) {
|
||||
if (manifests)
|
||||
if (manifests) {
|
||||
continue;
|
||||
}
|
||||
status = io->ensure_folder(selection->bus,
|
||||
selection->device,
|
||||
selection->storage,
|
||||
|
|
@ -351,12 +392,15 @@ static TrainlogStatus push_folder(const TrainlogGenerationMtpIo *io,
|
|||
entry->d_name,
|
||||
&child,
|
||||
&created);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = push_folder(io, selection, child, path, depth + 1U, false);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = push_folder(io, selection, child, path, depth + 1U, true);
|
||||
} else if (S_ISREG(value.st_mode))
|
||||
}
|
||||
} else if (S_ISREG(value.st_mode)) {
|
||||
status = push_file(io, selection, parent, path, entry->d_name);
|
||||
}
|
||||
}
|
||||
(void)closedir(directory);
|
||||
return status;
|
||||
|
|
@ -367,14 +411,16 @@ validate_local_folder(const char *local, unsigned int depth, uint64_t *total, si
|
|||
DIR *directory;
|
||||
struct dirent *entry;
|
||||
TrainlogStatus status = TRAINLOG_STATUS_OK;
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH || (directory = opendir(local)) == NULL)
|
||||
if (depth > TRAINLOG_GENERATION_MTP_MAX_DEPTH || (directory = opendir(local)) == NULL) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
while ((entry = readdir(directory)) != NULL && status == TRAINLOG_STATUS_OK) {
|
||||
char path[1024];
|
||||
struct stat value;
|
||||
int written;
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!safe_leaf(entry->d_name) || entry->d_name[0] == '.') {
|
||||
status = TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
break;
|
||||
|
|
@ -390,16 +436,18 @@ validate_local_folder(const char *local, unsigned int depth, uint64_t *total, si
|
|||
break;
|
||||
}
|
||||
++*objects;
|
||||
if (S_ISDIR(value.st_mode))
|
||||
if (S_ISDIR(value.st_mode)) {
|
||||
status = validate_local_folder(path, depth + 1U, total, objects);
|
||||
else if (S_ISREG(value.st_mode)) {
|
||||
} else if (S_ISREG(value.st_mode)) {
|
||||
if (value.st_size < 0 ||
|
||||
(uint64_t)value.st_size > TRAINLOG_GENERATION_MTP_MAX_TOTAL_BYTES - *total)
|
||||
(uint64_t)value.st_size > TRAINLOG_GENERATION_MTP_MAX_TOTAL_BYTES - *total) {
|
||||
status = TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
else
|
||||
} else {
|
||||
*total += (uint64_t)value.st_size;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
status = TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
(void)closedir(directory);
|
||||
return status;
|
||||
|
|
@ -414,13 +462,16 @@ TrainlogStatus trainlog_generation_mtp_pull(const TrainlogGenerationMtpIo *io,
|
|||
uint64_t total = 0U;
|
||||
size_t objects = 0U;
|
||||
TrainlogStatus status;
|
||||
if (io == NULL || expected == NULL || local == NULL)
|
||||
if (io == NULL || expected == NULL || local == NULL) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
status = mkdir_private(local);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = select_peer(io, expected, local, &selection, diagnostic, diagnostic_size);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = pull_folder(io, &selection, selection.root, local, 0U, &total, &objects);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -433,26 +484,32 @@ TrainlogStatus trainlog_generation_mtp_push(const TrainlogGenerationMtpIo *io,
|
|||
TrainlogStatus status;
|
||||
uint64_t total = 0U;
|
||||
size_t objects = 0U;
|
||||
if (io == NULL || expected == NULL || local == NULL)
|
||||
if (io == NULL || expected == NULL || local == NULL) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
status = validate_local_folder(local, 0U, &total, &objects);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = select_peer(io, expected, local, &selection, diagnostic, diagnostic_size);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = push_folder(io, &selection, selection.root, local, 0U, false);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = push_folder(io, &selection, selection.root, local, 0U, true);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static const TrainlogGenerationMtpIo production = {trainlog_usb_list_mtp_devices,
|
||||
trainlog_mtp_list_storages,
|
||||
trainlog_mtp_list_folder,
|
||||
trainlog_mtp_ensure_folder,
|
||||
trainlog_mtp_receive_file,
|
||||
trainlog_mtp_send_text_file,
|
||||
trainlog_mtp_delete_object,
|
||||
trainlog_mtp_rename_object};
|
||||
static const TrainlogGenerationMtpIo production = {
|
||||
.devices = trainlog_usb_list_mtp_devices,
|
||||
.storages = trainlog_mtp_list_storages,
|
||||
.children = trainlog_mtp_list_folder,
|
||||
.ensure_folder = trainlog_mtp_ensure_folder,
|
||||
.receive = trainlog_mtp_receive_file,
|
||||
.send = trainlog_mtp_send_text_file,
|
||||
.remove = trainlog_mtp_delete_object,
|
||||
.rename = trainlog_mtp_rename_object,
|
||||
};
|
||||
const TrainlogGenerationMtpIo *trainlog_generation_mtp_production_io(void) {
|
||||
return &production;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
static bool append(TrainlogJsonWriter *w, const char *s, size_t n) {
|
||||
if (w == NULL || s == NULL || w->failed ||
|
||||
n > w->capacity - (w->size < w->capacity ? w->size : w->capacity) - 1U) {
|
||||
if (w != NULL)
|
||||
if (w != NULL) {
|
||||
w->failed = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
(void)memcpy(w->data + w->size, s, n);
|
||||
|
|
@ -17,14 +18,16 @@ static bool append(TrainlogJsonWriter *w, const char *s, size_t n) {
|
|||
return true;
|
||||
}
|
||||
void trainlog_json_init(TrainlogJsonWriter *w, char *data, size_t capacity) {
|
||||
if (w == NULL)
|
||||
if (w == NULL) {
|
||||
return;
|
||||
}
|
||||
w->data = data;
|
||||
w->capacity = capacity;
|
||||
w->size = 0U;
|
||||
w->failed = data == NULL || capacity == 0U;
|
||||
if (!w->failed)
|
||||
if (!w->failed) {
|
||||
data[0] = '\0';
|
||||
}
|
||||
}
|
||||
bool trainlog_json_raw(TrainlogJsonWriter *w, const char *v) {
|
||||
return v != NULL && append(w, v, strlen(v));
|
||||
|
|
@ -32,8 +35,9 @@ bool trainlog_json_raw(TrainlogJsonWriter *w, const char *v) {
|
|||
bool trainlog_json_string(TrainlogJsonWriter *w, const char *v) {
|
||||
const unsigned char *p = (const unsigned char *)v;
|
||||
char escape[7];
|
||||
if (v == NULL || !append(w, "\"", 1U))
|
||||
if (v == NULL || !append(w, "\"", 1U)) {
|
||||
return false;
|
||||
}
|
||||
while (*p != '\0') {
|
||||
utf8proc_int32_t codepoint;
|
||||
utf8proc_ssize_t sequence = utf8proc_iterate(p, -1, &codepoint);
|
||||
|
|
@ -47,8 +51,9 @@ bool trainlog_json_string(TrainlogJsonWriter *w, const char *v) {
|
|||
if (*p == '\"' || *p == '\\') {
|
||||
escape[0] = '\\';
|
||||
escape[1] = (char)*p;
|
||||
if (!append(w, escape, 2U))
|
||||
if (!append(w, escape, 2U)) {
|
||||
return false;
|
||||
}
|
||||
} else if (*p == '\b' || *p == '\f' || *p == '\n' || *p == '\r' || *p == '\t') {
|
||||
const char codes[] = "bfnrt";
|
||||
const char chars[] = "\b\f\n\r\t";
|
||||
|
|
@ -57,14 +62,17 @@ bool trainlog_json_string(TrainlogJsonWriter *w, const char *v) {
|
|||
for (i = 0U; i < 5U && chars[i] != (char)*p; ++i) {
|
||||
}
|
||||
escape[1] = codes[i];
|
||||
if (!append(w, escape, 2U))
|
||||
if (!append(w, escape, 2U)) {
|
||||
return false;
|
||||
}
|
||||
} else if (*p < 0x20U) {
|
||||
int n = snprintf(escape, sizeof(escape), "\\u%04x", (unsigned int)*p);
|
||||
if (n != 6 || !append(w, escape, 6U))
|
||||
if (n != 6 || !append(w, escape, 6U)) {
|
||||
return false;
|
||||
} else if (!append(w, (const char *)p, (size_t)sequence))
|
||||
}
|
||||
} else if (!append(w, (const char *)p, (size_t)sequence)) {
|
||||
return false;
|
||||
}
|
||||
p += (size_t)sequence;
|
||||
}
|
||||
return append(w, "\"", 1U);
|
||||
|
|
|
|||
|
|
@ -18,23 +18,27 @@
|
|||
static bool copy_column(sqlite3_stmt *s, int column, char *out, size_t cap) {
|
||||
const unsigned char *text;
|
||||
int bytes;
|
||||
if (sqlite3_column_type(s, column) != SQLITE_TEXT)
|
||||
if (sqlite3_column_type(s, column) != SQLITE_TEXT) {
|
||||
return false;
|
||||
}
|
||||
text = sqlite3_column_text(s, column);
|
||||
bytes = sqlite3_column_bytes(s, column);
|
||||
if (text == NULL || bytes < 0 || (size_t)bytes >= cap)
|
||||
if (text == NULL || bytes < 0 || (size_t)bytes >= cap) {
|
||||
return false;
|
||||
}
|
||||
(void)memcpy(out, text, (size_t)bytes);
|
||||
out[bytes] = '\0';
|
||||
return true;
|
||||
}
|
||||
static bool read_size(sqlite3_stmt *s, int c, size_t *out) {
|
||||
sqlite3_int64 v;
|
||||
if (sqlite3_column_type(s, c) != SQLITE_INTEGER)
|
||||
if (sqlite3_column_type(s, c) != SQLITE_INTEGER) {
|
||||
return false;
|
||||
}
|
||||
v = sqlite3_column_int64(s, c);
|
||||
if (v < 0 || (uint64_t)v > (uint64_t)SIZE_MAX)
|
||||
if (v < 0 || (uint64_t)v > (uint64_t)SIZE_MAX) {
|
||||
return false;
|
||||
}
|
||||
*out = (size_t)v;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -45,8 +49,9 @@ static bool prepare(TrainlogDatabase *d, const char *sql, sqlite3_stmt **s) {
|
|||
static bool format_generated(int64_t now, char out[TRAINLOG_TIMESTAMP_MAX + 1U]) {
|
||||
time_t value = (time_t)now;
|
||||
struct tm utc;
|
||||
if ((int64_t)value != now || gmtime_r(&value, &utc) == NULL)
|
||||
if ((int64_t)value != now || gmtime_r(&value, &utc) == NULL) {
|
||||
return false;
|
||||
}
|
||||
return strftime(out, TRAINLOG_TIMESTAMP_MAX + 1U, "%Y-%m-%dT%H:%M:%SZ", &utc) > 0U;
|
||||
}
|
||||
static bool format_local_date(time_t value, char out[11]) {
|
||||
|
|
@ -59,8 +64,9 @@ static bool load_user(char out[TRAINLOG_NAME_MAX + 1U]) {
|
|||
char buffer[4096];
|
||||
int rc = getpwuid_r(geteuid(), &value, buffer, sizeof(buffer), &result);
|
||||
if (rc != 0 || result == NULL || result->pw_name == NULL || result->pw_name[0] == '\0' ||
|
||||
strlen(result->pw_name) > TRAINLOG_NAME_MAX)
|
||||
strlen(result->pw_name) > TRAINLOG_NAME_MAX) {
|
||||
return false;
|
||||
}
|
||||
(void)snprintf(out, TRAINLOG_NAME_MAX + 1U, "%s", result->pw_name);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -83,10 +89,12 @@ load_activity(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *ou
|
|||
size_t i;
|
||||
time_t reference = (time_t)now;
|
||||
struct tm current;
|
||||
if ((int64_t)reference != now)
|
||||
if ((int64_t)reference != now) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
if (localtime_r(&reference, ¤t) == NULL)
|
||||
}
|
||||
if (localtime_r(&reference, ¤t) == NULL) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
out->activity_count = TRAINLOG_WEB_ACTIVITY_DAYS;
|
||||
for (i = 0U; i < TRAINLOG_WEB_ACTIVITY_DAYS; ++i) {
|
||||
struct tm day = current;
|
||||
|
|
@ -94,32 +102,38 @@ load_activity(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *ou
|
|||
day.tm_mday -= (int)(TRAINLOG_WEB_ACTIVITY_DAYS - 1U - i);
|
||||
day.tm_isdst = -1;
|
||||
normalized = mktime(&day);
|
||||
if (normalized == (time_t)-1 || !format_local_date(normalized, out->activity[i].date))
|
||||
if (normalized == (time_t)-1 || !format_local_date(normalized, out->activity[i].date)) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
}
|
||||
if (!prepare(d, SQL, &s) || sqlite3_bind_int64(s, 1, now) != SQLITE_OK ||
|
||||
sqlite3_bind_text(s, 2, out->activity[0].date, -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||
sqlite3_bind_text(s, 2, out->activity[0].date, -1, SQLITE_TRANSIENT) != SQLITE_OK) {
|
||||
goto fail;
|
||||
}
|
||||
while ((step = sqlite3_step(s)) == SQLITE_ROW) {
|
||||
char date[11];
|
||||
size_t sessions, sets;
|
||||
if (!copy_column(s, 0, date, sizeof(date)) || !read_size(s, 1, &sessions) ||
|
||||
!read_size(s, 2, &sets))
|
||||
!read_size(s, 2, &sets)) {
|
||||
goto fail;
|
||||
for (i = 0U; i < out->activity_count; ++i)
|
||||
}
|
||||
for (i = 0U; i < out->activity_count; ++i) {
|
||||
if (strcmp(date, out->activity[i].date) == 0) {
|
||||
out->activity[i].active = true;
|
||||
out->activity[i].session_count = sessions;
|
||||
out->activity[i].set_count = sets;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
fail:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -146,8 +160,9 @@ static TrainlogStatus load_last(TrainlogDatabase *d, int64_t now, TrainlogWebLas
|
|||
sqlite3_stmt *s = NULL;
|
||||
int step;
|
||||
TrainlogTimestampKey start, end;
|
||||
if (!prepare(d, SESSION_SQL, &s) || sqlite3_bind_int64(s, 1, now) != SQLITE_OK)
|
||||
if (!prepare(d, SESSION_SQL, &s) || sqlite3_bind_int64(s, 1, now) != SQLITE_OK) {
|
||||
goto fail;
|
||||
}
|
||||
step = sqlite3_step(s);
|
||||
if (step == SQLITE_DONE) {
|
||||
(void)sqlite3_finalize(s);
|
||||
|
|
@ -157,8 +172,9 @@ static TrainlogStatus load_last(TrainlogDatabase *d, int64_t now, TrainlogWebLas
|
|||
!copy_column(s, 1, out->started_at, sizeof(out->started_at)) ||
|
||||
!copy_column(s, 2, out->ended_at, sizeof(out->ended_at)) ||
|
||||
!read_size(s, 3, &out->exercise_count) || !read_size(s, 4, &out->set_count) ||
|
||||
!read_size(s, 5, &out->continuous_count) || !read_size(s, 6, &out->max_count))
|
||||
!read_size(s, 5, &out->continuous_count) || !read_size(s, 6, &out->max_count)) {
|
||||
goto fail;
|
||||
}
|
||||
out->available = true;
|
||||
out->has_ended_at = out->ended_at[0] != '\0';
|
||||
if (!trainlog_timestamp_parse(out->started_at, strlen(out->started_at), &start)) {
|
||||
|
|
@ -167,17 +183,20 @@ static TrainlogStatus load_last(TrainlogDatabase *d, int64_t now, TrainlogWebLas
|
|||
}
|
||||
if (out->has_ended_at) {
|
||||
if (!trainlog_timestamp_parse(out->ended_at, strlen(out->ended_at), &end) ||
|
||||
end.utc_second < start.utc_second)
|
||||
end.utc_second < start.utc_second) {
|
||||
goto invalid;
|
||||
}
|
||||
out->has_duration = true;
|
||||
out->duration_seconds = (uint64_t)(end.utc_second - start.utc_second);
|
||||
}
|
||||
if (sqlite3_step(s) != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (sqlite3_step(s) != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
s = NULL;
|
||||
if (!prepare(d, ZONE_SQL, &s) ||
|
||||
sqlite3_bind_text(s, 1, out->session_id, -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||
sqlite3_bind_text(s, 1, out->session_id, -1, SQLITE_TRANSIENT) != SQLITE_OK) {
|
||||
goto fail;
|
||||
}
|
||||
while ((step = sqlite3_step(s)) == SQLITE_ROW) {
|
||||
TrainlogWebWorkedZone *z;
|
||||
const TrainlogBodyZone *catalog;
|
||||
|
|
@ -186,25 +205,30 @@ static TrainlogStatus load_last(TrainlogDatabase *d, int64_t now, TrainlogWebLas
|
|||
}
|
||||
z = &out->zones[out->zone_count];
|
||||
if (!copy_column(s, 0, z->zone_id, sizeof(z->zone_id)) ||
|
||||
!read_size(s, 1, &z->occurrence_count) || !read_size(s, 2, &z->set_count))
|
||||
!read_size(s, 1, &z->occurrence_count) || !read_size(s, 2, &z->set_count)) {
|
||||
goto fail;
|
||||
}
|
||||
z->session_count = 1U;
|
||||
catalog = trainlog_body_zone_catalog_lookup(z->zone_id);
|
||||
if (catalog == NULL || strlen(catalog->display_name) > TRAINLOG_NAME_MAX)
|
||||
if (catalog == NULL || strlen(catalog->display_name) > TRAINLOG_NAME_MAX) {
|
||||
goto invalid;
|
||||
}
|
||||
(void)snprintf(z->label, sizeof(z->label), "%s", catalog->display_name);
|
||||
++out->zone_count;
|
||||
}
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
invalid:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
fail:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -220,8 +244,9 @@ load_maxima(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *out)
|
|||
sqlite3_stmt *s = NULL;
|
||||
int step;
|
||||
if (!prepare(d, SQL, &s) || sqlite3_bind_int64(s, 1, now) != SQLITE_OK ||
|
||||
sqlite3_bind_int64(s, 2, (sqlite3_int64)(TRAINLOG_WEB_MAX_RECORDS + 1U)) != SQLITE_OK)
|
||||
sqlite3_bind_int64(s, 2, (sqlite3_int64)(TRAINLOG_WEB_MAX_RECORDS + 1U)) != SQLITE_OK) {
|
||||
goto fail;
|
||||
}
|
||||
while ((step = sqlite3_step(s)) == SQLITE_ROW) {
|
||||
TrainlogWebMaxRecord *r;
|
||||
if (out->max_record_count == TRAINLOG_WEB_MAX_RECORDS) {
|
||||
|
|
@ -244,12 +269,14 @@ load_maxima(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *out)
|
|||
r->weight_kg = sqlite3_column_double(s, 5);
|
||||
++out->max_record_count;
|
||||
}
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
fail:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -271,8 +298,9 @@ load_zones(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *out)
|
|||
sqlite3_bind_text(
|
||||
s, 1, out->activity[TRAINLOG_WEB_ACTIVITY_DAYS - 30U].date, -1, SQLITE_TRANSIENT) !=
|
||||
SQLITE_OK ||
|
||||
sqlite3_bind_int64(s, 2, now) != SQLITE_OK)
|
||||
sqlite3_bind_int64(s, 2, now) != SQLITE_OK) {
|
||||
goto fail;
|
||||
}
|
||||
while ((step = sqlite3_step(s)) == SQLITE_ROW) {
|
||||
TrainlogWebWorkedZone *z;
|
||||
const TrainlogBodyZone *catalog;
|
||||
|
|
@ -283,8 +311,9 @@ load_zones(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *out)
|
|||
z = &out->muscle_zones[out->muscle_zone_count];
|
||||
if (!copy_column(s, 0, z->zone_id, sizeof(z->zone_id)) ||
|
||||
!read_size(s, 1, &z->session_count) || !read_size(s, 2, &z->occurrence_count) ||
|
||||
!read_size(s, 3, &z->set_count))
|
||||
!read_size(s, 3, &z->set_count)) {
|
||||
goto fail;
|
||||
}
|
||||
catalog = trainlog_body_zone_catalog_lookup(z->zone_id);
|
||||
if (catalog == NULL || strlen(catalog->display_name) > TRAINLOG_NAME_MAX) {
|
||||
out->invalid_data = true;
|
||||
|
|
@ -293,12 +322,14 @@ load_zones(TrainlogDatabase *d, int64_t now, TrainlogWebDashboardSnapshot *out)
|
|||
(void)snprintf(z->label, sizeof(z->label), "%s", catalog->display_name);
|
||||
++out->muscle_zone_count;
|
||||
}
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
fail:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -313,8 +344,9 @@ static TrainlogStatus scan_quality(TrainlogDatabase *d, TrainlogWebDashboardSnap
|
|||
sqlite3_stmt *s = NULL;
|
||||
int step;
|
||||
size_t count = 0U;
|
||||
if (!prepare(d, SQL, &s))
|
||||
if (!prepare(d, SQL, &s)) {
|
||||
goto fail;
|
||||
}
|
||||
while ((step = sqlite3_step(s)) == SQLITE_ROW) {
|
||||
char started[TRAINLOG_TIMESTAMP_MAX + 1U], ended[TRAINLOG_TIMESTAMP_MAX + 1U];
|
||||
TrainlogTimestampKey start_key, end_key;
|
||||
|
|
@ -327,15 +359,18 @@ static TrainlogStatus scan_quality(TrainlogDatabase *d, TrainlogWebDashboardSnap
|
|||
!copy_column(s, 1, ended, sizeof(ended)) ||
|
||||
!trainlog_timestamp_parse(started, strlen(started), &start_key) ||
|
||||
(ended[0] != '\0' && (!trainlog_timestamp_parse(ended, strlen(ended), &end_key) ||
|
||||
trainlog_timestamp_compare(&end_key, &start_key) < 0)))
|
||||
trainlog_timestamp_compare(&end_key, &start_key) < 0))) {
|
||||
out->invalid_data = true;
|
||||
}
|
||||
}
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK)
|
||||
if (step != SQLITE_DONE || sqlite3_finalize(s) != SQLITE_OK) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
return TRAINLOG_STATUS_OK;
|
||||
fail:
|
||||
if (s != NULL)
|
||||
if (s != NULL) {
|
||||
(void)sqlite3_finalize(s);
|
||||
}
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
|
||||
|
|
@ -344,20 +379,24 @@ TrainlogStatus trainlog_web_dashboard_load(TrainlogDatabase *d,
|
|||
TrainlogWebDashboardSnapshot *out) {
|
||||
TrainlogStatus status, end_status;
|
||||
TrainlogDashboardQuery progression_query;
|
||||
if (d == NULL || q == NULL || out == NULL)
|
||||
if (d == NULL || q == NULL || out == NULL) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
(void)memset(out, 0, sizeof(*out));
|
||||
out->next_session_reason = "no_persisted_executable_plan";
|
||||
out->cardio_reason = "no_cardio_data_source";
|
||||
if (!format_generated(q->reference_unix_second, out->generated_at) ||
|
||||
!load_user(out->user_display_name))
|
||||
!load_user(out->user_display_name)) {
|
||||
return TRAINLOG_STATUS_DATABASE_ERROR;
|
||||
}
|
||||
status = trainlog_database_read_snapshot_begin(d);
|
||||
if (status != TRAINLOG_STATUS_OK)
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
status = load_activity(d, q->reference_unix_second, out);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = scan_quality(d, out);
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
progression_query.period = TRAINLOG_DASHBOARD_90_DAYS;
|
||||
progression_query.reference_unix_second = q->reference_unix_second;
|
||||
|
|
@ -372,12 +411,15 @@ TrainlogStatus trainlog_web_dashboard_load(TrainlogDatabase *d,
|
|||
status = TRAINLOG_STATUS_OK;
|
||||
}
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = load_maxima(d, q->reference_unix_second, out);
|
||||
if (status == TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (status == TRAINLOG_STATUS_OK) {
|
||||
status = load_zones(d, q->reference_unix_second, out);
|
||||
}
|
||||
end_status = trainlog_database_read_snapshot_end(d, status == TRAINLOG_STATUS_OK);
|
||||
if (end_status != TRAINLOG_STATUS_OK)
|
||||
if (end_status != TRAINLOG_STATUS_OK) {
|
||||
status = end_status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,25 +23,31 @@ static const char *tracking_name(TrainlogTrackingMode value) {
|
|||
return value == TRAINLOG_TRACKING_DURATION ? "duration" : "reps";
|
||||
}
|
||||
static const char *load_name(TrainlogLoadMode value) {
|
||||
if (value == TRAINLOG_LOAD_EXTERNAL)
|
||||
if (value == TRAINLOG_LOAD_EXTERNAL) {
|
||||
return "external";
|
||||
if (value == TRAINLOG_LOAD_ASSISTANCE)
|
||||
}
|
||||
if (value == TRAINLOG_LOAD_ASSISTANCE) {
|
||||
return "assistance";
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
static bool point_improved(const TrainlogDashboardSnapshot *p, size_t i) {
|
||||
const TrainlogExercisePerformancePoint *a, *b;
|
||||
if (i == 0U)
|
||||
if (i == 0U) {
|
||||
return false;
|
||||
}
|
||||
a = &p->performance[i - 1U];
|
||||
b = &p->performance[i];
|
||||
if (!a->has_weight && !b->has_weight)
|
||||
if (!a->has_weight && !b->has_weight) {
|
||||
return b->metric_value > a->metric_value;
|
||||
if (a->has_weight != b->has_weight)
|
||||
}
|
||||
if (a->has_weight != b->has_weight) {
|
||||
return false;
|
||||
if (p->performance_load_mode == TRAINLOG_LOAD_ASSISTANCE)
|
||||
}
|
||||
if (p->performance_load_mode == TRAINLOG_LOAD_ASSISTANCE) {
|
||||
return b->weight_kg < a->weight_kg ||
|
||||
(b->weight_kg == a->weight_kg && b->metric_value > a->metric_value);
|
||||
}
|
||||
return b->weight_kg > a->weight_kg ||
|
||||
(b->weight_kg == a->weight_kg && b->metric_value > a->metric_value);
|
||||
}
|
||||
|
|
@ -72,8 +78,9 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
size_t *out_size) {
|
||||
TrainlogJsonWriter w;
|
||||
size_t i;
|
||||
if (s == NULL || out == NULL || out_size == NULL)
|
||||
if (s == NULL || out == NULL || out_size == NULL) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
*out_size = 0U;
|
||||
trainlog_json_init(&w, out, cap);
|
||||
RAW("{\"api_version\":1,\"data\":{\"footer\":{\"user\":");
|
||||
|
|
@ -84,12 +91,14 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
(void)memcpy(date, s->last_session.started_at, 10U);
|
||||
date[10] = '\0';
|
||||
STR(date);
|
||||
} else
|
||||
} else {
|
||||
RAW("null");
|
||||
}
|
||||
RAW(",\"last_zones\":[");
|
||||
for (i = 0U; i < s->last_session.zone_count; ++i) {
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
}
|
||||
STR(s->last_session.zones[i].label);
|
||||
}
|
||||
RAW("]},");
|
||||
|
|
@ -99,8 +108,9 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW("\"activity\":{\"available\":true,\"window_days\":90,\"days\":[");
|
||||
for (i = 0U; i < s->activity_count; ++i) {
|
||||
const TrainlogWebActivityDay *d = &s->activity[i];
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
}
|
||||
RAW("{\"date\":");
|
||||
STR(d->date);
|
||||
RAW(",\"active\":");
|
||||
|
|
@ -129,26 +139,31 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW(",\"load_mode\":");
|
||||
STR(load_name(s->progression.performance_load_mode));
|
||||
RAW(",\"dose\":");
|
||||
if (!trainlog_json_i64(&w, (long long)s->progression.performance_dose))
|
||||
if (!trainlog_json_i64(&w, (long long)s->progression.performance_dose)) {
|
||||
goto overflow;
|
||||
}
|
||||
RAW("},\"points\":[");
|
||||
for (i = 0U; i < s->progression.performance_count; ++i) {
|
||||
const TrainlogExercisePerformancePoint *p = &s->progression.performance[i];
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
}
|
||||
RAW("{\"session_id\":");
|
||||
STR(p->session_id);
|
||||
RAW(",\"timestamp\":");
|
||||
STR(p->started_at);
|
||||
RAW(",\"metric_value\":");
|
||||
if (!trainlog_json_i64(&w, (long long)p->metric_value))
|
||||
if (!trainlog_json_i64(&w, (long long)p->metric_value)) {
|
||||
goto overflow;
|
||||
}
|
||||
RAW(",\"weight_kg\":");
|
||||
if (p->has_weight) {
|
||||
if (!trainlog_json_double(&w, p->weight_kg))
|
||||
if (!trainlog_json_double(&w, p->weight_kg)) {
|
||||
goto overflow;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
RAW("null");
|
||||
}
|
||||
RAW(",\"improved\":");
|
||||
RAW(point_improved(&s->progression, i) ? "true" : "false");
|
||||
RAW("}");
|
||||
|
|
@ -166,16 +181,19 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW(",\"started_at\":");
|
||||
STR(l->started_at);
|
||||
RAW(",\"ended_at\":");
|
||||
if (l->has_ended_at)
|
||||
if (l->has_ended_at) {
|
||||
STR(l->ended_at);
|
||||
else
|
||||
} else {
|
||||
RAW("null");
|
||||
}
|
||||
RAW(",\"duration_seconds\":");
|
||||
if (l->has_duration) {
|
||||
if (!trainlog_json_i64(&w, (long long)l->duration_seconds))
|
||||
if (!trainlog_json_i64(&w, (long long)l->duration_seconds)) {
|
||||
goto overflow;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
RAW("null");
|
||||
}
|
||||
RAW(",\"exercise_count\":");
|
||||
NUM(l->exercise_count);
|
||||
RAW(",\"set_count\":");
|
||||
|
|
@ -186,23 +204,27 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
NUM(l->max_count);
|
||||
RAW(",\"primary_zones\":[");
|
||||
for (i = 0U; i < l->zone_count; ++i) {
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
if (!write_zone(&w, &l->zones[i]))
|
||||
}
|
||||
if (!write_zone(&w, &l->zones[i])) {
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
RAW("]");
|
||||
}
|
||||
RAW("},");
|
||||
RAW("\"max_records\":{\"available\":");
|
||||
RAW(s->max_record_count > 0U ? "true" : "false");
|
||||
if (s->max_record_count == 0U)
|
||||
if (s->max_record_count == 0U) {
|
||||
RAW(",\"reason\":\"no_explicit_max_results\"");
|
||||
}
|
||||
RAW(",\"records\":[");
|
||||
for (i = 0U; i < s->max_record_count; ++i) {
|
||||
const TrainlogWebMaxRecord *r = &s->max_records[i];
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
}
|
||||
RAW("{\"session_id\":");
|
||||
STR(r->session_id);
|
||||
RAW(",\"entry_id\":");
|
||||
|
|
@ -214,8 +236,9 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW(",\"equipment_id\":");
|
||||
STR(r->equipment_id);
|
||||
RAW(",\"weight_kg\":");
|
||||
if (!trainlog_json_double(&w, r->weight_kg))
|
||||
if (!trainlog_json_double(&w, r->weight_kg)) {
|
||||
goto overflow;
|
||||
}
|
||||
RAW(",\"timestamp\":");
|
||||
STR(r->timestamp);
|
||||
RAW("}");
|
||||
|
|
@ -223,14 +246,17 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW("]},");
|
||||
RAW("\"muscle_distribution\":{\"available\":");
|
||||
RAW(s->muscle_zone_count > 0U ? "true" : "false");
|
||||
if (s->muscle_zone_count == 0U)
|
||||
if (s->muscle_zone_count == 0U) {
|
||||
RAW(",\"reason\":\"no_worked_primary_zones\"");
|
||||
}
|
||||
RAW(",\"window_days\":30,\"primary_zones\":[");
|
||||
for (i = 0U; i < s->muscle_zone_count; ++i) {
|
||||
if (i > 0U)
|
||||
if (i > 0U) {
|
||||
RAW(",");
|
||||
if (!write_zone(&w, &s->muscle_zones[i]))
|
||||
}
|
||||
if (!write_zone(&w, &s->muscle_zones[i])) {
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
RAW("]},");
|
||||
RAW("\"cardio\":{\"available\":false,\"reason\":");
|
||||
|
|
@ -242,12 +268,14 @@ TrainlogStatus trainlog_web_dashboard_serialize(const TrainlogWebDashboardSnapsh
|
|||
RAW(",\"generated_at\":");
|
||||
STR(s->generated_at);
|
||||
RAW("}}\n");
|
||||
if (w.failed)
|
||||
if (w.failed) {
|
||||
goto overflow;
|
||||
}
|
||||
*out_size = w.size;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
overflow:
|
||||
if (cap > 0U)
|
||||
if (cap > 0U) {
|
||||
out[0] = '\0';
|
||||
}
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@ static uint32_t scan(const char *path, uint32_t parent) {
|
|||
const char *slash = strrchr(path, '/');
|
||||
snprintf(n->name, sizeof(n->name), "%s", slash ? slash + 1 : path);
|
||||
if (n->folder && (d = opendir(path)) != NULL) {
|
||||
while ((e = readdir(d)))
|
||||
while ((e = readdir(d))) {
|
||||
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
||||
char child[1024];
|
||||
snprintf(child, sizeof(child), "%s/%s", path, e->d_name);
|
||||
scan(child, self);
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
return self;
|
||||
|
|
@ -46,14 +47,17 @@ static void rebuild(void) {
|
|||
}
|
||||
static Node *node(uint32_t id) {
|
||||
size_t i;
|
||||
for (i = 0; i < node_count; i++)
|
||||
if (nodes[i].id == id)
|
||||
for (i = 0; i < node_count; i++) {
|
||||
if (nodes[i].id == id) {
|
||||
return &nodes[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static TrainlogStatus devices(TrainlogUsbDevice *o, size_t c, size_t *n) {
|
||||
if (c < 1U)
|
||||
if (c < 1U) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
memset(o, 0, sizeof(*o));
|
||||
o->bus_number = 1;
|
||||
o->device_number = 1;
|
||||
|
|
@ -64,8 +68,9 @@ static TrainlogStatus
|
|||
storages(unsigned int b, unsigned int d, TrainlogMtpStorage *o, size_t c, size_t *n) {
|
||||
(void)b;
|
||||
(void)d;
|
||||
if (c < 1U)
|
||||
if (c < 1U) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
memset(o, 0, sizeof(*o));
|
||||
o->storage_id = 1;
|
||||
*n = 1;
|
||||
|
|
@ -83,12 +88,14 @@ static TrainlogStatus children(unsigned int b,
|
|||
(void)d;
|
||||
(void)s;
|
||||
rebuild();
|
||||
if (p == UINT32_MAX)
|
||||
if (p == UINT32_MAX) {
|
||||
p = nodes[0].id;
|
||||
for (i = 0; i < node_count; i++)
|
||||
}
|
||||
for (i = 0; i < node_count; i++) {
|
||||
if (nodes[i].parent == p) {
|
||||
if (k >= c)
|
||||
if (k >= c) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
memset(&o[k], 0, sizeof(o[k]));
|
||||
o[k].item_id = nodes[i].id;
|
||||
o[k].parent_id = p;
|
||||
|
|
@ -98,6 +105,7 @@ static TrainlogStatus children(unsigned int b,
|
|||
snprintf(o[k].name, sizeof(o[k].name), "%s", nodes[i].name);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
*n = k;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
|
|
@ -115,33 +123,39 @@ static TrainlogStatus ensure(unsigned int b,
|
|||
(void)s;
|
||||
rebuild();
|
||||
parent = node(p);
|
||||
if (!parent)
|
||||
if (!parent) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
snprintf(path, sizeof(path), "%s/%s", parent->path, name);
|
||||
if (mkdir(path, 0700) == 0)
|
||||
if (mkdir(path, 0700) == 0) {
|
||||
*created = true;
|
||||
else if (access(path, F_OK) == 0)
|
||||
} else if (access(path, F_OK) == 0) {
|
||||
*created = false;
|
||||
else
|
||||
} else {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
rebuild();
|
||||
size_t i;
|
||||
for (i = 0; i < node_count; i++)
|
||||
for (i = 0; i < node_count; i++) {
|
||||
if (strcmp(nodes[i].path, path) == 0) {
|
||||
*id = nodes[i].id;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
}
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
static int copy(const char *a, const char *b) {
|
||||
FILE *in = fopen(a, "rb"), *out = fopen(b, "wb");
|
||||
char buf[8192];
|
||||
size_t n;
|
||||
if (!in || !out)
|
||||
if (!in || !out) {
|
||||
return -1;
|
||||
while ((n = fread(buf, 1, sizeof(buf), in)) > 0)
|
||||
if (fwrite(buf, 1, n, out) != n)
|
||||
}
|
||||
while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
|
||||
if (fwrite(buf, 1, n, out) != n) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return fclose(in) | fclose(out);
|
||||
}
|
||||
static TrainlogStatus receive(unsigned int b, unsigned int d, uint32_t id, const char *path) {
|
||||
|
|
@ -166,18 +180,21 @@ static TrainlogStatus send_file(unsigned int b,
|
|||
(void)s;
|
||||
rebuild();
|
||||
parent = node(p);
|
||||
if (!parent)
|
||||
if (!parent) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
snprintf(path, sizeof(path), "%s/%s", parent->path, name);
|
||||
if (copy(local, path) != 0)
|
||||
if (copy(local, path) != 0) {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
rebuild();
|
||||
size_t i;
|
||||
for (i = 0; i < node_count; i++)
|
||||
for (i = 0; i < node_count; i++) {
|
||||
if (strcmp(nodes[i].path, path) == 0) {
|
||||
*id = nodes[i].id;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
}
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
static TrainlogStatus remove_object(unsigned int b, unsigned int d, uint32_t id) {
|
||||
|
|
@ -195,29 +212,41 @@ static TrainlogStatus rename_object(unsigned int b, unsigned int d, uint32_t id,
|
|||
(void)d;
|
||||
rebuild();
|
||||
n = node(id);
|
||||
if (!n)
|
||||
if (!n) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
snprintf(path, sizeof(path), "%s", n->path);
|
||||
slash = strrchr(path, '/');
|
||||
if (!slash)
|
||||
if (!slash) {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
snprintf(slash + 1, (size_t)(path + sizeof(path) - (slash + 1)), "%s", name);
|
||||
return rename(n->path, path) == 0 ? TRAINLOG_STATUS_OK : TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
static const TrainlogGenerationMtpIo io = {
|
||||
devices, storages, children, ensure, receive, send_file, remove_object, rename_object};
|
||||
.devices = devices,
|
||||
.storages = storages,
|
||||
.children = children,
|
||||
.ensure_folder = ensure,
|
||||
.receive = receive,
|
||||
.send = send_file,
|
||||
.remove = remove_object,
|
||||
.rename = rename_object,
|
||||
};
|
||||
int main(int argc, char **argv) {
|
||||
char diag[1024] = {0};
|
||||
TrainlogStatus status;
|
||||
remote = getenv("TRAINLOG_MTP_DOUBLE_ROOT");
|
||||
if (argc != 4 || !remote)
|
||||
if (argc != 4 || !remote) {
|
||||
return 64;
|
||||
if (strcmp(argv[1], "pull") == 0)
|
||||
}
|
||||
if (strcmp(argv[1], "pull") == 0) {
|
||||
status = trainlog_generation_mtp_pull(&io, argv[2], argv[3], diag, sizeof(diag));
|
||||
else if (strcmp(argv[1], "push") == 0)
|
||||
} else if (strcmp(argv[1], "push") == 0) {
|
||||
status = trainlog_generation_mtp_push(&io, argv[2], argv[3], diag, sizeof(diag));
|
||||
else
|
||||
} else {
|
||||
return 64;
|
||||
}
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
fprintf(stderr, "double failed %d %s\n", status, diag);
|
||||
return 2;
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ static void reset(void) {
|
|||
}
|
||||
static TrainlogStatus devices(TrainlogUsbDevice *out, size_t cap, size_t *count) {
|
||||
size_t i;
|
||||
if (cap < device_count)
|
||||
if (cap < device_count) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
*count = device_count;
|
||||
for (i = 0; i < device_count; i++) {
|
||||
memset(&out[i], 0, sizeof(out[i]));
|
||||
|
|
@ -63,8 +64,9 @@ static TrainlogStatus devices(TrainlogUsbDevice *out, size_t cap, size_t *count)
|
|||
static TrainlogStatus
|
||||
storages(unsigned int bus, unsigned int dev, TrainlogMtpStorage *out, size_t cap, size_t *count) {
|
||||
(void)bus;
|
||||
if (cap < 1U)
|
||||
if (cap < 1U) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->storage_id = dev;
|
||||
*count = 1U;
|
||||
|
|
@ -80,10 +82,11 @@ static TrainlogStatus children(unsigned int bus,
|
|||
size_t i, n = 0;
|
||||
(void)bus;
|
||||
(void)storage;
|
||||
for (i = 0; i < object_count; i++)
|
||||
for (i = 0; i < object_count; i++) {
|
||||
if (objects[i].parent == parent && (dev == 1U || objects[i].id <= 3U)) {
|
||||
if (n >= cap)
|
||||
if (n >= cap) {
|
||||
return TRAINLOG_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
memset(&out[n], 0, sizeof(out[n]));
|
||||
out[n].item_id = objects[i].id;
|
||||
out[n].parent_id = parent;
|
||||
|
|
@ -93,6 +96,7 @@ static TrainlogStatus children(unsigned int bus,
|
|||
snprintf(out[n].name, sizeof(out[n].name), "%s", objects[i].name);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
*count = n;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
|
|
@ -107,12 +111,13 @@ static TrainlogStatus ensure_folder(unsigned int b,
|
|||
(void)b;
|
||||
(void)d;
|
||||
(void)s;
|
||||
for (i = 0; i < object_count; i++)
|
||||
for (i = 0; i < object_count; i++) {
|
||||
if (objects[i].parent == p && objects[i].folder && strcmp(objects[i].name, name) == 0) {
|
||||
*id = objects[i].id;
|
||||
*created = false;
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
}
|
||||
*id = (uint32_t)(100U + object_count);
|
||||
*created = true;
|
||||
add(*id, p, true, name, NULL);
|
||||
|
|
@ -120,9 +125,11 @@ static TrainlogStatus ensure_folder(unsigned int b,
|
|||
}
|
||||
static Object *find(uint32_t id) {
|
||||
size_t i;
|
||||
for (i = 0; i < object_count; i++)
|
||||
if (objects[i].id == id)
|
||||
for (i = 0; i < object_count; i++) {
|
||||
if (objects[i].id == id) {
|
||||
return &objects[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static TrainlogStatus receive(unsigned int b, unsigned int d, uint32_t id, const char *path) {
|
||||
|
|
@ -131,11 +138,13 @@ static TrainlogStatus receive(unsigned int b, unsigned int d, uint32_t id, const
|
|||
size_t bytes;
|
||||
(void)b;
|
||||
(void)d;
|
||||
if (!o)
|
||||
if (!o) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
f = fopen(path, "wb");
|
||||
if (!f)
|
||||
if (!f) {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
bytes = id == 9U ? 3U : o->size;
|
||||
if (fwrite(o->data, 1, bytes, f) != bytes) {
|
||||
fclose(f);
|
||||
|
|
@ -157,8 +166,9 @@ static TrainlogStatus send_file(unsigned int b,
|
|||
(void)d;
|
||||
(void)s;
|
||||
f = fopen(path, "rb");
|
||||
if (!f)
|
||||
if (!f) {
|
||||
return TRAINLOG_STATUS_SYSTEM_ERROR;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
rewind(f);
|
||||
|
|
@ -183,8 +193,9 @@ static TrainlogStatus remove_object(unsigned int b, unsigned int d, uint32_t id)
|
|||
Object *o = find(id);
|
||||
(void)b;
|
||||
(void)d;
|
||||
if (!o)
|
||||
if (!o) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
o->name[0] = '\0';
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
|
|
@ -192,13 +203,22 @@ static TrainlogStatus rename_object(unsigned int b, unsigned int d, uint32_t id,
|
|||
Object *o = find(id);
|
||||
(void)b;
|
||||
(void)d;
|
||||
if (!o)
|
||||
if (!o) {
|
||||
return TRAINLOG_STATUS_NOT_FOUND;
|
||||
}
|
||||
snprintf(o->name, sizeof(o->name), "%s", name);
|
||||
return TRAINLOG_STATUS_OK;
|
||||
}
|
||||
static const TrainlogGenerationMtpIo io = {
|
||||
devices, storages, children, ensure_folder, receive, send_file, remove_object, rename_object};
|
||||
.devices = devices,
|
||||
.storages = storages,
|
||||
.children = children,
|
||||
.ensure_folder = ensure_folder,
|
||||
.receive = receive,
|
||||
.send = send_file,
|
||||
.remove = remove_object,
|
||||
.rename = rename_object,
|
||||
};
|
||||
static int write_file(const char *path, const char *value) {
|
||||
FILE *f = fopen(path, "wb");
|
||||
CHECK(f != NULL);
|
||||
|
|
|
|||
|
|
@ -39,8 +39,9 @@ static int reserve_port(uint16_t *port, int keep_open) {
|
|||
struct sockaddr_in address;
|
||||
socklen_t length = (socklen_t)sizeof(address);
|
||||
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0)
|
||||
if (socket_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
(void)memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
|
@ -70,10 +71,12 @@ static int connect_with_retry(uint16_t port) {
|
|||
for (attempt = 0U; attempt < 200U; ++attempt) {
|
||||
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd >= 0 &&
|
||||
connect(socket_fd, (const struct sockaddr *)&address, sizeof(address)) == 0)
|
||||
connect(socket_fd, (const struct sockaddr *)&address, sizeof(address)) == 0) {
|
||||
return socket_fd;
|
||||
if (socket_fd >= 0)
|
||||
}
|
||||
if (socket_fd >= 0) {
|
||||
(void)close(socket_fd);
|
||||
}
|
||||
(void)nanosleep(&delay, NULL);
|
||||
}
|
||||
return -1;
|
||||
|
|
@ -93,8 +96,9 @@ static bool exchange(uint16_t port, const char *request, char *response, size_t
|
|||
(void)shutdown(socket_fd, SHUT_WR);
|
||||
while (used + 1U < capacity) {
|
||||
ssize_t count = recv(socket_fd, response + used, capacity - used - 1U, 0);
|
||||
if (count == 0)
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
CHECK(count > 0);
|
||||
used += (size_t)count;
|
||||
}
|
||||
|
|
@ -109,8 +113,9 @@ static bool asset_path(const char *html, const char *suffix, char *output, size_
|
|||
while ((start = strstr(start, "/assets/")) != NULL) {
|
||||
const char *end = start;
|
||||
size_t length;
|
||||
while (*end != '\0' && *end != '"' && *end != '\'' && *end != '<' && *end != '>')
|
||||
while (*end != '\0' && *end != '"' && *end != '\'' && *end != '<' && *end != '>') {
|
||||
++end;
|
||||
}
|
||||
length = (size_t)(end - start);
|
||||
if (length >= suffix_length && memcmp(end - suffix_length, suffix, suffix_length) == 0 &&
|
||||
length < capacity) {
|
||||
|
|
@ -127,15 +132,18 @@ static bool response_header(const char *response, const char *name, char *output
|
|||
const char *start = strstr(response, name);
|
||||
const char *end;
|
||||
size_t length;
|
||||
if (start == NULL)
|
||||
if (start == NULL) {
|
||||
return false;
|
||||
}
|
||||
start += strlen(name);
|
||||
end = strstr(start, "\r\n");
|
||||
if (end == NULL)
|
||||
if (end == NULL) {
|
||||
return false;
|
||||
}
|
||||
length = (size_t)(end - start);
|
||||
if (length >= capacity)
|
||||
if (length >= capacity) {
|
||||
return false;
|
||||
}
|
||||
(void)memcpy(output, start, length);
|
||||
output[length] = '\0';
|
||||
return true;
|
||||
|
|
@ -451,10 +459,12 @@ int main(void) {
|
|||
char config_root[] = "/tmp/trainlog-web-config-XXXXXX";
|
||||
TrainlogDatabase *database = NULL;
|
||||
bool passed;
|
||||
if (mkdtemp(config_root) == NULL || setenv("XDG_CONFIG_HOME", config_root, 1) != 0)
|
||||
if (mkdtemp(config_root) == NULL || setenv("XDG_CONFIG_HOME", config_root, 1) != 0) {
|
||||
return 1;
|
||||
if (trainlog_database_open(":memory:", &database) != TRAINLOG_STATUS_OK)
|
||||
}
|
||||
if (trainlog_database_open(":memory:", &database) != TRAINLOG_STATUS_OK) {
|
||||
return 1;
|
||||
}
|
||||
if (!trainlog_web_assets_available()) {
|
||||
volatile sig_atomic_t stop = 0;
|
||||
char diagnostic[256];
|
||||
|
|
|
|||
|
|
@ -9,22 +9,24 @@ int main(int argc, char **argv) {
|
|||
(void)puts("usage: trainlog-generation-mtp-adapter pull|push EXPECTED_PEER LOCAL_ROOT");
|
||||
return 0;
|
||||
}
|
||||
if (argc != 4)
|
||||
if (argc != 4) {
|
||||
return 64;
|
||||
if (strcmp(argv[1], "pull") == 0)
|
||||
}
|
||||
if (strcmp(argv[1], "pull") == 0) {
|
||||
status = trainlog_generation_mtp_pull(trainlog_generation_mtp_production_io(),
|
||||
argv[2],
|
||||
argv[3],
|
||||
diagnostic,
|
||||
sizeof(diagnostic));
|
||||
else if (strcmp(argv[1], "push") == 0)
|
||||
} else if (strcmp(argv[1], "push") == 0) {
|
||||
status = trainlog_generation_mtp_push(trainlog_generation_mtp_production_io(),
|
||||
argv[2],
|
||||
argv[3],
|
||||
diagnostic,
|
||||
sizeof(diagnostic));
|
||||
else
|
||||
} else {
|
||||
return 64;
|
||||
}
|
||||
if (status != TRAINLOG_STATUS_OK) {
|
||||
(void)fprintf(
|
||||
stderr, "MTP adapter failed status=%d diagnostic=%s\n", (int)status, diagnostic);
|
||||
|
|
|
|||
Loading…
Reference in a new issue