qavahiservicepublisher.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2011 Girish Ramakrishnan (girish@forwardbias.in)
  4. **
  5. ** Use, modification and distribution is allowed without limitation,
  6. ** warranty, liability or support of any kind.
  7. **
  8. ****************************************************************************/
  9. #include "qavahiservicepublisher.h"
  10. #include <QtDebug>
  11. #include <stdarg.h>
  12. static void QAPDEBUG(const char *fmt, ...)
  13. {
  14. static const bool debug = qgetenv("DEBUG").toInt();
  15. if (debug) {
  16. char buf[1024];
  17. va_list ap;
  18. va_start(ap, fmt);
  19. vsnprintf(buf, sizeof(buf), fmt, ap);
  20. va_end(ap);
  21. qDebug("%s", buf);
  22. }
  23. }
  24. // http://avahi.sourcearchive.com/documentation/0.6.25-1ubuntu2/main.html
  25. QAvahiServicePublisher::QAvahiServicePublisher(QObject *parent)
  26. : QObject(parent), m_client(0), m_group(0)
  27. {
  28. initialize();
  29. }
  30. QAvahiServicePublisher::~QAvahiServicePublisher()
  31. {
  32. uninitialize();
  33. }
  34. void QAvahiServicePublisher::initialize()
  35. {
  36. const AvahiPoll *poll_api = avahi_qt_poll_get();
  37. m_client = avahi_client_new(poll_api, AVAHI_CLIENT_NO_FAIL, client_callback, this /* userdata */, &m_error);
  38. if (!m_client) {
  39. QAPDEBUG("Failed to create client : %s", avahi_strerror(m_error));
  40. m_errorString = avahi_strerror(m_error);
  41. emit changeNotification(Error);
  42. }
  43. }
  44. void QAvahiServicePublisher::uninitialize()
  45. {
  46. if (m_group) {
  47. avahi_entry_group_free(m_group);
  48. m_group = 0;
  49. }
  50. if (m_client) {
  51. avahi_client_free(m_client);
  52. m_client = 0;
  53. }
  54. }
  55. void QAvahiServicePublisher::publish(const QString &name, const QString &type, qint32 port, const QString &txtRecord)
  56. {
  57. Service service;
  58. service.name = name;
  59. service.type = type;
  60. service.port = port;
  61. service.txtRecord = txtRecord;
  62. publish(service);
  63. }
  64. void QAvahiServicePublisher::publish(const Service &service)
  65. {
  66. m_services.append(service);
  67. if (avahi_client_get_state(m_client) != AVAHI_CLIENT_S_RUNNING) {
  68. QAPDEBUG("Publishing later, server not running. Check if avahi-dameon is running");
  69. return;
  70. }
  71. doPublish(m_client);
  72. }
  73. // This function takes client as argument because it's called from clientCallback. Since
  74. // clientCallback maybe called from avahi_client_new, m_client may still be 0.
  75. void QAvahiServicePublisher::doPublish(AvahiClient *client)
  76. {
  77. if (m_services.isEmpty()) {
  78. QAPDEBUG("Nothing to publish");
  79. return;
  80. }
  81. QAPDEBUG("Publishing service");
  82. if (!m_group) {
  83. QAPDEBUG("Creating new group");
  84. m_group = avahi_entry_group_new(client, entry_group_callback, this /* userdata */);
  85. if (!m_group) {
  86. QAPDEBUG("Failed to create group");
  87. emit changeNotification(Error);
  88. return;
  89. }
  90. }
  91. avahi_entry_group_reset(m_group);
  92. for (int i = 0; i < m_services.count(); i++) {
  93. const Service &service = m_services[i];
  94. m_error = avahi_entry_group_add_service(m_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_USE_MULTICAST,
  95. service.name.toUtf8().constData(), service.type.toUtf8().constData(),
  96. NULL /* domain */, NULL /* host */,
  97. service.port, service.txtRecord.toUtf8().constData(), NULL); // ## use _strlst overload
  98. if (m_error < 0) {
  99. QAPDEBUG("Failed to add service to group: %s", avahi_strerror(m_error));
  100. m_errorString = avahi_strerror(m_error);
  101. avahi_entry_group_reset(m_group);
  102. emit changeNotification(Error);
  103. return;
  104. }
  105. }
  106. commit();
  107. }
  108. void QAvahiServicePublisher::commit()
  109. {
  110. m_error = avahi_entry_group_commit(m_group);
  111. if (m_error < 0) {
  112. QAPDEBUG("Failed to commit service group : %s", avahi_strerror(m_error));
  113. m_errorString = avahi_strerror(m_error);
  114. avahi_entry_group_reset(m_group);
  115. emit changeNotification(Error);
  116. return;
  117. }
  118. emit changeNotification(ServicesCommited);
  119. }
  120. void QAvahiServicePublisher::client_callback(AvahiClient *client, AvahiClientState state, void *userdata)
  121. {
  122. QAvahiServicePublisher *that = reinterpret_cast<QAvahiServicePublisher *>(userdata);
  123. that->clientCallback(client, state);
  124. }
  125. void QAvahiServicePublisher::entry_group_callback(AvahiEntryGroup *group, AvahiEntryGroupState state, void *userdata)
  126. {
  127. QAvahiServicePublisher *that = reinterpret_cast<QAvahiServicePublisher *>(userdata);
  128. that->entryGroupCallback(group, state);
  129. }
  130. void QAvahiServicePublisher::clientCallback(AvahiClient *client, AvahiClientState state)
  131. {
  132. switch (state) {
  133. case AVAHI_CLIENT_FAILURE:
  134. QAPDEBUG("Connection failure: %s", avahi_strerror(avahi_client_errno(client)));
  135. m_error = AVAHI_CLIENT_FAILURE;
  136. m_errorString = avahi_strerror(avahi_client_errno(client));
  137. emit changeNotification(Error);
  138. uninitialize();
  139. initialize();
  140. break;
  141. case AVAHI_CLIENT_CONNECTING:
  142. QAPDEBUG("Client is connecting");
  143. emit changeNotification(ClientConnecting);
  144. break;
  145. case AVAHI_CLIENT_S_RUNNING:
  146. QAPDEBUG("Server running");
  147. emit changeNotification(ServerRunning);
  148. doPublish(client);
  149. break;
  150. case AVAHI_CLIENT_S_COLLISION:
  151. QAPDEBUG("Server name Collission");
  152. emit changeNotification(ServerNameCollision);
  153. if (m_group)
  154. avahi_entry_group_reset(m_group);
  155. break;
  156. case AVAHI_CLIENT_S_REGISTERING:
  157. QAPDEBUG("Registering");
  158. emit changeNotification(ServerRegistering);
  159. if (m_group)
  160. avahi_entry_group_reset(m_group);
  161. break;
  162. }
  163. }
  164. void QAvahiServicePublisher::entryGroupCallback(AvahiEntryGroup *group, AvahiEntryGroupState state)
  165. {
  166. switch (state) {
  167. case AVAHI_ENTRY_GROUP_ESTABLISHED:
  168. QAPDEBUG("All fine and dandy");
  169. emit changeNotification(ServicesRegistered);
  170. return;
  171. case AVAHI_ENTRY_GROUP_COLLISION: {
  172. QAPDEBUG("Oops, service name Collission");
  173. // There's no way to tell which name caused a collision. Rename all!
  174. for (int i = 0; i < m_services.count(); i++) {
  175. QString name = m_services[i].name;
  176. char *newName = avahi_alternative_service_name(name.toUtf8().constData());
  177. QAPDEBUG("Service %s renamed to %s", qPrintable(name), newName);
  178. m_services[i].name = QString::fromUtf8(newName);
  179. avahi_free(newName);
  180. }
  181. emit changeNotification(ServiceNameCollision);
  182. doPublish(m_client);
  183. return;
  184. }
  185. case AVAHI_ENTRY_GROUP_FAILURE:
  186. QAPDEBUG("Entry group failure : %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(group))));
  187. m_error = avahi_client_errno(avahi_entry_group_get_client(group));
  188. m_errorString = avahi_strerror(m_error);
  189. emit changeNotification(Error);
  190. uninitialize();
  191. initialize();
  192. return;
  193. case AVAHI_ENTRY_GROUP_REGISTERING:
  194. QAPDEBUG("Registring the group");
  195. emit changeNotification(ServicesRegistering);
  196. return;
  197. case AVAHI_ENTRY_GROUP_UNCOMMITED:
  198. QAPDEBUG("Entry group uncommitted");
  199. emit changeNotification(ServicesUncommited);
  200. return;
  201. default:
  202. QAPDEBUG("Unhandled state in entryGroupCallback: %d", state);
  203. return;
  204. }
  205. }