diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2022-12-06 02:51:31 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2023-05-13 13:51:34 +0200 |
commit | 2f506e85b327a1869fb3f07e6555167bf0d13a72 (patch) | |
tree | d2bf356f45952e50af7937bdfdd559fd99911615 | |
parent | 082e9d6d015509e09abbffcb37f2d92b8fcfe8f7 (diff) | |
download | tracker-2f506e85b327a1869fb3f07e6555167bf0d13a72.tar.gz |
cli: Implement list/search for HTTP endpoints
Use an Avahi client so that we can look up network-local HTTP
endpoints. This is exposed as `tracker3 endpoint --list-http`
option.
-rw-r--r-- | docs/manpages/tracker3-endpoint.1.txt | 2 | ||||
-rw-r--r-- | src/tracker/meson.build | 2 | ||||
-rw-r--r-- | src/tracker/tracker-endpoint.c | 228 |
3 files changed, 230 insertions, 2 deletions
diff --git a/docs/manpages/tracker3-endpoint.1.txt b/docs/manpages/tracker3-endpoint.1.txt index 6fb89f586..0fe5ee669 100644 --- a/docs/manpages/tracker3-endpoint.1.txt +++ b/docs/manpages/tracker3-endpoint.1.txt @@ -53,6 +53,8 @@ The database itself will be stored according to _database_path_. Use the system bus. *-l, --list*:: List all SPARQL endpoints available in DBus +*-L, --list-http*:: + List network-local HTTP SPARQL endpoints *--http-port*:: Creates a HTTP endpoint that listens in the specified port *--loopback*:: diff --git a/src/tracker/meson.build b/src/tracker/meson.build index caaa4b3a3..0fb7fcfc0 100644 --- a/src/tracker/meson.build +++ b/src/tracker/meson.build @@ -27,7 +27,7 @@ executable(command_name, sources, ], install: true, install_rpath: tracker_internal_libs_dir, - dependencies: [tracker_common_dep, tracker_sparql_dep, tracker_sparql_private_dep, gio_unix], + dependencies: [tracker_common_dep, tracker_sparql_dep, tracker_sparql_private_dep, gio_unix, avahi_glib, avahi_client], include_directories: [commoninc, configinc, srcinc], ) diff --git a/src/tracker/tracker-endpoint.c b/src/tracker/tracker-endpoint.c index f0c7a298f..4cf7e89fa 100644 --- a/src/tracker/tracker-endpoint.c +++ b/src/tracker/tracker-endpoint.c @@ -30,6 +30,13 @@ #include <glib-unix.h> #include <glib/gi18n.h> +#ifdef HAVE_AVAHI +#include <avahi-common/malloc.h> +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-glib/glib-watch.h> +#endif + #include <libtracker-sparql/tracker-sparql.h> #include "tracker-endpoint.h" @@ -42,6 +49,7 @@ static gboolean session_bus = FALSE; static gboolean system_bus = FALSE; static gboolean name_owned = FALSE; static gboolean list = FALSE; +static gboolean list_http = FALSE; static gint http_port = -1; static gboolean http_loopback; @@ -82,6 +90,10 @@ static GOptionEntry entries[] = { N_("List SPARQL endpoints available in DBus"), NULL }, + { "list-http", 'L', 0, G_OPTION_ARG_NONE, &list_http, + N_("List network-local HTTP SPARQL endpoints"), + NULL + }, { NULL } }; @@ -104,7 +116,7 @@ sanity_check (void) return FALSE; } - if (!list && !!ontology_path == !!ontology_name) { + if (!list && !list_http && !!ontology_path == !!ontology_name) { /* TRANSLATORS: those are commandline arguments */ g_printerr ("%s\n", _("One “ontology” or “ontology-path” option should be provided")); return FALSE; @@ -370,6 +382,214 @@ run_list_endpoints (void) return EXIT_SUCCESS; } +#ifdef HAVE_AVAHI +static GList *resolvers = NULL; +static GList *services = NULL; + +static void +check_quit (GMainLoop *main_loop) +{ + if (resolvers) + return; + g_main_loop_quit (main_loop); +} + +static void +add_service (const gchar *uri) +{ + if (g_list_find_custom (services, uri, (GCompareFunc) g_strcmp0)) + return; + + services = g_list_prepend (services, g_strdup (uri)); +} + +static gboolean +validate_service (AvahiStringList *list, + gchar **path_out) +{ + AvahiStringList *txtvers, *protovers, *binding, *path; + gchar *txtvers_value, *protovers_value, *binding_value, *path_value; + gboolean valid = FALSE; + + txtvers = avahi_string_list_find (list, "txtvers"); + protovers = avahi_string_list_find (list, "protovers"); + binding = avahi_string_list_find (list, "binding"); + path = avahi_string_list_find (list, "path"); + + if (txtvers && protovers && binding && path) { + avahi_string_list_get_pair (txtvers, NULL, &txtvers_value, NULL); + avahi_string_list_get_pair (protovers, NULL, &protovers_value, NULL); + avahi_string_list_get_pair (binding, NULL, &binding_value, NULL); + avahi_string_list_get_pair (path, NULL, &path_value, NULL); + + valid = (g_strcmp0 (txtvers_value, "1") == 0 && + g_strcmp0 (protovers_value, "1.1") == 0 && + g_strcmp0 (binding_value, "HTTP") == 0); + if (path_out) + *path_out = g_strdup (path_value); + } + + g_clear_pointer (&txtvers_value, avahi_free); + g_clear_pointer (&protovers_value, avahi_free); + g_clear_pointer (&binding_value, avahi_free); + g_clear_pointer (&path_value, avahi_free); + + return valid; +} + +static void +service_resolver_cb (AvahiServiceResolver *service_resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + gpointer user_data) +{ + GMainLoop *main_loop = user_data; + gchar *path = NULL; + + switch (event) { + case AVAHI_RESOLVER_FOUND: + if (validate_service (txt, &path)) { + if (g_str_has_prefix (path, "http")) { + add_service (path); + } else { + char address_str[AVAHI_ADDRESS_STR_MAX]; + gchar *full_uri; + + /* Add the full URI, by IP address */ + avahi_address_snprint ((char *) &address_str, + AVAHI_ADDRESS_STR_MAX, + address); + full_uri = g_strdup_printf ("http://%s%s%s:%d%s", + protocol == AVAHI_PROTO_INET6 ? "[" : "", + address_str, + protocol == AVAHI_PROTO_INET6 ? "]" : "", + port, + path ? path : "/"); + add_service (full_uri); + g_free (full_uri); + + /* Add the full URI, by host name */ + full_uri = g_strdup_printf ("http://%s:%d%s", + host_name, + port, + path ? path : "/"); + add_service (full_uri); + g_free (full_uri); + } + + g_free (path); + } + break; + case AVAHI_RESOLVER_FAILURE: + break; + } + + resolvers = g_list_remove (resolvers, service_resolver); + avahi_service_resolver_free (service_resolver); + check_quit (main_loop); +} + +static void +service_browser_cb (AvahiServiceBrowser *service_browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + gpointer user_data) +{ + GMainLoop *main_loop = user_data; + AvahiServiceResolver *resolver; + + switch (event) { + case AVAHI_BROWSER_NEW: + resolver = avahi_service_resolver_new (avahi_service_browser_get_client (service_browser), + interface, + protocol, + name, + type, + domain, + AVAHI_PROTO_UNSPEC, + 0, + service_resolver_cb, + main_loop); + resolvers = g_list_prepend (resolvers, resolver); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_FAILURE: + check_quit (main_loop); + break; + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } +} + +int +run_list_http_endpoints (void) +{ + AvahiGLibPoll *avahi_glib_poll; + AvahiClient *avahi_client = NULL; + AvahiServiceBrowser *avahi_service_browser = NULL; + GMainLoop *main_loop; + GList *l; + + main_loop = g_main_loop_new (NULL, FALSE); + + /* Set up avahi service browser */ + avahi_glib_poll = + avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT); + if (avahi_glib_poll) { + avahi_client = + avahi_client_new (avahi_glib_poll_get (avahi_glib_poll), + AVAHI_CLIENT_IGNORE_USER_CONFIG | + AVAHI_CLIENT_NO_FAIL, + NULL, NULL, NULL); + } + + if (avahi_client) { + avahi_service_browser = + avahi_service_browser_new (avahi_client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_sparql._tcp", + NULL, + 0, + service_browser_cb, + main_loop); + } + + /* Collect running HTTP services */ + if (avahi_service_browser) + g_main_loop_run (main_loop); + + g_main_loop_unref (main_loop); + + /* Sort and print the collected services */ + services = g_list_sort (services, (GCompareFunc) g_strcmp0); + for (l = services; l; l = l->next) + g_print ("%s\n", (gchar *) l->data); + + g_clear_pointer (&avahi_service_browser, avahi_service_browser_free); + g_clear_pointer (&avahi_client, avahi_client_free); + g_clear_pointer (&avahi_glib_poll, avahi_glib_poll_free); + g_list_free_full (services, g_free); + + return EXIT_SUCCESS; +} +#endif /* HAVE_AVAHI */ + int tracker_endpoint (int argc, const char **argv) { @@ -402,6 +622,12 @@ tracker_endpoint (int argc, const char **argv) if (list) { return run_list_endpoints (); + } else if (list_http) { +#ifdef HAVE_AVAHI + return run_list_http_endpoints (); +#else + return EXIT_FAILURE; +#endif } if (database_path) |