Compare commits
173 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ef10354d06 | ||
![]() |
158458db5f | ||
![]() |
e183ab5cf8 | ||
![]() |
fef839e2a9 | ||
![]() |
9776e43bbe | ||
![]() |
5201147ab1 | ||
![]() |
fb7daa0d05 | ||
![]() |
2e9f3d8b9f | ||
![]() |
976731ab6c | ||
![]() |
0d8942e64a | ||
![]() |
37a0f04712 | ||
![]() |
cde9348009 | ||
![]() |
095e6e6ad4 | ||
![]() |
9d0bf5e95c | ||
![]() |
8b327f1d9b | ||
![]() |
aef0507abb | ||
![]() |
6bab3bcfea | ||
![]() |
a854595886 | ||
![]() |
8fc3c5c612 | ||
![]() |
4f408bd952 | ||
![]() |
7de8fd04a4 | ||
![]() |
8158bd218c | ||
![]() |
aa1d867b72 | ||
![]() |
34c8242133 | ||
![]() |
e22bdee808 | ||
![]() |
7f87de783f | ||
![]() |
c66389a453 | ||
![]() |
b63c1a2144 | ||
![]() |
808dd7cc54 | ||
![]() |
62a129c18f | ||
![]() |
c18cd941aa | ||
![]() |
6d12c22653 | ||
![]() |
b76d78e6ae | ||
![]() |
0a6e484b1a | ||
![]() |
0bb71f1f20 | ||
![]() |
1aa7cdd602 | ||
![]() |
a4b8a0d801 | ||
![]() |
3bf521d5ca | ||
![]() |
0acb55cde5 | ||
![]() |
6b89fd6100 | ||
![]() |
52ce39dc3e | ||
![]() |
7a3e15d8e5 | ||
![]() |
cf66a60c60 | ||
![]() |
9b26d451e4 | ||
![]() |
137ffba1b4 | ||
![]() |
5c5dc1b7c0 | ||
![]() |
9e9418294a | ||
![]() |
b850eb74b7 | ||
![]() |
67d73a2aee | ||
![]() |
fde9a470dd | ||
![]() |
8d1f30e55b | ||
![]() |
ddd2b60489 | ||
![]() |
8777737861 | ||
![]() |
cb71f6dd04 | ||
![]() |
1881b0e975 | ||
![]() |
98b29f6d1c | ||
![]() |
59fdfd25cb | ||
![]() |
0d98677212 | ||
![]() |
38f0c16904 | ||
![]() |
4fbf6b6c95 | ||
![]() |
1f8ff48168 | ||
![]() |
20b6e0d684 | ||
![]() |
713c1f2ba9 | ||
![]() |
a149bc4c5d | ||
![]() |
b3a458338a | ||
![]() |
44422b2b2f | ||
![]() |
f10afd38b5 | ||
![]() |
4c50a5e0b3 | ||
![]() |
f255a485b7 | ||
![]() |
1930d5774d | ||
![]() |
7220a76be0 | ||
![]() |
83f7610dd1 | ||
![]() |
30e0644722 | ||
![]() |
3ada464020 | ||
![]() |
d5983dd362 | ||
![]() |
98258acc37 | ||
![]() |
8002bc752f | ||
![]() |
834ad7a58f | ||
![]() |
e8f2f98048 | ||
![]() |
c672b60d07 | ||
![]() |
ea269c9c92 | ||
![]() |
1fe3a77640 | ||
![]() |
bbaeea1ab7 | ||
![]() |
0a3aee9d82 | ||
![]() |
2434020971 | ||
![]() |
41e0eb7378 | ||
![]() |
6adf964c81 | ||
![]() |
b59f37bc0a | ||
![]() |
cf2d171ccc | ||
![]() |
cc28a7b67f | ||
![]() |
8b5c33cecd | ||
![]() |
6c28adbcd2 | ||
![]() |
2125e3ed57 | ||
![]() |
3da7ecfadf | ||
![]() |
5bb02bbd39 | ||
![]() |
f11aa09f7c | ||
![]() |
02eb4752d3 | ||
![]() |
d9c3215584 | ||
![]() |
110e6d026b | ||
![]() |
c0f57b8a8b | ||
![]() |
57633fbcb3 | ||
![]() |
864c87e6c0 | ||
![]() |
1a516cf3c0 | ||
![]() |
5c25499c5e | ||
![]() |
da4bb4c298 | ||
![]() |
5b8ff61799 | ||
![]() |
56bded07b1 | ||
![]() |
db144a43ad | ||
![]() |
5965f62b56 | ||
![]() |
05aa9f72a9 | ||
![]() |
281461f0f0 | ||
![]() |
f70eb63879 | ||
![]() |
99c23cf139 | ||
![]() |
9aa75e738c | ||
![]() |
e9c45a9140 | ||
![]() |
a065c6e6b9 | ||
![]() |
feb5ff9bd2 | ||
![]() |
92ec3f0881 | ||
![]() |
98c47d9d36 | ||
![]() |
6c67408944 | ||
![]() |
261a816b21 | ||
![]() |
7a23c123c8 | ||
![]() |
e85b24bee0 | ||
![]() |
9e73ea77b4 | ||
![]() |
b0739eca87 | ||
![]() |
848f6aa5ab | ||
![]() |
c9ba4f3f9c | ||
![]() |
c0e9246a66 | ||
![]() |
096c23f27d | ||
![]() |
40bde1eac9 | ||
![]() |
4b55ed17a9 | ||
![]() |
4f757a5add | ||
![]() |
674c137e5f | ||
![]() |
ff1ff1e54a | ||
![]() |
42b22187c8 | ||
![]() |
cfe22502ab | ||
![]() |
d77b0c7dcd | ||
![]() |
5cf889b676 | ||
![]() |
ffc36d5255 | ||
![]() |
0126276e2f | ||
![]() |
58d6ddab9e | ||
![]() |
05db6934eb | ||
![]() |
02c68c5cdb | ||
![]() |
b02fee7309 | ||
![]() |
424f75c9e1 | ||
![]() |
f6e1176f97 | ||
![]() |
e4700c0a27 | ||
![]() |
cf23fd8774 | ||
![]() |
dee8872395 | ||
![]() |
4ba9357a9c | ||
![]() |
48ec09ab1e | ||
![]() |
754f4048a8 | ||
![]() |
037bb07d08 | ||
![]() |
87635c5268 | ||
![]() |
528b4338f4 | ||
![]() |
c780b8bba9 | ||
![]() |
ca34f3250b | ||
![]() |
6a68e1c3f3 | ||
![]() |
85f77ec81d | ||
![]() |
37debed0b8 | ||
![]() |
008383f24a | ||
![]() |
4f7d52dbf2 | ||
![]() |
c7848da8f2 | ||
![]() |
10a6c5c57d | ||
![]() |
2cc2bab309 | ||
![]() |
701fd1d939 | ||
![]() |
d1bdea8edb | ||
![]() |
0cea67ee70 | ||
![]() |
3a0480a482 | ||
![]() |
1fa99da3c2 | ||
![]() |
22d669da18 | ||
![]() |
772681f23d | ||
![]() |
1862a98a44 |
NEWS
android
build
doc
meson.buildmeson_options.txtpython/build
src
Instance.cxxInstance.hxxLogInit.cxxLogInit.hxxMain.cxxMapper.cxxMapper.hxxPlaylistFile.cxxStateFile.cxxStats.cxxTagPrint.cxx
archive
command
AllCommands.cxxDatabaseCommands.cxxFileCommands.cxxOtherCommands.cxxPlaylistCommands.cxxStorageCommands.cxx
config
db
Configured.cxxConfigured.hxxDatabaseGlue.cxxDatabaseGlue.hxxDatabasePlugin.hxxPtr.hxxVHelper.cxxVHelper.hxx
plugins
update
decoder
event
filter
fs
io
haiku
input
lib
cdio
curl
patches
dbus
gcrypt
nfs
xiph
mixer
plugins
net
AllocatedSocketAddress.cxxAllocatedSocketAddress.hxxHostParser.cxxHostParser.hxxIPv4Address.cxxIPv4Address.hxxIPv6Address.cxxIPv6Address.hxxSocketAddress.cxxSocketAddress.hxxSocketDescriptor.cxxSocketDescriptor.hxxSocketUtil.cxxSocketUtil.hxxStaticSocketAddress.cxxStaticSocketAddress.hxxToString.cxx
output
plugins
playlist
PlaylistAny.cxxPlaylistAny.hxxPlaylistQueue.cxxPlaylistQueue.hxxPlaylistRegistry.hxxPrint.cxxPrint.hxx
plugins
song
storage
system
tag
thread
unix
util
zeroconf
test
ConfigGlue.hxxContainerScan.cxxDumpDatabase.cxxMakeTag.hxxRunChromaprint.cxxTestTagSongFilter.cxxdump_playlist.cxxdump_text_file.cxxmeson.build
valgrind.suppressionsnet
read_tags.cxxrun_decoder.cxxrun_filter.cxxrun_input.cxxrun_neighbor_explorer.cxxrun_output.cxxtest_translate_song.cxxvisit_archive.cxxwin32/res
52
NEWS
52
NEWS
@@ -1,3 +1,55 @@
|
||||
ver 0.21.7 (2019/04/03)
|
||||
* input
|
||||
- qobuz/tidal: scan tags when loading a playlist
|
||||
* require Meson 0.49.0 for native libgcrypt-config support
|
||||
* fix build failure with -Dlocal_socket=false
|
||||
* Haiku
|
||||
- fix build
|
||||
- add version info
|
||||
|
||||
ver 0.21.6 (2019/03/17)
|
||||
* protocol
|
||||
- allow loading playlists specified as absolute filesystem paths
|
||||
- fix negated filter expressions with multiple tag values
|
||||
- fix "list" with filter expression
|
||||
- omit empty playlist names in "listplaylists"
|
||||
* input
|
||||
- cdio_paranoia: fix build failure due to missing #include
|
||||
* decoder
|
||||
- opus: fix replay gain when there are no other tags
|
||||
- opus: fix seeking to beginning of song
|
||||
- vorbis: fix Tremor conflict resulting in crash
|
||||
* output
|
||||
- pulse: work around error with unusual channel count
|
||||
- osx: fix build failure
|
||||
* playlist
|
||||
- flac: fix use-after-free bug
|
||||
* support abstract sockets on Linux
|
||||
* Windows
|
||||
- remove the unused libwinpthread-1.dll dependency
|
||||
* Android
|
||||
- enable SLES power saving mode
|
||||
|
||||
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 ..."
|
||||
|
@@ -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="29"
|
||||
android:versionName="0.21.7">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
|
||||
|
@@ -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"`
|
||||
|
||||
|
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.21.4'
|
||||
version = '0.21.7'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
@@ -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
|
||||
@@ -674,6 +690,8 @@ Valid quality values for libsoxr:
|
||||
* "low"
|
||||
* "quick"
|
||||
|
||||
.. _output_plugins:
|
||||
|
||||
Output plugins
|
||||
--------------
|
||||
|
||||
@@ -784,6 +802,15 @@ The fifo plugin writes raw PCM data to a FIFO (First In, First Out) file. The da
|
||||
* - **path P**
|
||||
- This specifies the path of the FIFO to write to. Must be an absolute path. If the path does not exist, it will be created when MPD is started, and removed when MPD is stopped. The FIFO will be created with the same user and group as MPD is running as. Default permissions can be modified by using the builtin shell command umask. If a FIFO already exists at the specified path it will be reused, and will not be removed when MPD is stopped. You can use the "mkfifo" command to create this, and then you may modify the permissions to your liking.
|
||||
|
||||
haiku
|
||||
~~~~~
|
||||
|
||||
Use the SoundPlayer API on the Haiku operating system.
|
||||
|
||||
This plugin is unmaintained and contains known bugs. It will be
|
||||
removed soon, unless there is a new maintainer.
|
||||
|
||||
|
||||
jack
|
||||
~~~~
|
||||
The jack plugin connects to a `JACK server <http://jackaudio.org/>`_.
|
||||
@@ -822,7 +849,7 @@ It is highly recommended to configure a fixed format, because a stream cannot sw
|
||||
* - **port P**
|
||||
- Binds the HTTP server to the specified port.
|
||||
* - **bind_to_address ADDR**
|
||||
- Binds the HTTP server to the specified address (IPv4, IPv6 or UNIX socket). Multiple addresses in parallel are not supported.
|
||||
- Binds the HTTP server to the specified address (IPv4, IPv6 or local socket). Multiple addresses in parallel are not supported.
|
||||
* - **encoder NAME**
|
||||
- Chooses an encoder plugin. A list of encoder plugins can be found in the encoder plugin reference :ref:`encoder_plugins`.
|
||||
* - **max_clients MC**
|
||||
@@ -935,6 +962,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 +1003,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**
|
||||
@@ -1017,8 +1048,41 @@ The "Solaris" plugin runs only on SUN Solaris, and plays via /dev/audio.
|
||||
* - **device PATH**
|
||||
- Sets the path of the audio device, defaults to /dev/audio.
|
||||
|
||||
|
||||
.. _filter_plugins:
|
||||
|
||||
Filter plugins
|
||||
--------------
|
||||
|
||||
normalize
|
||||
~~~~~~~~~
|
||||
|
||||
Normalize the volume during playback (at the expensve of quality).
|
||||
|
||||
|
||||
null
|
||||
~~~~
|
||||
|
||||
A no-op filter. Audio data is returned as-is.
|
||||
|
||||
|
||||
route
|
||||
~~~~~
|
||||
|
||||
Reroute channels.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
:header-rows: 1
|
||||
|
||||
* - Setting
|
||||
- Description
|
||||
* - **routes "0>0, 1>1, ..."**
|
||||
- Specifies the channel mapping.
|
||||
|
||||
|
||||
.. _playlist_plugins:
|
||||
|
||||
|
||||
Playlist plugins
|
||||
----------------
|
||||
|
||||
|
@@ -144,15 +144,20 @@ syntax::
|
||||
``EXPRESSION`` is a string enclosed in parantheses which can be one
|
||||
of:
|
||||
|
||||
- ``(TAG == 'VALUE')``: match a tag value.
|
||||
``(TAG != 'VALUE')``: mismatch a tag value.
|
||||
The special tag "*any*" checks all
|
||||
tag values.
|
||||
*albumartist* looks for
|
||||
- ``(TAG == 'VALUE')``: match a tag value; if there are multiple
|
||||
values of the given type, at least one must match.
|
||||
``(TAG != 'VALUE')``: mismatch a tag value; if there are multiple
|
||||
values of the given type, none of them must match.
|
||||
The special tag ``any`` checks all
|
||||
tag types.
|
||||
``AlbumArtist`` looks for
|
||||
``VALUE`` in ``AlbumArtist``
|
||||
and falls back to ``Artist`` tags if
|
||||
``AlbumArtist`` does not exist.
|
||||
``VALUE`` is what to find.
|
||||
An empty value string means: match only if the given tag type does
|
||||
not exist at all; this implies that negation with an empty value
|
||||
checks for the existence of the given tag type.
|
||||
|
||||
- ``(TAG contains 'VALUE')`` checks if the given value is a substring
|
||||
of the tag value.
|
||||
@@ -178,7 +183,7 @@ of:
|
||||
|
||||
- ``(AudioFormat =~ 'SAMPLERATE:BITS:CHANNELS')``:
|
||||
matches the audio format with the given mask (i.e. one
|
||||
or more attributes may be "*").
|
||||
or more attributes may be ``*``).
|
||||
|
||||
- ``(!EXPRESSION)``: negate an expression. Note that each expression
|
||||
must be enclosed in parantheses, e.g. :code:`(!(artist == 'VALUE'))`
|
||||
@@ -207,11 +212,11 @@ backslash.
|
||||
|
||||
Example expression which matches an artist named ``foo'bar"``::
|
||||
|
||||
(artist "foo\'bar\"")
|
||||
(Artist == "foo\'bar\"")
|
||||
|
||||
At the protocol level, the command must look like this::
|
||||
|
||||
find "(artist \"foo\\'bar\\\"\")"
|
||||
find "(Artist == \"foo\\'bar\\\"\")"
|
||||
|
||||
The double quotes enclosing the artist name must be escaped because
|
||||
they are inside a double-quoted ``find`` parameter. The single quote
|
||||
@@ -714,7 +719,9 @@ and without the `.m3u` suffix).
|
||||
Some of the commands described in this section can be used to
|
||||
run playlist plugins instead of the hard-coded simple
|
||||
`m3u` parser. They can access playlists in
|
||||
the music directory (relative path including the suffix) or
|
||||
the music directory (relative path including the suffix),
|
||||
playlists in arbitrary location (absolute path including the suffix;
|
||||
allowed only for clients that are connected via local socket), or
|
||||
remote playlists (absolute URI with a supported scheme).
|
||||
|
||||
:command:`listplaylist {NAME}`
|
||||
@@ -909,7 +916,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``,
|
||||
@@ -924,7 +931,7 @@ The music database
|
||||
This command may be used to list metadata of remote
|
||||
files (e.g. URI beginning with "http://" or "smb://").
|
||||
|
||||
Clients that are connected via UNIX domain socket may
|
||||
Clients that are connected via local socket may
|
||||
use this command to read the tags of an arbitrary local
|
||||
file (URI is an absolute path).
|
||||
|
||||
@@ -1131,7 +1138,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.
|
||||
@@ -1215,7 +1222,7 @@ Reflection
|
||||
:command:`config`
|
||||
Dumps configuration values that may be interesting for
|
||||
the client. This command is only permitted to "local"
|
||||
clients (connected via UNIX domain socket).
|
||||
clients (connected via local socket).
|
||||
|
||||
The following response attributes are available:
|
||||
|
||||
|
29
doc/user.rst
29
doc/user.rst
@@ -54,7 +54,7 @@ Download the source tarball from the `MPD home page <https://musicpd.org>`_ and
|
||||
In any case, you need:
|
||||
|
||||
* a C++14 compiler (e.g. gcc 6.0 or clang 3.9)
|
||||
* `Meson 0.47.2 <http://mesonbuild.com/>`__ and `Ninja
|
||||
* `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* Boost 1.58
|
||||
* pkg-config
|
||||
@@ -365,10 +365,14 @@ More information can be found in the :ref:`decoder_plugins` reference.
|
||||
Configuring encoder plugins
|
||||
---------------------------
|
||||
|
||||
Encoders are used by some of the output plugins (such as shout). The encoder settings are included in the audio_output section.
|
||||
Encoders are used by some of the output plugins (such as shout). The
|
||||
encoder settings are included in the ``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
More information can be found in the :ref:`encoder_plugins` reference.
|
||||
|
||||
|
||||
.. _config_audio_output:
|
||||
|
||||
Configuring audio outputs
|
||||
-------------------------
|
||||
|
||||
@@ -421,6 +425,15 @@ The following table lists the audio_output options valid for all plugins:
|
||||
implement an external mixer :ref:`external_mixer`) or no mixer
|
||||
(:samp:`none`). By default, the hardware mixer is used for
|
||||
devices which support it, and none for the others.
|
||||
* - **filters "name,...**"
|
||||
- The specified configured filters are instantiated in the given
|
||||
order. Each filter name refers to a ``filter`` block, see
|
||||
:ref:`config_filter`.
|
||||
|
||||
More information can be found in the :ref:`output_plugins` reference.
|
||||
|
||||
|
||||
.. _config_filter:
|
||||
|
||||
Configuring filters
|
||||
-------------------
|
||||
@@ -436,6 +449,9 @@ To configure a filter, add a :code:`filter` block to :file:`mpd.conf`:
|
||||
name "software volume"
|
||||
}
|
||||
|
||||
Configured filters may then be added to the ``filters`` setting of an
|
||||
``audio_output`` section, see :ref:`config_audio_output`.
|
||||
|
||||
The following table lists the filter options valid for all plugins:
|
||||
|
||||
.. list-table::
|
||||
@@ -449,6 +465,9 @@ The following table lists the filter options valid for all plugins:
|
||||
* - **name**
|
||||
- The name of the filter
|
||||
|
||||
More information can be found in the :ref:`filter_plugins` reference.
|
||||
|
||||
|
||||
Configuring playlist plugins
|
||||
----------------------------
|
||||
|
||||
@@ -531,6 +550,12 @@ choice::
|
||||
|
||||
bind_to_address "/var/run/mpd/socket"
|
||||
|
||||
On Linux, local sockets can be bound to a name without a socket inode
|
||||
on the filesystem; MPD implements this by prepending ``@`` to the
|
||||
address::
|
||||
|
||||
bind_to_address "@mpd"
|
||||
|
||||
If no port is specified, the default port is 6600. This default can
|
||||
be changed with the port setting::
|
||||
|
||||
|
25
meson.build
25
meson.build
@@ -1,8 +1,8 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.21.4',
|
||||
meson_version: '>= 0.47.2',
|
||||
version: '0.21.7',
|
||||
meson_version: '>= 0.49.0',
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
'cpp_std=c++14'
|
||||
@@ -20,7 +20,7 @@ conf.set_quoted('PACKAGE', meson.project_name())
|
||||
conf.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||
conf.set_quoted('PACKAGE_VERSION', meson.project_version())
|
||||
conf.set_quoted('VERSION', meson.project_version())
|
||||
conf.set_quoted('PROTOCOL_VERSION', '0.21.4')
|
||||
conf.set_quoted('PROTOCOL_VERSION', '0.21.6')
|
||||
conf.set_quoted('SYSTEM_CONFIG_FILE_LOCATION', join_paths(get_option('prefix'), get_option('sysconfdir'), 'mpd.conf'))
|
||||
|
||||
common_cppflags = [
|
||||
@@ -390,6 +390,7 @@ more_deps = []
|
||||
if is_android
|
||||
subdir('src/java')
|
||||
target_type = 'shared_library'
|
||||
target_name = 'mpd'
|
||||
link_args += [
|
||||
'-Wl,--no-undefined,-shared,-Bsymbolic',
|
||||
'-llog',
|
||||
@@ -399,12 +400,20 @@ if is_android
|
||||
declare_dependency(sources: [classes_jar]),
|
||||
java_dep,
|
||||
]
|
||||
elif is_haiku
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd.nores'
|
||||
link_args += [
|
||||
'-lnetwork',
|
||||
'-lbe',
|
||||
]
|
||||
else
|
||||
target_type = 'executable'
|
||||
target_name = 'mpd'
|
||||
endif
|
||||
|
||||
mpd = build_target(
|
||||
'mpd',
|
||||
target_name,
|
||||
sources,
|
||||
target_type: target_type,
|
||||
include_directories: inc,
|
||||
@@ -443,6 +452,14 @@ endif
|
||||
|
||||
if is_haiku
|
||||
subdir('src/haiku')
|
||||
custom_target(
|
||||
'mpd',
|
||||
output: 'mpd',
|
||||
input: [mpd, rsrc],
|
||||
command: [addres, '@OUTPUT@', '@INPUT0@', '@INPUT1@'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
endif
|
||||
|
||||
configure_file(output: 'config.h', configuration: conf)
|
||||
|
@@ -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')
|
||||
|
@@ -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.3.tar.xz',
|
||||
'0c3020452880581a8face91595b239198078645e7d7184273b8bcc7758beb63d',
|
||||
'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.1.tar.xz',
|
||||
'9252332a7f871ce37bfa7f78bdd0a0e3924d8187cc27cb57c76c9474a7168fb3',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -375,8 +375,8 @@ libexpat = AutotoolsProject(
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz',
|
||||
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5',
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
|
||||
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -387,12 +387,12 @@ libnfs = AutotoolsProject(
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-3.0.0',
|
||||
base='libnfs-libnfs-4.0.0',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
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',
|
||||
)
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 */
|
||||
|
89
src/Main.cxx
89
src/Main.cxx
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -37,9 +37,6 @@ class AllocatedPath;
|
||||
void
|
||||
mapper_init(AllocatedPath &&playlist_dir);
|
||||
|
||||
void
|
||||
mapper_finish() noexcept;
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
/**
|
||||
|
@@ -134,7 +134,9 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
||||
const auto *const name_fs_end =
|
||||
FindStringSuffix(name_fs_str,
|
||||
PATH_LITERAL(PLAYLIST_FILE_SUFFIX));
|
||||
if (name_fs_end == nullptr)
|
||||
if (name_fs_end == nullptr ||
|
||||
/* no empty playlist names (raw file name = ".m3u") */
|
||||
name_fs_end == name_fs_str)
|
||||
return false;
|
||||
|
||||
FileInfo fi;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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]);
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -285,11 +285,6 @@ command_init()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
command_finish()
|
||||
{
|
||||
}
|
||||
|
||||
static const struct command *
|
||||
command_lookup(const char *name)
|
||||
{
|
||||
|
@@ -268,7 +268,10 @@ handle_list(Client &client, Request args, Response &r)
|
||||
std::unique_ptr<SongFilter> filter;
|
||||
TagType group = TAG_NUM_OF_ITEM_TYPES;
|
||||
|
||||
if (args.size == 1) {
|
||||
if (args.size == 1 &&
|
||||
/* parantheses are the syntax for filter expressions: no
|
||||
compatibility mode */
|
||||
args.front()[0] != '(') {
|
||||
/* for compatibility with < 0.12.0 */
|
||||
if (tagType != TAG_ALBUM) {
|
||||
r.FormatError(ACK_ERROR_ARG,
|
||||
|
@@ -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",
|
||||
|
@@ -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
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "PlaylistCommands.hxx"
|
||||
#include "Request.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "db/DatabasePlaylist.hxx"
|
||||
#include "CommandError.hxx"
|
||||
#include "PlaylistSave.hxx"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "PlaylistError.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "SongLoader.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "BulkEdit.hxx"
|
||||
#include "playlist/PlaylistQueue.hxx"
|
||||
#include "playlist/Print.hxx"
|
||||
@@ -38,6 +40,7 @@
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/ChronoUtil.hxx"
|
||||
#include "LocateUri.hxx"
|
||||
|
||||
bool
|
||||
playlist_commands_available() noexcept
|
||||
@@ -66,22 +69,41 @@ handle_save(Client &client, Request args, gcc_unused Response &r)
|
||||
CommandResult
|
||||
handle_load(Client &client, Request args, gcc_unused Response &r)
|
||||
{
|
||||
const auto uri = LocateUri(args.front(), &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
);
|
||||
RangeArg range = args.ParseOptional(1, RangeArg::All());
|
||||
|
||||
const ScopeBulkEdit bulk_edit(client.GetPartition());
|
||||
|
||||
auto &playlist = client.GetPlaylist();
|
||||
const unsigned old_size = playlist.GetLength();
|
||||
|
||||
const SongLoader loader(client);
|
||||
playlist_open_into_queue(args.front(),
|
||||
playlist_open_into_queue(uri,
|
||||
range.start, range.end,
|
||||
client.GetPlaylist(),
|
||||
playlist,
|
||||
client.GetPlayerControl(), loader);
|
||||
|
||||
/* invoke the RemoteTagScanner on all newly added songs */
|
||||
auto &instance = client.GetInstance();
|
||||
const unsigned new_size = playlist.GetLength();
|
||||
for (unsigned i = old_size; i < new_size; ++i)
|
||||
instance.LookupRemoteTag(playlist.queue.Get(i).GetURI());
|
||||
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_listplaylist(Client &client, Request args, Response &r)
|
||||
{
|
||||
const char *const name = args.front();
|
||||
const auto name = LocateUri(args.front(), &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
);
|
||||
|
||||
if (playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
||||
name, false))
|
||||
@@ -93,7 +115,11 @@ handle_listplaylist(Client &client, Request args, Response &r)
|
||||
CommandResult
|
||||
handle_listplaylistinfo(Client &client, Request args, Response &r)
|
||||
{
|
||||
const char *const name = args.front();
|
||||
const auto name = LocateUri(args.front(), &client
|
||||
#ifdef ENABLE_DATABASE
|
||||
, nullptr
|
||||
#endif
|
||||
);
|
||||
|
||||
if (playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
||||
name, true))
|
||||
|
@@ -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);
|
||||
|
@@ -29,6 +29,8 @@ ServerSocketAddGeneric(ServerSocket &server_socket, const char *address, unsigne
|
||||
server_socket.AddPort(port);
|
||||
} else if (address[0] == '/' || address[0] == '~') {
|
||||
server_socket.AddPath(ParsePath(address));
|
||||
} else if (address[0] == '@') {
|
||||
server_socket.AddAbstract(address);
|
||||
} else {
|
||||
server_socket.AddHost(address, port);
|
||||
}
|
||||
|
@@ -23,14 +23,14 @@
|
||||
class ServerSocket;
|
||||
|
||||
/**
|
||||
* Sets the address or unix socket of a ServerSocket instance
|
||||
* Sets the address or local socket of a ServerSocket instance
|
||||
* There are three possible ways
|
||||
* 1) Set address to a valid ip address and specify port.
|
||||
* server_socket will listen on this address/port tuple.
|
||||
* 2) Set address to null and specify port.
|
||||
* server_socket will listen on ANY address on that port.
|
||||
* 3) Set address to a path of a unix socket. port is ignored.
|
||||
* server_socket will listen on this unix socket.
|
||||
* 3) Set address to a path of a local socket. port is ignored.
|
||||
* server_socket will listen on this local socket.
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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
29
src/db/Ptr.hxx
Normal 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
|
@@ -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,
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "Visitor.hxx"
|
||||
#include "Selection.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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");
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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]))
|
||||
|
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static constexpr Domain audiofile_domain("audiofile");
|
||||
|
||||
|
@@ -39,8 +39,8 @@ InitHybridDsdDecoder(const ConfigBlock &block)
|
||||
without a DSD DAC, the PCM (=ALAC) part of the file is
|
||||
better */
|
||||
if (block.GetBlockParam("enabled") == nullptr) {
|
||||
LogInfo(hybrid_dsd_domain,
|
||||
"The Hybrid DSD decoder is disabled because it was not explicitly enabled");
|
||||
LogDebug(hybrid_dsd_domain,
|
||||
"The Hybrid DSD decoder is disabled because it was not explicitly enabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -208,10 +208,12 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
||||
TagBuilder tag_builder;
|
||||
AddTagHandler h(tag_builder);
|
||||
|
||||
if (ScanOpusTags(packet.packet, packet.bytes, &rgi, h) &&
|
||||
!tag_builder.empty()) {
|
||||
client.SubmitReplayGain(&rgi);
|
||||
if (!ScanOpusTags(packet.packet, packet.bytes, &rgi, h))
|
||||
return;
|
||||
|
||||
client.SubmitReplayGain(&rgi);
|
||||
|
||||
if (!tag_builder.empty()) {
|
||||
Tag tag = tag_builder.Commit();
|
||||
auto cmd = client.SubmitTag(input_stream, std::move(tag));
|
||||
if (cmd != DecoderCommand::NONE)
|
||||
|
@@ -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,
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -392,7 +392,29 @@ ServerSocket::AddPath(AllocatedPath &&path)
|
||||
#else /* !HAVE_UN */
|
||||
(void)path;
|
||||
|
||||
throw std::runtime_error("UNIX domain socket support is disabled");
|
||||
throw std::runtime_error("Local socket support is disabled");
|
||||
#endif /* !HAVE_UN */
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ServerSocket::AddAbstract(const char *name)
|
||||
{
|
||||
#if !defined(__linux__)
|
||||
(void)name;
|
||||
|
||||
throw std::runtime_error("Abstract sockets are only available on Linux");
|
||||
#elif !defined(HAVE_UN)
|
||||
(void)name;
|
||||
|
||||
throw std::runtime_error("Local socket support is disabled");
|
||||
#else
|
||||
assert(name != nullptr);
|
||||
assert(*name == '@');
|
||||
|
||||
AllocatedSocketAddress address;
|
||||
address.SetLocal(name);
|
||||
|
||||
AddAddress(std::move(address));
|
||||
#endif
|
||||
}
|
||||
|
@@ -90,7 +90,7 @@ public:
|
||||
void AddHost(const char *hostname, unsigned port);
|
||||
|
||||
/**
|
||||
* Add a listener on a Unix domain socket.
|
||||
* Add a listener on a local socket.
|
||||
*
|
||||
* Throws #std::runtime_error on error.
|
||||
*
|
||||
@@ -99,6 +99,16 @@ public:
|
||||
*/
|
||||
void AddPath(AllocatedPath &&path);
|
||||
|
||||
/**
|
||||
* Add a listener on an abstract local socket (Linux specific).
|
||||
*
|
||||
* Throws on error.
|
||||
*
|
||||
* @param name the abstract socket name, starting with a '@'
|
||||
* instead of a null byte
|
||||
*/
|
||||
void AddAbstract(const char *name);
|
||||
|
||||
/**
|
||||
* Add a socket descriptor that is accepting connections. After this
|
||||
* has been called, don't call server_socket_open(), because the
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
/**
|
||||
* Flush pending data and return it. This should be called
|
||||
* repepatedly until it returns nullptr.
|
||||
* repeatedly until it returns nullptr.
|
||||
*/
|
||||
virtual ConstBuffer<void> Flush();
|
||||
};
|
||||
|
@@ -56,6 +56,7 @@ public:
|
||||
}
|
||||
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
|
||||
ConstBuffer<void> Flush() override;
|
||||
};
|
||||
|
||||
class PreparedAutoConvertFilter final : public PreparedFilter {
|
||||
@@ -104,6 +105,18 @@ AutoConvertFilter::FilterPCM(ConstBuffer<void> src)
|
||||
return filter->FilterPCM(src);
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
AutoConvertFilter::Flush()
|
||||
{
|
||||
if (convert != nullptr) {
|
||||
auto result = convert->Flush();
|
||||
if (!result.IsNull())
|
||||
return filter->FilterPCM(result);
|
||||
}
|
||||
|
||||
return filter->Flush();
|
||||
}
|
||||
|
||||
std::unique_ptr<PreparedFilter>
|
||||
autoconvert_filter_new(std::unique_ptr<PreparedFilter> filter) noexcept
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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());
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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 {
|
||||
|
3
src/haiku/add_resources.sh
Executable file
3
src/haiku/add_resources.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cp "$2" "$1" && xres -o "$1" -- "$3" && mimeset -f "$1" || (rm -f "$1"; exit 1)
|
@@ -1,18 +1,26 @@
|
||||
rc = meson.find_program('rc')
|
||||
xres = meson.find_program('xres')
|
||||
haiku_conf = configuration_data()
|
||||
haiku_conf.set('VERSION', meson.project_version())
|
||||
|
||||
splitted_version = meson.project_version().split('~')[0].split('.')
|
||||
haiku_conf.set('VERSION_MAJOR', splitted_version[0])
|
||||
haiku_conf.set('VERSION_MINOR', splitted_version.get(1, '0'))
|
||||
haiku_conf.set('VERSION_REVISION', splitted_version.get(2, '0'))
|
||||
haiku_conf.set('VERSION_EXTRA', splitted_version.get(3, '0'))
|
||||
|
||||
mpd_rdef = configure_file(
|
||||
input: 'mpd.rdef.in',
|
||||
output: 'mpd.rdef',
|
||||
configuration: haiku_conf,
|
||||
)
|
||||
|
||||
rc = find_program('rc')
|
||||
xres = find_program('xres')
|
||||
|
||||
rsrc = custom_target(
|
||||
'mpd.rsrc',
|
||||
output: 'mpd.rsrc',
|
||||
input: 'mpd.rdef',
|
||||
input: mpd_rdef,
|
||||
command: [rc, '-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
|
||||
custom_target(
|
||||
'mpd.rsrc',
|
||||
output: 'mpd',
|
||||
input: [mpd, rsrc],
|
||||
command: [xres, '-o', '@OUTPUT@', '--', '@INPUT@'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
addres = files('add_resources.sh')
|
||||
|
@@ -2,7 +2,15 @@ resource app_signature "application/x-vnd.MusicPD";
|
||||
|
||||
resource app_flags B_BACKGROUND_APP;
|
||||
|
||||
// TODO: resource app_version {};
|
||||
resource app_version {
|
||||
major = @VERSION_MAJOR@,
|
||||
middle = @VERSION_MINOR@,
|
||||
minor = @VERSION_REVISION@,
|
||||
variety = B_APPV_ALPHA,
|
||||
internal = @VERSION_EXTRA@,
|
||||
short_info = "Music Player Daemon @VERSION@",
|
||||
long_info = "Music Player Daemon @VERSION@ ©The Music Player Daemon Project"
|
||||
};
|
||||
|
||||
resource vector_icon {
|
||||
$"6E6369661F050102031604BEE29BBEC5403EC540BEE29B4A10004A10000001C6"
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -306,9 +306,8 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block)
|
||||
{
|
||||
try {
|
||||
curl_init = new CurlInit(event_loop);
|
||||
} catch (const std::runtime_error &e) {
|
||||
LogError(e);
|
||||
throw PluginUnavailable(e.what());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(PluginUnavailable("CURL initialization failed"));
|
||||
}
|
||||
|
||||
const auto version_info = curl_version_info(CURLVERSION_FIRST);
|
||||
|
@@ -28,8 +28,6 @@
|
||||
|
||||
#include <libsmbclient.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
class SmbclientInputStream final : public InputStream {
|
||||
SMBCCTX *ctx;
|
||||
int fd;
|
||||
@@ -72,9 +70,8 @@ input_smbclient_init(EventLoop &, const ConfigBlock &)
|
||||
{
|
||||
try {
|
||||
SmbclientInit();
|
||||
} catch (const std::runtime_error &e) {
|
||||
// TODO: use std::throw_with_nested()?
|
||||
throw PluginUnavailable(e.what());
|
||||
} catch (...) {
|
||||
std::throw_with_nested(PluginUnavailable("libsmbclient initialization failed"));
|
||||
}
|
||||
|
||||
// TODO: create one global SMBCCTX here?
|
||||
|
@@ -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()
|
||||
|
166
src/lib/cdio/Paranoia.hxx
Normal file
166
src/lib/cdio/Paranoia.hxx
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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
|
@@ -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) {
|
||||
|
@@ -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.
|
||||
|
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -1,4 +1,17 @@
|
||||
# Since version 0.49.0 Meson has native libgcrypt dependency support, which has
|
||||
# the advantage over find_library() as it uses libgcrypt-config to query the
|
||||
# required linker flags.
|
||||
# However, we still need to use find_library() first, to prevent Meson
|
||||
# falsly assuming a target libgcrypt is available in case there is no
|
||||
# libgcrypt-config entry in the cross file and libgcrypt is installed on the
|
||||
# host. In this case, Meson will falsly use the native libgcrypt-config and
|
||||
# will falsly assume it has found the gcrypt library for the target.
|
||||
#
|
||||
# See: https://github.com/MusicPlayerDaemon/MPD/pull/495
|
||||
gcrypt_dep = c_compiler.find_library('gcrypt', required: get_option('qobuz'))
|
||||
if gcrypt_dep.found()
|
||||
gcrypt_dep = dependency('libgcrypt')
|
||||
endif
|
||||
if not gcrypt_dep.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "OggVisitor.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
void
|
||||
OggVisitor::EndStream()
|
||||
@@ -51,7 +52,13 @@ OggVisitor::ReadNextPage()
|
||||
inline void
|
||||
OggVisitor::HandlePacket(const ogg_packet &packet)
|
||||
{
|
||||
const bool _post_seek = std::exchange(post_seek, false);
|
||||
|
||||
if (packet.b_o_s) {
|
||||
if (_post_seek)
|
||||
/* ignore the BOS packet after seeking */
|
||||
return;
|
||||
|
||||
EndStream();
|
||||
has_stream = true;
|
||||
OnOggBeginning(packet);
|
||||
@@ -97,4 +104,6 @@ OggVisitor::PostSeek()
|
||||
|
||||
/* find the next Ogg page and feed it into the stream */
|
||||
sync.ExpectPageSeekIn(stream);
|
||||
|
||||
post_seek = true;
|
||||
}
|
||||
|
@@ -39,6 +39,14 @@ class OggVisitor {
|
||||
|
||||
bool has_stream = false;
|
||||
|
||||
/**
|
||||
* This is true after seeking; its one-time effect is to
|
||||
* ignore the BOS packet, just in case we have been seeking to
|
||||
* the beginning of the file, because that would disrupt
|
||||
* playback.
|
||||
*/
|
||||
bool post_seek = false;
|
||||
|
||||
public:
|
||||
explicit OggVisitor(Reader &reader)
|
||||
:sync(reader), stream(0) {}
|
||||
|
@@ -1,6 +1,24 @@
|
||||
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'))
|
||||
|
||||
if get_option('tremor').enabled()
|
||||
# no libvorbis if Tremor was explicitly enabled
|
||||
libvorbis_dep = dependency('', required: false)
|
||||
else
|
||||
libvorbis_dep = dependency('vorbis', required: get_option('vorbis'))
|
||||
endif
|
||||
|
||||
if libvorbis_dep.found()
|
||||
# no Tremor if libvorbis is used
|
||||
libvorbisidec_dep = dependency('', required: false)
|
||||
else
|
||||
# attempt to auto-detect Tremor only if libvorbis was disabled or not found
|
||||
libvorbisidec_dep = dependency('vorbisidec', required: get_option('tremor'))
|
||||
endif
|
||||
|
||||
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 +31,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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
@@ -315,6 +315,13 @@ SocketDescriptor::SetTcpDeferAccept(const int &seconds) noexcept
|
||||
return SetOption(IPPROTO_TCP, TCP_DEFER_ACCEPT, &seconds, sizeof(seconds));
|
||||
}
|
||||
|
||||
bool
|
||||
SocketDescriptor::SetTcpUserTimeout(const unsigned &milliseconds) noexcept
|
||||
{
|
||||
return SetOption(IPPROTO_TCP, TCP_USER_TIMEOUT,
|
||||
&milliseconds, sizeof(milliseconds));
|
||||
}
|
||||
|
||||
bool
|
||||
SocketDescriptor::SetV6Only(bool value) noexcept
|
||||
{
|
||||
|
@@ -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
|
||||
@@ -188,6 +188,12 @@ public:
|
||||
bool SetCork(bool value=true) noexcept;
|
||||
|
||||
bool SetTcpDeferAccept(const int &seconds) noexcept;
|
||||
|
||||
/**
|
||||
* Setter for TCP_USER_TIMEOUT.
|
||||
*/
|
||||
bool SetTcpUserTimeout(const unsigned &milliseconds) noexcept;
|
||||
|
||||
bool SetV6Only(bool value) noexcept;
|
||||
|
||||
/**
|
||||
|
@@ -35,7 +35,7 @@ socket_bind_listen(int domain, int type, int protocol,
|
||||
throw MakeSocketError("Failed to create socket");
|
||||
|
||||
#ifdef HAVE_UN
|
||||
if (domain == AF_UNIX) {
|
||||
if (domain == AF_LOCAL) {
|
||||
/* Prevent access until right permissions are set */
|
||||
fchmod(fd.Get(), 0);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class SocketAddress;
|
||||
/**
|
||||
* Creates a socket listening on the specified address. This is a
|
||||
* shortcut for socket(), bind() and listen().
|
||||
* When a unix socket is created (domain == AF_UNIX), its
|
||||
* When a local socket is created (domain == AF_LOCAL), its
|
||||
* permissions will be stripped down to prevent unauthorized
|
||||
* access. The caller is responsible to apply proper permissions
|
||||
* at a later point.
|
||||
|
@@ -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 "StaticSocketAddress.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -50,6 +51,16 @@ StaticSocketAddress::operator=(SocketAddress other) noexcept
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef HAVE_UN
|
||||
|
||||
StringView
|
||||
StaticSocketAddress::GetLocalRaw() const noexcept
|
||||
{
|
||||
return SocketAddress(*this).GetLocalRaw();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TCP
|
||||
|
||||
bool
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user