Compare commits

...

26 Commits

Author SHA1 Message Date
Max Kellermann
575d1786af release v0.20.5 2017-02-20 21:51:31 +01:00
Max Kellermann
bc1c927952 util/TimeFormat: suppress -Wunused on Windows 2017-02-20 21:44:01 +01:00
Max Kellermann
f95bc85f91 python/build/libs: upgrade FFmpeg to 3.2.4 2017-02-20 21:13:43 +01:00
Max Kellermann
4015195314 doc/user: instructions to compile the Windows binary 2017-02-20 21:06:43 +01:00
Max Kellermann
c3d883c6cb win32/build.py: default to x64 build 2017-02-20 21:06:43 +01:00
Max Kellermann
097e30321b win32/build.py: add option --32 2017-02-20 21:06:31 +01:00
Max Kellermann
b6ddeaacf2 win32/build.py: convert argument parser to loop
Allow multiple arguments.
2017-02-20 21:06:21 +01:00
Max Kellermann
b0c60ec124 win32/build.py: add variable "x64", assign host_arch later 2017-02-20 21:04:34 +01:00
Max Kellermann
f3b788703e tag/Handler: improve snprintf() return value check 2017-02-19 19:34:13 +01:00
Max Kellermann
4bb83781e8 output/httpd/IcyMetaDataServer: cast length to unsigned
Fixes another buffer overflow: if the stream has a very long title or
URL, resulting in a metadata string of more than 2 kB, icy_string[0]
is a negative value, which gets casted to size_t - ouch!

 https://bugs.musicpd.org/view.php?id=4652
2017-02-19 19:28:52 +01:00
Max Kellermann
a73195b7cc output/httpd/IcyMetaDataServer: pad the string with 15 spaces
Fixes a buffer overflow due to the bad formula rounding the buffer
size up.  At the same time, remove the "+1" from the meta_length
calculation, which takes the padding into account and at the same time
implements proper rounding.
2017-02-19 19:27:37 +01:00
Max Kellermann
1bd00b8a9a output/httpd/IcyMetaDataServer: remove the int cast
Why did this cast exist??
2017-02-19 19:27:37 +01:00
Max Kellermann
d84eaeafc5 doc/include/tags.xml: clarify that track/disc are decimal 2017-02-18 19:01:04 +01:00
Max Kellermann
20ae84bff9 {input,mixer}/alsa: cancel the DeferredMonitor in the destructor
Yet another potential crash bug fix.
2017-02-10 15:05:49 +01:00
Max Kellermann
7372c931b3 event/Loop: make IsInsideOrNull() available in the NDEBUG build
Fixes build breakage by commit 4e5271fcdf7; and this method does make
sense in non-debug builds.
2017-02-09 21:21:49 +01:00
Max Kellermann
29e1b6e465 mixer/alsa: reset the MultiSocketMonitor in the destructor
Fixes potential crash bug.
2017-02-09 21:13:19 +01:00
Max Kellermann
eda06993f8 event/MultiSocketMonitor: add method Reset() 2017-02-09 21:12:23 +01:00
Max Kellermann
4b30ef1cf2 event/MultiSocketMonitor: use C++11 initializer 2017-02-09 21:12:23 +01:00
Max Kellermann
e92e5e8eb8 event/MultiSocketMonitor: more API documentation
Now ClearSocketList() may only be called from PrepareSockets().
Calling it before destroying the object doesn't work properly, because
it doesn't unregister the TimeoutMonitor and the IdleMonitor.  Some of
its callers need to be fixed.
2017-02-09 21:12:23 +01:00
Max Kellermann
4e5271fcdf event/Call: allow usage during shutdown
Change EventLoop::IsInside() call to EventLoop::IsInsideOrNull().
This means that BlockingCall() may be used during shutdown, after the
main EventLoop::Run() has finished.  This is important because mixers
are currently registered in the main EventLoop.
2017-02-09 21:12:23 +01:00
Max Kellermann
3c55487a16 configure.ac: don't require libsidutils when building with libsidplayfp
The libsidplayfp fork has merged libsidutils into the main library.
The libsidutils we used to link with was part of the original
libsidplay project.
2017-02-09 13:09:03 +01:00
Max Kellermann
76a1cae5d8 {input,mixer}/alsa: fix off-by-one bug in count check
Doesn't make a practical difference - but it's more correct this way.
2017-02-09 12:46:49 +01:00
Max Kellermann
81a97315e3 NEWS: mention ID3 memory leak fix 2017-02-08 08:44:47 +01:00
Max Kellermann
53c14d97a6 lib/nfs/FileReader: remove debug line 2017-02-08 08:43:56 +01:00
Max Kellermann
69a82eec17 tag/TagId3: use AtScopeExit() for exception-safety 2017-02-06 23:32:07 +01:00
Max Kellermann
45cadef22f configure.ac: prepare for 0.20.5 2017-02-06 23:28:36 +01:00
17 changed files with 219 additions and 40 deletions

10
NEWS

@@ -1,3 +1,13 @@
ver 0.20.5 (2017/02/20)
* tags
- id3: fix memory leak on corrupt ID3 tags
* decoder
- sidplay: don't require libsidutils when building with libsidplayfp
* output
- httpd: fix two buffer overflows in IcyMetaData length calculation
* mixer
- alsa: fix crash bug
ver 0.20.4 (2017/02/01)
* input
- nfs: fix freeze after reconnect

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.4, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.5, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=4
VERSION_REVISION=5
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -992,7 +992,7 @@ AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enab
dnl --------------------------------- sidplay ---------------------------------
if test x$enable_sidplay != xno; then
dnl Check for libsidplayfp first
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp libsidutils],
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp],
[found_sidplayfp=yes],
[found_sidplayfp=no])
found_sidplay=$found_sidplayfp

@@ -55,7 +55,8 @@
<listitem>
<para>
<varname>track</varname>: the track number within the album.
<varname>track</varname>: the decimal track number within the
album.
</para>
</listitem>
@@ -103,7 +104,8 @@
<listitem>
<para>
<varname>disc</varname>: the disc number in a multi-disc album.
<varname>disc</varname>: the decimal disc number in a multi-disc
album.
</para>
</listitem>

@@ -135,6 +135,91 @@ apt-get install g++ \
</para>
<programlisting>make install</programlisting>
<section id="windows_build">
<title>Compiling for Windows</title>
<para>
Even though it does not "feel" like a Windows application,
<application>MPD</application> works well under Windows.
Its build process follows the "Linux style", and may seem
awkward for Windows people (who are not used to compiling
their software, anyway).
</para>
<para>
Basically, there are three ways to compile
<application>MPD</application> for Windows:
</para>
<orderedlist>
<listitem>
<para>
Build on Windows for Windows. All you need to do is
described above already: configure and make.
</para>
<para>
For Windows users, this is kind of unusual, because few
Windows users have a GNU toolchain and a UNIX shell
installed.
</para>
</listitem>
<listitem>
<para>
Build on Linux for Windows. This is described above
already: configure and make. You need the <ulink
url="https://mingw-w64.org/"><application>mingw-w64</application>
cross compiler</ulink>. Pass
<parameter>--host=i686-w64-mingw32</parameter> (32 bit)
or <parameter>--host=x86_64-w64-mingw32</parameter> (64
bit) to configure.
</para>
<para>
This is somewhat natural for Linux users. Many
distributions have <application>mingw-w64</application>
packages. The remaining difficulty here is installing
all the external libraries. And
<application>MPD</application> usually needs many,
making this method cumbersome for the casual user.
</para>
</listitem>
<listitem>
<para>
Build on Linux for Windows using the
<application>MPD</application>'s library build script.
</para>
</listitem>
</orderedlist>
<para>
This section is about the latter.
</para>
<para>
Just like with the native build, unpack the
<application>MPD</application> source tarball and change
into the directory. Then, instead of
<command>./configure</command>, type:
</para>
<programlisting>./win32/build.py --64</programlisting>
<para>
This downloads various library sources, and then configures
and builds <application>MPD</application> (for x64; to build
a 32 bit binary, pass <parameter>--32</parameter>). The
resulting EXE files is linked statically, i.e. it contains
all the libraries already, and you do not need carry DLLs
around. It is large, but easy to use. If you wish to have
a small <filename>mpd.exe</filename> with DLLs, you need to
compile manually, without the <filename>build.py</filename>
script.
</para>
</section>
</section>
<section id="systemd_socket">

@@ -58,8 +58,8 @@ libmad = AutotoolsProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-3.2.2.tar.xz',
'3f01bd1fe1a17a277f8c84869e5d9192b4b978cb660872aa2b54c3cc8a2fedfc',
'http://ffmpeg.org/releases/ffmpeg-3.2.4.tar.xz',
'6e38ff14f080c98b58cf5967573501b8cb586e3a173b591f3807d8f0660daf7a',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',

@@ -79,7 +79,7 @@ private:
void
BlockingCall(EventLoop &loop, std::function<void()> &&f)
{
if (loop.IsInside()) {
if (loop.IsInsideOrNull()) {
/* we're already inside the loop - we can simply call
the function */
f();

@@ -222,8 +222,9 @@ EventLoop::Run()
#ifndef NDEBUG
assert(busy);
assert(thread.IsInside());
thread = ThreadId::Null();
#endif
thread = ThreadId::Null();
}
void

@@ -212,12 +212,16 @@ public:
}
#endif
#ifndef NDEBUG
/**
* Like IsInside(), but also returns true if the thread has
* already ended (or was not started yet). This is useful for
* code which may run during startup or shutdown, when events
* are not yet/anymore handled.
*/
gcc_pure
bool IsInsideOrNull() const {
return thread.IsNull() || thread.IsInside();
}
#endif
};
#endif /* MAIN_NOTIFY_H */

@@ -28,12 +28,18 @@
#endif
MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
:IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) {
:IdleMonitor(_loop), TimeoutMonitor(_loop) {
}
MultiSocketMonitor::~MultiSocketMonitor()
void
MultiSocketMonitor::Reset()
{
// TODO
assert(GetEventLoop().IsInsideOrNull());
fds.clear();
IdleMonitor::Cancel();
TimeoutMonitor::Cancel();
ready = refresh = false;
}
void

@@ -96,7 +96,19 @@ class MultiSocketMonitor : IdleMonitor, TimeoutMonitor
friend class SingleFD;
bool ready, refresh;
/**
* DispatchSockets() should be called.
*/
bool ready = false;
/**
* PrepareSockets() should be called.
*
* Note that this doesn't need to be initialized by the
* constructor; this class is activated with the first
* InvalidateSockets() call, which initializes this flag.
*/
bool refresh;
std::forward_list<SingleFD> fds;
@@ -107,11 +119,26 @@ public:
static constexpr unsigned HANGUP = SocketMonitor::HANGUP;
MultiSocketMonitor(EventLoop &_loop);
~MultiSocketMonitor();
using IdleMonitor::GetEventLoop;
public:
/**
* Clear the socket list and disable all #EventLoop
* registrations. Run this in the #EventLoop thread before
* destroying this object.
*
* Later, this object can be reused and reactivated by calling
* InvalidateSockets().
*
* Note that this class doesn't have a destructor which calls
* this method, because this would be racy and thus pointless:
* at the time ~MultiSocketMonitor() is called, our virtual
* methods have been morphed to be pure again, and in the
* meantime the #EventLoop thread could invoke those pure
* methods.
*/
void Reset();
/**
* Invalidate the socket list. A call to PrepareSockets() is
* scheduled which will then update the list.
@@ -121,12 +148,19 @@ public:
IdleMonitor::Schedule();
}
/**
* Add one socket to the list of monitored sockets.
*
* May only be called from PrepareSockets().
*/
void AddSocket(int fd, unsigned events) {
fds.emplace_front(*this, fd, events);
}
/**
* Remove all sockets.
*
* May only be called from PrepareSockets().
*/
void ClearSocketList();
@@ -135,6 +169,8 @@ public:
* each one; its return value is the events bit mask. A
* return value of 0 means the socket will be removed from the
* list.
*
* May only be called from PrepareSockets().
*/
template<typename E>
void UpdateSocketList(E &&e) {
@@ -157,15 +193,26 @@ public:
/**
* Replace the socket list with the given file descriptors.
* The given pollfd array will be modified by this method.
*
* May only be called from PrepareSockets().
*/
void ReplaceSocketList(pollfd *pfds, unsigned n);
#endif
protected:
/**
* Override this method and update the socket registrations.
* To do that, call AddSocket(), ClearSocketList(),
* UpdateSocketList() and ReplaceSocketList().
*
* @return timeout or a negative value for no timeout
*/
virtual std::chrono::steady_clock::duration PrepareSockets() = 0;
/**
* At least one socket is ready or the timeout has expired.
* This method should be used to perform I/O.
*/
virtual void DispatchSockets() = 0;
private:

@@ -98,12 +98,9 @@ public:
}
~AlsaInputStream() {
/* ClearSocketList must be called from within the
IOThread; if we don't do it manually here, the
~MultiSocketMonitor() will do it in the current
thread */
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
ClearSocketList();
MultiSocketMonitor::Reset();
DeferredMonitor::Cancel();
});
snd_pcm_close(capture_handle);
@@ -181,7 +178,7 @@ AlsaInputStream::PrepareSockets()
}
int count = snd_pcm_poll_descriptors_count(capture_handle);
if (count < 0) {
if (count <= 0) {
ClearSocketList();
return std::chrono::steady_clock::duration(-1);
}

@@ -23,6 +23,7 @@
#include "output/OutputAPI.hxx"
#include "event/MultiSocketMonitor.hxx"
#include "event/DeferredMonitor.hxx"
#include "event/Call.hxx"
#include "util/ASCII.hxx"
#include "util/ReusableArray.hxx"
#include "util/Domain.hxx"
@@ -53,6 +54,13 @@ public:
DeferredMonitor::Schedule();
}
~AlsaMixerMonitor() {
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
MultiSocketMonitor::Reset();
DeferredMonitor::Cancel();
});
}
private:
virtual void RunDeferred() override {
InvalidateSockets();
@@ -102,7 +110,7 @@ AlsaMixerMonitor::PrepareSockets()
}
int count = snd_mixer_poll_descriptors_count(mixer);
if (count < 0)
if (count <= 0)
count = 0;
struct pollfd *pfds = pfd_buffer.Get(count);

@@ -60,7 +60,11 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
{
// The leading n is a placeholder for the length information
auto icy_metadata = FormatString("nStreamTitle='%s';"
"StreamUrl='%s';",
"StreamUrl='%s';"
/* pad 15 spaces just in case
the length needs to be
rounded up */
" ",
stream_title,
stream_url);
@@ -68,7 +72,7 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
meta_length--; // subtract placeholder
meta_length = ((int)meta_length / 16) + 1;
meta_length = meta_length / 16;
icy_metadata[0] = meta_length;
@@ -109,5 +113,5 @@ icy_server_metadata_page(const Tag &tag, const TagType *types)
if (icy_string.IsNull())
return nullptr;
return Page::Copy(icy_string.c_str(), (icy_string[0] * 16) + 1);
return Page::Copy(icy_string.c_str(), uint8_t(icy_string[0]) * 16 + 1);
}

@@ -44,7 +44,7 @@ add_tag_tag(TagType type, const char *value, void *ctx)
unsigned n = strtoul(value, &end, 10);
if (value != end) {
char s[21];
if (snprintf(s, 21, "%u", n) >= 0)
if (snprintf(s, 21, "%u", n) > 0)
tag.AddItem(type, s);
}
} else

@@ -24,6 +24,7 @@
#include "TagTable.hxx"
#include "TagBuilder.hxx"
#include "util/Alloc.hxx"
#include "util/ScopeExit.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
@@ -78,11 +79,9 @@ import_id3_string(const id3_ucs4_t *ucs4)
if (gcc_unlikely(utf8 == nullptr))
return nullptr;
id3_utf8_t *utf8_stripped = (id3_utf8_t *)
xstrdup(Strip((char *)utf8));
free(utf8);
AtScopeExit(utf8) { free(utf8); };
return utf8_stripped;
return (id3_utf8_t *)xstrdup(Strip((char *)utf8));
}
/**
@@ -126,9 +125,10 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
if (utf8 == nullptr)
continue;
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char *)utf8);
free(utf8);
}
}
@@ -177,8 +177,9 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
if (utf8 == nullptr)
return;
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
free(utf8);
}
/**
@@ -236,22 +237,23 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
if (name == nullptr)
continue;
AtScopeExit(name) { free(name); };
id3_utf8_t *value = tag_id3_getstring(frame, 2);
if (value == nullptr)
continue;
AtScopeExit(value) { free(value); };
tag_handler_invoke_pair(handler, handler_ctx,
(const char *)name,
(const char *)value);
TagType type = tag_id3_parse_txxx_name((const char*)name);
free(name);
if (type != TAG_NUM_OF_ITEM_TYPES)
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char*)value);
free(value);
}
}

@@ -60,6 +60,8 @@ ParseTimePoint(const char *s, const char *format)
#ifdef WIN32
/* TODO: emulate strptime()? */
(void)s;
(void)format;
throw std::runtime_error("Time parsing not implemented on Windows");
#else
struct tm tm;

@@ -5,11 +5,22 @@ import sys, subprocess
configure_args = sys.argv[1:]
host_arch = 'i686-w64-mingw32'
x64 = True
if len(configure_args) > 0 and configure_args[0] == '--64':
configure_args = configure_args[1:]
while len(configure_args) > 0:
arg = configure_args[0]
if arg == '--64':
x64 = True
elif arg == '--32':
x64 = False
else:
break
configure_args.pop(0)
if x64:
host_arch = 'x86_64-w64-mingw32'
else:
host_arch = 'i686-w64-mingw32'
# the path to the MPD sources
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))