diff --git a/core/libgamestream/src/client.c b/core/libgamestream/src/client.c index 8060b982c..4fdd3519d 100644 --- a/core/libgamestream/src/client.c +++ b/core/libgamestream/src/client.c @@ -866,7 +866,7 @@ static int load_server_status(GS_CLIENT hnd, PSERVER_DATA server) { } i++; - } while (ret != GS_OK && i < 2); + } while (ret == GS_ERROR && i < 2); if (ret == GS_OK && !server->unsupported) { if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) { diff --git a/src/app/backend/pcmanager.h b/src/app/backend/pcmanager.h index 7dc2a3347..f6af837d9 100644 --- a/src/app/backend/pcmanager.h +++ b/src/app/backend/pcmanager.h @@ -133,4 +133,6 @@ bool pcmanager_send_wol(pcmanager_t *manager, const uuidstr_t *uuid, pcmanager_c * @param userdata * @return */ -int pcmanager_update_by_ip(worker_context_t *context, const char *ip, uint16_t port, bool force); \ No newline at end of file +int pcmanager_update_by_ip(worker_context_t *context, const char *ip, uint16_t port, bool force); + +int pcmanager_update_by_addr(worker_context_t *context, sockaddr_t *addr, bool force); \ No newline at end of file diff --git a/src/app/backend/pcmanager/CMakeLists.txt b/src/app/backend/pcmanager/CMakeLists.txt index 9fbfedaa5..c633b46e9 100644 --- a/src/app/backend/pcmanager/CMakeLists.txt +++ b/src/app/backend/pcmanager/CMakeLists.txt @@ -1 +1,3 @@ +target_sources(moonlight-lib PRIVATE discovery_callback.c) + add_subdirectory(discovery) \ No newline at end of file diff --git a/src/app/backend/pcmanager/discovery/CMakeLists.txt b/src/app/backend/pcmanager/discovery/CMakeLists.txt index fb286be64..954775387 100644 --- a/src/app/backend/pcmanager/discovery/CMakeLists.txt +++ b/src/app/backend/pcmanager/discovery/CMakeLists.txt @@ -1,17 +1,2 @@ -include(CheckSymbolExists) - -check_symbol_exists(DNSServiceCreateConnection "dns_sd.h" HAVE_DNSSD) - -if (HAVE_DNSSD) - target_sources(moonlight-lib PRIVATE discovery_dnssd.c) -elseif (OS_WINDOWS) - pkg_check_modules(MICRODNS REQUIRED microdns) - target_link_libraries(moonlight-lib PUBLIC ${MICRODNS_LIBRARIES}) - target_sources(moonlight-lib PRIVATE discovery_libmicrodns.c) -else () - set(BUILD_SHARED_LIBS ON) - include(BuildMicrodns) - target_link_libraries(moonlight-lib PUBLIC microdns) - unset(BUILD_SHARED_LIBS) - target_sources(moonlight-lib PRIVATE discovery_libmicrodns.c) -endif () \ No newline at end of file +target_sources(moonlight-lib PRIVATE discovery.c throttle.c) +add_subdirectory(impl) \ No newline at end of file diff --git a/src/app/backend/pcmanager/discovery/discovery.c b/src/app/backend/pcmanager/discovery/discovery.c new file mode 100644 index 000000000..d2c2672dd --- /dev/null +++ b/src/app/backend/pcmanager/discovery/discovery.c @@ -0,0 +1,62 @@ +#include "discovery.h" +#include "throttle.h" + +#include "impl/impl.h" + +#include "util/bus.h" +#include "logging.h" + +static int discovery_worker_wrapper(void *arg); + +void discovery_init(discovery_t *discovery, discovery_callback callback, void *user_data) { + discovery->lock = SDL_CreateMutex(); + discovery->task = NULL; + discovery_throttle_init(&discovery->throttle, callback, user_data); +} + +void discovery_deinit(discovery_t *discovery) { + discovery_throttle_deinit(&discovery->throttle); + discovery_stop(discovery); + SDL_DestroyMutex(discovery->lock); +} + +void discovery_start(discovery_t *discovery) { + SDL_LockMutex(discovery->lock); + if (discovery->task != NULL) { + SDL_UnlockMutex(discovery->lock); + return; + } + discovery_task_t *task = SDL_calloc(1, sizeof(discovery_task_t)); + commons_log_info("Discovery", "Start task %p", task); + task->discovery = discovery; + task->lock = SDL_CreateMutex(); + task->stop = false; + SDL_Thread *thread = SDL_CreateThread(discovery_worker_wrapper, "discovery", task); + SDL_DetachThread(thread); + discovery->task = task; + SDL_UnlockMutex(discovery->lock); +} + +void discovery_stop(discovery_t *discovery) { + SDL_LockMutex(discovery->lock); + discovery_task_t *task = discovery->task; + if (task == NULL) { + SDL_UnlockMutex(discovery->lock); + return; + } + discovery->task = NULL; + discovery_worker_stop(task); + SDL_UnlockMutex(discovery->lock); +} + +void discovery_discovered(struct discovery_t *discovery, const sockaddr_t *addr) { + discovery_throttle_on_discovered(&discovery->throttle, addr, 10000); +} + +int discovery_worker_wrapper(void *arg) { + discovery_task_t *task = (discovery_task_t *) arg; + int result = discovery_worker(task); + SDL_DestroyMutex(task->lock); + free(arg); + return result; +} \ No newline at end of file diff --git a/src/app/backend/pcmanager/discovery/discovery.h b/src/app/backend/pcmanager/discovery/discovery.h new file mode 100644 index 000000000..6b5905fdb --- /dev/null +++ b/src/app/backend/pcmanager/discovery/discovery.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include +#include +#include "sockaddr.h" + +typedef void (*discovery_callback)(const sockaddr_t *addr, void *user_data); + +typedef struct discovery_throttle_host_t discovery_throttle_host_t; + +typedef struct discovery_throttle_t { + discovery_callback callback; + void *user_data; + discovery_throttle_host_t *hosts; + SDL_mutex *lock; +} discovery_throttle_t; + +typedef struct discovery_t { + SDL_mutex *lock; + struct discovery_task_t *task; + discovery_throttle_t throttle; +} discovery_t; + +void discovery_init(discovery_t *discovery, discovery_callback callback, void *user_data); + +void discovery_start(discovery_t *discovery); + +void discovery_stop(discovery_t *discovery); + +void discovery_deinit(discovery_t *discovery); diff --git a/src/app/backend/pcmanager/discovery/impl/CMakeLists.txt b/src/app/backend/pcmanager/discovery/impl/CMakeLists.txt new file mode 100644 index 000000000..4e72134bf --- /dev/null +++ b/src/app/backend/pcmanager/discovery/impl/CMakeLists.txt @@ -0,0 +1,17 @@ +include(CheckSymbolExists) + +check_symbol_exists(DNSServiceCreateConnection "dns_sd.h" HAVE_DNSSD) + +if (HAVE_DNSSD) + target_sources(moonlight-lib PRIVATE dnssd.c) +else () + target_sources(moonlight-lib PRIVATE microdns.c) + if (OS_WINDOWS) + pkg_check_modules(MICRODNS REQUIRED microdns) + target_link_libraries(moonlight-lib PUBLIC ${MICRODNS_LIBRARIES}) + endif () + set(BUILD_SHARED_LIBS ON) + include(BuildMicrodns) + target_link_libraries(moonlight-lib PUBLIC microdns) + unset(BUILD_SHARED_LIBS) +endif () diff --git a/src/app/backend/pcmanager/discovery/discovery_dnssd.c b/src/app/backend/pcmanager/discovery/impl/dnssd.c similarity index 100% rename from src/app/backend/pcmanager/discovery/discovery_dnssd.c rename to src/app/backend/pcmanager/discovery/impl/dnssd.c diff --git a/src/app/backend/pcmanager/discovery/impl/impl.h b/src/app/backend/pcmanager/discovery/impl/impl.h new file mode 100644 index 000000000..369209371 --- /dev/null +++ b/src/app/backend/pcmanager/discovery/impl/impl.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include +#include + +#include "sockaddr.h" + +typedef struct discovery_task_t { + struct discovery_t *discovery; + SDL_mutex *lock; + bool stop; +} discovery_task_t; + +int discovery_worker(discovery_task_t *task); + +void discovery_worker_stop(discovery_task_t *task); + +void discovery_discovered(struct discovery_t *discovery, const sockaddr_t *addr); \ No newline at end of file diff --git a/src/app/backend/pcmanager/discovery/discovery_libmicrodns.c b/src/app/backend/pcmanager/discovery/impl/microdns.c similarity index 52% rename from src/app/backend/pcmanager/discovery/discovery_libmicrodns.c rename to src/app/backend/pcmanager/discovery/impl/microdns.c index 591513657..ae6fe0175 100644 --- a/src/app/backend/pcmanager/discovery/discovery_libmicrodns.c +++ b/src/app/backend/pcmanager/discovery/impl/microdns.c @@ -1,54 +1,31 @@ -#include "backend/pcmanager/priv.h" -#include "logging.h" +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "impl.h" + #include -#include "util/bus.h" -#include "backend/pcmanager/worker/worker.h" #include "sockaddr.h" - -struct discovery_task_t { - pcmanager_t *manager; - SDL_mutex *lock; - SDL_Thread *thread; - bool stop; -}; - -static int discovery_worker(discovery_task_t *task); - -static bool discovery_stopped(discovery_task_t *task); +#include "logging.h" static void discovery_callback(discovery_task_t *task, int status, const struct rr_entry *entries); -static void discovery_finalize(void *arg, int result); +static bool discovery_is_stopped(discovery_task_t *task); -void pcmanager_auto_discovery_start(pcmanager_t *manager) { - pcmanager_lock(manager); - if (manager->discovery_task != NULL) { - pcmanager_unlock(manager); - return; - } - discovery_task_t *task = SDL_calloc(1, sizeof(discovery_task_t)); - commons_log_info("Discovery", "Start task %p", task); - task->manager = manager; - task->lock = SDL_CreateMutex(); - task->stop = false; - task->thread = SDL_CreateThread((SDL_ThreadFunction) discovery_worker, "discovery", task); - manager->discovery_task = task; - pcmanager_unlock(manager); -} - -void pcmanager_auto_discovery_stop(pcmanager_t *manager) { - pcmanager_lock(manager); - discovery_task_t *task = manager->discovery_task; - if (task == NULL) { - pcmanager_unlock(manager); - return; - } - manager->discovery_task = NULL; - executor_submit(manager->executor, executor_noop, discovery_finalize, task); - pcmanager_unlock(manager); -} - -static int discovery_worker(discovery_task_t *task) { +int discovery_worker(discovery_task_t *task) { int r; char err[128]; static const char *const service_name[] = {"_nvstream._tcp.local"}; @@ -58,7 +35,7 @@ static int discovery_worker(discovery_task_t *task) { goto err; } commons_log_info("Discovery", "Start mDNS discovery"); - if ((r = mdns_listen(ctx, service_name, 1, RR_PTR, 10, (mdns_stop_func) discovery_stopped, + if ((r = mdns_listen(ctx, service_name, 1, RR_PTR, 10, (mdns_stop_func) discovery_is_stopped, (mdns_listen_callback) discovery_callback, task)) < 0) { goto err; } @@ -74,14 +51,13 @@ static int discovery_worker(discovery_task_t *task) { return r; } -static bool discovery_stopped(discovery_task_t *task) { +void discovery_worker_stop(discovery_task_t *task) { SDL_LockMutex(task->lock); - bool stop = task->stop; + task->stop = true; SDL_UnlockMutex(task->lock); - return stop; } -static void discovery_callback(discovery_task_t *task, int status, const struct rr_entry *entries) { +void discovery_callback(discovery_task_t *task, int status, const struct rr_entry *entries) { char err[128]; if (status < 0) { @@ -114,19 +90,12 @@ static void discovery_callback(discovery_task_t *task, int status, const struct if (addr->sa_family == AF_UNSPEC) { return; } - worker_context_t *ctx = worker_context_new(task->manager, NULL, NULL, NULL); - ctx->arg1 = addr; - pcmanager_worker_queue(task->manager, worker_host_discovered, ctx); + discovery_discovered(task->discovery, addr); } -static void discovery_finalize(void *arg, int result) { - (void) result; - commons_log_info("Discovery", "Finalize task %p", arg); - discovery_task_t *task = arg; +bool discovery_is_stopped(discovery_task_t *task) { SDL_LockMutex(task->lock); - task->stop = true; + bool stop = task->stop; SDL_UnlockMutex(task->lock); - SDL_WaitThread(task->thread, NULL); - SDL_DestroyMutex(task->lock); - free(task); -} + return stop; +} \ No newline at end of file diff --git a/src/app/backend/pcmanager/discovery/throttle.c b/src/app/backend/pcmanager/discovery/throttle.c new file mode 100644 index 000000000..93563641e --- /dev/null +++ b/src/app/backend/pcmanager/discovery/throttle.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "throttle.h" +#include +#include + +struct discovery_throttle_host_t { + sockaddr_t *addr; + Uint32 ttl, last_discovered; + discovery_throttle_host_t *next; + discovery_throttle_host_t *prev; +}; + +#define LINKEDLIST_IMPL +#define LINKEDLIST_MODIFIER static +#define LINKEDLIST_TYPE discovery_throttle_host_t +#define LINKEDLIST_PREFIX throttle_hosts +#define LINKEDLIST_DOUBLE 1 + +#include "linked_list.h" + +#undef LINKEDLIST_DOUBLE +#undef LINKEDLIST_TYPE +#undef LINKEDLIST_PREFIX + +static int throttle_hosts_compare_time(discovery_throttle_host_t *a, discovery_throttle_host_t *b); + +static int throttle_hosts_find_addr(discovery_throttle_host_t *node, const void *addr); + +static int throttle_hosts_find_not_expired(discovery_throttle_host_t *node, const void *now); + +static void throttle_hosts_evict(discovery_throttle_host_t **head); + +void discovery_throttle_init(discovery_throttle_t *throttle, discovery_callback callback, void *user_data) { + throttle->callback = callback; + throttle->user_data = user_data; + throttle->hosts = NULL; + throttle->lock = SDL_CreateMutex(); +} + +void discovery_throttle_deinit(discovery_throttle_t *throttle) { + SDL_LockMutex(throttle->lock); + throttle_hosts_free(throttle->hosts, NULL); + SDL_UnlockMutex(throttle->lock); + SDL_DestroyMutex(throttle->lock); +} + +void discovery_throttle_on_discovered(discovery_throttle_t *throttle, const sockaddr_t *addr, Uint32 ttl) { + // Remove all expired hosts + SDL_LockMutex(throttle->lock); + throttle_hosts_evict(&throttle->hosts); + + // Find existing host + discovery_throttle_host_t *find = throttle_hosts_find_by(throttle->hosts, addr, throttle_hosts_find_addr); + + if (find != NULL) { + // Ignore existing host + SDL_UnlockMutex(throttle->lock); + return; + } + + discovery_throttle_host_t *node = throttle_hosts_new(); + node->addr = sockaddr_clone(addr); + node->ttl = ttl; + node->last_discovered = SDL_GetTicks(); + throttle->hosts = throttle_hosts_sortedinsert(throttle->hosts, node, throttle_hosts_compare_time); + + if (throttle->callback != NULL) { + throttle->callback(addr, throttle->user_data); + } + SDL_UnlockMutex(throttle->lock); +} + +int throttle_hosts_compare_time(discovery_throttle_host_t *a, discovery_throttle_host_t *b) { + return (int) a->last_discovered - (int) b->last_discovered; +} + +static int throttle_hosts_find_addr(discovery_throttle_host_t *node, const void *addr) { + return sockaddr_compare(node->addr, addr); +} + +void throttle_hosts_evict(discovery_throttle_host_t **head) { + Uint32 now = SDL_GetTicks(); + discovery_throttle_host_t *find = throttle_hosts_find_by(*head, &now, throttle_hosts_find_not_expired); + if (find != NULL) { + if (find->prev != NULL) { + find->prev->next = NULL; + } + find->prev = NULL; + return; + } + throttle_hosts_free(*head, NULL); + *head = find; +} + +int throttle_hosts_find_not_expired(discovery_throttle_host_t *node, const void *now) { + return SDL_TICKS_PASSED(*(Uint32 *) now, node->last_discovered + node->ttl); +} diff --git a/src/app/backend/pcmanager/discovery/throttle.h b/src/app/backend/pcmanager/discovery/throttle.h new file mode 100644 index 000000000..bdbf1fcb8 --- /dev/null +++ b/src/app/backend/pcmanager/discovery/throttle.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include "discovery.h" + +void discovery_throttle_init(discovery_throttle_t *throttle, discovery_callback callback, void *user_data); + +void discovery_throttle_deinit(discovery_throttle_t *throttle); + +void discovery_throttle_on_discovered(discovery_throttle_t *throttle, const sockaddr_t *addr, Uint32 ttl); diff --git a/src/app/backend/pcmanager/discovery_callback.c b/src/app/backend/pcmanager/discovery_callback.c new file mode 100644 index 000000000..142d57878 --- /dev/null +++ b/src/app/backend/pcmanager/discovery_callback.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Mariotaku . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#include "priv.h" +#include "pclist.h" +#include "app.h" +#include "errors.h" + +#include "logging.h" + +static void lan_host_status_update(pcmanager_t *manager, SERVER_DATA *server); + +static void lan_host_offline(pcmanager_t *manager, const sockaddr_t *addr); + +void pcmanager_lan_host_discovered(const sockaddr_t *addr, pcmanager_t *manager) { + GS_CLIENT client = app_gs_client_new(manager->app); + SERVER_DATA *server = serverdata_new(); + char ip[64]; + sockaddr_get_ip_str(addr, ip, sizeof(ip)); + int ret = gs_get_status(client, server, strndup(ip, sizeof(ip)), sockaddr_get_port(addr), + app_configuration->unsupported); + if (ret == GS_OK) { + commons_log_info("PCManager", "Finished updating status from %s", ip); + lan_host_status_update(manager, server); + } else { + serverdata_free(server); + const char *gs_error = NULL; + ret = gs_get_error(&gs_error); + if (ret == GS_IO_ERROR) { + commons_log_warn("PCManager", "Error while updating status from %s. Host seems to be offline", ip); + lan_host_offline(manager, addr); + } else { + commons_log_warn("PCManager", "Error while updating status from %s: %d (%s)", ip, ret, gs_error); + } + } + gs_destroy(client); +} + + +void lan_host_status_update(pcmanager_t *manager, SERVER_DATA *server) { + SERVER_STATE state = {.code = server->paired ? SERVER_STATE_AVAILABLE : SERVER_STATE_NOT_PAIRED}; + pclist_upsert(manager, (const uuidstr_t *) server->uuid, &state, server); +} + +void lan_host_offline(pcmanager_t *manager, const sockaddr_t *addr) { + pclist_t *existing = pclist_find_by_addr(manager, addr); + if (!existing) { + return; + } + pcmanager_lock(manager); + existing->state.code = SERVER_STATE_OFFLINE; + pcmanager_unlock(manager); +} \ No newline at end of file diff --git a/src/app/backend/pcmanager/pclist.c b/src/app/backend/pcmanager/pclist.c index 4ccd110bf..5e2da9435 100644 --- a/src/app/backend/pcmanager/pclist.c +++ b/src/app/backend/pcmanager/pclist.c @@ -37,6 +37,8 @@ static void remove_perform(pclist_update_context_t *context); static void pclist_ll_nodefree(pclist_t *node); +static int pclist_ll_compare_ip(pclist_t *other, const void *v); + static int pclist_ll_compare_address(pclist_t *other, const void *v); static int pclist_ll_compare_uuid(pclist_t *other, const void *v); @@ -160,7 +162,15 @@ void pclist_ll_nodefree(pclist_t *node) { pclist_t *pclist_find_by_ip(pcmanager_t *manager, const char *ip) { SDL_assert_release(ip != NULL); pcmanager_lock(manager); - pclist_t *result = pclist_ll_find_by(manager->servers, ip, pclist_ll_compare_address); + pclist_t *result = pclist_ll_find_by(manager->servers, ip, pclist_ll_compare_ip); + pcmanager_unlock(manager); + return result; +} + +pclist_t *pclist_find_by_addr(pcmanager_t *manager, const sockaddr_t *addr) { + SDL_assert_release(addr != NULL); + pcmanager_lock(manager); + pclist_t *result = pclist_ll_find_by(manager->servers, addr, pclist_ll_compare_address); pcmanager_unlock(manager); return result; } @@ -173,7 +183,7 @@ pclist_t *pclist_find_by_uuid(pcmanager_t *manager, const uuidstr_t *uuid) { return result; } -static int pclist_ll_compare_address(pclist_t *other, const void *v) { +static int pclist_ll_compare_ip(pclist_t *other, const void *v) { SDL_assert_release(v); SDL_assert_release(other); SDL_assert_release(other->server); @@ -181,6 +191,15 @@ static int pclist_ll_compare_address(pclist_t *other, const void *v) { return SDL_strcmp(other->server->serverInfo.address, (const char *) v); } +static int pclist_ll_compare_address(pclist_t *other, const void *v) { + SDL_assert_release(v); + SDL_assert_release(other); + SDL_assert_release(other->server); + SDL_assert_release(other->server->serverInfo.address); + const sockaddr_t *addr = (const sockaddr_t *) v; + return 0; +} + static int pclist_ll_compare_uuid(pclist_t *other, const void *v) { SDL_assert_release(v); SDL_assert_release(other); diff --git a/src/app/backend/pcmanager/pclist.h b/src/app/backend/pcmanager/pclist.h index da2f7fdbb..1ee53749d 100644 --- a/src/app/backend/pcmanager/pclist.h +++ b/src/app/backend/pcmanager/pclist.h @@ -29,3 +29,5 @@ void pclist_free(pcmanager_t *manager); pclist_t *pclist_find_by_uuid(pcmanager_t *manager, const uuidstr_t *uuid); pclist_t *pclist_find_by_ip(pcmanager_t *manager, const char *ip); + +pclist_t *pclist_find_by_addr(pcmanager_t *manager, const sockaddr_t *addr); diff --git a/src/app/backend/pcmanager/pcmanager.c b/src/app/backend/pcmanager/pcmanager.c index 1349761fe..a2dd228be 100644 --- a/src/app/backend/pcmanager/pcmanager.c +++ b/src/app/backend/pcmanager/pcmanager.c @@ -1,5 +1,5 @@ - #include "backend/pcmanager.h" +#include "discovery/discovery.h" #include "priv.h" #include "pclist.h" @@ -13,6 +13,7 @@ pcmanager_t *pcmanager_new(app_t *app, executor_t *executor) { manager->executor = executor; manager->thread_id = SDL_ThreadID(); manager->lock = SDL_CreateMutex(); + discovery_init(&manager->discovery, (discovery_callback) pcmanager_lan_host_discovered, manager); pcmanager_load_known_hosts(manager); return manager; } @@ -21,10 +22,20 @@ void pcmanager_destroy(pcmanager_t *manager) { pcmanager_auto_discovery_stop(manager); pcmanager_save_known_hosts(manager); pclist_free(manager); + discovery_deinit(&manager->discovery); SDL_DestroyMutex(manager->lock); SDL_free(manager); } + +void pcmanager_auto_discovery_start(pcmanager_t *manager) { + discovery_start(&manager->discovery); +} + +void pcmanager_auto_discovery_stop(pcmanager_t *manager) { + discovery_stop(&manager->discovery); +} + void pcmanager_lock(pcmanager_t *manager) { SDL_LockMutex(manager->lock); } @@ -129,4 +140,3 @@ bool pcmanager_send_wol(pcmanager_t *manager, const uuidstr_t *uuid, pcmanager_c const pclist_t *pcmanager_servers(pcmanager_t *manager) { return manager->servers; } - diff --git a/src/app/backend/pcmanager/priv.h b/src/app/backend/pcmanager/priv.h index 0c6916e01..82b4d2f32 100644 --- a/src/app/backend/pcmanager/priv.h +++ b/src/app/backend/pcmanager/priv.h @@ -1,6 +1,7 @@ #pragma once #include "../pcmanager.h" +#include "discovery/discovery.h" #include "executor.h" #include "uuidstr.h" #include @@ -29,7 +30,7 @@ struct pcmanager_t { pclist_t *servers; SDL_mutex *lock; pcmanager_listener_list *listeners; - discovery_task_t *discovery_task; + discovery_t discovery; }; void serverdata_free(PSERVER_DATA data); @@ -45,3 +46,5 @@ void pcmanager_unlock(pcmanager_t *manager); void pcmanager_load_known_hosts(pcmanager_t *manager); void pcmanager_save_known_hosts(pcmanager_t *manager); + +void pcmanager_lan_host_discovered(const sockaddr_t *addr, pcmanager_t *manager); \ No newline at end of file diff --git a/src/app/backend/pcmanager/worker/manual_add.c b/src/app/backend/pcmanager/worker/manual_add.c index 274491041..6bdcaac17 100644 --- a/src/app/backend/pcmanager/worker/manual_add.c +++ b/src/app/backend/pcmanager/worker/manual_add.c @@ -14,11 +14,5 @@ int worker_host_discovered(worker_context_t *context) { } int updated_by_addr(worker_context_t *context, bool force) { - struct sockaddr *addr = context->arg1; - char ip[64]; - if (sockaddr_get_ip_str(addr, ip, sizeof(ip)) != 0) { - return GS_FAILED; - } - uint16_t port = sockaddr_get_port(addr); - return pcmanager_update_by_ip(context, ip, port, force); + return pcmanager_update_by_addr(context, context->arg1, force); } diff --git a/src/app/backend/pcmanager/worker/update.c b/src/app/backend/pcmanager/worker/update.c index 17658290c..b769767b4 100644 --- a/src/app/backend/pcmanager/worker/update.c +++ b/src/app/backend/pcmanager/worker/update.c @@ -3,6 +3,8 @@ #include "backend/pcmanager/pclist.h" #include "backend/pcmanager/listeners.h" +#include + #include "errors.h" #include "util/bus.h" #include "app.h" @@ -20,11 +22,17 @@ int worker_host_update(worker_context_t *context) { } int pcmanager_update_by_ip(worker_context_t *context, const char *ip, uint16_t port, bool force) { - SDL_assert_release(context != NULL); - SDL_assert_release(context->manager != NULL); - SDL_assert_release(ip != NULL); + return 0; +} + +int pcmanager_update_by_addr(worker_context_t *context, sockaddr_t *addr, bool force) { + assert(context != NULL); + assert(context->manager != NULL); + assert(addr != NULL); + int ret = 0; pcmanager_t *manager = context->manager; - char *ip_dup = strdup(ip); + // FIXME: Implement this + char *ip_dup = strdup(""); pclist_t *existing = pclist_find_by_ip(manager, ip_dup); if (existing) { SERVER_STATE_ENUM state = existing->state.code; @@ -47,7 +55,7 @@ int pcmanager_update_by_ip(worker_context_t *context, const char *ip, uint16_t p } GS_CLIENT client = app_gs_client_new(context->app); PSERVER_DATA server = serverdata_new(); - int ret = gs_get_status(client, server, ip_dup, port, app_configuration->unsupported); + ret = gs_get_status(client, server, ip_dup, sockaddr_get_port(addr), app_configuration->unsupported); ip_dup = NULL; gs_destroy(client); if (existing) { diff --git a/tests/app/backend/pcmanager/CMakeLists.txt b/tests/app/backend/pcmanager/CMakeLists.txt index bfea330ae..97712bff1 100644 --- a/tests/app/backend/pcmanager/CMakeLists.txt +++ b/tests/app/backend/pcmanager/CMakeLists.txt @@ -1 +1,3 @@ -add_unit_test(test_known_hosts test_known_hosts.c) \ No newline at end of file +add_unit_test(test_known_hosts test_known_hosts.c) + +add_subdirectory(discovery) \ No newline at end of file diff --git a/tests/app/backend/pcmanager/discovery/CMakeLists.txt b/tests/app/backend/pcmanager/discovery/CMakeLists.txt new file mode 100644 index 000000000..f2cf18c55 --- /dev/null +++ b/tests/app/backend/pcmanager/discovery/CMakeLists.txt @@ -0,0 +1 @@ +add_unit_test(test_throttle test_throttle.c) \ No newline at end of file diff --git a/tests/app/backend/pcmanager/discovery/test_throttle.c b/tests/app/backend/pcmanager/discovery/test_throttle.c new file mode 100644 index 000000000..10a14d9bd --- /dev/null +++ b/tests/app/backend/pcmanager/discovery/test_throttle.c @@ -0,0 +1,42 @@ +#include "unity.h" +#include "backend/pcmanager/discovery/throttle.h" + +#include + +static discovery_throttle_t throttle; +static int counter = 0; + +void callback(const sockaddr_t *addr, void *user_data) { + (void) addr; + (void) user_data; + counter++; +} + +void setUp(void) { + counter = 0; + discovery_throttle_init(&throttle, callback, NULL); +} + +void tearDown(void) { + discovery_throttle_deinit(&throttle); +} + +void test_discovery_throttle(void) { + sockaddr_t *addr = sockaddr_new(); + sockaddr_set_ip_str(addr, AF_INET, "192.168.1.110"); + sockaddr_set_port(addr, 47989); + discovery_throttle_on_discovered(&throttle, addr, 10); + discovery_throttle_on_discovered(&throttle, addr, 10); + discovery_throttle_on_discovered(&throttle, addr, 10); + TEST_ASSERT_EQUAL(1, counter); + SDL_Delay(20); + discovery_throttle_on_discovered(&throttle, addr, 10); + TEST_ASSERT_EQUAL(2, counter); + sockaddr_free(addr); +} + +int main() { + UNITY_BEGIN(); + RUN_TEST(test_discovery_throttle); + return UNITY_END(); +} \ No newline at end of file diff --git a/third_party/commons b/third_party/commons index 3a22070ce..a8c933035 160000 --- a/third_party/commons +++ b/third_party/commons @@ -1 +1 @@ -Subproject commit 3a22070ce74b35119f892751d4cd4eb5b2289e02 +Subproject commit a8c93303577178a9b5a66815f031417db7fdeb1f diff --git a/third_party/ss4s b/third_party/ss4s index 03ad710b9..f0e90f0c7 160000 --- a/third_party/ss4s +++ b/third_party/ss4s @@ -1 +1 @@ -Subproject commit 03ad710b9033aa21f83e3c34d6437f11a39bf803 +Subproject commit f0e90f0c7258714f53bdc8d11f7a0abd0b205216