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