Compare commits

...

104 Commits

Author SHA1 Message Date
Max Kellermann
1930d5774d release v0.21.5 2019-02-22 15:23:33 +01:00
Max Kellermann
7220a76be0 doc/plugins.rst: document udisks2/policykit rule 2019-02-22 15:22:20 +01:00
Max Kellermann
83f7610dd1 storage/udisks2: move empty string check out of the fallback block in MapUTF8()
Even if the LocalStorage is available, return the "udisks://" URI when
the MapUTF8() parameter is an empty string.  This fixes the mount URI
in the state file.
2019-02-22 15:07:40 +01:00
Max Kellermann
30e0644722 db/simple: call ReturnSong() on mounted database
Fixes a memory leak, or an assertion failure in the debug build.
2019-02-22 14:52:13 +01:00
Max Kellermann
3ada464020 db/simple: use C++11 initializer 2019-02-22 14:52:01 +01:00
Max Kellermann
d5983dd362 storage/udisks2: use the relative path
Closes #487
2019-02-22 14:41:56 +01:00
Max Kellermann
98258acc37 storage/udisks2: pass Path to SetMountPoint() 2019-02-22 14:41:56 +01:00
Max Kellermann
8002bc752f NEWS: mention the udisks2 AlreadyMounted fix 2019-02-22 14:41:56 +01:00
Max Kellermann
834ad7a58f TagPrint: omit tags which were disabled by the client
Closes #471
2019-02-22 13:05:38 +01:00
Max Kellermann
e8f2f98048 tag/Mask: fix another typo, this time in operator^=
Similar to commit ff1ff1e54a
2019-02-22 12:44:36 +01:00
Max Kellermann
c672b60d07 build/pkg-config.sh: add comment 2019-02-22 12:39:59 +01:00
Max Kellermann
ea269c9c92 python/build/libs.py: upgrade CURL to 7.64.0 2019-02-22 12:10:06 +01:00
Max Kellermann
1fe3a77640 python/build/libs.py: upgrade FFmpeg to 4.1.1 2019-02-22 12:09:35 +01:00
Max Kellermann
bbaeea1ab7 storage/udisks2: use existing mount point if already mounted
Fixes the "org.freedesktop.UDisks2.Error.AlreadyMounted" error.

Closes #485
2019-02-21 13:32:03 +01:00
Max Kellermann
0a3aee9d82 storage/udisks2: move code to SetMountPoint() 2019-02-21 13:31:59 +01:00
Max Kellermann
2434020971 storage/udisks2: adjust lambda indent 2019-02-21 13:31:57 +01:00
Max Kellermann
41e0eb7378 lib/dbus/udisks2: parse the MountPoints property 2019-02-21 13:28:26 +01:00
Max Kellermann
6adf964c81 lib/dbus/ReadIter: add dbus_message_iter_get_fixed_array() wrapper 2019-02-21 12:56:05 +01:00
Max Kellermann
b59f37bc0a db/simple/Directory: close the Database in destructor
Fixes assertion failure.
2019-02-20 22:50:15 +01:00
Max Kellermann
cf2d171ccc db/simple: reorder checks in assert() to fix assertion failure
`light_song.Get()` could cause an assertion failure because the
`Manual<>` object must not be used if uninitialized.

Regression by commit ebc006ab52
2019-02-20 21:24:01 +01:00
Max Kellermann
cc28a7b67f Main: create Database on stack, move to Instance after Open() succeeded
This fixes use-after-free bug in SimpleDatabase::Close(), accessing
the `root` object which was already freed by the `catch` block in
Open().

By having the Database on the stack first, we can avoid calling
Close() on the failed-to-open Database from Instance's destructor.

Closes #482
2019-02-20 20:50:28 +01:00
Max Kellermann
8b5c33cecd Instance: use std::unique_ptr<> to manage the Database pointer 2019-02-20 20:48:20 +01:00
Max Kellermann
6c28adbcd2 db/Plugin: use std::unique_ptr<> to manage Database pointers 2019-02-20 20:43:31 +01:00
Max Kellermann
2125e3ed57 db/simple/Directory: add noexcept 2019-02-20 20:39:49 +01:00
Max Kellermann
3da7ecfadf mixer/pulse: add missing ParseFloat() check 2019-02-20 19:27:13 +01:00
Max Kellermann
5bb02bbd39 mixer/pulse: move volume_scale_factor up to improve struct packing 2019-02-20 19:25:55 +01:00
Max Kellermann
f11aa09f7c mixer/pulse: add const to volume_scale_factor 2019-02-20 19:25:53 +01:00
Max Kellermann
02eb4752d3 mixer/pulse: use C++11 initializer 2019-02-20 19:25:45 +01:00
Max Kellermann
d9c3215584 mixer/pulse: rename scale to scale_volume
Make it less generic, to avoid clashes.
2019-02-20 19:23:11 +01:00
Clément Pit-Claudel
110e6d026b mixer/pulse: Add a new 'scale' parameter to allow volumes above 100
Closes GH-479.
2019-02-17 16:14:52 -05:00
Max Kellermann
c0f57b8a8b net/IPv[46]Address: update copyright 2019-02-19 13:00:45 +01:00
Max Kellermann
57633fbcb3 net/AllocatedSocketAddress: add methods IsV6Any(), IsV4Mapped() 2019-02-19 12:51:24 +01:00
Max Kellermann
864c87e6c0 net/SocketAddress: add method GetLocalPath() 2019-02-19 12:50:40 +01:00
Max Kellermann
1a516cf3c0 net/AllocatedSocketAddress: add method GetLocalRaw() 2019-02-19 12:43:16 +01:00
Max Kellermann
5c25499c5e lib/cdio/Paranoia: add method GetDiscSectorRange() 2019-02-19 12:40:36 +01:00
Max Kellermann
da4bb4c298 fs/io/OutputStream: update include guard 2019-02-19 12:39:29 +01:00
Max Kellermann
5b8ff61799 fs/io/BufferedOutputStream: add WithBufferedOutputStream() 2019-02-19 12:37:53 +01:00
Max Kellermann
56bded07b1 system/UniqueFileDescriptor: import std::swap 2019-02-19 12:36:54 +01:00
Max Kellermann
db144a43ad system/Open: add OpenWriteOnly(), OpenDirectory() 2019-02-19 12:16:41 +01:00
Max Kellermann
5965f62b56 system/EpollFD: include cleanup 2019-02-19 11:51:52 +01:00
Max Kellermann
05aa9f72a9 util/StringView: add SkipPrefix(), RemoveSuffix() 2019-02-19 11:51:32 +01:00
Max Kellermann
281461f0f0 nfs: work around assertion failure on exception during program init
Closes #477
2019-02-15 18:33:58 +01:00
Max Kellermann
f70eb63879 Instance: eliminate FinishShutdownUpdate(), move code to destructor 2019-02-15 18:20:11 +01:00
Max Kellermann
99c23cf139 Instance: eliminate ShutdownDatabase(), move code to destructor
Destruct automatically, even if leaving the scope due to exception
being thrown.
2019-02-15 18:04:23 +01:00
Max Kellermann
9aa75e738c Merge branch 'protocol-doc-typo' of git://github.com/mxjeff/MPD 2019-02-15 18:03:49 +01:00
Max Kellermann
e9c45a9140 playlist/Registry: add RAII class 2019-02-05 23:03:29 +01:00
Max Kellermann
a065c6e6b9 Main: use AtScopeExit() to call DeinitFS() 2019-02-05 23:02:50 +01:00
Max Kellermann
feb5ff9bd2 Mapper: remove empty function mapper_finish() 2019-02-05 23:01:09 +01:00
Max Kellermann
92ec3f0881 valgrind.suppressions: add GObject/libgcrypt/libsmbclient suppressions 2019-02-05 22:53:02 +01:00
Max Kellermann
98c47d9d36 Instance: remove FinishShutdownPartitions()
The list of partitions is cleared automatically.
2019-02-05 22:53:02 +01:00
Max Kellermann
6c67408944 event/Loop: add flag alive
This replaces the old `dead` flag which was unreliable; it was `false`
if the EventThread was not yet started, which could cause deadlocks in
BlockingCall().
2019-02-05 22:38:45 +01:00
Max Kellermann
261a816b21 command/AllCommands: remove empty function command_finish() 2019-02-05 22:15:41 +01:00
Max Kellermann
7a23c123c8 decoder/List: add RAII class 2019-02-05 22:12:22 +01:00
Max Kellermann
e85b24bee0 decoder/List: add noexcept 2019-02-05 22:11:51 +01:00
Max Kellermann
9e73ea77b4 input/Init: add RAII class 2019-02-05 22:07:49 +01:00
Max Kellermann
b0739eca87 test/ConfigGlue: merge duplicate code from various debug programs 2019-02-05 21:56:20 +01:00
Max Kellermann
848f6aa5ab Main: stop io_thread and rtio_thread automatically
They will be stopped by ~EventThread() when the `Instance` is deleted.
2019-02-05 21:49:59 +01:00
Max Kellermann
c9ba4f3f9c archive/List: add RAII class 2019-02-05 21:40:07 +01:00
Max Kellermann
c0e9246a66 archive/List: add noexcept 2019-02-05 21:38:46 +01:00
Max Kellermann
096c23f27d unix/SignalHandlers: add RAII class 2019-02-05 21:36:51 +01:00
Max Kellermann
40bde1eac9 unix/SignalHandlers: add noexcept 2019-02-05 21:36:35 +01:00
Max Kellermann
4b55ed17a9 LogInit: add noexcept 2019-02-05 21:36:35 +01:00
kaliko
4f757a5add Fixed protocol documentation
* "lsinfo" argument is optional
 * "tagtypes disable" arguments are mandatory (typo)
2019-02-03 10:38:34 +01:00
Max Kellermann
674c137e5f NEWS: mention the TagMask typo fix 2019-02-02 15:17:25 +01:00
kaliko
ff1ff1e54a Fixed typo in TagMask 2019-02-02 15:14:31 +01:00
Yue Wang
42b22187c8 [OSXOutput] Throw an error when device not found
Currently it falls back to system default device (either internal speaker or headphone) when device not found. 
I believe it is a better to fail in this case, to make it better aligned with platforms (such as alsa).
2019-01-25 19:50:27 -08:00
Max Kellermann
cfe22502ab fs/io/StdioOutputStream: add noexcept 2019-01-22 09:03:49 +01:00
Max Kellermann
d77b0c7dcd net/SocketAddress: add constexpr 2019-01-22 08:42:35 +01:00
Max Kellermann
5cf889b676 util/WStringView: add missing include 2019-01-22 08:38:03 +01:00
Max Kellermann
ffc36d5255 input/buffered: implement seeking to end of file
Previously, a seek to the end of the file would cause an assertion
failure in SparseMap::Check() because the given offset was invalid.

Closes #453
2019-01-22 07:42:00 +01:00
Max Kellermann
0126276e2f FileCommands: log irregular errors while looking for cover art 2019-01-21 22:21:11 +01:00
Max Kellermann
58d6ddab9e FileCommands: catch all exceptions 2019-01-21 22:19:32 +01:00
Max Kellermann
05db6934eb FileCommands: fix deadlock in "albumart" command
Must lock the mutex before calling any of the unprotected InputStream methods.

Closes #462
2019-01-21 22:16:46 +01:00
Max Kellermann
02c68c5cdb net/HostParser: add noexcept 2019-01-21 21:20:43 +01:00
Max Kellermann
b02fee7309 util/PrintException: support "const char *" 2019-01-21 21:19:35 +01:00
Max Kellermann
424f75c9e1 util/OffsetPointer: remove redundant inline keywords from constexpr functions 2019-01-21 21:19:09 +01:00
Max Kellermann
f6e1176f97 util/CharUtil: remove redundant inline keywords from constexpr functions 2019-01-21 21:18:23 +01:00
Max Kellermann
e4700c0a27 util/Cast: remove redundant inline keywords from constexpr functions 2019-01-21 21:17:58 +01:00
Max Kellermann
cf23fd8774 fs/io/FileOutputStream: add constructor with directory fd 2019-01-21 21:10:02 +01:00
Max Kellermann
dee8872395 fs/io/FileOutputStream: move code to Open() 2019-01-21 21:09:34 +01:00
Max Kellermann
4ba9357a9c input/CdioParanoia: C++ wrappers for libcdio types 2019-01-21 20:20:20 +01:00
Max Kellermann
48ec09ab1e test/net/TestIPv4Address: make literal unsigned to work around -Wsign-compare 2019-01-21 14:39:24 +01:00
Max Kellermann
754f4048a8 output/shout: evaluate tls option only if TLS is enabled in libshout
Fixes build failure after commit
0cea67ee70
2019-01-21 14:36:43 +01:00
Max Kellermann
037bb07d08 db/VHelper: include DetachedSong.hxx to fix GCC 9 build failure
GCC 9's libstdc++ is unable to use forward-declared types as
std::vector item because the compiler wants to resolve `noexcept()` on
the item destructor.
2019-01-21 14:34:12 +01:00
Max Kellermann
87635c5268 input/CdioParanoia: use the new function names 2019-01-21 14:18:55 +01:00
Max Kellermann
528b4338f4 input/CdioParanoia: use cdio_cddap_free_messages() on recent library versions 2019-01-21 14:16:51 +01:00
Max Kellermann
c780b8bba9 input/CdioParanoia: remove useless cdda_messages() call 2019-01-21 12:36:59 +01:00
Max Kellermann
ca34f3250b input/CdioParanoia: detect libcdio version at compile time
libcdio_paranoia was split from libcdio in version 90, and at the same
time, the header was moved from cdio/paranoia.h to
cdio/paranoia/paranoia.h.  We can easily detect this version at
compile time which is faster than configure time.
2019-01-21 12:14:13 +01:00
Max Kellermann
6a68e1c3f3 test/net/TestIPv6Address: work around failure on macOS 2019-01-21 12:13:52 +01:00
Max Kellermann
85f77ec81d test/net/TestLocalSocketAddress: can't use strcmp() if the string isn't null-terminated. 2019-01-21 12:12:36 +01:00
Max Kellermann
37debed0b8 python/build/libs.py: upgrade Boost to 1.69.0 2019-01-21 10:19:46 +01:00
Max Kellermann
008383f24a python/build/libs.py: upgrade CURL to 7.63.0 2019-01-21 10:11:50 +01:00
Jörg Krause
4f7d52dbf2 meson: add fixed-point Vorbis (Tremor) decoder support
Re-add build support for the fixed-point Vorbis (Tremor) decoder, which
was dropped when switching from Autotools to Meson.

Note, that it is not possible to build both, the Vorbis and the Tremor
decoder.

Closes: #405
2019-01-21 08:35:17 +01:00
Max Kellermann
c7848da8f2 input/CdioParanoia: add const to pointer 2019-01-20 22:03:49 +01:00
Max Kellermann
10a6c5c57d input/CdioParanoia: make variables more local 2019-01-20 21:59:57 +01:00
Max Kellermann
2cc2bab309 test/net: new unit tests 2019-01-20 21:05:21 +01:00
Max Kellermann
701fd1d939 net/IPv4Address: fix comment typo 2019-01-20 21:05:12 +01:00
Max Kellermann
d1bdea8edb Merge branch 'shout_tls' of git://github.com/JakobOvrum/MPD 2019-01-20 21:03:42 +01:00
Jakob Ovrum
0cea67ee70 shout output plugin: add support for TLS 2019-01-19 17:36:14 +01:00
Thomas Klausner
3a0480a482 Add missing include of stdlib.h.
Closes https://github.com/MusicPlayerDaemon/MPD/issues/456
2019-01-15 16:52:40 +01:00
Max Kellermann
1fa99da3c2 net/IPv[46]Address: make the initializers even more portable
Similar to 5a5229b499: use more C++14
constexpr.
2019-01-14 19:21:07 +01:00
James D. Smith
22d669da18 Add APE mapping for album artist.
"De-facto" field mappings are available at http://wiki.hydrogenaud.io/index.php?title=Tag_Mapping.
2019-01-14 19:15:42 +01:00
Thomas Zander
772681f23d Fix link_args for mDNSResponder on non-darwin platforms 2019-01-13 14:09:14 +01:00
Max Kellermann
1862a98a44 increment version number to 0.21.5 2019-01-04 19:31:07 +01:00
115 changed files with 1455 additions and 567 deletions

20
NEWS
View File

@@ -1,3 +1,23 @@
ver 0.21.5 (2019/02/22)
* protocol
- fix deadlock in "albumart" command
- fix "tagtypes disable" command
* database
- simple: fix assertion failure
- fix assertion failures with mount points
* storage
- udisks: fix "AlreadyMounted" error
- udisks: use relative path from mount URI
- fix memory leak
* input
- buffer: fix crash bug when playing remote WAV file
* tags
- ape: map "Album Artist"
* output
- shout: add support for TLS
* mixer
- pulse: add "scale_volume" setting
ver 0.21.4 (2019/01/04)
* database
- inotify: fix crash bug "terminate called after throwing ..."

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="26"
android:versionName="0.21.4">
android:versionCode="27"
android:versionName="0.21.5">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>

View File

@@ -1,5 +1,9 @@
#!/bin/sh -e
# This is a wrapper for pkg-config which helps with cross-compiling;
# it sets up environment variables to pkg-config searches for
# libraries in the sysroot where a copy of this script is located.
BIN=`dirname $0`
ROOT=`dirname "$BIN"`

View File

@@ -38,7 +38,7 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.21.4'
version = '0.21.5'
# The full version, including alpha/beta/rc tags.
release = version

View File

@@ -87,6 +87,22 @@ Mount file systems (e.g. USB sticks or other removable media) using
the udisks2 daemon via D-Bus. To obtain a valid udisks2 URI, consult
:ref:`the according neighbor plugin <neighbor_plugin>`.
It might be necessary to grant :program:`MPD` privileges to control
:program:`udisks2` through :program:`policykit`. To do this, create a
file called :file:`/usr/share/polkit-1/rules.d/mpd-udisks.rules` with
the following text::
polkit.addRule(function(action, subject) {
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
action.id == "org.freedesktop.udisks2.filesystem-mount-other-seat") &&
subject.user == "mpd") {
return polkit.Result.YES;
}
});
If you run MPD as a different user, change ``mpd`` to the name of your
MPD user.
.. _neighbor_plugin:
Neighbor plugins
@@ -935,6 +951,8 @@ The pulse plugin connects to a `PulseAudio <http://www.freedesktop.org/wiki/Soft
- Sets the host name of the PulseAudio server. By default, :program:`MPD` connects to the local PulseAudio server.
* - **sink NAME**
- Specifies the name of the PulseAudio sink :program:`MPD` should play on.
* - **scale_volume FACTOR**
- Specifies a linear scaling coefficient (ranging from 0.5 to 5.0) to apply when adjusting volume through :program:`MPD`. For example, chosing a factor equal to ``"0.7"`` means that setting the volume to 100 in :program:`MPD` will set the PulseAudio volume to 70%, and a factor equal to ``"3.5"`` means that volume 100 in :program:`MPD` corresponds to a 350% PulseAudio volume.
recorder
~~~~~~~~
@@ -974,6 +992,8 @@ You must set a format.
- Set the timeout for the shout connection in seconds. Defaults to 2 seconds.
* - **protocol icecast2|icecast1|shoutcast**
- Specifies the protocol that wil be used to connect to the server. The default is "icecast2".
* - **tls disabled|auto|auto_no_plain|rfc2818|rfc2817**
- Specifies what kind of TLS to use. The default is "disabled" (no TLS).
* - **mount URI**
- Mounts the :program:`MPD` stream in the specified URI.
* - **user USERNAME**

View File

@@ -909,7 +909,7 @@ The music database
.. _command_lsinfo:
:command:`lsinfo {URI}`
:command:`lsinfo [URI]`
Lists the contents of the directory
``URI``. The response contains records
starting with ``file``,
@@ -1131,7 +1131,7 @@ Connection settings
``tagtypes`` sub commands configure this
list.
:command:`tagtypes disable {NAME...]`
:command:`tagtypes disable {NAME...}`
Remove one or more tags from the list of tag types the
client is interested in. These will be omitted from
responses to this client.

View File

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.21.4',
version: '0.21.5',
meson_version: '>= 0.47.2',
default_options: [
'c_std=c99',

View File

@@ -128,6 +128,7 @@ option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
option('opus', type: 'feature', description: 'Opus decoder plugin')
option('sidplay', type: 'feature', description: 'C64 SID support via libsidplayfp or libsidplay2')
option('sndfile', type: 'feature', description: 'libsndfile decoder plugin')
option('tremor', type: 'feature', description: 'Fixed-point vorbis decoder plugin')
option('vorbis', type: 'feature', description: 'Vorbis decoder plugin')
option('wavpack', type: 'feature', description: 'WavPack decoder plugin')
option('wildmidi', type: 'feature', description: 'WildMidi decoder plugin')

View File

@@ -112,8 +112,8 @@ liblame = AutotoolsProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.1.tar.xz',
'a38ec4d026efb58506a99ad5cd23d5a9793b4bf415f2c4c2e9c1bb444acd1994',
'http://ffmpeg.org/releases/ffmpeg-4.1.1.tar.xz',
'373749824dfd334d84e55dff406729edfd1606575ee44dd485d97d45ea4d2d86',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.62.0.tar.xz',
'dab5643a5fe775ae92570b9f3df6b0ef4bc2a827a959361fb130c73b721275c1',
'http://curl.haxx.se/download/curl-7.64.0.tar.xz',
'2f2f13fa34d44aa29cb444077ad7dc4dc6d189584ad552e0aaeb06e608af6001',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -392,7 +392,7 @@ libnfs = AutotoolsProject(
)
boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2',
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7',
'http://downloads.sourceforge.net/project/boost/boost/1.69.0/boost_1_69_0.tar.bz2',
'8f32d4617390d1c2d16f26a27ab60d97807b35440d45891fa340fc2648b04406',
'include/boost/version.hpp',
)

View File

@@ -30,6 +30,9 @@
#ifdef ENABLE_DATABASE
#include "db/DatabaseError.hxx"
#include "db/Interface.hxx"
#include "db/update/Service.hxx"
#include "storage/StorageInterface.hxx"
#ifdef ENABLE_SQLITE
#include "sticker/StickerDatabase.hxx"
@@ -48,7 +51,19 @@ Instance::Instance()
{
}
Instance::~Instance() noexcept = default;
Instance::~Instance() noexcept
{
#ifdef ENABLE_DATABASE
delete update;
if (database != nullptr) {
database->Close();
database.reset();
}
delete storage;
#endif
}
Partition *
Instance::FindPartition(const char *name) noexcept

View File

@@ -41,7 +41,7 @@ class NeighborGlue;
#ifdef ENABLE_DATABASE
#include "db/DatabaseListener.hxx"
class Database;
#include "db/Ptr.hxx"
class Storage;
class UpdateService;
#endif
@@ -104,7 +104,7 @@ struct Instance final
#endif
#ifdef ENABLE_DATABASE
Database *database;
DatabasePtr database;
/**
* This is really a #CompositeStorage. To avoid heavy include
@@ -147,7 +147,6 @@ struct Instance final
Partition *FindPartition(const char *name) noexcept;
void BeginShutdownPartitions() noexcept;
void FinishShutdownPartitions() noexcept;
#ifdef ENABLE_DATABASE
/**
@@ -156,7 +155,7 @@ struct Instance final
* music_directory was configured).
*/
Database *GetDatabase() {
return database;
return database.get();
}
/**
@@ -168,8 +167,6 @@ struct Instance final
#endif
void BeginShutdownUpdate() noexcept;
void FinishShutdownUpdate() noexcept;
void ShutdownDatabase() noexcept;
#ifdef ENABLE_CURL
void LookupRemoteTag(const char *uri) noexcept;

View File

@@ -109,7 +109,7 @@ parse_log_level(const char *value, int line)
#endif
void
log_early_init(bool verbose)
log_early_init(bool verbose) noexcept
{
#ifdef ANDROID
(void)verbose;
@@ -171,7 +171,7 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
#ifndef ANDROID
static void
close_log_files(void)
close_log_files() noexcept
{
#ifdef HAVE_SYSLOG
LogFinishSysLog();
@@ -181,7 +181,7 @@ close_log_files(void)
#endif
void
log_deinit(void)
log_deinit() noexcept
{
#ifndef ANDROID
close_log_files();
@@ -213,7 +213,8 @@ void setup_log_output()
#endif
}
int cycle_log_files(void)
int
cycle_log_files() noexcept
{
#ifdef ANDROID
return 0;

View File

@@ -31,7 +31,7 @@ struct ConfigData;
* @param verbose true when the program is started with --verbose
*/
void
log_early_init(bool verbose);
log_early_init(bool verbose) noexcept;
/**
* Throws #std::runtime_error on error.
@@ -40,12 +40,12 @@ void
log_init(const ConfigData &config, bool verbose, bool use_stdout);
void
log_deinit();
log_deinit() noexcept;
void
setup_log_output();
int
cycle_log_files();
cycle_log_files() noexcept;
#endif /* LOG_H */

View File

@@ -185,19 +185,16 @@ InitStorage(const ConfigData &config, EventLoop &event_loop)
static bool
glue_db_init_and_load(const ConfigData &config)
{
instance->database =
CreateConfiguredDatabase(config, instance->event_loop,
instance->io_thread.GetEventLoop(),
*instance);
if (instance->database == nullptr)
auto db = CreateConfiguredDatabase(config, instance->event_loop,
instance->io_thread.GetEventLoop(),
*instance);
if (!db)
return true;
if (instance->database->GetPlugin().RequireStorage()) {
if (db->GetPlugin().RequireStorage()) {
InitStorage(config, instance->io_thread.GetEventLoop());
if (instance->storage == nullptr) {
delete instance->database;
instance->database = nullptr;
LogDefault(config_domain,
"Found database setting without "
"music_directory - disabling database");
@@ -211,22 +208,24 @@ glue_db_init_and_load(const ConfigData &config)
}
try {
instance->database->Open();
db->Open();
} catch (...) {
std::throw_with_nested(std::runtime_error("Failed to open database plugin"));
}
auto *db = dynamic_cast<SimpleDatabase *>(instance->database);
if (db == nullptr)
instance->database = std::move(db);
auto *sdb = dynamic_cast<SimpleDatabase *>(instance->database.get());
if (sdb == nullptr)
return true;
instance->update = new UpdateService(config,
instance->event_loop, *db,
instance->event_loop, *sdb,
static_cast<CompositeStorage &>(*instance->storage),
*instance);
/* run database update after daemonization? */
return db->FileExists();
return sdb->FileExists();
}
static bool
@@ -351,27 +350,6 @@ Instance::BeginShutdownUpdate() noexcept
#endif
}
inline void
Instance::FinishShutdownUpdate() noexcept
{
#ifdef ENABLE_DATABASE
delete update;
#endif
}
inline void
Instance::ShutdownDatabase() noexcept
{
#ifdef ENABLE_DATABASE
if (instance->database != nullptr) {
instance->database->Close();
delete instance->database;
}
delete instance->storage;
#endif
}
inline void
Instance::BeginShutdownPartitions() noexcept
{
@@ -381,12 +359,6 @@ Instance::BeginShutdownPartitions() noexcept
}
}
inline void
Instance::FinishShutdownPartitions() noexcept
{
partitions.clear();
}
void
Instance::OnIdle(unsigned flags)
{
@@ -523,18 +495,19 @@ static int
mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
{
ConfigureFS(raw_config);
AtScopeExit() { DeinitFS(); };
glue_mapper_init(raw_config);
initPermissions(raw_config);
spl_global_init(raw_config);
#ifdef ENABLE_ARCHIVE
archive_plugin_init_all();
const ScopeArchivePluginsInit archive_plugins_init;
#endif
pcm_convert_global_init(raw_config);
decoder_plugin_init_all(raw_config);
const ScopeDecoderPluginsInit decoder_plugins_init(raw_config);
#ifdef ENABLE_DATABASE
const bool create_db = InitDatabaseAndStorage(raw_config);
@@ -553,9 +526,9 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
}
client_manager_init(raw_config);
input_stream_global_init(raw_config,
instance->io_thread.GetEventLoop());
playlist_list_global_init(raw_config);
const ScopeInputPluginsInit input_plugins_init(raw_config,
instance->io_thread.GetEventLoop());
const ScopePlaylistPluginsInit playlist_plugins_init(raw_config);
#ifdef ENABLE_DAEMON
daemonize_commit();
@@ -564,7 +537,7 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
#ifndef ANDROID
setup_log_output();
SignalHandlersInit(instance->event_loop);
const ScopeSignalHandlersInit signal_handlers_init(instance->event_loop);
#endif
instance->io_thread.Start();
@@ -652,34 +625,10 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
}
#endif
instance->FinishShutdownUpdate();
instance->ShutdownDatabase();
#ifdef ENABLE_SQLITE
sticker_global_finish();
#endif
playlist_list_global_finish();
input_stream_global_finish();
#ifdef ENABLE_DATABASE
mapper_finish();
#endif
DeinitFS();
instance->FinishShutdownPartitions();
command_finish();
decoder_plugin_deinit_all();
#ifdef ENABLE_ARCHIVE
archive_plugin_deinit_all();
#endif
instance->rtio_thread.Stop();
instance->io_thread.Stop();
#ifndef ANDROID
SignalHandlersFinish();
#endif
return EXIT_SUCCESS;
}

View File

@@ -58,11 +58,6 @@ mapper_init(AllocatedPath &&_playlist_dir)
mapper_set_playlist_dir(std::move(_playlist_dir));
}
void
mapper_finish() noexcept
{
}
#ifdef ENABLE_DATABASE
AllocatedPath

View File

@@ -37,9 +37,6 @@ class AllocatedPath;
void
mapper_init(AllocatedPath &&playlist_dir);
void
mapper_finish() noexcept;
#ifdef ENABLE_DATABASE
/**

View File

@@ -119,7 +119,7 @@ try {
TextFile file(config.path);
#ifdef ENABLE_DATABASE
const SongLoader song_loader(partition.instance.database,
const SongLoader song_loader(partition.instance.GetDatabase(),
partition.instance.storage);
#else
const SongLoader song_loader(nullptr, nullptr);

View File

@@ -124,7 +124,7 @@ stats_print(Response &r, const Partition &partition)
std::lround(partition.pc.GetTotalPlayTime().count()));
#ifdef ENABLE_DATABASE
const Database *db = partition.instance.database;
const Database *db = partition.instance.GetDatabase();
if (db != nullptr)
db_stats_print(r, *db);
#endif

View File

@@ -25,8 +25,9 @@
void
tag_print_types(Response &r) noexcept
{
const auto tag_mask = global_tag_mask & r.GetTagMask();
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++)
if (IsTagEnabled(i))
if (tag_mask.Test(TagType(i)))
r.Format("tagtype: %s\n", tag_item_names[i]);
}

View File

@@ -49,7 +49,7 @@ static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1];
if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins])
const ArchivePlugin *
archive_plugin_from_suffix(const char *suffix)
archive_plugin_from_suffix(const char *suffix) noexcept
{
if (suffix == nullptr)
return nullptr;
@@ -63,7 +63,7 @@ archive_plugin_from_suffix(const char *suffix)
}
const ArchivePlugin *
archive_plugin_from_name(const char *name)
archive_plugin_from_name(const char *name) noexcept
{
archive_plugins_for_each_enabled(plugin)
if (strcmp(plugin->name, name) == 0)
@@ -81,7 +81,8 @@ void archive_plugin_init_all(void)
}
}
void archive_plugin_deinit_all(void)
void
archive_plugin_deinit_all() noexcept
{
archive_plugins_for_each_enabled(plugin)
if (plugin->finish != nullptr)

View File

@@ -33,10 +33,10 @@ extern const ArchivePlugin *const archive_plugins[];
/* interface for using plugins */
const ArchivePlugin *
archive_plugin_from_suffix(const char *suffix);
archive_plugin_from_suffix(const char *suffix) noexcept;
const ArchivePlugin *
archive_plugin_from_name(const char *name);
archive_plugin_from_name(const char *name) noexcept;
/* this is where we "load" all the "plugins" ;-) */
void
@@ -44,6 +44,17 @@ archive_plugin_init_all();
/* this is where we "unload" all the "plugins" */
void
archive_plugin_deinit_all();
archive_plugin_deinit_all() noexcept;
class ScopeArchivePluginsInit {
public:
ScopeArchivePluginsInit() {
archive_plugin_init_all();
}
~ScopeArchivePluginsInit() noexcept {
archive_plugin_deinit_all();
}
};
#endif

View File

@@ -285,11 +285,6 @@ command_init()
#endif
}
void
command_finish()
{
}
static const struct command *
command_lookup(const char *name)
{

View File

@@ -37,9 +37,11 @@
#include "fs/FileInfo.hxx"
#include "fs/DirectoryReader.hxx"
#include "input/InputStream.hxx"
#include "input/Error.hxx"
#include "LocateUri.hxx"
#include "TimePrint.hxx"
#include "thread/Mutex.hxx"
#include "Log.hxx"
#include <assert.h>
#include <inttypes.h> /* for PRIu64 */
@@ -256,7 +258,11 @@ find_stream_art(const char *directory, Mutex &mutex)
try {
return InputStream::OpenReady(art_file.c_str(), mutex);
} catch (const std::exception &e) {}
} catch (...) {
auto e = std::current_exception();
if (!IsFileNotFound(e))
LogError(e);
}
}
return nullptr;
}
@@ -285,8 +291,11 @@ read_stream_art(Response &r, const char *uri, size_t offset)
uint8_t buffer[CHUNK_SIZE];
size_t read_size;
is->Seek(offset);
read_size = is->Read(&buffer, CHUNK_SIZE);
{
const std::lock_guard<Mutex> protect(mutex);
is->Seek(offset);
read_size = is->Read(&buffer, CHUNK_SIZE);
}
r.Format("size: %" PRIoffset "\n"
"binary: %u\n",

View File

@@ -294,7 +294,7 @@ handle_update(Client &client, Request args, Response &r, bool discard)
if (update != nullptr)
return handle_update(r, *update, path, discard);
Database *db = client.GetInstance().database;
Database *db = client.GetInstance().GetDatabase();
if (db != nullptr)
return handle_update(r, *db, path, discard);
#else

View File

@@ -209,7 +209,7 @@ handle_mount(Client &client, Request args, Response &r)
instance.EmitIdle(IDLE_MOUNT);
#ifdef ENABLE_DATABASE
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
try {
db->Mount(local_uri, remote_uri);
} catch (...) {
@@ -253,7 +253,7 @@ handle_unmount(Client &client, Request args, Response &r)
destroy here */
instance.update->CancelMount(local_uri);
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
if (db->Unmount(local_uri))
// TODO: call Instance::OnDatabaseModified()?
instance.EmitIdle(IDLE_DATABASE);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "Configured.hxx"
#include "DatabaseGlue.hxx"
#include "Interface.hxx"
#include "config/Data.hxx"
#include "config/Param.hxx"
#include "config/Block.hxx"
@@ -26,7 +27,7 @@
#include "fs/StandardDirectory.hxx"
#include "util/RuntimeError.hxx"
Database *
DatabasePtr
CreateConfiguredDatabase(const ConfigData &config,
EventLoop &main_event_loop, EventLoop &io_event_loop,
DatabaseListener &listener)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,10 +20,11 @@
#ifndef MPD_DB_CONFIG_HXX
#define MPD_DB_CONFIG_HXX
#include "Ptr.hxx"
struct ConfigData;
class EventLoop;
class DatabaseListener;
class Database;
/**
* Read database configuration settings and create a #Database
@@ -32,7 +33,7 @@ class Database;
*
* Throws #std::runtime_error on error.
*/
Database *
DatabasePtr
CreateConfiguredDatabase(const ConfigData &config,
EventLoop &main_event_loop, EventLoop &io_event_loop,
DatabaseListener &listener);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,14 @@
*/
#include "DatabaseGlue.hxx"
#include "Interface.hxx"
#include "Registry.hxx"
#include "DatabaseError.hxx"
#include "util/RuntimeError.hxx"
#include "config/Block.hxx"
#include "DatabasePlugin.hxx"
Database *
DatabasePtr
DatabaseGlobalInit(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,12 +20,11 @@
#ifndef MPD_DATABASE_GLUE_HXX
#define MPD_DATABASE_GLUE_HXX
#include "util/Compiler.h"
#include "Ptr.hxx"
struct ConfigBlock;
class EventLoop;
class DatabaseListener;
class Database;
/**
* Initialize the database library.
@@ -34,7 +33,7 @@ class Database;
*
* @param block the database configuration block
*/
Database *
DatabasePtr
DatabaseGlobalInit(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -26,10 +26,11 @@
#ifndef MPD_DATABASE_PLUGIN_HXX
#define MPD_DATABASE_PLUGIN_HXX
#include "Ptr.hxx"
struct ConfigBlock;
class EventLoop;
class DatabaseListener;
class Database;
struct DatabasePlugin {
/**
@@ -52,10 +53,10 @@ struct DatabasePlugin {
* @param io_event_loop the #EventLoop running on the
* #EventThread, i.e. the one used for background I/O
*/
Database *(*create)(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
DatabasePtr (*create)(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
constexpr bool RequireStorage() const {
return flags & FLAG_REQUIRE_STORAGE;

29
src/db/Ptr.hxx Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_DATABASE_PTR_HXX
#define MPD_DATABASE_PTR_HXX
#include <memory>
class Database;
typedef std::unique_ptr<Database> DatabasePtr;
#endif

View File

@@ -18,11 +18,11 @@
*/
#include "VHelper.hxx"
#include "song/DetachedSong.hxx"
#include "song/LightSong.hxx"
#include "song/Filter.hxx"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
DatabaseVisitorHelper::DatabaseVisitorHelper(const DatabaseSelection &_selection,

View File

@@ -22,6 +22,7 @@
#include "Visitor.hxx"
#include "Selection.hxx"
#include "song/DetachedSong.hxx"
#include <vector>

View File

@@ -112,10 +112,10 @@ public:
ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
const ConfigBlock &block);
static Database *Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
static DatabasePtr Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
void Open() override;
void Close() noexcept override;
@@ -440,12 +440,12 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
{
}
Database *
DatabasePtr
ProxyDatabase::Create(EventLoop &loop, EventLoop &,
DatabaseListener &listener,
const ConfigBlock &block)
{
return new ProxyDatabase(loop, listener, block);
return std::make_unique<ProxyDatabase>(loop, listener, block);
}
void

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -37,22 +37,25 @@
#include <string.h>
#include <stdlib.h>
Directory::Directory(std::string &&_path_utf8, Directory *_parent)
Directory::Directory(std::string &&_path_utf8, Directory *_parent) noexcept
:parent(_parent),
path(std::move(_path_utf8))
{
}
Directory::~Directory()
Directory::~Directory() noexcept
{
delete mounted_database;
if (mounted_database != nullptr) {
mounted_database->Close();
mounted_database.reset();
}
songs.clear_and_dispose(Song::Disposer());
children.clear_and_dispose(DeleteDisposer());
}
void
Directory::Delete()
Directory::Delete() noexcept
{
assert(holding_db_lock());
assert(parent != nullptr);
@@ -70,7 +73,7 @@ Directory::GetName() const noexcept
}
Directory *
Directory::CreateChild(const char *name_utf8)
Directory::CreateChild(const char *name_utf8) noexcept
{
assert(holding_db_lock());
assert(name_utf8 != nullptr);
@@ -160,7 +163,7 @@ Directory::LookupDirectory(const char *uri) noexcept
}
void
Directory::AddSong(Song *song)
Directory::AddSong(Song *song) noexcept
{
assert(holding_db_lock());
assert(song != nullptr);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "util/Compiler.h"
#include "db/Visitor.hxx"
#include "db/PlaylistVector.hxx"
#include "db/Ptr.hxx"
#include "Song.hxx"
#include <boost/intrusive/list.hpp>
@@ -43,7 +44,6 @@ static constexpr unsigned DEVICE_INARCHIVE = -1;
static constexpr unsigned DEVICE_CONTAINER = -2;
class SongFilter;
class Database;
struct Directory {
static constexpr auto link_mode = boost::intrusive::normal_link;
@@ -96,21 +96,21 @@ struct Directory {
* If this is not nullptr, then this directory does not really
* exist, but is a mount point for another #Database.
*/
Database *mounted_database = nullptr;
DatabasePtr mounted_database;
public:
Directory(std::string &&_path_utf8, Directory *_parent);
~Directory();
Directory(std::string &&_path_utf8, Directory *_parent) noexcept;
~Directory() noexcept;
/**
* Create a new root #Directory object.
*/
gcc_malloc gcc_returns_nonnull
static Directory *NewRoot() {
static Directory *NewRoot() noexcept {
return new Directory(std::string(), nullptr);
}
bool IsMount() const {
bool IsMount() const noexcept {
return mounted_database != nullptr;
}
@@ -120,7 +120,7 @@ public:
*
* Caller must lock the #db_mutex.
*/
void Delete();
void Delete() noexcept;
/**
* Create a new #Directory object as a child of the given one.
@@ -129,7 +129,7 @@ public:
*
* @param name_utf8 the UTF-8 encoded name of the new sub directory
*/
Directory *CreateChild(const char *name_utf8);
Directory *CreateChild(const char *name_utf8) noexcept;
/**
* Caller must lock the #db_mutex.
@@ -149,7 +149,7 @@ public:
*
* Caller must lock the #db_mutex.
*/
Directory *MakeChild(const char *name_utf8) {
Directory *MakeChild(const char *name_utf8) noexcept {
Directory *child = FindChild(name_utf8);
if (child == nullptr)
child = CreateChild(name_utf8);
@@ -242,7 +242,7 @@ public:
* Add a song object to this directory. Its "parent" attribute must
* be set already.
*/
void AddSong(Song *song);
void AddSong(Song *song) noexcept;
/**
* Remove a song object from this directory (which effectively

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -60,8 +60,7 @@ inline SimpleDatabase::SimpleDatabase(const ConfigBlock &block)
#ifdef ENABLE_ZLIB
compress(block.GetBlockValue("compress", true)),
#endif
cache_path(block.GetPath("cache_directory")),
prefixed_light_song(nullptr)
cache_path(block.GetPath("cache_directory"))
{
if (path.IsNull())
throw std::runtime_error("No \"path\" parameter specified");
@@ -84,12 +83,12 @@ inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path,
prefixed_light_song(nullptr) {
}
Database *
DatabasePtr
SimpleDatabase::Create(EventLoop &, EventLoop &,
gcc_unused DatabaseListener &listener,
const ConfigBlock &block)
{
return new SimpleDatabase(block);
return std::make_unique<SimpleDatabase>(block);
}
void
@@ -219,6 +218,7 @@ SimpleDatabase::GetSong(const char *uri) const
prefixed_light_song =
new PrefixedLightSong(*song, r.directory->GetPath());
r.directory->mounted_database->ReturnSong(song);
return prefixed_light_song;
}
@@ -251,7 +251,7 @@ void
SimpleDatabase::ReturnSong(gcc_unused const LightSong *song) const noexcept
{
assert(song != nullptr);
assert(song == &light_song.Get() || song == prefixed_light_song);
assert(song == prefixed_light_song || song == &light_song.Get());
if (prefixed_light_song != nullptr) {
delete prefixed_light_song;
@@ -390,13 +390,13 @@ SimpleDatabase::Save()
}
void
SimpleDatabase::Mount(const char *uri, Database *db)
SimpleDatabase::Mount(const char *uri, DatabasePtr db)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(uri != nullptr);
assert(db != nullptr);
#endif
assert(db != nullptr);
assert(*uri != 0);
ScopeDatabaseLock protect;
@@ -411,7 +411,7 @@ SimpleDatabase::Mount(const char *uri, Database *db)
"Parent not found");
Directory *mnt = r.directory->CreateChild(r.uri);
mnt->mounted_database = db;
mnt->mounted_database = std::move(db);
}
static constexpr bool
@@ -441,27 +441,21 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri)
#ifndef ENABLE_ZLIB
constexpr bool compress = false;
#endif
auto db = new SimpleDatabase(cache_path / name_fs,
compress);
try {
db->Open();
} catch (...) {
delete db;
throw;
}
auto db = std::make_unique<SimpleDatabase>(cache_path / name_fs,
compress);
db->Open();
// TODO: update the new database instance?
try {
Mount(local_uri, db);
Mount(local_uri, std::move(db));
} catch (...) {
db->Close();
delete db;
throw;
}
}
inline Database *
inline DatabasePtr
SimpleDatabase::LockUmountSteal(const char *uri) noexcept
{
ScopeDatabaseLock protect;
@@ -470,8 +464,7 @@ SimpleDatabase::LockUmountSteal(const char *uri) noexcept
if (r.uri != nullptr || !r.directory->IsMount())
return nullptr;
Database *db = r.directory->mounted_database;
r.directory->mounted_database = nullptr;
auto db = std::move(r.directory->mounted_database);
r.directory->Delete();
return db;
@@ -480,12 +473,11 @@ SimpleDatabase::LockUmountSteal(const char *uri) noexcept
bool
SimpleDatabase::Unmount(const char *uri) noexcept
{
Database *db = LockUmountSteal(uri);
auto db = LockUmountSteal(uri);
if (db == nullptr)
return false;
db->Close();
delete db;
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
#include "db/Interface.hxx"
#include "db/Ptr.hxx"
#include "fs/AllocatedPath.hxx"
#include "song/LightSong.hxx"
#include "util/Manual.hxx"
@@ -57,7 +58,7 @@ class SimpleDatabase : public Database {
* A buffer for GetSong() when prefixing the #LightSong
* instance from a mounted #Database.
*/
mutable PrefixedLightSong *prefixed_light_song;
mutable PrefixedLightSong *prefixed_light_song = nullptr;
/**
* A buffer for GetSong().
@@ -68,15 +69,14 @@ class SimpleDatabase : public Database {
mutable unsigned borrowed_song_count;
#endif
public:
SimpleDatabase(const ConfigBlock &block);
SimpleDatabase(AllocatedPath &&_path, bool _compress) noexcept;
public:
static Database *Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
static DatabasePtr Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block);
gcc_pure
Directory &GetRoot() noexcept {
@@ -99,7 +99,7 @@ public:
* success, this object gains ownership of the given #Database
*/
gcc_nonnull_all
void Mount(const char *uri, Database *db);
void Mount(const char *uri, DatabasePtr db);
/**
* Throws #std::runtime_error on error.
@@ -142,7 +142,7 @@ private:
*/
void Load();
Database *LockUmountSteal(const char *uri) noexcept;
DatabasePtr LockUmountSteal(const char *uri) noexcept;
};
extern const DatabasePlugin simple_db_plugin;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -82,10 +82,10 @@ public:
:Database(upnp_db_plugin),
event_loop(_event_loop) {}
static Database *Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block) noexcept;
static DatabasePtr Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
DatabaseListener &listener,
const ConfigBlock &block) noexcept;
void Open() override;
void Close() noexcept override;
@@ -146,12 +146,12 @@ private:
const UPnPDirObject& dirent) const;
};
Database *
DatabasePtr
UpnpDatabase::Create(EventLoop &, EventLoop &io_event_loop,
gcc_unused DatabaseListener &listener,
const ConfigBlock &) noexcept
{
return new UpnpDatabase(io_event_loop);
return std::make_unique<UpnpDatabase>(io_event_loop);
}
void

View File

@@ -94,7 +94,7 @@ UpdateService::CancelMount(const char *uri)
cancel_current = next.IsDefined() && next.storage == storage2;
}
if (auto *db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database)) {
if (auto *db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database.get())) {
queue.Erase(*db2);
cancel_current |= next.IsDefined() && next.db == db2;
}
@@ -188,7 +188,7 @@ UpdateService::Enqueue(const char *path, bool discard)
/* follow the mountpoint, update the mounted
database */
db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database);
db2 = dynamic_cast<SimpleDatabase *>(lr.directory->mounted_database.get());
if (db2 == nullptr)
throw std::runtime_error("Cannot update this type of database");

View File

@@ -152,7 +152,8 @@ decoder_plugin_init_all(const ConfigData &config)
}
}
void decoder_plugin_deinit_all(void)
void
decoder_plugin_deinit_all() noexcept
{
decoder_plugins_for_each_enabled([=](const DecoderPlugin &plugin){
plugin.Finish();

View File

@@ -40,11 +40,22 @@ decoder_plugin_init_all(const ConfigData &config);
/* this is where we "unload" all the "plugins" */
void
decoder_plugin_deinit_all();
decoder_plugin_deinit_all() noexcept;
class ScopeDecoderPluginsInit {
public:
explicit ScopeDecoderPluginsInit(const ConfigData &config) {
decoder_plugin_init_all(config);
}
~ScopeDecoderPluginsInit() noexcept {
decoder_plugin_deinit_all();
}
};
template<typename F>
static inline const DecoderPlugin *
decoder_plugins_find(F f)
decoder_plugins_find(F f) noexcept
{
for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i)
if (decoder_plugins_enabled[i] && f(*decoder_plugins[i]))

View File

@@ -33,6 +33,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
static constexpr Domain audiofile_domain("audiofile");

View File

@@ -36,8 +36,9 @@ if flac_dep.found()
]
endif
conf.set('ENABLE_VORBIS_DECODER', libvorbis_dep.found())
if libvorbis_dep.found()
conf.set('ENABLE_VORBIS_DECODER', libvorbis_dep.found() or libvorbisidec_dep.found())
conf.set('HAVE_TREMOR', libvorbisidec_dep.found())
if libvorbis_dep.found() or libvorbisidec_dep.found()
decoder_plugins_sources += [
'VorbisDecoderPlugin.cxx',
'VorbisDomain.cxx',
@@ -181,6 +182,7 @@ decoder_plugins = static_library(
libsidplay_dep,
libsndfile_dep,
libvorbis_dep,
libvorbisidec_dep,
ogg_dep,
wavpack_dep,
wildmidi_dep,

View File

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

View File

@@ -25,7 +25,13 @@
EventLoop::EventLoop(ThreadId _thread)
:SocketMonitor(*this),
quit(false), dead(false),
/* if this instance is hosted by an EventThread (no ThreadId
known yet) then we're not yet alive until the thread is
started; for the main EventLoop instance, we assume it's
already alive, because nobody but EventThread will call
SetAlive() */
alive(!_thread.IsNull()),
quit(false),
thread(_thread)
{
SocketMonitor::Open(SocketDescriptor(wake_fd.Get()));
@@ -143,12 +149,11 @@ EventLoop::Run() noexcept
assert(IsInside());
assert(!quit);
assert(!dead);
assert(alive);
assert(busy);
SocketMonitor::Schedule(SocketMonitor::READ);
AtScopeExit(this) {
dead = true;
SocketMonitor::Cancel();
};
@@ -215,7 +220,6 @@ EventLoop::Run() noexcept
} while (!quit);
#ifndef NDEBUG
assert(!dead);
assert(busy);
assert(thread.IsInside());
#endif

View File

@@ -85,12 +85,15 @@ class EventLoop final : SocketMonitor
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
std::atomic_bool quit;
/**
* If this is true, then Run() has returned.
* Is this #EventLoop alive, i.e. can events be scheduled?
* This is used by BlockingCall() to determine whether
* schedule in the #EventThread or to call directly (if
* there's no #EventThread yet/anymore).
*/
std::atomic_bool dead;
bool alive;
std::atomic_bool quit;
/**
* True when the object has been modified and another check is
@@ -207,9 +210,12 @@ private:
bool OnSocketReady(unsigned flags) noexcept override;
public:
gcc_pure
bool IsDead() const noexcept {
return dead;
void SetAlive(bool _alive) noexcept {
alive = _alive;
}
bool IsAlive() const noexcept {
return alive;
}
/**

View File

@@ -26,8 +26,11 @@
void
EventThread::Start()
{
assert(!event_loop.IsAlive());
assert(!thread.IsDefined());
event_loop.SetAlive(true);
thread.Start();
}
@@ -35,6 +38,9 @@ void
EventThread::Stop() noexcept
{
if (thread.IsDefined()) {
assert(event_loop.IsAlive());
event_loop.SetAlive(false);
event_loop.Break();
thread.Join();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -88,4 +88,13 @@ private:
#endif
};
template<typename F>
void
WithBufferedOutputStream(OutputStream &os, F &&f)
{
BufferedOutputStream bos(os);
f(bos);
bos.Flush();
}
#endif

View File

@@ -31,8 +31,33 @@
#include "system/Error.hxx"
#include "util/StringFormat.hxx"
#ifdef __linux__
#include <fcntl.h>
#endif
#ifdef __linux__
FileOutputStream::FileOutputStream(FileDescriptor _directory_fd,
Path _path, Mode _mode)
:path(_path),
directory_fd(_directory_fd),
mode(_mode)
{
Open();
}
#endif
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
:path(_path), mode(_mode)
:path(_path),
#ifdef __linux__
directory_fd(AT_FDCWD),
#endif
mode(_mode)
{
Open();
}
inline void
FileOutputStream::Open()
{
switch (mode) {
case Mode::CREATE:
@@ -149,8 +174,12 @@ FileOutputStream::Cancel() noexcept
* Open a file using Linux's O_TMPFILE for writing the given file.
*/
static bool
OpenTempFile(FileDescriptor &fd, Path path)
OpenTempFile(FileDescriptor directory_fd,
FileDescriptor &fd, Path path)
{
if (directory_fd != FileDescriptor(AT_FDCWD))
return fd.Open(directory_fd, ".", O_TMPFILE|O_WRONLY, 0666);
const auto directory = path.GetDirectoryName();
if (directory.IsNull())
return false;
@@ -165,11 +194,15 @@ FileOutputStream::OpenCreate(bool visible)
{
#ifdef HAVE_O_TMPFILE
/* try Linux's O_TMPFILE first */
is_tmpfile = !visible && OpenTempFile(fd, GetPath());
is_tmpfile = !visible && OpenTempFile(directory_fd, fd, GetPath());
if (!is_tmpfile) {
#endif
/* fall back to plain POSIX */
if (!fd.Open(GetPath().c_str(),
if (!fd.Open(
#ifdef __linux__
directory_fd,
#endif
GetPath().c_str(),
O_WRONLY|O_CREAT|O_TRUNC,
0666))
throw FormatErrno("Failed to create %s",
@@ -188,7 +221,11 @@ FileOutputStream::OpenAppend(bool create)
if (create)
flags |= O_CREAT;
if (!fd.Open(path.c_str(), flags))
if (!fd.Open(
#ifdef __linux__
directory_fd,
#endif
path.c_str(), flags))
throw FormatErrno("Failed to append to %s",
path.c_str());
}
@@ -219,12 +256,12 @@ FileOutputStream::Commit()
#ifdef HAVE_O_TMPFILE
if (is_tmpfile) {
unlink(GetPath().c_str());
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
/* hard-link the temporary file to the final path */
if (linkat(AT_FDCWD,
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
AT_FDCWD, path.c_str(),
directory_fd.Get(), path.c_str(),
AT_SYMLINK_FOLLOW) < 0)
throw FormatErrno("Failed to commit %s",
path.c_str());
@@ -253,7 +290,11 @@ FileOutputStream::Cancel() noexcept
#ifdef HAVE_O_TMPFILE
if (!is_tmpfile)
#endif
unlink(GetPath().c_str());
#ifdef __linux__
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
#else
unlink(GetPath().c_str());
#endif
break;
case Mode::CREATE_VISIBLE:

View File

@@ -59,6 +59,10 @@ class Path;
class FileOutputStream final : public OutputStream {
const AllocatedPath path;
#ifdef __linux__
const FileDescriptor directory_fd;
#endif
#ifdef _WIN32
HANDLE handle = INVALID_HANDLE_VALUE;
#else
@@ -108,6 +112,11 @@ private:
public:
explicit FileOutputStream(Path _path, Mode _mode=Mode::CREATE);
#ifdef __linux__
FileOutputStream(FileDescriptor _directory_fd, Path _path,
Mode _mode=Mode::CREATE);
#endif
~FileOutputStream() noexcept {
if (IsDefined())
Cancel();
@@ -133,6 +142,7 @@ public:
private:
void OpenCreate(bool visible);
void OpenAppend(bool create);
void Open();
bool Close() noexcept {
assert(IsDefined());

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2014-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,8 +27,8 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MPD_OUTPUT_STREAM_HXX
#define MPD_OUTPUT_STREAM_HXX
#ifndef OUTPUT_STREAM_HXX
#define OUTPUT_STREAM_HXX
#include <stddef.h>

View File

@@ -39,7 +39,7 @@ class StdioOutputStream final : public OutputStream {
FILE *const file;
public:
explicit StdioOutputStream(FILE *_file):file(_file) {}
explicit StdioOutputStream(FILE *_file) noexcept:file(_file) {}
/* virtual methods from class OutputStream */
void Write(const void *data, size_t size) override {

View File

@@ -66,6 +66,11 @@ BufferedInputStream::Check()
void
BufferedInputStream::Seek(offset_type new_offset)
{
if (new_offset >= size) {
offset = size;
return;
}
auto r = buffer.Read(new_offset);
if (r.HasData()) {
/* nice, we already have some data at the desired

View File

@@ -35,4 +35,16 @@ input_stream_global_init(const ConfigData &config, EventLoop &event_loop);
void
input_stream_global_finish() noexcept;
class ScopeInputPluginsInit {
public:
ScopeInputPluginsInit(const ConfigData &config,
EventLoop &event_loop) {
input_stream_global_init(config, event_loop);
}
~ScopeInputPluginsInit() noexcept {
input_stream_global_finish();
}
};
#endif

View File

@@ -23,6 +23,7 @@
#include "config.h"
#include "CdioParanoiaInputPlugin.hxx"
#include "lib/cdio/Paranoia.hxx"
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "util/TruncateString.hxx"
@@ -41,18 +42,12 @@
#include <stdlib.h>
#include <assert.h>
#ifdef HAVE_CDIO_PARANOIA_PARANOIA_H
#include <cdio/paranoia/paranoia.h>
#else
#include <cdio/paranoia.h>
#endif
#include <cdio/cd_types.h>
class CdioParanoiaInputStream final : public InputStream {
cdrom_drive_t *const drv;
CdIo_t *const cdio;
cdrom_paranoia_t *const para;
CdromParanoia para;
const lsn_t lsn_from, lsn_to;
int lsn_relofs;
@@ -66,18 +61,17 @@ class CdioParanoiaInputStream final : public InputStream {
bool reverse_endian,
lsn_t _lsn_from, lsn_t _lsn_to)
:InputStream(_uri, _mutex),
drv(_drv), cdio(_cdio), para(cdio_paranoia_init(drv)),
drv(_drv), cdio(_cdio), para(drv),
lsn_from(_lsn_from), lsn_to(_lsn_to),
lsn_relofs(0),
buffer_lsn(-1)
{
/* Set reading mode for full paranoia, but allow
skipping sectors. */
paranoia_modeset(para,
PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
para.SetMode(PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
/* seek to beginning of the track */
cdio_paranoia_seek(para, lsn_from, SEEK_SET);
para.Seek(lsn_from);
seekable = true;
size = (lsn_to - lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;
@@ -90,7 +84,7 @@ class CdioParanoiaInputStream final : public InputStream {
}
~CdioParanoiaInputStream() {
cdio_paranoia_free(para);
para = {};
cdio_cddap_close_no_free_cdio(drv);
cdio_destroy(cdio);
}
@@ -208,10 +202,10 @@ input_cdio_open(const char *uri,
throw std::runtime_error("Unable to identify audio CD disc.");
}
cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
cdio_cddap_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
if (speed > 0) {
FormatDebug(cdio_domain,"Attempting to set CD speed to %dx",speed);
cdda_speed_set(drv,speed);
cdio_cddap_speed_set(drv,speed);
}
if (0 != cdio_cddap_open(drv)) {
@@ -277,7 +271,7 @@ CdioParanoiaInputStream::Seek(offset_type new_offset)
{
const ScopeUnlock unlock(mutex);
cdio_paranoia_seek(para, lsn_from + lsn_relofs, SEEK_SET);
para.Seek(lsn_from + lsn_relofs);
}
}
@@ -285,56 +279,53 @@ size_t
CdioParanoiaInputStream::Read(void *ptr, size_t length)
{
size_t nbytes = 0;
int diff;
size_t len, maxwrite;
int16_t *rbuf;
char *s_err, *s_mess;
char *wptr = (char *) ptr;
while (length > 0) {
/* end of track ? */
if (lsn_from + lsn_relofs > lsn_to)
break;
//current sector was changed ?
const int16_t *rbuf;
if (lsn_relofs != buffer_lsn) {
const ScopeUnlock unlock(mutex);
rbuf = cdio_paranoia_read(para, nullptr);
try {
rbuf = para.Read().data;
} catch (...) {
char *s_err = cdio_cddap_errors(drv);
if (s_err) {
FormatError(cdio_domain,
"paranoia_read: %s", s_err);
#if LIBCDIO_VERSION_NUM >= 90
cdio_cddap_free_messages(s_err);
#else
free(s_err);
#endif
}
s_err = cdda_errors(drv);
if (s_err) {
FormatError(cdio_domain,
"paranoia_read: %s", s_err);
free(s_err);
throw;
}
s_mess = cdda_messages(drv);
if (s_mess) {
free(s_mess);
}
if (!rbuf)
throw std::runtime_error("paranoia read error");
//store current buffer
memcpy(buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
buffer_lsn = lsn_relofs;
} else {
//use cached sector
rbuf = (int16_t *)buffer;
rbuf = (const int16_t *)buffer;
}
//correct offset
diff = offset - lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
const int diff = offset - lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
assert(diff >= 0 && diff < CDIO_CD_FRAMESIZE_RAW);
maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
len = (length < maxwrite? length : maxwrite);
const size_t maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
const size_t len = (length < maxwrite? length : maxwrite);
//skip diff bytes from this lsn
memcpy(wptr, ((char*)rbuf) + diff, len);
memcpy(wptr, ((const char *)rbuf) + diff, len);
//update pointer
wptr += len;
nbytes += len;

View File

@@ -10,10 +10,6 @@ libcdio_paranoia_dep = dependency('libcdio_paranoia', version: '>= 0.4', require
conf.set('ENABLE_CDIO_PARANOIA', libcdio_paranoia_dep.found())
if libcdio_paranoia_dep.found()
input_plugins_sources += 'CdioParanoiaInputPlugin.cxx'
conf.set('HAVE_CDIO_PARANOIA_PARANOIA_H',
compiler.has_header('cdio/paranoia/paranoia.h',
dependencies: libcdio_paranoia_dep))
endif
if curl_dep.found()

164
src/lib/cdio/Paranoia.hxx Normal file
View File

@@ -0,0 +1,164 @@
/*
* Copyright 2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CDIO_PARANOIA_HXX
#define CDIO_PARANOIA_HXX
#include "util/ConstBuffer.hxx"
#include "util/Compiler.h"
#include <cdio/version.h>
#if LIBCDIO_VERSION_NUM >= 90
#include <cdio/paranoia/paranoia.h>
#else
#include <cdio/paranoia.h>
#endif
#include <stdexcept>
#include <utility>
class CdromDrive {
cdrom_drive_t *drv = nullptr;
public:
CdromDrive() = default;
explicit CdromDrive(CdIo_t *cdio)
:drv(cdio_cddap_identify_cdio(cdio, 1, nullptr))
{
if (drv == nullptr)
throw std::runtime_error("Failed to identify audio CD");
cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT,
CDDA_MESSAGE_FORGETIT);
}
~CdromDrive() noexcept {
if (drv != nullptr)
cdio_cddap_close_no_free_cdio(drv);
}
CdromDrive(CdromDrive &&src) noexcept
:drv(std::exchange(src.drv, nullptr)) {}
CdromDrive &operator=(CdromDrive &&src) noexcept {
using std::swap;
swap(drv, src.drv);
return *this;
}
auto get() const noexcept {
return drv;
}
void Open() {
if (cdio_cddap_open(drv) != 0)
throw std::runtime_error("Failed to open disc");
}
auto GetDiscSectorRange() const {
auto first = cdio_cddap_disc_firstsector(drv);
auto last = cdio_cddap_disc_lastsector(drv);
if (first < 0 || last < 0)
throw std::runtime_error("Failed to get disc audio sectors");
return std::make_pair(first, last);
}
gcc_pure
bool IsAudioTrack(track_t i) const noexcept {
return cdio_cddap_track_audiop(drv, i);
}
auto GetTrackSectorRange(track_t i) const {
auto first = cdio_cddap_track_firstsector(drv, i);
auto last = cdio_cddap_track_lastsector(drv, i);
if (first < 0 || last < 0)
throw std::runtime_error("Invalid track number");
return std::make_pair(first, last);
}
gcc_pure
unsigned GetTrackCount() const noexcept {
return cdio_cddap_tracks(drv);
}
unsigned GetTrackChannels(track_t i) const {
auto value = cdio_cddap_track_channels(drv, i);
if (value < 0)
throw std::runtime_error("cdio_cddap_track_channels() failed");
return unsigned(value);
}
};
class CdromParanoia {
cdrom_paranoia_t *paranoia = nullptr;
public:
CdromParanoia() = default;
explicit CdromParanoia(cdrom_drive_t *drv) noexcept
:paranoia(cdio_paranoia_init(drv)) {}
~CdromParanoia() noexcept {
if (paranoia != nullptr)
cdio_paranoia_free(paranoia);
}
CdromParanoia(CdromParanoia &&src) noexcept
:paranoia(std::exchange(src.paranoia, nullptr)) {}
CdromParanoia &operator=(CdromParanoia &&src) noexcept {
using std::swap;
swap(paranoia, src.paranoia);
return *this;
}
auto get() const noexcept {
return paranoia;
}
void SetMode(int mode_flags) noexcept {
paranoia_modeset(paranoia, mode_flags);
}
void Seek(int32_t seek, int whence=SEEK_SET) {
if (cdio_paranoia_seek(paranoia, seek, whence) < 0)
throw std::runtime_error("Failed to seek disc");
}
ConstBuffer<int16_t> Read() {
const int16_t *data = cdio_paranoia_read(paranoia, nullptr);
if (data == nullptr)
throw std::runtime_error("Read from audio CD failed");
return {data, CDIO_CD_FRAMESIZE_RAW / sizeof(int16_t)};
}
};
#endif

View File

@@ -1,20 +1,19 @@
Index: curl-7.58.0/lib/url.c
===================================================================
--- curl-7.58.0.orig/lib/url.c
+++ curl-7.58.0/lib/url.c
@@ -3503,6 +3503,7 @@ static CURLcode override_login(struct Cu
diff -ur curl-7.63.0.orig/lib/url.c curl-7.63.0/lib/url.c
--- curl-7.63.0.orig/lib/url.c 2019-01-21 10:15:51.368019445 +0100
+++ curl-7.63.0/lib/url.c 2019-01-21 10:19:16.307523984 +0100
@@ -3057,6 +3057,7 @@
}
conn->bits.netrc = FALSE;
+#ifndef __BIONIC__
if(data->set.use_netrc != CURL_NETRC_IGNORED) {
int ret = Curl_parsenetrc(conn->host.name,
userp, passwdp,
@@ -3524,6 +3525,7 @@ static CURLcode override_login(struct Cu
conn->bits.user_passwd = TRUE; /* enable user+password */
if(data->set.use_netrc != CURL_NETRC_IGNORED &&
(!*userp || !**userp || !*passwdp || !**passwdp)) {
bool netrc_user_changed = FALSE;
@@ -3090,6 +3091,7 @@
}
}
}
+#endif
return CURLE_OK;
}
/* for updated strings, we update them in the URL */
if(user_changed) {

View File

@@ -35,6 +35,7 @@
#include "Iter.hxx"
#include "util/Compiler.h"
#include "util/ConstBuffer.hxx"
#if GCC_OLDER_THAN(8,0)
/* switch off completely bogus shadow warnings in older GCC
@@ -81,6 +82,14 @@ public:
return value;
}
template<typename T>
ConstBuffer<T> GetFixedArray() noexcept {
void *value;
int n_elements;
dbus_message_iter_get_fixed_array(&iter, &value, &n_elements);
return {(const T *)value, size_t(n_elements)};
}
/**
* Create a new iterator which recurses into a container
* value.

View File

@@ -22,6 +22,7 @@
#include "ReadIter.hxx"
#include "ObjectManager.hxx"
#include "util/StringAPI.hxx"
#include "util/StringView.hxx"
#include "util/Compiler.h"
#include <functional>
@@ -39,6 +40,40 @@ CheckString(I &&i) noexcept
return i.GetString();
}
template<typename I>
gcc_pure
static StringView
CheckRecursedByteArrayToString(I &&i) noexcept
{
if (i.GetArgType() != DBUS_TYPE_BYTE)
return nullptr;
auto value = i.template GetFixedArray<char>();
return { value.data, value.size };
}
template<typename I>
gcc_pure
static StringView
CheckByteArrayToString(I &&i) noexcept
{
if (i.GetArgType() != DBUS_TYPE_ARRAY)
return nullptr;
return CheckRecursedByteArrayToString(i.Recurse());
}
template<typename I>
gcc_pure
static StringView
CheckByteArrayArrayFrontToString(I &&i) noexcept
{
if (i.GetArgType() != DBUS_TYPE_ARRAY)
return nullptr;
return CheckByteArrayToString(i.Recurse());
}
static void
ParseDriveDictEntry(Object &o, const char *name,
ODBus::ReadMessageIter &&value_i) noexcept
@@ -61,6 +96,25 @@ ParseBlockDictEntry(Object &o, const char *name,
}
}
static void
ParseFileesystemDictEntry(Object &o, const char *name,
ODBus::ReadMessageIter &&value_i) noexcept
{
if (StringIsEqual(name, "MountPoints")) {
if (!o.mount_point.empty())
/* we already know one mount point, and we're
not interested in more */
return;
/* get the first string in the array */
auto value = CheckByteArrayArrayFrontToString(value_i);
if (value != nullptr)
o.mount_point = {value.data, value.size};
// TODO: check whether the string is a valid filesystem path
}
}
static void
ParseInterface(Object &o, const char *interface,
ODBus::ReadMessageIter &&i) noexcept
@@ -74,6 +128,10 @@ ParseInterface(Object &o, const char *interface,
std::ref(o), _1, _2));
} else if (StringIsEqual(interface, "org.freedesktop.UDisks2.Filesystem")) {
o.is_filesystem = true;
i.ForEachProperty(std::bind(ParseFileesystemDictEntry,
std::ref(o), _1, _2));
}
}

View File

@@ -39,6 +39,13 @@ struct Object {
std::string drive_id, block_id;
/**
* The first element of the "MountPoints" array of the
* "Filesystem" interface. Empty if no "MountPoints" property
* exists.
*/
std::string mount_point;
bool is_filesystem = false;
explicit Object(const char *_path) noexcept

View File

@@ -73,7 +73,7 @@ NfsManager::Compare::operator()(const ManagedConnection &a,
NfsManager::~NfsManager() noexcept
{
assert(GetEventLoop().IsInside());
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
CollectGarbage();
@@ -103,7 +103,7 @@ NfsManager::GetConnection(const char *server, const char *export_name) noexcept
void
NfsManager::CollectGarbage() noexcept
{
assert(GetEventLoop().IsInside());
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
garbage.clear_and_dispose(DeleteDisposer());
}

View File

@@ -1,6 +1,11 @@
libflac_dep = dependency('flac', version: '>= 1.2', required: get_option('flac'))
libopus_dep = dependency('opus', required: get_option('opus'))
libvorbis_dep = dependency('vorbis', required: get_option('vorbis'))
libvorbisidec_dep = dependency('vorbisidec', required: get_option('tremor'))
if get_option('vorbis').enabled() and get_option('tremor').enabled()
error('Cannot build both, the Vorbis decoder AND the Tremor (Vorbis fixed-point) decoder')
endif
libvorbisenc_dep = dependency('', required: false)
if need_encoder and not get_option('vorbisenc').disabled()
@@ -13,7 +18,7 @@ if need_encoder and not get_option('vorbisenc').disabled()
endif
endif
if libopus_dep.found() or libvorbis_dep.found() or libvorbisenc_dep.found()
if libopus_dep.found() or libvorbis_dep.found() or libvorbisenc_dep.found() or libvorbisidec_dep.found()
libogg_dep = dependency('ogg')
else
libogg_dep = dependency('', required: false)

View File

@@ -24,6 +24,9 @@
#include "mixer/MixerInternal.hxx"
#include "mixer/Listener.hxx"
#include "output/plugins/PulseOutputPlugin.hxx"
#include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "config/Block.hxx"
#include <pulse/context.h>
#include <pulse/introspect.h>
@@ -37,13 +40,18 @@
class PulseMixer final : public Mixer {
PulseOutput &output;
bool online;
const float volume_scale_factor;
bool online = false;
struct pa_cvolume volume;
public:
PulseMixer(PulseOutput &_output, MixerListener &_listener)
PulseMixer(PulseOutput &_output, MixerListener &_listener,
double _volume_scale_factor)
:Mixer(pulse_mixer_plugin, _listener),
output(_output), online(false)
output(_output),
volume_scale_factor(_volume_scale_factor)
{
}
@@ -159,13 +167,30 @@ pulse_mixer_on_change(PulseMixer &pm,
pm.Update(context, stream);
}
static float
parse_volume_scale_factor(const char *value) {
if (value == nullptr)
return 1.0;
char *endptr;
float factor = ParseFloat(value, &endptr);
if (endptr == value || *endptr != '\0' || factor < 0.5 || factor > 5.0)
throw FormatRuntimeError("\"%s\" is not a number in the "
"range 0.5 to 5.0",
value);
return factor;
}
static Mixer *
pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
MixerListener &listener,
gcc_unused const ConfigBlock &block)
const ConfigBlock &block)
{
PulseOutput &po = (PulseOutput &)ao;
PulseMixer *pm = new PulseMixer(po, listener);
float scale = parse_volume_scale_factor(block.GetBlockValue("scale_volume"));
PulseMixer *pm = new PulseMixer(po, listener, scale);
pulse_output_set_mixer(po, *pm);
@@ -191,8 +216,9 @@ PulseMixer::GetVolume()
int
PulseMixer::GetVolumeInternal()
{
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
return online ?
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / PA_VOLUME_NORM)
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / max_pa_volume)
: -1;
}
@@ -204,9 +230,11 @@ PulseMixer::SetVolume(unsigned new_volume)
if (!online)
throw std::runtime_error("disconnected");
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels,
(new_volume * PA_VOLUME_NORM + 50) / 100);
(new_volume * max_pa_volume + 50) / 100);
pulse_output_set_volume(output, &cvolume);
volume = cvolume;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,6 +29,7 @@
#include "config.h"
#include "AllocatedSocketAddress.hxx"
#include "util/StringView.hxx"
#include <string.h>
@@ -70,6 +71,12 @@ AllocatedSocketAddress::SetSize(size_type new_size) noexcept
#ifdef HAVE_UN
StringView
AllocatedSocketAddress::GetLocalRaw() const noexcept
{
return SocketAddress(*this).GetLocalRaw();
}
void
AllocatedSocketAddress::SetLocal(const char *path) noexcept
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,8 +27,8 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ALLOCATED_SOCKET_ADDRESS_HPP
#define ALLOCATED_SOCKET_ADDRESS_HPP
#ifndef ALLOCATED_SOCKET_ADDRESS_HXX
#define ALLOCATED_SOCKET_ADDRESS_HXX
#include "SocketAddress.hxx"
#include "Features.hxx"
@@ -140,6 +140,20 @@ public:
}
#ifdef HAVE_UN
/**
* @see SocketAddress::GetLocalRaw()
*/
gcc_pure
StringView GetLocalRaw() const noexcept;
/**
* @see SocketAddress::GetLocalPath()
*/
gcc_pure
const char *GetLocalPath() const noexcept {
return ((SocketAddress)*this).GetLocalPath();
}
/**
* Make this a "local" address (UNIX domain socket). If the path
* begins with a '@', then the rest specifies an "abstract" local
@@ -149,6 +163,14 @@ public:
#endif
#ifdef HAVE_TCP
bool IsV6Any() const noexcept {
return ((SocketAddress)*this).IsV6Any();
}
bool IsV4Mapped() const noexcept {
return ((SocketAddress)*this).IsV4Mapped();
}
/**
* Extract the port number. Returns 0 if not applicable.
*/

View File

@@ -36,7 +36,7 @@
#include <string.h>
static inline bool
IsValidHostnameChar(char ch)
IsValidHostnameChar(char ch) noexcept
{
return IsAlphaNumericASCII(ch) ||
ch == '-' || ch == '.' ||
@@ -44,14 +44,14 @@ IsValidHostnameChar(char ch)
}
static inline bool
IsValidScopeChar(char ch)
IsValidScopeChar(char ch) noexcept
{
return IsAlphaNumericASCII(ch) ||
ch == '-' || ch == '_';
}
static const char *
FindScopeEnd(const char *p)
FindScopeEnd(const char *p) noexcept
{
if (*p == '%' && IsValidScopeChar(p[1])) {
p += 2;
@@ -63,7 +63,7 @@ FindScopeEnd(const char *p)
}
static inline bool
IsValidIPv6Char(char ch)
IsValidIPv6Char(char ch) noexcept
{
return IsDigitASCII(ch) ||
(ch >= 'a' && ch <= 'f') ||
@@ -72,7 +72,7 @@ IsValidIPv6Char(char ch)
}
static const char *
FindIPv6End(const char *p)
FindIPv6End(const char *p) noexcept
{
while (IsValidIPv6Char(*p))
++p;
@@ -84,7 +84,7 @@ FindIPv6End(const char *p)
}
ExtractHostResult
ExtractHost(const char *src)
ExtractHost(const char *src) noexcept
{
ExtractHostResult result{nullptr, src};
const char *hostname;

View File

@@ -57,7 +57,7 @@ struct ExtractHostResult {
*/
const char *end;
constexpr bool HasFailed() const {
constexpr bool HasFailed() const noexcept {
return host == nullptr;
}
};
@@ -71,6 +71,6 @@ struct ExtractHostResult {
*/
gcc_pure
ExtractHostResult
ExtractHost(const char *src);
ExtractHost(const char *src) noexcept;
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -51,7 +51,12 @@ class IPv4Address {
#ifdef _WIN32
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
uint8_t c, uint8_t d) noexcept {
return {{{ a, b, c, d }}};
struct in_addr result{};
result.s_net = a;
result.s_host = b;
result.s_lh = c;
result.s_impno = d;
return result;
}
#else
@@ -66,7 +71,7 @@ class IPv4Address {
static constexpr struct in_addr ConstructInAddr(uint8_t a, uint8_t b,
uint8_t c, uint8_t d) noexcept {
return { ConstructInAddrT(a, b, c, d) };
return ConstructInAddrBE(ConstructInAddrT(a, b, c, d));
}
#endif
@@ -158,7 +163,7 @@ public:
*/
static constexpr const IPv4Address &Cast(const SocketAddress &src) noexcept {
/* this reinterpret_cast works because this class is
just a wrapper for struct sockaddr_in6 */
just a wrapper for struct sockaddr_in */
return *(const IPv4Address *)(const void *)src.GetAddress();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,22 +53,24 @@ class IPv6Address {
uint16_t c, uint16_t d,
uint16_t e, uint16_t f,
uint16_t g, uint16_t h) noexcept {
return {{
#ifndef __HAIKU__
{
#endif
uint8_t(a >> 8), uint8_t(a),
uint8_t(b >> 8), uint8_t(b),
uint8_t(c >> 8), uint8_t(c),
uint8_t(d >> 8), uint8_t(d),
uint8_t(e >> 8), uint8_t(e),
uint8_t(f >> 8), uint8_t(f),
uint8_t(g >> 8), uint8_t(g),
uint8_t(h >> 8), uint8_t(h),
#ifndef __HAIKU__
}
#endif
}};
struct in6_addr result{};
result.s6_addr[0] = a >> 8;
result.s6_addr[1] = a;
result.s6_addr[2] = b >> 8;
result.s6_addr[3] = b;
result.s6_addr[4] = c >> 8;
result.s6_addr[5] = c;
result.s6_addr[6] = d >> 8;
result.s6_addr[7] = d;
result.s6_addr[8] = e >> 8;
result.s6_addr[9] = e;
result.s6_addr[10] = f >> 8;
result.s6_addr[11] = f;
result.s6_addr[12] = g >> 8;
result.s6_addr[13] = g;
result.s6_addr[14] = h >> 8;
result.s6_addr[15] = h;
return result;
}
static constexpr struct sockaddr_in6 Construct(struct in6_addr address,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -73,6 +73,21 @@ SocketAddress::GetLocalRaw() const noexcept
return {path, size - header_size};
}
const char *
SocketAddress::GetLocalPath() const noexcept
{
const auto raw = GetLocalRaw();
return !raw.empty() &&
/* must be an absolute path */
raw.front() == '/' &&
/* must be null-terminated */
raw.back() == 0 &&
/* there must not be any other null byte */
memchr(raw.data, 0, raw.size - 1) == nullptr
? raw.data
: nullptr;
}
#endif
#ifdef HAVE_TCP

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2017 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -93,7 +93,7 @@ public:
* Does the object have a well-defined address? Check !IsNull()
* before calling this method.
*/
bool IsDefined() const noexcept {
constexpr bool IsDefined() const noexcept {
return GetFamily() != AF_UNSPEC;
}
@@ -106,6 +106,13 @@ public:
*/
gcc_pure
StringView GetLocalRaw() const noexcept;
/**
* Returns the local socket path or nullptr if not applicable
* (or if the path is corrupt).
*/
gcc_pure
const char *GetLocalPath() const noexcept;
#endif
#ifdef HAVE_TCP

View File

@@ -637,11 +637,8 @@ osx_output_set_device(OSXOutput *oo)
}
}
if (i == numdevices) {
FormatWarning(osx_output_domain,
"Found no audio device with name '%s' "
"(will use default audio device)",
throw FormatRuntimeError("Found no audio device with name '%s' ",
oo->device_name);
return;
}
status = AudioUnitSetProperty(oo->au,

View File

@@ -141,6 +141,27 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
protocol = SHOUT_PROTOCOL_HTTP;
}
#ifdef SHOUT_TLS
unsigned tls;
value = block.GetBlockValue("tls");
if (value != nullptr) {
if (0 == strcmp(value, "disabled"))
tls = SHOUT_TLS_DISABLED;
else if(0 == strcmp(value, "auto"))
tls = SHOUT_TLS_AUTO;
else if(0 == strcmp(value, "auto_no_plain"))
tls = SHOUT_TLS_AUTO_NO_PLAIN;
else if(0 == strcmp(value, "rfc2818"))
tls = SHOUT_TLS_RFC2818;
else if(0 == strcmp(value, "rfc2817"))
tls = SHOUT_TLS_RFC2817;
else
throw FormatRuntimeError("invalid shout TLS option \"%s\"", value);
} else {
tls = SHOUT_TLS_DISABLED;
}
#endif
if (shout_set_host(shout_conn, host) != SHOUTERR_SUCCESS ||
shout_set_port(shout_conn, port) != SHOUTERR_SUCCESS ||
shout_set_password(shout_conn, passwd) != SHOUTERR_SUCCESS ||
@@ -151,6 +172,9 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
shout_set_format(shout_conn, shout_format)
!= SHOUTERR_SUCCESS ||
shout_set_protocol(shout_conn, protocol) != SHOUTERR_SUCCESS ||
#ifdef SHOUT_TLS
shout_set_tls(shout_conn, tls) != SHOUTERR_SUCCESS ||
#endif
shout_set_agent(shout_conn, "MPD") != SHOUTERR_SUCCESS)
throw std::runtime_error(shout_get_error(shout_conn));

View File

@@ -47,6 +47,17 @@ playlist_list_global_init(const ConfigData &config);
void
playlist_list_global_finish() noexcept;
class ScopePlaylistPluginsInit {
public:
explicit ScopePlaylistPluginsInit(const ConfigData &config) {
playlist_list_global_init(config);
}
~ScopePlaylistPluginsInit() noexcept {
playlist_list_global_finish();
}
};
/**
* Opens a playlist by its URI.
*/

View File

@@ -106,7 +106,7 @@ storage_state_restore(const char *line, TextFile &file, Instance &instance)
return true;
}
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.database)) {
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
try {
db->Mount(uri.c_str(), url.c_str());
} catch (...) {

View File

@@ -192,7 +192,7 @@ private:
}
void Disconnect() noexcept {
assert(GetEventLoop().IsInside());
assert(!GetEventLoop().IsAlive() || GetEventLoop().IsInside());
switch (state) {
case State::INITIAL:

View File

@@ -46,6 +46,8 @@ class UdisksStorage final : public Storage {
const std::string base_uri;
const std::string id;
const AllocatedPath inside_path;
std::string dbus_path;
SafeSingleton<ODBus::Glue> dbus_glue;
@@ -64,10 +66,12 @@ class UdisksStorage final : public Storage {
DeferEvent defer_mount, defer_unmount;
public:
template<typename B, typename I>
UdisksStorage(EventLoop &_event_loop, B &&_base_uri, I &&_id)
template<typename B, typename I, typename IP>
UdisksStorage(EventLoop &_event_loop, B &&_base_uri, I &&_id,
IP &&_inside_path)
:base_uri(std::forward<B>(_base_uri)),
id(std::forward<I>(_id)),
inside_path(std::forward<IP>(_inside_path)),
dbus_glue(_event_loop),
defer_mount(_event_loop, BIND_THIS_METHOD(DeferredMount)),
defer_unmount(_event_loop, BIND_THIS_METHOD(DeferredUnmount)) {}
@@ -120,6 +124,9 @@ public:
const char *MapToRelativeUTF8(const char *uri_utf8) const noexcept override;
private:
void SetMountPoint(Path mount_point);
void LockSetMountPoint(Path mount_point);
void OnListReply(ODBus::Message reply) noexcept;
void MountWait();
@@ -131,20 +138,52 @@ private:
void OnUnmountNotify(ODBus::Message reply) noexcept;
};
inline void
UdisksStorage::SetMountPoint(Path mount_point)
{
mounted_storage = inside_path.IsNull()
? CreateLocalStorage(mount_point)
: CreateLocalStorage(mount_point / inside_path);
mount_error = {};
want_mount = false;
cond.broadcast();
}
void
UdisksStorage::LockSetMountPoint(Path mount_point)
{
const std::lock_guard<Mutex> lock(mutex);
SetMountPoint(mount_point);
}
void
UdisksStorage::OnListReply(ODBus::Message reply) noexcept
{
using namespace UDisks2;
try {
ParseObjects(reply, [this](Object &&o) {
if (o.IsId(id))
dbus_path = std::move(o.path);
});
std::string mount_point;
ParseObjects(reply, [this, &mount_point](Object &&o) {
if (!o.IsId(id))
return;
dbus_path = std::move(o.path);
mount_point = std::move(o.mount_point);
});
if (dbus_path.empty())
throw FormatRuntimeError("No such UDisks2 object: %s",
id.c_str());
if (!mount_point.empty()) {
/* already mounted: don't attempt to mount
again, because this would result in
org.freedesktop.UDisks2.Error.AlreadyMounted */
LockSetMountPoint(Path::FromFS(mount_point.c_str()));
return;
}
} catch (...) {
const std::lock_guard<Mutex> lock(mutex);
mount_error = std::current_exception();
@@ -222,12 +261,7 @@ try {
throw std::runtime_error("Malformed 'Mount' response");
const char *mount_path = i.GetString();
const std::lock_guard<Mutex> lock(mutex);
mounted_storage = CreateLocalStorage(Path::FromFS(mount_path));
mount_error = {};
want_mount = false;
cond.broadcast();
LockSetMountPoint(Path::FromFS(mount_path));
} catch (...) {
const std::lock_guard<Mutex> lock(mutex);
mount_error = std::current_exception();
@@ -297,16 +331,18 @@ UdisksStorage::MapUTF8(const char *uri_utf8) const noexcept
{
assert(uri_utf8 != nullptr);
if (StringIsEmpty(uri_utf8))
/* kludge for a special case: return the "udisks://"
URI if the parameter is an empty string to fix the
mount URIs in the state file */
return base_uri;
try {
const_cast<UdisksStorage *>(this)->MountWait();
return mounted_storage->MapUTF8(uri_utf8);
} catch (...) {
/* fallback - not usable but the best we can do */
if (StringIsEmpty(uri_utf8))
return base_uri;
return PathTraitsUTF8::Build(base_uri.c_str(), uri_utf8);
}
}
@@ -333,12 +369,17 @@ CreateUdisksStorageURI(EventLoop &event_loop, const char *base_uri)
} else {
id = {id_begin, relative_path};
++relative_path;
while (*relative_path == '/')
++relative_path;
}
// TODO: use relative_path
auto inside_path = *relative_path != 0
? AllocatedPath::FromUTF8Throw(relative_path)
: nullptr;
return std::make_unique<UdisksStorage>(event_loop, base_uri,
std::move(id));
std::move(id),
std::move(inside_path));
}
const StoragePlugin udisks_storage_plugin = {

View File

@@ -32,9 +32,7 @@
#include "UniqueFileDescriptor.hxx"
#include <assert.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdint.h>
struct epoll_event;

View File

@@ -43,6 +43,30 @@ OpenReadOnly(const char *path)
return fd;
}
UniqueFileDescriptor
OpenWriteOnly(const char *path, int flags)
{
UniqueFileDescriptor fd;
if (!fd.Open(path, O_WRONLY|flags))
throw FormatErrno("Failed to open '%s'", path);
return fd;
}
#ifndef _WIN32
UniqueFileDescriptor
OpenDirectory(const char *path, int flags)
{
UniqueFileDescriptor fd;
if (!fd.Open(path, O_DIRECTORY|O_RDONLY|flags))
throw FormatErrno("Failed to open '%s'", path);
return fd;
}
#endif
#ifdef __linux__
UniqueFileDescriptor
@@ -75,6 +99,16 @@ OpenReadOnly(FileDescriptor directory, const char *name, int flags)
return fd;
}
UniqueFileDescriptor
OpenWriteOnly(FileDescriptor directory, const char *name, int flags)
{
UniqueFileDescriptor fd;
if (!fd.Open(directory, name, O_WRONLY|flags))
throw FormatErrno("Failed to open '%s'", name);
return fd;
}
UniqueFileDescriptor
OpenDirectory(FileDescriptor directory, const char *name, int flags)
{

View File

@@ -36,6 +36,16 @@ class UniqueFileDescriptor;
UniqueFileDescriptor
OpenReadOnly(const char *path);
UniqueFileDescriptor
OpenWriteOnly(const char *path, int flags=0);
#ifndef _WIN32
UniqueFileDescriptor
OpenDirectory(const char *name, int flags=0);
#endif
#ifdef __linux__
UniqueFileDescriptor
@@ -47,6 +57,9 @@ OpenPath(FileDescriptor directory, const char *name, int flags=0);
UniqueFileDescriptor
OpenReadOnly(FileDescriptor directory, const char *name, int flags=0);
UniqueFileDescriptor
OpenWriteOnly(FileDescriptor directory, const char *name, int flags=0);
UniqueFileDescriptor
OpenDirectory(FileDescriptor directory, const char *name, int flags=0);

View File

@@ -60,7 +60,8 @@ public:
}
UniqueFileDescriptor &operator=(UniqueFileDescriptor &&other) noexcept {
std::swap(fd, other.fd);
using std::swap;
swap(fd, other.fd);
return *this;
}

View File

@@ -29,6 +29,7 @@
#include <string.h>
static constexpr struct tag_table ape_tags[] = {
{ "album artist", TAG_ALBUM_ARTIST },
{ "year", TAG_DATE },
{ nullptr, TAG_NUM_OF_ITEM_TYPES }
};

View File

@@ -62,7 +62,7 @@ public:
}
TagMask &operator&=(TagMask other) {
value |= other.value;
value &= other.value;
return *this;
}
@@ -72,7 +72,7 @@ public:
}
TagMask &operator^=(TagMask other) {
value |= other.value;
value ^= other.value;
return *this;
}

View File

@@ -33,7 +33,7 @@
static constexpr Domain signal_handlers_domain("signal_handlers");
static void
HandleShutdownSignal(void *ctx)
HandleShutdownSignal(void *ctx) noexcept
{
auto &loop = *(EventLoop *)ctx;
loop.Break();
@@ -47,7 +47,7 @@ x_sigaction(int signum, const struct sigaction *act)
}
static void
handle_reload_event(void *)
handle_reload_event(void *) noexcept
{
LogDebug(signal_handlers_domain, "got SIGHUP, reopening log files");
cycle_log_files();
@@ -76,7 +76,7 @@ SignalHandlersInit(EventLoop &loop)
}
void
SignalHandlersFinish()
SignalHandlersFinish() noexcept
{
SignalMonitorFinish();
}

View File

@@ -26,6 +26,17 @@ void
SignalHandlersInit(EventLoop &loop);
void
SignalHandlersFinish();
SignalHandlersFinish() noexcept;
class ScopeSignalHandlersInit {
public:
ScopeSignalHandlersInit(EventLoop &loop) {
SignalHandlersInit(loop);
}
~ScopeSignalHandlersInit() noexcept {
SignalHandlersFinish();
}
};
#endif

View File

@@ -36,28 +36,28 @@
#include <stddef.h>
template<typename T, typename U>
static inline constexpr T *
constexpr T *
OffsetCast(U *p, ptrdiff_t offset)
{
return reinterpret_cast<T *>(OffsetPointer(p, offset));
}
template<typename T, typename U>
static inline constexpr T *
constexpr T *
OffsetCast(const U *p, ptrdiff_t offset)
{
return reinterpret_cast<const T *>(OffsetPointer(p, offset));
}
template<class C, class A>
static constexpr inline ptrdiff_t
constexpr ptrdiff_t
ContainerAttributeOffset(const C *null_c, const A C::*p)
{
return ptrdiff_t((const char *)&(null_c->*p) - (const char *)null_c);
}
template<class C, class A>
static constexpr inline ptrdiff_t
constexpr ptrdiff_t
ContainerAttributeOffset(const A C::*p)
{
return ContainerAttributeOffset<C, A>(nullptr, p);
@@ -67,7 +67,7 @@ ContainerAttributeOffset(const A C::*p)
* Cast the given pointer to a struct member to its parent structure.
*/
template<class C, class A>
static inline constexpr C &
constexpr C &
ContainerCast(A &a, const A C::*member)
{
return *OffsetCast<C, A>(&a, -ContainerAttributeOffset<C, A>(member));
@@ -77,7 +77,7 @@ ContainerCast(A &a, const A C::*member)
* Cast the given pointer to a struct member to its parent structure.
*/
template<class C, class A>
static inline constexpr const C &
constexpr const C &
ContainerCast(const A &a, const A C::*member)
{
return *OffsetCast<const C, const A>(&a, -ContainerAttributeOffset<C, A>(member));

View File

@@ -34,29 +34,25 @@
#include "WCharUtil.hxx"
#endif
constexpr
static inline bool
constexpr bool
IsASCII(const unsigned char ch)
{
return ch < 0x80;
}
constexpr
static inline bool
constexpr bool
IsASCII(const char ch)
{
return IsASCII((unsigned char)ch);
}
constexpr
static inline bool
constexpr bool
IsWhitespaceOrNull(const char ch)
{
return (unsigned char)ch <= 0x20;
}
constexpr
static inline bool
constexpr bool
IsWhitespaceNotNull(const char ch)
{
return ch > 0 && ch <= 0x20;
@@ -68,50 +64,43 @@ IsWhitespaceNotNull(const char ch)
* want the fastest implementation, and you don't care if a null byte
* matches.
*/
constexpr
static inline bool
constexpr bool
IsWhitespaceFast(const char ch)
{
return IsWhitespaceOrNull(ch);
}
constexpr
static inline bool
constexpr bool
IsPrintableASCII(char ch)
{
return (signed char)ch >= 0x20;
}
constexpr
static inline bool
constexpr bool
IsDigitASCII(char ch)
{
return ch >= '0' && ch <= '9';
}
constexpr
static inline bool
constexpr bool
IsUpperAlphaASCII(char ch)
{
return ch >= 'A' && ch <= 'Z';
}
constexpr
static inline bool
constexpr bool
IsLowerAlphaASCII(char ch)
{
return ch >= 'a' && ch <= 'z';
}
constexpr
static inline bool
constexpr bool
IsAlphaASCII(char ch)
{
return IsUpperAlphaASCII(ch) || IsLowerAlphaASCII(ch);
}
constexpr
static inline bool
constexpr bool
IsAlphaNumericASCII(char ch)
{
return IsAlphaASCII(ch) || IsDigitASCII(ch);
@@ -121,8 +110,7 @@ IsAlphaNumericASCII(char ch)
* Convert the specified ASCII character (0x00..0x7f) to upper case.
* Unlike toupper(), it ignores the system locale.
*/
constexpr
static inline char
constexpr char
ToUpperASCII(char ch)
{
return ch >= 'a' && ch <= 'z'
@@ -134,8 +122,7 @@ ToUpperASCII(char ch)
* Convert the specified ASCII character (0x00..0x7f) to lower case.
* Unlike tolower(), it ignores the system locale.
*/
constexpr
static inline char
constexpr char
ToLowerASCII(char ch)
{
return ch >= 'A' && ch <= 'Z'

View File

@@ -34,7 +34,7 @@
/**
* Offset the given pointer by the specified number of bytes.
*/
static inline constexpr void *
constexpr void *
OffsetPointer(void *p, ptrdiff_t offset)
{
return (char *)p + offset;
@@ -43,7 +43,7 @@ OffsetPointer(void *p, ptrdiff_t offset)
/**
* Offset the given pointer by the specified number of bytes.
*/
static inline constexpr const void *
constexpr const void *
OffsetPointer(const void *p, ptrdiff_t offset)
{
return (const char *)p + offset;

View File

@@ -42,6 +42,8 @@ PrintException(const std::exception &e) noexcept
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
PrintException(nested);
} catch (const char *s) {
fprintf(stderr, "%s\n", s);
} catch (...) {
fprintf(stderr, "Unrecognized nested exception\n");
}
@@ -54,6 +56,8 @@ PrintException(const std::exception_ptr &ep) noexcept
std::rethrow_exception(ep);
} catch (const std::exception &e) {
PrintException(e);
} catch (const char *s) {
fprintf(stderr, "%s\n", s);
} catch (...) {
fprintf(stderr, "Unrecognized exception\n");
}

View File

@@ -69,6 +69,7 @@ struct BasicStringView : ConstBuffer<T> {
using ConstBuffer<T>::back;
using ConstBuffer<T>::pop_front;
using ConstBuffer<T>::pop_back;
using ConstBuffer<T>::skip_front;
gcc_pure
pointer_type Find(value_type ch) const noexcept {
@@ -114,6 +115,20 @@ struct BasicStringView : ConstBuffer<T> {
StripLeft();
StripRight();
}
bool SkipPrefix(BasicStringView<T> needle) noexcept {
bool match = StartsWith(needle);
if (match)
skip_front(needle.size);
return match;
}
bool RemoveSuffix(BasicStringView<T> needle) noexcept {
bool match = EndsWith(needle);
if (match)
size -= needle.size;
return match;
}
};
struct StringView : BasicStringView<char> {

View File

@@ -32,6 +32,8 @@
#include "StringView.hxx"
#include <wchar.h>
struct WStringView : BasicStringView<wchar_t> {
using BasicStringView::BasicStringView;

View File

@@ -30,10 +30,14 @@ if zeroconf_option == 'bonjour'
if not compiler.has_header('dns_sd.h')
error('dns_sd.h not found')
endif
bonjour_dep = declare_dependency(link_args: ['-framework', 'dnssd'])
if is_darwin
bonjour_dep = declare_dependency(link_args: ['-framework', 'dnssd'])
else
bonjour_dep = declare_dependency(link_args: ['-ldns_sd'])
endif
conf.set('HAVE_BONJOUR', true)
zeroconf = static_library(
'zeroconf_bonjour',
'ZeroconfGlue.cxx',

41
test/ConfigGlue.hxx Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_TEST_CONFIG_GLUE_HXX
#define MPD_TEST_CONFIG_GLUE_HXX
#include "config/File.hxx"
#include "config/Migrate.hxx"
#include "config/Data.hxx"
#include "fs/Path.hxx"
inline ConfigData
AutoLoadConfigFile(Path path)
{
ConfigData data;
if (!path.IsNull()) {
ReadConfigFile(data, path);
Migrate(data);
}
return data;
}
#endif

View File

@@ -65,7 +65,7 @@ try {
const Path path = Path::FromFS(argv[1]);
decoder_plugin_init_all(ConfigData());
const ScopeDecoderPluginsInit decoder_plugins_init({});
const auto *plugin = FindContainerDecoderPlugin(path);
if (plugin == nullptr) {
@@ -87,8 +87,6 @@ try {
bos.Flush();
decoder_plugin_deinit_all();
return EXIT_SUCCESS;
} catch (...) {
PrintException(std::current_exception());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -26,11 +26,7 @@
#include "db/LightDirectory.hxx"
#include "song/LightSong.hxx"
#include "db/PlaylistVector.hxx"
#include "config/File.hxx"
#include "config/Migrate.hxx"
#include "config/Data.hxx"
#include "config/Param.hxx"
#include "config/Block.hxx"
#include "ConfigGlue.hxx"
#include "tag/Config.hxx"
#include "fs/Path.hxx"
#include "event/Thread.hxx"
@@ -124,9 +120,7 @@ try {
GlobalInit init;
ConfigData config;
ReadConfigFile(config, config_path);
Migrate(config);
const auto config = AutoLoadConfigFile(config_path);
TagLoadConfig(config);
@@ -139,15 +133,13 @@ try {
if (path != nullptr)
block.AddBlockParam("path", path->value, path->line);
Database *db = plugin->create(init.GetEventLoop(),
init.GetEventLoop(),
database_listener, block);
AtScopeExit(db) { delete db; };
auto db = plugin->create(init.GetEventLoop(),
init.GetEventLoop(),
database_listener, block);
db->Open();
AtScopeExit(db) { db->Close(); };
AtScopeExit(&db) { db->Close(); };
const DatabaseSelection selection("", true);

View File

@@ -17,9 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config/File.hxx"
#include "config/Migrate.hxx"
#include "config/Data.hxx"
#include "ConfigGlue.hxx"
#include "tag/Chromaprint.hxx"
#include "pcm/PcmConvert.hxx"
#include "event/Thread.hxx"
@@ -90,31 +88,21 @@ ParseCommandLine(int argc, char **argv)
}
class GlobalInit {
ConfigData config;
const ConfigData config;
EventThread io_thread;
const ScopeInputPluginsInit input_plugins_init;
const ScopeDecoderPluginsInit decoder_plugins_init;
public:
GlobalInit(Path config_path, bool verbose) {
SetLogThreshold(verbose ? LogLevel::DEBUG : LogLevel::INFO);
if (!config_path.IsNull()) {
ReadConfigFile(config, config_path);
Migrate(config);
}
explicit GlobalInit(Path config_path)
:config(AutoLoadConfigFile(config_path)),
input_plugins_init(config, io_thread.GetEventLoop()),
decoder_plugins_init(config)
{
io_thread.Start();
input_stream_global_init(config,
io_thread.GetEventLoop());
decoder_plugin_init_all(config);
pcm_convert_global_init(config);
}
~GlobalInit() {
decoder_plugin_deinit_all();
input_stream_global_finish();
}
};
class ChromaprintDecoderClient final : public DecoderClient {
@@ -245,7 +233,8 @@ int main(int argc, char **argv)
try {
const auto c = ParseCommandLine(argc, argv);
const GlobalInit init(c.config_path, c.verbose);
SetLogThreshold(c.verbose ? LogLevel::DEBUG : LogLevel::INFO);
const GlobalInit init(c.config_path);
const DecoderPlugin *plugin = decoder_plugin_from_name(c.decoder);
if (plugin == nullptr) {

Some files were not shown because too many files have changed in this diff Show More