refactor(c): clarify control flow and MTP callbacks

This commit is contained in:
fy59 2026-09-17 14:46:23 +02:00
parent 7b769076c1
commit 7d29dc83a2
9 changed files with 484 additions and 246 deletions

View file

@ -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(&current, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK)
}
if (trainlog_dashboard_layout_load(&current, &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(&current, &source) != TRAINLOG_DASHBOARD_LAYOUT_OK)
if (trainlog_dashboard_layout_load(&current, &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;
}

View file

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

View file

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

View file

@ -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, &current) == NULL)
}
if (localtime_r(&reference, &current) == 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;
}

View file

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

View file

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

View file

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

View file

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

View file

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