aboutsummaryrefslogtreecommitdiff
path: root/mdns.c
blob: b4def8d0fe3643d0ea04d40ee370a68a0fee7953 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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();
}