2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "zeroconf-internal.h"
26 #include <avahi-client/client.h>
27 #include <avahi-client/publish.h>
29 #include <avahi-common/alternative.h>
30 #include <avahi-common/domain.h>
31 #include <avahi-common/malloc.h>
32 #include <avahi-common/error.h>
34 #include <avahi-glib/glib-watch.h>
37 #define G_LOG_DOMAIN "avahi"
39 static char *avahiName
;
40 static int avahiRunning
;
41 static AvahiGLibPoll
*avahi_glib_poll
;
42 static const AvahiPoll
*avahi_poll
;
43 static AvahiClient
*avahiClient
;
44 static AvahiEntryGroup
*avahiGroup
;
46 static void avahiRegisterService(AvahiClient
* c
);
48 /* Callback when the EntryGroup changes state */
49 static void avahiGroupCallback(AvahiEntryGroup
* g
,
50 AvahiEntryGroupState state
,
51 G_GNUC_UNUSED
void *userdata
)
56 g_debug("Service group changed to state %d", state
);
59 case AVAHI_ENTRY_GROUP_ESTABLISHED
:
60 /* The entry group has been established successfully */
61 g_message("Service '%s' successfully established.",
65 case AVAHI_ENTRY_GROUP_COLLISION
:
66 /* A service name collision happened. Let's pick a new name */
67 n
= avahi_alternative_service_name(avahiName
);
68 avahi_free(avahiName
);
71 g_message("Service name collision, renaming service to '%s'",
74 /* And recreate the services */
75 avahiRegisterService(avahi_entry_group_get_client(g
));
78 case AVAHI_ENTRY_GROUP_FAILURE
:
79 g_warning("Entry group failure: %s",
80 avahi_strerror(avahi_client_errno
81 (avahi_entry_group_get_client(g
))));
82 /* Some kind of failure happened while we were registering our services */
86 case AVAHI_ENTRY_GROUP_UNCOMMITED
:
87 g_debug("Service group is UNCOMMITED");
89 case AVAHI_ENTRY_GROUP_REGISTERING
:
90 g_debug("Service group is REGISTERING");
94 /* Registers a new service with avahi */
95 static void avahiRegisterService(AvahiClient
* c
)
99 g_debug("Registering service %s/%s", SERVICE_TYPE
, avahiName
);
101 /* If this is the first time we're called,
102 * let's create a new entry group */
104 avahiGroup
= avahi_entry_group_new(c
, avahiGroupCallback
, NULL
);
106 g_warning("Failed to create avahi EntryGroup: %s",
107 avahi_strerror(avahi_client_errno(c
)));
112 /* Add the service */
113 /* TODO: This currently binds to ALL interfaces.
114 * We could maybe add a service per actual bound interface,
115 * if that's better. */
116 ret
= avahi_entry_group_add_service(avahiGroup
,
117 AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
,
118 0, avahiName
, SERVICE_TYPE
, NULL
,
119 NULL
, listen_port
, NULL
);
121 g_warning("Failed to add service %s: %s", SERVICE_TYPE
,
122 avahi_strerror(ret
));
126 /* Tell the server to register the service group */
127 ret
= avahi_entry_group_commit(avahiGroup
);
129 g_warning("Failed to commit service group: %s",
130 avahi_strerror(ret
));
139 /* Callback when avahi changes state */
140 static void avahiClientCallback(AvahiClient
* c
, AvahiClientState state
,
141 G_GNUC_UNUSED
void *userdata
)
146 /* Called whenever the client or server state changes */
147 g_debug("Client changed to state %d", state
);
150 case AVAHI_CLIENT_S_RUNNING
:
151 g_debug("Client is RUNNING");
153 /* The server has startup successfully and registered its host
154 * name on the network, so it's time to create our services */
156 avahiRegisterService(c
);
159 case AVAHI_CLIENT_FAILURE
:
160 reason
= avahi_client_errno(c
);
161 if (reason
== AVAHI_ERR_DISCONNECTED
) {
162 g_message("Client Disconnected, will reconnect shortly");
164 avahi_entry_group_free(avahiGroup
);
168 avahi_client_free(avahiClient
);
170 avahi_client_new(avahi_poll
,
171 AVAHI_CLIENT_NO_FAIL
,
172 avahiClientCallback
, NULL
,
175 g_warning("Could not reconnect: %s",
176 avahi_strerror(reason
));
180 g_warning("Client failure: %s (terminal)",
181 avahi_strerror(reason
));
186 case AVAHI_CLIENT_S_COLLISION
:
187 g_debug("Client is COLLISION");
188 /* Let's drop our registered services. When the server is back
189 * in AVAHI_SERVER_RUNNING state we will register them
190 * again with the new host name. */
192 g_debug("Resetting group");
193 avahi_entry_group_reset(avahiGroup
);
196 case AVAHI_CLIENT_S_REGISTERING
:
197 g_debug("Client is REGISTERING");
198 /* The server records are now being established. This
199 * might be caused by a host name change. We need to wait
200 * for our own records to register until the host name is
201 * properly esatblished. */
204 g_debug("Resetting group");
205 avahi_entry_group_reset(avahiGroup
);
210 case AVAHI_CLIENT_CONNECTING
:
211 g_debug("Client is CONNECTING");
215 void init_avahi(const char *serviceName
)
218 g_debug("Initializing interface");
220 if (!avahi_is_valid_service_name(serviceName
))
221 g_error("Invalid zeroconf_name \"%s\"", serviceName
);
223 avahiName
= avahi_strdup(serviceName
);
227 avahi_glib_poll
= avahi_glib_poll_new(NULL
, G_PRIORITY_DEFAULT
);
228 avahi_poll
= avahi_glib_poll_get(avahi_glib_poll
);
230 avahiClient
= avahi_client_new(avahi_poll
, AVAHI_CLIENT_NO_FAIL
,
231 avahiClientCallback
, NULL
, &error
);
234 g_warning("Failed to create client: %s",
235 avahi_strerror(error
));
245 void avahi_finish(void)
247 g_debug("Shutting down interface");
250 avahi_entry_group_free(avahiGroup
);
255 avahi_client_free(avahiClient
);
259 if (avahi_glib_poll
!= NULL
) {
260 avahi_glib_poll_free(avahi_glib_poll
);
261 avahi_glib_poll
= NULL
;
264 avahi_free(avahiName
);