5 * Created by C. Ramakrishnan on Wed Dec 18 2002.
8 * Avahi implementation (c) 2006 stefan kersten
9 * Howl implementation (c) 2005 2006 stefan kersten
13 SuperCollider real time audio synthesis system
14 Copyright (c) 2002 James McCartney. All rights reserved.
15 http://www.audiosynth.com
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2 of the License, or
20 (at your option) any later version.
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "Rendezvous.h"
33 #include "SC_Export.h"
35 SC_DLLEXPORT_C
int scprintf(const char *fmt
, ...);
38 static const char* kSCRendezvousServiceName
= "SuperCollider";
40 static const char* SCRendezvousProtocolString(SCRendezvousProtocol proto
)
43 case kSCRendezvous_UDP
: return "_osc._udp.";
44 case kSCRendezvous_TCP
: return "_osc._tcp.";
50 #if defined(__APPLE__) && !defined(SC_IPHONE)
51 #include <CoreServices/CoreServices.h>
53 void PublishPortToRendezvous(SCRendezvousProtocol protocol
, short portNum
)
55 scprintf("PublishPortToRendezvous %d %hu\n", protocol
, portNum
);
56 CFStringRef serviceType
= 0;
59 case kSCRendezvous_UDP
:
60 serviceType
= CFSTR("_osc._udp.");
62 case kSCRendezvous_TCP
:
63 serviceType
= CFSTR("_osc._tcp.");
67 CFNetServiceRef netServiceRef
=
68 CFNetServiceCreate(NULL
, // use default allocator
69 CFSTR(""), // use default domain
71 CFSTR("SuperCollider"),
76 scprintf("Couldn't create a Rendezvous net service.\n");
80 CFNetServiceRegisterWithOptions(netServiceRef
, 0, NULL
); // don't care about the error
84 # include <avahi-client/client.h>
85 # include <avahi-client/publish.h>
86 # include <avahi-common/alternative.h>
87 # include <avahi-common/error.h>
88 # include <avahi-common/malloc.h>
89 # include <avahi-common/thread-watch.h>
90 # include <avahi-common/timeval.h>
98 SCRendezvousProtocol mProto
;
108 void PublishPort(SCRendezvousProtocol proto
, short port
);
109 void CreateServices(AvahiClient
* client
);
110 void CreateServices() { CreateServices(mClient
); }
111 void ResetServices();
112 void RenameService();
114 static void client_cb(AvahiClient
* client
, AvahiClientState state
, void* data
);
115 static void group_cb(AVAHI_GCC_UNUSED AvahiEntryGroup
*, AvahiEntryGroupState state
, void* data
);
116 static void modify_cb(AVAHI_GCC_UNUSED AvahiTimeout
*, void* data
);
118 AvahiThreadedPoll
* mPoll
;
119 AvahiClient
* mClient
;
120 AvahiEntryGroup
* mGroup
;
121 AvahiEntry
* mEntries
;
122 pthread_mutex_t mMutex
;
126 struct AvahiSessionInstance
128 AvahiSessionInstance()
132 ~AvahiSessionInstance()
138 AvahiSession
* GetSession()
141 mSession
= new AvahiSession();
146 AvahiSession
* mSession
;
149 static AvahiSessionInstance gAvahiSession
;
151 AvahiSession::AvahiSession()
160 pthread_mutex_init(&mMutex
, 0);
161 mServiceName
= avahi_strdup(kSCRendezvousServiceName
);
163 mPoll
= avahi_threaded_poll_new();
165 scprintf("Zeroconf: failed to create poll API\n");
169 mClient
= avahi_client_new(
170 avahi_threaded_poll_get(mPoll
),
171 (AvahiClientFlags
)0, client_cb
, this, &err
);
173 scprintf("Zeroconf: failed to create client: %s\n", avahi_strerror(err
));
174 avahi_threaded_poll_free(mPoll
);
179 avahi_threaded_poll_start(mPoll
);
182 AvahiSession::~AvahiSession()
185 avahi_threaded_poll_stop(mPoll
);
186 avahi_client_free(mClient
);
187 avahi_threaded_poll_free(mPoll
);
188 AvahiEntry
* entry
= mEntries
;
190 AvahiEntry
* next
= entry
->mNext
;
196 pthread_mutex_destroy(&mMutex
);
199 void AvahiSession::client_cb(AvahiClient
* client
, AvahiClientState state
, void* data
)
201 AvahiSession
* self
= (AvahiSession
*)data
;
204 case AVAHI_CLIENT_S_RUNNING
:
205 self
->CreateServices(client
);
207 case AVAHI_CLIENT_S_COLLISION
:
208 self
->ResetServices();
210 case AVAHI_CLIENT_FAILURE
:
211 scprintf("Zeroconf: client failure: %s\n",
212 avahi_strerror(avahi_client_errno(self
->mClient
)));
214 case AVAHI_CLIENT_CONNECTING
:
215 case AVAHI_CLIENT_S_REGISTERING
:
220 void AvahiSession::group_cb(AVAHI_GCC_UNUSED AvahiEntryGroup
*, AvahiEntryGroupState state
, void* data
)
222 AvahiSession
* self
= (AvahiSession
*)data
;
225 case AVAHI_ENTRY_GROUP_ESTABLISHED
:
226 scprintf("Zeroconf: registered service '%s'\n", self
->mServiceName
);
228 case AVAHI_ENTRY_GROUP_COLLISION
: {
229 self
->RenameService();
230 self
->CreateServices();
233 case AVAHI_ENTRY_GROUP_FAILURE
:
234 case AVAHI_ENTRY_GROUP_UNCOMMITED
:
235 case AVAHI_ENTRY_GROUP_REGISTERING
:
240 void AvahiSession::modify_cb(AVAHI_GCC_UNUSED AvahiTimeout
* e
, void* data
)
242 AvahiSession
* self
= (AvahiSession
*)data
;
243 if (avahi_client_get_state(self
->mClient
) == AVAHI_CLIENT_S_RUNNING
)
244 self
->CreateServices();
247 void AvahiSession::PublishPort(SCRendezvousProtocol proto
, short port
)
249 if (!mClient
) return;
251 AvahiEntry
* entry
= new AvahiEntry
;
252 entry
->mProto
= proto
;
254 entry
->mRegistered
= false;
256 pthread_mutex_lock(&mMutex
);
257 entry
->mNext
= mEntries
;
259 pthread_mutex_unlock(&mMutex
);
261 avahi_threaded_poll_lock(mPoll
);
263 avahi_threaded_poll_get(mPoll
)->timeout_new(
264 avahi_threaded_poll_get(mPoll
),
265 avahi_elapse_time(&tv
, 0, 0),
268 avahi_threaded_poll_unlock(mPoll
);
271 void AvahiSession::CreateServices(AvahiClient
* client
)
276 avahi_entry_group_reset(mGroup
);
278 mGroup
= avahi_entry_group_new(client
, group_cb
, this);
280 scprintf("Zeroconf: failed to create entry group: %s\n",
281 avahi_strerror(avahi_client_errno(client
)));
286 pthread_mutex_lock(&mMutex
);
287 AvahiEntry
* entry
= mEntries
;
289 const char* type
= SCRendezvousProtocolString(entry
->mProto
);
290 err
= avahi_entry_group_add_service(
291 mGroup
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
,
292 (AvahiPublishFlags
)0, mServiceName
, type
,
293 NULL
, NULL
, entry
->mPort
,
295 if (err
== AVAHI_ERR_COLLISION
) {
296 // BUG: shouldn't this actually be triggered in the entry
299 err
= avahi_entry_group_add_service(
300 mGroup
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
,
301 (AvahiPublishFlags
)0, mServiceName
, type
,
302 NULL
, NULL
, entry
->mPort
,
306 scprintf("Zeroconf: failed to register service '%s': %s\n",
307 mServiceName
, avahi_strerror(err
));
309 entry
= entry
->mNext
;
311 pthread_mutex_unlock(&mMutex
);
313 if (!avahi_entry_group_is_empty(mGroup
)) {
314 err
= avahi_entry_group_commit(mGroup
);
316 scprintf("Zeroconf: failed to commit entry group: %s\n",
317 avahi_strerror(err
));
323 void AvahiSession::ResetServices()
325 if (mGroup
) avahi_entry_group_reset(mGroup
);
328 void AvahiSession::RenameService()
330 char* name
= avahi_alternative_service_name(mServiceName
);
331 avahi_free(mServiceName
);
335 void PublishPortToRendezvous(SCRendezvousProtocol proto
, short port
)
337 gAvahiSession
.GetSession()->PublishPort(proto
, port
);
342 # include <sys/poll.h>
343 # include <pthread.h>
350 void PublishPort(SCRendezvousProtocol protocol
, short portNum
);
351 void PublishPort(sw_discovery session
, SCRendezvousProtocol protocol
, short portNum
);
353 sw_discovery mSession
;
354 pthread_mutex_t mMutex
;
357 struct HowlSessionInstance
359 HowlSessionInstance()
363 ~HowlSessionInstance()
369 HowlSession
* GetSession()
372 mSession
= new HowlSession();
377 HowlSession
* mSession
;
380 static HowlSessionInstance gHowlSession
;
382 HowlSession::HowlSession()
385 pthread_mutex_init(&mMutex
, 0);
388 HowlSession::~HowlSession()
390 if (mSession
) sw_discovery_fina(mSession
);
391 pthread_mutex_destroy(&mMutex
);
394 void HowlSession::PublishPort(SCRendezvousProtocol protocol
, short portNum
)
396 pthread_mutex_lock(&mMutex
);
397 if (!mSession
) sw_discovery_init(&mSession
);
398 if (mSession
) PublishPort(mSession
, protocol
, portNum
);
399 pthread_mutex_unlock(&mMutex
);
403 howl_publish_reply_func(
406 sw_discovery_publish_status
,
413 void HowlSession::PublishPort(sw_discovery session
, SCRendezvousProtocol protocol
, short portNum
)
415 const char* serviceType
= SCRendezvousProtocolString(protocol
);
416 sw_discovery_oid oid
;
419 sw_discovery_publish(
422 kSCRendezvousServiceName
, // name
424 0, // domain (.local)
427 0, 0, // text records
428 howl_publish_reply_func
, 0, // reply func
433 "Zeroconf: publishing service %s on port %hu %s\n",
434 serviceType
, portNum
,
435 res
== SW_OKAY
? "succeeded" : "failed"
439 void PublishPortToRendezvous(SCRendezvousProtocol protocol
, short portNum
)
441 gHowlSession
.GetSession()->PublishPort(protocol
, portNum
);
444 #else // !__APPLE__ && !HAVE_AVAHI && !HAVE_HOWL
446 void PublishPortToRendezvous(SCRendezvousProtocol protocol
, short portNum
)
450 #endif // __APPLE__ || HAVE_AVAHI || HAVE_HOWL