1 /*
2 * Copyright 2003-2021 The Music Player Daemon Project
3 * http://www.musicpd.org
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 #include "ClientInit.hxx"
21 #include "Init.hxx"
22 #include "Callback.hxx"
23 #include "thread/Mutex.hxx"
24 #include "util/RuntimeError.hxx"
25
26 #include <upnptools.h>
27
28 #include <cassert>
29
30 static Mutex upnp_client_init_mutex;
31 static unsigned upnp_client_ref;
32 static UpnpClient_Handle upnp_client_handle;
33
34 static int
UpnpClientCallback(Upnp_EventType et,const void * evp,void * cookie)35 UpnpClientCallback(Upnp_EventType et,
36 const void *evp,
37 void *cookie) noexcept
38 {
39 if (cookie == nullptr)
40 /* this is the cookie passed to UpnpRegisterClient();
41 but can this ever happen? Will libupnp ever invoke
42 the registered callback without that cookie? */
43 return UPNP_E_SUCCESS;
44
45 UpnpCallback &callback = UpnpCallback::FromUpnpCookie(cookie);
46 return callback.Invoke(et, evp);
47 }
48
49 static void
DoInit()50 DoInit()
51 {
52 auto code = UpnpRegisterClient(UpnpClientCallback, nullptr,
53 &upnp_client_handle);
54 if (code != UPNP_E_SUCCESS)
55 throw FormatRuntimeError("UpnpRegisterClient() failed: %s",
56 UpnpGetErrorMessage(code));
57 }
58
59 UpnpClient_Handle
UpnpClientGlobalInit(const char * iface)60 UpnpClientGlobalInit(const char* iface)
61 {
62 UpnpGlobalInit(iface);
63
64 try {
65 const std::scoped_lock<Mutex> protect(upnp_client_init_mutex);
66 if (upnp_client_ref == 0)
67 DoInit();
68 } catch (...) {
69 UpnpGlobalFinish();
70 throw;
71 }
72
73 ++upnp_client_ref;
74 return upnp_client_handle;
75 }
76
77 void
UpnpClientGlobalFinish()78 UpnpClientGlobalFinish() noexcept
79 {
80 {
81 const std::scoped_lock<Mutex> protect(upnp_client_init_mutex);
82
83 assert(upnp_client_ref > 0);
84 if (--upnp_client_ref == 0)
85 UpnpUnRegisterClient(upnp_client_handle);
86 }
87
88 UpnpGlobalFinish();
89 }
90