aboutsummaryrefslogtreecommitdiff
path: root/mdns.c
diff options
context:
space:
mode:
Diffstat (limited to 'mdns.c')
-rw-r--r--mdns.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/mdns.c b/mdns.c
new file mode 100644
index 0000000..b4def8d
--- /dev/null
+++ b/mdns.c
@@ -0,0 +1,186 @@
+#include "rodisc.h"
+
+#include <avahi-gobject/ga-client.h>
+#include <avahi-gobject/ga-entry-group.h>
+
+static GaClient *mdns_client;
+static GaEntryGroup *mdns_group;
+static GaEntryGroupService *mdns_service;
+
+static guint disc_change_count = 0;
+
+static void mdns_service_freeze()
+{
+ g_return_if_fail(mdns_service);
+ ga_entry_group_service_freeze(mdns_service);
+}
+
+static void mdns_service_thaw()
+{
+ GError *error = NULL;
+ g_return_if_fail(mdns_service);
+ if (!ga_entry_group_service_thaw(mdns_service, &error)) {
+ g_warning("Could not update service TXT entries: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+static void mdns_service_update()
+{
+ GError *error = NULL;
+
+ g_return_if_fail(mdns_service);
+
+ // "sys=waMA=00:00:00:00:00:00,adVF=0x4,adDT=0x2,adCC=0"
+ // waMA = MAC address
+ // adVF = Volume flags (0x200 "ask me first")
+ // adDT = Supported media?
+ // adCC = Disc change count?
+
+ guint flags = 0;
+ guint media = 2;
+ gchar *record =
+ g_strdup_printf("waMA=00:00:00:00:00:00,adVF=0x%x,adDT=0x%x,adCC=%u",
+ flags, media, disc_change_count);
+
+ if (!ga_entry_group_service_set(mdns_service, "sys", record, &error))
+ {
+ g_warning("Could not update main TXT record: %s", error->message);
+ g_error_free(error);
+ }
+
+ g_free(record);
+}
+
+static void mdns_service_update_disc(RODisc *disc)
+{
+ GError *error = NULL;
+
+ g_return_if_fail(mdns_service);
+
+ // "CdRom0=adVN=DiscLabel,adVT=public.cd-media"
+ gchar *record = g_strdup_printf("adVN=%s,adVT=%s", disc->label, disc->type);
+
+ const gchar *uri_basename = &disc->uri[1]; // Skip first '/'
+ if (!ga_entry_group_service_set(mdns_service, uri_basename, record, &error))
+ {
+ g_warning("Could not update TXT record for disc at '%s': %s",
+ disc->uri, error->message);
+ g_error_free(error);
+ }
+
+ g_free(record);
+}
+
+static void mdns_service_remove_disc(RODisc *disc)
+{
+ GError *error = NULL;
+ g_return_if_fail(mdns_service);
+ const gchar *uri_basename = &disc->uri[1];
+ if (!ga_entry_group_service_remove_key(mdns_service, uri_basename, &error)) {
+ g_warning("Could not update TXT record for disc at '%s': %s",
+ disc->uri, error->message);
+ g_error_free(error);
+ }
+}
+
+static void mdns_register_service()
+{
+ GError * error = NULL;
+ if (!mdns_group) {
+ mdns_group = ga_entry_group_new();
+
+ if (!ga_entry_group_attach(mdns_group, mdns_client, &error)) {
+ g_warning("Could not attach MDNS group to client: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ }
+
+ const gchar *name = avahi_client_get_host_name(mdns_client->avahi_client);
+ const unsigned int port = server_get_port();
+ mdns_service = ga_entry_group_add_service(mdns_group,
+ name, RODISC_MDNS_SERVICE,
+ port, &error,
+ NULL);
+ if (!mdns_service) {
+ g_warning("Could not create service: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ // Create TXT records, disc records, etc.
+ mdns_service_update();
+ rodisc_refresh_all();
+
+ if (!ga_entry_group_commit(mdns_group, &error)) {
+ g_warning("Could not announce MDNS service: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+}
+
+static void mdns_client_state_changed_cb(GaClient *client, GaClientState state, gpointer user_data)
+{
+ switch (state) {
+ case GA_CLIENT_STATE_FAILURE:
+ g_warning("MDNS client state failure");
+ break;
+ case GA_CLIENT_STATE_S_RUNNING:
+ g_debug("MDNS client found server running");
+ mdns_register_service();
+ break;
+ case GA_CLIENT_STATE_S_COLLISION:
+ case GA_CLIENT_STATE_S_REGISTERING:
+ g_message("MDNS collision");
+ if (mdns_group) {
+ ga_entry_group_reset(mdns_group, NULL);
+ mdns_service = 0;
+ }
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+}
+
+bool mdns_start()
+{
+ GError *error = NULL;
+
+ mdns_client = ga_client_new(GA_CLIENT_FLAG_NO_FLAGS);
+
+ g_signal_connect(mdns_client, "state-changed",
+ G_CALLBACK(mdns_client_state_changed_cb), NULL);
+
+ if (!ga_client_start(mdns_client, &error)) {
+ g_printerr("Could not start MDNS client: %s\n", error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ return true;
+}
+
+void mdns_stop()
+{
+ g_object_unref(mdns_client);
+}
+
+void mdns_publish(RODisc *disc)
+{
+ mdns_service_freeze();
+ disc_change_count++;
+ mdns_service_update_disc(disc);
+ mdns_service_update();
+ mdns_service_thaw();
+}
+
+void mdns_unpublish(RODisc *disc)
+{
+ mdns_service_freeze();
+ disc_change_count++;
+ mdns_service_remove_disc(disc);
+ mdns_service_update();
+ mdns_service_thaw();
+}