]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
paraslash 0.7.3 master v0.7.3
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 12 Mar 2024 18:02:39 +0000 (19:02 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 12 Mar 2024 18:02:39 +0000 (19:02 +0100)
124 files changed:
.gitignore
Doxyfile
Makefile.real
NEWS.md
aac_afh.c
aacdec_filter.c
acl.c
afh_recv.c
afs.c
afs.h
aft.c
alsa_mix.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.h
audiod_command.c
autogen.sh
base64.c
bitstream.c
blob.c
buffer_tree.c
check_wav.c
check_wav.h
chunk_queue.c
client.c
client_common.c
close_on_fork.c
command.c
compress_filter.c
configure.ac
crypt.h
crypt_common.c
daemon.c
daemon.h
dccp_recv.c
dccp_send.c
error.h
fd.c
fd.h
fec.c
fecdec_filter.c
file_write.c
filter.c
filter.h
filter_common.c
flac_afh.c
flacdec_filter.c
gcrypt.c
grab_client.c
gui.c
gui_theme.c
http_recv.c
http_send.c
imdct.c
interactive.c
interactive.h
m4/lls/audiod_cmd.suite.m4
m4/lls/include/com_ll.m4 [new file with mode: 0644]
m4/lls/makefile
m4/lls/mixer.suite.m4
m4/lls/server_cmd.suite.m4
mixer.c
mood.c
mood.h [deleted file]
mp.c
mp3_afh.c
mp3dec_filter.c
mp4.c [new file with mode: 0644]
mp4.h [new file with mode: 0644]
net.c
net.h
ogg_afh_common.c
ogg_afh_common.h
oggdec_filter.c
openssl.c
opus_afh.c
opusdec_filter.c
oss_mix.c
oss_write.c
para.h
play.c
playlist.c
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
ringbuffer.c
sched.c
sched.h
score.c
send.h
send_common.c
server.c
server.h
sideband.c
signal.c
signal.h
spx_afh.c
spxdec_filter.c
stdin.c
stdout.c
string.c
string.h
sync_filter.c
t/audio_files/short-44100-2.mp3 [new file with mode: 0644]
t/t0004-server.sh
t/test-lib.sh
udp_recv.c
udp_send.c
user_list.c
vss.c
wav_filter.c
web/manual.md
wma_afh.c
wmadec_filter.c
write.c
write.h
write_common.c
yy/mp.y

index bd5e04801c4bec2714dd515810296f1bc89cbab0..8f8d0af736cee205b638502171eea526d613cc22 100644 (file)
@@ -24,3 +24,4 @@ confdefs.h
 conftest
 conftest.c
 git-version.h
 conftest
 conftest.c
 git-version.h
+*-local*
index b11683e424443baef8754b34097ad3cb8dbed4fe..58e8023973d510edbe95c74148b55b5ce6aa1796 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.11
+# Doxyfile 1.8.17
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 # Project related configuration options
 #---------------------------------------------------------------------------
 
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
-# for the list of possible encodings.
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
 # The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
 # The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
@@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES    = NO
 
 OUTPUT_LANGUAGE        = English
 
 
 OUTPUT_LANGUAGE        = English
 
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
 # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
 # descriptions after the members that are listed in the file and class
 # documentation (similar to Javadoc). Set to NO to disable this.
 # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
 # descriptions after the members that are listed in the file and class
 # documentation (similar to Javadoc). Set to NO to disable this.
@@ -179,6 +187,16 @@ SHORT_NAMES            = NO
 
 JAVADOC_AUTOBRIEF      = YES
 
 
 JAVADOC_AUTOBRIEF      = YES
 
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
 # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
 # line (until the first dot) of a Qt-style comment as the brief description. If
 # set to NO, the Qt-style will behave just like regular Qt-style comments (thus
 # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
 # line (until the first dot) of a Qt-style comment as the brief description. If
 # set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -226,16 +244,15 @@ TAB_SIZE               = 8
 # will allow you to put the command \sideeffect (or @sideeffect) in the
 # documentation, which will result in a user-defined paragraph with heading
 # "Side Effects:". You can put \n's in the value part of an alias to insert
 # will allow you to put the command \sideeffect (or @sideeffect) in the
 # documentation, which will result in a user-defined paragraph with heading
 # "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines.
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
 
 ALIASES                =
 
 
 ALIASES                =
 
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST              =
-
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
 # only. Doxygen will then generate output that is more tailored for C. For
 # instance, some of the names that are used will be different. The list of all
 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
 # only. Doxygen will then generate output that is more tailored for C. For
 # instance, some of the names that are used will be different. The list of all
@@ -264,17 +281,26 @@ OPTIMIZE_FOR_FORTRAN   = NO
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
 # Doxygen selects the parser to use depending on the extension of the files it
 # parses. With this tag you can assign which parser to use for a given
 # extension. Doxygen has a built-in mapping, but you can override or extend it
 # using this tag. The format is ext=language, where ext is a file extension, and
 # Doxygen selects the parser to use depending on the extension of the files it
 # parses. With this tag you can assign which parser to use for a given
 # extension. Doxygen has a built-in mapping, but you can override or extend it
 # using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
-# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
-# Fortran. In the later case the parser tries to guess whether the code is fixed
-# or free formatted code, this is the default for Fortran type files), VHDL. For
-# instance to make doxygen treat .inc files as Fortran files (default is PHP),
-# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is
+# Fortran), use: inc=Fortran f=C.
 #
 # Note: For files without extension you can use no_extension as a placeholder.
 #
 #
 # Note: For files without extension you can use no_extension as a placeholder.
 #
@@ -285,7 +311,7 @@ EXTENSION_MAPPING      =
 
 # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
 # according to the Markdown format, which allows for more readable
 
 # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
 # according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
 # The output of markdown processing is further processed by doxygen, so you can
 # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
 # case of backward compatibilities issues.
 # The output of markdown processing is further processed by doxygen, so you can
 # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
 # case of backward compatibilities issues.
@@ -293,6 +319,15 @@ EXTENSION_MAPPING      =
 
 MARKDOWN_SUPPORT       = YES
 
 
 MARKDOWN_SUPPORT       = YES
 
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 5
+
 # When enabled doxygen tries to link words that correspond to documented
 # classes, or namespaces to their corresponding documentation. Such a link can
 # be prevented in individual cases by putting a % sign in front of the word or
 # When enabled doxygen tries to link words that correspond to documented
 # classes, or namespaces to their corresponding documentation. Such a link can
 # be prevented in individual cases by putting a % sign in front of the word or
@@ -318,7 +353,7 @@ BUILTIN_STL_SUPPORT    = NO
 CPP_CLI_SUPPORT        = NO
 
 # Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
 CPP_CLI_SUPPORT        = NO
 
 # Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
 # will parse them like normal C++ but will assume all classes use public instead
 # of private inheritance when no explicit protection keyword is present.
 # The default value is: NO.
 # will parse them like normal C++ but will assume all classes use public instead
 # of private inheritance when no explicit protection keyword is present.
 # The default value is: NO.
@@ -424,6 +459,12 @@ EXTRACT_ALL            = YES
 
 EXTRACT_PRIVATE        = NO
 
 
 EXTRACT_PRIVATE        = NO
 
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
 # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
 # scope will be included in the documentation.
 # The default value is: NO.
 # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
 # scope will be included in the documentation.
 # The default value is: NO.
@@ -478,8 +519,8 @@ HIDE_UNDOC_MEMBERS     = NO
 HIDE_UNDOC_CLASSES     = NO
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
 HIDE_UNDOC_CLASSES     = NO
 
 # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
 # The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 # The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
@@ -502,7 +543,7 @@ INTERNAL_DOCS          = NO
 # names in lower-case letters. If set to YES, upper-case letters are also
 # allowed. This is useful if you have classes or files whose names only differ
 # in case and if your file system supports case sensitive file names. Windows
 # names in lower-case letters. If set to YES, upper-case letters are also
 # allowed. This is useful if you have classes or files whose names only differ
 # in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
+# (including Cygwin) ands Mac users are advised to set this option to NO.
 # The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
 # The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
@@ -689,7 +730,7 @@ LAYOUT_FILE            =
 # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
 # the reference definitions. This must be a list of .bib files. The .bib
 # extension is automatically appended if omitted. This requires the bibtex tool
 # The CITE_BIB_FILES tag can be used to specify one or more bib files containing
 # the reference definitions. This must be a list of .bib files. The .bib
 # extension is automatically appended if omitted. This requires the bibtex tool
-# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
 # For LaTeX the style of the bibliography can be controlled using
 # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
 # search path. See also \cite for info how to create references.
 # For LaTeX the style of the bibliography can be controlled using
 # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
 # search path. See also \cite for info how to create references.
@@ -734,7 +775,8 @@ WARN_IF_DOC_ERROR      = YES
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
 # value. If set to NO, doxygen will only warn about wrong or incomplete
 # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
 # are documented, but have no documentation for their parameters or return
 # value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation.
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
 # The default value is: NO.
 
 WARN_NO_PARAMDOC       = YES
 # The default value is: NO.
 
 WARN_NO_PARAMDOC       = YES
@@ -776,7 +818,7 @@ INPUT                  = .
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
 # possible encodings.
 # The default value is: UTF-8.
 
 # possible encodings.
 # The default value is: UTF-8.
 
@@ -793,8 +835,10 @@ INPUT_ENCODING         = UTF-8
 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
 # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
 # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
 # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
 # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
-# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
+# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
 
 FILE_PATTERNS          = *.c \
                          *.h
 
 FILE_PATTERNS          = *.c \
                          *.h
@@ -950,7 +994,7 @@ INLINE_SOURCES         = NO
 STRIP_CODE_COMMENTS    = YES
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
 STRIP_CODE_COMMENTS    = YES
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
+# entity all documented functions referencing it will be listed.
 # The default value is: NO.
 
 REFERENCED_BY_RELATION = YES
 # The default value is: NO.
 
 REFERENCED_BY_RELATION = YES
@@ -982,12 +1026,12 @@ SOURCE_TOOLTIPS        = YES
 # If the USE_HTAGS tag is set to YES then the references to source code will
 # point to the HTML generated by the htags(1) tool instead of doxygen built-in
 # source browser. The htags tool is part of GNU's global source tagging system
 # If the USE_HTAGS tag is set to YES then the references to source code will
 # point to the HTML generated by the htags(1) tool instead of doxygen built-in
 # source browser. The htags tool is part of GNU's global source tagging system
-# (see http://www.gnu.org/software/global/global.html). You will need version
+# (see https://www.gnu.org/software/global/global.html). You will need version
 # 4.8.6 or higher.
 #
 # To use it do the following:
 # - Install the latest version of global
 # 4.8.6 or higher.
 #
 # To use it do the following:
 # - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
 # - Make sure the INPUT points to the root of the source tree
 # - Run doxygen as normal
 #
 # - Make sure the INPUT points to the root of the source tree
 # - Run doxygen as normal
 #
@@ -1127,7 +1171,7 @@ HTML_EXTRA_FILES       =
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
 # will adjust the colors in the style sheet and background images according to
 # this color. Hue is specified as an angle on a colorwheel, see
 # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
 # will adjust the colors in the style sheet and background images according to
 # this color. Hue is specified as an angle on a colorwheel, see
-# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
 # purple, and 360 is red again.
 # Minimum value: 0, maximum value: 359, default value: 220.
 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
 # purple, and 360 is red again.
 # Minimum value: 0, maximum value: 359, default value: 220.
@@ -1163,6 +1207,17 @@ HTML_COLORSTYLE_GAMMA  = 80
 
 HTML_TIMESTAMP         = YES
 
 
 HTML_TIMESTAMP         = YES
 
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
 # page has loaded.
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
 # page has loaded.
@@ -1186,13 +1241,13 @@ HTML_INDEX_NUM_ENTRIES = 100
 
 # If the GENERATE_DOCSET tag is set to YES, additional index files will be
 # generated that can be used as input for Apple's Xcode 3 integrated development
 
 # If the GENERATE_DOCSET tag is set to YES, additional index files will be
 # generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: http://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
 # Makefile in the HTML output directory. Running make will produce the docset in
 # that directory and running make install will install the docset in
 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
 # Makefile in the HTML output directory. Running make will produce the docset in
 # that directory and running make install will install the docset in
 # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
@@ -1231,7 +1286,7 @@ DOCSET_PUBLISHER_NAME  = Publisher
 # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
 # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
 # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
 # additional HTML index files: index.hhp, index.hhc, and index.hhk. The
 # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
 # Windows.
 #
 # The HTML Help Workshop contains a compiler that can convert all HTML output
 # Windows.
 #
 # The HTML Help Workshop contains a compiler that can convert all HTML output
@@ -1307,7 +1362,7 @@ QCH_FILE               =
 
 # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
 # Project output. For more information please see Qt Help Project / Namespace
 
 # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
 # Project output. For more information please see Qt Help Project / Namespace
-# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
 # The default value is: org.doxygen.Project.
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 # The default value is: org.doxygen.Project.
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1315,7 +1370,7 @@ QHP_NAMESPACE          = org.doxygen.Project
 
 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
 # Help Project output. For more information please see Qt Help Project / Virtual
 
 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
 # Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
 # folders).
 # The default value is: doc.
 # This tag requires that the tag GENERATE_QHP is set to YES.
 # folders).
 # The default value is: doc.
 # This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1324,7 +1379,7 @@ QHP_VIRTUAL_FOLDER     = doc
 
 # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
 # filter to add. For more information please see Qt Help Project / Custom
 
 # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
 # filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1332,7 +1387,7 @@ QHP_CUST_FILTER_NAME   =
 
 # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
 # custom filter to add. For more information please see Qt Help Project / Custom
 
 # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
 # custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 # filters).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
@@ -1340,7 +1395,7 @@ QHP_CUST_FILTER_ATTRS  =
 
 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
 # project's filter section matches. Qt Help Project / Filter Attributes (see:
 
 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
 # project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_SECT_FILTER_ATTRS  =
 # This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_SECT_FILTER_ATTRS  =
@@ -1433,7 +1488,7 @@ EXT_LINKS_IN_WINDOW    = NO
 
 FORMULA_FONTSIZE       = 10
 
 
 FORMULA_FONTSIZE       = 10
 
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
 # generated for formulas are transparent PNGs. Transparent PNGs are not
 # supported properly for IE 6.0, but are supported on all modern browsers.
 #
 # generated for formulas are transparent PNGs. Transparent PNGs are not
 # supported properly for IE 6.0, but are supported on all modern browsers.
 #
@@ -1444,8 +1499,14 @@ FORMULA_FONTSIZE       = 10
 
 FORMULA_TRANSPARENT    = YES
 
 
 FORMULA_TRANSPARENT    = YES
 
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
 # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
 # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# http://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
 # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
 # installed or if you want to formulas look prettier in the HTML output. When
 # enabled you may also need to install MathJax separately and configure the path
 # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
 # installed or if you want to formulas look prettier in the HTML output. When
 # enabled you may also need to install MathJax separately and configure the path
@@ -1472,8 +1533,8 @@ MATHJAX_FORMAT         = HTML-CSS
 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
 # Content Delivery Network so you can quickly see the result without installing
 # MathJax. However, it is strongly recommended to install a local copy of
 # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
 # Content Delivery Network so you can quickly see the result without installing
 # MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from http://www.mathjax.org before deployment.
-# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
 # This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
@@ -1515,7 +1576,7 @@ MATHJAX_CODEFILE       =
 SEARCHENGINE           = NO
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
 SEARCHENGINE           = NO
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
 # are two flavors of web server based searching depending on the EXTERNAL_SEARCH
 # setting. When disabled, doxygen will generate a PHP script for searching and
 # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
 # are two flavors of web server based searching depending on the EXTERNAL_SEARCH
 # setting. When disabled, doxygen will generate a PHP script for searching and
 # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1534,7 +1595,7 @@ SERVER_BASED_SEARCH    = NO
 #
 # Doxygen ships with an example indexer (doxyindexer) and search engine
 # (doxysearch.cgi) which are based on the open source search engine library
 #
 # Doxygen ships with an example indexer (doxyindexer) and search engine
 # (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/).
+# Xapian (see: https://xapian.org/).
 #
 # See the section "External Indexing and Searching" for details.
 # The default value is: NO.
 #
 # See the section "External Indexing and Searching" for details.
 # The default value is: NO.
@@ -1547,7 +1608,7 @@ EXTERNAL_SEARCH        = NO
 #
 # Doxygen ships with an example indexer (doxyindexer) and search engine
 # (doxysearch.cgi) which are based on the open source search engine library
 #
 # Doxygen ships with an example indexer (doxyindexer) and search engine
 # (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
 # Searching" for details.
 # This tag requires that the tag SEARCHENGINE is set to YES.
 
 # Searching" for details.
 # This tag requires that the tag SEARCHENGINE is set to YES.
 
@@ -1599,21 +1660,35 @@ LATEX_OUTPUT           = latex
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
 # invoked.
 #
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
 # invoked.
 #
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
 # index for LaTeX.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
 # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
 # index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
 # The default file is: makeindex.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
 # The default file is: makeindex.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
 # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
 # documents. This may be useful for small projects and may help to save some
 # trees in general.
 # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
 # documents. This may be useful for small projects and may help to save some
 # trees in general.
@@ -1629,7 +1704,7 @@ COMPACT_LATEX          = NO
 # The default value is: a4.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 # The default value is: a4.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
-PAPER_TYPE             = a4wide
+PAPER_TYPE             = a4
 
 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
 # that should be included in the LaTeX output. The package can be specified just
 
 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
 # that should be included in the LaTeX output. The package can be specified just
@@ -1734,7 +1809,7 @@ LATEX_SOURCE_CODE      = NO
 
 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
 # bibliography, e.g. plainnat, or ieeetr. See
 
 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
 # bibliography, e.g. plainnat, or ieeetr. See
-# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
 # The default value is: plain.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
 # The default value is: plain.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
@@ -1748,6 +1823,14 @@ LATEX_BIB_STYLE        = plain
 
 LATEX_TIMESTAMP        = NO
 
 
 LATEX_TIMESTAMP        = NO
 
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
 #---------------------------------------------------------------------------
 # Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 #---------------------------------------------------------------------------
 # Configuration options related to the RTF output
 #---------------------------------------------------------------------------
@@ -1787,9 +1870,9 @@ COMPACT_RTF            = NO
 
 RTF_HYPERLINKS         = NO
 
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
 #
 # See also section "Doxygen usage" for information on how to generate the
 # default style sheet that doxygen normally uses.
 #
 # See also section "Doxygen usage" for information on how to generate the
 # default style sheet that doxygen normally uses.
@@ -1798,8 +1881,8 @@ RTF_HYPERLINKS         = NO
 RTF_STYLESHEET_FILE    =
 
 # Set optional variables used in the generation of an RTF document. Syntax is
 RTF_STYLESHEET_FILE    =
 
 # Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
 # This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_EXTENSIONS_FILE    =
 # This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_EXTENSIONS_FILE    =
@@ -1885,6 +1968,13 @@ XML_OUTPUT             = xml
 
 XML_PROGRAMLISTING     = YES
 
 
 XML_PROGRAMLISTING     = YES
 
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
 #---------------------------------------------------------------------------
 #---------------------------------------------------------------------------
 # Configuration options related to the DOCBOOK output
 #---------------------------------------------------------------------------
@@ -1917,9 +2007,9 @@ DOCBOOK_PROGRAMLISTING = NO
 #---------------------------------------------------------------------------
 
 # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
 #---------------------------------------------------------------------------
 
 # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sf.net) file that captures the
-# structure of the code including all documentation. Note that this feature is
-# still experimental and incomplete at the moment.
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
 # The default value is: NO.
 
 GENERATE_AUTOGEN_DEF   = NO
 # The default value is: NO.
 
 GENERATE_AUTOGEN_DEF   = NO
@@ -2019,8 +2109,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = __GNUC__=4 \
-                         __GNUC_MINOR__=4
+PREDEFINED             =
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
@@ -2087,12 +2176,6 @@ EXTERNAL_GROUPS        = YES
 
 EXTERNAL_PAGES         = YES
 
 
 EXTERNAL_PAGES         = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH              = /usr/bin/perl
-
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
@@ -2106,15 +2189,6 @@ PERL_PATH              = /usr/bin/perl
 
 CLASS_DIAGRAMS         = YES
 
 
 CLASS_DIAGRAMS         = YES
 
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
 # You can include diagrams made with dia in doxygen documentation. Doxygen will
 # then run dia to produce the diagram and insert it in the documentation. The
 # DIA_PATH tag allows you to specify the directory where the dia binary resides.
 # You can include diagrams made with dia in doxygen documentation. Doxygen will
 # then run dia to produce the diagram and insert it in the documentation. The
 # DIA_PATH tag allows you to specify the directory where the dia binary resides.
@@ -2342,6 +2416,11 @@ DIAFILE_DIRS           =
 
 PLANTUML_JAR_PATH      =
 
 
 PLANTUML_JAR_PATH      =
 
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
 # When using plantuml, the specified paths are searched for files specified by
 # the !include statement in a plantuml block.
 
 # When using plantuml, the specified paths are searched for files specified by
 # the !include statement in a plantuml block.
 
index 6e8084d542b8888e2a838671b6a9be845ac2cae2..bd2bd9d95fe8356f09e756268340f2f4cad52706 100644 (file)
@@ -10,6 +10,7 @@ endif
 .SHELLFLAGS := -ec
 
 LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
 .SHELLFLAGS := -ec
 
 LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
+SEVERITIES := \"debug\",\"info\",\"notice\",\"warning\",\"error\",\"crit\",\"emerg\"
 vardir := /var/paraslash
 mandir := $(datarootdir)/man/man1
 MKDIR_P := mkdir -p
 vardir := /var/paraslash
 mandir := $(datarootdir)/man/man1
 MKDIR_P := mkdir -p
@@ -20,7 +21,7 @@ uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
 GIT_VERSION := $(shell ./GIT-VERSION-GEN git-version.h)
-COPYRIGHT_YEAR := 2022
+COPYRIGHT_YEAR := 2024
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
 
 ifeq ("$(origin O)", "command line")
        build_dir := $(O)
@@ -112,13 +113,16 @@ CPPFLAGS += -DBINDIR='"$(bindir)"'
 CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
 CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
 CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
 CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
 CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
 CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
+CPPFLAGS += -DSEVERITIES=$(SEVERITIES)
 CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -I$(lls_suite_dir)
 CPPFLAGS += -I$(yy_build_dir)
 CPPFLAGS += $(lopsub_cppflags)
 CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
 CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
 CPPFLAGS += -I$(lls_suite_dir)
 CPPFLAGS += -I$(yy_build_dir)
 CPPFLAGS += $(lopsub_cppflags)
+CPPFLAGS += -Wunused-macros
 
 STRICT_CFLAGS += -fno-strict-aliasing
 
 STRICT_CFLAGS += -fno-strict-aliasing
+STRICT_CFLAGS += -ftrapv
 STRICT_CFLAGS += -g
 STRICT_CFLAGS += -Os
 STRICT_CFLAGS += -Wundef -W -Wuninitialized
 STRICT_CFLAGS += -g
 STRICT_CFLAGS += -Os
 STRICT_CFLAGS += -Wundef -W -Wuninitialized
@@ -130,6 +134,11 @@ STRICT_CFLAGS += -Wredundant-decls
 STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
 STRICT_CFLAGS += -Wdeclaration-after-statement
 STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
+STRICT_CFLAGS += -fdata-sections -ffunction-sections
+STRICT_CFLAGS += -Wstrict-prototypes
+STRICT_CFLAGS += -Wshadow
+
+LDFLAGS += -Wl,--gc-sections
 
 ifeq ($(ENABLE_UBSAN), yes)
        STRICT_CFLAGS += -fsanitize=undefined
 
 ifeq ($(ENABLE_UBSAN), yes)
        STRICT_CFLAGS += -fsanitize=undefined
@@ -138,12 +147,7 @@ endif
 
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
 
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
-       CPPFLAGS += -Wunused-macros
-       STRICT_CFLAGS += -fdata-sections -ffunction-sections
-       STRICT_CFLAGS += -Wstrict-prototypes
-       STRICT_CFLAGS += -Wshadow
        STRICT_CFLAGS += -Wunused -Wall
        STRICT_CFLAGS += -Wunused -Wall
-       LDFLAGS += -Wl,--gc-sections
 endif
 
 cc-option = $(shell \
 endif
 
 cc-option = $(shell \
@@ -153,6 +157,7 @@ cc-option = $(shell \
 
 STRICT_CFLAGS += $(call cc-option, -Wformat-signedness)
 STRICT_CFLAGS += $(call cc-option, -Wdiscarded-qualifiers)
 
 STRICT_CFLAGS += $(call cc-option, -Wformat-signedness)
 STRICT_CFLAGS += $(call cc-option, -Wdiscarded-qualifiers)
+STRICT_CFLAGS += $(call cc-option, -Wsuggest-attribute=malloc)
 
 # To put more focus on warnings, be less verbose as default
 # Use 'make V=1' to see the full commands
 
 # To put more focus on warnings, be less verbose as default
 # Use 'make V=1' to see the full commands
@@ -192,73 +197,48 @@ $(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
 
 $(object_dir)/%.o: %.c | $(object_dir)
 
 
 $(object_dir)/%.o: %.c | $(object_dir)
 
-$(object_dir)/opus%.o: CPPFLAGS += $(opus_cppflags)
-$(object_dir)/gui.o $(object_dir)/gui%.o \
-: CPPFLAGS += $(curses_cppflags)
-$(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags)
-$(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
-
-$(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/openssl.o: CPPFLAGS += $(openssl_cppflags)
-$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
-$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
-$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
-
-$(object_dir)/interactive.o \
-: CPPFLAGS += $(readline_cppflags)
-
-$(object_dir)/resample_filter.o \
-: CPPFLAGS += $(samplerate_cppflags)
-
-$(object_dir)/oss_write.o \
-: CPPFLAGS += $(oss_cppflags)
-
-$(object_dir)/ao_write.o \
-: CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
-
-$(object_dir)/mp3dec_filter.o \
-: CPPFLAGS += $(mad_cppflags)
-
-$(object_dir)/aacdec_filter.o \
-$(object_dir)/aac_afh.o \
-: CPPFLAGS += $(faad_cppflags)
-
-$(object_dir)/ogg_afh.o \
-$(object_dir)/oggdec_filter.o \
-: CPPFLAGS += $(vorbis_cppflags)
-
-$(object_dir)/spx_common.o \
-$(object_dir)/spxdec_filter.o \
-$(object_dir)/spx_afh.o \
-$(object_dir)/oggdec_filter.o \
-$(object_dir)/ogg_afh.o \
-$(object_dir)/ogg_afh_common.o \
-$(object_dir)/opus%.o \
-: CPPFLAGS += $(ogg_cppflags)
-
-$(object_dir)/afs.o \
-$(object_dir)/aft.o \
-$(object_dir)/attribute.o \
-$(object_dir)/blob.o  \
-$(object_dir)/mood.o \
-$(object_dir)/playlist.o \
-$(object_dir)/score.o \
-$(object_dir)/server.o \
-$(object_dir)/vss.o \
-$(object_dir)/command.o \
-$(object_dir)/http_send.o \
-$(object_dir)/dccp_send.o \
-$(object_dir)/udp_send.o \
-$(object_dir)/send_common.o \
-$(object_dir)/mm.o \
-: CPPFLAGS += $(osl_cppflags)
-
-$(object_dir)/compress_filter.o: CFLAGS += -O3
+OD = $(addsuffix .d, $(addprefix $(dep_dir)/, $(1))) \
+       $(addsuffix .o, $(addprefix $(object_dir)/, $(1)))
+
+$(call OD, opus%): CPPFLAGS += $(opus_cppflags)
+$(call OD, gui gui%): CPPFLAGS += $(curses_cppflags)
+$(call OD, spx%): CPPFLAGS += $(speex_cppflags)
+$(call OD, flac%): CPPFLAGS += $(flac_cppflags)
+$(call OD, mp3_afh): CPPFLAGS += $(id3tag_cppflags)
+$(call OD, openssl): CPPFLAGS += $(openssl_cppflags)
+$(call OD, gcrypt): CPPFLAGS += $(gcrypt_cppflags)
+$(call OD, ao_write): CPPFLAGS += $(ao_cppflags)
+$(call OD, alsa%): CPPFLAGS += $(alsa_cppflags)
+$(call OD, interactive): CPPFLAGS += $(readline_cppflags)
+$(call OD, resample_filter): CPPFLAGS += $(samplerate_cppflags)
+$(call OD, oss_write): CPPFLAGS += $(oss_cppflags)
+$(call OD, ao_write): CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
+$(call OD, mp3dec_filter): CPPFLAGS += $(mad_cppflags)
+$(call OD, aacdec_filter aac_afh): CPPFLAGS += $(faad_cppflags)
+$(call OD, ogg_afh oggdec_filter): CPPFLAGS += $(vorbis_cppflags)
+$(call OD, spx_common spxdec_filter spx_afh oggdec_filter ogg_afh \
+       ogg_afh_common opus%): CPPFLAGS += $(ogg_cppflags)
+$(call OD, afs aft attribute blob mood playlist score server vss command \
+       http_send dccp_send udp_send send_common mm.o): \
+       CPPFLAGS += $(osl_cppflags)
+
+$(call OD, compress_filter): CFLAGS += -O3
+$(call OD, openssl): CFLAGS += -Wno-deprecated-declarations
 
 $(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
 
 $(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
+define CC_CMD
        $(call SAY, CC $<)
        $(call SAY, CC $<)
-       $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
-               $(STRICT_CFLAGS) $(CFLAGS) $<
+       $(CC) -c -o $(object_dir)/$(*F).o -MMD -MF \
+               $(dep_dir)/$(*F).d -MT $(object_dir)/$(*F).o \
+               $(CPPFLAGS) $(STRICT_CFLAGS) $(CFLAGS) $<
+endef
+CC_PREREQUISITES := %.c | $(object_dir) $(dep_dir) $(lsg_h) $(yy_h)
+# These two have the same prerequisites and the same recipe. There should be a
+# better way to write this.
+$(object_dir)/%.o: $(CC_PREREQUISITES)
+       $(CC_CMD)
+$(dep_dir)/%.d: $(CC_PREREQUISITES)
+       $(CC_CMD)
 
 para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
 para_write para_play para_audiod \
 
 para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
 para_write para_play para_audiod \
@@ -337,7 +317,7 @@ distclean: clean
        $(call SAY, DISTCLEAN)
        rm -f Makefile autoscan.log config.status config.log
        rm -f config.h configure config.h.in
        $(call SAY, DISTCLEAN)
        rm -f Makefile autoscan.log config.status config.log
        rm -f config.h configure config.h.in
-maintainer-clean: distclean
+maintainer-clean: distclean test-clean
        $(call SAY, MAINTAINER-CLEAN)
        rm -f *.tar.bz2 *.tar.xz
        rm -f GPATH GRTAGS GSYMS GTAGS
        $(call SAY, MAINTAINER-CLEAN)
        rm -f *.tar.bz2 *.tar.xz
        rm -f GPATH GRTAGS GSYMS GTAGS
diff --git a/NEWS.md b/NEWS.md
index e9713d9826ff74e25aa01ceaad9a9db13f3113a5..d5812289640d71c52a60bd850928c72b3c5ba0b8 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
 NEWS
 ====
 
 NEWS
 ====
 
--------------------------------------------
-0.7.1 (to be announced) "digital spindrift"
--------------------------------------------
+---------------------------------------------
+0.7.4 (to be announced) "genetic contraction"
+---------------------------------------------
 
 
+Downloads:
 [tarball](./releases/paraslash-git.tar.xz)
 
 [tarball](./releases/paraslash-git.tar.xz)
 
+-----------------------------------------
+0.7.3 (2024-03-12) "weighted correctness"
+-----------------------------------------
+
+The highlight of this release is the new "ls --admissible=m/foo"
+feature described below. Other user-visible changes include minor
+additions to the "ls" and "select" server commands. The release also
+includes a fair number of cleanups for the crypto code and the file
+descriptor utilities, both without visible effects. Old ssh keys
+and outdated openssl library versions are now deprecated and cause
+warnings.
+
+- Old style PEM keys are now deprecated. They still work but their
+  use results in a run-time warning. The removal of PEM key support is
+  scheduled for paraslash-0.8.0.
+- Version 1.0 of the openssl library has been deprecated. A warning
+  is printed at compile-time on systems which have this outdated version
+  because it will no longer be supported once paraslash-0.8.0 comes out.
+- A spring cleanup for the senescent code in fd.c.
+- The --admissible option of the ls command now takes an optional
+  argument. When invoked like --admissible=m/foo, only files which are
+  admissible with respect to mood foo are listed.
+- The select server command is now quiet by default, The new --verbose
+  option can be used to show information about the newly loaded mood
+  or playlist.
+- The ls server command gained the --limit option to force a limit
+  on the number of files listed.
+- Cleanup of the openssl-specific code.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.3.tar.xz),
+[signature](./releases/paraslash-0.7.3.tar.xz.asc)
+
+-------------------------------------
+0.7.2 (2023-03-08) "optical friction"
+-------------------------------------
+
+The improved error reporting of afs commands and the two new options
+for the sleep subcommand of para_mixer are the most prominent features
+of this minor release. The bulk of the changes are cleanups of the
+afs and net subsystems, which should both have no user-visible impact.
+
+- A major cleanup of the audio file selector.
+- The client no longer prints error messages from afs commands to
+  stdout but to stderr.
+- The sleep subcommand of para_mixer gained two options to control
+  the startup mood and the time period before fade-out starts. A bunch
+  of further improvements for this subcommand went in as well.
+- Minor cleanup of the net subsystem.
+- The openssl specific code now employs the EVP API to compute hashes.
+  It should compile without warnings against openssl-3.
+- The deprecated syntax for specifying negative offsets in the argument
+  to the "ff" server command has been removed.
+
+Downloads:
+[tarball](./releases/paraslash-0.7.2.tar.xz),
+[signature](./releases/paraslash-0.7.2.tar.xz.asc)
+
+--------------------------------------
+0.7.1 (2022-10-03) "digital spindrift"
+--------------------------------------
+
+The two new ll commands and the internal mp4ff library are the most
+user-visible changes of this release. On top of that there are two
+core changes which aim to improve the robustness of the code but which
+are otherwise invisible: the switch from select(2) to poll(2) and the
+revised memory allocation API which checks for integer overflows. The
+release also comes with a slight change to the build system and the
+usual mix of bug fixes and minor improvements not mentioned here.
+
+- The autogen.sh script now only creates the autoconf specific files
+  but no longer runs configure, make and the test suite.
+- A stripped down copy of the discontinued libmp4ff library has become
+  part of the paraslash code base. As a result it is no longer necessary
+  to install faad from source to get support for aac/m4a files. The
+  faad decoder package must still be installed.
+- The log level of the running daemon can now be changed with the
+  new ll command. It is available for para_server and para_audiod.
+- All calls to select(2) have been replaced by calls to poll(2)
+  to avoid known shortcomings of the select API.
+- All allocation functions now check for integer overflow. Since this
+  requires support from the compiler, the oldest supported gcc version
+  has been bumped to gcc-5.4 (released in 2015).
+
+Downloads:
+[tarball](./releases/paraslash-0.7.1.tar.xz),
+[signature](./releases/paraslash-0.7.1.tar.xz.asc)
+
 ----------------------------------
 0.7.0 (2022-03-12) "seismic orbit"
 ----------------------------------
 ----------------------------------
 0.7.0 (2022-03-12) "seismic orbit"
 ----------------------------------
@@ -196,6 +285,23 @@ Downloads:
 [tarball](./releases/paraslash-0.6.1.tar.xz),
 [signature](./releases/paraslash-0.6.1.tar.xz.asc)
 
 [tarball](./releases/paraslash-0.6.1.tar.xz),
 [signature](./releases/paraslash-0.6.1.tar.xz.asc)
 
+---------------------------------------
+0.5.9 (2021-11-04) "reversed dimension"
+---------------------------------------
+This release contains a few important fixes which have accumulated in
+the maint branch. The paraslash-0.5.x series has now reached its end
+of life and will no longer be supported. All users should upgrade to
+a more recent version at this point.
+
+- Fix an issue with the bash completion script.
+- Initialize the random seed also when using libgrypt.
+- Fix some compiler warnings in the resample filter
+- Don't return spurious errors from the ff server command.
+
+Downloads:
+[tarball](./releases/paraslash-0.5.9.tar.bz2),
+[signature](./releases/paraslash-0.5.9.tar.bz2.asc)
+
 ---------------------------------------
 0.5.8 (2017-09-23) "branching parabola"
 ---------------------------------------
 ---------------------------------------
 0.5.8 (2017-09-23) "branching parabola"
 ---------------------------------------
index 5c1225b62b2e179026dadf7638d4f44f041345cb..c4301a2f178257b19d56b3cf297da3e1eaff337c 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
 #include <neaacdec.h>
 
 #include "para.h"
 #include <neaacdec.h>
 
 #include "para.h"
-
-/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
-#define USE_TAGGING
-#include <mp4ff.h>
-
+#include "mp4.h"
 #include "error.h"
 #include "portable_io.h"
 #include "afh.h"
 #include "error.h"
 #include "portable_io.h"
 #include "afh.h"
@@ -26,13 +22,11 @@ struct aac_afh_context {
        const void *map;
        size_t mapsize;
        size_t fpos;
        const void *map;
        size_t mapsize;
        size_t fpos;
-       int32_t track;
-       mp4ff_t *mp4ff;
-       mp4AudioSpecificConfig masc;
-       mp4ff_callback_t cb;
+       struct mp4 *mp4;
+       struct mp4_callback cb;
 };
 
 };
 
-static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_read_cb(void *user_data, void *dest, size_t want)
 {
        struct aac_afh_context *c = user_data;
        size_t have, rv;
 {
        struct aac_afh_context *c = user_data;
        size_t have, rv;
@@ -40,45 +34,32 @@ static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
        if (want == 0 || c->fpos >= c->mapsize)
                return 0;
        have = c->mapsize - c->fpos;
        if (want == 0 || c->fpos >= c->mapsize)
                return 0;
        have = c->mapsize - c->fpos;
-       rv = PARA_MIN(have, (size_t)want);
+       rv = PARA_MIN(have, want);
        PARA_DEBUG_LOG("reading %zu bytes @%zu\n", rv, c->fpos);
        memcpy(dest, c->map + c->fpos, rv);
        c->fpos += rv;
        return rv;
 }
 
        PARA_DEBUG_LOG("reading %zu bytes @%zu\n", rv, c->fpos);
        memcpy(dest, c->map + c->fpos, rv);
        c->fpos += rv;
        return rv;
 }
 
-static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_seek_cb(void *user_data, off_t offset, int whence)
 {
        struct aac_afh_context *c = user_data;
 {
        struct aac_afh_context *c = user_data;
-       c->fpos = pos;
-       return 0;
-}
 
 
-static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
-{
-       int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
-
-       assert(num_tracks >= 0);
-       for (i = 0; i < num_tracks; i++) {
-               unsigned char *buf = NULL;
-               unsigned buf_size = 0;
-
-               mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
-               if (buf) {
-                       rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
-                       free(buf);
-                       if (rc < 0)
-                               continue;
-                       return i;
-               }
-       }
-       return -1; /* no audio track */
+       if (whence == SEEK_SET)
+               c->fpos = offset;
+       else if (whence == SEEK_CUR)
+               c->fpos += offset;
+       else if (whence == SEEK_END)
+               c->fpos = c->mapsize + offset;
+       else
+               assert(false);
+       return c->fpos;
 }
 
 static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
 {
        int ret;
 }
 
 static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
 {
        int ret;
-       struct aac_afh_context *c = para_malloc(sizeof(*c));
+       struct aac_afh_context *c = alloc(sizeof(*c));
 
        c->map = map;
        c->mapsize = mapsize;
 
        c->map = map;
        c->mapsize = mapsize;
@@ -87,18 +68,11 @@ static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
        c->cb.seek = aac_afh_seek_cb;
        c->cb.user_data = c;
 
        c->cb.seek = aac_afh_seek_cb;
        c->cb.user_data = c;
 
-       ret = -E_MP4FF_OPEN;
-       c->mp4ff = mp4ff_open_read(&c->cb);
-       if (!c->mp4ff)
+       ret = mp4_open(&c->cb, &c->mp4);
+       if (ret < 0)
                goto free_ctx;
                goto free_ctx;
-       c->track = aac_afh_get_track(c->mp4ff, &c->masc);
-       ret = -E_MP4FF_TRACK;
-       if (c->track < 0)
-               goto close_mp4ff;
        *afh_context = c;
        return 0;
        *afh_context = c;
        return 0;
-close_mp4ff:
-       mp4ff_close(c->mp4ff);
 free_ctx:
        free(c);
        *afh_context = NULL;
 free_ctx:
        free(c);
        *afh_context = NULL;
@@ -108,51 +82,39 @@ free_ctx:
 static void aac_afh_close(void *afh_context)
 {
        struct aac_afh_context *c = afh_context;
 static void aac_afh_close(void *afh_context)
 {
        struct aac_afh_context *c = afh_context;
-       mp4ff_close(c->mp4ff);
+       mp4_close(c->mp4);
        free(c);
 }
 
        free(c);
 }
 
-/**
- * Libmp4ff function to reposition the file to the given sample.
- *
- * \param f The opaque handle returned by mp4ff_open_read().
- * \param track The number of the (audio) track.
- * \param sample Destination.
- *
- * We need this function to obtain the offset of the sample within the audio
- * file. Unfortunately, it is not exposed in the mp4ff header.
- *
- * \return This function always returns 0.
- */
-int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
-
 static int aac_afh_get_chunk(uint32_t chunk_num, void *afh_context,
                const char **buf, uint32_t *len)
 {
        struct aac_afh_context *c = afh_context;
 static int aac_afh_get_chunk(uint32_t chunk_num, void *afh_context,
                const char **buf, uint32_t *len)
 {
        struct aac_afh_context *c = afh_context;
-       int32_t ss;
+       uint32_t ss;
        size_t offset;
        size_t offset;
+       int ret;
 
 
-       assert(chunk_num <= INT_MAX);
-       /* this function always returns zero */
-       mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+       ret = mp4_set_sample_position(c->mp4, chunk_num);
+       if (ret < 0)
+               return ret;
        offset = c->fpos;
        offset = c->fpos;
-       ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
-       if (ss <= 0)
-               return -E_MP4FF_BAD_SAMPLE;
-       assert(ss + offset <= c->mapsize);
+       ret = mp4_get_sample_size(c->mp4, chunk_num, &ss);
+       if (ret < 0)
+               return ret;
+       if (ss + offset > c->mapsize) /* file got truncated?! */
+               return -E_MP4_CORRUPT;
        *buf = c->map + offset;
        *len = ss;
        return 1;
 }
 
        *buf = c->map + offset;
        *len = ss;
        return 1;
 }
 
-static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+static void aac_afh_get_taginfo(const struct mp4 *mp4, struct taginfo *tags)
 {
 {
-       mp4ff_meta_get_artist(mp4ff, &tags->artist);
-       mp4ff_meta_get_title(mp4ff, &tags->title);
-       mp4ff_meta_get_date(mp4ff, &tags->year);
-       mp4ff_meta_get_album(mp4ff, &tags->album);
-       mp4ff_meta_get_comment(mp4ff, &tags->comment);
+       tags->artist = mp4_get_tag_value(mp4, "artist");
+       tags->title = mp4_get_tag_value(mp4, "title");
+       tags->year = mp4_get_tag_value(mp4, "date");
+       tags->album = mp4_get_tag_value(mp4, "album");
+       tags->comment = mp4_get_tag_value(mp4, "comment");
 }
 
 /*
 }
 
 /*
@@ -162,9 +124,8 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
                struct afh_info *afhi)
 {
        int ret;
                struct afh_info *afhi)
 {
        int ret;
-       int32_t rv;
        struct aac_afh_context *c;
        struct aac_afh_context *c;
-       int64_t tmp;
+       uint64_t milliseconds;
        const char *buf;
        uint32_t n, len;
 
        const char *buf;
        uint32_t n, len;
 
@@ -172,157 +133,116 @@ static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
 
-       ret = -E_MP4FF_BAD_SAMPLERATE;
-       rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->frequency = rv;
-
-       ret = -E_MP4FF_BAD_CHANNEL_COUNT;
-       rv = mp4ff_get_channel_count(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->channels = rv;
-
-       ret = -E_MP4FF_BAD_SAMPLE_COUNT;
-       rv = mp4ff_num_samples(c->mp4ff, c->track);
-       if (rv <= 0)
-               goto close;
-       afhi->chunks_total = rv;
+       afhi->frequency = mp4_get_sample_rate(c->mp4);
+       assert(afhi->frequency > 0);
+       afhi->channels = mp4_get_channel_count(c->mp4);
+       assert(afhi->channels > 0);
+       afhi->chunks_total = mp4_num_samples(c->mp4);
+       assert(afhi->chunks_total > 0);
+
        afhi->max_chunk_size = 0;
        for (n = 0; n < afhi->chunks_total; n++) {
        afhi->max_chunk_size = 0;
        for (n = 0; n < afhi->chunks_total; n++) {
-               if (aac_afh_get_chunk(n, c, &buf, &len) < 0)
-                       break;
+               ret = aac_afh_get_chunk(n, c, &buf, &len);
+               if (ret < 0)
+                       goto out;
                afhi->max_chunk_size = PARA_MAX(afhi->max_chunk_size, len);
        }
                afhi->max_chunk_size = PARA_MAX(afhi->max_chunk_size, len);
        }
-
-       tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
-       afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
-       ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
-
-       if (aac_afh_get_chunk(0, c, &buf, &len) >= 0)
-               numbytes -= buf - map;
+       milliseconds = mp4_get_duration(c->mp4);
+       afhi->seconds_total = milliseconds / 1000;
+       ms2tv(milliseconds / afhi->chunks_total, &afhi->chunk_tv);
+       if (aac_afh_get_chunk(0, c, &buf, &len) < 0)
+               goto out;
+       numbytes -= buf - map;
        afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
        afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
-       _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+       aac_afh_get_taginfo(c->mp4, &afhi->tags);
        ret = 1;
        ret = 1;
-close:
+out:
        aac_afh_close(c);
        return ret;
 }
 
        aac_afh_close(c);
        return ret;
 }
 
-static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_read_cb(void *user_data, void *dest, size_t want)
 {
        int fd = *(int *)user_data;
        return read(fd, dest, want);
 }
 
 {
        int fd = *(int *)user_data;
        return read(fd, dest, want);
 }
 
-static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
+static off_t aac_afh_meta_seek_cb(void *user_data, off_t offset, int whence)
 {
        int fd = *(int *)user_data;
 {
        int fd = *(int *)user_data;
-       return lseek(fd, pos, SEEK_SET);
+       off_t ret = lseek(fd, offset, whence);
+
+       assert(ret != (off_t)-1);
+       return ret;
 }
 
 }
 
-static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+static ssize_t aac_afh_meta_write_cb(void *user_data, void *dest, size_t count)
 {
        int fd = *(int *)user_data;
 {
        int fd = *(int *)user_data;
-       return write(fd, dest, want);
+       return write(fd, dest, count);
 }
 
 }
 
-static uint32_t aac_afh_meta_truncate_cb(void *user_data)
+static int aac_afh_meta_truncate_cb(void *user_data)
 {
        int fd = *(int *)user_data;
        off_t offset = lseek(fd, 0, SEEK_CUR);
        return ftruncate(fd, offset);
 }
 
 {
        int fd = *(int *)user_data;
        off_t offset = lseek(fd, 0, SEEK_CUR);
        return ftruncate(fd, offset);
 }
 
-static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
-{
-       free(tag->value);
-       tag->value = para_strdup(new_val);
-       *found = true;
-}
-
-static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
+static void replace_or_add_tag(const char *item, const char *value,
+               struct mp4_metadata *meta)
 {
 {
-       md->tags[md->count].item = para_strdup(item);
-       md->tags[md->count].value = para_strdup(value);
-       md->count++;
+       uint32_t n;
+       struct mp4_tag *t;
+
+       for (n = 0; n < meta->count; n++) {
+               t = meta->tags + n;
+               if (strcasecmp(t->item, item))
+                       continue;
+               free(t->value);
+               t->value = para_strdup(value);
+               return;
+       }
+       /* item not found, add new tag */
+       meta->tags = para_realloc(meta->tags, (meta->count + 1)
+               * sizeof(struct mp4_tag));
+       t = meta->tags + meta->count;
+       t->item = para_strdup(item);
+       t->value = para_strdup(value);
+       meta->count++;
 }
 
 static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
                struct taginfo *tags, int fd, __a_unused const char *filename)
 {
 }
 
 static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
                struct taginfo *tags, int fd, __a_unused const char *filename)
 {
-       int ret, i;
-       int32_t rv;
-       mp4ff_metadata_t metadata;
-       mp4ff_t *mp4ff;
-       mp4ff_callback_t cb = {
+       int ret;
+       struct mp4_metadata *metadata;
+       struct mp4 *mp4;
+       struct mp4_callback cb = {
                .read = aac_afh_meta_read_cb,
                .seek = aac_afh_meta_seek_cb,
                .write = aac_afh_meta_write_cb,
                .truncate = aac_afh_meta_truncate_cb,
                .user_data = &fd
        };
                .read = aac_afh_meta_read_cb,
                .seek = aac_afh_meta_seek_cb,
                .write = aac_afh_meta_write_cb,
                .truncate = aac_afh_meta_truncate_cb,
                .user_data = &fd
        };
-       bool found_artist = false, found_title = false, found_album = false,
-               found_year = false, found_comment = false;
 
        ret = write_all(fd, map, mapsize);
        if (ret < 0)
                return ret;
        lseek(fd, 0, SEEK_SET);
 
 
        ret = write_all(fd, map, mapsize);
        if (ret < 0)
                return ret;
        lseek(fd, 0, SEEK_SET);
 
-       mp4ff = mp4ff_open_read_metaonly(&cb);
-       if (!mp4ff)
-               return -E_MP4FF_OPEN;
-
-       ret = -E_MP4FF_META_READ;
-       rv = mp4ff_meta_get_num_items(mp4ff);
-       if (rv < 0)
-               goto close;
-       metadata.count = rv;
-       PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
-
-       metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
-       for (i = 0; i < metadata.count; i++) {
-               mp4ff_tag_t *tag = metadata.tags + i;
-
-               ret = -E_MP4FF_META_READ;
-               if (!mp4ff_meta_get_by_index(mp4ff, i, &tag->item, &tag->value))
-                       goto free_tags;
-               PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
-               if (!strcmp(tag->item, "artist"))
-                       replace_tag(tag, tags->artist, &found_artist);
-               else if (!strcmp(tag->item, "title"))
-                       replace_tag(tag, tags->title, &found_title);
-               else if (!strcmp(tag->item, "album"))
-                       replace_tag(tag, tags->album, &found_album);
-               else if (!strcmp(tag->item, "date"))
-                       replace_tag(tag, tags->year, &found_year);
-               else if (!strcmp(tag->item, "comment"))
-                       replace_tag(tag, tags->comment, &found_comment);
-       }
-       if (!found_artist)
-               add_tag(&metadata, "artist", tags->artist);
-       if (!found_title)
-               add_tag(&metadata, "title", tags->title);
-       if (!found_album)
-               add_tag(&metadata, "album", tags->album);
-       if (!found_year)
-               add_tag(&metadata, "date", tags->year);
-       if (!found_comment)
-               add_tag(&metadata, "comment", tags->comment);
-       ret = -E_MP4FF_META_WRITE;
-       if (!mp4ff_meta_update(&cb, &metadata))
-               goto free_tags;
-       ret = 1;
-free_tags:
-       for (; i > 0; i--) {
-               free(metadata.tags[i - 1].item);
-               free(metadata.tags[i - 1].value);
-       }
-       free(metadata.tags);
-close:
-       mp4ff_close(mp4ff);
+       ret = mp4_open_meta(&cb, &mp4);
+       if (ret < 0)
+               return ret;
+       metadata = mp4_get_meta(mp4);
+       PARA_NOTICE_LOG("%u metadata item(s) found\n", metadata->count);
+       replace_or_add_tag("artist", tags->artist, metadata);
+       replace_or_add_tag("title", tags->title, metadata);
+       replace_or_add_tag("album", tags->album, metadata);
+       replace_or_add_tag("date", tags->year, metadata);
+       replace_or_add_tag("comment", tags->comment, metadata);
+       ret = mp4_update_meta(mp4);
+       mp4_close(mp4);
        return ret;
 }
 
        return ret;
 }
 
index a2459d82b31991a8e9ac578e0559a398c83f4e10..87a7900af3ee9b43e1a661d66cdd54d1625b0952 100644 (file)
@@ -52,7 +52,7 @@ static int aacdec_execute(struct btr_node *btrn, const char *cmd, char **result)
 static void aacdec_open(struct filter_node *fn)
 {
        NeAACDecConfigurationPtr c;
 static void aacdec_open(struct filter_node *fn)
 {
        NeAACDecConfigurationPtr c;
-       struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+       struct private_aacdec_data *padd = zalloc(sizeof(*padd));
 
        padd->handle = NeAACDecOpen();
        c = NeAACDecGetCurrentConfiguration(padd->handle);
 
        padd->handle = NeAACDecOpen();
        c = NeAACDecGetCurrentConfiguration(padd->handle);
@@ -74,7 +74,7 @@ static void aacdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
        fn->private_data = NULL;
 }
 
-static int aacdec_post_select(__a_unused struct sched *s, void *context)
+static int aacdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -136,7 +136,7 @@ next_buffer:
        consumed += frame_info.bytesconsumed;
        if (!frame_info.samples)
                goto success;
        consumed += frame_info.bytesconsumed;
        if (!frame_info.samples)
                goto success;
-       btrbuf = para_malloc(2 * frame_info.samples);
+       btrbuf = arr_alloc(2, frame_info.samples);
        for (i = 0; i < frame_info.samples; i++) {
                short sh = ((short *)outbuf)[i];
                write_int16_host_endian(btrbuf + loaded, sh);
        for (i = 0; i < frame_info.samples; i++) {
                short sh = ((short *)outbuf)[i];
                write_int16_host_endian(btrbuf + loaded, sh);
@@ -158,7 +158,7 @@ err:
 const struct filter lsg_filter_cmd_com_aacdec_user_data = {
        .open = aacdec_open,
        .close = aacdec_close,
 const struct filter lsg_filter_cmd_com_aacdec_user_data = {
        .open = aacdec_open,
        .close = aacdec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = aacdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = aacdec_post_monitor,
        .execute = aacdec_execute
 };
        .execute = aacdec_execute
 };
diff --git a/acl.c b/acl.c
index 59ffab3e0a188a8ac921555b83a9276907ecfa46..ddf93ecc6e083299daf7a573b0adce16c3b4051f 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -81,7 +81,7 @@ no_match:
  */
 void acl_add_entry(struct list_head *acl, char *addr, int netmask)
 {
  */
 void acl_add_entry(struct list_head *acl, char *addr, int netmask)
 {
-       struct access_info *ai = para_malloc(sizeof(struct access_info));
+       struct access_info *ai = alloc(sizeof(struct access_info));
 
        inet_pton(AF_INET, addr, &ai->addr);
        ai->netmask = netmask;
 
        inet_pton(AF_INET, addr, &ai->addr);
        ai->netmask = netmask;
index 6a0ec239bbcbd120efd130b5f8f24c2b136d4516..8449e787f96fa24b60c2c3af9ec5218eb264ff53 100644 (file)
@@ -75,7 +75,7 @@ static int afh_recv_open(struct receiver_node *rn)
 
        if (!fn || *fn == '\0')
                return -E_AFH_RECV_BAD_FILENAME;
 
        if (!fn || *fn == '\0')
                return -E_AFH_RECV_BAD_FILENAME;
-       rn->private_data = pard = para_calloc(sizeof(*pard));
+       rn->private_data = pard = zalloc(sizeof(*pard));
        afhi = &pard->afhi;
        ret = mmap_full_file(fn, O_RDONLY, &pard->map,
                &pard->map_size, &pard->fd);
        afhi = &pard->afhi;
        ret = mmap_full_file(fn, O_RDONLY, &pard->map,
                &pard->map_size, &pard->fd);
@@ -142,14 +142,14 @@ static void afh_recv_close(struct receiver_node *rn)
        freep(&rn->private_data);
 }
 
        freep(&rn->private_data);
 }
 
-static void afh_recv_pre_select(struct sched *s, void *context)
+static void afh_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_afh_recv_data *pard = rn->private_data;
        struct afh_info *afhi = &pard->afhi;
        struct lls_parse_result *lpr = rn->lpr;
        struct timeval chunk_time;
 {
        struct receiver_node *rn = context;
        struct private_afh_recv_data *pard = rn->private_data;
        struct afh_info *afhi = &pard->afhi;
        struct lls_parse_result *lpr = rn->lpr;
        struct timeval chunk_time;
-       int state = generic_recv_pre_select(s, rn);
+       int state = generic_recv_pre_monitor(s, rn);
        unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
 
        if (state <= 0)
        unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
 
        if (state <= 0)
@@ -163,7 +163,7 @@ static void afh_recv_pre_select(struct sched *s, void *context)
        sched_request_barrier_or_min_delay(&chunk_time, s);
 }
 
        sched_request_barrier_or_min_delay(&chunk_time, s);
 }
 
-static int afh_recv_post_select(__a_unused struct sched *s, void *context)
+static int afh_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct lls_parse_result *lpr = rn->lpr;
 {
        struct receiver_node *rn = context;
        struct lls_parse_result *lpr = rn->lpr;
@@ -188,7 +188,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                        pard->map_size, &header, &size);
                if (size > 0) {
                        PARA_INFO_LOG("writing header (%zu bytes)\n", size);
                        pard->map_size, &header, &size);
                if (size > 0) {
                        PARA_INFO_LOG("writing header (%zu bytes)\n", size);
-                       buf = para_malloc(size);
+                       buf = alloc(size);
                        memcpy(buf, header, size);
                        btr_add_output(buf, size, btrn);
                        afh_free_header(header, pard->audio_format_num);
                        memcpy(buf, header, size);
                        btr_add_output(buf, size, btrn);
                        afh_free_header(header, pard->audio_format_num);
@@ -205,7 +205,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                        PARA_DEBUG_LOG("adding %u bytes\n", len);
                        btr_add_output_dont_free(start, len, btrn);
                }
                        PARA_DEBUG_LOG("adding %u bytes\n", len);
                        btr_add_output_dont_free(start, len, btrn);
                }
-               ret = -E_RECV_EOF;
+               ret = -E_EOF;
                goto out;
        }
        if (pard->current_chunk == pard->first_chunk)
                goto out;
        }
        if (pard->current_chunk == pard->first_chunk)
@@ -226,7 +226,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
        btr_add_output_dont_free(start, len, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
        PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
        btr_add_output_dont_free(start, len, btrn);
        if (pard->current_chunk >= pard->last_chunk) {
-               ret = -E_RECV_EOF;
+               ret = -E_EOF;
                goto out;
        }
        pard->current_chunk++;
                goto out;
        }
        pard->current_chunk++;
@@ -242,7 +242,7 @@ out:
 const struct receiver lsg_recv_cmd_com_afh_user_data = {
        .open = afh_recv_open,
        .close = afh_recv_close,
 const struct receiver lsg_recv_cmd_com_afh_user_data = {
        .open = afh_recv_open,
        .close = afh_recv_close,
-       .pre_select = afh_recv_pre_select,
-       .post_select = afh_recv_post_select,
+       .pre_monitor = afh_recv_pre_monitor,
+       .post_monitor = afh_recv_post_monitor,
        .execute = afh_execute,
 };
        .execute = afh_execute,
 };
diff --git a/afs.c b/afs.c
index 710670255b2ec1cf67b9ab4e74823bfe19dd3a02..445d5871097b79cdcd14170c2a1f59998354dc98 100644 (file)
--- a/afs.c
+++ b/afs.c
 #include "afs.h"
 #include "net.h"
 #include "server.h"
 #include "afs.h"
 #include "net.h"
 #include "server.h"
+#include "daemon.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
 #include "fd.h"
 #include "signal.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
 #include "fd.h"
 #include "signal.h"
-#include "mood.h"
 #include "sideband.h"
 #include "command.h"
 
 #include "sideband.h"
 #include "command.h"
 
-/** The osl tables used by afs. \sa \ref blob.c. */
-enum afs_table_num {
-       /** Contains audio file information. See \ref aft.c. */
-       TBLNUM_AUDIO_FILES,
-       /** The table for the paraslash attributes. See \ref attribute.c. */
-       TBLNUM_ATTRIBUTES,
-       /*
-        * Moods and playlists organize the current set of admissible files in
-        * an osl table which contains only volatile columns. Each row consists
-        * of a pointer to an audio file and the score value of this file.
-        */
-       TBLNUM_SCORES,
-       /**
-        * A standard blob table containing the mood definitions. For details
-        * see \ref mood.c.
-        */
-       TBLNUM_MOODS,
-       /** A blob table containing lyrics on a per-song basis. */
-       TBLNUM_LYRICS,
-       /** Another blob table for images (for example album cover art). */
-       TBLNUM_IMAGES,
-       /** Yet another blob table for storing standard playlists. */
-       TBLNUM_PLAYLIST,
-       /** How many tables are in use? */
-       NUM_AFS_TABLES
-};
-
-static struct afs_table afs_tables[NUM_AFS_TABLES] = {
-       [TBLNUM_AUDIO_FILES] = {.init = aft_init, .name = "audio_files"},
-       [TBLNUM_ATTRIBUTES] = {.init = attribute_init, .name = "attributes"},
-       [TBLNUM_SCORES] = {.init = score_init, .name = "scores"},
-       [TBLNUM_MOODS] = {.init = moods_init, .name = "moods"},
-       [TBLNUM_LYRICS] = {.init = lyrics_init, .name = "lyrics"},
-       [TBLNUM_IMAGES] = {.init = images_init, .name = "images"},
-       [TBLNUM_PLAYLIST] = {.init = playlists_init, .name = "playlists"},
+/**
+ * The array of tables of the audio file selector.
+ *
+ * We organize them in an array to be able to loop over all tables.
+ */
+static const struct afs_table {
+       /** The name is no table operation, so define it here. */
+       const char * const name;
+       /** The only way to invoke the ops is via this pointer. */
+       const struct afs_table_operations *ops;
+} afs_tables[] = {
+       {.name = "audio_files", .ops = &aft_ops},
+       {.name = "attributes", .ops = &attr_ops},
+       {.name = "scores", .ops = &score_ops},
+       {.name = "moods", .ops = &moods_ops},
+       {.name = "lyrics", .ops = &lyrics_ops},
+       {.name = "images", .ops = &images_ops},
+       {.name = "playlists", .ops = &playlists_ops},
 };
 };
+/** Used to loop over the afs tables. */
+#define NUM_AFS_TABLES ARRAY_SIZE(afs_tables)
 
 struct command_task {
        /** The file descriptor for the local socket. */
 
 struct command_task {
        /** The file descriptor for the local socket. */
@@ -115,7 +100,7 @@ extern uint32_t afs_socket_cookie;
  */
 struct callback_query {
        /** The function to be called. */
  */
 struct callback_query {
        /** The function to be called. */
-       afs_callback *handler;
+       afs_callback *cb;
        /** The number of bytes of the query */
        size_t query_size;
 };
        /** The number of bytes of the query */
        size_t query_size;
 };
@@ -206,7 +191,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
        if (ret < 0)
                goto out;
        cq = query_shm;
        if (ret < 0)
                goto out;
        cq = query_shm;
-       cq->handler = f;
+       cq->cb = f;
        cq->query_size = query_shm_size - sizeof(*cq);
 
        if (query)
        cq->query_size = query_shm_size - sizeof(*cq);
 
        if (query)
@@ -446,40 +431,30 @@ no_admissible_files:
        return write_all(server_socket, buf, 8);
 }
 
        return write_all(server_socket, buf, 8);
 }
 
-static int activate_mood_or_playlist(const char *arg, int *num_admissible,
-               char **errmsg)
+static int activate_mood_or_playlist(const char *arg, struct para_buffer *pb)
 {
        enum play_mode mode;
        int ret;
 {
        enum play_mode mode;
        int ret;
+       char *msg;
 
 
-       if (!arg) {
+       if (!arg) { /* load dummy mood */
+               ret = mood_load(NULL, NULL, &msg);
+               mode = PLAY_MODE_MOOD;
+       } else if (!strncmp(arg, "p/", 2)) {
+               ret = playlist_load(arg + 2, NULL, &msg);
+               mode = PLAY_MODE_PLAYLIST;
+       } else if (!strncmp(arg, "m/", 2)) {
+               ret = mood_load(arg + 2, NULL, &msg);
                mode = PLAY_MODE_MOOD;
                mode = PLAY_MODE_MOOD;
-               ret = change_current_mood(NULL, errmsg);
-               if (ret < 0) {
-                       if (num_admissible)
-                               *num_admissible = 0;
-                       return ret;
-               }
        } else {
        } else {
-               if (!strncmp(arg, "p/", 2)) {
-                       ret = playlist_open(arg + 2);
-                       if (ret < 0 && errmsg)
-                               *errmsg = make_message( "could not open %s",
-                                       arg);
-                       mode = PLAY_MODE_PLAYLIST;
-               } else if (!strncmp(arg, "m/", 2)) {
-                       ret = change_current_mood(arg + 2, errmsg);
-                       mode = PLAY_MODE_MOOD;
-               } else {
-                       if (errmsg)
-                               *errmsg = make_message("%s: parse error", arg);
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               if (ret < 0)
-                       return ret;
+               ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               msg = make_message("%s: parse error\n", arg);
        }
        }
-       if (num_admissible)
-               *num_admissible = ret;
+       if (pb)
+               para_printf(pb, "%s", msg);
+       free(msg);
+       if (ret < 0)
+               return ret;
        current_play_mode = mode;
        /*
         * We get called with arg == current_mop from the signal dispatcher
        current_play_mode = mode;
        /*
         * We get called with arg == current_mop from the signal dispatcher
@@ -489,22 +464,15 @@ static int activate_mood_or_playlist(const char *arg, int *num_admissible,
         */
        if (arg != current_mop) {
                free(current_mop);
         */
        if (arg != current_mop) {
                free(current_mop);
-               if (arg) {
-                       current_mop = para_strdup(arg);
-                       mutex_lock(mmd_mutex);
-                       strncpy(mmd->afs_mode_string, arg,
-                               sizeof(mmd->afs_mode_string));
-                       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-               } else {
-                       mutex_lock(mmd_mutex);
-                       strcpy(mmd->afs_mode_string, "dummy");
-                       mmd->events++;
-                       mutex_unlock(mmd_mutex);
-                       current_mop = NULL;
-               }
+               current_mop = arg? para_strdup(arg) : NULL;
        }
        }
+       /* Notify the server about the mood/playlist change. */
+       mutex_lock(mmd_mutex);
+       strncpy(mmd->afs_mode_string, arg? arg: "dummy",
+               sizeof(mmd->afs_mode_string));
+       mmd->afs_mode_string[sizeof(mmd->afs_mode_string) - 1] = '\0';
+       mmd->events++;
+       mutex_unlock(mmd_mutex);
        return 1;
 }
 
        return 1;
 }
 
@@ -557,77 +525,14 @@ static void flush_and_free_pb(struct para_buffer *pb)
        free(pb->buf);
 }
 
        free(pb->buf);
 }
 
-static int com_select_callback(struct afs_callback_arg *aca)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       const char *arg;
-       int num_admissible, ret;
-       char *errmsg;
-
-       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
-       assert(ret >= 0);
-       arg = lls_input(0, aca->lpr);
-       ret = clear_score_table();
-       if (ret < 0) {
-               para_printf(&aca->pbout, "could not clear score table\n");
-               goto free_lpr;
-       }
-       if (current_play_mode == PLAY_MODE_MOOD)
-               close_current_mood();
-       else
-               playlist_close();
-       ret = activate_mood_or_playlist(arg, &num_admissible, &errmsg);
-       if (ret >= 0)
-               goto out;
-       /* ignore subsequent errors (but log them) */
-       para_printf(&aca->pbout, "%s\n", errmsg);
-       free(errmsg);
-       para_printf(&aca->pbout, "could not activate %s\n", arg);
-       if (current_mop && strcmp(current_mop, arg) != 0) {
-               int ret2;
-               para_printf(&aca->pbout, "switching back to %s\n", current_mop);
-               ret2 = activate_mood_or_playlist(current_mop, &num_admissible,
-                       &errmsg);
-               if (ret2 >= 0)
-                       goto out;
-               para_printf(&aca->pbout, "%s\n", errmsg);
-               free(errmsg);
-               para_printf(&aca->pbout, "could not reactivate %s: %s\n",
-                       current_mop, para_strerror(-ret2));
-       }
-       para_printf(&aca->pbout, "activating dummy mood\n");
-       activate_mood_or_playlist(NULL, &num_admissible, NULL);
-out:
-       para_printf(&aca->pbout, "activated %s (%d admissible file%s)\n",
-               current_mop? current_mop : "dummy mood", num_admissible,
-                       num_admissible == 1? "" : "s");
-free_lpr:
-       lls_free_parse_result(aca->lpr, cmd);
-       return ret;
-}
-
-static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
-{
-       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
-       char *errctx;
-       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
-
-       if (ret < 0) {
-               send_errctx(cc, errctx);
-               return ret;
-       }
-       return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
-}
-EXPORT_SERVER_CMD_HANDLER(select);
-
 static void init_admissible_files(const char *arg)
 {
 static void init_admissible_files(const char *arg)
 {
-       int ret = activate_mood_or_playlist(arg, NULL, NULL);
+       int ret = activate_mood_or_playlist(arg, NULL);
        if (ret < 0) {
        if (ret < 0) {
-               PARA_WARNING_LOG("could not activate %s: %s\n", arg,
-                       para_strerror(-ret));
+               PARA_WARNING_LOG("could not activate %s: %s\n", arg?
+                       arg : "dummy", para_strerror(-ret));
                if (arg)
                if (arg)
-                       activate_mood_or_playlist(NULL, NULL, NULL);
+                       activate_mood_or_playlist(NULL, NULL);
        }
 }
 
        }
 }
 
@@ -655,7 +560,7 @@ static void close_afs_tables(void)
        int i;
        PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
        int i;
        PARA_NOTICE_LOG("closing afs tables\n");
        for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].close();
+               afs_tables[i].ops->close();
        free(database_dir);
        database_dir = NULL;
 }
        free(database_dir);
        database_dir = NULL;
 }
@@ -675,26 +580,15 @@ static void get_database_dir(void)
        PARA_INFO_LOG("afs_database dir %s\n", database_dir);
 }
 
        PARA_INFO_LOG("afs_database dir %s\n", database_dir);
 }
 
-static int make_database_dir(void)
-{
-       int ret;
-
-       get_database_dir();
-       ret = para_mkdir(database_dir, 0777);
-       if (ret >= 0 || ret == -ERRNO_TO_PARA_ERROR(EEXIST))
-               return 1;
-       return ret;
-}
-
 static int open_afs_tables(void)
 {
        int i, ret;
 
        get_database_dir();
 static int open_afs_tables(void)
 {
        int i, ret;
 
        get_database_dir();
-       PARA_NOTICE_LOG("opening %d osl tables in %s\n", NUM_AFS_TABLES,
+       PARA_NOTICE_LOG("opening %zu osl tables in %s\n", NUM_AFS_TABLES,
                database_dir);
        for (i = 0; i < NUM_AFS_TABLES; i++) {
                database_dir);
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               ret = afs_tables[i].open(database_dir);
+               ret = afs_tables[i].ops->open(database_dir);
                if (ret >= 0)
                        continue;
                PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
                if (ret >= 0)
                        continue;
                PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
@@ -703,11 +597,11 @@ static int open_afs_tables(void)
        if (ret >= 0)
                return ret;
        while (i)
        if (ret >= 0)
                return ret;
        while (i)
-               afs_tables[--i].close();
+               afs_tables[--i].ops->close();
        return ret;
 }
 
        return ret;
 }
 
-static int afs_signal_post_select(struct sched *s, __a_unused void *context)
+static int afs_signal_post_monitor(struct sched *s, __a_unused void *context)
 {
        int signum, ret;
 
 {
        int signum, ret;
 
@@ -715,7 +609,7 @@ static int afs_signal_post_select(struct sched *s, __a_unused void *context)
                PARA_EMERG_LOG("para_server died\n");
                goto shutdown;
        }
                PARA_EMERG_LOG("para_server died\n");
                goto shutdown;
        }
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        if (signum == 0)
                return 0;
        if (signum == SIGHUP) {
        if (signum == 0)
                return 0;
        if (signum == SIGHUP) {
@@ -743,8 +637,8 @@ static void register_signal_task(struct sched *s)
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = afs_signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = afs_signal_post_monitor,
                .context = signal_task,
 
        }, s);
                .context = signal_task,
 
        }, s);
@@ -762,15 +656,15 @@ struct afs_client {
        struct timeval connect_time;
 };
 
        struct timeval connect_time;
 };
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
        struct afs_client *client;
 
 {
        struct command_task *ct = context;
        struct afs_client *client;
 
-       para_fd_set(server_socket, &s->rfds, &s->max_fileno);
-       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(server_socket, s);
+       sched_monitor_readfd(ct->fd, s);
        list_for_each_entry(client, &afs_client_list, node)
        list_for_each_entry(client, &afs_client_list, node)
-               para_fd_set(client->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(client->fd, s);
 }
 
 /**
 }
 
 /**
@@ -823,6 +717,43 @@ err:
        return ret;
 }
 
        return ret;
 }
 
+/**
+ * Format and send an error message to the command handler.
+ *
+ * To pass an error message from the callback of an afs command to the client,
+ * this function should be called. It formats the message into a buffer which
+ * is passed as a shared memory area to the command handler from where it
+ * propagates to the client.
+ *
+ * The message will be tagged with the ERROR_LOG sideband designator so that
+ * the client writes it to its stderr stream rather than to stdout as with
+ * aca->pbout. In analogy to the default Unix semantics of stderr, the message
+ * is sent without buffering.
+ *
+ * If sending the error message fails, an error is logged on the server side,
+ * but no other action is taken.
+ *
+ * \param aca Used to obtain the fd to send the shmid to.
+ * \param fmt Usual format string.
+ */
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...)
+{
+       va_list argp;
+       char *msg;
+       unsigned n;
+       int ret;
+
+       va_start(argp, fmt);
+       n = xvasprintf(&msg, fmt, argp);
+       va_end(argp);
+       ret = pass_buffer_as_shm(aca->fd, SBD_ERROR_LOG, msg, n + 1);
+       if (ret < 0)
+               PARA_ERROR_LOG("Could not send %s: %s\n", msg,
+                       para_strerror(-ret));
+       free(msg);
+}
+
 static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
 static int call_callback(int fd, int query_shmid)
 {
        void *query_shm;
@@ -842,7 +773,7 @@ static int call_callback(int fd, int query_shmid)
                .fd = fd,
                .band = SBD_OUTPUT
        };
                .fd = fd,
                .band = SBD_OUTPUT
        };
-       ret = cq->handler(&aca);
+       ret = cq->cb(&aca);
        ret2 = shm_detach(query_shm);
        if (ret2 < 0) {
                if (ret < 0) /* ignore (but log) detach error */
        ret2 = shm_detach(query_shm);
        if (ret2 < 0) {
                if (ret < 0) /* ignore (but log) detach error */
@@ -862,11 +793,11 @@ static int call_callback(int fd, int query_shmid)
        return ret;
 }
 
        return ret;
 }
 
-static int execute_server_command(fd_set *rfds)
+static int execute_server_command(void)
 {
        char buf[8];
        size_t n;
 {
        char buf[8];
        size_t n;
-       int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, rfds, &n);
+       int ret = read_nonblock(server_socket, buf, sizeof(buf) - 1, &n);
 
        if (ret < 0 || n == 0)
                return ret;
 
        if (ret < 0 || n == 0)
                return ret;
@@ -877,13 +808,13 @@ static int execute_server_command(fd_set *rfds)
 }
 
 /* returns 0 if no data available, 1 else */
 }
 
 /* returns 0 if no data available, 1 else */
-static int execute_afs_command(int fd, fd_set *rfds)
+static int execute_afs_command(int fd)
 {
        uint32_t cookie;
        int query_shmid;
        char buf[sizeof(cookie) + sizeof(query_shmid)];
        size_t n;
 {
        uint32_t cookie;
        int query_shmid;
        char buf[sizeof(cookie) + sizeof(query_shmid)];
        size_t n;
-       int ret = read_nonblock(fd, buf, sizeof(buf), rfds, &n);
+       int ret = read_nonblock(fd, buf, sizeof(buf), &n);
 
        if (ret < 0)
                goto err;
 
        if (ret < 0)
                goto err;
@@ -917,7 +848,7 @@ err:
 /** Shutdown connection if query has not arrived until this many seconds. */
 #define AFS_CLIENT_TIMEOUT 3
 
 /** Shutdown connection if query has not arrived until this many seconds. */
 #define AFS_CLIENT_TIMEOUT 3
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
        struct sockaddr_un unix_addr;
 {
        struct command_task *ct = context;
        struct sockaddr_un unix_addr;
@@ -927,7 +858,7 @@ static int command_post_select(struct sched *s, void *context)
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
-       ret = execute_server_command(&s->rfds);
+       ret = execute_server_command();
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                task_notify_all(s, -ret);
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                task_notify_all(s, -ret);
@@ -935,7 +866,7 @@ static int command_post_select(struct sched *s, void *context)
        }
        /* Check the list of connected clients. */
        list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
        }
        /* Check the list of connected clients. */
        list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
-               ret = execute_afs_command(client->fd, &s->rfds);
+               ret = execute_afs_command(client->fd);
                if (ret == 0) { /* prevent bogus connection flooding */
                        struct timeval diff;
                        tv_diff(now, &client->connect_time, &diff);
                if (ret == 0) { /* prevent bogus connection flooding */
                        struct timeval diff;
                        tv_diff(now, &client->connect_time, &diff);
@@ -948,7 +879,7 @@ static int command_post_select(struct sched *s, void *context)
                free(client);
        }
        /* Accept connections on the local socket. */
                free(client);
        }
        /* Accept connections on the local socket. */
-       ret = para_accept(ct->fd, &s->rfds, &unix_addr, sizeof(unix_addr), &fd);
+       ret = para_accept(ct->fd, &unix_addr, sizeof(unix_addr), &fd);
        if (ret < 0)
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
        if (ret <= 0)
        if (ret < 0)
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
        if (ret <= 0)
@@ -959,7 +890,7 @@ static int command_post_select(struct sched *s, void *context)
                close(fd);
                return 0;
        }
                close(fd);
                return 0;
        }
-       client = para_malloc(sizeof(*client));
+       client = alloc(sizeof(*client));
        client->fd = fd;
        client->connect_time = *now;
        para_list_add(&client->node, &afs_client_list);
        client->fd = fd;
        client->connect_time = *now;
        para_list_add(&client->node, &afs_client_list);
@@ -973,12 +904,20 @@ static void register_command_task(struct sched *s)
 
        ct->task = task_register(&(struct task_info) {
                .name = "afs command",
 
        ct->task = task_register(&(struct task_info) {
                .name = "afs command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = ct,
        }, s);
 }
 
                .context = ct,
        }, s);
 }
 
+static int afs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+       mutex_lock(mmd_mutex);
+       daemon_set_loglevel(mmd->loglevel);
+       mutex_unlock(mmd_mutex);
+       return xpoll(fds, nfds, timeout);
+}
+
 /**
  * Initialize the audio file selector process.
  *
 /**
  * Initialize the audio file selector process.
  *
@@ -987,12 +926,10 @@ static void register_command_task(struct sched *s)
 __noreturn void afs_init(int socket_fd)
 {
        static struct sched s;
 __noreturn void afs_init(int socket_fd)
 {
        static struct sched s;
-       int i, ret;
+       int ret;
 
        register_signal_task(&s);
        init_list_head(&afs_client_list);
 
        register_signal_task(&s);
        init_list_head(&afs_client_list);
-       for (i = 0; i < NUM_AFS_TABLES; i++)
-               afs_tables[i].init(&afs_tables[i]);
        ret = open_afs_tables();
        if (ret < 0)
                goto out;
        ret = open_afs_tables();
        if (ret < 0)
                goto out;
@@ -1003,8 +940,8 @@ __noreturn void afs_init(int socket_fd)
        PARA_INFO_LOG("server_socket: %d\n", server_socket);
        init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
        register_command_task(&s);
        PARA_INFO_LOG("server_socket: %d\n", server_socket);
        init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
        register_command_task(&s);
-       s.default_timeout.tv_sec = 0;
-       s.default_timeout.tv_usec = 999 * 1000;
+       s.default_timeout = 1000;
+       s.poll_function = afs_poll;
        ret = write(socket_fd, "\0", 1);
        if (ret != 1) {
                if (ret == 0)
        ret = write(socket_fd, "\0", 1);
        if (ret != 1) {
                if (ret == 0)
@@ -1014,7 +951,8 @@ __noreturn void afs_init(int socket_fd)
        }
        ret = schedule(&s);
        sched_shutdown(&s);
        }
        ret = schedule(&s);
        sched_shutdown(&s);
-       close_current_mood();
+       mood_unload(NULL);
+       playlist_unload(NULL);
 out_close:
        close_afs_tables();
 out:
 out_close:
        close_afs_tables();
 out:
@@ -1027,6 +965,57 @@ out:
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
+static int com_select_callback(struct afs_callback_arg *aca)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       const char *arg;
+       int ret;
+       struct para_buffer *pbout;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       arg = lls_input(0, aca->lpr);
+       pbout = SERVER_CMD_OPT_GIVEN(SELECT, VERBOSE, aca->lpr)?
+               &aca->pbout : NULL;
+       score_clear();
+       if (current_play_mode == PLAY_MODE_MOOD)
+               mood_unload(NULL);
+       else
+               playlist_unload(NULL);
+       ret = activate_mood_or_playlist(arg, pbout);
+       if (ret >= 0)
+               goto free_lpr;
+       /* ignore subsequent errors (but log them) */
+       if (current_mop && strcmp(current_mop, arg) != 0) {
+               int ret2;
+               afs_error(aca, "switching back to %s\n", current_mop);
+               ret2 = activate_mood_or_playlist(current_mop, pbout);
+               if (ret2 >= 0)
+                       goto free_lpr;
+               afs_error(aca, "could not reactivate %s: %s\n", current_mop,
+                       para_strerror(-ret2));
+       }
+       activate_mood_or_playlist(NULL, pbout);
+free_lpr:
+       lls_free_parse_result(aca->lpr, cmd);
+       return ret;
+}
+
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
+{
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       ret = send_lls_callback_request(com_select_callback, cmd, lpr, cc);
+       return ret == osl(-E_OSL_RB_KEY_NOT_FOUND)? -E_BAD_MOP : ret;
+}
+EXPORT_SERVER_CMD_HANDLER(select);
+
 static int com_init_callback(struct afs_callback_arg *aca)
 {
        uint32_t table_mask = *(uint32_t *)aca->query.data;
 static int com_init_callback(struct afs_callback_arg *aca)
 {
        uint32_t table_mask = *(uint32_t *)aca->query.data;
@@ -1035,16 +1024,15 @@ static int com_init_callback(struct afs_callback_arg *aca)
        close_afs_tables();
        get_database_dir();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
        close_afs_tables();
        get_database_dir();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
+               const struct afs_table *t = afs_tables + i;
 
                if (!(table_mask & (1 << i)))
                        continue;
 
                if (!(table_mask & (1 << i)))
                        continue;
-               if (!t->create)
+               if (!t->ops->create)
                        continue;
                        continue;
-               ret = t->create(database_dir);
+               ret = t->ops->create(database_dir);
                if (ret < 0) {
                if (ret < 0) {
-                       para_printf(&aca->pbout, "cannot create table %s\n",
-                               t->name);
+                       afs_error(aca, "cannot create table %s\n", t->name);
                        goto out;
                }
                para_printf(&aca->pbout, "successfully created %s table\n",
                        goto out;
                }
                para_printf(&aca->pbout, "successfully created %s table\n",
@@ -1052,7 +1040,7 @@ static int com_init_callback(struct afs_callback_arg *aca)
        }
        ret = open_afs_tables();
        if (ret < 0)
        }
        ret = open_afs_tables();
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot open afs tables: %s\n",
+               afs_error(aca, "cannot open afs tables: %s\n",
                        para_strerror(-ret));
 out:
        return ret;
                        para_strerror(-ret));
 out:
        return ret;
@@ -1066,14 +1054,15 @@ static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
                .size = sizeof(table_mask)};
        unsigned num_inputs = lls_num_inputs(lpr);
 
                .size = sizeof(table_mask)};
        unsigned num_inputs = lls_num_inputs(lpr);
 
-       ret = make_database_dir();
+       get_database_dir();
+       ret = para_mkdir(database_dir);
        if (ret < 0)
                return ret;
        if (num_inputs > 0) {
                table_mask = 0;
                for (i = 0; i < num_inputs; i++) {
                        for (j = 0; j < NUM_AFS_TABLES; j++) {
        if (ret < 0)
                return ret;
        if (num_inputs > 0) {
                table_mask = 0;
                for (i = 0; i < num_inputs; i++) {
                        for (j = 0; j < NUM_AFS_TABLES; j++) {
-                               struct afs_table *t = &afs_tables[j];
+                               const struct afs_table *t = afs_tables + j;
 
                                if (strcmp(lls_input(i, lpr), t->name))
                                        continue;
 
                                if (strcmp(lls_input(i, lpr), t->name))
                                        continue;
@@ -1147,10 +1136,10 @@ __must_check int afs_event(enum afs_events event, struct para_buffer *pb,
        int i, ret;
 
        for (i = 0; i < NUM_AFS_TABLES; i++) {
        int i, ret;
 
        for (i = 0; i < NUM_AFS_TABLES; i++) {
-               struct afs_table *t = &afs_tables[i];
-               if (!t->event_handler)
+               const struct afs_table *t = afs_tables + i;
+               if (!t->ops->event_handler)
                        continue;
                        continue;
-               ret = t->event_handler(event, pb, data);
+               ret = t->ops->event_handler(event, pb, data);
                if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %u: %s\n", t->name,
                                event, para_strerror(-ret));
                if (ret < 0) {
                        PARA_CRIT_LOG("table %s, event %u: %s\n", t->name,
                                event, para_strerror(-ret));
diff --git a/afs.h b/afs.h
index b1606493a05f047afb1e8ecb0fdd0a5bbcbafcfe..e8b8c865bda36b9f905b2c1cb435d03bc12d3cbd 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -73,19 +73,15 @@ struct afsi_change_event_data {
        struct afs_info *old_afsi;
 };
 
        struct afs_info *old_afsi;
 };
 
-/** Function pointers for table handling.  */
-struct afs_table {
-       /** Initializes the other pointers in this struct. */
-       void (*init)(struct afs_table *t);
-       /** The name of this table. */
-       const char *name;
-       /** Gets called on startup and on \p SIGHUP. */
+/** Methods for table startup/shutdown and event handling. */
+struct afs_table_operations {
+       /** Gets called on startup and on SIGHUP. */
        int (*open)(const char *base_dir);
        int (*open)(const char *base_dir);
-       /** Gets called on shutdown and on \p SIGHUP. */
+       /** Gets called on shutdown and on SIGHUP. */
        void (*close)(void);
        void (*close)(void);
-       /** Called by the \a init afs command. */
+       /** Called from the init command. */
        int (*create)(const char *);
        int (*create)(const char *);
-       /** Handles afs events. */
+       /** Handle events generated by other tables. See enum \ref afs_events. */
        int (*event_handler)(enum afs_events event, struct para_buffer *pb,
                void *data);
 };
        int (*event_handler)(enum afs_events event, struct para_buffer *pb,
                void *data);
 };
@@ -173,6 +169,8 @@ struct afs_callback_arg {
 };
 
 /**
 };
 
 /**
+ * The "top half" of an afs command.
+ *
  * Afs command handlers run as a process which is not related to the afs
  * process, i.e. they can not change the address space of afs directly.
  * Therefore afs commands typically consist of two functions: The command
  * Afs command handlers run as a process which is not related to the afs
  * process, i.e. they can not change the address space of afs directly.
  * Therefore afs commands typically consist of two functions: The command
@@ -183,9 +181,13 @@ struct afs_callback_arg {
 typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
 typedef int afs_callback(struct afs_callback_arg *aca);
 
 /**
+ * Dispatch the output of an afs callback.
+ *
  * Some AFS callbacks need to send data back to the command handler. Pointers
  * to this type of function are passed to \ref send_callback_request() and
  * Some AFS callbacks need to send data back to the command handler. Pointers
  * to this type of function are passed to \ref send_callback_request() and
- * related functions to dispatch the data in the command handler process.
+ * related functions to dispatch the data in the command handler process. Most
+ * (but not all) afs commands pass \ref afs_cb_result_handler(), which sends
+ * the output of the callback to the connected client.
  */
 typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
 int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
  */
 typedef int callback_result_handler(struct osl_object *result, uint8_t band, void *private);
 int afs_cb_result_handler(struct osl_object *result, uint8_t band, void *private);
@@ -229,30 +231,33 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
 int send_lls_callback_request(afs_callback *f,
                const struct lls_command * const cmd,
                struct lls_parse_result *lpr, void *private_result_data);
 int send_lls_callback_request(afs_callback *f,
                const struct lls_command * const cmd,
                struct lls_parse_result *lpr, void *private_result_data);
+__printf_2_3 void afs_error(const struct afs_callback_arg *aca,
+               const char *fmt,...);
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
 int for_each_matching_row(struct pattern_match_data *pmd);
 
 /* score */
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
 int for_each_matching_row(struct pattern_match_data *pmd);
 
 /* score */
-void score_init(struct afs_table *t);
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func);
+extern const struct afs_table_operations score_ops;
+void score_open(struct osl_table **result);
+void score_close(struct osl_table *t);
+int score_loop(osl_rbtree_loop_func *func, struct osl_table *t, void *data);
 int score_get_best(struct osl_row **aft_row, long *score);
 int get_score_and_aft_row(struct osl_row *score_row, long *score, struct osl_row **aft_row);
 int score_get_best(struct osl_row **aft_row, long *score);
 int get_score_and_aft_row(struct osl_row *score_row, long *score, struct osl_row **aft_row);
-int score_add(const struct osl_row *row, long score);
+int score_add(const struct osl_row *aft_row, long score, struct osl_table *t);
 int score_update(const struct osl_row *aft_row, long new_score);
 int score_update(const struct osl_row *aft_row, long new_score);
-int get_num_admissible_files(unsigned *num);
 int score_delete(const struct osl_row *aft_row);
 int score_delete(const struct osl_row *aft_row);
-int clear_score_table(void);
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank);
+void score_clear(void);
+bool row_belongs_to_score_table(const struct osl_row *aft_row);
 
 /* attribute */
 
 /* attribute */
-void attribute_init(struct afs_table *t);
+extern const struct afs_table_operations attr_ops;
 void get_attribute_bitmap(const uint64_t *atts, char *buf); /* needed by com_ls() */
 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum);
 int get_attribute_text(uint64_t *atts, const char *delim, char **text);
 int attribute_check_callback(struct afs_callback_arg *aca);
 
 /* aft */
 void get_attribute_bitmap(const uint64_t *atts, char *buf); /* needed by com_ls() */
 int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum);
 int get_attribute_text(uint64_t *atts, const char *delim, char **text);
 int attribute_check_callback(struct afs_callback_arg *aca);
 
 /* aft */
-void aft_init(struct afs_table *t);
+extern const struct afs_table_operations aft_ops;
 int aft_get_row_of_path(const char *path, struct osl_row **row);
 int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
 int open_and_update_audio_file(int *fd);
 int aft_get_row_of_path(const char *path, struct osl_row **row);
 int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb);
 int open_and_update_audio_file(int *fd);
@@ -264,9 +269,18 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
 int aft_check_callback(struct afs_callback_arg *aca);
 void free_status_items(void);
 
 int aft_check_callback(struct afs_callback_arg *aca);
 void free_status_items(void);
 
+/* mood */
+struct mood_instance;
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg);
+int mood_loop(struct mood_instance *m, osl_rbtree_loop_func *func, void *data);
+void mood_unload(struct mood_instance *m);
+int mood_check_callback(struct afs_callback_arg *aca);
+
 /* playlist */
 /* playlist */
-int playlist_open(const char *name);
-void playlist_close(void);
+struct playlist_instance;
+int playlist_load(const char *name, struct playlist_instance **result, char **msg);
+int playlist_loop(struct playlist_instance *pi, osl_rbtree_loop_func *func, void *data);
+void playlist_unload(struct playlist_instance *pi);
 int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
 int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */
@@ -275,15 +289,15 @@ int playlist_check_callback(struct afs_callback_arg *aca);
 
 /** Define exported functions and a table pointer for an osl blob table. */
 #define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \
 
 /** Define exported functions and a table pointer for an osl blob table. */
 #define DECLARE_BLOB_SYMBOLS(table_name, cmd_prefix) \
-       void table_name ## _init(struct afs_table *t); \
        int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
        int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
        int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
        int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def); \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def); \
        int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
                char **name, struct osl_object *def); \
        int table_name ##_event_handler(enum afs_events event, \
                struct para_buffer *pb, void *data); \
        int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
                char **name, struct osl_object *def); \
        int table_name ##_event_handler(enum afs_events event, \
                struct para_buffer *pb, void *data); \
-       extern struct osl_table *table_name ## _table;
+       extern struct osl_table *table_name ## _table; \
+       extern const struct afs_table_operations table_name ## _ops;
 
 /** \cond blob_symbols */
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
 
 /** \cond blob_symbols */
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
diff --git a/aft.c b/aft.c
index 5f9098aa458955d1338bec6183c67493ce798cad..f1aca7fb8ccc3e31575303a4762d6a7ea35a054f 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -6,6 +6,7 @@
 #include <sys/mman.h>
 #include <fnmatch.h>
 #include <sys/shm.h>
 #include <sys/mman.h>
 #include <fnmatch.h>
 #include <sys/shm.h>
+#include <dirent.h>
 #include <osl.h>
 #include <lopsub.h>
 
 #include <osl.h>
 #include <lopsub.h>
 
@@ -412,7 +413,7 @@ static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
                return;
        }
        sz  = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
                return;
        }
        sz  = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
-       afhi->chunk_table = para_malloc(sz);
+       afhi->chunk_table = alloc(sz);
        for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
                afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
 }
        for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
                afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
 }
@@ -779,18 +780,17 @@ static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
 static void write_filename_items(struct para_buffer *b, const char *path,
                bool basename)
 {
 static void write_filename_items(struct para_buffer *b, const char *path,
                bool basename)
 {
-       char *val;
+       const char *slash;
 
        if (basename) {
                WRITE_STATUS_ITEM(b, SI_basename, "%s\n", path);
                return;
        }
        WRITE_STATUS_ITEM(b, SI_path, "%s\n", path);
 
        if (basename) {
                WRITE_STATUS_ITEM(b, SI_basename, "%s\n", path);
                return;
        }
        WRITE_STATUS_ITEM(b, SI_path, "%s\n", path);
-       val = para_basename(path);
-       WRITE_STATUS_ITEM(b, SI_basename, "%s\n", val? val : "");
-       val = para_dirname(path);
-       WRITE_STATUS_ITEM(b, SI_directory, "%s\n", val? val : "");
-       free(val);
+       slash = strrchr(path, '/');
+       WRITE_STATUS_ITEM(b, SI_basename, "%s\n", slash? slash + 1 : path);
+       WRITE_STATUS_ITEM(b, SI_directory, "%.*s\n",
+               slash? (int)(slash - path) : (int)strlen(path), path);
 }
 
 static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
 }
 
 static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
@@ -800,6 +800,12 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
        int ret, i;
        char *buf;
 
        int ret, i;
        char *buf;
 
+       para_printf(b, "%s\nchunk_time: %lu:%lu\n", d->path,
+               (long unsigned) d->afhi.chunk_tv.tv_sec,
+               (long unsigned) d->afhi.chunk_tv.tv_usec
+       );
+       if (afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+               return 0;
        ret = aft_get_row_of_hash(d->hash, &aft_row);
        if (ret < 0)
                return ret;
        ret = aft_get_row_of_hash(d->hash, &aft_row);
        if (ret < 0)
                return ret;
@@ -807,12 +813,7 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0)
                return ret;
                AFTCOL_CHUNKS, &chunk_table_obj));
        if (ret < 0)
                return ret;
-       para_printf(b, "%s\n"
-               "chunk_time: %lu:%lu\nchunk_offsets: ",
-               d->path,
-               (long unsigned) d->afhi.chunk_tv.tv_sec,
-               (long unsigned) d->afhi.chunk_tv.tv_usec
-       );
+       para_printf(b, "chunk_offsets: ");
        buf = chunk_table_obj.data;
        for (
                i = 0;
        buf = chunk_table_obj.data;
        for (
                i = 0;
@@ -897,13 +898,13 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                goto out;
        }
        if (opts->mode == LS_MODE_MBOX) {
                goto out;
        }
        if (opts->mode == LS_MODE_MBOX) {
-               const char *bn = para_basename(d->path);
+               const char *slash = strrchr(d->path, '/');
                para_printf(b,
                        "From foo@localhost %s\n"
                        "Received: from\nTo: bar\nFrom: a\n"
                        "Subject: %s\n\n",
                        last_played_time,
                para_printf(b,
                        "From foo@localhost %s\n"
                        "Received: from\nTo: bar\nFrom: a\n"
                        "Subject: %s\n\n",
                        last_played_time,
-                       bn? bn : "?");
+                       slash? slash + 1 : "?");
        }
        write_filename_items(b, d->path, lls_opt_given(r_b));
        if (lls_opt_given(r_a))
        }
        write_filename_items(b, d->path, lls_opt_given(r_b));
        if (lls_opt_given(r_a))
@@ -913,7 +914,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                goto out;
        write_image_items(b, afsi);
        write_lyrics_items(b, afsi);
                goto out;
        write_image_items(b, afsi);
        write_lyrics_items(b, afsi);
-       hash_to_asc(d->hash, asc_hash);
+       hash2_to_asc(d->hash, asc_hash);
        WRITE_STATUS_ITEM(b, SI_hash, "%s\n", asc_hash);
        WRITE_STATUS_ITEM(b, SI_bitrate, "%dkbit/s\n", afhi->bitrate);
        WRITE_STATUS_ITEM(b, SI_format, "%s\n",
        WRITE_STATUS_ITEM(b, SI_hash, "%s\n", asc_hash);
        WRITE_STATUS_ITEM(b, SI_bitrate, "%dkbit/s\n", afhi->bitrate);
        WRITE_STATUS_ITEM(b, SI_format, "%s\n",
@@ -1067,8 +1068,8 @@ again:
        if (ret < 0)
                return ret;
        if (!d->hash)
        if (ret < 0)
                return ret;
        if (!d->hash)
-               d->hash = para_malloc(HASH_SIZE);
-       memcpy(d->hash, tmp_hash, HASH_SIZE);
+               d->hash = alloc(HASH2_SIZE);
+       memcpy(d->hash, tmp_hash, HASH2_SIZE);
        free(d->path);
        ret = get_audio_file_path_of_row(current_aft_row, &d->path);
        if (ret < 0)
        free(d->path);
        ret = get_audio_file_path_of_row(current_aft_row, &d->path);
        if (ret < 0)
@@ -1102,7 +1103,7 @@ again:
        if (ret < 0)
                goto out;
        hash2_function(map.data, map.size, file_hash);
        if (ret < 0)
                goto out;
        hash2_function(map.data, map.size, file_hash);
-       ret = hash_compare(file_hash, d->hash);
+       ret = hash2_compare(file_hash, d->hash);
        para_munmap(map.data, map.size);
        if (ret) {
                ret = -E_HASH_MISMATCH;
        para_munmap(map.data, map.size);
        if (ret) {
                ret = -E_HASH_MISMATCH;
@@ -1225,7 +1226,7 @@ static int sort_matching_paths(struct ls_options *options)
        int (*compar)(const void *, const void *);
        int i;
 
        int (*compar)(const void *, const void *);
        int i;
 
-       options->data_ptr = para_malloc(nmemb * sizeof(*options->data_ptr));
+       options->data_ptr = arr_alloc(nmemb, sizeof(*options->data_ptr));
        for (i = 0; i < nmemb; i++)
                options->data_ptr[i] = options->data + i;
 
        for (i = 0; i < nmemb; i++)
                options->data_ptr[i] = options->data + i;
 
@@ -1318,8 +1319,8 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        if (options->num_matching_paths > options->array_size) {
                options->array_size++;
                options->array_size *= 2;
        if (options->num_matching_paths > options->array_size) {
                options->array_size++;
                options->array_size *= 2;
-               options->data = para_realloc(options->data, options->array_size
-                       sizeof(*options->data));
+               options->data = arr_realloc(options->data, options->array_size,
+                       sizeof(*options->data));
        }
        d = options->data + tmp;
        ret = get_afsi_of_row(aft_row, &d->afsi);
        }
        d = options->data + tmp;
        ret = get_afsi_of_row(aft_row, &d->afsi);
@@ -1361,28 +1362,67 @@ err:
        return ret;
 }
 
        return ret;
 }
 
+static int mop_loop(const char *arg, struct afs_callback_arg *aca,
+               struct ls_options *opts)
+{
+       int ret;
+       char *msg;
+
+       if (!arg || strcmp(arg, ".") == 0)
+               return score_loop(prepare_ls_row, NULL, opts);
+       if (!strncmp(arg, "m/", 2)) {
+               struct mood_instance *m;
+               ret = mood_load(arg + 2, &m, &msg);
+               if (ret < 0)
+                       afs_error(aca, "%s", msg);
+               free(msg);
+               if (ret < 0)
+                       return ret;
+               ret = mood_loop(m, prepare_ls_row, opts);
+               mood_unload(m);
+               return ret;
+       }
+       if (!strncmp(arg, "p/", 2)) {
+               struct playlist_instance *pi;
+               ret = playlist_load(arg + 2, &pi, &msg);
+               if (ret < 0)
+                       afs_error(aca, "%s", msg);
+               free(msg);
+               if (ret < 0)
+                       return ret;
+               ret = playlist_loop(pi, prepare_ls_row, opts);
+               playlist_unload(pi);
+               return ret;
+       }
+       afs_error(aca, "bad mood/playlist specifier: %s\n", arg);
+       return -ERRNO_TO_PARA_ERROR(EINVAL);
+}
+
 static int com_ls_callback(struct afs_callback_arg *aca)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
        struct ls_options *opts = aca->query.data;
 static int com_ls_callback(struct afs_callback_arg *aca)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
        struct ls_options *opts = aca->query.data;
-       int i = 0, ret;
+       int ret;
        time_t current_time;
        time_t current_time;
-       const struct lls_opt_result *r_r;
+       const struct lls_opt_result *r_r, *r_a;
+       uint32_t limit, k, n;
 
        ret = lls_deserialize_parse_result(
                (char *)aca->query.data + sizeof(*opts), cmd, &opts->lpr);
        assert(ret >= 0);
        r_r = SERVER_CMD_OPT_RESULT(LS, REVERSE, opts->lpr);
 
        ret = lls_deserialize_parse_result(
                (char *)aca->query.data + sizeof(*opts), cmd, &opts->lpr);
        assert(ret >= 0);
        r_r = SERVER_CMD_OPT_RESULT(LS, REVERSE, opts->lpr);
-
+       r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
        aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
        aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
-       if (admissible_only(opts))
-               ret = admissible_file_loop(opts, prepare_ls_row);
-       else
+       if (admissible_only(opts)) {
+               const char *arg = lls_string_val(0, r_a);
+               ret = mop_loop(arg, aca, opts);
+       } else
                ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
                        prepare_ls_row));
        if (ret < 0)
                goto out;
                ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
                        prepare_ls_row));
        if (ret < 0)
                goto out;
-       if (opts->num_matching_paths == 0) {
+       n = opts->num_matching_paths;
+       if (n == 0) {
                ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
                goto out;
        }
                ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
                goto out;
        }
@@ -1390,20 +1430,14 @@ static int com_ls_callback(struct afs_callback_arg *aca)
        if (ret < 0)
                goto out;
        time(&current_time);
        if (ret < 0)
                goto out;
        time(&current_time);
-       if (lls_opt_given(r_r))
-               for (i = opts->num_matching_paths - 1; i >= 0; i--) {
-                       ret = print_list_item(opts->data_ptr[i], opts,
-                               &aca->pbout, current_time);
-                       if (ret < 0)
-                               goto out;
-               }
-       else
-               for (i = 0; i < opts->num_matching_paths; i++) {
-                       ret = print_list_item(opts->data_ptr[i], opts,
-                               &aca->pbout, current_time);
-                       if (ret < 0)
-                               goto out;
-               }
+       limit = SERVER_CMD_UINT32_VAL(LS, LIMIT, opts->lpr);
+       for (k = 0; k < n && (limit == 0 || k < limit); k++) {
+               uint32_t idx = lls_opt_given(r_r)? n - 1 - k : k;
+               ret = print_list_item(opts->data_ptr[idx], opts, &aca->pbout,
+                       current_time);
+               if (ret < 0)
+                       goto out;
+       }
 out:
        lls_free_parse_result(opts->lpr, cmd);
        free(opts->data);
 out:
        lls_free_parse_result(opts->lpr, cmd);
        free(opts->data);
@@ -1425,7 +1459,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
        ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
        assert(ret >= 0);
        query.size += sizeof(*opts);
        ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
        assert(ret >= 0);
        query.size += sizeof(*opts);
-       query.data = para_malloc(query.size);
+       query.data = alloc(query.size);
        opts = query.data;
        memset(opts, 0, sizeof(*opts));
        slpr = query.data + sizeof(*opts);
        opts = query.data;
        memset(opts, 0, sizeof(*opts));
        slpr = query.data + sizeof(*opts);
@@ -1444,7 +1478,7 @@ static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
                else if (!strcmp(val, "m") || !strcmp(val, "mbox"))
                        opts->mode = LS_MODE_MBOX;
                else if (!strcmp(val, "c") || !strcmp(val, "chunk-table"))
                else if (!strcmp(val, "m") || !strcmp(val, "mbox"))
                        opts->mode = LS_MODE_MBOX;
                else if (!strcmp(val, "c") || !strcmp(val, "chunk-table"))
-                       opts->mode = LS_MODE_MBOX;
+                       opts->mode = LS_MODE_CHUNKS;
                else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
                        opts->mode = LS_MODE_PARSER;
                else {
                else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
                        opts->mode = LS_MODE_PARSER;
                else {
@@ -1554,7 +1588,7 @@ static void save_add_callback_buffer(unsigned char *hash, const char *path,
        size_t afhi_size = sizeof_afhi_buf(afhi);
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
                + sizeof_chunk_table(afhi) + slpr_size;
        size_t afhi_size = sizeof_afhi_buf(afhi);
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
                + sizeof_chunk_table(afhi) + slpr_size;
-       char *buf = para_malloc(size);
+       char *buf = alloc(size);
        uint32_t pos;
 
        assert(size <= ~(uint32_t)0);
        uint32_t pos;
 
        assert(size <= ~(uint32_t)0);
@@ -1638,7 +1672,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
        char asc[2 * HASH2_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
        char asc[2 * HASH2_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
-       char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
+       uint32_t slpr_offset = read_u32(buf + CAB_LPR_OFFSET);
+       char *slpr = buf + slpr_offset;
        struct afs_info default_afsi = {.last_played = 0};
        uint16_t afhi_offset, chunks_offset;
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
        struct afs_info default_afsi = {.last_played = 0};
        uint16_t afhi_offset, chunks_offset;
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
@@ -1650,7 +1685,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
        r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
-       hash_to_asc(hash, asc);
+       hash2_to_asc(hash, asc);
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
        objs[AFTCOL_HASH].size = HASH2_SIZE;
 
        objs[AFTCOL_HASH].data = buf + CAB_HASH_OFFSET;
        objs[AFTCOL_HASH].size = HASH2_SIZE;
 
@@ -1706,6 +1741,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        /* no hs or force mode, child must have sent afhi */
        afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
        chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
        /* no hs or force mode, child must have sent afhi */
        afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
        chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
+       assert(chunks_offset <= slpr_offset);
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
@@ -1713,14 +1749,14 @@ static int com_add_callback(struct afs_callback_arg *aca)
        if (!objs[AFTCOL_AFHI].size) /* "impossible" */
                goto out;
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
        if (!objs[AFTCOL_AFHI].size) /* "impossible" */
                goto out;
        objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
-       objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
+       objs[AFTCOL_CHUNKS].size = slpr_offset - chunks_offset;
        if (pb && !hs) { /* update pb's hash */
                char old_asc[2 * HASH2_SIZE + 1];
                unsigned char *old_hash;
                ret = get_hash_of_row(pb, &old_hash);
                if (ret < 0)
                        goto out;
        if (pb && !hs) { /* update pb's hash */
                char old_asc[2 * HASH2_SIZE + 1];
                unsigned char *old_hash;
                ret = get_hash_of_row(pb, &old_hash);
                if (ret < 0)
                        goto out;
-               hash_to_asc(old_hash, old_asc);
+               hash2_to_asc(old_hash, old_asc);
                if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
                if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
@@ -1762,7 +1798,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
 out:
        if (ret < 0)
        ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "could not add %s\n", path);
+               afs_error(aca, "could not add %s\n", path);
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
@@ -1904,6 +1940,53 @@ out_free:
        return send_ret;
 }
 
        return send_ret;
 }
 
+/*
+ * Call back once for each regular file below a directory.
+ *
+ * Traverse the given directory recursively and call the supplied callback for
+ * each regular file encountered. The first argument to the callback will be
+ * the path to the regular file and the second argument will be the data
+ * pointer. All file types except regular files and directories are ignored. In
+ * particular, symlinks are not followed. Subdirectories are ignored silently
+ * if the calling process has insufficient access permissions.
+ */
+static int for_each_file_in_dir(const char *dirname,
+               int (*func)(const char *, void *), void *data)
+{
+       int ret;
+       DIR *dir;
+       struct dirent *entry;
+
+       dir = opendir(dirname);
+       if (!dir)
+               return errno == EACCES? 1 : -ERRNO_TO_PARA_ERROR(errno);
+       /* scan cwd recursively */
+       while ((entry = readdir(dir))) {
+               char *tmp;
+               struct stat s;
+
+               if (!strcmp(entry->d_name, "."))
+                       continue;
+               if (!strcmp(entry->d_name, ".."))
+                       continue;
+               tmp = make_message("%s/%s", dirname, entry->d_name);
+               ret = 0;
+               if (lstat(tmp, &s) != -1) {
+                       if (S_ISREG(s.st_mode))
+                               ret = func(tmp, data);
+                       else if (S_ISDIR(s.st_mode))
+                               ret = for_each_file_in_dir(tmp, func, data);
+               }
+               free(tmp);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = 1;
+out:
+       closedir(dir);
+       return ret;
+}
+
 static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret;
 static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret;
@@ -1980,12 +2063,12 @@ static int touch_audio_file(__a_unused struct osl_table *table,
 
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
 
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        ret = load_afsi(&old_afsi, &obj);
        if (ret < 0) {
                return ret;
        }
        ret = load_afsi(&old_afsi, &obj);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot touch %s\n", name);
+               afs_error(aca, "cannot touch %s\n", name);
                return ret;
        }
        new_afsi = old_afsi;
                return ret;
        }
        new_afsi = old_afsi;
@@ -2039,7 +2122,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                uint32_t id = lls_uint32_val(0, r_i);
                ret = img_get_name_by_id(id, NULL);
                if (ret < 0) {
                uint32_t id = lls_uint32_val(0, r_i);
                ret = img_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid image ID: %u\n", id);
+                       afs_error(aca, "invalid image ID: %u\n", id);
                        return ret;
                }
        }
                        return ret;
                }
        }
@@ -2048,7 +2131,7 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                uint32_t id = lls_uint32_val(0, r_y);
                ret = lyr_get_name_by_id(id, NULL);
                if (ret < 0) {
                uint32_t id = lls_uint32_val(0, r_y);
                ret = lyr_get_name_by_id(id, NULL);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
+                       afs_error(aca, "invalid lyrics ID: %u\n", id);
                        return ret;
                }
        }
                        return ret;
                }
        }
@@ -2091,7 +2174,7 @@ static int remove_audio_file(__a_unused struct osl_table *table,
                return ret;
        ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
                return ret;
        ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
        return ret;
 }
 
        return ret;
 }
 
@@ -2321,13 +2404,13 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                                goto out; /* no attribute modifier given */
                        goto set_atts;
                }
                                goto out; /* no attribute modifier given */
                        goto set_atts;
                }
-               p = para_malloc(len);
+               p = alloc(len);
                memcpy(p, arg, len - 1);
                p[len - 1] = '\0';
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                free(p);
                if (ret < 0) {
                memcpy(p, arg, len - 1);
                p[len - 1] = '\0';
                ret = get_attribute_bitnum_by_name(p, &bitnum);
                free(p);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "invalid argument: %s\n", arg);
+                       afs_error(aca, "invalid argument: %s\n", arg);
                        goto out;
                }
                if (c == '+')
                        goto out;
                }
                if (c == '+')
@@ -2649,15 +2732,10 @@ static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
        }
 }
 
        }
 }
 
-/**
- * Initialize the audio file table.
- *
- * \param t Pointer to the structure to be initialized.
- */
-void aft_init(struct afs_table *t)
-{
-       t->open = aft_open;
-       t->close = aft_close;
-       t->create = aft_create;
-       t->event_handler = aft_event_handler;
-}
+/** The audio file table contains information about known audio files. */
+const struct afs_table_operations aft_ops = {
+       .open = aft_open,
+       .close = aft_close,
+       .create = aft_create,
+       .event_handler = aft_event_handler,
+};
index 1d81e5d9dbabbcef772d182eabe6a03ba5a51915..af4adc46382da236e7fec5786fa3d40234f16546 100644 (file)
@@ -52,7 +52,7 @@ static int alsa_mix_open(const char *dev, struct mixer_handle **handle)
 
        PARA_INFO_LOG("snd_mixer_{open,attach,register,load}\n");
        *handle = NULL;
 
        PARA_INFO_LOG("snd_mixer_{open,attach,register,load}\n");
        *handle = NULL;
-       h = para_calloc(sizeof(*h));
+       h = zalloc(sizeof(*h));
        h->card = para_strdup(dev? dev : "hw:0");
        ret = snd_mixer_open(&h->mixer, 0);
        if (ret < 0) {
        h->card = para_strdup(dev? dev : "hw:0");
        ret = snd_mixer_open(&h->mixer, 0);
        if (ret < 0) {
index bbbf8b650ac86c4952ac46f22c166583018bfb88..53d7b1b454932c36b3e578d794d688cee6cf7270 100644 (file)
@@ -48,7 +48,7 @@ struct private_alsa_write_data {
        /* time until buffer underrun occurs, in milliseconds */
        unsigned buffer_time;
        struct timeval drain_barrier;
        /* time until buffer underrun occurs, in milliseconds */
        unsigned buffer_time;
        struct timeval drain_barrier;
-       /* File descriptor for select(). */
+       /* File descriptor to monitor for reading. */
        int poll_fd;
 };
 
        int poll_fd;
 };
 
@@ -202,7 +202,7 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-static void alsa_write_pre_select(struct sched *s, void *context)
+static void alsa_write_pre_monitor(struct sched *s, void *context)
 {
        struct pollfd pfd;
        struct writer_node *wn = context;
 {
        struct pollfd pfd;
        struct writer_node *wn = context;
@@ -230,7 +230,7 @@ static void alsa_write_pre_select(struct sched *s, void *context)
                return;
        }
        pad->poll_fd = pfd.fd;
                return;
        }
        pad->poll_fd = pfd.fd;
-       para_fd_set(pfd.fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(pfd.fd, s);
 }
 
 static void alsa_close(struct writer_node *wn)
 }
 
 static void alsa_close(struct writer_node *wn)
@@ -254,7 +254,7 @@ free_pad:
        free(pad);
 }
 
        free(pad);
 }
 
-static int alsa_write_post_select(__a_unused struct sched *s, void *context)
+static int alsa_write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_alsa_write_data *pad = wn->private_data;
@@ -277,7 +277,7 @@ again:
        bytes = btr_next_buffer(btrn, &data);
        if (ret < 0 || bytes < wn->min_iqs) { /* eof */
                assert(btr_no_parent(btrn));
        bytes = btr_next_buffer(btrn, &data);
        if (ret < 0 || bytes < wn->min_iqs) { /* eof */
                assert(btr_no_parent(btrn));
-               ret = -E_WRITE_COMMON_EOF;
+               ret = -E_EOF;
                if (!pad)
                        goto err;
                /* wait until pending frames are played */
                if (!pad)
                        goto err;
                /* wait until pending frames are played */
@@ -296,7 +296,7 @@ again:
 
                if (bytes == 0) /* no data available */
                        return 0;
 
                if (bytes == 0) /* no data available */
                        return 0;
-               pad = wn->private_data = para_calloc(sizeof(*pad));
+               pad = wn->private_data = zalloc(sizeof(*pad));
                ret = get_btr_sample_rate(btrn, &val);
                if (ret < 0)
                        goto err;
                ret = get_btr_sample_rate(btrn, &val);
                if (ret < 0)
                        goto err;
@@ -321,7 +321,7 @@ again:
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
                char buf[100];
        frames = snd_pcm_writei(pad->handle, data, frames);
        if (frames == 0 || frames == -EAGAIN) {
                char buf[100];
-               if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+               if (pad->poll_fd >= 0 && sched_read_ok(pad->poll_fd, s))
                        if (read(pad->poll_fd, buf, 100))
                                do_nothing;
                return 0;
                        if (read(pad->poll_fd, buf, 100))
                                do_nothing;
                return 0;
@@ -349,7 +349,7 @@ err:
 
 struct writer lsg_write_cmd_com_alsa_user_data = {
 
 
 struct writer lsg_write_cmd_com_alsa_user_data = {
 
-       .pre_select = alsa_write_pre_select,
-       .post_select = alsa_write_post_select,
+       .pre_monitor = alsa_write_pre_monitor,
+       .post_monitor = alsa_write_post_monitor,
        .close = alsa_close,
 };
        .close = alsa_close,
 };
index 61b1653ea069ce479b6b609da5703fc57bc717c1..be69ad67a8d78d419f3147546785130e42c7b80b 100644 (file)
@@ -29,7 +29,7 @@ static void amp_close(struct filter_node *fn)
 
 static void amp_open(struct filter_node *fn)
 {
 
 static void amp_open(struct filter_node *fn)
 {
-       struct private_amp_data *pad = para_calloc(sizeof(*pad));
+       struct private_amp_data *pad = zalloc(sizeof(*pad));
        unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
        uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
 
        unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
        uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
 
@@ -43,7 +43,7 @@ static void amp_open(struct filter_node *fn)
                pad->amp, pad->amp / 64.0 + 1.0);
 }
 
                pad->amp, pad->amp / 64.0 + 1.0);
 }
 
-static int amp_post_select(__a_unused struct sched *s, void *context)
+static int amp_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_amp_data *pad = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_amp_data *pad = fn->private_data;
@@ -67,14 +67,14 @@ next_buffer:
        in_bytes = btr_next_buffer(btrn, (char **)&in);
        len = in_bytes / 2;
        if (len == 0) { /* eof and in_bytes == 1 */
        in_bytes = btr_next_buffer(btrn, (char **)&in);
        len = in_bytes / 2;
        if (len == 0) { /* eof and in_bytes == 1 */
-               ret = -E_AMP_EOF;
+               ret = -E_EOF;
                goto err;
        }
 
        if (inplace)
                out = in;
        else
                goto err;
        }
 
        if (inplace)
                out = in;
        else
-               out = para_malloc(len * 2);
+               out = alloc(len * 2);
 
        for (i = 0; i < len; i++) {
                int x = (in[i] * factor) >> 6;
 
        for (i = 0; i < len; i++) {
                int x = (in[i] * factor) >> 6;
@@ -100,6 +100,6 @@ err:
 const struct filter lsg_filter_cmd_com_amp_user_data = {
        .open = amp_open,
        .close = amp_close,
 const struct filter lsg_filter_cmd_com_amp_user_data = {
        .open = amp_open,
        .close = amp_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = amp_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = amp_post_monitor,
 };
 };
index 037b92993325539552ddd625b881a843aa57c5a9..43a58dd6d5a364f2370659608302e6c7d9f24324 100644 (file)
@@ -42,7 +42,7 @@ static void aow_close(struct writer_node *wn)
        ao_shutdown();
 }
 
        ao_shutdown();
 }
 
-static void aow_pre_select(struct sched *s, void *context)
+static void aow_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
@@ -184,7 +184,7 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        ao_info *info;
        const struct lls_opt_result *r;
        unsigned n;
        ao_info *info;
        const struct lls_opt_result *r;
        unsigned n;
-       struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
+       struct private_aow_data *pawd = alloc(sizeof(*pawd));
 
        ao_initialize();
        aow_show_drivers();
 
        ao_initialize();
        aow_show_drivers();
@@ -266,7 +266,7 @@ static void *aow_play(void *priv)
                                if (frames > 0)
                                        break;
                                /* eof and less than a single frame available */
                                if (frames > 0)
                                        break;
                                /* eof and less than a single frame available */
-                               ret = -E_WRITE_COMMON_EOF;
+                               ret = -E_EOF;
                                goto fail;
                        }
                        /*
                                goto fail;
                        }
                        /*
@@ -342,7 +342,7 @@ fail:
        return -E_AO_PTHREAD;
 }
 
        return -E_AO_PTHREAD;
 }
 
-static int aow_post_select(__a_unused struct sched *s, void *context)
+static int aow_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_aow_data *pawd = wn->private_data;
@@ -388,7 +388,7 @@ static int aow_post_select(__a_unused struct sched *s, void *context)
        if (!wn->btrn) {
                if (!pawd->thread_btrn) {
                        pthread_join(pawd->thread, NULL);
        if (!wn->btrn) {
                if (!pawd->thread_btrn) {
                        pthread_join(pawd->thread, NULL);
-                       return -E_AO_EOF;
+                       return -E_EOF;
                }
                PARA_INFO_LOG("waiting for play thread to terminate\n");
                return 0;
                }
                PARA_INFO_LOG("waiting for play thread to terminate\n");
                return 0;
@@ -421,7 +421,7 @@ out:
 
 struct writer lsg_write_cmd_com_ao_user_data = {
        .close = aow_close,
 
 struct writer lsg_write_cmd_com_ao_user_data = {
        .close = aow_close,
-       .pre_select = aow_pre_select,
-       .post_select = aow_post_select,
+       .pre_monitor = aow_pre_monitor,
+       .post_monitor = aow_post_monitor,
 };
 
 };
 
index fb1b3eac3482f1c0725e0e79a4b80166be511350..51630b257f7d88829c657361b06c1fd09d008326 100644 (file)
@@ -119,7 +119,7 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
        }
        ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "%s: %s\n", name, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", name, para_strerror(-ret));
                return ret;
        }
        para_printf(&aca->pbout, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
                return ret;
        }
        para_printf(&aca->pbout, "%u\t%s\n", *(unsigned char*)bitnum_obj.data,
@@ -168,11 +168,6 @@ static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
 }
 EXPORT_SERVER_CMD_HANDLER(lsatt);
 
 }
 EXPORT_SERVER_CMD_HANDLER(lsatt);
 
-struct addatt_event_data {
-       const char *name;
-       unsigned char bitnum;
-};
-
 static int com_addatt_callback(struct afs_callback_arg *aca)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
 static int com_addatt_callback(struct afs_callback_arg *aca)
 {
        const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
@@ -188,12 +183,10 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
                struct osl_object objs[NUM_ATT_COLUMNS];
                struct osl_row *row;
                unsigned char bitnum;
-               struct addatt_event_data aed;
 
                len = strlen(name);
                if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
 
                len = strlen(name);
                if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
-                       para_printf(&aca->pbout,
-                               "invalid attribute name: %s\n", name);
+                       afs_error(aca, "invalid attribute name: %s\n", name);
                        continue;
                }
                ret = get_attribute_bitnum_by_name(name, &bitnum);
                        continue;
                }
                ret = get_attribute_bitnum_by_name(name, &bitnum);
@@ -225,16 +218,14 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
                ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
-               aed.name = name;
-               aed.bitnum = bitnum;
-               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
+               ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, NULL);
                if (ret < 0)
                        goto out;
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
        if (ret < 0)
                if (ret < 0)
                        goto out;
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
        }
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "error while adding %s\n",
+               afs_error(aca, "error while adding %s\n",
                        lls_input(i, aca->lpr));
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
                        lls_input(i, aca->lpr));
        lls_free_parse_result(aca->lpr, cmd);
        return ret;
@@ -277,7 +268,7 @@ static int com_mvatt_callback(struct afs_callback_arg *aca)
        ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
 out:
        if (ret < 0)
        ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
+               afs_error(aca, "cannot rename %s to %s\n", old, new);
        else
                ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
        lls_free_parse_result(aca->lpr, cmd);
        else
                ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
        lls_free_parse_result(aca->lpr, cmd);
@@ -306,13 +297,13 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0) {
 
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        para_printf(&aca->pbout, "removing attribute %s\n", name);
        ret = osl(osl_del_row(table, row));
        if (ret < 0) {
                return ret;
        }
        para_printf(&aca->pbout, "removing attribute %s\n", name);
        ret = osl(osl_del_row(table, row));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return afs_event(ATTRIBUTE_REMOVE, &aca->pbout, &red);
                return ret;
        }
        return afs_event(ATTRIBUTE_REMOVE, &aca->pbout, &red);
@@ -499,14 +490,9 @@ static int attribute_create(const char *dir)
        return osl(osl_create_table(&attribute_table_desc));
 }
 
        return osl(osl_create_table(&attribute_table_desc));
 }
 
-/**
- * Initialize the attribute table structure.
- *
- * \param t The table structure to initialize.
- */
-void attribute_init(struct afs_table *t)
-{
-       t->open = attribute_open;
-       t->close = attribute_close;
-       t->create = attribute_create;
-}
+/** The attribute table stores name/bitnum pairs. */
+const struct afs_table_operations attr_ops = { /* no event handler */
+       .open = attribute_open,
+       .close = attribute_close,
+       .create = attribute_create,
+};
index af67063367044b675877173a1ed351bdd28f2dd1..f2e4cb91de228dcd78cae534e08d76e8514b6d88 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -107,6 +107,12 @@ static void help_completer(struct i9e_completion_info *ci,
        cr->matches = i9e_complete_commands(ci->word, audiod_completers);
 }
 
        cr->matches = i9e_complete_commands(ci->word, audiod_completers);
 }
 
+static void ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       i9e_ll_completer(ci, cr);
+}
+
 static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
 static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -143,17 +149,17 @@ static struct i9e_completer audiod_completers[] = {
        {.name = NULL}
 };
 
        {.name = NULL}
 };
 
-static void audioc_pre_select(struct sched *s, void *context)
+static void audioc_pre_monitor(struct sched *s, void *context)
 {
        struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
 
        if (ret < 0)
                sched_min_delay(s);
 {
        struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
 
        if (ret < 0)
                sched_min_delay(s);
-       para_fd_set(at->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(at->fd, s);
 }
 
 }
 
-static int audioc_post_select(struct sched *s, void *context)
+static int audioc_post_monitor(struct sched *s, void *context)
 {
        char *buf = NULL;
        struct audioc_task *at = context;
 {
        char *buf = NULL;
        struct audioc_task *at = context;
@@ -162,14 +168,14 @@ static int audioc_post_select(struct sched *s, void *context)
 
        if (ret < 0)
                goto out;
 
        if (ret < 0)
                goto out;
-       if (!FD_ISSET(at->fd, &s->rfds))
+       if (!sched_read_ok(at->fd, s))
                return 0;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
                return 0;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
-       buf = para_malloc(bufsize);
+       buf = alloc(bufsize);
        ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
        ret = recv_bin_buffer(at->fd, buf, bufsize);
        PARA_DEBUG_LOG("recv: %d\n", ret);
        if (ret == 0)
-               ret = -E_AUDIOC_EOF;
+               ret = -E_EOF;
        if (ret < 0)
                goto out;
        btr_add_output(buf, ret, at->btrn);
        if (ret < 0)
                goto out;
        btr_add_output(buf, ret, at->btrn);
@@ -211,8 +217,8 @@ static int audioc_i9e_line_handler(char *line)
                EMBRACE(.name = "audioc line handler"));
        at->task = task_register(&(struct task_info) {
                .name = "audioc",
                EMBRACE(.name = "audioc line handler"));
        at->task = task_register(&(struct task_info) {
                .name = "audioc",
-               .pre_select = audioc_pre_select,
-               .post_select = audioc_post_select,
+               .pre_monitor = audioc_pre_monitor,
+               .post_monitor = audioc_post_monitor,
                .context = at,
        }, &sched);
        i9e_attach_to_stdout(at->btrn);
                .context = at,
        }, &sched);
        i9e_attach_to_stdout(at->btrn);
@@ -250,9 +256,9 @@ __noreturn static void interactive_session(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
 
-       sched.default_timeout.tv_sec = 1;
+       sched.default_timeout = 1000;
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
                goto out;
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
                goto out;
@@ -364,7 +370,7 @@ int main(int argc, char *argv[])
        if (ret < 0)
                goto out;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
        if (ret < 0)
                goto out;
        bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
-       buf = para_malloc(bufsize);
+       buf = alloc(bufsize);
        do {
                size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
                if (ret <= 0)
        do {
                size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
                if (ret <= 0)
index 838f375fe6df2c2d96c4bfa2aa41d28e64c29f72..7c223995a9a29cec3c8e8ae5497c3be58655f63c 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -44,8 +44,6 @@ static struct lls_parse_result *lpr;
 #define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
 #define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
 #define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
 #define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
 #define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
 #define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
-       lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
 
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 /** define the array containing all supported audio formats */
 
 __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 /** define the array containing all supported audio formats */
@@ -123,7 +121,7 @@ enum vss_status_flags {
  * This is needed also in audiod_command.c (for the tasks command), so it can
  * not be made static.
  */
  * This is needed also in audiod_command.c (for the tasks command), so it can
  * not be made static.
  */
-struct sched sched = {.max_fileno = 0};
+struct sched sched = {.timeout = 0};
 
 /* The task for obtaining para_server's status (para_client stat). */
 struct status_task {
 
 /* The task for obtaining para_server's status (para_client stat). */
 struct status_task {
@@ -292,7 +290,7 @@ static int get_play_time_slot_num(void)
  *
  * \return A string that must be freed by the caller.
  */
  *
  * \return A string that must be freed by the caller.
  */
-char *get_time_string(void)
+__malloc char *get_time_string(void)
 {
        int ret, seconds = 0, length = stat_task->length_seconds;
        struct timeval *tmp, sum, sss, /* server stream start */
 {
        int ret, seconds = 0, length = stat_task->length_seconds;
        struct timeval *tmp, sum, sss, /* server stream start */
@@ -389,11 +387,11 @@ static void parse_config_or_die(void)
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        n = OPT_GIVEN(USER_ALLOW);
        if (n == 0)
                return;
        n = OPT_GIVEN(USER_ALLOW);
        if (n == 0)
                return;
-       uid_whitelist = para_malloc(n * sizeof(uid_t));
+       uid_whitelist = arr_alloc(n, sizeof(uid_t));
        for (i = 0; i < n; i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(USER_ALLOW));
                int32_t val;
        for (i = 0; i < n; i++) {
                const char *arg = lls_string_val(i, OPT_RESULT(USER_ALLOW));
                int32_t val;
@@ -564,7 +562,7 @@ static void open_filters(struct slot_info *s)
                return;
        PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]);
        assert(s->fns == NULL);
                return;
        PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]);
        assert(s->fns == NULL);
-       s->fns = para_calloc(nf * sizeof(struct filter_node));
+       s->fns = zalloc(nf * sizeof(struct filter_node));
        parent = s->receiver_node->btrn;
        for (i = 0; i < nf; i++) {
                char buf[20];
        parent = s->receiver_node->btrn;
        for (i = 0; i < nf; i++) {
                char buf[20];
@@ -584,8 +582,8 @@ static void open_filters(struct slot_info *s)
                sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
                fn->task = task_register(&(struct task_info) {
                        .name = buf,
                sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
                fn->task = task_register(&(struct task_info) {
                        .name = buf,
-                       .pre_select = f->pre_select,
-                       .post_select = f->post_select,
+                       .pre_monitor = f->pre_monitor,
+                       .post_monitor = f->post_monitor,
                        .context = fn,
                }, &sched);
                parent = fn->btrn;
                        .context = fn,
                }, &sched);
                parent = fn->btrn;
@@ -602,7 +600,7 @@ static void open_writers(struct slot_info *s)
        struct btr_node *parent = s->fns[a->num_filters - 1].btrn;
 
        assert(s->wns == NULL);
        struct btr_node *parent = s->fns[a->num_filters - 1].btrn;
 
        assert(s->wns == NULL);
-       s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
+       s->wns = zalloc(PARA_MAX(1U, a->num_writers)
                * sizeof(struct writer_node));
        for (i = 0; i < a->num_writers; i++) {
                wn = s->wns + i;
                * sizeof(struct writer_node));
        for (i = 0; i < a->num_writers; i++) {
                wn = s->wns + i;
@@ -629,7 +627,7 @@ static int open_receiver(int format)
        if (ret < 0)
                return ret;
        slot_num = ret;
        if (ret < 0)
                return ret;
        slot_num = ret;
-       rn = para_calloc(sizeof(*rn));
+       rn = zalloc(sizeof(*rn));
        rn->receiver = r;
        rn->lpr = a->receiver_lpr;
        rn->btrn = btr_new_node(&(struct btr_node_description)
        rn->receiver = r;
        rn->lpr = a->receiver_lpr;
        rn->btrn = btr_new_node(&(struct btr_node_description)
@@ -648,8 +646,8 @@ static int open_receiver(int format)
                audio_formats[format], name, slot_num);
        rn->task = task_register(&(struct task_info) {
                .name = name,
                audio_formats[format], name, slot_num);
        rn->task = task_register(&(struct task_info) {
                .name = name,
-               .pre_select = r->pre_select,
-               .post_select = r->post_select,
+               .pre_monitor = r->pre_monitor,
+               .post_monitor = r->post_monitor,
                .context = rn,
        }, &sched);
        return slot_num;
                .context = rn,
        }, &sched);
        return slot_num;
@@ -749,8 +747,8 @@ static void compute_time_diff(const struct timeval *status_time)
        if (count > 5) {
                int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
                if (tv_diff(&max_deviation, &tmp, NULL) < 0)
        if (count > 5) {
                int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
                if (tv_diff(&max_deviation, &tmp, NULL) < 0)
-                       PARA_WARNING_LOG("time diff jump: %lums\n",
-                               s * tv2ms(&tmp));
+                       PARA_WARNING_LOG("time diff jump: %c%lums\n",
+                               s < 0? '-' : '+', tv2ms(&tmp));
        }
        count++;
        sa_time_diff_sign = tv_convex_combination(
        }
        count++;
        sa_time_diff_sign = tv_convex_combination(
@@ -819,7 +817,7 @@ static int parse_stream_command(const char *txt, const char **cmd)
                return -E_MISSING_COLON;
        *cmd = p + 1;
        len = p - txt;
                return -E_MISSING_COLON;
        *cmd = p + 1;
        len = p - txt;
-       re = para_malloc(len + 1);
+       re = alloc(len + 1);
        strncpy(re, txt, len);
        re[len] = '\0';
        ret = get_matching_audio_format_nums(re);
        strncpy(re, txt, len);
        re[len] = '\0';
        ret = get_matching_audio_format_nums(re);
@@ -835,12 +833,9 @@ static int add_filter(int format, const char *cmdline)
        struct lls_parse_result *flpr;
 
        filter_num = filter_setup(cmdline, &cfg, &flpr);
        struct lls_parse_result *flpr;
 
        filter_num = filter_setup(cmdline, &cfg, &flpr);
-       a->filter_lpr = para_realloc(a->filter_lpr,
-               (nf + 1) * sizeof(flpr));
-       a->filter_conf = para_realloc(a->filter_conf,
-               (nf + 1) * sizeof(void *));
-       a->filter_nums = para_realloc(a->filter_nums,
-               (nf + 1) * sizeof(unsigned));
+       a->filter_lpr = arr_realloc(a->filter_lpr, nf + 1, sizeof(flpr));
+       a->filter_conf = arr_realloc(a->filter_conf, nf + 1, sizeof(void *));
+       a->filter_nums = arr_realloc(a->filter_nums, nf + 1, sizeof(unsigned));
 
        a->filter_nums[nf] = filter_num;
        a->filter_conf[nf] = cfg;
 
        a->filter_nums[nf] = filter_num;
        a->filter_conf[nf] = cfg;
@@ -886,8 +881,8 @@ static int parse_writer_args(void)
                if (a->num_writers > 0)
                        continue; /* already set up */
                a->num_writers = 1;
                if (a->num_writers > 0)
                        continue; /* already set up */
                a->num_writers = 1;
-               a->wids = para_malloc(sizeof(int));
-               a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+               a->wids = alloc(sizeof(int));
+               a->writer_lpr = alloc(sizeof(struct lls_parse_result *));
                a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
                PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
                        writer_name(a->wids[0]));
                a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
                PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
                        writer_name(a->wids[0]));
@@ -963,7 +958,7 @@ static int init_default_filters(void)
                 */
                if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
                        tmp = para_strdup("fecdec");
                 */
                if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
                        tmp = para_strdup("fecdec");
-                       add_filter(i, tmp);
+                       ret = add_filter(i, tmp);
                        free(tmp);
                        if (ret < 0)
                                goto out;
                        free(tmp);
                        if (ret < 0)
                                goto out;
@@ -1055,7 +1050,7 @@ static void init_local_socket(struct command_task *ct)
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
-static int signal_post_select(struct sched *s, void *context)
+static int signal_post_monitor(struct sched *s, void *context)
 {
        struct signal_task *st = context;
        int ret, signum;
 {
        struct signal_task *st = context;
        int ret, signum;
@@ -1063,25 +1058,25 @@ static int signal_post_select(struct sched *s, void *context)
        ret = task_get_notification(st->task);
        if (ret < 0)
                return ret;
        ret = task_get_notification(st->task);
        if (ret < 0)
                return ret;
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        switch (signum) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
        switch (signum) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
-               PARA_NOTICE_LOG("received signal %d\n", signum);
+               PARA_WARNING_LOG("terminating on signal %d\n", signum);
                task_notify_all(s, E_AUDIOD_SIGNAL);
                return -E_AUDIOD_SIGNAL;
        }
        return 0;
 }
 
                task_notify_all(s, E_AUDIOD_SIGNAL);
                return -E_AUDIOD_SIGNAL;
        }
        return 0;
 }
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        struct command_task *ct = context;
 {
        struct command_task *ct = context;
-       para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(ct->fd, s);
 }
 
 }
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        int ret;
        struct command_task *ct = context;
 {
        int ret;
        struct command_task *ct = context;
@@ -1092,9 +1087,9 @@ static int command_post_select(struct sched *s, void *context)
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
        ret = task_get_notification(ct->task);
        if (ret < 0)
                return ret;
-       ret = handle_connect(ct->fd, &s->rfds);
+       ret = dispatch_local_connection(ct->fd);
        if (ret < 0) {
        if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
                if (ret == -E_AUDIOD_TERM) {
                        task_notify_all(s, -ret);
                        return ret;
                if (ret == -E_AUDIOD_TERM) {
                        task_notify_all(s, -ret);
                        return ret;
@@ -1132,8 +1127,8 @@ static void init_command_task(struct command_task *ct)
 
        ct->task = task_register(&(struct task_info) {
                .name = "command",
 
        ct->task = task_register(&(struct task_info) {
                .name = "command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = ct,
        }, &sched);
 }
                .context = ct,
        }, &sched);
 }
@@ -1254,7 +1249,7 @@ static void start_stop_decoders(void)
        audiod_status_dump(true);
 }
 
        audiod_status_dump(true);
 }
 
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
        int i, ret, cafn = stat_task->current_audio_format_num;
 {
        struct status_task *st = context;
        int i, ret, cafn = stat_task->current_audio_format_num;
@@ -1286,7 +1281,7 @@ min_delay:
 }
 
 /* restart the client task if necessary */
 }
 
 /* restart the client task if necessary */
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
        int ret;
 {
        struct status_task *st = context;
        int ret;
@@ -1377,8 +1372,8 @@ static void init_status_task(struct status_task *st)
 
        stat_task->task = task_register(&(struct task_info) {
                .name = "stat",
 
        stat_task->task = task_register(&(struct task_info) {
                .name = "stat",
-               .pre_select = status_pre_select,
-               .post_select = status_post_select,
+               .pre_monitor = status_pre_monitor,
+               .post_monitor = status_post_monitor,
                .context = stat_task,
        }, &sched);
 }
                .context = stat_task,
        }, &sched);
 }
@@ -1462,7 +1457,7 @@ int main(int argc, char *argv[])
        ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
        if (ret < 0)
                goto out;
        ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
        if (ret < 0)
                goto out;
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("audiod", OPT_GIVEN(VERSION));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("audiod", OPT_GIVEN(VERSION));
@@ -1505,13 +1500,12 @@ int main(int argc, char *argv[])
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
 
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
        }, &sched);
 
                .context = signal_task,
        }, &sched);
 
-       sched.default_timeout.tv_sec = 2;
-       sched.default_timeout.tv_usec = 999 * 1000;
+       sched.default_timeout = 2999;
        ret = schedule(&sched);
        audiod_cleanup();
        sched_shutdown(&sched);
        ret = schedule(&sched);
        audiod_cleanup();
        sched_shutdown(&sched);
index b40fdd6743faa4650888c2bdf69756d6c12e40d8..39beda1bd4223d145fb327f07be4c5e5d4845d94 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -15,11 +15,11 @@ extern int audiod_status;
 struct btr_node *audiod_get_btr_root(void);
 __malloc char *audiod_get_decoder_flags(void);
 void clear_and_dump_items(void);
 struct btr_node *audiod_get_btr_root(void);
 __malloc char *audiod_get_decoder_flags(void);
 void clear_and_dump_items(void);
-char *get_time_string(void);
+__malloc char *get_time_string(void);
 bool uid_is_whitelisted(uid_t uid);
 
 /* defined in audiod_command.c */
 void audiod_status_dump(bool force);
 void close_stat_clients(void);
 bool uid_is_whitelisted(uid_t uid);
 
 /* defined in audiod_command.c */
 void audiod_status_dump(bool force);
 void close_stat_clients(void);
-int handle_connect(int accept_fd, fd_set *rfds);
+int dispatch_local_connection(int accept_fd);
 void stat_client_write_item(int item_num);
 void stat_client_write_item(int item_num);
index bb54dfab87f7965a18f6cccbe6bc4077c147fa29..5f0b35a5dc0f3e4f02d134faa2ef828c6b357763 100644 (file)
@@ -81,27 +81,12 @@ static int num_clients;
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEMS};
 
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEMS};
 
-static void dump_stat_client_list(void)
-{
-       struct stat_client *sc;
-
-       list_for_each_entry(sc, &client_list, node)
-               PARA_INFO_LOG("stat client on fd %d\n", sc->fd);
-}
-/**
- * Add a status client to the list.
- *
- * \param fd The file descriptor of the client.
- * \param mask Bitfield of status items for this client.
- * \param parser_friendly Enable parser-friendly output mode.
- *
- * Only those status items having the bit set in \a mask will be
- * sent to the client.
+/*
+ * Add a status client to the global client list and increment num_clients.
  *
  *
- * \return Positive value on success, or -E_TOO_MANY_CLIENTS if
- * the number of connected clients exceeds #MAX_STAT_CLIENTS.
+ * The mask parameter specifies which status items are sent to the client.
  */
  */
-static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
+static int stat_client_add(int fd, uint64_t mask, bool parser_friendly)
 {
        struct stat_client *new_client;
        int ret;
 {
        struct stat_client *new_client;
        int ret;
@@ -114,14 +99,13 @@ static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
        ret = dup(fd);
        if (ret < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
        ret = dup(fd);
        if (ret < 0)
                return -ERRNO_TO_PARA_ERROR(errno);
-       new_client = para_calloc(sizeof(*new_client));
+       new_client = zalloc(sizeof(*new_client));
        new_client->fd = ret;
        PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
        new_client->item_mask = mask;
        if (parser_friendly)
                new_client->flags = SCF_PARSER_FRIENDLY;
        para_list_add(&new_client->node, &client_list);
        new_client->fd = ret;
        PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
        new_client->item_mask = mask;
        if (parser_friendly)
                new_client->flags = SCF_PARSER_FRIENDLY;
        para_list_add(&new_client->node, &client_list);
-       dump_stat_client_list();
        num_clients++;
        return 1;
 }
        num_clients++;
        return 1;
 }
@@ -180,20 +164,12 @@ void stat_client_write_item(int item_num)
                        continue;
                /* write error or short write */
                close_stat_client(sc);
                        continue;
                /* write error or short write */
                close_stat_client(sc);
-               dump_stat_client_list();
        }
        free(pb.buf);
        free(pfpb.buf);
 }
 
        }
        free(pb.buf);
        free(pfpb.buf);
 }
 
-/**
- * Check if string is a known status item.
- *
- * \param item Buffer containing the text to check.
- *
- * \return If \a item is a valid status item, the number of that status item is
- * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM.
- */
+/* Check if the given string is a known status item and return its index. */
 static int stat_item_valid(const char *item)
 {
        int i;
 static int stat_item_valid(const char *item)
 {
        int i;
@@ -240,6 +216,42 @@ static int com_help(int fd, struct lls_parse_result *lpr)
 }
 EXPORT_AUDIOD_CMD_HANDLER(help)
 
 }
 EXPORT_AUDIOD_CMD_HANDLER(help)
 
+static int com_ll(int fd, struct lls_parse_result *lpr)
+{
+       unsigned ll;
+       char *errctx;
+       const char *sev[] = {SEVERITIES};
+       const char *arg;
+       int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+       if (ret < 0) {
+               char *tmp = make_message("%s\n", errctx);
+               free(errctx);
+               client_write(fd, tmp);
+               free(tmp);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) {
+               char *msg;
+               ll = daemon_get_loglevel();
+               msg = make_message("%s\n", sev[ll]);
+               ret = client_write(fd, msg);
+               free(msg);
+               return ret;
+       }
+       arg = lls_input(0, lpr);
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+               if (!strcmp(arg, sev[ll]))
+                       break;
+       }
+       if (ll >= NUM_LOGLEVELS)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+       daemon_set_loglevel(ll);
+       return 1;
+}
+EXPORT_AUDIOD_CMD_HANDLER(ll)
+
 static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
        int ret;
 static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
        int ret;
@@ -255,7 +267,8 @@ EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
 static int com_stat(int fd, struct lls_parse_result *lpr)
 {
 
 static int com_stat(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret, parser_friendly = 0;
+       int i, ret;
+       bool parser_friendly = false;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
@@ -267,7 +280,7 @@ static int com_stat(int fd, struct lls_parse_result *lpr)
                return ret;
        r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
        if (lls_opt_given(r) > 0) {
                return ret;
        r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
        if (lls_opt_given(r) > 0) {
-               parser_friendly = 1;
+               parser_friendly = true;
                b.flags = PBF_SIZE_PREFIX;
        }
        num_inputs = lls_num_inputs(lpr);
                b.flags = PBF_SIZE_PREFIX;
        }
        num_inputs = lls_num_inputs(lpr);
@@ -360,10 +373,9 @@ EXPORT_AUDIOD_CMD_HANDLER(version)
  * Handle arriving connections on the local socket.
  *
  * \param accept_fd The fd to accept connections on.
  * Handle arriving connections on the local socket.
  *
  * \param accept_fd The fd to accept connections on.
- * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
  *
  *
- * This is called in each iteration of the select loop. If there is an incoming
- * connection on \a accept_fd, this function reads the command sent by the peer,
+ * This is called in each iteration of the main loop of the scheduler. If there
+ * is an incoming connection, the function reads the command sent by the peer,
  * checks the connecting user's permissions by using unix socket credentials
  * (if supported by the OS) and calls the corresponding command handler if
  * permissions are OK.
  * checks the connecting user's permissions by using unix socket credentials
  * (if supported by the OS) and calls the corresponding command handler if
  * permissions are OK.
@@ -372,8 +384,8 @@ EXPORT_AUDIOD_CMD_HANDLER(version)
  * connection to accept.
  *
  * \sa \ref para_accept(), \ref recv_cred_buffer().
  * connection to accept.
  *
  * \sa \ref para_accept(), \ref recv_cred_buffer().
- * */
-int handle_connect(int accept_fd, fd_set *rfds)
+ */
+int dispatch_local_connection(int accept_fd)
 {
        int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
 {
        int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
@@ -384,7 +396,7 @@ int handle_connect(int accept_fd, fd_set *rfds)
        char *errctx = NULL;
        const struct audiod_command_info *aci;
 
        char *errctx = NULL;
        const struct audiod_command_info *aci;
 
-       ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
+       ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
                return ret;
        ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
        if (ret <= 0)
                return ret;
        ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
index caf1401d8bf02e2d299bdcdf58ae902cfb260092..cbfea7fdfb2ea5e2b8ad84c8faba815b67b8a966 100755 (executable)
@@ -1,28 +1,8 @@
 #!/bin/sh
 #!/bin/sh
-# check if we have multiple processors/cores
-n=$(nproc 2>/dev/null)
-if [ -z "$n" ]; then
-       n=$(grep ^processor /proc/cpuinfo 2>/dev/null | wc -l)
-       [ $n -eq 0 ] && n=1
-fi
-# If we are compiling with distcc, try to guess a reasonable number
-# based on (a) the number of cores on this machine and (b) the number
-# of words in the DISTCC_HOSTS variable.
-d="$(echo $DISTCC_HOSTS | wc -w)"
-n=$(($n + 2 * $n * $d))
-echo preparing, parallel=$n...
-if test -f Makefile; then
-       make maintainer-clean > /dev/null 2>&1
-fi
 autom4te \
        --language=autoconf \
        --output=configure \
        --no-cache \
        --warnings=all \
 autom4te \
        --language=autoconf \
        --output=configure \
        --no-cache \
        --warnings=all \
-       configure.ac
+       configure.ac &&
 autoheader
 autoheader
-echo configuring...
-./configure $@ > /dev/null
-echo compiling...
-make clean > /dev/null 2>&1
-make -j $n > /dev/null && make check
index 122465f8feea79cf06d09bfd759d2446ee0f3f12..ac6f65aa85d469df3ba4a70a058ed85b31eb6d11 100644 (file)
--- a/base64.c
+++ b/base64.c
@@ -79,7 +79,7 @@ int base64_decode(char const *src, size_t encoded_size, char **result,
 
        if (encoded_size == (size_t)-1)
                encoded_size = strlen(src);
 
        if (encoded_size == (size_t)-1)
                encoded_size = strlen(src);
-       target = para_malloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
+       target = alloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
 
        for (
                i = 0, j = 0, state = 0;
 
        for (
                i = 0, j = 0, state = 0;
index dfc1e55e67aced611014fc90b86128e8bd61a457..a1c7c943138d41993807109be09969a58e914a0b 100644 (file)
@@ -46,8 +46,8 @@ static void alloc_table(struct vlc *vlc, int size)
        vlc->table_size += size;
        if (vlc->table_size > vlc->table_allocated) {
                vlc->table_allocated += (1 << vlc->bits);
        vlc->table_size += size;
        if (vlc->table_size > vlc->table_allocated) {
                vlc->table_allocated += (1 << vlc->bits);
-               vlc->table = para_realloc(vlc->table,
-                       sizeof(int16_t) * 2 * vlc->table_allocated);
+               vlc->table = arr_realloc(vlc->table, vlc->table_allocated,
+                       sizeof(int16_t) * 2);
        }
 }
 
        }
 }
 
diff --git a/blob.c b/blob.c
index 4ecbc45bb15f42c15342e7900f4de94522ef45bb..1802de5d31f89bf6fe951c5977cbef4967f9c11e 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -100,7 +100,7 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        }
        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
        }
        ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot list %s\n", name);
+               afs_error(aca, "cannot list %s\n", name);
                return ret;
        }
        id = read_u32(obj.data);
                return ret;
        }
        id = read_u32(obj.data);
@@ -210,7 +210,7 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        int ret = osl(osl_del_row(table, row));
 
        if (ret < 0) {
        int ret = osl(osl_del_row(table, row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot remove %s\n", name);
+               afs_error(aca, "cannot remove %s\n", name);
                return ret;
        }
        return 1;
                return ret;
        }
        return 1;
@@ -338,7 +338,7 @@ static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
        ret = afs_event(BLOB_ADD, NULL, table);
 out:
        if (ret < 0)
        ret = afs_event(BLOB_ADD, NULL, table);
 out:
        if (ret < 0)
-               para_printf(&aca->pbout, "cannot add %s\n", name);
+               afs_error(aca, "cannot add %s\n", name);
        else
                para_printf(&aca->pbout, "added %s as id %u\n", name, id);
        return ret;
        else
                para_printf(&aca->pbout, "added %s as id %u\n", name, id);
        return ret;
@@ -403,7 +403,7 @@ static int stdin_command(struct command_context *cc,
        if (ret < 0)
                return ret;
        query.size = len + 1 + stdin_obj.size;
        if (ret < 0)
                return ret;
        query.size = len + 1 + stdin_obj.size;
-       query.data = para_malloc(query.size);
+       query.data = alloc(query.size);
        memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
                memcpy((char *)query.data + len + 1, stdin_obj.data,
        memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
                memcpy((char *)query.data + len + 1, stdin_obj.data,
@@ -446,15 +446,14 @@ static int com_mvblob_callback(const struct lls_command * const cmd,
        ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
        ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot find source blob %s\n", src);
+               afs_error(aca, "cannot find source blob %s\n", src);
                goto out;
        }
        obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
                goto out;
        }
        obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
-               para_printf(&aca->pbout, "cannot rename blob %s to %s\n",
-                       src, dest);
+               afs_error(aca, "cannot rename blob %s to %s\n", src, dest);
                goto out;
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
                goto out;
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
@@ -520,11 +519,11 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
-static int blob_get_def_by_name(struct osl_table *table, char *name,
+static int blob_get_def_by_name(struct osl_table *table, const char *name,
                struct osl_object *def)
 {
        struct osl_row *row;
                struct osl_object *def)
 {
        struct osl_row *row;
-       struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+       struct osl_object obj = {.data = (void *)name, .size = strlen(name) + 1};
        int ret;
 
        def->data = NULL;
        int ret;
 
        def->data = NULL;
@@ -538,7 +537,7 @@ static int blob_get_def_by_name(struct osl_table *table, char *name,
 
 /** Define the \p get_def_by_id function for this blob type. */
 #define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
 
 /** Define the \p get_def_by_id function for this blob type. */
 #define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
-       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
+       int cmd_prefix ## _get_def_by_name(const char *name, struct osl_object *def) \
        { \
                return blob_get_def_by_name(table_name ## _table, name, def); \
        }
        { \
                return blob_get_def_by_name(table_name ## _table, name, def); \
        }
@@ -625,25 +624,21 @@ static int blob_open(struct osl_table **table,
                        &table_name ## _table_desc, dir); \
        }
 
                        &table_name ## _table_desc, dir); \
        }
 
-
-/** Define the \p init function for this blob type. */
-#define DEFINE_BLOB_INIT(table_name) \
-       void table_name ## _init(struct afs_table *t) \
-       { \
-               t->open = table_name ## _open; \
-               t->close = table_name ## _close; \
-               t->create = table_name ## _create;\
-               t->event_handler = table_name ##_event_handler; \
-               table_name ## _table = NULL; \
-       }
-
+/** Blob tables map integers to blobs. */
+#define DEFINE_BLOB_AFS_TABLE_OPS(table_name) \
+       const struct afs_table_operations table_name ## _ops = { \
+               .open = table_name ## _open, \
+               .close = table_name ## _close, \
+               .create = table_name ## _create, \
+               .event_handler = table_name ##_event_handler, \
+       };
 
 /** Define all functions for this blob type. */
 #define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
 
 /** Define all functions for this blob type. */
 #define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
-       DEFINE_BLOB_INIT(table_name) \
+       DEFINE_BLOB_AFS_TABLE_OPS(table_name) \
        DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
        DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
index f0d2002d13c28b31c17f7130a4f3c34fc858bd58..35353f56c9f69cb99be6bb6a319f680876d4a9cc 100644 (file)
@@ -72,8 +72,8 @@ struct btr_pool *btr_pool_new(const char *name, size_t area_size)
        struct btr_pool *btrp;
 
        PARA_INFO_LOG("%s, %zu bytes\n", name, area_size);
        struct btr_pool *btrp;
 
        PARA_INFO_LOG("%s, %zu bytes\n", name, area_size);
-       btrp = para_malloc(sizeof(*btrp));
-       btrp->area_start = para_malloc(area_size);
+       btrp = alloc(sizeof(*btrp));
+       btrp->area_start = alloc(area_size);
        btrp->area_end = btrp->area_start + area_size;
        btrp->rhead = btrp->area_start;
        btrp->whead = btrp->area_start;
        btrp->area_end = btrp->area_start + area_size;
        btrp->rhead = btrp->area_start;
        btrp->whead = btrp->area_start;
@@ -262,7 +262,7 @@ static void btr_pool_deallocate(struct btr_pool *btrp, size_t size)
  */
 struct btr_node *btr_new_node(struct btr_node_description *bnd)
 {
  */
 struct btr_node *btr_new_node(struct btr_node_description *bnd)
 {
-       struct btr_node *btrn = para_malloc(sizeof(*btrn));
+       struct btr_node *btrn = alloc(sizeof(*btrn));
 
        btrn->name = para_strdup(bnd->name);
        btrn->parent = bnd->parent;
 
        btrn->name = para_strdup(bnd->name);
        btrn->parent = bnd->parent;
@@ -307,7 +307,7 @@ out:
  */
 static struct btr_buffer *new_btrb(char *buf, size_t size)
 {
  */
 static struct btr_buffer *new_btrb(char *buf, size_t size)
 {
-       struct btr_buffer *btrb = para_calloc(sizeof(*btrb));
+       struct btr_buffer *btrb = zalloc(sizeof(*btrb));
 
        btrb->buf = buf;
        btrb->size = size;
 
        btrb->buf = buf;
        btrb->size = size;
@@ -354,7 +354,7 @@ static void add_btrb_to_children(struct btr_buffer *btrb,
        if (btrn->start.tv_sec == 0)
                btrn->start = *now;
        FOR_EACH_CHILD(ch, btrn) {
        if (btrn->start.tv_sec == 0)
                btrn->start = *now;
        FOR_EACH_CHILD(ch, btrn) {
-               struct btr_buffer_reference *br = para_calloc(sizeof(*br));
+               struct btr_buffer_reference *br = zalloc(sizeof(*br));
                br->btrb = btrb;
                br->consumed = consumed;
                list_add_tail(&br->node, &ch->input_queue);
                br->btrb = btrb;
                br->consumed = consumed;
                list_add_tail(&br->node, &ch->input_queue);
@@ -570,7 +570,7 @@ bool btr_no_parent(struct btr_node *btrn)
  * buffer.
  *
  * Since the buffer tree may change at any time, this function should be called
  * buffer.
  *
  * Since the buffer tree may change at any time, this function should be called
- * during each post_select call.
+ * during each post_monitor call.
  *
  * \return True if \a btrn has no siblings.
  */
  *
  * \return True if \a btrn has no siblings.
  */
@@ -1007,12 +1007,12 @@ next:
        if (!wbr) {
                /* Make a new wrap buffer combining buf1 and buf2. */
                sz = sz1 + sz2;
        if (!wbr) {
                /* Make a new wrap buffer combining buf1 and buf2. */
                sz = sz1 + sz2;
-               buf = para_malloc(sz);
+               buf = alloc(sz);
                PARA_DEBUG_LOG("merging input buffers: (%p:%zu, %p:%zu) -> %p:%zu\n",
                        buf1, sz1, buf2, sz2, buf, sz);
                memcpy(buf, buf1, sz1);
                memcpy(buf + sz1, buf2, sz2);
                PARA_DEBUG_LOG("merging input buffers: (%p:%zu, %p:%zu) -> %p:%zu\n",
                        buf1, sz1, buf2, sz2, buf, sz);
                memcpy(buf, buf1, sz1);
                memcpy(buf + sz1, buf2, sz2);
-               br = para_calloc(sizeof(*br));
+               br = zalloc(sizeof(*br));
                br->btrb = new_btrb(buf, sz);
                br->btrb->refcount = 1;
                br->consumed = 0;
                br->btrb = new_btrb(buf, sz);
                br->btrb->refcount = 1;
                br->consumed = 0;
@@ -1067,13 +1067,13 @@ static int merge_input(struct btr_node *btrn)
        assert(i == 2);
        /* make a new btrb that combines the two buffers and a br to it. */
        sz = szs[0] + szs[1];
        assert(i == 2);
        /* make a new btrb that combines the two buffers and a br to it. */
        sz = szs[0] + szs[1];
-       buf = para_malloc(sz);
+       buf = alloc(sz);
        PARA_DEBUG_LOG("%s: memory merging input buffers: (%zu, %zu) -> %zu\n",
                btrn->name, szs[0], szs[1], sz);
        memcpy(buf, bufs[0], szs[0]);
        memcpy(buf + szs[0], bufs[1], szs[1]);
 
        PARA_DEBUG_LOG("%s: memory merging input buffers: (%zu, %zu) -> %zu\n",
                btrn->name, szs[0], szs[1], sz);
        memcpy(buf, bufs[0], szs[0]);
        memcpy(buf + szs[0], bufs[1], szs[1]);
 
-       br = para_calloc(sizeof(*br));
+       br = zalloc(sizeof(*br));
        br->btrb = new_btrb(buf, sz);
        br->btrb->refcount = 1;
 
        br->btrb = new_btrb(buf, sz);
        br->btrb->refcount = 1;
 
@@ -1181,7 +1181,7 @@ struct btr_node *btr_search_node(const char *name, struct btr_node *root)
  * \param type The supposed type of \a btrn.
  *
  * Most users of the buffer tree subsystem call this function from both
  * \param type The supposed type of \a btrn.
  *
  * Most users of the buffer tree subsystem call this function from both
- * their pre_select and the post_select methods.
+ * their ->pre_monitor() and ->post_monitor() methods.
  *
  * \return Negative if an error condition was detected, zero if there
  * is nothing to do and positive otherwise.
  *
  * \return Negative if an error condition was detected, zero if there
  * is nothing to do and positive otherwise.
@@ -1208,7 +1208,7 @@ int btr_node_status(struct btr_node *btrn, size_t min_iqs,
        if (type != BTR_NT_LEAF && btr_no_children(btrn))
                return -E_BTR_NO_CHILD;
        if (type != BTR_NT_ROOT && btr_eof(btrn))
        if (type != BTR_NT_LEAF && btr_no_children(btrn))
                return -E_BTR_NO_CHILD;
        if (type != BTR_NT_ROOT && btr_eof(btrn))
-               return -E_BTR_EOF;
+               return -E_EOF;
 
        if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING)
                return 0;
 
        if (btr_get_output_queue_size(btrn) > BTRN_MAX_PENDING)
                return 0;
index 89ebdacc0805e9575acea92d09ed3b2dd81ebac3..3789f30aa5a20ef5433df83362a6be02563c8d17 100644 (file)
@@ -39,15 +39,15 @@ struct check_wav_context {
 };
 
 /**
 };
 
 /**
- * Set select timeout according to the given context.
+ * Request a minimal timeout if not idle.
  *
  *
- * \param s Contains the timeval that should be set.
- * \param cwc Contains a pointer to the buffer tree node.
+ * \param s The scheduler instance.
+ * \param cwc The buffer tree node is derived from this.
  *
  *
- * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
- * idle.
+ * If no data is available and the buffer tree node is not in error state, the
+ * function does nothing.
  */
  */
-void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
+void check_wav_pre_monitor(struct sched *s, struct check_wav_context *cwc)
 {
        int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
        if (ret != 0)
 {
        int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
        if (ret != 0)
@@ -121,7 +121,7 @@ out:
  *
  * \return Standard.
  */
  *
  * \return Standard.
  */
-int check_wav_post_select(struct check_wav_context *cwc)
+int check_wav_post_monitor(struct check_wav_context *cwc)
 {
        struct btr_node *btrn = cwc->btrn;
        unsigned char *a;
 {
        struct btr_node *btrn = cwc->btrn;
        unsigned char *a;
@@ -198,8 +198,8 @@ out:
  * children of this node can figure out channel count, sample rate, etc.
  *
  * \return The (opaque) handle of the newly created check_wav instance. It is
  * children of this node can figure out channel count, sample rate, etc.
  *
  * \return The (opaque) handle of the newly created check_wav instance. It is
- * supposed to be passed to \ref check_wav_pre_select() and \ref
- * check_wav_post_select().
+ * supposed to be passed to \ref check_wav_pre_monitor() and \ref
+ * check_wav_post_monitor().
  *
  * \sa \ref btr_new_node.
  */
  *
  * \sa \ref btr_new_node.
  */
@@ -207,7 +207,7 @@ struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn)
 {
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn)
 {
-       struct check_wav_context *cwc = para_calloc(sizeof(*cwc));
+       struct check_wav_context *cwc = zalloc(sizeof(*cwc));
 
        cwc->state = CWS_NEED_HEADER;
        cwc->min_iqs = WAV_HEADER_LEN;
 
        cwc->state = CWS_NEED_HEADER;
        cwc->min_iqs = WAV_HEADER_LEN;
@@ -225,7 +225,7 @@ struct check_wav_context *check_wav_init(struct btr_node *parent,
  *
  * \param cwc Determines the instance to shut down.
  *
  *
  * \param cwc Determines the instance to shut down.
  *
- * This function may only be called after check_wav_post_select() has returned
+ * This function may only be called after check_wav_post_monitor() has returned
  * negative.
  */
 void check_wav_shutdown(struct check_wav_context *cwc)
  * negative.
  */
 void check_wav_shutdown(struct check_wav_context *cwc)
index 79b11962baea9b3fe743f08ad68bca6a70e552bc..e6188c5243db16d82c524fc47f4066c8f9aec9df 100644 (file)
@@ -42,6 +42,6 @@ struct wav_params {
 struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn);
 struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
                struct btr_node **cw_btrn);
-void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc);
-int check_wav_post_select(struct check_wav_context *cwc);
+void check_wav_pre_monitor(struct sched *s, struct check_wav_context *cwc);
+int check_wav_post_monitor(struct check_wav_context *cwc);
 void check_wav_shutdown(struct check_wav_context *cwc);
 void check_wav_shutdown(struct check_wav_context *cwc);
index cf74cc336de25caa593b198bcfd79a3166996381..1f11359fcb16e4e55073a5fd36d5d5cff7f5504c 100644 (file)
@@ -52,7 +52,7 @@ int cq_enqueue(struct chunk_queue *cq, const char *buf, size_t num_bytes)
 
        if (cq->num_pending + num_bytes > cq->max_pending)
                return -E_QUEUE;
 
        if (cq->num_pending + num_bytes > cq->max_pending)
                return -E_QUEUE;
-       qc = para_malloc(sizeof(struct queued_chunk));
+       qc = alloc(sizeof(struct queued_chunk));
        cq->num_pending += num_bytes;
        qc->buf = buf;
        qc->num_bytes = num_bytes;
        cq->num_pending += num_bytes;
        qc->buf = buf;
        qc->num_bytes = num_bytes;
@@ -130,7 +130,7 @@ int cq_get(struct queued_chunk *qc, const char **buf, size_t *num_bytes)
  */
 struct chunk_queue *cq_new(size_t max_pending)
 {
  */
 struct chunk_queue *cq_new(size_t max_pending)
 {
-       struct chunk_queue *cq = para_malloc(sizeof(*cq));
+       struct chunk_queue *cq = alloc(sizeof(*cq));
        init_list_head(&cq->q);
        cq->max_pending = max_pending;
        cq->num_pending = 0;
        init_list_head(&cq->q);
        cq->max_pending = max_pending;
        cq->num_pending = 0;
index 8caf44839919fb33c1368715c528f507ebc7a5c9..84b7580cf9554c433651da1f3eed28a6f1f39a74 100644 (file)
--- a/client.c
+++ b/client.c
@@ -42,7 +42,7 @@ struct exec_task {
        size_t result_size;
 };
 
        size_t result_size;
 };
 
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
 {
        struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
 {
        struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
@@ -51,7 +51,7 @@ static void exec_pre_select(struct sched *s, void *context)
                sched_min_delay(s);
 }
 
                sched_min_delay(s);
 }
 
-static int exec_post_select(__a_unused struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
 {
        struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
@@ -123,7 +123,7 @@ fail:
 static int execute_client_command(const char *cmd, char **result)
 {
        int ret;
 static int execute_client_command(const char *cmd, char **result)
 {
        int ret;
-       struct sched command_sched = {.default_timeout = {.tv_sec = 1}};
+       struct sched command_sched = {.default_timeout = 1000};
        struct exec_task exec_task = {
                .result_buf = para_strdup(""),
                .result_size = 1,
        struct exec_task exec_task = {
                .result_buf = para_strdup(""),
                .result_size = 1,
@@ -138,8 +138,8 @@ static int execute_client_command(const char *cmd, char **result)
                EMBRACE(.name = "exec_collect"));
        exec_task.task = task_register(&(struct task_info) {
                .name = "client exec",
                EMBRACE(.name = "exec_collect"));
        exec_task.task = task_register(&(struct task_info) {
                .name = "client exec",
-               .pre_select = exec_pre_select,
-               .post_select = exec_post_select,
+               .pre_monitor = exec_pre_monitor,
+               .post_monitor = exec_post_monitor,
                .context = &exec_task,
        }, &command_sched);
        ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
                .context = &exec_task,
        }, &command_sched);
        ret = client_connect(ct, &command_sched, NULL, exec_task.btrn);
@@ -246,6 +246,12 @@ I9E_DUMMY_COMPLETER(init);
 
 static struct i9e_completer completers[];
 
 
 static struct i9e_completer completers[];
 
+static void ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       i9e_ll_completer(ci, cr);
+}
+
 static void help_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
 static void help_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
@@ -341,7 +347,7 @@ static void setatt_completer(struct i9e_completion_info *ci,
        if (ret < 0)
                goto out;
        num_atts = ret;
        if (ret < 0)
                goto out;
        num_atts = ret;
-       sl = para_realloc(sl, (2 * num_atts + 1) * sizeof(char *));
+       sl = arr_realloc(sl, 2 * num_atts + 1, sizeof(char *));
        for (i = 0; i < num_atts; i++) {
                char *orig = sl[i];
                sl[i] = make_message("%s+", orig);
        for (i = 0; i < num_atts; i++) {
                char *orig = sl[i];
                sl[i] = make_message("%s+", orig);
@@ -435,7 +441,7 @@ static void select_completer(struct i9e_completion_info *ci,
                goto free_moods;
        num_pl = ret;
        n = num_moods + num_pl;
                goto free_moods;
        num_pl = ret;
        n = num_moods + num_pl;
-       mops = para_malloc((n + 1) * sizeof(char *));
+       mops = arr_alloc(n + 1, sizeof(char *));
        for (i = 0; i < num_moods; i++)
                mops[i] = make_message("m/%s", moods[i]);
        for (i = 0; i < num_pl; i++)
        for (i = 0; i < num_moods; i++)
                mops[i] = make_message("m/%s", moods[i]);
        for (i = 0; i < num_pl; i++)
@@ -532,7 +538,7 @@ __noreturn static void interactive_session(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
 
        ret = i9e_open(&ici, &sched);
        if (ret < 0)
@@ -578,7 +584,7 @@ struct supervisor_task {
        struct task *task;
 };
 
        struct task *task;
 };
 
-static int supervisor_post_select(struct sched *s, void *context)
+static int supervisor_post_monitor(struct sched *s, void *context)
 {
        struct supervisor_task *svt = context;
        int ret = task_status(ct->task);
 {
        struct supervisor_task *svt = context;
        int ret = task_status(ct->task);
@@ -624,7 +630,7 @@ int main(int argc, char *argv[])
        int ret;
 
        crypt_init();
        int ret;
 
        crypt_init();
-       sched.default_timeout.tv_sec = 1;
+       sched.default_timeout = 1000;
 
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
        if (ret < 0)
 
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
        if (ret < 0)
@@ -648,7 +654,7 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
        supervisor_task.task = task_register(&(struct task_info) {
                .name = "supervisor",
                EMBRACE(.name = "stdout", .parent = ct->btrn[0]));
        supervisor_task.task = task_register(&(struct task_info) {
                .name = "supervisor",
-               .post_select = supervisor_post_select,
+               .post_monitor = supervisor_post_monitor,
                .context = &supervisor_task,
        }, &sched);
 
                .context = &supervisor_task,
        }, &sched);
 
@@ -660,8 +666,6 @@ int main(int argc, char *argv[])
                        /* these are not errors */
                        case -E_SERVER_CMD_SUCCESS:
                        case -E_EOF:
                        /* these are not errors */
                        case -E_SERVER_CMD_SUCCESS:
                        case -E_EOF:
-                       case -E_SERVER_EOF:
-                       case -E_BTR_EOF:
                                ret = 0;
                                break;
                        default: ret = -E_SERVER_CMD_FAILURE;
                                ret = 0;
                                break;
                        default: ret = -E_SERVER_CMD_FAILURE;
index 94a6e86544e63824664b114cc400161d4e223684..fe8234f98fe90f6ce74ea5e22699fbfc0059a042 100644 (file)
@@ -57,7 +57,7 @@ void client_close(struct client_task *ct)
  * The context pointer is assumed to refer to a client task structure that was
  * initialized earlier by client_open().
  */
  * The context pointer is assumed to refer to a client task structure that was
  * initialized earlier by client_open().
  */
-static void client_pre_select(struct sched *s, void *context)
+static void client_pre_monitor(struct sched *s, void *context)
 {
        int ret;
        struct client_task *ct = context;
 {
        int ret;
        struct client_task *ct = context;
@@ -68,13 +68,13 @@ static void client_pre_select(struct sched *s, void *context)
        case CL_CONNECTED:
        case CL_SENT_AUTH:
        case CL_SENT_CH_RESPONSE:
        case CL_CONNECTED:
        case CL_SENT_AUTH:
        case CL_SENT_CH_RESPONSE:
-               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(ct->scc.fd, s);
                return;
 
        case CL_RECEIVED_WELCOME:
        case CL_RECEIVED_PROCEED:
        case CL_RECEIVED_CHALLENGE:
                return;
 
        case CL_RECEIVED_WELCOME:
        case CL_RECEIVED_PROCEED:
        case CL_RECEIVED_CHALLENGE:
-               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(ct->scc.fd, s);
                return;
 
        case CL_SENDING:
                return;
 
        case CL_SENDING:
@@ -83,7 +83,7 @@ static void client_pre_select(struct sched *s, void *context)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
-                               para_fd_set(ct->scc.fd, &s->wfds, &s->max_fileno);
+                               sched_monitor_writefd(ct->scc.fd, s);
                }
                __attribute__ ((fallthrough));
        case CL_EXECUTING:
                }
                __attribute__ ((fallthrough));
        case CL_EXECUTING:
@@ -92,7 +92,7 @@ static void client_pre_select(struct sched *s, void *context)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
                        if (ret < 0)
                                sched_min_delay(s);
                        else if (ret > 0)
-                               para_fd_set(ct->scc.fd, &s->rfds, &s->max_fileno);
+                               sched_monitor_readfd(ct->scc.fd, s);
                }
                return;
        }
                }
                return;
        }
@@ -125,8 +125,7 @@ static int send_sb(struct client_task *ct, int channel, void *buf, size_t numbyt
        return 0;
 }
 
        return 0;
 }
 
-static int recv_sb(struct client_task *ct, fd_set *rfds,
-               struct sb_buffer *result)
+static int recv_sb(struct client_task *ct, struct sb_buffer *result)
 {
        int ret;
        size_t n;
 {
        int ret;
        size_t n;
@@ -134,8 +133,6 @@ static int recv_sb(struct client_task *ct, fd_set *rfds,
        void *trafo_context;
        struct iovec iov;
 
        void *trafo_context;
        struct iovec iov;
 
-       if (!FD_ISSET(ct->scc.fd, rfds))
-               return 0;
        if (ct->status < CL_SENT_CH_RESPONSE)
                trafo = trafo_context = NULL;
        else {
        if (ct->status < CL_SENT_CH_RESPONSE)
                trafo = trafo_context = NULL;
        else {
@@ -146,7 +143,7 @@ static int recv_sb(struct client_task *ct, fd_set *rfds,
                ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
 again:
        sb_get_recv_buffer(ct->sbc[0], &iov);
                ct->sbc[0] = sb_new_recv(0, trafo, trafo_context);
 again:
        sb_get_recv_buffer(ct->sbc[0], &iov);
-       ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, rfds, &n);
+       ret = read_nonblock(ct->scc.fd, iov.iov_base, iov.iov_len, &n);
        if (ret < 0) {
                sb_free(ct->sbc[0]);
                ct->sbc[0] = NULL;
        if (ret < 0) {
                sb_free(ct->sbc[0]);
                ct->sbc[0] = NULL;
@@ -248,7 +245,7 @@ static int send_sb_command(struct client_task *ct)
 
        for (i = 0; i < num_inputs; i++)
                len += strlen(lls_input(i, ct->lpr)) + 1;
 
        for (i = 0; i < num_inputs; i++)
                len += strlen(lls_input(i, ct->lpr)) + 1;
-       p = command = para_malloc(len);
+       p = command = alloc(len);
        for (i = 0; i < num_inputs; i++) {
                const char *str = lls_input(i, ct->lpr);
                strcpy(p, str);
        for (i = 0; i < num_inputs; i++) {
                const char *str = lls_input(i, ct->lpr);
                strcpy(p, str);
@@ -258,9 +255,15 @@ static int send_sb_command(struct client_task *ct)
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
 }
 
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
 }
 
+/* Find out if the given string is contained in the features vector. */
 static bool has_feature(const char *feature, struct client_task *ct)
 {
 static bool has_feature(const char *feature, struct client_task *ct)
 {
-       return find_arg(feature, ct->features) >= 0? true : false;
+       if (!ct->features)
+               return false;
+       for (int i = 0; ct->features[i]; i++)
+               if (strcmp(feature, ct->features[i]) == 0)
+                       return true;
+       return false;
 }
 
 /*
 }
 
 /*
@@ -274,7 +277,7 @@ static bool has_feature(const char *feature, struct client_task *ct)
  * The context pointer refers to a client task structure that was initialized
  * earlier by client_open().
  */
  * The context pointer refers to a client task structure that was initialized
  * earlier by client_open().
  */
-static int client_post_select(struct sched *s, void *context)
+static int client_post_monitor(struct sched *s, void *context)
 {
        struct client_task *ct = context;
        int ret = 0;
 {
        struct client_task *ct = context;
        int ret = 0;
@@ -288,7 +291,7 @@ static int client_post_select(struct sched *s, void *context)
                return 0;
        switch (ct->status) {
        case CL_CONNECTED: /* receive welcome message */
                return 0;
        switch (ct->status) {
        case CL_CONNECTED: /* receive welcome message */
-               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &s->rfds, &n);
+               ret = read_nonblock(ct->scc.fd, buf, sizeof(buf), &n);
                if (ret < 0 || n == 0)
                        goto out;
                ct->features = parse_features(buf);
                if (ret < 0 || n == 0)
                        goto out;
                ct->features = parse_features(buf);
@@ -302,7 +305,7 @@ static int client_post_select(struct sched *s, void *context)
                 * 0.8.0 we no longer need to request the feature.
                 */
                bool has_sha256;
                 * 0.8.0 we no longer need to request the feature.
                 */
                bool has_sha256;
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
+               if (!sched_write_ok(ct->scc.fd, s))
                        return 0;
                has_sha256 = has_feature("sha256", ct);
                sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
                        return 0;
                has_sha256 = has_feature("sha256", ct);
                sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256?
@@ -324,7 +327,7 @@ static int client_post_select(struct sched *s, void *context)
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
 
                unsigned char crypt_buf[1024];
                struct sb_buffer sbb;
 
-               ret = recv_sb(ct, &s->rfds, &sbb);
+               ret = recv_sb(ct, &sbb);
                if (ret <= 0)
                        goto out;
                if (sbb.band != SBD_CHALLENGE) {
                if (ret <= 0)
                        goto out;
                if (sbb.band != SBD_CHALLENGE) {
@@ -339,18 +342,20 @@ static int client_post_select(struct sched *s, void *context)
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
-               ct->challenge_hash = para_malloc(HASH2_SIZE);
-
+               ct->challenge_hash = alloc(HASH2_SIZE);
                if (has_feature("sha256", ct)) {
                if (has_feature("sha256", ct)) {
-                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE,
+                               ct->challenge_hash);
                        hash2_to_asc(ct->challenge_hash, buf);
                } else {
                        hash2_to_asc(ct->challenge_hash, buf);
                } else {
-                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+                       hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE,
+                               ct->challenge_hash);
                        hash_to_asc(ct->challenge_hash, buf);
                }
                        hash_to_asc(ct->challenge_hash, buf);
                }
-               ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
-               ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
-                       SESSION_KEY_LEN);
+               ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE,
+                        SESSION_KEY_LEN);
+               ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE
+                       + SESSION_KEY_LEN, SESSION_KEY_LEN);
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
                PARA_INFO_LOG("--> %s\n", buf);
                ct->status = CL_RECEIVED_CHALLENGE;
                return 0;
@@ -371,7 +376,7 @@ static int client_post_select(struct sched *s, void *context)
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
                struct sb_buffer sbb;
        case CL_SENT_CH_RESPONSE: /* read server response */
                {
                struct sb_buffer sbb;
-               ret = recv_sb(ct, &s->rfds, &sbb);
+               ret = recv_sb(ct, &sbb);
                if (ret <= 0)
                        goto out;
                free(sbb.iov.iov_base);
                if (ret <= 0)
                        goto out;
                free(sbb.iov.iov_base);
@@ -383,7 +388,7 @@ static int client_post_select(struct sched *s, void *context)
                }
        case CL_RECEIVED_PROCEED: /* concat args and send command */
                {
                }
        case CL_RECEIVED_PROCEED: /* concat args and send command */
                {
-               if (!FD_ISSET(ct->scc.fd, &s->wfds))
+               if (!sched_write_ok(ct->scc.fd, s))
                        return 0;
                ret = send_sb_command(ct);
                if (ret <= 0)
                        return 0;
                ret = send_sb_command(ct);
                if (ret <= 0)
@@ -396,16 +401,16 @@ static int client_post_select(struct sched *s, void *context)
                        char *buf2;
                        size_t sz;
                        ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
                        char *buf2;
                        size_t sz;
                        ret = btr_node_status(ct->btrn[1], 0, BTR_NT_LEAF);
-                       if (ret == -E_BTR_EOF) {
+                       if (ret == -E_EOF) {
                                /* empty blob data packet indicates EOF */
                                PARA_INFO_LOG("blob sent\n");
                                ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true);
                                if (ret >= 0)
                                /* empty blob data packet indicates EOF */
                                PARA_INFO_LOG("blob sent\n");
                                ret = send_sb(ct, 1, NULL, 0, SBD_BLOB_DATA, true);
                                if (ret >= 0)
-                                       ret = -E_BTR_EOF;
+                                       ret = -E_EOF;
                        }
                        if (ret < 0)
                                goto close1;
                        }
                        if (ret < 0)
                                goto close1;
-                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->wfds)) {
+                       if (ret > 0 && sched_write_ok(ct->scc.fd, s)) {
                                sz = btr_next_buffer(ct->btrn[1], &buf2);
                                assert(sz);
                                ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
                                sz = btr_next_buffer(ct->btrn[1], &buf2);
                                assert(sz);
                                ret = send_sb(ct, 1, buf2, sz, SBD_BLOB_DATA, true);
@@ -421,9 +426,9 @@ static int client_post_select(struct sched *s, void *context)
                        ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
                        if (ret < 0)
                                goto close0;
                        ret = btr_node_status(ct->btrn[0], 0, BTR_NT_ROOT);
                        if (ret < 0)
                                goto close0;
-                       if (ret > 0 && FD_ISSET(ct->scc.fd, &s->rfds)) {
+                       if (ret > 0 && sched_read_ok(ct->scc.fd, s)) {
                                struct sb_buffer sbb;
                                struct sb_buffer sbb;
-                               ret = recv_sb(ct, &s->rfds, &sbb);
+                               ret = recv_sb(ct, &sbb);
                                if (ret < 0)
                                        goto close0;
                                if (ret > 0) {
                                if (ret < 0)
                                        goto close0;
                                if (ret > 0) {
@@ -452,8 +457,7 @@ out:
                return 0;
        btr_remove_node(&ct->btrn[0]);
        btr_remove_node(&ct->btrn[1]);
                return 0;
        btr_remove_node(&ct->btrn[0]);
        btr_remove_node(&ct->btrn[1]);
-       if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       PARA_NOTICE_LOG("closing connection (%s)\n", para_strerror(-ret));
        if (ct->scc.fd >= 0) {
                close(ct->scc.fd);
                ct->scc.fd = -1;
        if (ct->scc.fd >= 0) {
                close(ct->scc.fd);
                ct->scc.fd = -1;
@@ -489,7 +493,7 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
 
        PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, host, port);
+       ret = para_connect(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
@@ -504,8 +508,8 @@ int client_connect(struct client_task *ct, struct sched *s,
 
        ct->task = task_register(&(struct task_info) {
                .name = "client",
 
        ct->task = task_register(&(struct task_info) {
                .name = "client",
-               .pre_select = client_pre_select,
-               .post_select = client_post_select,
+               .pre_monitor = client_pre_monitor,
+               .post_monitor = client_post_monitor,
                .context = ct,
        }, s);
        return 1;
                .context = ct,
        }, s);
        return 1;
@@ -577,8 +581,9 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
                kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
        else {
        if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
                kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
        else {
+               struct stat statbuf;
                kf = make_message("%s/.paraslash/key.%s", home, user);
                kf = make_message("%s/.paraslash/key.%s", home, user);
-               if (!file_exists(kf)) {
+               if (stat(kf, &statbuf) != 0) { /* assume file does not exist */
                        free(kf);
                        kf = make_message("%s/.ssh/id_rsa", home);
                }
                        free(kf);
                        kf = make_message("%s/.ssh/id_rsa", home);
                }
@@ -586,7 +591,7 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        PARA_INFO_LOG("user: %s\n", user);
        PARA_INFO_LOG("key file: %s\n", kf);
        PARA_INFO_LOG("loglevel: %d\n", ll);
        PARA_INFO_LOG("user: %s\n", user);
        PARA_INFO_LOG("key file: %s\n", kf);
        PARA_INFO_LOG("loglevel: %d\n", ll);
-       ct = para_calloc(sizeof(*ct));
+       ct = zalloc(sizeof(*ct));
        ct->scc.fd = -1;
        ct->lpr = lpr;
        ct->key_file = kf;
        ct->scc.fd = -1;
        ct->lpr = lpr;
        ct->key_file = kf;
index 7b464d09cab0ed446aac327fbba6d32f9aadd7b3..809f027e278d44f4413482ba433b8893b8be0843 100644 (file)
@@ -31,7 +31,7 @@ struct close_on_fork {
  */
 void add_close_on_fork_list(int fd)
 {
  */
 void add_close_on_fork_list(int fd)
 {
-       struct close_on_fork *cof = para_malloc(sizeof(struct close_on_fork));
+       struct close_on_fork *cof = alloc(sizeof(struct close_on_fork));
 
        if (!initialized) {
                init_list_head(&close_on_fork_list);
 
        if (!initialized) {
                init_list_head(&close_on_fork_list);
index 5b17f116d53c93956faf60f88d903b233b7e9b69..60c2aeba4300124c51af13d03c522fe2c2bbb1f1 100644 (file)
--- a/command.c
+++ b/command.c
@@ -10,7 +10,6 @@
 #include <netdb.h>
 #include <lopsub.h>
 
 #include <netdb.h>
 #include <lopsub.h>
 
-#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "lsu.h"
 #include "para.h"
 #include "error.h"
 #include "lsu.h"
@@ -22,8 +21,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "daemon.h"
 #include "fd.h"
 #include "vss.h"
 #include "daemon.h"
 #include "fd.h"
@@ -48,12 +47,14 @@ extern struct misc_meta_data *mmd;
 int send_afs_status(struct command_context *cc, int parser_friendly);
 static bool subcmd_should_die;
 
 int send_afs_status(struct command_context *cc, int parser_friendly);
 static bool subcmd_should_die;
 
+/*
+ * Don't call PARA_XXX_LOG() here as we might already hold the log mutex. See
+ * generic_signal_handler() for details.
+ */
 static void command_handler_sighandler(int s)
 {
 static void command_handler_sighandler(int s)
 {
-       if (s != SIGTERM)
-               return;
-       PARA_EMERG_LOG("terminating on signal %d\n", SIGTERM);
-       subcmd_should_die = true;
+       if (s == SIGTERM)
+               subcmd_should_die = true;
 }
 
 /*
 }
 
 /*
@@ -79,7 +80,7 @@ static char *vss_status_tohuman(unsigned int flags)
  */
 static char *vss_get_status_flags(unsigned int flags)
 {
  */
 static char *vss_get_status_flags(unsigned int flags)
 {
-       char *msg = para_malloc(5 * sizeof(char));
+       char *msg = alloc(5 * sizeof(char));
 
        msg[0] = (flags & VSS_PLAYING)? 'P' : '_';
        msg[1] = (flags & VSS_NOMORE)? 'O' : '_';
 
        msg[0] = (flags & VSS_PLAYING)? 'P' : '_';
        msg[1] = (flags & VSS_NOMORE)? 'O' : '_';
@@ -391,7 +392,6 @@ static int com_si(struct command_context *cc,
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
                "server_pid: %d\n"
                "afs_pid: %d\n"
                "connections (active/accepted/total): %u/%u/%u\n"
-               "current loglevel: %s\n"
                "supported audio formats: %s\n",
                ut, mmd->num_played,
                (int)getppid(),
                "supported audio formats: %s\n",
                ut, mmd->num_played,
                (int)getppid(),
@@ -399,7 +399,6 @@ static int com_si(struct command_context *cc,
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
-               ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
@@ -508,6 +507,7 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
         * while we sleep.
         */
        para_block_signal(SIGTERM);
         * while we sleep.
         */
        para_block_signal(SIGTERM);
+       para_block_signal(SIGUSR1);
        for (;;) {
                sigset_t set;
                /*
        for (;;) {
                sigset_t set;
                /*
@@ -539,8 +539,10 @@ static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
                 * open a race window similar to the one described above.
                 */
                pselect(1, NULL, NULL, NULL, &ts, &set);
                 * open a race window similar to the one described above.
                 */
                pselect(1, NULL, NULL, NULL, &ts, &set);
-               if (subcmd_should_die)
+               if (subcmd_should_die) {
+                       PARA_EMERG_LOG("terminating on SIGTERM\n");
                        goto out;
                        goto out;
+               }
                ret = -E_SERVER_CRASH;
                if (getppid() == 1)
                        goto out;
                ret = -E_SERVER_CRASH;
                if (getppid() == 1)
                        goto out;
@@ -590,9 +592,56 @@ static int com_hup(__a_unused struct command_context *cc,
 }
 EXPORT_SERVER_CMD_HANDLER(hup);
 
 }
 EXPORT_SERVER_CMD_HANDLER(hup);
 
+static int com_ll(struct command_context *cc, struct lls_parse_result *lpr)
+{
+       unsigned ll, perms;
+       char *errctx;
+       const char *sev[] = {SEVERITIES}, *arg;
+       int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) { /* reporting is an unprivileged op. */
+               const char *severity;
+               mutex_lock(mmd_mutex);
+               severity = sev[mmd->loglevel];
+               mutex_unlock(mmd_mutex);
+               return send_sb_va(&cc->scc, SBD_OUTPUT, "%s\n", severity);
+       }
+       /*
+        * Changing the loglevel changes the state of both the afs and the vss,
+        * so we require both AFS_WRITE and VSS_WRITE.
+        */
+       perms = AFS_WRITE | VSS_WRITE;
+       if ((cc->u->perms & perms) != perms)
+               return -ERRNO_TO_PARA_ERROR(EPERM);
+       arg = lls_input(0, lpr);
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++)
+               if (!strcmp(arg, sev[ll]))
+                       break;
+       if (ll >= NUM_LOGLEVELS)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+       /* Ask the server and afs processes to adjust their log level. */
+       mutex_lock(mmd_mutex);
+       mmd->loglevel = ll;
+       mutex_unlock(mmd_mutex);
+       return 1;
+}
+EXPORT_SERVER_CMD_HANDLER(ll);
+
 static int com_term(__a_unused struct command_context *cc,
                __a_unused struct lls_parse_result *lpr)
 {
 static int com_term(__a_unused struct command_context *cc,
                __a_unused struct lls_parse_result *lpr)
 {
+       /*
+        * The server catches SIGTERM and propagates this signal to all its
+        * children. We are about to exit anyway, but we'd leak tons of memory
+        * if being terminated by the signal. So we ignore the signal here and
+        * terminate via the normal exit path, deallocating all memory.
+        */
+       para_sigaction(SIGTERM, SIG_IGN);
        kill(getppid(), SIGTERM);
        return 1;
 }
        kill(getppid(), SIGTERM);
        return 1;
 }
@@ -661,7 +710,7 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
 {
        long promille;
        int i, ret;
 {
        long promille;
        int i, ret;
-       char c, *errctx;
+       char *errctx;
 
        ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
        if (ret < 0) {
 
        ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
        if (ret < 0) {
@@ -669,21 +718,8 @@ static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
                return ret;
        }
        ret = para_atoi32(lls_input(0, lpr), &i);
                return ret;
        }
        ret = para_atoi32(lls_input(0, lpr), &i);
-       if (ret < 0) {
-               if (ret != -E_ATOI_JUNK_AT_END)
-                       return ret;
-               /*
-                * Compatibility code to keep the historic syntax (ff 30-)
-                * working. This can be removed after 0.7.0.
-                */
-               ret = sscanf(lls_input(0, lpr), "%i%c", &i, &c);
-               if (ret <= 0)
-                       return -E_COMMAND_SYNTAX;
-               if (ret > 1 && c == '-') {
-                       PARA_WARNING_LOG("use of obsolete syntax\n");
-                       i = -i;
-               }
-       }
+       if (ret < 0)
+               return ret;
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
@@ -775,19 +811,11 @@ static int parse_auth_request(char *buf, int len, const struct user **u,
                *p = '\0';
                p++;
                create_argv(p, ",", &features);
                *p = '\0';
                p++;
                create_argv(p, ",", &features);
-               /*
-                * Still accept sideband and AES feature requests (as a no-op)
-                * because some 0.6.x clients request them. The two checks
-                * below may be removed after 0.7.1.
-                */
                for (i = 0; features[i]; i++) {
                for (i = 0; features[i]; i++) {
-                       if (strcmp(features[i], "sideband") == 0)
-                               continue;
-                       if (strcmp(features[i], "aes_ctr128") == 0)
-                               continue;
                        /*
                        /*
-                        * ->sha256_requested can go away after 0.7.0 but the
-                        * check has to stay until 0.9.0.
+                        * ->sha256_requested can go away after 0.7.0 so that
+                        * sha256 is used unconditionally, but we need to
+                        * accept the feature request until 0.9.0.
                         */
                        if (strcmp(features[i], "sha256") == 0)
                                cf->sha256_requested = true;
                         */
                        if (strcmp(features[i], "sha256") == 0)
                                cf->sha256_requested = true;
@@ -828,13 +856,13 @@ static int run_command(struct command_context *cc, struct iovec *iov)
        }
        perms = server_command_perms[ret];
        if ((perms & cc->u->perms) != perms)
        }
        perms = server_command_perms[ret];
        if ((perms & cc->u->perms) != perms)
-               return -E_PERM;
+               return -ERRNO_TO_PARA_ERROR(EPERM);
        lcmd = lls_cmd(ret, server_cmd_suite);
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
        argc = i;
        lcmd = lls_cmd(ret, server_cmd_suite);
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
        argc = i;
-       argv = para_malloc((argc + 1) * sizeof(char *));
+       argv = arr_alloc(argc + 1, sizeof(char *));
        for (i = 0, p = iov->iov_base; p < end; i++) {
                argv[i] = para_strdup(p);
                p += strlen(p) + 1;
        for (i = 0, p = iov->iov_base; p < end; i++) {
                argv[i] = para_strdup(p);
                p += strlen(p) + 1;
@@ -889,7 +917,7 @@ int handle_connect(int fd)
        int ret;
        unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH2_SIZE];
        int ret;
        unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH2_SIZE];
-       char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
+       char *command = NULL, *buf = alloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct;
        struct iovec iov;
        size_t numbytes;
        struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct;
        struct iovec iov;
index ff4ce6fb7663c5a0c35e1a42b433060b3b162b89..1bce35f53f9ef3411a57bc6ea4564df6eea419b7 100644 (file)
@@ -37,7 +37,7 @@ static void compress_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
        free(fn->private_data);
 }
 
-static int compress_post_select(__a_unused struct sched *s, void *context)
+static int compress_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_compress_data *pcd = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_compress_data *pcd = fn->private_data;
@@ -59,14 +59,14 @@ next_buffer:
        btr_merge(btrn, fn->min_iqs);
        length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1;
        if (length == 0) { /* eof and 1 byte available */
        btr_merge(btrn, fn->min_iqs);
        length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1;
        if (length == 0) { /* eof and 1 byte available */
-               ret = -E_COMPRESS_EOF;
+               ret = -E_EOF;
                goto err;
        }
        ip = (int16_t *)inbuf;
        if (inplace)
                op = ip;
        else
                goto err;
        }
        ip = (int16_t *)inbuf;
        if (inplace)
                op = ip;
        else
-               op = para_malloc(length);
+               op = alloc(length);
        for (i = 0; i < length / 2; i++) {
                /* be careful in that heat, my dear */
                int sample = *ip++;
        for (i = 0; i < length / 2; i++) {
                /* be careful in that heat, my dear */
                int sample = *ip++;
@@ -116,7 +116,7 @@ err:
 
 static void compress_open(struct filter_node *fn)
 {
 
 static void compress_open(struct filter_node *fn)
 {
-       struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+       struct private_compress_data *pcd = zalloc(sizeof(*pcd));
        uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
        uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
 
        uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
        uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
 
@@ -162,6 +162,6 @@ const struct filter lsg_filter_cmd_com_compress_user_data = {
        .setup = compress_setup,
        .open = compress_open,
        .close = compress_close,
        .setup = compress_setup,
        .open = compress_open,
        .close = compress_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = compress_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = compress_post_monitor,
 };
 };
index b817979f39ec79c141cd67eaaef6f49f559b5d21..92560e00ec50b7fa7ae4fcfd7d8966bdca2c63bf 100644 (file)
@@ -102,9 +102,14 @@ if test $HAVE_OPENSSL = yes; then
        if test "$ac_cv_have_decl_RSA_set0_key" != "$ac_cv_lib_crypto_RSA_set0_key"; then
                AC_MSG_ERROR([openssl header/library mismatch])
        fi
        if test "$ac_cv_have_decl_RSA_set0_key" != "$ac_cv_lib_crypto_RSA_set0_key"; then
                AC_MSG_ERROR([openssl header/library mismatch])
        fi
-       test "$ac_cv_have_decl_RSA_set0_key" = yes &&
+       if test "$ac_cv_have_decl_RSA_set0_key" = yes; then
                AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl >= 1.1])
                AC_DEFINE([HAVE_RSA_SET0_KEY], [1], [openssl >= 1.1])
-
+       else
+               AC_MSG_WARN([
+       Old openssl library detected. Support for openssl-1.0 and earlier
+       will be removed in the next major paraslash release. Please upgrade
+       your openssl installation.])
+       fi
        HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes
        AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [],
                [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no],
        HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=yes
        AC_CHECK_DECL([CRYPTO_cleanup_all_ex_data], [],
                [HAVE_CRYPTO_CLEANUP_ALL_EX_DATA=no],
@@ -291,12 +296,10 @@ AC_DEFUN([NEED_FLAC_OBJECTS], [{
 }])
 ########################################################################### faad
 STASH_FLAGS
 }])
 ########################################################################### faad
 STASH_FLAGS
-LIB_ARG_WITH([faad], [-lfaad -lmp4ff])
+LIB_ARG_WITH([faad], [-lfaad])
 HAVE_FAAD=yes
 AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
 HAVE_FAAD=yes
 AC_CHECK_HEADER(neaacdec.h, [], HAVE_FAAD=no)
-AC_CHECK_HEADER(mp4ff.h, [], HAVE_FAAD=no)
 AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
 AC_CHECK_LIB([faad], [NeAACDecOpen], [], HAVE_FAAD=no)
-AC_CHECK_LIB([mp4ff], [mp4ff_meta_get_artist], [], HAVE_FAAD=no)
 LIB_SUBST_FLAGS(faad)
 UNSTASH_FLAGS
 ########################################################################### mad
 LIB_SUBST_FLAGS(faad)
 UNSTASH_FLAGS
 ########################################################################### mad
@@ -434,7 +437,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
        NEED_OPUS_OBJECTS() && server_errlist_objs="$server_errlist_objs opus_afh opus_common"
        NEED_FLAC_OBJECTS && server_errlist_objs="$server_errlist_objs flac_afh"
        if test $HAVE_FAAD = yes; then
        NEED_OPUS_OBJECTS() && server_errlist_objs="$server_errlist_objs opus_afh opus_common"
        NEED_FLAC_OBJECTS && server_errlist_objs="$server_errlist_objs flac_afh"
        if test $HAVE_FAAD = yes; then
-               server_errlist_objs="$server_errlist_objs aac_afh"
+               server_errlist_objs="$server_errlist_objs aac_afh mp4"
        fi
        server_objs="$server_errlist_objs"
        AC_SUBST(server_objs, add_dot_o($server_objs))
        fi
        server_objs="$server_errlist_objs"
        AC_SUBST(server_objs, add_dot_o($server_objs))
@@ -586,7 +589,7 @@ fi
 if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
        build_mixer="yes"
        executables="$executables mixer"
 if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
        build_mixer="yes"
        executables="$executables mixer"
-       mixer_errlist_objs="mixer exec string fd lsu version"
+       mixer_errlist_objs="mixer exec string fd time lsu version"
        if test $HAVE_OSS = yes; then
                mixer_errlist_objs="$mixer_errlist_objs oss_mix"
        fi
        if test $HAVE_OSS = yes; then
                mixer_errlist_objs="$mixer_errlist_objs oss_mix"
        fi
@@ -693,7 +696,7 @@ NEED_OPUS_OBJECTS && recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
 NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
 
 if test $HAVE_FAAD = yes; then
 NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
 
 if test $HAVE_FAAD = yes; then
-       recv_errlist_objs="$recv_errlist_objs aac_afh"
+       recv_errlist_objs="$recv_errlist_objs aac_afh mp4"
 fi
 recv_objs="$recv_errlist_objs"
 AC_SUBST(recv_objs, add_dot_o($recv_objs))
 fi
 recv_objs="$recv_errlist_objs"
 AC_SUBST(recv_objs, add_dot_o($recv_objs))
@@ -728,7 +731,7 @@ NEED_FLAC_OBJECTS && {
        audio_format_handlers="$audio_format_handlers flac"
 }
 if test $HAVE_FAAD = yes; then
        audio_format_handlers="$audio_format_handlers flac"
 }
 if test $HAVE_FAAD = yes; then
-       afh_errlist_objs="$afh_errlist_objs aac_afh"
+       afh_errlist_objs="$afh_errlist_objs aac_afh mp4"
        audio_format_handlers="$audio_format_handlers aac"
 fi
 
        audio_format_handlers="$audio_format_handlers aac"
 fi
 
@@ -786,7 +789,7 @@ NEED_FLAC_OBJECTS && {
        play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
 }
 if test $HAVE_FAAD = yes; then
        play_errlist_objs="$play_errlist_objs flacdec_filter flac_afh"
 }
 if test $HAVE_FAAD = yes; then
-       play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter"
+       play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter mp4"
 fi
 if test $HAVE_MAD = yes; then
        play_errlist_objs="$play_errlist_objs mp3dec_filter"
 fi
 if test $HAVE_MAD = yes; then
        play_errlist_objs="$play_errlist_objs mp3dec_filter"
@@ -842,6 +845,7 @@ audioc_errlist_objs="
        lsu
        net
        fd
        lsu
        net
        fd
+       time
        version
 "
 if test $HAVE_READLINE = yes; then
        version
 "
 if test $HAVE_READLINE = yes; then
@@ -849,7 +853,6 @@ if test $HAVE_READLINE = yes; then
                buffer_tree
                interactive
                sched
                buffer_tree
                interactive
                sched
-               time
        "
 fi
 audioc_objs="$audioc_errlist_objs"
        "
 fi
 audioc_objs="$audioc_errlist_objs"
diff --git a/crypt.h b/crypt.h
index 5ca6a54112b60f6d61b16485248fb28ff708df6b..5578cd563fae4dc24d673e653dcf21329eba90bc 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -48,7 +48,7 @@ int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
  * \param key_file The file containing the key.
  * \param result The key structure is returned here.
  *
  * \param key_file The file containing the key.
  * \param result The key structure is returned here.
  *
- * \return The size of the key on success, negative on errors.
+ * \return The size of the key in bytes on success, negative on errors.
  */
 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result);
 
  */
 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result);
 
index 3a44dbdddcd1cf59108ba110c8d83c4fac11d609..286ebe38f2215b879c3171183490b2199779f500 100644 (file)
@@ -295,7 +295,7 @@ int decode_private_key(const char *key_file, unsigned char **result,
                key_type = PKT_PEM;
                begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
                footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
                key_type = PKT_PEM;
                begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
                footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
-               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
+               PARA_WARNING_LOG("detected legacy PEM key %s\n", key_file);
        } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
                        strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
                key_type = PKT_OPENSSH;
        } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
                        strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
                key_type = PKT_OPENSSH;
@@ -317,7 +317,7 @@ int decode_private_key(const char *key_file, unsigned char **result,
                goto unmap;
 
        key_size = footer - begin;
                goto unmap;
 
        key_size = footer - begin;
-       key = para_malloc(key_size + 1);
+       key = alloc(key_size + 1);
        for (i = 0, j = 0; begin + i < footer; i++) {
                if (para_isspace(begin[i]))
                        continue;
        for (i = 0, j = 0; begin + i < footer; i++) {
                if (para_isspace(begin[i]))
                        continue;
index dd5420a6eab92de518e3af60ea49d094ae5db6bb..d8f598bef0c2eb1709fa46169c314e17a212d7b8 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -64,18 +64,24 @@ static void daemon_set_default_log_colors(void)
  */
 void daemon_set_log_color_or_die(const char *arg)
 {
  */
 void daemon_set_log_color_or_die(const char *arg)
 {
+       unsigned ll;
+       const char * const sev[] = {SEVERITIES};
        char *p = strchr(arg, ':');
        char *p = strchr(arg, ':');
-       int ret, ll;
 
        if (!p)
                goto err;
 
        if (!p)
                goto err;
-       ret = get_loglevel_by_name(arg);
-       if (ret < 0)
-               goto err;
-       ll = ret;
-       p++;
-       color_parse_or_die(p, me->log_colors[ll]);
-       return;
+       for (ll = 0; ll < NUM_LOGLEVELS; ll++) {
+               const char *name = sev[ll];
+               /*
+                * Parse only the first part of the string so that, for
+                * example, the argument "info:something_else" is recognized.
+                * Note that the string comparison is performed
+                * case-insensitively.
+                */
+               if (strncasecmp(arg, name, strlen(name)))
+                       continue;
+               return color_parse_or_die(p + 1, me->log_colors[ll]);
+       }
 err:
        PARA_EMERG_LOG("%s: invalid color argument\n", arg);
        exit(EXIT_FAILURE);
 err:
        PARA_EMERG_LOG("%s: invalid color argument\n", arg);
        exit(EXIT_FAILURE);
@@ -134,16 +140,29 @@ void daemon_set_logfile(const char *logfile_name)
 }
 
 /**
 }
 
 /**
- * Suppress log messages with severity lower than the given loglevel.
+ * Control the verbosity for logging.
  *
  *
- * \param loglevel The smallest level that should be logged.
+ * This instructs the daemon to not log subsequent messages whose severity is
+ * lower than the given value.
+ *
+ * \param loglevel The new log level.
  */
  */
-void daemon_set_loglevel(const char *loglevel)
+void daemon_set_loglevel(int loglevel)
 {
 {
-       int ret = get_loglevel_by_name(loglevel);
+       assert(loglevel >= 0);
+       assert(loglevel < NUM_LOGLEVELS);
+       me->loglevel = loglevel;
+}
 
 
-       assert(ret >= 0);
-       me->loglevel = ret;
+/**
+ * Get the current log level of the daemon.
+ *
+ * \return Greater or equal than zero and less than NUM_LOGLEVELS. This
+ * function never fails.
+ */
+int daemon_get_loglevel(void)
+{
+       return me->loglevel;
 }
 
 /**
 }
 
 /**
@@ -263,9 +282,9 @@ void daemon_close_log(void)
 }
 
 /**
 }
 
 /**
- * fopen() the logfile in append mode.
+ * Open the logfile in append mode.
  *
  *
- * \return Either succeeds or exits.
+ * This function either succeeds or exits.
  */
 void daemon_open_log_or_die(void)
 {
  */
 void daemon_open_log_or_die(void)
 {
index b530b0d76b48b673fd7ae7626c48a6dd91ab7751..54879924954d0352d25cec928c1bcff9a6f9e58e 100644 (file)
--- a/daemon.h
+++ b/daemon.h
@@ -13,7 +13,8 @@ __malloc char *daemon_get_uptime_str(const struct timeval *current_time);
 void daemon_set_logfile(const char *logfile_name);
 void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void));
 void daemon_set_flag(unsigned flag);
 void daemon_set_logfile(const char *logfile_name);
 void daemon_set_hooks(void (*pre_log_hook)(void), void (*post_log_hook)(void));
 void daemon_set_flag(unsigned flag);
-void daemon_set_loglevel(const char *loglevel);
+int daemon_get_loglevel(void);
+void daemon_set_loglevel(int loglevel);
 bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
                int color_arg_no, bool logfile_given);
 void daemon_set_log_color_or_die(const char *arg);
 bool daemon_init_colors_or_die(int color_arg, int color_arg_auto,
                int color_arg_no, bool logfile_given);
 void daemon_set_log_color_or_die(const char *arg);
index 639c93fcbf99bc93ddc054ad43d03f30811270ac..0b20bcc8b1149132c4a1fa81a238c4479cb60df6 100644 (file)
 #include "net.h"
 #include "fd.h"
 
 #include "net.h"
 #include "fd.h"
 
+#ifndef DCCP_SOCKOPT_CCID
+#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
+#endif
+
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -59,6 +63,9 @@ static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
        return 1;
 }
 
        return 1;
 }
 
+/** Flowopt shortcut */
+#define OPT_ADD(fo, lev, opt, val, len) flowopt_add(fo, lev, opt, #opt, val, len)
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
        struct lls_parse_result *lpr = rn->lpr;
 static int dccp_recv_open(struct receiver_node *rn)
 {
        struct lls_parse_result *lpr = rn->lpr;
@@ -76,14 +83,14 @@ static int dccp_recv_open(struct receiver_node *rn)
        /* Copy CCID preference list (u8 array required) */
        given = lls_opt_given(r_c);
        if (given) {
        /* Copy CCID preference list (u8 array required) */
        given = lls_opt_given(r_c);
        if (given) {
-               ccids = para_malloc(given);
+               ccids = alloc(given);
                fo = flowopt_new();
                for (i = 0; i < given; i++)
                        ccids[i] = lls_int32_val(i, r_c);
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
                fo = flowopt_new();
                for (i = 0; i < given; i++)
                        ccids[i] = lls_int32_val(i, r_c);
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
+       fd = makesock(IPPROTO_DCCP, false, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
@@ -109,16 +116,16 @@ err:
        return ret;
 }
 
        return ret;
 }
 
-static void dccp_recv_pre_select(struct sched *s, void *context)
+static void dccp_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
 
 {
        struct receiver_node *rn = context;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
                return;
-       para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(rn->fd, s);
 }
 
 }
 
-static int dccp_recv_post_select(struct sched *s, void *context)
+static int dccp_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
@@ -136,7 +143,7 @@ static int dccp_recv_post_select(struct sched *s, void *context)
        ret = -E_DCCP_OVERRUN;
        if (iovcnt == 0)
                goto out;
        ret = -E_DCCP_OVERRUN;
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
@@ -154,6 +161,6 @@ out:
 const struct receiver lsg_recv_cmd_com_dccp_user_data = {
        .open = dccp_recv_open,
        .close = dccp_recv_close,
 const struct receiver lsg_recv_cmd_com_dccp_user_data = {
        .open = dccp_recv_open,
        .close = dccp_recv_close,
-       .pre_select = dccp_recv_pre_select,
-       .post_select = dccp_recv_post_select,
+       .pre_monitor = dccp_recv_pre_monitor,
+       .post_monitor = dccp_recv_post_monitor,
 };
 };
index bca7ad6781e29d2bb1bea686d3971e02ede59909..6182c964fde04719ee4736dc43482caf434ee395 100644 (file)
@@ -24,8 +24,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "fd.h"
 
 #include "vss.h"
 #include "fd.h"
 
@@ -36,16 +36,19 @@ struct dccp_fec_client {
        struct fec_client *fc;
 };
 
        struct fec_client *fc;
 };
 
-static void dccp_pre_select(int *max_fileno, fd_set *rfds,
-               __a_unused fd_set *wfds)
+static void dccp_pre_monitor(struct sched *s)
 {
        unsigned n;
 
        FOR_EACH_LISTEN_FD(n, dss)
                if (dss->listen_fds[n] >= 0)
 {
        unsigned n;
 
        FOR_EACH_LISTEN_FD(n, dss)
                if (dss->listen_fds[n] >= 0)
-                       para_fd_set(dss->listen_fds[n], rfds, max_fileno);
+                       sched_monitor_readfd(dss->listen_fds[n], s);
 }
 
 }
 
+#ifndef DCCP_SOCKOPT_TX_CCID
+#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
+#endif
+
 /**
  * Query the TX CCID used on the sender-client half connection.
  * \param sockfd Server socket descriptor to query (after accept(2)).
 /**
  * Query the TX CCID used on the sender-client half connection.
  * \param sockfd Server socket descriptor to query (after accept(2)).
@@ -90,6 +93,13 @@ static void dccp_shutdown(void)
        free_sender_status(dss);
 }
 
        free_sender_status(dss);
 }
 
+#ifndef DCCP_SOCKOPT_GET_CUR_MPS
+#define DCCP_SOCKOPT_GET_CUR_MPS 5 /**< Max packet size, RFC 4340, 14. */
+#endif
+
+/** Estimated worst-case length of a DCCP header including options. */
+#define DCCP_MAX_HEADER 128
+
 /** * Obtain current MPS according to RFC 4340, sec. 14. */
 static int dccp_init_fec(struct sender_client *sc)
 {
 /** * Obtain current MPS according to RFC 4340, sec. 14. */
 static int dccp_init_fec(struct sender_client *sc)
 {
@@ -119,14 +129,14 @@ static void dccp_send_fec(struct sender_client *sc, char *buf, size_t len)
                dccp_shutdown_client(sc);
 }
 
                dccp_shutdown_client(sc);
 }
 
-static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void dccp_post_monitor(__a_unused struct sched *s)
 {
        struct sender_client *sc;
        struct dccp_fec_client *dfc;
        int tx_ccid;
        uint32_t k, n;
 
 {
        struct sender_client *sc;
        struct dccp_fec_client *dfc;
        int tx_ccid;
        uint32_t k, n;
 
-       sc = accept_sender_client(dss, rfds);
+       sc = accept_sender_client(dss);
        if (!sc)
                return;
 
        if (!sc)
                return;
 
@@ -148,7 +158,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                shutdown_client(sc, dss);
                return;
        }
                shutdown_client(sc, dss);
                return;
        }
-       dfc = para_calloc(sizeof(*dfc));
+       dfc = zalloc(sizeof(*dfc));
        sc->private_data = dfc;
        k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
        n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
        sc->private_data = dfc;
        k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
        n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
@@ -249,8 +259,8 @@ const struct sender dccp_sender = {
        .name = "dccp",
        .init = dccp_send_init,
        .shutdown = dccp_shutdown,
        .name = "dccp",
        .init = dccp_send_init,
        .shutdown = dccp_shutdown,
-       .pre_select = dccp_pre_select,
-       .post_select = dccp_post_select,
+       .pre_monitor = dccp_pre_monitor,
+       .post_monitor = dccp_post_monitor,
        .shutdown_clients = dccp_shutdown_clients,
        .client_cmds = {
                [SENDER_on] = dccp_com_on,
        .shutdown_clients = dccp_shutdown_clients,
        .client_cmds = {
                [SENDER_on] = dccp_com_on,
diff --git a/error.h b/error.h
index cd5c20ef8f44501429ea8a9ab16b4bfe25d62b54..8805c9c7a4c0de6fe958e1523df883ad47effaad 100644 (file)
--- a/error.h
+++ b/error.h
@@ -2,6 +2,7 @@
 
 /** \file error.h List of error codes and messages. */
 
 
 /** \file error.h List of error codes and messages. */
 
+/** \cond para_error */
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
 /** Codes and messages. */
 #define PARA_ERRORS \
        PARA_ERROR(SUCCESS, "success"), \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
        PARA_ERROR(ALSA_MIX_RANGE, "value control element out of range"), \
        PARA_ERROR(ALSA_MIX_SET_VAL, "could not set control element state"), \
        PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
        PARA_ERROR(ALSA_MIX_RANGE, "value control element out of range"), \
        PARA_ERROR(ALSA_MIX_SET_VAL, "could not set control element state"), \
-       PARA_ERROR(AMP_EOF, "amp: end of file"), \
        PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \
        PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
        PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
        PARA_ERROR(AMP_ZERO_AMP, "no amplification necessary"), \
        PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
        PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
        PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
        PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
-       PARA_ERROR(AO_EOF, "ao: end of file"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
        PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
        PARA_ERROR(AO_PTHREAD, "pthread error"), \
        PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
        PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
        PARA_ERROR(AO_PLAY, "ao_play() failed"), \
        PARA_ERROR(AO_PTHREAD, "pthread error"), \
-       PARA_ERROR(ARG_NOT_FOUND, "argument not found in arg vector"), \
        PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
        PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
        PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \
        PARA_ERROR(ATT_TABLE_FULL, "no more space left in attribute table"), \
        PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
        PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
        PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
        PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
        PARA_ERROR(ATTR_SYNTAX, "attribute syntax error"), \
        PARA_ERROR(ATT_TABLE_FULL, "no more space left in attribute table"), \
-       PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
        PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
        PARA_ERROR(AUDIOD_SIGNAL, "caught deadly signal"), \
        PARA_ERROR(AUDIOD_TERM, "terminating on user request"), \
        PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \
        PARA_ERROR(AUDIOD_SIGNAL, "caught deadly signal"), \
        PARA_ERROR(AUDIOD_TERM, "terminating on user request"), \
@@ -51,7 +48,7 @@
        PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
        PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
        PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
        PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
-       PARA_ERROR(BAD_LL, "invalid loglevel"), \
+       PARA_ERROR(BAD_MOP, "invalid mood or playlist"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
@@ -65,7 +62,6 @@
        PARA_ERROR(BIGNUM, "bignum error"), \
        PARA_ERROR(BLINDING, "failed to activate key blinding"), \
        PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
        PARA_ERROR(BIGNUM, "bignum error"), \
        PARA_ERROR(BLINDING, "failed to activate key blinding"), \
        PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
-       PARA_ERROR(BTR_EOF, "buffer tree: end of file"), \
        PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
        PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
        PARA_ERROR(CHILD_CONTEXT, "now running in child context"), \
        PARA_ERROR(BTR_NAVAIL, "btr node: value currently unavailable"), \
        PARA_ERROR(BTR_NO_CHILD, "btr node has no children"), \
        PARA_ERROR(CHILD_CONTEXT, "now running in child context"), \
@@ -73,7 +69,6 @@
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(CLIENT_WRITE, "client write error"), \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(CLIENT_WRITE, "client write error"), \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
-       PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
        PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
        PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
        PARA_ERROR(DECRYPT, "decrypt error"), \
        PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
        PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
        PARA_ERROR(DECRYPT, "decrypt error"), \
@@ -84,7 +79,6 @@
        PARA_ERROR(EOF, "end of file"), \
        PARA_ERROR(EOP, "end of playlist"), \
        PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
        PARA_ERROR(EOF, "end of file"), \
        PARA_ERROR(EOP, "end of playlist"), \
        PARA_ERROR(FEC_BAD_IDX, "invalid index vector"), \
-       PARA_ERROR(FECDEC_EOF, "received eof packet"), \
        PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
        PARA_ERROR(FEC_PARMS, "invalid fec parameters"), \
        PARA_ERROR(FEC_PIVOT, "pivot column not found"), \
        PARA_ERROR(FECDEC_OVERRUN, "fecdec output buffer overrun"), \
        PARA_ERROR(FEC_PARMS, "invalid fec parameters"), \
        PARA_ERROR(FEC_PIVOT, "pivot column not found"), \
@@ -97,7 +91,6 @@
        PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \
        PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
        PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
        PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \
        PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \
        PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \
-       PARA_ERROR(FLACDEC_EOF, "flacdec encountered end of file condition"), \
        PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \
        PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \
        PARA_ERROR(FLAC_REPLACE_COMMENT, "could not replace vorbis comment"), \
        PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \
        PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \
        PARA_ERROR(FLAC_REPLACE_COMMENT, "could not replace vorbis comment"), \
        PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
        PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
        PARA_ERROR(HTTP_RECV_OVERRUN, "http_recv: output buffer overrun"), \
        PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
        PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
        PARA_ERROR(HTTP_RECV_OVERRUN, "http_recv: output buffer overrun"), \
-       PARA_ERROR(I9E_EOF, "end of input"), \
        PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \
        PARA_ERROR(I9E_TERM_RQ, "received termination request"), \
        PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \
        PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \
        PARA_ERROR(I9E_TERM_RQ, "received termination request"), \
        PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \
        PARA_ERROR(MAKESOCK, "makesock error"), \
        PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
        PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
        PARA_ERROR(MAKESOCK, "makesock error"), \
        PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
        PARA_ERROR(MISSING_COLON, "syntax error: missing colon"), \
-       PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
        PARA_ERROR(MOOD_PARSE, "mood parse error"), \
        PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
        PARA_ERROR(MOOD_PARSE, "mood parse error"), \
        PARA_ERROR(MP3DEC_CORRUPT, "too many corrupt frames"), \
-       PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
        PARA_ERROR(MP3_INFO, "could not read mp3 info"), \
-       PARA_ERROR(MP4FF_BAD_CHANNEL_COUNT, "mp4ff: invalid number of channels"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLE, "mp4ff: invalid sample number"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLERATE, "mp4ff: invalid sample rate"), \
-       PARA_ERROR(MP4FF_BAD_SAMPLE_COUNT, "mp4ff: invalid number of samples"), \
-       PARA_ERROR(MP4FF_META_READ, "mp4ff: could not read mp4 metadata"), \
-       PARA_ERROR(MP4FF_META_WRITE, "mp4ff: could not update mp4 metadata"), \
-       PARA_ERROR(MP4FF_OPEN, "mp4ff: open failed"), \
-       PARA_ERROR(MP4FF_TRACK, "mp4ff: no audio track"), \
+       PARA_ERROR(MP4_READ, "mp4: read error or unexpected end of file"), \
+       PARA_ERROR(MP4_CORRUPT, "invalid/corrupt mp4 file"), \
+       PARA_ERROR(MP4_BAD_SAMPLE, "mp4: invalid sample number"), \
+       PARA_ERROR(MP4_BAD_SAMPLERATE, "mp4: invalid sample rate"), \
+       PARA_ERROR(MP4_BAD_SAMPLE_COUNT, "mp4: invalid number of samples"), \
+       PARA_ERROR(MP4_TRACK, "mp4: no audio track"), \
+       PARA_ERROR(MP4_MISSING_ATOM, "mp4: essential atom not found"), \
        PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
        PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
        PARA_ERROR(NO_AFHI, "audio format handler info required"), \
        PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
        PARA_ERROR(NAME_TOO_LONG, "name too long for struct sockaddr_un"), \
        PARA_ERROR(NO_AFHI, "audio format handler info required"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
        PARA_ERROR(NO_MATCH, "no matches"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
        PARA_ERROR(NO_MATCH, "no matches"), \
-       PARA_ERROR(NO_MOOD, "no mood available"), \
        PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
        PARA_ERROR(NOT_PLAYING, "not playing"), \
        PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
        PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
        PARA_ERROR(NOT_PLAYING, "not playing"), \
        PARA_ERROR(NO_VALID_FILES, "no valid file found in playlist"), \
        PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
        PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
        PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
        PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
        PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
        PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
-       PARA_ERROR(PERM, "permission denied"), \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
-       PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(QUEUE, "packet queue overrun"), \
        PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(QUEUE, "packet queue overrun"), \
        PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
-       PARA_ERROR(RECV_EOF, "end of file"), \
        PARA_ERROR(RECVMSG, "recvmsg() failed"), \
        PARA_ERROR(REGEX, "regular expression error"), \
        PARA_ERROR(RECVMSG, "recvmsg() failed"), \
        PARA_ERROR(REGEX, "regular expression error"), \
-       PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \
        PARA_ERROR(RSA, "RSA error"), \
        PARA_ERROR(RSA_DECODE, "RSA decoding error"), \
        PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
        PARA_ERROR(RSA, "RSA error"), \
        PARA_ERROR(RSA_DECODE, "RSA decoding error"), \
        PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
        PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
        PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
        PARA_ERROR(SERVER_CMD_FAILURE, "command failed"), \
        PARA_ERROR(SERVER_CMD_SUCCESS, "command terminated successfully"), \
        PARA_ERROR(SERVER_CRASH, "para_server crashed -- can not live without it"), \
-       PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
        PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
        PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
        PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
        PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
        PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
        PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
        PARA_ERROR(VORBIS_COMMENTHEADER, "could not create vorbis comment header"), \
        PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
        PARA_ERROR(WAV_BAD_FC, "invalid filter configuration"), \
        PARA_ERROR(VORBIS_COMMENTHEADER, "could not create vorbis comment header"), \
        PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \
        PARA_ERROR(WAV_BAD_FC, "invalid filter configuration"), \
-       PARA_ERROR(WAV_EOF, "wav filter: end of file"), \
        PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \
        PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \
        PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \
        PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \
        PARA_ERROR(WAV_SUCCESS, "successfully wrote wav header"), \
        PARA_ERROR(WMA_BAD_PARAMS, "invalid WMA parameters"), \
        PARA_ERROR(WMA_BAD_SUPERFRAME, "invalid superframe"), \
        PARA_ERROR(WMA_BLOCK_SIZE, "invalid block size"), \
-       PARA_ERROR(WMADEC_EOF, "wmadec: end of file"), \
        PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
        PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \
        PARA_ERROR(WMA_NO_GUID, "audio stream guid not found"), \
        PARA_ERROR(WMA_OUTPUT_SPACE, "insufficient output space"), \
-       PARA_ERROR(WRITE_COMMON_EOF, "end of file"), \
 
 /**
  * This is temporarily defined to expand to its first argument (prefixed by
 
 /**
  * This is temporarily defined to expand to its first argument (prefixed by
@@ -266,6 +246,7 @@ enum para_error_codes {PARA_ERRORS};
 extern const char * const para_errlist[];
 /** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
 extern const char * const para_errlist[];
 /** Exactly one .c file per executable must define the array. */
 #define DEFINE_PARA_ERRLIST const char * const para_errlist[] = {PARA_ERRORS}
+/** \endcond para_error */
 
 /**
  * This bit indicates whether a number is considered a system error number
 
 /**
  * This bit indicates whether a number is considered a system error number
@@ -309,18 +290,20 @@ static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror"
  */
 _static_inline_ const char *para_strerror(int num)
 {
  */
 _static_inline_ const char *para_strerror(int num)
 {
+       unsigned idx = num & ~((1U << OSL_ERROR_BIT) | (1U << LLS_ERROR_BIT)
+               | (1U << SYSTEM_ERROR_BIT));
        assert(num > 0);
        if (IS_OSL_ERROR(num)) {
                assert(weak_osl_strerror);
        assert(num > 0);
        if (IS_OSL_ERROR(num)) {
                assert(weak_osl_strerror);
-               return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
+               return weak_osl_strerror(idx);
        }
        if (IS_LLS_ERROR(num)) {
                assert(weak_lls_strerror);
        }
        if (IS_LLS_ERROR(num)) {
                assert(weak_lls_strerror);
-               return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+               return weak_lls_strerror(idx);
        }
        if (IS_SYSTEM_ERROR(num))
        }
        if (IS_SYSTEM_ERROR(num))
-               return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
-       return para_errlist[num];
+               return strerror(idx);
+       return para_errlist[idx];
 }
 
 /**
 }
 
 /**
diff --git a/fd.c b/fd.c
index 33891d2e6c9f1c3568428b1df93b4b92e295ef61..1af902f9af440cbf570377ed219eea08e76417a2 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -37,26 +37,27 @@ int xrename(const char *oldpath, const char *newpath)
 }
 
 /**
 }
 
 /**
- * Write an array of buffers to a file descriptor.
+ * Write an array of buffers, handling non-fatal errors.
  *
  *
- * \param fd The file descriptor.
+ * \param fd The file descriptor to write to.
  * \param iov Pointer to one or more buffers.
  * \param iovcnt The number of buffers.
  *
  * \param iov Pointer to one or more buffers.
  * \param iovcnt The number of buffers.
  *
- * EAGAIN/EWOULDBLOCK is not considered a fatal error condition. For example
- * DCCP CCID3 has a sending wait queue which fills up and is emptied
- * asynchronously. The EAGAIN case means that there is currently no space in
- * the wait queue, but this can change at any moment.
+ * EAGAIN, EWOULDBLOCK and EINTR are not considered error conditions. If a
+ * write operation fails with EAGAIN or EWOULDBLOCK, the number of bytes that
+ * have been written so far is returned. In the EINTR case the operation is
+ * retried. Short writes are handled by issuing a subsequent write operation
+ * for the remaining part.
  *
  * \return Negative on fatal errors, number of bytes written else.
  *
  * For blocking file descriptors, this function returns either the sum of all
  *
  * \return Negative on fatal errors, number of bytes written else.
  *
  * For blocking file descriptors, this function returns either the sum of all
- * buffer sizes, or the error code of the fatal error that caused the last
- * write call to fail.
+ * buffer sizes or a negative error code which indicates the fatal error that
+ * caused a write call to fail.
  *
  *
- * For nonblocking file descriptors there is a third possibility: Any positive
- * return value less than the sum of the buffer sizes indicates that some bytes
- * have been written but the next write would block.
+ * For nonblocking file descriptors there is a third possibility: Any
+ * non-negative return value less than the sum of the buffer sizes indicates
+ * that a write operation returned EAGAIN/EWOULDBLOCK.
  *
  * \sa writev(2), \ref xwrite().
  */
  *
  * \sa writev(2), \ref xwrite().
  */
@@ -126,14 +127,15 @@ int xwrite(int fd, const char *buf, size_t len)
 }
 
 /**
 }
 
 /**
- * Write all data to a file descriptor.
+ * Write to a file descriptor, fail on short writes.
  *
  * \param fd The file descriptor.
  *
  * \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
+ * \param buf The buffer to be written.
+ * \param len The length of the buffer.
  *
  *
- * This is like \ref xwrite() but returns \p -E_SHORT_WRITE if not
- * all data could be written.
+ * For blocking file descriptors this function behaves identical to \ref
+ * xwrite(). For non-blocking file descriptors it returns -E_SHORT_WRITE
+ * (rather than a value less than len) if not all data could be written.
  *
  * \return Number of bytes written on success, negative error code else.
  */
  *
  * \return Number of bytes written on success, negative error code else.
  */
@@ -149,12 +151,20 @@ int write_all(int fd, const char *buf, size_t len)
 }
 
 /**
 }
 
 /**
- * Write a buffer given by a format string.
+ * A fprintf-like function for raw file descriptors.
+ *
+ * This function creates a string buffer according to the given format and
+ * writes this buffer to a file descriptor.
  *
  * \param fd The file descriptor.
  * \param fmt A format string.
  *
  *
  * \param fd The file descriptor.
  * \param fmt A format string.
  *
+ * The difference to fprintf(3) is that the first argument is a file
+ * descriptor, not a FILE pointer. This function does not rely on stdio.
+ *
  * \return The return value of the underlying call to \ref write_all().
  * \return The return value of the underlying call to \ref write_all().
+ *
+ * \sa fprintf(3), \ref xvasprintf().
  */
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
 {
  */
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
 {
@@ -176,14 +186,11 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  * \param fd The file descriptor to read from.
  * \param iov Scatter/gather array used in readv().
  * \param iovcnt Number of elements in \a iov.
  * \param fd The file descriptor to read from.
  * \param iov Scatter/gather array used in readv().
  * \param iovcnt Number of elements in \a iov.
- * \param rfds An optional fd set pointer.
  * \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
  *
  * \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
  *
- * If rfds is not NULL and the (non-blocking) file descriptor fd is not set in
- * rfds, this function returns early without doing anything. Otherwise it tries
- * to read up to sz bytes from fd, where sz is the sum of the lengths of all
- * vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are not considered
- * error conditions. However, EOF is.
+ * This function tries to read up to sz bytes from fd, where sz is the sum of
+ * the lengths of all vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are
+ * not considered error conditions. However, EOF is.
  *
  * \return Zero or a negative error code. If the underlying call to readv(2)
  * returned zero (indicating an end of file condition) or failed for some
  *
  * \return Zero or a negative error code. If the underlying call to readv(2)
  * returned zero (indicating an end of file condition) or failed for some
@@ -197,24 +204,12 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  *
  * \sa \ref xwrite(), read(2), readv(2).
  */
  *
  * \sa \ref xwrite(), read(2), readv(2).
  */
-int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
-               size_t *num_bytes)
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes)
 {
        int ret, i, j;
 
        *num_bytes = 0;
 {
        int ret, i, j;
 
        *num_bytes = 0;
-       /*
-        * Avoid a shortcoming of select(): Reads from a non-blocking fd might
-        * return EAGAIN even if FD_ISSET() returns true. However, FD_ISSET()
-        * returning false definitely means that no data can currently be read.
-        * This is the common case, so it is worth to avoid the overhead of the
-        * read() system call in this case.
-        */
-       if (rfds && !FD_ISSET(fd, rfds))
-               return 0;
-
        for (i = 0, j = 0; i < iovcnt;) {
        for (i = 0, j = 0; i < iovcnt;) {
-
                /* fix up the first iov */
                assert(j < iov[i].iov_len);
                iov[i].iov_base += j;
                /* fix up the first iov */
                assert(j < iov[i].iov_len);
                iov[i].iov_base += j;
@@ -251,7 +246,6 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
  * \param fd The file descriptor to read from.
  * \param buf The buffer to read data to.
  * \param sz The size of \a buf.
  * \param fd The file descriptor to read from.
  * \param buf The buffer to read data to.
  * \param sz The size of \a buf.
- * \param rfds \see \ref readv_nonblock().
  * \param num_bytes \see \ref readv_nonblock().
  *
  * This is a simple wrapper for readv_nonblock() which uses an iovec with a single
  * \param num_bytes \see \ref readv_nonblock().
  *
  * This is a simple wrapper for readv_nonblock() which uses an iovec with a single
@@ -259,102 +253,51 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
  *
  * \return The return value of the underlying call to readv_nonblock().
  */
  *
  * \return The return value of the underlying call to readv_nonblock().
  */
-int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes)
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes)
 {
        struct iovec iov = {.iov_base = buf, .iov_len = sz};
 {
        struct iovec iov = {.iov_base = buf, .iov_len = sz};
-       return readv_nonblock(fd, &iov, 1, rfds, num_bytes);
+       return readv_nonblock(fd, &iov, 1, num_bytes);
 }
 
 /**
 }
 
 /**
- * Read a buffer and check its content for a pattern.
+ * Read a buffer and compare its contents to a string, ignoring case.
  *
  *
- * \param fd The file descriptor to receive from.
- * \param pattern The expected pattern.
- * \param bufsize The size of the internal buffer.
- * \param rfds Passed to read_nonblock().
- *
- * This function tries to read at most \a bufsize bytes from the non-blocking
- * file descriptor \a fd. If at least \p strlen(\a pattern) bytes have been
- * received, the beginning of the received buffer is compared with \a pattern,
- * ignoring case.
+ * \param fd The file descriptor to read from.
+ * \param expectation The expected string to compare to.
  *
  *
- * \return Positive if \a pattern was received, negative on errors, zero if no data
- * was available to read.
+ * The given file descriptor is expected to be in non-blocking mode. The string
+ * comparison is performed using strncasecmp(3).
  *
  *
- * \sa \ref read_nonblock(), \sa strncasecmp(3).
+ * \return Zero if no data was available, positive if a buffer was read whose
+ * contents compare as equal to the expected string, negative otherwise.
+ * Possible errors: (a) not enough data was read, (b) the buffer contents
+ * compared as non-equal, (c) a read error occurred. In the first two cases,
+ * -E_READ_PATTERN is returned. In the read error case the (negative) return
+ * value of the underlying call to \ref read_nonblock() is returned.
  */
  */
-int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds)
+int read_and_compare(int fd, const char *expectation)
 {
 {
-       size_t n, len;
-       char *buf = para_malloc(bufsize + 1);
-       int ret = read_nonblock(fd, buf, bufsize, rfds, &n);
+       size_t n, len = strlen(expectation);
+       char *buf = alloc(len + 1);
+       int ret = read_nonblock(fd, buf, len, &n);
 
 
-       buf[n] = '\0';
        if (ret < 0)
                goto out;
        if (ret < 0)
                goto out;
+       buf[n] = '\0';
        ret = 0;
        if (n == 0)
                goto out;
        ret = -E_READ_PATTERN;
        ret = 0;
        if (n == 0)
                goto out;
        ret = -E_READ_PATTERN;
-       len = strlen(pattern);
        if (n < len)
                goto out;
        if (n < len)
                goto out;
-       if (strncasecmp(buf, pattern, len) != 0)
+       if (strncasecmp(buf, expectation, len) != 0)
                goto out;
        ret = 1;
 out:
                goto out;
        ret = 1;
 out:
-       if (ret < 0) {
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
-               PARA_NOTICE_LOG("recvd %zu bytes: %s\n", n, buf);
-       }
        free(buf);
        return ret;
 }
 
        free(buf);
        return ret;
 }
 
-/**
- * Check whether a file exists.
- *
- * \param fn The file name.
- *
- * \return True iff file exists.
- */
-bool file_exists(const char *fn)
-{
-       struct stat statbuf;
-
-       return !stat(fn, &statbuf);
-}
-
-/**
- * Paraslash's wrapper for select(2).
- *
- * It calls select(2) (with no exceptfds) and starts over if select() was
- * interrupted by a signal.
- *
- * \param n The highest-numbered descriptor in any of the two sets, plus 1.
- * \param readfds fds that should be checked for readability.
- * \param writefds fds that should be checked for writablility.
- * \param timeout_tv upper bound on the amount of time elapsed before select()
- * returns.
- *
- * \return The return value of the underlying select() call on success, the
- * negative system error code on errors.
- *
- * All arguments are passed verbatim to select(2).
- * \sa select(2) select_tut(2).
- */
-int para_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
-{
-       int ret;
-       do
-               ret = select(n, readfds, writefds, NULL, timeout_tv);
-       while (ret < 0 && errno == EINTR);
-       if (ret < 0)
-               return -ERRNO_TO_PARA_ERROR(errno);
-       return ret;
-}
-
 /**
  * Set a file descriptor to blocking mode.
  *
 /**
  * Set a file descriptor to blocking mode.
  *
@@ -391,34 +334,6 @@ __must_check int mark_fd_nonblocking(int fd)
        return 1;
 }
 
        return 1;
 }
 
-/**
- * Set a file descriptor in a fd_set.
- *
- * \param fd The file descriptor to be set.
- * \param fds The file descriptor set.
- * \param max_fileno Highest-numbered file descriptor.
- *
- * This wrapper for FD_SET() passes its first two arguments to \p FD_SET. Upon
- * return, \a max_fileno contains the maximum of the old_value and \a fd.
- *
- * \sa \ref para_select.
-*/
-void para_fd_set(int fd, fd_set *fds, int *max_fileno)
-{
-       assert(fd >= 0 && fd < FD_SETSIZE);
-#if 0
-       {
-               int flags = fcntl(fd, F_GETFL);
-               if (!(flags & O_NONBLOCK)) {
-                       PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
-                       exit(EXIT_FAILURE);
-               }
-       }
-#endif
-       FD_SET(fd, fds);
-       *max_fileno = PARA_MAX(*max_fileno, fd);
-}
-
 /**
  * Paraslash's wrapper for mmap.
  *
 /**
  * Paraslash's wrapper for mmap.
  *
@@ -472,86 +387,32 @@ int para_open(const char *path, int flags, mode_t mode)
 }
 
 /**
 }
 
 /**
- * Wrapper for chdir(2).
- *
- * \param path The specified directory.
- *
- * \return Standard.
- */
-int para_chdir(const char *path)
-{
-       int ret = chdir(path);
-
-       if (ret >= 0)
-               return 1;
-       return -ERRNO_TO_PARA_ERROR(errno);
-}
-
-/**
- * Save the cwd and open a given directory.
+ * Create a directory, don't fail if it already exists.
  *
  *
- * \param dirname Path to the directory to open.
- * \param dir Result pointer.
- * \param cwd File descriptor of the current working directory.
- *
- * \return Standard.
- *
- * Opening the current directory (".") and calling fchdir() to return is
- * usually faster and more reliable than saving cwd in some buffer and calling
- * chdir() afterwards.
- *
- * If \a cwd is not \p NULL "." is opened and the resulting file descriptor is
- * stored in \a cwd. If the function returns success, and \a cwd is not \p
- * NULL, the caller must close this file descriptor (probably after calling
- * fchdir(*cwd)).
- *
- * On errors, the function undos everything, so the caller needs neither close
- * any files, nor change back to the original working directory.
+ * \param path Name of the directory to create.
  *
  *
- * \sa getcwd(3).
+ * This function passes the fixed mode value 0777 to mkdir(3) (which consults
+ * the file creation mask and restricts this value).
  *
  *
+ * \return Zero if the path already existed as a directory or as a symbolic
+ * link which leads to a directory, one if the path did not exist and the
+ * directory has been created successfully, negative error code else.
  */
  */
-static int para_opendir(const char *dirname, DIR **dir, int *cwd)
+int para_mkdir(const char *path)
 {
 {
-       int ret;
+       /*
+        * We call opendir(3) rather than relying on stat(2) because this way
+        * we don't need extra code to get the symlink case right.
+        */
+       DIR *dir = opendir(path);
 
 
-       *dir = NULL;
-       if (cwd) {
-               ret = para_open(".", O_RDONLY, 0);
-               if (ret < 0)
-                       return ret;
-               *cwd = ret;
-       }
-       ret = para_chdir(dirname);
-       if (ret < 0)
-               goto close_cwd;
-       *dir = opendir(".");
-       if (*dir)
-               return 1;
-       ret = -ERRNO_TO_PARA_ERROR(errno);
-       /* Ignore return value of fchdir() and close(). We're busted anyway. */
-       if (cwd) {
-               int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */
+       if (dir) {
+               closedir(dir);
+               return 0;
        }
        }
-close_cwd:
-       if (cwd)
-               close(*cwd);
-       return ret;
-}
-
-/**
- * A wrapper for mkdir(2).
- *
- * \param path Name of the directory to create.
- * \param mode The permissions to use.
- *
- * \return Standard.
- */
-int para_mkdir(const char *path, mode_t mode)
-{
-       if (!mkdir(path, mode))
-               return 1;
-       return -ERRNO_TO_PARA_ERROR(errno);
+       if (errno != ENOENT)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       return mkdir(path, 0777) == 0? 1 : -ERRNO_TO_PARA_ERROR(errno);
 }
 
 /**
 }
 
 /**
@@ -624,22 +485,62 @@ out:
  * \param start The start address of the memory mapping.
  * \param length The size of the mapping.
  *
  * \param start The start address of the memory mapping.
  * \param length The size of the mapping.
  *
- * \return Standard.
+ * If NULL is passed as the start address, the length value is ignored and the
+ * function does nothing.
+ *
+ * \return Zero if NULL was passed, one if the memory area was successfully
+ * unmapped, a negative error code otherwise.
  *
  * \sa munmap(2), \ref mmap_full_file().
  */
 int para_munmap(void *start, size_t length)
 {
  *
  * \sa munmap(2), \ref mmap_full_file().
  */
 int para_munmap(void *start, size_t length)
 {
-       int err;
-
        if (!start)
                return 0;
        if (munmap(start, length) >= 0)
                return 1;
        if (!start)
                return 0;
        if (munmap(start, length) >= 0)
                return 1;
-       err = errno;
-       PARA_ERROR_LOG("munmap (%p/%zu) failed: %s\n", start, length,
-               strerror(err));
-       return -ERRNO_TO_PARA_ERROR(err);
+       return -ERRNO_TO_PARA_ERROR(errno);
+}
+
+/**
+ * Simple wrapper for poll(2).
+ *
+ * It calls poll(2) and starts over if the call was interrupted by a signal.
+ *
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
+ *
+ * \return The return value of the underlying poll() call on success, the
+ * negative paraslash error code on errors.
+ *
+ * All arguments are passed verbatim to poll(2).
+ */
+int xpoll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+       int ret;
+
+       do
+               ret = poll(fds, nfds, timeout);
+       while (ret < 0 && errno == EINTR);
+       return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
+}
+
+/**
+ * Check a file descriptor for readability.
+ *
+ * \param fd The file descriptor.
+ *
+ * \return positive if fd is ready for reading, zero if it isn't, negative if
+ * an error occurred.
+ *
+ * \sa \ref write_ok().
+ */
+int read_ok(int fd)
+{
+       struct pollfd pfd = {.fd = fd, .events = POLLIN};
+       int ret = xpoll(&pfd, 1, 0);
+       return ret < 0? ret : pfd.revents & POLLIN;
 }
 
 /**
 }
 
 /**
@@ -649,18 +550,14 @@ int para_munmap(void *start, size_t length)
  *
  * \return positive if fd is ready for writing, zero if it isn't, negative if
  * an error occurred.
  *
  * \return positive if fd is ready for writing, zero if it isn't, negative if
  * an error occurred.
+ *
+ * \sa \ref read_ok().
  */
  */
-
 int write_ok(int fd)
 {
 int write_ok(int fd)
 {
-       struct timeval tv;
-       fd_set wfds;
-
-       FD_ZERO(&wfds);
-       FD_SET(fd, &wfds);
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       return para_select(fd + 1, NULL, &wfds, &tv);
+       struct pollfd pfd = {.fd = fd, .events = POLLOUT};
+       int ret = xpoll(&pfd, 1, 0);
+       return ret < 0? ret : pfd.revents & POLLOUT;
 }
 
 /**
 }
 
 /**
@@ -681,64 +578,3 @@ void valid_fd_012(void)
                }
        }
 }
                }
        }
 }
-
-/**
- * Traverse the given directory recursively.
- *
- * \param dirname The directory to traverse.
- * \param func The function to call for each entry.
- * \param private_data Pointer to an arbitrary data structure.
- *
- * For each regular file under \a dirname, the supplied function \a func is
- * called.  The full path of the regular file and the \a private_data pointer
- * are passed to \a func. Directories for which the calling process has no
- * permissions to change to are silently ignored.
- *
- * \return Standard.
- */
-int for_each_file_in_dir(const char *dirname,
-               int (*func)(const char *, void *), void *private_data)
-{
-       DIR *dir;
-       struct dirent *entry;
-       int cwd_fd, ret = para_opendir(dirname, &dir, &cwd_fd);
-
-       if (ret < 0)
-               return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret;
-       /* scan cwd recursively */
-       while ((entry = readdir(dir))) {
-               mode_t m;
-               char *tmp;
-               struct stat s;
-
-               if (!strcmp(entry->d_name, "."))
-                       continue;
-               if (!strcmp(entry->d_name, ".."))
-                       continue;
-               if (lstat(entry->d_name, &s) == -1)
-                       continue;
-               m = s.st_mode;
-               if (!S_ISREG(m) && !S_ISDIR(m))
-                       continue;
-               tmp = make_message("%s/%s", dirname, entry->d_name);
-               if (!S_ISDIR(m)) {
-                       ret = func(tmp, private_data);
-                       free(tmp);
-                       if (ret < 0)
-                               goto out;
-                       continue;
-               }
-               /* directory */
-               ret = for_each_file_in_dir(tmp, func, private_data);
-               free(tmp);
-               if (ret < 0)
-                       goto out;
-       }
-       ret = 1;
-out:
-       closedir(dir);
-       if (fchdir(cwd_fd) < 0 && ret >= 0)
-               ret = -ERRNO_TO_PARA_ERROR(errno);
-       close(cwd_fd);
-       return ret;
-}
diff --git a/fd.h b/fd.h
index c9e79426f5c27ebf621367ce24b1c073c4e97e23..e4f3090301b94c90c96452a94fb4b5fa11b7d801 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -5,29 +5,24 @@
 int xrename(const char *oldpath, const char *newpath);
 int write_all(int fd, const char *buf, size_t len);
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
 int xrename(const char *oldpath, const char *newpath);
 int write_all(int fd, const char *buf, size_t len);
 __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...);
-bool file_exists(const char *);
-int para_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv);
+int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
 __must_check int mark_fd_nonblocking(int fd);
 __must_check int mark_fd_blocking(int fd);
 __must_check int mark_fd_nonblocking(int fd);
 __must_check int mark_fd_blocking(int fd);
-void para_fd_set(int fd, fd_set *fds, int *max_fileno);
 int para_mmap(size_t length, int prot, int flags, int fd, void *map);
 int para_open(const char *path, int flags, mode_t mode);
 int para_mmap(size_t length, int prot, int flags, int fd, void *map);
 int para_open(const char *path, int flags, mode_t mode);
-int para_mkdir(const char *path, mode_t mode);
-int para_chdir(const char *path);
+int para_mkdir(const char *path);
 int mmap_full_file(const char *filename, int open_mode, void **map,
        size_t *size, int *fd_ptr);
 int para_munmap(void *start, size_t length);
 int mmap_full_file(const char *filename, int open_mode, void **map,
        size_t *size, int *fd_ptr);
 int para_munmap(void *start, size_t length);
+int read_ok(int fd);
 int write_ok(int fd);
 void valid_fd_012(void);
 int write_ok(int fd);
 void valid_fd_012(void);
-int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
-               size_t *num_bytes);
-int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes);
-int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds);
+int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes);
+int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes);
+int read_and_compare(int fd, const char *expectation);
 int xwrite(int fd, const char *buf, size_t len);
 int xwritev(int fd, struct iovec *iov, int iovcnt);
 int xwrite(int fd, const char *buf, size_t len);
 int xwritev(int fd, struct iovec *iov, int iovcnt);
-int for_each_file_in_dir(const char *dirname,
-               int (*func)(const char *, void *), void *private_data);
+
 /**
  * Write a \p NULL-terminated buffer.
  *
 /**
  * Write a \p NULL-terminated buffer.
  *
diff --git a/fec.c b/fec.c
index 2301cc8d2d4653b8bc82a8bcc46b5867b5d7af14..932e0693e3786fc28b5a180428a9b296534a1fd5 100644 (file)
--- a/fec.c
+++ b/fec.c
@@ -97,7 +97,7 @@ static void init_mul_table(void)
 
 static unsigned char *alloc_matrix(int rows, int cols)
 {
 
 static unsigned char *alloc_matrix(int rows, int cols)
 {
-       return para_malloc(rows * cols);
+       return arr_alloc(rows, cols);
 }
 
 /*
 }
 
 /*
@@ -245,9 +245,9 @@ static void matmul(unsigned char *a, unsigned char *b, unsigned char *c,
 static int invert_mat(unsigned char *src, int k)
 {
        int irow, icol, row, col, ix, error;
 static int invert_mat(unsigned char *src, int k)
 {
        int irow, icol, row, col, ix, error;
-       int *indxc = para_malloc(k * sizeof(int));
-       int *indxr = para_malloc(k * sizeof(int));
-       int *ipiv = para_malloc(k * sizeof(int)); /* elements used as pivots */
+       int *indxc = arr_alloc(k, sizeof(int));
+       int *indxr = arr_alloc(k, sizeof(int));
+       int *ipiv = arr_alloc(k, sizeof(int)); /* elements used as pivots */
        unsigned char c, *p, *id_row = alloc_matrix(1, k),
                *temp_row = alloc_matrix(1, k);
 
        unsigned char c, *p, *id_row = alloc_matrix(1, k),
                *temp_row = alloc_matrix(1, k);
 
@@ -371,9 +371,9 @@ static void invert_vdm(unsigned char *src, int k)
         * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
         * b holds the coefficient for the matrix inversion
         */
         * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
         * b holds the coefficient for the matrix inversion
         */
-       c = para_malloc(k);
-       b = para_malloc(k);
-       p = para_malloc(k);
+       c = alloc(k);
+       b = alloc(k);
+       p = alloc(k);
 
        for (j = 1, i = 0; i < k; i++, j += k) {
                c[i] = 0;
 
        for (j = 1, i = 0; i < k; i++, j += k) {
                c[i] = 0;
@@ -466,7 +466,7 @@ int fec_new(int k, int n, struct fec_parms **result)
 
        if (k < 1 || k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n)
                return -E_FEC_PARMS;
 
        if (k < 1 || k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n)
                return -E_FEC_PARMS;
-       parms = para_malloc(sizeof(struct fec_parms));
+       parms = alloc(sizeof(struct fec_parms));
        parms->k = k;
        parms->n = n;
        parms->enc_matrix = alloc_matrix(n, k);
        parms->k = k;
        parms->n = n;
        parms->enc_matrix = alloc_matrix(n, k);
@@ -607,10 +607,10 @@ int fec_decode(struct fec_parms *parms, unsigned char **data, int *idx,
        if (ret < 0)
                return ret;
        /* do the actual decoding */
        if (ret < 0)
                return ret;
        /* do the actual decoding */
-       slice = para_malloc(k * sizeof(unsigned char *));
+       slice = arr_alloc(k, sizeof(unsigned char *));
        for (row = 0; row < k; row++) {
                if (idx[row] >= k) {
        for (row = 0; row < k; row++) {
                if (idx[row] >= k) {
-                       slice[row] = para_calloc(sz);
+                       slice[row] = zalloc(sz);
                        for (col = 0; col < k; col++)
                                addmul(slice[row], data[col],
                                        m_dec[row * k + col], sz);
                        for (col = 0; col < k; col++)
                                addmul(slice[row], data[col],
                                        m_dec[row * k + col], sz);
index 13d4f7b22f6a46da75ef4b629275d603fc5f39d1..375f4c0afb244b52e5d7b437a6b612c3da9d7f5b 100644 (file)
@@ -225,8 +225,8 @@ static int add_slice(char *buf, struct fecdec_group *fg)
        }
        if (fg->num_slices == 0) {
                fg->num_slices = fg->h.slices_per_group;
        }
        if (fg->num_slices == 0) {
                fg->num_slices = fg->h.slices_per_group;
-               fg->idx = para_malloc(fg->num_slices * sizeof(int));
-               fg->data = para_calloc(fg->num_slices * sizeof(unsigned char *));
+               fg->idx = arr_alloc(fg->num_slices, sizeof(int));
+               fg->data = arr_zalloc(fg->num_slices, sizeof(unsigned char *));
        }
        r = fg->num_received_slices;
        /* Check if we already have this slice. */
        }
        r = fg->num_received_slices;
        /* Check if we already have this slice. */
@@ -236,7 +236,7 @@ static int add_slice(char *buf, struct fecdec_group *fg)
                return 0;
        }
        fg->idx[r] = slice_num;
                return 0;
        }
        fg->idx[r] = slice_num;
-       fg->data[r] = para_malloc(fg->h.slice_bytes);
+       fg->data[r] = alloc(fg->h.slice_bytes);
        memcpy(fg->data[r], buf, fg->h.slice_bytes);
        fg->num_received_slices++;
        return 1;
        memcpy(fg->data[r], buf, fg->h.slice_bytes);
        fg->num_received_slices++;
        return 1;
@@ -360,7 +360,7 @@ static int read_fec_header(char *buf, size_t len, struct fec_header *h)
        h->bos = read_u8(buf + 22);
        h->header_stream = read_u8(buf + 23);
        if (!memcmp(buf, FEC_EOF_PACKET, FEC_EOF_PACKET_LEN))
        h->bos = read_u8(buf + 22);
        h->header_stream = read_u8(buf + 23);
        if (!memcmp(buf, FEC_EOF_PACKET, FEC_EOF_PACKET_LEN))
-               return -E_FECDEC_EOF;
+               return -E_EOF;
 //     PARA_DEBUG_LOG("group %u, slize %u, slices per group: %u\n",
 //             h->group_num, h->slice_num, h->slices_per_group);
        return 1;
 //     PARA_DEBUG_LOG("group %u, slize %u, slices per group: %u\n",
 //             h->group_num, h->slice_num, h->slices_per_group);
        return 1;
@@ -431,7 +431,7 @@ static void fecdec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
        fn->private_data = NULL;
 }
 
-static int fecdec_post_select(__a_unused struct sched *s, void *context)
+static int fecdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -471,14 +471,14 @@ out:
 static void fecdec_open(struct filter_node *fn)
 {
        struct private_fecdec_data *pfd;
 static void fecdec_open(struct filter_node *fn)
 {
        struct private_fecdec_data *pfd;
-       pfd = para_calloc(sizeof(*pfd));
+       pfd = zalloc(sizeof(*pfd));
        fn->private_data = pfd;
        fn->min_iqs = FEC_HEADER_SIZE;
 }
 
 const struct filter lsg_filter_cmd_com_fecdec_user_data = {
        .open = fecdec_open,
        fn->private_data = pfd;
        fn->min_iqs = FEC_HEADER_SIZE;
 }
 
 const struct filter lsg_filter_cmd_com_fecdec_user_data = {
        .open = fecdec_open,
-       .pre_select = generic_filter_pre_select,
-       .post_select = fecdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = fecdec_post_monitor,
        .close = fecdec_close,
 };
        .close = fecdec_close,
 };
index 9a5ed5d7fab7dc01fe46fa5ecab6940ea3a28828..ba902070d9498677e94872860aeb9bbcbcd5235e 100644 (file)
@@ -64,12 +64,12 @@ static int prepare_output_file(struct writer_node *wn)
                close(fd);
                return ret;
        }
                close(fd);
                return ret;
        }
-       pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+       pfwd = wn->private_data = zalloc(sizeof(*pfwd));
        pfwd->fd = fd;
        return 1;
 }
 
        pfwd->fd = fd;
        return 1;
 }
 
-static void file_write_pre_select(struct sched *s, void *context)
+static void file_write_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
@@ -79,7 +79,7 @@ static void file_write_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0 || !pfwd)
                return sched_min_delay(s);
                return;
        if (ret < 0 || !pfwd)
                return sched_min_delay(s);
-       para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(pfwd->fd, s);
 }
 
 static void file_write_close(struct writer_node *wn)
 }
 
 static void file_write_close(struct writer_node *wn)
@@ -92,7 +92,7 @@ static void file_write_close(struct writer_node *wn)
        free(pfwd);
 }
 
        free(pfwd);
 }
 
-static int file_write_post_select(__a_unused struct sched *s, void *context)
+static int file_write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_file_write_data *pfwd = wn->private_data;
@@ -111,7 +111,7 @@ static int file_write_post_select(__a_unused struct sched *s, void *context)
                ret = prepare_output_file(wn);
                goto out;
        }
                ret = prepare_output_file(wn);
                goto out;
        }
-       if (!FD_ISSET(pfwd->fd, &s->wfds))
+       if (!sched_write_ok(pfwd->fd, s))
                return 0;
        bytes = btr_next_buffer(btrn, &buf);
        assert(bytes > 0);
                return 0;
        bytes = btr_next_buffer(btrn, &buf);
        assert(bytes > 0);
@@ -128,7 +128,7 @@ out:
 
 /** the init function of the file writer */
 struct writer lsg_write_cmd_com_file_user_data = {
 
 /** the init function of the file writer */
 struct writer lsg_write_cmd_com_file_user_data = {
-       .pre_select = file_write_pre_select,
-       .post_select = file_write_post_select,
+       .pre_monitor = file_write_pre_monitor,
+       .post_monitor = file_write_post_monitor,
        .close = file_write_close,
 };
        .close = file_write_close,
 };
index d4a2423904ca96f87f1ec11600f1c67e2b101ae1..722cb16fb35bfaec09bbaca046ebf62931df8036 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -120,14 +120,14 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdin"));
        stdin_task_register(sit, &s);
 
                EMBRACE(.name = "stdin"));
        stdin_task_register(sit, &s);
 
-       fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+       fns = arr_alloc(OPT_GIVEN(FILTER), sizeof(*fns));
        for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
                const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
                const char *name;
                struct filter_node *fn;
                struct task_info ti;
 
        for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
                const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
                const char *name;
                struct filter_node *fn;
                struct task_info ti;
 
-               fn = fns[i] = para_calloc(sizeof(*fn));
+               fn = fns[i] = zalloc(sizeof(*fn));
                fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
                name = filter_name(fn->filter_num);
                fn->lpr = filter_lpr;
                fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
                name = filter_name(fn->filter_num);
                fn->lpr = filter_lpr;
@@ -137,8 +137,8 @@ int main(int argc, char *argv[])
                        EMBRACE(.name = name, .parent = parent,
                        .handler = f->execute, .context = fn));
                ti.name = name;
                        EMBRACE(.name = name, .parent = parent,
                        .handler = f->execute, .context = fn));
                ti.name = name;
-               ti.pre_select = f->pre_select;
-               ti.post_select = f->post_select;
+               ti.pre_monitor = f->pre_monitor;
+               ti.post_monitor = f->post_monitor;
                ti.context = fn;
                if (f->open)
                        f->open(fn);
                ti.context = fn;
                if (f->open)
                        f->open(fn);
@@ -149,8 +149,7 @@ int main(int argc, char *argv[])
                EMBRACE(.name = "stdout", .parent = parent));
        stdout_task_register(sot, &s);
 
                EMBRACE(.name = "stdout", .parent = parent));
        stdout_task_register(sot, &s);
 
-       s.default_timeout.tv_sec = 1;
-       s.default_timeout.tv_usec = 0;
+       s.default_timeout = 1000;
        btr_log_tree(sit->btrn, LL_INFO);
        ret = schedule(&s);
        sched_shutdown(&s);
        btr_log_tree(sit->btrn, LL_INFO);
        ret = schedule(&s);
        sched_shutdown(&s);
index 69d4dfee8534941ab74bb17d7d2a5ff8e23eed6b..77057e6a7d2b9a997219d907b3ddd459716283ad 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -28,16 +28,18 @@ struct filter_node {
 };
 
 /**
 };
 
 /**
- * The structure associated with a paraslash filter.
+ * Describes a method to convert audio data.
  *
  *
- * Paraslash filters are "modules" which transform an audio stream. struct
- * filter contains methods which are implemented by each filter.
+ * Paraslash filters are "modules" which transform the data of an audio stream.
+ * This structure contains the methods which have to be implemented by each
+ * filter.
  *
  *
- * Note: As several instances of the same filter may be running at the same
- * time, all these filter functions must be reentrant; no static non-constant
- * variables may be used.
+ * As several instances of the same filter may be running at the same time, all
+ * filter methods must be reentrant and no static non-constant variables must
+ * be used.
  *
  *
- * \sa \ref filter_node.
+ * \sa \ref filter_node, struct \ref receiver, struct \ref writer, struct \ref
+ * sched.
  */
 struct filter {
        /**
  */
 struct filter {
        /**
@@ -81,24 +83,10 @@ struct filter {
         * This should free whatever ->setup() has allocated.
         */
        void (*teardown)(const struct lls_parse_result *lpr, void *conf);
         * This should free whatever ->setup() has allocated.
         */
        void (*teardown)(const struct lls_parse_result *lpr, void *conf);
-       /**
-        * Set scheduler timeout and add file descriptors to fd sets.
-        *
-        * This function controls the timeout value for the next call to
-        * select(2). It may decrease the current timeout but shall never
-        * increase it. The second purpose of this function is to add file
-        * descriptors to the two fd sets of the sched structure. The
-        * descriptors in these sets will be watched by the subsequent
-        * select(2) call.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Convert (filter) the given data.
-        *
-        * Pointer to the converting function of the filter. On errors, the
-        * post_select function is supposed to return a negative error code.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Force a zero timeout if data is available in the buffer tree. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Convert (filter) input data into output data. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Answer a buffer tree query.
         *
        /**
         * Answer a buffer tree query.
         *
@@ -124,7 +112,7 @@ int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp);
 #define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
        (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
 
 #define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
        (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
 
-void generic_filter_pre_select(struct sched *s, void *context);
+void generic_filter_pre_monitor(struct sched *s, void *context);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
 
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
 
index add788a8f30465251ff111822b7131cd2e94054b..f48e457005ca3510fb51d5e5f95405af15d927c1 100644 (file)
@@ -169,17 +169,16 @@ void print_filter_list(void)
 }
 
 /**
 }
 
 /**
- * Set select timeout of the scheduler.
+ * Request a minimal timeout if not idle.
  *
  *
- * \param s The scheduler.
- * \param context Pointer to the filter node (task context).
+ * \param s The scheduler instance.
+ * \param context Pointer to the filter node.
  *
  *
- * This looks at the status of the btr node of the filter. If data is available
- * in the input queue of the filter, or if an error occurred, a minimal timeout
- * for the next select call is requested from the scheduler. Otherwise the
- * scheduler timeout is left unchanged.
+ * If the buffer tree node of the given filter node has data available (or is
+ * in error state) a minimal I/O timeout is requested from the scheduler.
+ * Otherwise the function does nothing.
  */
  */
-void generic_filter_pre_select(struct sched *s, void *context)
+void generic_filter_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
 
 {
        struct filter_node *fn = context;
 
index 6e23683937f6932aaa739095ef8281fc9075da55..2b5b6c1d9aa758e3d2e769756cc3d75f3bb57120 100644 (file)
@@ -374,8 +374,8 @@ static int flac_afh_read_chunks(struct private_flac_afh_data *pfad)
                        goto free_decoder;
                if (c >= chunk_table_size) {
                        chunk_table_size = 2 * chunk_table_size + 100;
                        goto free_decoder;
                if (c >= chunk_table_size) {
                        chunk_table_size = 2 * chunk_table_size + 100;
-                       afhi->chunk_table = para_realloc(afhi->chunk_table,
-                               chunk_table_size * sizeof(uint32_t));
+                       afhi->chunk_table = arr_realloc(afhi->chunk_table,
+                               chunk_table_size, sizeof(uint32_t));
                }
                afhi->chunk_table[c] = pos;
 
                }
                afhi->chunk_table[c] = pos;
 
@@ -481,7 +481,7 @@ static int flac_rewrite_tags(const char *map, size_t map_bytes,
        FLAC__Metadata_Iterator *iter;
        FLAC__StreamMetadata *b = NULL;
        FLAC__bool ok;
        FLAC__Metadata_Iterator *iter;
        FLAC__StreamMetadata *b = NULL;
        FLAC__bool ok;
-       struct private_flac_afh_data *pfad = para_calloc(sizeof(*pfad));
+       struct private_flac_afh_data *pfad = zalloc(sizeof(*pfad));
 
        pfad->map = map;
        pfad->map_bytes = map_bytes;
 
        pfad->map = map;
        pfad->map_bytes = map_bytes;
index 6a3a8effaf83b3b9e333e429762f0ff2d3938afe..fb8ebf15d1ec664e39923c58515a3704441ab0da 100644 (file)
@@ -135,7 +135,7 @@ static FLAC__StreamDecoderWriteStatus write_cb(
        struct btr_node *btrn = fn->btrn;
        size_t k, n = frame->header.blocksize;
        unsigned channels = FLAC__stream_decoder_get_channels(decoder);
        struct btr_node *btrn = fn->btrn;
        size_t k, n = frame->header.blocksize;
        unsigned channels = FLAC__stream_decoder_get_channels(decoder);
-       char *outbuffer = para_malloc(n * channels * 2);
+       char *outbuffer = arr_alloc(n, channels * 2);
 
        if (channels == 1) {
                for (k = 0; k < n; k++) {
 
        if (channels == 1) {
                for (k = 0; k < n; k++) {
@@ -205,7 +205,7 @@ static bool output_queue_full(struct btr_node *btrn)
        return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
 }
 
        return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
 }
 
-static void flacdec_pre_select(struct sched *s, void *context)
+static void flacdec_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
@@ -221,7 +221,7 @@ static void flacdec_pre_select(struct sched *s, void *context)
                return sched_min_delay(s);
 }
 
                return sched_min_delay(s);
 }
 
-static int flacdec_post_select(__a_unused struct sched *s, void *context)
+static int flacdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_flacdec_data *pfd = fn->private_data;
@@ -232,7 +232,7 @@ static int flacdec_post_select(__a_unused struct sched *s, void *context)
        if (output_queue_full(btrn))
                return 0;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (output_queue_full(btrn))
                return 0;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
+       if (ret < 0 && ret != -E_EOF) /* fatal error */
                goto out;
        if (ret <= 0 && !pfd->have_more) /* nothing to do */
                goto out;
                goto out;
        if (ret <= 0 && !pfd->have_more) /* nothing to do */
                goto out;
@@ -248,7 +248,7 @@ static int flacdec_post_select(__a_unused struct sched *s, void *context)
        pfd->have_more = false;
        FLAC__stream_decoder_process_single(pfd->decoder);
        state = FLAC__stream_decoder_get_state(pfd->decoder);
        pfd->have_more = false;
        FLAC__stream_decoder_process_single(pfd->decoder);
        state = FLAC__stream_decoder_get_state(pfd->decoder);
-       ret = -E_FLACDEC_EOF;
+       ret = -E_EOF;
        if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
                goto out;
        if (state == FLAC__STREAM_DECODER_ABORTED) {
        if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
                goto out;
        if (state == FLAC__STREAM_DECODER_ABORTED) {
@@ -286,7 +286,7 @@ static void flacdec_close(struct filter_node *fn)
 
 static void flacdec_open(struct filter_node *fn)
 {
 
 static void flacdec_open(struct filter_node *fn)
 {
-       struct private_flacdec_data *pfd = para_calloc(sizeof(*pfd));
+       struct private_flacdec_data *pfd = zalloc(sizeof(*pfd));
        fn->private_data = pfd;
        fn->min_iqs = 0;
 }
        fn->private_data = pfd;
        fn->min_iqs = 0;
 }
@@ -294,7 +294,7 @@ static void flacdec_open(struct filter_node *fn)
 const struct filter lsg_filter_cmd_com_flacdec_user_data = {
        .open = flacdec_open,
        .close = flacdec_close,
 const struct filter lsg_filter_cmd_com_flacdec_user_data = {
        .open = flacdec_open,
        .close = flacdec_close,
-       .pre_select = flacdec_pre_select,
-       .post_select = flacdec_post_select,
+       .pre_monitor = flacdec_pre_monitor,
+       .post_monitor = flacdec_post_monitor,
        .execute = flacdec_execute,
 };
        .execute = flacdec_execute,
 };
index 506f0bb84e6c199d065942553935dc27a8833d93..b46f8f9555824eb86b7bbf600b4352558ed2db26 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -114,7 +114,6 @@ void crypt_shutdown(void)
 
 struct asymmetric_key {
        gcry_sexp_t sexp;
 
 struct asymmetric_key {
        gcry_sexp_t sexp;
-       int num_bytes;
 };
 
 static const char *gcrypt_strerror(gcry_error_t gret)
 };
 
 static const char *gcrypt_strerror(gcry_error_t gret)
@@ -406,7 +405,7 @@ static int get_private_key(const char *key_file, struct asymmetric_key **result)
                ret = -E_SEXP_BUILD;
                goto free_params;
        }
                ret = -E_SEXP_BUILD;
                goto free_params;
        }
-       key = para_malloc(sizeof(*key));
+       key = alloc(sizeof(*key));
        key->sexp = sexp;
        *result = key;
        ret = bits;
        key->sexp = sexp;
        *result = key;
        ret = bits;
@@ -456,11 +455,10 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
                goto release_n;
        }
        PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
                goto release_n;
        }
        PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
-       key = para_malloc(sizeof(*key));
-       key->num_bytes = ret;
+       key = alloc(sizeof(*key));
        key->sexp = sexp;
        *result = key;
        key->sexp = sexp;
        *result = key;
-       ret = bits;
+       ret = bits / 8;
 release_n:
        gcry_mpi_release(n);
 release_e:
 release_n:
        gcry_mpi_release(n);
 release_e:
@@ -564,8 +562,6 @@ int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
        size_t nbytes;
        int ret;
 
        size_t nbytes;
        int ret;
 
-       PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes);
-
        /* get pub key */
        pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
        if (!pub_key)
        /* get pub key */
        pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
        if (!pub_key)
@@ -624,7 +620,7 @@ struct stream_cipher {
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        gcry_error_t gret;
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        gcry_error_t gret;
-       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+       struct stream_cipher *sc = alloc(sizeof(*sc));
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128,
index 8370649301906eaef68a4c30d1b1f3975283dea8..1019e579a39d8559b2aecc658c7651fd6b7704dc 100644 (file)
@@ -89,7 +89,7 @@ err:
        return -E_GC_WRITE;
 }
 
        return -E_GC_WRITE;
 }
 
-static void gc_pre_select(struct sched *s, void *context)
+static void gc_pre_monitor(struct sched *s, void *context)
 {
        struct grab_client *gc = context;
        int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
 {
        struct grab_client *gc = context;
        int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
@@ -98,14 +98,14 @@ static void gc_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0)
                sched_min_delay(s);
                return;
        if (ret < 0)
                sched_min_delay(s);
-       para_fd_set(gc->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(gc->fd, s);
 }
 
 /*
 }
 
 /*
- * We need this forward declaration as post_select() needs
+ * We need this forward declaration as gc_post_monitor() needs
  * activate_grab_client and vice versa.
  */
  * activate_grab_client and vice versa.
  */
-static int gc_post_select(struct sched *s, void *context);
+static int gc_post_monitor(struct sched *s, void *context);
 
 /**
  * Move a grab client to the active list and start it.
 
 /**
  * Move a grab client to the active list and start it.
@@ -129,8 +129,8 @@ static void gc_activate(struct grab_client *gc, struct sched *s)
 
        gc->task = task_register(&(struct task_info) {
                .name = name,
 
        gc->task = task_register(&(struct task_info) {
                .name = name,
-               .pre_select = gc_pre_select,
-               .post_select = gc_post_select,
+               .pre_monitor = gc_pre_monitor,
+               .post_monitor = gc_post_monitor,
                .context = gc,
        }, s);
 }
                .context = gc,
        }, s);
 }
@@ -171,7 +171,7 @@ static int gc_close(struct grab_client *gc, int err)
                /*
                 * We must not free the gc structure here as it contains ->task
                 * which is still used because this function is called from
                /*
                 * We must not free the gc structure here as it contains ->task
                 * which is still used because this function is called from
-                * post_select().
+                * post_monitor().
                 */
                close(gc->fd);
                gc->fd = -1;
                 */
                close(gc->fd);
                gc->fd = -1;
@@ -182,7 +182,7 @@ static int gc_close(struct grab_client *gc, int err)
        return 0;
 }
 
        return 0;
 }
 
-static int gc_post_select(__a_unused struct sched *s, void *context)
+static int gc_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct grab_client *gc = context;
        struct btr_node *btrn = gc->btrn;
 {
        struct grab_client *gc = context;
        struct btr_node *btrn = gc->btrn;
@@ -261,7 +261,7 @@ static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
 int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
 int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
-       struct grab_client *gc = para_calloc(sizeof(struct grab_client));
+       struct grab_client *gc = zalloc(sizeof(struct grab_client));
 
        ret = gc_check_args(lpr, gc);
        if (ret < 0)
 
        ret = gc_check_args(lpr, gc);
        if (ret < 0)
diff --git a/gui.c b/gui.c
index d779ff864ddebfc38a34ba1a0d166b5c04089556..66fb7870bd65868a750b5918747da59ad4681b6d 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -431,7 +431,7 @@ static void rb_add_entry(int color, char *msg)
 
        if (strwidth(msg, &len) < 0)
                return;
 
        if (strwidth(msg, &len) < 0)
                return;
-       new = para_malloc(sizeof(struct rb_entry));
+       new = alloc(sizeof(struct rb_entry));
        new->color = color;
        new->len = len;
        new->msg = msg;
        new->color = color;
        new->len = len;
        new->msg = msg;
@@ -609,19 +609,19 @@ static void clear_all_items(void)
        }
 }
 
        }
 }
 
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
 {
        struct status_task *st = context;
 
        if (st->fd >= 0)
 {
        struct status_task *st = context;
 
        if (st->fd >= 0)
-               para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(st->fd, s);
        if (task_get_notification(st->task) < 0)
                return sched_min_delay(s);
        if (st->fd < 0)
                sched_request_barrier_or_min_delay(&st->next_exec, s);
 }
 
        if (task_get_notification(st->task) < 0)
                return sched_min_delay(s);
        if (st->fd < 0)
                sched_request_barrier_or_min_delay(&st->next_exec, s);
 }
 
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct status_task *st = context;
        size_t sz;
 {
        struct status_task *st = context;
        size_t sz;
@@ -667,7 +667,7 @@ static int status_post_select(struct sched *s, void *context)
        }
        assert(st->loaded < st->bufsize);
        ret = read_nonblock(st->fd, st->buf + st->loaded,
        }
        assert(st->loaded < st->bufsize);
        ret = read_nonblock(st->fd, st->buf + st->loaded,
-               st->bufsize - st->loaded, &s->rfds, &sz);
+               st->bufsize - st->loaded, &sz);
        st->loaded += sz;
        ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
        if (ret < 0 || ret2 < 0) {
        st->loaded += sz;
        ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
        if (ret < 0 || ret2 < 0) {
@@ -892,9 +892,9 @@ static void reread_conf(void)
 }
 
 /* React to various signal-related events. */
 }
 
 /* React to various signal-related events. */
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
 {
 {
-       int ret = para_next_signal(&s->rfds);
+       int ret = para_next_signal();
 
        if (ret <= 0)
                return 0;
 
        if (ret <= 0)
                return 0;
@@ -931,18 +931,18 @@ static enum exec_status exec_status(void)
        return EXEC_IDLE;
 }
 
        return EXEC_IDLE;
 }
 
-static void exec_pre_select(struct sched *s, void *context)
+static void exec_pre_monitor(struct sched *s, void *context)
 {
        struct exec_task *et = context;
        if (exec_fds[0] >= 0)
 {
        struct exec_task *et = context;
        if (exec_fds[0] >= 0)
-               para_fd_set(exec_fds[0], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(exec_fds[0], s);
        if (exec_fds[1] >= 0)
        if (exec_fds[1] >= 0)
-               para_fd_set(exec_fds[1], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(exec_fds[1], s);
        if (task_get_notification(et->task) < 0)
                sched_min_delay(s);
 }
 
        if (task_get_notification(et->task) < 0)
                sched_min_delay(s);
 }
 
-static int exec_post_select(struct sched *s, void *context)
+static int exec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct exec_task *ct = context;
        int i, ret;
 {
        struct exec_task *ct = context;
        int i, ret;
@@ -963,7 +963,7 @@ static int exec_post_select(struct sched *s, void *context)
                        continue;
                ret = read_nonblock(exec_fds[i],
                        ct->command_buf[i] + ct->cbo[i],
                        continue;
                ret = read_nonblock(exec_fds[i],
                        ct->command_buf[i] + ct->cbo[i],
-                       COMMAND_BUF_SIZE - 1 - ct->cbo[i], &s->rfds, &sz);
+                       COMMAND_BUF_SIZE - 1 - ct->cbo[i], &sz);
                ct->cbo[i] += sz;
                sz = ct->cbo[i];
                ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
                ct->cbo[i] += sz;
                sz = ct->cbo[i];
                ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
@@ -992,10 +992,10 @@ static int exec_post_select(struct sched *s, void *context)
        return 0;
 }
 
        return 0;
 }
 
-static void input_pre_select(struct sched *s, __a_unused void *context)
+static void input_pre_monitor(struct sched *s, __a_unused void *context)
 {
        if (exec_status() != EXEC_XCMD)
 {
        if (exec_status() != EXEC_XCMD)
-               para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(STDIN_FILENO, s);
        if (window_update_needed())
                sched_min_delay(s);
 }
        if (window_update_needed())
                sched_min_delay(s);
 }
@@ -1089,7 +1089,7 @@ static void handle_command(int c)
                keyname);
 }
 
                keyname);
 }
 
-static int input_post_select(__a_unused struct sched *s,
+static int input_post_monitor(__a_unused struct sched *s,
                __a_unused void *context)
 {
        int ret;
                __a_unused void *context)
 {
        int ret;
@@ -1115,7 +1115,7 @@ static int input_post_select(__a_unused struct sched *s,
        ret = wgetch(top.win);
        if (ret == ERR)
                return 0;
        ret = wgetch(top.win);
        if (ret == ERR)
                return 0;
-       if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
+       if (ret == KEY_RESIZE) /* already handled in signal_post_monitor() */
                return 0;
        if (exs == EXEC_IDLE)
                handle_command(ret);
                return 0;
        if (exs == EXEC_IDLE)
                handle_command(ret);
@@ -1166,6 +1166,7 @@ static void com_cancel_scroll(void)
        }
        scroll_position = 0;
        redraw_bot_win();
        }
        scroll_position = 0;
        redraw_bot_win();
+       print_in_bar(COLOR_MSG, " ");
 }
 
 static void com_page_down(void)
 }
 
 static void com_page_down(void)
@@ -1267,6 +1268,12 @@ err_out:
        print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
 }
 
        print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
 }
 
+static void print_ll_msg(void)
+{
+       const char *sev[] = {SEVERITIES};
+       print_in_bar(COLOR_MSG, "new loglevel: %s\n", sev[loglevel]);
+}
+
 static void com_ll_decr(void)
 {
        if (loglevel <= LL_DEBUG) {
 static void com_ll_decr(void)
 {
        if (loglevel <= LL_DEBUG) {
@@ -1275,7 +1282,7 @@ static void com_ll_decr(void)
                return;
        }
        loglevel--;
                return;
        }
        loglevel--;
-       print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+       print_ll_msg();
 }
 
 static void com_ll_incr(void)
 }
 
 static void com_ll_incr(void)
@@ -1286,7 +1293,7 @@ static void com_ll_incr(void)
                return;
        }
        loglevel++;
                return;
        }
        loglevel++;
-       print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+       print_ll_msg();
 }
 
 static void com_reread_conf(void)
 }
 
 static void com_reread_conf(void)
@@ -1391,26 +1398,26 @@ static int setup_tasks_and_schedule(void)
        struct status_task status_task = {.fd = -1};
        struct input_task input_task = {.task = NULL};
        struct signal_task *signal_task;
        struct status_task status_task = {.fd = -1};
        struct input_task input_task = {.task = NULL};
        struct signal_task *signal_task;
-       struct sched sched = {.default_timeout = {.tv_sec = 1}};
+       struct sched sched = {.default_timeout = 1000};
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
-               .pre_select = exec_pre_select,
-               .post_select = exec_post_select,
+               .pre_monitor = exec_pre_monitor,
+               .post_monitor = exec_post_monitor,
                .context = &exec_task,
        }, &sched);
 
        status_task.task = task_register(&(struct task_info) {
                .name = "status",
                .context = &exec_task,
        }, &sched);
 
        status_task.task = task_register(&(struct task_info) {
                .name = "status",
-               .pre_select = status_pre_select,
-               .post_select = status_post_select,
+               .pre_monitor = status_pre_monitor,
+               .post_monitor = status_post_monitor,
                .context = &status_task,
        }, &sched);
 
        input_task.task = task_register(&(struct task_info) {
                .name = "input",
                .context = &status_task,
        }, &sched);
 
        input_task.task = task_register(&(struct task_info) {
                .name = "input",
-               .pre_select = input_pre_select,
-               .post_select = input_post_select,
+               .pre_monitor = input_pre_monitor,
+               .post_monitor = input_post_monitor,
                .context = &input_task,
        }, &sched);
 
                .context = &input_task,
        }, &sched);
 
@@ -1422,8 +1429,8 @@ static int setup_tasks_and_schedule(void)
        para_install_sighandler(SIGWINCH);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
        para_install_sighandler(SIGWINCH);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
        }, &sched);
        ret = schedule(&sched);
                .context = signal_task,
        }, &sched);
        ret = schedule(&sched);
index 6f4acf37a318834f95614ccc1365c12208048b90..81bbe0f6760b82f88fb9f97f45f2f5a42ed494ab 100644 (file)
@@ -122,16 +122,16 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_status_flags].align = LEFT;
        d[SI_status_flags].x = 11;
        d[SI_status_flags].y = 17;
        d[SI_status_flags].align = LEFT;
        d[SI_status_flags].x = 11;
        d[SI_status_flags].y = 17;
-       d[SI_status_flags].len = 10;
+       d[SI_status_flags].len = 8;
 
        d[SI_image_id].prefix = "img: ";
        d[SI_image_id].postfix = "";
        d[SI_image_id].color.fg = COLOR_RED;
        d[SI_image_id].color.bg = COLOR_BLACK;
        d[SI_image_id].align = CENTER;
 
        d[SI_image_id].prefix = "img: ";
        d[SI_image_id].postfix = "";
        d[SI_image_id].color.fg = COLOR_RED;
        d[SI_image_id].color.bg = COLOR_BLACK;
        d[SI_image_id].align = CENTER;
-       d[SI_image_id].x = 21;
+       d[SI_image_id].x = 19;
        d[SI_image_id].y = 17;
        d[SI_image_id].y = 17;
-       d[SI_image_id].len = 10;
+       d[SI_image_id].len = 12;
 
        d[SI_lyrics_id].prefix = "lyr: ";
        d[SI_lyrics_id].postfix = "";
 
        d[SI_lyrics_id].prefix = "lyr: ";
        d[SI_lyrics_id].postfix = "";
@@ -266,7 +266,7 @@ static void init_theme_colorful_blackness(struct gui_theme *t)
        d[SI_amplification].align = RIGHT;
        d[SI_amplification].x = 92;
        d[SI_amplification].y = 27;
        d[SI_amplification].align = RIGHT;
        d[SI_amplification].x = 92;
        d[SI_amplification].y = 27;
-       d[SI_amplification].len = 8;
+       d[SI_amplification].len = 9;
 
        d[SI_techinfo].prefix = "";
        d[SI_techinfo].postfix = "";
 
        d[SI_techinfo].prefix = "";
        d[SI_techinfo].postfix = "";
index 1fb60bad810d86dbbb0098a3714c2caa3d9e835b..8d2add19091f4894814c1aeeaeeefee5d5c8c19e 100644 (file)
@@ -56,17 +56,17 @@ static char *make_request_msg(void)
        return ret;
 }
 
        return ret;
 }
 
-static void http_recv_pre_select(struct sched *s, void *context)
+static void http_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
 
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
        if  (phd->status == HTTP_CONNECTED)
                return;
        if  (phd->status == HTTP_CONNECTED)
-               para_fd_set(rn->fd, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(rn->fd, s);
        else
        else
-               para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(rn->fd, s);
 }
 
 /*
 }
 
 /*
@@ -74,7 +74,7 @@ static void http_recv_pre_select(struct sched *s, void *context)
  * area with data read from the socket. In any case, update the state of the
  * connection if necessary.
  */
  * area with data read from the socket. In any case, update the state of the
  * connection if necessary.
  */
-static int http_recv_post_select(struct sched *s, void *context)
+static int http_recv_post_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
 {
        struct receiver_node *rn = context;
        struct private_http_recv_data *phd = rn->private_data;
@@ -93,7 +93,7 @@ static int http_recv_post_select(struct sched *s, void *context)
                return 0;
        if (phd->status == HTTP_CONNECTED) {
                char *rq;
                return 0;
        if (phd->status == HTTP_CONNECTED) {
                char *rq;
-               if (!FD_ISSET(rn->fd, &s->wfds))
+               if (!sched_write_ok(rn->fd, s))
                        return 0;
                rq = make_request_msg();
                PARA_INFO_LOG("sending http request\n");
                        return 0;
                rq = make_request_msg();
                PARA_INFO_LOG("sending http request\n");
@@ -105,7 +105,7 @@ static int http_recv_post_select(struct sched *s, void *context)
                return 0;
        }
        if (phd->status == HTTP_SENT_GET_REQUEST) {
                return 0;
        }
        if (phd->status == HTTP_SENT_GET_REQUEST) {
-               ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
+               ret = read_and_compare(rn->fd, HTTP_OK_MSG);
                if (ret < 0) {
                        PARA_ERROR_LOG("did not receive HTTP OK message\n");
                        goto out;
                if (ret < 0) {
                        PARA_ERROR_LOG("did not receive HTTP OK message\n");
                        goto out;
@@ -120,7 +120,7 @@ static int http_recv_post_select(struct sched *s, void *context)
        iovcnt = btr_pool_get_buffers(rn->btrp, iov);
        if (iovcnt == 0)
                goto out;
        iovcnt = btr_pool_get_buffers(rn->btrp, iov);
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
        if (num_bytes == 0)
                goto out;
        if (num_bytes <= iov[0].iov_len) /* only the first buffer was filled */
@@ -150,7 +150,7 @@ static int http_recv_open(struct receiver_node *rn)
        struct lls_parse_result *lpr = rn->lpr;
        const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
        uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
        struct lls_parse_result *lpr = rn->lpr;
        const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
        uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
-       int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
+       int fd, ret = para_connect(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
@@ -160,7 +160,7 @@ static int http_recv_open(struct receiver_node *rn)
                close(fd);
                return ret;
        }
                close(fd);
                return ret;
        }
-       rn->private_data = phd = para_calloc(sizeof(struct private_http_recv_data));
+       rn->private_data = phd = zalloc(sizeof(struct private_http_recv_data));
        rn->fd = fd;
        phd->status = HTTP_CONNECTED;
        rn->btrp = btr_pool_new("http_recv", 320 * 1024);
        rn->fd = fd;
        phd->status = HTTP_CONNECTED;
        rn->btrp = btr_pool_new("http_recv", 320 * 1024);
@@ -170,6 +170,6 @@ static int http_recv_open(struct receiver_node *rn)
 const struct receiver lsg_recv_cmd_com_http_user_data = {
        .open = http_recv_open,
        .close = http_recv_close,
 const struct receiver lsg_recv_cmd_com_http_user_data = {
        .open = http_recv_open,
        .close = http_recv_close,
-       .pre_select = http_recv_pre_select,
-       .post_select = http_recv_post_select,
+       .pre_monitor = http_recv_pre_monitor,
+       .post_monitor = http_recv_post_monitor,
 };
 };
index c6b9decca941489c82b629982ddb6d6dbdb2ea33..429b4662a70497d522fd68a0e3467941e7edc959 100644 (file)
@@ -20,8 +20,8 @@
 #include "server.h"
 #include "http.h"
 #include "list.h"
 #include "server.h"
 #include "http.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "close_on_fork.h"
 #include "fd.h"
 #include "vss.h"
 #include "close_on_fork.h"
 #include "fd.h"
@@ -158,7 +158,7 @@ static void http_send(long unsigned current_chunk,
        }
 }
 
        }
 }
 
-static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
+static void http_post_monitor(__a_unused struct sched *s)
 {
        struct sender_client *sc, *tmp;
        struct private_http_sender_data *phsd;
 {
        struct sender_client *sc, *tmp;
        struct private_http_sender_data *phsd;
@@ -170,7 +170,7 @@ static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                case HTTP_STREAMING: /* nothing to do */
                        break;
                case HTTP_CONNECTED: /* need to recv get request */
                case HTTP_STREAMING: /* nothing to do */
                        break;
                case HTTP_CONNECTED: /* need to recv get request */
-                       ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE, rfds);
+                       ret = read_and_compare(sc->fd, HTTP_GET_MSG);
                        if (ret < 0)
                                phsd->status = HTTP_INVALID_GET_REQUEST;
                        else if (ret > 0) {
                        if (ret < 0)
                                phsd->status = HTTP_INVALID_GET_REQUEST;
                        else if (ret > 0) {
@@ -188,15 +188,15 @@ static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
                        break;
                }
        }
                        break;
                }
        }
-       sc = accept_sender_client(hss, rfds);
+       sc = accept_sender_client(hss);
        if (!sc)
                return;
        if (!sc)
                return;
-       phsd = para_malloc(sizeof(*phsd));
+       phsd = alloc(sizeof(*phsd));
        sc->private_data = phsd;
        phsd->status = HTTP_CONNECTED;
 }
 
        sc->private_data = phsd;
        phsd->status = HTTP_CONNECTED;
 }
 
-static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
+static void http_pre_monitor(struct sched *s)
 {
        struct sender_client *sc, *tmp;
        unsigned n;
 {
        struct sender_client *sc, *tmp;
        unsigned n;
@@ -204,15 +204,15 @@ static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
        FOR_EACH_LISTEN_FD(n, hss) {
                if (hss->listen_fds[n] < 0)
                        continue;
        FOR_EACH_LISTEN_FD(n, hss) {
                if (hss->listen_fds[n] < 0)
                        continue;
-               para_fd_set(hss->listen_fds[n], rfds, max_fileno);
+               sched_monitor_readfd(hss->listen_fds[n], s);
        }
        list_for_each_entry_safe(sc, tmp, &hss->client_list, node) {
                struct private_http_sender_data *phsd = sc->private_data;
                if (phsd->status == HTTP_CONNECTED) /* need to recv get request */
        }
        list_for_each_entry_safe(sc, tmp, &hss->client_list, node) {
                struct private_http_sender_data *phsd = sc->private_data;
                if (phsd->status == HTTP_CONNECTED) /* need to recv get request */
-                       para_fd_set(sc->fd, rfds, max_fileno);
+                       sched_monitor_readfd(sc->fd, s);
                if (phsd->status == HTTP_GOT_GET_REQUEST ||
                                phsd->status == HTTP_INVALID_GET_REQUEST)
                if (phsd->status == HTTP_GOT_GET_REQUEST ||
                                phsd->status == HTTP_INVALID_GET_REQUEST)
-                       para_fd_set(sc->fd, wfds, max_fileno);
+                       sched_monitor_writefd(sc->fd, s);
        }
 }
 
        }
 }
 
@@ -274,8 +274,8 @@ const struct sender http_sender = {
        .name = "http",
        .init = http_send_init,
        .shutdown = http_shutdown,
        .name = "http",
        .init = http_send_init,
        .shutdown = http_shutdown,
-       .pre_select = http_pre_select,
-       .post_select = http_post_select,
+       .pre_monitor = http_pre_monitor,
+       .post_monitor = http_post_monitor,
        .send = http_send,
        .shutdown_clients = http_shutdown_clients,
        .client_cmds = {
        .send = http_send,
        .shutdown_clients = http_shutdown_clients,
        .client_cmds = {
diff --git a/imdct.c b/imdct.c
index 93577b5451a0cbb8c8da58e997a9f4d77179243e..2e1089f1f818be4bcdeb20be3d4d7a865943e00e 100644 (file)
--- a/imdct.c
+++ b/imdct.c
@@ -336,7 +336,7 @@ static int fft_init(struct fft_context *s, int nbits)
        s->nbits = nbits;
        n = 1 << nbits;
 
        s->nbits = nbits;
        n = 1 << nbits;
 
-       s->revtab = para_malloc(n * sizeof(uint16_t));
+       s->revtab = arr_alloc(n, sizeof(uint16_t));
        for (j = 4; j <= nbits; j++) {
                int k = 1 << j;
                double freq = 2 * M_PI / k;
        for (j = 4; j <= nbits; j++) {
                int k = 1 << j;
                double freq = 2 * M_PI / k;
@@ -366,13 +366,13 @@ int imdct_init(int nbits, struct mdct_context **result)
        double alpha;
        struct mdct_context *s;
 
        double alpha;
        struct mdct_context *s;
 
-       s = para_calloc(sizeof(*s));
+       s = zalloc(sizeof(*s));
        n = 1 << nbits;
        s->nbits = nbits;
        s->n = n;
        n4 = n >> 2;
        n = 1 << nbits;
        s->nbits = nbits;
        s->n = n;
        n4 = n >> 2;
-       s->tcos = para_malloc(n4 * sizeof(fftsample_t));
-       s->tsin = para_malloc(n4 * sizeof(fftsample_t));
+       s->tcos = arr_alloc(n4, sizeof(fftsample_t));
+       s->tsin = arr_alloc(n4, sizeof(fftsample_t));
 
        for (i = 0; i < n4; i++) {
                alpha = 2 * M_PI * (i + 1.0 / 8.0) / n;
 
        for (i = 0; i < n4; i++) {
                alpha = 2 * M_PI * (i + 1.0 / 8.0) / n;
index 8c4545b476e8f792ea7bba6f88b4e83fb952e540..1376cf1d72739b3f7c9c3218dd96666a5d475812 100644 (file)
@@ -189,8 +189,6 @@ static char **i9e_completer(const char *text, int start, __a_unused int end)
  *
  * This function attaches the i9e input queue to an output queue of \a
  * producer.
  *
  * This function attaches the i9e input queue to an output queue of \a
  * producer.
- *
- * \return Standard.
  */
 void i9e_attach_to_stdout(struct btr_node *producer)
 {
  */
 void i9e_attach_to_stdout(struct btr_node *producer)
 {
@@ -258,18 +256,6 @@ static void clear_bottom_line(void)
        rl_point = point;
 }
 
        rl_point = point;
 }
 
-static bool input_available(void)
-{
-       fd_set rfds;
-       struct timeval tv = {0, 0};
-       int ret;
-
-       FD_ZERO(&rfds);
-       FD_SET(i9ep->ici->fds[0], &rfds);
-       ret = para_select(1, &rfds, NULL, &tv);
-       return ret > 0;
-}
-
 static void i9e_line_handler(char *line)
 {
        int ret;
 static void i9e_line_handler(char *line)
 {
        int ret;
@@ -294,14 +280,14 @@ free_line:
        free(line);
 }
 
        free(line);
 }
 
-static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int i9e_post_monitor(__a_unused struct sched *s, __a_unused void *context)
 {
        int ret;
        struct i9e_client_info *ici = i9ep->ici;
        char *buf;
        size_t sz, consumed = 0;
 
 {
        int ret;
        struct i9e_client_info *ici = i9ep->ici;
        char *buf;
        size_t sz, consumed = 0;
 
-       ret = -E_I9E_EOF;
+       ret = -E_EOF;
        if (i9ep->input_eof)
                goto rm_btrn;
        ret = -E_I9E_TERM_RQ;
        if (i9ep->input_eof)
                goto rm_btrn;
        ret = -E_I9E_TERM_RQ;
@@ -310,24 +296,29 @@ static int i9e_post_select(__a_unused struct sched *s, __a_unused void *context)
        ret = 0;
        if (i9ep->caught_sigint)
                goto rm_btrn;
        ret = 0;
        if (i9ep->caught_sigint)
                goto rm_btrn;
-       while (input_available()) {
+       while (read_ok(i9ep->ici->fds[0]) > 0) {
                if (i9ep->stdout_btrn) {
                if (i9ep->stdout_btrn) {
-                       unsigned len = i9ep->key_sequence_length;
-                       assert(len < sizeof(i9ep->key_sequence) - 1);
-                       buf = i9ep->key_sequence + len;
-                       ret = read(i9ep->ici->fds[0], buf, 1);
-                       if (ret < 0) {
-                               ret = -ERRNO_TO_PARA_ERROR(errno);
-                               goto rm_btrn;
+                       while (i9ep->key_sequence_length < sizeof(i9ep->key_sequence) - 1) {
+                               buf = i9ep->key_sequence + i9ep->key_sequence_length;
+                               ret = read(i9ep->ici->fds[0], buf, 1);
+                               if (ret < 0) {
+                                       ret = -ERRNO_TO_PARA_ERROR(errno);
+                                       goto rm_btrn;
+                               }
+                               if (ret == 0) {
+                                       ret = -E_EOF;
+                                       goto rm_btrn;
+                               }
+                               buf[1] = '\0';
+                               i9ep->key_sequence_length++;
+                               rl_stuff_char((int)(unsigned char)*buf);
+                               rl_callback_read_char();
+                               if (read_ok(i9ep->ici->fds[0]) <= 0)
+                                       break;
                        }
                        }
-                       ret = -E_I9E_EOF;
-                       if (ret == 0)
-                               goto rm_btrn;
-                       buf[1] = '\0';
-                       i9ep->key_sequence_length++;
-                       rl_stuff_char((int)(unsigned char)*buf);
-               }
-               rl_callback_read_char();
+                       i9ep->key_sequence_length = 0;
+               } else
+                       rl_callback_read_char();
                ret = 0;
        }
        if (!i9ep->stdout_btrn)
                ret = 0;
        }
        if (!i9ep->stdout_btrn)
@@ -369,7 +360,7 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-static void i9e_pre_select(struct sched *s, __a_unused void *context)
+static void i9e_pre_monitor(struct sched *s, __a_unused void *context)
 {
        int ret;
 
 {
        int ret;
 
@@ -384,7 +375,7 @@ static void i9e_pre_select(struct sched *s, __a_unused void *context)
                        return;
                }
                if (ret > 0)
                        return;
                }
                if (ret > 0)
-                       para_fd_set(i9ep->ici->fds[1], &s->wfds, &s->max_fileno);
+                       sched_monitor_writefd(i9ep->ici->fds[1], s);
        }
        /*
         * fd[0] might have been reset to blocking mode if our job was moved to
        }
        /*
         * fd[0] might have been reset to blocking mode if our job was moved to
@@ -395,7 +386,7 @@ static void i9e_pre_select(struct sched *s, __a_unused void *context)
        if (ret < 0)
                PARA_WARNING_LOG("set to nonblock failed: (fd0 %d, %s)\n",
                        i9ep->ici->fds[0], para_strerror(-ret));
        if (ret < 0)
                PARA_WARNING_LOG("set to nonblock failed: (fd0 %d, %s)\n",
                        i9ep->ici->fds[0], para_strerror(-ret));
-       para_fd_set(i9ep->ici->fds[0], &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(i9ep->ici->fds[0], s);
 }
 
 static void update_winsize(void)
 }
 
 static void update_winsize(void)
@@ -472,8 +463,8 @@ int i9e_open(struct i9e_client_info *ici, struct sched *s)
                return ret;
        i9ep->task = task_register(&(struct task_info) {
                .name = "i9e",
                return ret;
        i9ep->task = task_register(&(struct task_info) {
                .name = "i9e",
-               .pre_select = i9e_pre_select,
-               .post_select = i9e_post_select,
+               .pre_monitor = i9e_pre_monitor,
+               .post_monitor = i9e_post_monitor,
                .context = i9ep,
        }, s);
 
                .context = i9ep,
        }, s);
 
@@ -600,23 +591,21 @@ void i9e_signal_dispatch(int sig_num)
 }
 
 /**
 }
 
 /**
- * Wrapper for select(2) which does not restart on interrupts.
+ * Wrapper for poll(2) which handles EINTR and returns paraslash error codes.
  *
  *
- * \param n \sa \ref para_select().
- * \param readfds \sa \ref para_select().
- * \param writefds \sa \ref para_select().
- * \param timeout_tv \sa \ref para_select().
+ * \param fds See poll(2).
+ * \param nfds See poll(2).
+ * \param timeout See poll(2).
  *
  *
- * \return \sa \ref para_select().
+ * \return See poll(2).
  *
  *
- * The only difference between this function and \ref para_select() is that
- * \ref i9e_select() returns zero if the select call returned \p EINTR.
+ * The only difference between this function and \ref xpoll() is that \ref
+ * i9e_poll() returns zero if the system call was interrupted while xpoll()
+ * restarts the system call in this case.
  */
  */
-int i9e_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
+int i9e_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
 {
-       int ret = select(n, readfds, writefds, NULL, timeout_tv);
-
+       int ret = poll(fds, nfds, timeout);
        if (ret < 0) {
                if (errno == EINTR)
                        ret = 0;
        if (ret < 0) {
                if (errno == EINTR)
                        ret = 0;
@@ -645,7 +634,7 @@ int i9e_select(int n, fd_set *readfds, fd_set *writefds,
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result)
 {
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result)
 {
-       char **matches = para_malloc(sizeof(char *));
+       char **matches = alloc(sizeof(char *));
        int match_count = 0, matches_len = 1;
        char **p;
        int len = strlen(word);
        int match_count = 0, matches_len = 1;
        char **p;
        int len = strlen(word);
@@ -656,8 +645,8 @@ int i9e_extract_completions(const char *word, char **string_list,
                match_count++;
                if (match_count >= matches_len) {
                        matches_len *= 2;
                match_count++;
                if (match_count >= matches_len) {
                        matches_len *= 2;
-                       matches = para_realloc(matches,
-                               matches_len * sizeof(char *));
+                       matches = arr_realloc(matches, matches_len,
+                               sizeof(char *));
                }
                matches[match_count - 1] = para_strdup(*p);
        }
                }
                matches[match_count - 1] = para_strdup(*p);
        }
@@ -693,7 +682,7 @@ char **i9e_complete_commands(const char *word, struct i9e_completer *completers)
                if (is_prefix(word, cmd, len))
                        match_count++;
        }
                if (is_prefix(word, cmd, len))
                        match_count++;
        }
-       matches = para_malloc((match_count + 1) * sizeof(*matches));
+       matches = arr_alloc(match_count + 1, sizeof(*matches));
        for (i = 0, match_count = 0; (cmd = completers[i].name); i++)
                if (is_prefix(word, cmd, len))
                        matches[match_count++] = para_strdup(cmd);
        for (i = 0, match_count = 0; (cmd = completers[i].name); i++)
                if (is_prefix(word, cmd, len))
                        matches[match_count++] = para_strdup(cmd);
@@ -777,7 +766,7 @@ int i9e_print_completions(struct i9e_completer *completers)
        if (*p == ' ')
                p++;
        n = end - p + 1;
        if (*p == ' ')
                p++;
        n = end - p + 1;
-       ci.word = para_malloc(n + 1);
+       ci.word = alloc(n + 1);
        strncpy(ci.word, p, n);
        ci.word[n] = '\0';
 create_matches:
        strncpy(ci.word, p, n);
        ci.word[n] = '\0';
 create_matches:
@@ -805,3 +794,25 @@ create_matches:
        free(ci.word);
        return ret;
 }
        free(ci.word);
        return ret;
 }
+
+/**
+ * Complete on severity strings.
+ *
+ * \param ci See struct \ref i9e_completer.
+ * \param cr See struct \ref i9e_completer.
+ *
+ * This is used by para_client and para_audioc which need the same completion
+ * primitive for the ll server/audiod command. Both define their own completer
+ * which is implemented as a trivial wrapper that calls this function.
+ */
+void i9e_ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       char *sev[] = {SEVERITIES, NULL};
+
+       if (ci->word_num != 1) {
+               cr->matches = NULL;
+               return;
+       }
+       i9e_extract_completions(ci->word, sev, &cr->matches);
+}
index ddf02d76d2bc44a71133844fa0dddf4d9db03778..6ef7f8e259b0045a5c808c01560b5a81166b01fd 100644 (file)
@@ -84,8 +84,7 @@ void i9e_print_status_bar(char *buf, unsigned len);
 void i9e_close(void);
 void i9e_signal_dispatch(int sig_num);
 __printf_2_3 void i9e_log(int ll, const char* fmt,...);
 void i9e_close(void);
 void i9e_signal_dispatch(int sig_num);
 __printf_2_3 void i9e_log(int ll, const char* fmt,...);
-int i9e_select(int n, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv);
+int i9e_poll(struct pollfd *fds, nfds_t nfds, int timeout);
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result);
 char **i9e_complete_commands(const char *word, struct i9e_completer *completers);
 int i9e_extract_completions(const char *word, char **string_list,
                char ***result);
 char **i9e_complete_commands(const char *word, struct i9e_completer *completers);
@@ -93,3 +92,5 @@ void i9e_complete_option(char **opts, struct i9e_completion_info *ci,
                struct i9e_completion_result *cr);
 int i9e_print_completions(struct i9e_completer *completers);
 int i9e_get_error(void);
                struct i9e_completion_result *cr);
 int i9e_print_completions(struct i9e_completer *completers);
 int i9e_get_error(void);
+void i9e_ll_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr);
index 80ae2e42be9773f0e619e1746b9bfdefbe02f2c4..bd2a23e2354dde09cc8090a08ccf6ad31d7ac9b9 100644 (file)
@@ -51,6 +51,8 @@ caption = list of audiod commands
                short_opt = o
                summary = One-shot mode: Stop grabbing if audio file changes
 
                short_opt = o
                summary = One-shot mode: Stop grabbing if audio file changes
 
+m4_include(`com_ll.m4')
+
 [subcommand off]
        purpose = deactivate para_audiod
        [description]
 [subcommand off]
        purpose = deactivate para_audiod
        [description]
diff --git a/m4/lls/include/com_ll.m4 b/m4/lls/include/com_ll.m4
new file mode 100644 (file)
index 0000000..d7576ea
--- /dev/null
@@ -0,0 +1,10 @@
+[subcommand ll]
+       purpose = Query or set the log level of the daemon
+       m4_ifelse(SUITE, `server_cmd', `aux_info = NO_PERMISSION_REQUIRED')
+       non-opts-name = [severity]
+       [description]
+               If no argument is given, the command prints the severity string (one
+               of the possible string arguments to --loglevel) which corresponds to
+               the current loglevel. Otherwise, if the given argument is a severity
+               string, the current log level is set accordingly.
+       [/description]
index daf6de92525d334d3c1791e4d0b53907ddd7fb7f..a13f079e9b60195240bb48f3b847978263a486bc 100644 (file)
@@ -11,6 +11,7 @@ $(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
        $(call SAY, M4 $<)
        $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
                -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
        $(call SAY, M4 $<)
        $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
                -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+               -D SUITE=$(basename $(notdir $<)) \
                $< > $@
 
 $(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
                $< > $@
 
 $(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
index 3019f7693a1d986f53cfcea45d13e572a4295a5f..e366a201569e538bc374f5356bf3d1c957164955 100644 (file)
@@ -147,11 +147,10 @@ caption = List of subcommands
 [subcommand sleep]
        purpose = stream, fade out, sleep, fade in
        [description]
 [subcommand sleep]
        purpose = stream, fade out, sleep, fade in
        [description]
-               Change to the initial volume and select the initial mood/playlist.
-               Fade out to the given fade-out volume in the specified time. Switch
-               to the sleep mood/playlist and wait until wake time minus fade-in
-               time. Finally, switch to the wake mood/playlist and fade in to the
-               fade-in volume.
+               Set the initial volume and mood, start playing and sleep. Then switch
+               to the fade-out mood and fade to the fade-out volume. Next, switch to
+               the sleep mood and wait until wake time minus fade-in time. Finally,
+               switch to the wake mood and fade in to the fade-in volume.
        [/description]
        [option ivol]
                summary = set initial volume
        [/description]
        [option ivol]
                summary = set initial volume
@@ -165,6 +164,26 @@ caption = List of subcommands
                        channel part may be omitted, in which case the default channel is
                        used. This option may be given multiple times.
                [/help]
                        channel part may be omitted, in which case the default channel is
                        used. This option may be given multiple times.
                [/help]
+       [option initial-mood]
+               summary = mood or playlist to start with
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               [help]
+                       This mood or playlist is selected right after setting the initial
+                       volume and before fade-out starts. If unset, fade-out starts
+                       immediately.
+               [/help]
+       [option initial-delay]
+               summary = time before fade-out starts.
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 0
+               [help]
+                       If left at the default, no initial delay occurs even if an initial
+                       mood is given.
+               [/help]
        [option fo-mood]
                summary = mood or playlist for fade-out
                arg_info = required_arg
        [option fo-mood]
                summary = mood or playlist for fade-out
                arg_info = required_arg
index f73d66b26a713ce70ed144e38362a73f5ddcf8c1..02afaabb5252a0580711547ad399cef932cfcadc 100644 (file)
@@ -169,6 +169,8 @@ aux_info_prefix = Permissions:
                playlists. Otherwise only the given tables are created.
        [/description]
 
                playlists. Otherwise only the given tables are created.
        [/description]
 
+m4_include(`com_ll.m4')
+
 [subcommand jmp]
        purpose = reposition the current stream
        non-opts-name = n
 [subcommand jmp]
        purpose = reposition the current stream
        non-opts-name = n
@@ -220,6 +222,16 @@ aux_info_prefix = Permissions:
                        also given), chunk time and chunk offsets.
 
                [/help]
                        also given), chunk time and chunk offsets.
 
                [/help]
+       [option limit]
+               short_opt = L
+               summary = list at most this many files
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       An argument of zero means "unlimited". This is also the default which
+                       applies if the option is not given.
+               [/help]
        [option basename]
                short_opt = b
                summary = list and match basenames only
        [option basename]
                short_opt = b
                summary = list and match basenames only
@@ -231,9 +243,19 @@ aux_info_prefix = Permissions:
        [option admissible]
                short_opt = a
                summary = list only admissible files
        [option admissible]
                short_opt = a
                summary = list only admissible files
+               arg_type = string
+               arg_info = optional_arg
+               typestr = specifier/name
+               default_val = .
                [help]
                [help]
-                       List only files which are admissible with respect to the current mood
-                       or playlist.
+                       If the optional argument is supplied, it must be of the form "p/foo"
+                       or "m/bar" (which refer to the playlist named "foo" and the mood named
+                       "bar", respectively). The command then restricts its output to the set
+                       of files which are admissible with respect to the thusly identified
+                       mood or playlist.
+
+                       If no argument is given, or if the argument is the special value ".",
+                       the current mood or playlist is assumed.
                [/help]
        [option reverse]
                short_opt = r
                [/help]
        [option reverse]
                short_opt = r
@@ -405,6 +427,9 @@ aux_info_prefix = Permissions:
 
                activates the mood named 'foo'.
        [/description]
 
                activates the mood named 'foo'.
        [/description]
+       [option verbose]
+               short_opt = v
+               summary = print information about the loaded mood or playlist
 
 [subcommand sender]
        purpose = control paraslash senders
 
 [subcommand sender]
        purpose = control paraslash senders
diff --git a/mixer.c b/mixer.c
index eae8929164770053c5c18843f4cf4e4e8bc5e616..dda7fc1df729722266db7136733a28ac2391e977 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -187,11 +187,11 @@ static int com_fade(const struct mixer *m)
 }
 EXPORT_CMD(fade);
 
 }
 EXPORT_CMD(fade);
 
-static void client_cmd(const char *cmd)
+static void run(const char *exe, const char *cmd)
 {
        int ret, status, fds[3] = {0, 0, 0};
        pid_t pid;
 {
        int ret, status, fds[3] = {0, 0, 0};
        pid_t pid;
-       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+       char *cmdline = make_message("%s %s", exe, cmd);
 
        PARA_NOTICE_LOG("%s\n", cmdline);
        ret = para_exec_cmdline_pid(&pid, cmdline, fds);
 
        PARA_NOTICE_LOG("%s\n", cmdline);
        ret = para_exec_cmdline_pid(&pid, cmdline, fds);
@@ -215,6 +215,16 @@ fail:
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
+static void client_cmd(const char *cmd)
+{
+       run(BINDIR "/para_client", cmd);
+}
+
+static void audioc_cmd(const char *cmd)
+{
+       run(BINDIR "/para_audioc", cmd);
+}
+
 static void change_afs_mode(const char *afs_mode)
 {
        char *cmd;
 static void change_afs_mode(const char *afs_mode)
 {
        char *cmd;
@@ -263,6 +273,20 @@ static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
        return 1;
 }
 
        return 1;
 }
 
+static void stop(const struct mixer *m, struct mixer_handle *h)
+{
+       int ret, old_vol = 0;
+
+       ret = m->get(h);
+       if (ret > 0)
+               old_vol = ret;
+       fade(m, h, 0, 3);
+       audioc_cmd("off");
+       client_cmd("stop");
+       audioc_cmd("on");
+       m->set(h, old_vol);
+}
+
 static int com_sleep(const struct mixer *m)
 {
        time_t t1, wake_time_epoch;
 static int com_sleep(const struct mixer *m)
 {
        time_t t1, wake_time_epoch;
@@ -270,6 +294,7 @@ static int com_sleep(const struct mixer *m)
        struct tm *tm;
        int ret;
        const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
        struct tm *tm;
        int ret;
        const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+       const char *initial_mood = OPT_STRING_VAL(SLEEP, INITIAL_MOOD);
        const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
        const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
        const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
        const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
        const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
        const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
@@ -316,17 +341,27 @@ static int com_sleep(const struct mixer *m)
        }
        wake_time_epoch = mktime(tm);
        PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
        }
        wake_time_epoch = mktime(tm);
        PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
-       client_cmd("stop");
-       sleep(1);
+       stop(m, h);
+       ret = set_initial_volume(m, h);
+       if (ret < 0)
+               goto close_mixer;
+       /*
+        * Setting the volume invalidates the current channel setting, so we
+        * have to set it again.
+        */
+       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret < 0)
+               goto close_mixer;
+       delay = OPT_UINT32_VAL(SLEEP, INITIAL_DELAY);
+       if (delay > 0 && initial_mood && *initial_mood) {
+               change_afs_mode(initial_mood);
+               client_cmd("play");
+               sleep(delay);
+               stop(m, h);
+       }
        if (fot && fo_mood && *fo_mood) {
        if (fot && fo_mood && *fo_mood) {
-               ret = set_initial_volume(m, h);
-               if (ret < 0)
-                       goto close_mixer;
                change_afs_mode(fo_mood);
                client_cmd("play");
                change_afs_mode(fo_mood);
                client_cmd("play");
-               ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
-               if (ret < 0)
-                       goto close_mixer;
                ret = fade(m, h, fov, fot);
                if (ret < 0)
                        goto close_mixer;
                ret = fade(m, h, fov, fot);
                if (ret < 0)
                        goto close_mixer;
@@ -340,7 +375,7 @@ static int com_sleep(const struct mixer *m)
                if (!fot || !fo_mood) /* currently stopped */
                        client_cmd("play");
        } else if (fot && fo_mood && *fo_mood) /* currently playing */
                if (!fot || !fo_mood) /* currently stopped */
                        client_cmd("play");
        } else if (fot && fo_mood && *fo_mood) /* currently playing */
-               client_cmd("stop");
+               stop(m, h);
        m->close(&h);
        if (!fit || !fi_mood || !*fi_mood) /* nothing to do */
                return 1;
        m->close(&h);
        if (!fit || !fi_mood || !*fi_mood) /* nothing to do */
                return 1;
@@ -355,13 +390,12 @@ static int com_sleep(const struct mixer *m)
                sleep(delay);
        }
        change_afs_mode(fi_mood);
                sleep(delay);
        }
        change_afs_mode(fi_mood);
-       if (sleep_mood && *sleep_mood) /* currently playing */
-               client_cmd("next");
-       else /* currently stopped */
-               client_cmd("play");
        ret = open_mixer_and_set_channel(m, &h);
        if (ret < 0)
                return ret;
        ret = open_mixer_and_set_channel(m, &h);
        if (ret < 0)
                return ret;
+       if (sleep_mood && *sleep_mood) /* currently playing */
+               stop(m, h);
+       client_cmd("play");
        ret = fade(m, h, fiv, fit);
 close_mixer:
        m->close(&h);
        ret = fade(m, h, fiv, fit);
 close_mixer:
        m->close(&h);
diff --git a/mood.c b/mood.c
index a228b2432f9b56d3bc51535c13d74094314e7da9..1e15ef0e081480381fcbc4fdad2179848f429c1f 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -12,7 +12,6 @@
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
 #include "afh.h"
 #include "afs.h"
 #include "list.h"
-#include "mood.h"
 
 /*
  * Mood parser API. It's overkill to have an own header file for
 
 /*
  * Mood parser API. It's overkill to have an own header file for
@@ -47,20 +46,31 @@ struct afs_statistics {
        /** Number of admissible files */
        unsigned num;
 };
        /** Number of admissible files */
        unsigned num;
 };
-static struct afs_statistics statistics = {.normalization_divisor = 1};
 
 
-struct mood {
-       /** The name of this mood. */
+/**
+ * Stores an instance of a loaded mood (parser and statistics).
+ *
+ * A structure of this type is allocated and initialized when a mood is loaded.
+ */
+struct mood_instance {
+       /** NULL means that this is the "dummy" mood. */
        char *name;
        char *name;
-       /** Info for the bison parser. */
+       /** Bison's abstract syntax tree, used to determine admissibility. */
        struct mp_context *parser_context;
        struct mp_context *parser_context;
+       /** To compute the score. */
+       struct afs_statistics stats;
+       /** NULL means to operate on the global score table. */
+       struct osl_table *score_table;
 };
 
 /*
 };
 
 /*
- * If current_mood is NULL then no mood is currently open. If
- * current_mood->name is NULL, the dummy mood is currently open.
+ * If current_mood is NULL then no mood is currently loaded. If
+ * current_mood->name is NULL, the current mood is the dummy mood.
+ *
+ * The statistics are adjusted dynamically through this pointer as files are
+ * added, removed or played.
  */
  */
-static struct mood *current_mood;
+static struct mood_instance *current_mood;
 
 /*
  * Find the position of the most-significant set bit.
 
 /*
  * Find the position of the most-significant set bit.
@@ -119,49 +129,47 @@ __a_const static uint64_t int_sqrt(uint64_t x)
        return res;
 }
 
        return res;
 }
 
-/* returns 1 if row admissible, 0 if not, negative on errors */
-static int row_is_admissible(const struct osl_row *aft_row, struct mood *m)
-{
-       if (!m)
-               return -E_NO_MOOD;
-       return mp_eval_row(aft_row, m->parser_context);
-}
-
-static void destroy_mood(struct mood *m)
+static void destroy_mood(struct mood_instance *m)
 {
        if (!m)
                return;
        mp_shutdown(m->parser_context);
 {
        if (!m)
                return;
        mp_shutdown(m->parser_context);
+       if (m->score_table)
+               score_close(m->score_table);
        free(m->name);
        free(m);
 }
 
        free(m->name);
        free(m);
 }
 
-static struct mood *alloc_new_mood(const char *name)
+static struct mood_instance *alloc_new_mood(const char *name)
 {
 {
-       struct mood *m = para_calloc(sizeof(struct mood));
+       struct mood_instance *m = zalloc(sizeof(*m));
+
        if (name)
                m->name = para_strdup(name);
        if (name)
                m->name = para_strdup(name);
+       m->stats.normalization_divisor = 1;
        return m;
 }
 
        return m;
 }
 
-static int load_mood(const struct osl_row *mood_row, struct mood **m,
-               char **errmsg)
+static int init_mood_parser(const char *mood_name, struct mood_instance **m,
+               char **err)
 {
 {
-       char *mood_name;
        struct osl_object mood_def;
        int ret;
 
        struct osl_object mood_def;
        int ret;
 
-       ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
+       if (!*mood_name) {
+               if (err)
+                       *err = make_message("empty mood name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = mood_get_def_by_name(mood_name, &mood_def);
        if (ret < 0) {
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message(
-                               "could not read mood definition");
+               if (err)
+                       *err = make_message("could not read mood definition\n");
                return ret;
        }
                return ret;
        }
-       assert(*mood_name);
        *m = alloc_new_mood(mood_name);
        *m = alloc_new_mood(mood_name);
-       PARA_INFO_LOG("opening mood %s\n", mood_name);
-       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, errmsg);
+       PARA_INFO_LOG("loading mood %s\n", mood_name);
+       ret = mp_init(mood_def.data, mood_def.size, &(*m)->parser_context, err);
        osl_close_disk_object(&mood_def);
        if (ret < 0)
                destroy_mood(*m);
        osl_close_disk_object(&mood_def);
        if (ret < 0)
                destroy_mood(*m);
@@ -170,14 +178,14 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m,
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
 
 static int check_mood(struct osl_row *mood_row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        char *mood_name, *errmsg;
        struct osl_object mood_def;
        char *mood_name, *errmsg;
        struct osl_object mood_def;
-       struct mood *m;
+       struct mood_instance *m;
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
        int ret = mood_get_name_and_def_by_row(mood_row, &mood_name, &mood_def);
 
        if (ret < 0) {
-               para_printf(pb, "cannot read mood\n");
+               afs_error(aca, "cannot read mood\n");
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
                return ret;
        }
        if (!*mood_name) /* ignore dummy row */
@@ -186,9 +194,9 @@ static int check_mood(struct osl_row *mood_row, void *data)
        ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
                &errmsg);
        if (ret < 0) {
        ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
                &errmsg);
        if (ret < 0) {
-               para_printf(pb, "%s: %s\n", mood_name, errmsg);
+               afs_error(aca, "%s: %s\n%s\n", mood_name, errmsg,
+                       para_strerror(-ret));
                free(errmsg);
                free(errmsg);
-               para_printf(pb, "%s\n", para_strerror(-ret));
        } else
                destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
        } else
                destroy_mood(m);
        ret = 1; /* don't fail the loop on invalid mood definitions */
@@ -200,7 +208,7 @@ out:
 /**
  * Check all moods for syntax errors.
  *
 /**
  * Check all moods for syntax errors.
  *
- * \param aca Only ->pbout is used for diagnostics.
+ * \param aca Output goes to ->pbout, errors to ->fd on the error band.
  *
  * \return Negative on fatal errors. Inconsistent mood definitions are not
  * considered an error.
  *
  * \return Negative on fatal errors. Inconsistent mood definitions are not
  * considered an error.
@@ -208,8 +216,7 @@ out:
 int mood_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking moods...\n");
 int mood_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking moods...\n");
-       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, &aca->pbout,
-               check_mood));
+       return osl(osl_rbtree_loop(moods_table, BLOBCOL_ID, aca, check_mood));
 }
 
 /*
 }
 
 /*
@@ -249,23 +256,27 @@ int mood_check_callback(struct afs_callback_arg *aca)
  * overflows and rounding errors we store the common divisor of the
  * correction factors separately.
  */
  * overflows and rounding errors we store the common divisor of the
  * correction factors separately.
  */
-static int64_t normalized_value(int64_t x, int64_t n, int64_t sum, int64_t qd)
-{
-       if (!n || !qd)
-               return 0;
-       return 100 * (n * x - sum) / (int64_t)int_sqrt(n) / (int64_t)int_sqrt(qd);
-}
-
-static long compute_score(struct afs_info *afsi)
+static long compute_score(struct afs_info *afsi,
+               const struct afs_statistics *stats)
 {
 {
-       long score = -normalized_value(afsi->num_played, statistics.num,
-               statistics.num_played_sum, statistics.num_played_qd);
-       score -= normalized_value(afsi->last_played, statistics.num,
-               statistics.last_played_sum, statistics.last_played_qd);
-       return score / 2;
+       int64_t mean_n, mean_l,score_n, score_l;
+
+       assert(stats->normalization_divisor > 0);
+       assert(stats->num > 0);
+       mean_n = stats->num_played_sum / stats->num;
+       mean_l = stats->last_played_sum / stats->num;
+
+       score_n = -((int64_t)afsi->num_played - mean_n)
+               * stats->num_played_correction
+               / stats->normalization_divisor;
+       score_l = -((int64_t)afsi->last_played - mean_l)
+               * stats->last_played_correction
+               / stats->normalization_divisor;
+       return (score_n + score_l) / 2;
 }
 
 }
 
-static int add_afs_statistics(const struct osl_row *row)
+static int add_afs_statistics(const struct osl_row *row,
+               struct afs_statistics *stats)
 {
        uint64_t n, x, s, q;
        struct afs_info afsi;
 {
        uint64_t n, x, s, q;
        struct afs_info afsi;
@@ -274,64 +285,65 @@ static int add_afs_statistics(const struct osl_row *row)
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        x = afsi.last_played;
        x = afsi.last_played;
-       s = statistics.last_played_sum;
+       s = stats->last_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.last_played_qd += q * q * n / (n + 1);
+               stats->last_played_qd += q * q * n / (n + 1);
        }
        }
-       statistics.last_played_sum += x;
+       stats->last_played_sum += x;
 
        x = afsi.num_played;
 
        x = afsi.num_played;
-       s = statistics.num_played_sum;
+       s = stats->num_played_sum;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
        if (n > 0) {
                q = (x > s / n)? x - s / n : s / n - x;
-               statistics.num_played_qd += q * q * n / (n + 1);
+               stats->num_played_qd += q * q * n / (n + 1);
        }
        }
-       statistics.num_played_sum += x;
-       statistics.num++;
+       stats->num_played_sum += x;
+       stats->num++;
        return 1;
 }
 
 static int del_afs_statistics(const struct osl_row *row)
 {
        return 1;
 }
 
 static int del_afs_statistics(const struct osl_row *row)
 {
+       struct afs_statistics *stats = &current_mood->stats;
        uint64_t n, s, q, a, new_s;
        struct afs_info afsi;
        int ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
        uint64_t n, s, q, a, new_s;
        struct afs_info afsi;
        int ret;
        ret = get_afsi_of_row(row, &afsi);
        if (ret < 0)
                return ret;
-       n = statistics.num;
+       n = stats->num;
        assert(n);
        if (n == 1) {
        assert(n);
        if (n == 1) {
-               memset(&statistics, 0, sizeof(statistics));
-               statistics.normalization_divisor = 1;
+               memset(stats, 0, sizeof(*stats));
+               stats->normalization_divisor = 1;
                return 1;
        }
 
                return 1;
        }
 
-       s = statistics.last_played_sum;
-       q = statistics.last_played_qd;
+       s = stats->last_played_sum;
+       q = stats->last_played_qd;
        a = afsi.last_played;
        new_s = s - a;
        a = afsi.last_played;
        new_s = s - a;
-       statistics.last_played_sum = new_s;
-       statistics.last_played_qd = q + s * s / n - a * a
+       stats->last_played_sum = new_s;
+       stats->last_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
                - new_s * new_s / (n - 1);
 
-       s = statistics.num_played_sum;
-       q = statistics.num_played_qd;
+       s = stats->num_played_sum;
+       q = stats->num_played_qd;
        a = afsi.num_played;
        new_s = s - a;
        a = afsi.num_played;
        new_s = s - a;
-       statistics.num_played_sum = new_s;
-       statistics.num_played_qd = q + s * s / n - a * a
+       stats->num_played_sum = new_s;
+       stats->num_played_qd = q + s * s / n - a * a
                - new_s * new_s / (n - 1);
 
                - new_s * new_s / (n - 1);
 
-       statistics.num--;
+       stats->num--;
        return 1;
 }
 
 /*
        return 1;
 }
 
 /*
- * At mood open time we determine the set of admissible files for the given
+ * At mood load time we determine the set of admissible files for the given
  * mood where each file is identified by a pointer to a row of the audio file
  * table. In the first pass the pointers are added to a temporary array and
  * statistics are computed. When all admissible files have been processed in
  * mood where each file is identified by a pointer to a row of the audio file
  * table. In the first pass the pointers are added to a temporary array and
  * statistics are computed. When all admissible files have been processed in
@@ -341,7 +353,7 @@ static int del_afs_statistics(const struct osl_row *row)
  */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
  */
 struct admissible_array {
        /** Files are admissible wrt. this mood. */
-       struct mood *m;
+       struct mood_instance *m;
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
        /** The size of the array */
        unsigned size;
        /** Pointer to the array of admissible files. */
@@ -355,19 +367,18 @@ struct admissible_array {
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
 static int add_if_admissible(struct osl_row *aft_row, void *data)
 {
        struct admissible_array *aa = data;
-       int ret;
+       struct afs_statistics *stats = &aa->m->stats;
 
 
-       ret = row_is_admissible(aft_row, aa->m);
-       if (ret <= 0)
-               return ret;
-       if (statistics.num >= aa->size) {
+       if (!mp_eval_row(aft_row, aa->m->parser_context))
+               return 0;
+       if (stats->num >= aa->size) {
                aa->size *= 2;
                aa->size += 100;
                aa->size *= 2;
                aa->size += 100;
-               aa->array = para_realloc(aa->array,
-                       aa->size * sizeof(struct osl_row *));
+               aa->array = arr_realloc(aa->array, aa->size,
+                       sizeof(struct osl_row *));
        }
        }
-       aa->array[statistics.num] = aft_row;
-       return add_afs_statistics(aft_row);
+       aa->array[stats->num] = aft_row;
+       return add_afs_statistics(aft_row, stats);
 }
 
 /**
 }
 
 /**
@@ -412,29 +423,25 @@ _static_inline_ int64_t update_quadratic_deviation(int64_t n, int64_t old_qd,
        return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
 }
 
        return old_qd + delta * (sigma - 2 * old_sum / n - delta / n);
 }
 
-static int update_afs_statistics(struct afs_info *old_afsi,
+static void update_afs_statistics(struct afs_info *old_afsi,
                struct afs_info *new_afsi)
 {
                struct afs_info *new_afsi)
 {
-       unsigned n;
-       int ret = get_num_admissible_files(&n);
-
-       if (ret < 0)
-               return ret;
-       assert(n);
-
-       statistics.last_played_qd = update_quadratic_deviation(n,
-               statistics.last_played_qd, old_afsi->last_played,
-               new_afsi->last_played, statistics.last_played_sum);
-       statistics.last_played_sum += new_afsi->last_played - old_afsi->last_played;
-
-       statistics.num_played_qd = update_quadratic_deviation(n,
-               statistics.num_played_qd, old_afsi->num_played,
-               new_afsi->num_played, statistics.num_played_sum);
-       statistics.num_played_sum += new_afsi->num_played - old_afsi->num_played;
-       return 1;
+       struct afs_statistics *stats = &current_mood->stats;
+
+       assert(stats->num > 0);
+       stats->last_played_qd = update_quadratic_deviation(stats->num,
+               stats->last_played_qd, old_afsi->last_played,
+               new_afsi->last_played, stats->last_played_sum);
+       stats->last_played_sum += new_afsi->last_played - old_afsi->last_played;
+
+       stats->num_played_qd = update_quadratic_deviation(stats->num,
+               stats->num_played_qd, old_afsi->num_played,
+               new_afsi->num_played, stats->num_played_sum);
+       stats->num_played_sum += new_afsi->num_played - old_afsi->num_played;
 }
 
 }
 
-static int add_to_score_table(const struct osl_row *aft_row)
+static int add_to_score_table(const struct osl_row *aft_row,
+               struct mood_instance *m)
 {
        long score;
        struct afs_info afsi;
 {
        long score;
        struct afs_info afsi;
@@ -442,8 +449,8 @@ static int add_to_score_table(const struct osl_row *aft_row)
 
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
-       score = compute_score(&afsi);
-       return score_add(aft_row, score);
+       score = compute_score(&afsi, &m->stats);
+       return score_add(aft_row, score, m->score_table);
 }
 
 static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
 }
 
 static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
@@ -455,23 +462,18 @@ static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
 }
 
 /**
 }
 
 /**
- * Delete one entry from the statistics and from the score table.
+ * Delete an audio file from the score table and update mood statistics.
  *
  *
- * \param aft_row The audio file which is no longer admissible.
+ * \param aft_row Identifies the row to delete.
  *
  *
- * \return Positive on success, negative on errors.
+ * \return Standard.
  *
  * \sa \ref score_delete().
  */
 static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
  *
  * \sa \ref score_delete().
  */
 static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
-       int ret;
-
-       ret = row_belongs_to_score_table(aft_row, NULL);
-       if (ret < 0)
-               return ret;
-       if (!ret) /* not admissible, nothing to do */
-               return 1;
+       if (!row_belongs_to_score_table(aft_row))
+               return 0;
        return delete_from_statistics_and_score_table(aft_row);
 }
 
        return delete_from_statistics_and_score_table(aft_row);
 }
 
@@ -490,95 +492,84 @@ static int mood_update_audio_file(const struct osl_row *aft_row,
                struct afs_info *old_afsi)
 {
        long score, percent;
                struct afs_info *old_afsi)
 {
        long score, percent;
-       int ret, is_admissible, was_admissible = 0;
+       int ret;
+       bool is_admissible, was_admissible;
        struct afs_info afsi;
        struct afs_info afsi;
-       unsigned rank;
 
        if (!current_mood)
                return 1; /* nothing to do */
 
        if (!current_mood)
                return 1; /* nothing to do */
-       ret = row_belongs_to_score_table(aft_row, &rank);
-       if (ret < 0)
-               return ret;
-       was_admissible = ret;
-       ret = row_is_admissible(aft_row, current_mood);
-       if (ret < 0)
-               return ret;
-       is_admissible = (ret > 0);
+       was_admissible = row_belongs_to_score_table(aft_row);
+       is_admissible = mp_eval_row(aft_row, current_mood->parser_context);
        if (!was_admissible && !is_admissible)
                return 1;
        if (was_admissible && !is_admissible)
                return delete_from_statistics_and_score_table(aft_row);
        if (!was_admissible && is_admissible) {
        if (!was_admissible && !is_admissible)
                return 1;
        if (was_admissible && !is_admissible)
                return delete_from_statistics_and_score_table(aft_row);
        if (!was_admissible && is_admissible) {
-               ret = add_afs_statistics(aft_row);
+               ret = add_afs_statistics(aft_row, &current_mood->stats);
                if (ret < 0)
                        return ret;
                if (ret < 0)
                        return ret;
-               return add_to_score_table(aft_row);
+               return add_to_score_table(aft_row, current_mood);
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
        }
        /* update score */
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
-       if (old_afsi) {
-               ret = update_afs_statistics(old_afsi, &afsi);
-               if (ret < 0)
-                       return ret;
-       }
-       score = compute_score(&afsi);
+       if (old_afsi)
+               update_afs_statistics(old_afsi, &afsi);
+       score = compute_score(&afsi, &current_mood->stats);
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
                percent = 100;
        else if (percent < 0)
                percent = 0;
        PARA_DEBUG_LOG("score: %li\n", score);
        percent = (score + 100) / 3;
        if (percent > 100)
                percent = 100;
        else if (percent < 0)
                percent = 0;
-       PARA_DEBUG_LOG("moving from rank %u to %li%%\n", rank, percent);
+       PARA_DEBUG_LOG("moving to %li%%\n", percent);
        return score_update(aft_row, percent);
 }
 
 /* sse: seconds since epoch. */
        return score_update(aft_row, percent);
 }
 
 /* sse: seconds since epoch. */
-static void log_statistics(int64_t sse)
+static char *get_statistics(struct mood_instance *m, int64_t sse)
 {
 {
-       unsigned n = statistics.num;
+       unsigned n = m->stats.num;
        int mean_days, sigma_days;
 
        int mean_days, sigma_days;
 
-       assert(current_mood);
-       PARA_NOTICE_LOG("loaded mood %s\n", current_mood->name?
-               current_mood->name : "(dummy)");
-       if (!n) {
-               PARA_WARNING_LOG("no admissible files\n");
-               return;
-       }
-       PARA_NOTICE_LOG("%u admissible files\n", statistics.num);
-       mean_days = (sse - statistics.last_played_sum / n) / 3600 / 24;
-       sigma_days = int_sqrt(statistics.last_played_qd / n) / 3600 / 24;
-       PARA_NOTICE_LOG("last_played mean/sigma: %d/%d days\n", mean_days, sigma_days);
-       PARA_NOTICE_LOG("num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n",
-               statistics.num_played_sum / n,
-               int_sqrt(statistics.num_played_qd / n));
-       PARA_NOTICE_LOG("num_played correction factor: %" PRId64 "\n",
-               statistics.num_played_correction);
-       PARA_NOTICE_LOG("last_played correction factor: %" PRId64 "\n",
-               statistics.last_played_correction);
-       PARA_NOTICE_LOG("normalization divisor: %" PRId64 "\n",
-               statistics.normalization_divisor);
+       if (n == 0)
+               return make_message("no admissible files\n");
+       mean_days = (sse - m->stats.last_played_sum / n) / 3600 / 24;
+       sigma_days = int_sqrt(m->stats.last_played_qd / n) / 3600 / 24;
+       return make_message(
+               "loaded mood %s (%u files)\n"
+               "last_played mean/sigma: %d/%d days\n"
+               "num_played mean/sigma: %" PRId64 "/%" PRIu64 "\n"
+               "correction factor ratio: %.2lf\n"
+       ,
+               m->name? m->name : "(dummy)",
+               n,
+               mean_days, sigma_days,
+               m->stats.num_played_sum / n,
+               int_sqrt(m->stats.num_played_qd / n),
+               86400.0 * m->stats.last_played_correction /
+                       m->stats.num_played_correction
+       );
 }
 
 /**
 }
 
 /**
- * Close the current mood.
+ * Free all resources of a mood instance.
+ *
+ * \param m As obtained by \ref mood_load(). If NULL, unload the current mood.
  *
  *
- * Frees all resources of the current mood.
+ * It's OK to call this with m == NULL even if no current mood is loaded.
  */
  */
-void close_current_mood(void)
+void mood_unload(struct mood_instance *m)
 {
 {
+       if (m)
+               return destroy_mood(m);
        destroy_mood(current_mood);
        current_mood = NULL;
        destroy_mood(current_mood);
        current_mood = NULL;
-       memset(&statistics, 0, sizeof(statistics));
-       statistics.normalization_divisor = 1;
 }
 
 }
 
-static void compute_correction_factors(int64_t sse)
+static void compute_correction_factors(int64_t sse, struct afs_statistics *s)
 {
 {
-       struct afs_statistics *s = &statistics;
-
        if (s->num > 0) {
                s->normalization_divisor = int_sqrt(s->last_played_qd)
                        * int_sqrt(s->num_played_qd) / s->num / 100;
        if (s->num > 0) {
                s->normalization_divisor = int_sqrt(s->last_played_qd)
                        * int_sqrt(s->num_played_qd) / s->num / 100;
@@ -594,30 +585,45 @@ static void compute_correction_factors(int64_t sse)
 }
 
 /**
 }
 
 /**
- * Change the current mood.
+ * Populate a score table with admissible files for the given mood.
  *
  *
- * \param mood_name The name of the mood to open.
- * \param errmsg Error description is returned here.
+ * This consults the mood table to initialize the mood parser with the mood
+ * expression stored in the blob object which corresponds to the given name. A
+ * score table is allocated and populated with references to those entries of
+ * the audio file table which evaluate as admissible with respect to the mood
+ * expression. For each audio file a score value is computed and stored along
+ * with the file reference.
  *
  *
- * If \a mood_name is \a NULL, load the dummy mood that accepts every audio file
- * and uses a scoring method based only on the \a last_played information.
+ * \param mood_name The name of the mood to load.
+ * \param result Opaque, refers to the mood parser and the score table.
+ * \param msg Error message or mood info is returned here.
  *
  *
- * The errmsg pointer may be NULL, in which case no error message will be
- * returned. If a non-NULL pointer is given, the caller must free *errmsg.
+ * If the mood name is NULL, the dummy mood is loaded. This mood regards every
+ * audio file as admissible.
  *
  *
- * If there is already an open mood, it will be closed first.
+ * A NULL result pointer instructs the function to operate on the current mood.
+ * That is, on the mood instance which is used by the server to select the next
+ * audio file for streaming. In this mode of operation, the mood which was
+ * active before the call, if any, is unloaded on success.
  *
  *
- * \return Positive on success, negative on errors.
+ * If result is not NULL, the current mood is unaffected and *result points to
+ * an initialized mood instance on success. The caller can pass this reference
+ * to \ref mood_loop() to iterate over the admissible files, and should call
+ * \ref mood_unload() to free the mood instance afterwards.
+ *
+ * If the message pointer is not NULL, a suitable message is returned there in
+ * all cases. The caller must free this string.
  *
  *
- * \sa struct \ref afs_info::last_played, \ref mp_eval_row().
+ * \return The number of admissible files on success, negative on errors. On
+ * errors, the current mood remains unaffected even if result is NULL. It is
+ * not considered an error if no files are admissible.
+ *
+ * \sa \ref mp_eval_row().
  */
  */
-int change_current_mood(const char *mood_name, char **errmsg)
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg)
 {
        int i, ret;
 {
        int i, ret;
-       struct admissible_array aa = {
-               .size = 0,
-               .array = NULL
-       };
+       struct admissible_array aa = {.size = 0};
        /*
         * We can not use the "now" pointer from sched.c here because we are
         * called before schedule(), which initializes "now".
        /*
         * We can not use the "now" pointer from sched.c here because we are
         * called before schedule(), which initializes "now".
@@ -625,85 +631,86 @@ int change_current_mood(const char *mood_name, char **errmsg)
        struct timeval rnow;
 
        if (mood_name) {
        struct timeval rnow;
 
        if (mood_name) {
-               struct mood *m;
-               struct osl_row *row;
-               struct osl_object obj;
-
-               if (!*mood_name) {
-                       *errmsg = make_message("empty mood name");
-                       return -ERRNO_TO_PARA_ERROR(EINVAL);
-               }
-               obj.data = (char *)mood_name;
-               obj.size = strlen(mood_name) + 1;
-               ret = osl(osl_get_row(moods_table, BLOBCOL_NAME, &obj, &row));
-               if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message("no such mood: %s",
-                                       mood_name);
-                       return ret;
-               }
-               ret = load_mood(row, &m, errmsg);
+               ret = init_mood_parser(mood_name, &aa.m, msg);
                if (ret < 0)
                        return ret;
                if (ret < 0)
                        return ret;
-               close_current_mood();
-               current_mood = m;
-       } else { /* load dummy mood */
-               close_current_mood();
-               current_mood = alloc_new_mood(NULL);
-       }
-       aa.m = current_mood;
+       } else /* load dummy mood */
+               aa.m = alloc_new_mood(NULL);
        PARA_NOTICE_LOG("computing statistics of admissible files\n");
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0) {
        PARA_NOTICE_LOG("computing statistics of admissible files\n");
        ret = audio_file_loop(&aa, add_if_admissible);
        if (ret < 0) {
-               if (errmsg)
-                       *errmsg = make_message("audio file loop failed");
+               if (msg) /* false if we are called via the event handler */
+                       *msg = make_message("audio file loop failed\n");
                goto out;
        }
        clock_get_realtime(&rnow);
                goto out;
        }
        clock_get_realtime(&rnow);
-       compute_correction_factors(rnow.tv_sec);
-       log_statistics(rnow.tv_sec);
-       for (i = 0; i < statistics.num; i++) {
-               ret = add_to_score_table(aa.array[i]);
+       compute_correction_factors(rnow.tv_sec, &aa.m->stats);
+       if (result)
+               score_open(&aa.m->score_table);
+       for (i = 0; i < aa.m->stats.num; i++) {
+               ret = add_to_score_table(aa.array[i], aa.m);
                if (ret < 0) {
                if (ret < 0) {
-                       if (errmsg)
-                               *errmsg = make_message(
-                                       "could not add row to score table");
+                       if (msg)
+                               *msg = make_message(
+                                       "could not add row to score table\n");
                        goto out;
                }
        }
                        goto out;
                }
        }
-       ret = statistics.num;
+       /* success */
+       if (msg)
+               *msg = get_statistics(aa.m, rnow.tv_sec);
+       ret = aa.m->stats.num;
+       if (result)
+               *result = aa.m;
+       else {
+               mood_unload(NULL);
+               current_mood = aa.m;
+       }
+       ret = 1;
 out:
        free(aa.array);
 out:
        free(aa.array);
-       if (ret < 0)
-               close_current_mood();
+       if (ret <= 0) /* error, or no admissible files */
+               destroy_mood(aa.m);
        return ret;
 }
 
        return ret;
 }
 
-/*
- * Close and re-open the current mood.
+/**
+ * Iterate over the admissible files of a mood instance.
  *
  *
- * This function is called on events which render the current list of
- * admissible files useless, for example if an attribute is removed from the
- * attribute table.
+ * This wrapper around \ref score_loop() is the mood counterpart of \ref
+ * playlist_loop().
  *
  *
- * If no mood is currently open, the function returns success.
+ * \param m Determines the score table to iterate. Must not be NULL.
+ * \param func See \ref score_loop().
+ * \param data See \ref score_loop().
+ *
+ * \return See \ref score_loop(), \ref playlist_loop().
+ */
+int mood_loop(struct mood_instance *m, osl_rbtree_loop_func *func, void *data)
+{
+       return score_loop(func, m->score_table, data);
+}
+
+/*
+ * Empty the score table and start over.
+ *
+ * This function is called on events which render the current set of admissible
+ * files invalid, for example if an attribute is removed from the attribute
+ * table.
  */
 static int reload_current_mood(void)
 {
        int ret;
        char *mood_name = NULL;
 
  */
 static int reload_current_mood(void)
 {
        int ret;
        char *mood_name = NULL;
 
-       ret = clear_score_table();
-       if (ret < 0)
-               return ret;
-       if (!current_mood)
-               return 1;
+       assert(current_mood);
+       score_clear();
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
        PARA_NOTICE_LOG("reloading %s\n", current_mood->name?
                current_mood->name : "(dummy)");
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
-       close_current_mood();
-       ret = change_current_mood(mood_name, NULL);
+       mood_unload(NULL);
+       ret = mood_load(mood_name, NULL, NULL);
        free(mood_name);
        return ret;
 }
        free(mood_name);
        return ret;
 }
@@ -715,9 +722,17 @@ static int reload_current_mood(void)
  * \param pb Unused.
  * \param data Its type depends on the event.
  *
  * \param pb Unused.
  * \param data Its type depends on the event.
  *
- * This function performs actions required due to the occurrence of the given
- * event. Possible actions include reload of the current mood and update of the
- * score of an audio file.
+ * This function updates the score table according to the event that has
+ * occurred. Two actions are possible: (a) reload the current mood, or (b)
+ * add/remove/update the row of the score table which corresponds to the audio
+ * file that has been modified or whose afs info has been changed. It depends
+ * on the type of the event which action (if any) is performed.
+ *
+ * The callbacks of command handlers such as com_add() or com_touch() which
+ * modify the audio file table call this function. The virtual streaming system
+ * also calls this after it has updated the afs info of the file it is about to
+ * stream (the one with the highest score). If the file stays admissible, its
+ * score is recomputed so that a different file is picked next time.
  *
  * \return Standard.
  */
  *
  * \return Standard.
  */
diff --git a/mood.h b/mood.h
deleted file mode 100644 (file)
index fcfe1ef..0000000
--- a/mood.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file mood.h Public functions of mood.c. */
-
-int change_current_mood(const char *mood_name, char **errmsg);
-void close_current_mood(void);
-int mood_check_callback(struct afs_callback_arg *aca);
diff --git a/mp.c b/mp.c
index c537e72998d2ae46e0e9dbaa55175eaebf14f07f..a068b043faf3c09b72befde39771058108f1aec9 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -90,7 +90,7 @@ unsigned parse_quoted_string(const char *src, const char quote_chars[2],
 
        assert(len >= 2);
        assert(src[0] == quote_chars[0]);
 
        assert(len >= 2);
        assert(src[0] == quote_chars[0]);
-       p = dst = para_malloc(len - 1);
+       p = dst = alloc(len - 1);
        backslash = false;
        for (n = 1;; n++) {
                char c;
        backslash = false;
        for (n = 1;; n++) {
                char c;
@@ -518,7 +518,7 @@ int mp_init(const char *definition, int nbytes, struct mp_context **result,
                        *errmsg = NULL;
                return 0;
        }
                        *errmsg = NULL;
                return 0;
        }
-       ctx = para_calloc(sizeof(*ctx));
+       ctx = zalloc(sizeof(*ctx));
        ctx->errmsg = NULL;
        ctx->ast = NULL;
 
        ctx->errmsg = NULL;
        ctx->ast = NULL;
 
@@ -557,7 +557,7 @@ int mp_init(const char *definition, int nbytes, struct mp_context **result,
  * function returns true (without looking at the audio file metadata) to
  * indicate that the given audio file should be considered admissible.
  *
  * function returns true (without looking at the audio file metadata) to
  * indicate that the given audio file should be considered admissible.
  *
- * \sa \ref change_current_mood(), \ref mp_eval_ast().
+ * \sa \ref mood_load(), \ref mp_eval_ast().
  */
 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
 {
  */
 bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx)
 {
index 728b25b81f94aa177a1e74ae01fca07419f7fe90..56f28a09d8eb9fd18a9ba2262f14a58c6cec7997 100644 (file)
--- a/mp3_afh.c
+++ b/mp3_afh.c
@@ -37,9 +37,6 @@ struct mp3header {
        unsigned int freq;
        unsigned int padding;
        unsigned int mode;
        unsigned int freq;
        unsigned int padding;
        unsigned int mode;
-       unsigned int copyright;
-       unsigned int original;
-       unsigned int emphasis;
 };
 
 static const int frequencies[3][4] = {
 };
 
 static const int frequencies[3][4] = {
@@ -348,7 +345,7 @@ static int mp3_rewrite_tags(const char *map, size_t mapsize,
        if (ret < 0)
                goto out;
        new_v2size = id3_tag_render(v2_tag, NULL);
        if (ret < 0)
                goto out;
        new_v2size = id3_tag_render(v2_tag, NULL);
-       v2_buffer = para_malloc(new_v2size);
+       v2_buffer = alloc(new_v2size);
        id3_tag_render(v2_tag, v2_buffer);
        PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
        ret = write_all(fd, (char *)v2_buffer, new_v2size);
        id3_tag_render(v2_tag, v2_buffer);
        PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
        ret = write_all(fd, (char *)v2_buffer, new_v2size);
@@ -473,7 +470,7 @@ static int frame_length(struct mp3header *header)
                + header->padding;
 }
 
                + header->padding;
 }
 
-static int compare_headers(struct mp3header *h1,struct mp3header *h2)
+static int compare_headers(struct mp3header *h1, struct mp3header *h2)
 {
        if ((*(unsigned int*)h1) == (*(unsigned int*)h2))
                return 1;
 {
        if ((*(unsigned int*)h1) == (*(unsigned int*)h2))
                return 1;
@@ -481,10 +478,7 @@ static int compare_headers(struct mp3header *h1,struct mp3header *h2)
                        (h1->layer == h2->layer) &&
                        (h1->crc == h2->crc) &&
                        (h1->freq == h2->freq) &&
                        (h1->layer == h2->layer) &&
                        (h1->crc == h2->crc) &&
                        (h1->freq == h2->freq) &&
-                       (h1->mode == h2->mode) &&
-                       (h1->copyright == h2->copyright) &&
-                       (h1->original == h2->original) &&
-                       (h1->emphasis == h2->emphasis))
+                       (h1->mode == h2->mode))
                return 1;
        return 0;
 }
                return 1;
        return 0;
 }
@@ -598,7 +592,7 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
        const char *tag_versions[] = {"no", "id3v1", "id3v2", "id3v1+id3v2"};
 
        afhi->chunks_total = 0;
        const char *tag_versions[] = {"no", "id3v1", "id3v2", "id3v1+id3v2"};
 
        afhi->chunks_total = 0;
-       afhi->chunk_table = para_malloc(chunk_table_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(chunk_table_size, sizeof(uint32_t));
        while (1) {
                int freq, br;
                struct timeval tmp, cct; /* current chunk time */
        while (1) {
                int freq, br;
                struct timeval tmp, cct; /* current chunk time */
@@ -633,8 +627,8 @@ static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
                total_time = tmp;
                if (afhi->chunks_total >= chunk_table_size) {
                        chunk_table_size *= 2;
                total_time = tmp;
                if (afhi->chunks_total >= chunk_table_size) {
                        chunk_table_size *= 2;
-                       afhi->chunk_table = para_realloc(afhi->chunk_table,
-                               chunk_table_size * sizeof(uint32_t));
+                       afhi->chunk_table = arr_realloc(afhi->chunk_table,
+                               chunk_table_size, sizeof(uint32_t));
                }
                afhi->chunk_table[afhi->chunks_total] = fpos;
                afhi->chunks_total++;
                }
                afhi->chunk_table[afhi->chunks_total] = fpos;
                afhi->chunks_total++;
index ccb1553b820357e13f2cafe2c0e8a7e0502ac8fd..d40df85edbe24b9dc54f01fc9305a1575c022507 100644 (file)
@@ -73,7 +73,7 @@ static void mp3dec_close(struct filter_node *fn)
 
 #define MP3DEC_MAX_FRAME 8192
 
 
 #define MP3DEC_MAX_FRAME 8192
 
-static int mp3dec_post_select(__a_unused struct sched *s, void *context)
+static int mp3dec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        int i, ret;
 {
        struct filter_node *fn = context;
        int i, ret;
@@ -93,7 +93,7 @@ next_buffer:
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &inbuffer);
        /*
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &inbuffer);
        /*
-        * Decode at most 8K in one go to give the post_select() functions of
+        * Decode at most 8K in one go to give the post_monitor() functions of
         * other buffer tree nodes a chance to run. This is necessary to avoid
         * buffer underruns on slow machines.
         */
         * other buffer tree nodes a chance to run. This is necessary to avoid
         * buffer underruns on slow machines.
         */
@@ -105,7 +105,7 @@ next_frame:
                mp3dec_consume(btrn, &pmd->stream, len);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
                        if (len == iqs && btr_no_parent(btrn)) {
                mp3dec_consume(btrn, &pmd->stream, len);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
                        if (len == iqs && btr_no_parent(btrn)) {
-                               ret = -E_MP3DEC_EOF;
+                               ret = -E_EOF;
                                goto err;
                        }
                        fn->min_iqs += 100;
                                goto err;
                        }
                        fn->min_iqs += 100;
@@ -127,7 +127,7 @@ decode:
                        goto err;
                mad_stream_sync(&pmd->stream);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
                        goto err;
                mad_stream_sync(&pmd->stream);
                if (pmd->stream.error == MAD_ERROR_BUFLEN) {
-                       ret = -E_MP3DEC_EOF;
+                       ret = -E_EOF;
                        if (len == iqs && btr_no_parent(btrn))
                                goto err;
                        fn->min_iqs += 100;
                        if (len == iqs && btr_no_parent(btrn))
                                goto err;
                        fn->min_iqs += 100;
@@ -144,7 +144,7 @@ decode:
        }
        fn->min_iqs = 0;
        mad_synth_frame(&pmd->synth, &pmd->frame);
        }
        fn->min_iqs = 0;
        mad_synth_frame(&pmd->synth, &pmd->frame);
-       outbuffer = para_malloc(pmd->synth.pcm.length * 2 * pmd->channels);
+       outbuffer = arr_alloc(pmd->synth.pcm.length, 2 * pmd->channels);
        loaded = 0;
        for (i = 0; i < pmd->synth.pcm.length; i++) {
                int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
        loaded = 0;
        for (i = 0; i < pmd->synth.pcm.length; i++) {
                int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
@@ -166,7 +166,7 @@ err:
 
 static void mp3dec_open(struct filter_node *fn)
 {
 
 static void mp3dec_open(struct filter_node *fn)
 {
-       struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
+       struct private_mp3dec_data *pmd = zalloc(sizeof(*pmd));
 
        fn->private_data = pmd;
        mad_stream_init(&pmd->stream);
 
        fn->private_data = pmd;
        mad_stream_init(&pmd->stream);
@@ -187,7 +187,7 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
 const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
        .open = mp3dec_open,
        .close = mp3dec_close,
 const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
        .open = mp3dec_open,
        .close = mp3dec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = mp3dec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = mp3dec_post_monitor,
        .execute = mp3dec_execute,
 };
        .execute = mp3dec_execute,
 };
diff --git a/mp4.c b/mp4.c
new file mode 100644 (file)
index 0000000..5ca1307
--- /dev/null
+++ b/mp4.c
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+ * FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+ *
+ * See file COPYING.
+ */
+
+/** \file mp4.c Paraslash's internal mp4 parser. */
+
+/*
+ * This is a stripped down version of the former mp4ff library which used to be
+ * part of the faad decoder project but was removed from the faad code base in
+ * 2017. The original code has been cleaned up substantially and the public API
+ * has been documented. See the git commit log for details.
+ */
+
+#include <regex.h>
+
+#include "para.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "mp4.h"
+
+/**
+ * The three states of the mp4 parser. The parser only loads the audio specific
+ * values and tables when it is in the second state.
+ */
+enum audio_track_state {
+       /** We haven't encountered an mp4a atom so far. */
+       ATS_INITIAL,
+       /** We have seen an mp4a atom but no subsequent trak atom yet. */
+       ATS_SEEN_MP4A,
+       /** A trak atom was seen *after* the mp4a atom. */
+       ATS_TRACK_CHANGE,
+};
+
+struct mp4_track {
+       /* determines which atoms we still need to parse. */
+       enum audio_track_state state;
+
+       /* mp4a */
+       uint16_t channel_count;
+       uint16_t sample_rate;
+
+       /* stsz */
+       uint32_t stsz_sample_size;
+       uint32_t stsz_sample_count;
+       uint32_t *stsz_table;
+
+       /* stts */
+       uint32_t stts_entry_count;
+       uint32_t *stts_sample_count;
+
+       /* stsc */
+       uint32_t stsc_entry_count;
+       uint32_t *stsc_first_chunk;
+       uint32_t *stsc_samples_per_chunk;
+
+       /* stsc */
+       uint32_t stco_entry_count;
+       uint32_t *stco_chunk_offset;
+
+       /* mdhd */
+       uint32_t time_scale;
+       uint64_t duration;
+};
+
+struct mp4 {
+       const struct mp4_callback *cb;
+
+       uint64_t moov_offset;
+       uint64_t moov_size;
+       uint64_t meta_offset;
+       uint32_t meta_size;
+       uint64_t ilst_offset;
+       uint32_t ilst_size;
+       uint64_t udta_offset;
+       uint32_t udta_size;
+
+       uint8_t last_atom;
+       struct mp4_track track;
+       struct mp4_metadata meta;
+};
+
+/*
+ * Returns -E_MP4_READ, 0, or 1 on errors/EOF/success. Partial reads followed
+ * by EOF or read errors are treated as errors.
+ */
+static int read_data(struct mp4 *f, void *data, size_t size)
+{
+       while (size > 0) {
+               ssize_t ret = f->cb->read(f->cb->user_data, data, size);
+               if (ret < 0 && errno == EINTR)
+                       continue;
+               /* regard EAGAIN as an error as reads should be blocking. */
+               if (ret <= 0)
+                       return ret < 0? -E_MP4_READ : 0;
+               size -= ret;
+       }
+       return 1;
+}
+
+static int read_int64(struct mp4 *f, uint64_t *result)
+{
+       uint8_t data[8];
+       int ret = read_data(f, data, 8);
+
+       if (ret > 0)
+               *result = read_u64_be(data);
+       return ret;
+}
+
+static int read_int32(struct mp4 *f, uint32_t *result)
+{
+       uint8_t data[4];
+       int ret = read_data(f, data, 4);
+
+       if (ret > 0)
+               *result = read_u32_be(data);
+       return ret;
+}
+
+static int read_int16(struct mp4 *f, uint16_t *result)
+{
+       uint8_t data[2];
+       int ret = read_data(f, data, 2);
+
+       if (ret > 0)
+               *result = read_u16_be(data);
+       return ret;
+}
+
+/** A macro defining the atoms we care about. It gets expanded twice. */
+#define ATOM_ITEMS \
+       ATOM_ITEM(MOOV, 'm', 'o', 'o', 'v') /* movie (top-level container) */ \
+       ATOM_ITEM(TRAK, 't', 'r', 'a', 'k') /* container for a single track */ \
+       ATOM_ITEM(MDIA, 'm', 'd', 'i', 'a') /* media information */ \
+       ATOM_ITEM(MINF, 'm', 'i', 'n', 'f') /* extends mdia */ \
+       ATOM_ITEM(STBL, 's', 't', 'b', 'l') /* sample table container */ \
+       ATOM_ITEM(UDTA, 'u', 'd', 't', 'a') /* user data */ \
+       ATOM_ITEM(ILST, 'i', 'l', 's', 't') /* iTunes Metadata list */ \
+       ATOM_ITEM(ARTIST, 0xa9, 'A', 'R', 'T') /* artist */ \
+       ATOM_ITEM(TITLE, 0xa9, 'n', 'a', 'm') /* title */ \
+       ATOM_ITEM(ALBUM, 0xa9, 'a', 'l', 'b') /* album */ \
+       ATOM_ITEM(DATE, 0xa9, 'd', 'a', 'y') /* date */ \
+       ATOM_ITEM(COMMENT, 0xa9, 'c', 'm', 't') /* comment */ \
+       ATOM_ITEM(MDHD, 'm', 'd', 'h', 'd') /* track header */ \
+       ATOM_ITEM(STSD, 's', 't', 's', 'd') /* sample description box */ \
+       ATOM_ITEM(STTS, 's', 't', 't', 's') /* time to sample box */ \
+       ATOM_ITEM(STSZ, 's', 't', 's', 'z') /* sample size box */ \
+       ATOM_ITEM(STCO, 's', 't', 'c', 'o') /* chunk offset box */ \
+       ATOM_ITEM(STSC, 's', 't', 's', 'c') /* sample to chunk box */ \
+       ATOM_ITEM(MP4A, 'm', 'p', '4', 'a') /* mp4 audio */ \
+       ATOM_ITEM(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
+       ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
+
+/** For the C enumeration we concatenate ATOM_ with the first argument. */
+#define ATOM_ITEM(_name, a, b, c, d) ATOM_ ## _name,
+/** The enumeration of interesting atoms. */
+enum atom {ATOM_ITEMS};
+#undef ATOM_ITEM
+
+/** A cpp version of read_u32_be(). */
+#define ATOM_VALUE(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
+
+static uint8_t atom_name_to_type(uint8_t *p)
+{
+       /** Expands to an instance of the following unnamed structure. */
+       #define ATOM_ITEM(_name, a, b, c, d) \
+               {.name = # _name, .val = ATOM_VALUE(a, b, c, d)},
+       static const struct {
+               const char *name;
+               uint32_t val;
+       } atom_table[] = {ATOM_ITEMS};
+       #undef ATOM_ITEM
+       uint32_t val = read_u32_be(p);
+
+       for (uint8_t n = 0; n < ARRAY_SIZE(atom_table); n++)
+               if (val == atom_table[n].val)
+                       return n;
+       return 255;
+}
+
+/* read atom header, atom size is returned with header included. */
+static int atom_read_header(struct mp4 *f, uint8_t *atom_type,
+               uint8_t *header_size, uint64_t *atom_size)
+{
+       uint32_t size;
+       int ret;
+       uint8_t atom_header[8];
+
+       ret = read_data(f, atom_header, 8);
+       if (ret <= 0)
+               return ret;
+       size = read_u32_be(atom_header);
+       if (size == 1) { /* 64 bit atom size */
+               if (header_size)
+                       *header_size = 16;
+               ret = read_int64(f, atom_size);
+               if (ret <= 0)
+                       return ret;
+       } else {
+               if (header_size)
+                       *header_size = 8;
+               *atom_size = size;
+       }
+       *atom_type = atom_name_to_type(atom_header + 4);
+       return 1;
+}
+
+static off_t get_position(const struct mp4 *f)
+{
+       return f->cb->seek(f->cb->user_data, 0, SEEK_CUR);
+}
+
+static void set_position(struct mp4 *f, off_t position)
+{
+       f->cb->seek(f->cb->user_data, position, SEEK_SET);
+}
+
+static void skip_bytes(struct mp4 *f, off_t num_skip)
+{
+       f->cb->seek(f->cb->user_data, num_skip, SEEK_CUR);
+}
+
+static int read_stsz(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stsz_table)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stsz_sample_size);
+       if (ret <= 0)
+               return ret;
+       ret = read_int32(f, &t->stsz_sample_count);
+       if (ret <= 0)
+               return ret;
+       if (t->stsz_sample_size != 0)
+               return 1;
+       t->stsz_table = arr_alloc(t->stsz_sample_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stsz_sample_count; n++) {
+               ret = read_int32(f, &t->stsz_table[n]);
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+static int read_stts(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stts_sample_count)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stts_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stts_sample_count = arr_alloc(t->stts_entry_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stts_entry_count; n++) {
+               ret = read_int32(f, &t->stts_sample_count[n]);
+               if (ret <= 0)
+                       return ret;
+               skip_bytes(f, 4); /* sample delta */
+       }
+       return 1;
+}
+
+static int read_stsc(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A)
+               return 1;
+       if (t->stsc_first_chunk || t->stsc_samples_per_chunk)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stsc_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stsc_first_chunk = arr_alloc(t->stsc_entry_count, sizeof(int32_t));
+       t->stsc_samples_per_chunk = arr_alloc(t->stsc_entry_count,
+               sizeof (int32_t));
+       for (uint32_t n = 0; n < t->stsc_entry_count; n++) {
+               ret = read_int32(f, &t->stsc_first_chunk[n]);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int32(f, &t->stsc_samples_per_chunk[n]);
+               if (ret <= 0)
+                       return ret;
+               skip_bytes(f, 4); /* sample desc index */
+       }
+       return 1;
+}
+
+static int read_stco(struct mp4 *f)
+{
+       int ret;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_SEEN_MP4A || t->stco_chunk_offset)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &t->stco_entry_count);
+       if (ret <= 0)
+               return ret;
+       t->stco_chunk_offset = arr_alloc(t->stco_entry_count, sizeof(int32_t));
+       for (uint32_t n = 0; n < t->stco_entry_count; n++) {
+               ret = read_int32(f, &t->stco_chunk_offset[n]);
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+static int read_stsd(struct mp4 *f)
+{
+       int ret;
+       uint32_t entry_count;
+
+       if (f->track.state != ATS_INITIAL)
+               return 1;
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       ret = read_int32(f, &entry_count);
+       if (ret <= 0)
+               return ret;
+       for (uint32_t n = 0; n < entry_count; n++) {
+               uint64_t skip = get_position(f);
+               uint64_t size;
+               uint8_t atom_type = 0;
+               ret = atom_read_header(f, &atom_type, NULL, &size);
+               if (ret <= 0)
+                       return ret;
+               skip += size;
+               if (atom_type == ATOM_MP4A) {
+                       f->track.state = ATS_SEEN_MP4A;
+                       /* reserved (6), data reference index (2), reserved (8) */
+                       skip_bytes(f, 16);
+                       ret = read_int16(f, &f->track.channel_count);
+                       if (ret <= 0)
+                               return ret;
+                       skip_bytes(f, 6);
+                       ret = read_int16(f, &f->track.sample_rate);
+                       if (ret <= 0)
+                               return ret;
+               }
+               set_position(f, skip);
+       }
+       return 1;
+}
+
+static const char *get_metadata_name(uint8_t atom_type)
+{
+       switch (atom_type) {
+       case ATOM_TITLE: return "title";
+       case ATOM_ARTIST: return "artist";
+       case ATOM_ALBUM: return "album";
+       case ATOM_DATE: return "date";
+       case ATOM_COMMENT: return "comment";
+       default: return "unknown";
+       }
+}
+
+static int parse_tag(struct mp4 *f, uint8_t parent, int32_t size)
+{
+       int ret;
+       uint64_t subsize, sumsize;
+       char *value = NULL;
+       uint32_t len = 0;
+       uint64_t destpos;
+       struct mp4_tag *tag;
+
+       for (
+               sumsize = 0;
+               sumsize < size;
+               set_position(f, destpos), sumsize += subsize
+       ) {
+               uint8_t atom_type;
+               uint8_t header_size = 0;
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       goto fail;
+               destpos = get_position(f) + subsize - header_size;
+               if (atom_type != ATOM_DATA)
+                       continue;
+               skip_bytes(f, 8); /* version (1), flags (3), reserved (4) */
+               ret = -E_MP4_CORRUPT;
+               if (subsize < header_size + 8 || subsize > UINT_MAX)
+                       goto fail;
+               len = subsize - (header_size + 8);
+               free(value);
+               value = alloc(len + 1);
+               ret = read_data(f, value, len);
+               if (ret <= 0)
+                       goto fail;
+               value[len] = '\0';
+       }
+       if (!value)
+               return -E_MP4_CORRUPT;
+       f->meta.tags = para_realloc(f->meta.tags, (f->meta.count + 1)
+               * sizeof(struct mp4_tag));
+       tag = f->meta.tags + f->meta.count;
+       tag->item = para_strdup(get_metadata_name(parent));
+       tag->value = value;
+       f->meta.count++;
+       return 1;
+fail:
+       free(value);
+       return ret;
+}
+
+static int read_mdhd(struct mp4 *f)
+{
+       int ret;
+       uint32_t version;
+       struct mp4_track *t = &f->track;
+
+       if (t->state != ATS_INITIAL)
+               return 1;
+       ret = read_int32(f, &version);
+       if (ret <= 0)
+               return ret;
+       if (version == 1) {
+               skip_bytes(f, 16); /* creation time (8), modification time (8) */
+               ret = read_int32(f, &t->time_scale);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int64(f, &t->duration);
+               if (ret <= 0)
+                       return ret;
+       } else { /* version == 0 */
+               uint32_t temp;
+
+               skip_bytes(f, 8); /* creation time (4), modification time (4) */
+               ret = read_int32(f, &t->time_scale);
+               if (ret <= 0)
+                       return ret;
+               ret = read_int32(f, &temp);
+               if (ret <= 0)
+                       return ret;
+               t->duration = (temp == (uint32_t) (-1))?
+                       (uint64_t) (-1) : (uint64_t) (temp);
+       }
+       skip_bytes(f, 4);
+       return 1;
+}
+
+static int read_ilst(struct mp4 *f, int32_t size)
+{
+       int ret;
+       uint64_t sumsize = 0;
+
+       while (sumsize < size) {
+               uint8_t atom_type;
+               uint64_t subsize, destpos;
+               uint8_t header_size = 0;
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       return ret;
+               destpos = get_position(f) + subsize - header_size;
+               switch (atom_type) {
+               case ATOM_ARTIST:
+               case ATOM_TITLE:
+               case ATOM_ALBUM:
+               case ATOM_COMMENT:
+               case ATOM_DATE:
+                       ret = parse_tag(f, atom_type, subsize - header_size);
+                       if (ret <= 0)
+                               return ret;
+               }
+               set_position(f, destpos);
+               sumsize += subsize;
+       }
+       return 1;
+}
+
+static int read_meta(struct mp4 *f, uint64_t size)
+{
+       int ret;
+       uint64_t subsize, sumsize = 0;
+       uint8_t atom_type;
+       uint8_t header_size = 0;
+
+       skip_bytes(f, 4); /* version (1), flags (3) */
+       while (sumsize < (size - (header_size + 4))) {
+               ret = atom_read_header(f, &atom_type, &header_size, &subsize);
+               if (ret <= 0)
+                       return ret;
+               if (subsize <= header_size + 4)
+                       return 1;
+               if (atom_type == ATOM_ILST) {
+                       f->ilst_offset = get_position(f) - header_size;
+                       f->ilst_size = subsize;
+                       ret = read_ilst(f, subsize - (header_size + 4));
+                       if (ret <= 0)
+                               return ret;
+               } else
+                       set_position(f, get_position(f) + subsize - header_size);
+               sumsize += subsize;
+       }
+       return 1;
+}
+
+static bool need_atom(uint8_t atom_type, bool meta_only)
+{
+       /* these are needed in any case */
+       switch (atom_type) {
+       case ATOM_STSD:
+       case ATOM_META:
+       case ATOM_TRAK:
+       case ATOM_MDIA:
+       case ATOM_MINF:
+       case ATOM_STBL:
+       case ATOM_UDTA:
+               return true;
+       }
+       /* meta-only opens don't need anything else */
+       if (meta_only)
+               return false;
+       /* these are only required for regular opens */
+       switch (atom_type) {
+       case ATOM_STTS:
+       case ATOM_STSZ:
+       case ATOM_STCO:
+       case ATOM_STSC:
+       case ATOM_MDHD:
+               return true;
+       }
+       return false;
+}
+
+/* parse atoms that are sub atoms of other atoms */
+static int parse_sub_atoms(struct mp4 *f, uint64_t total_size, bool meta_only)
+{
+       int ret;
+       uint64_t dest, size, end = get_position(f) + total_size;
+
+       for (dest = get_position(f); dest < end; set_position(f, dest)) {
+               uint8_t header_size, atom_type;
+               ret = atom_read_header(f, &atom_type, &header_size, &size);
+               if (ret <= 0)
+                       return ret;
+               if (size == 0)
+                       return -E_MP4_CORRUPT;
+               dest = get_position(f) + size - header_size;
+               if (atom_type == ATOM_TRAK && f->track.state == ATS_SEEN_MP4A) {
+                       f->track.state = ATS_TRACK_CHANGE;
+                       continue;
+               }
+               if (atom_type == ATOM_UDTA) {
+                       f->udta_offset = get_position(f) - header_size;
+                       f->udta_size = size;
+               }
+               if (!need_atom(atom_type, meta_only))
+                       continue;
+               switch (atom_type) {
+               case ATOM_STSZ: ret = read_stsz(f); break;
+               case ATOM_STTS: ret = read_stts(f); break;
+               case ATOM_STSC: ret = read_stsc(f); break;
+               case ATOM_STCO: ret = read_stco(f); break;
+               case ATOM_STSD: ret = read_stsd(f); break;
+               case ATOM_MDHD: ret = read_mdhd(f); break;
+               case ATOM_META:
+                       f->meta_offset = get_position(f) - header_size;
+                       f->meta_size = size;
+                       ret = read_meta(f, size);
+                       break;
+               default:
+                       ret = parse_sub_atoms(f, size - header_size, meta_only);
+               }
+               if (ret <= 0)
+                       return ret;
+       }
+       return 1;
+}
+
+/**
+ * Deallocate all resources associated with an mp4 file handle.
+ *
+ * \param f File handle returned by \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * This frees the metadata items and various tables which were allocated when
+ * the file was opened. The given file handle must not be NULL.
+ */
+void mp4_close(struct mp4 *f)
+{
+       free(f->track.stsz_table);
+       free(f->track.stts_sample_count);
+       free(f->track.stsc_first_chunk);
+       free(f->track.stsc_samples_per_chunk);
+       free(f->track.stco_chunk_offset);
+       for (uint32_t n = 0; n < f->meta.count; n++) {
+               free(f->meta.tags[n].item);
+               free(f->meta.tags[n].value);
+       }
+       free(f->meta.tags);
+       free(f);
+}
+
+static int open_file(const struct mp4_callback *cb, bool meta_only, struct mp4 **result)
+{
+       int ret;
+       uint64_t size;
+       uint8_t atom_type, header_size;
+       struct mp4 *f = zalloc(sizeof(*f));
+
+       f->cb = cb;
+       while ((ret = atom_read_header(f, &atom_type, &header_size, &size)) > 0) {
+               f->last_atom = atom_type;
+               if (atom_type != ATOM_MOOV || size <= header_size) { /* skip */
+                       set_position(f, get_position(f) + size - header_size);
+                       continue;
+               }
+               f->moov_offset = get_position(f) - header_size;
+               f->moov_size = size;
+               ret = parse_sub_atoms(f, size - header_size, meta_only);
+               if (ret <= 0)
+                       break;
+       }
+       if (ret < 0)
+               goto fail;
+       ret = -E_MP4_TRACK;
+       if (f->track.channel_count == 0)
+               goto fail;
+       ret = -E_MP4_BAD_SAMPLERATE;
+       if (f->track.sample_rate == 0)
+               goto fail;
+       ret = -E_MP4_MISSING_ATOM;
+       if (f->udta_size == 0 || f->meta_size == 0 || f->ilst_size == 0)
+               goto fail;
+       *result = f;
+       return 1;
+fail:
+       *result = NULL;
+       mp4_close(f);
+       return ret;
+}
+
+/**
+ * Read the audio track and the metadata of an mp4 file.
+ *
+ * \param cb Only the ->read() and ->seek() methods need to be supplied.
+ * \param result Initialized to a non-NULL pointer iff the function succeeds.
+ *
+ * This detects and parses the first audio track and the metadata information
+ * of the mp4 file. Various error checks are performed after the mp4 atoms have
+ * been parsed successfully.
+ *
+ * This function does not modify the file. However, if the caller intents to
+ * update the metadata later, the ->write() and ->truncate() methods must be
+ * supplied in the callback structure.
+ *
+ * \return Standard. Several errors are possible.
+ *
+ * \sa \ref mp4_open_meta().
+ */
+int mp4_open(const struct mp4_callback *cb, struct mp4 **result)
+{
+       struct mp4 *f;
+       int ret;
+
+       *result = NULL;
+       ret = open_file(cb, false, &f);
+       if (ret < 0)
+               return ret;
+       ret = -E_MP4_BAD_SAMPLE_COUNT;
+       if (f->track.stsz_sample_count == 0)
+               goto fail;
+       ret = -E_MP4_CORRUPT;
+       if (f->track.time_scale == 0)
+               goto fail;
+       *result = f;
+       return 1;
+fail:
+       mp4_close(f);
+       return ret;
+}
+
+static int32_t chunk_of_sample(const struct mp4 *f, int32_t sample,
+               int32_t *chunk)
+{
+       const struct mp4_track *t = &f->track;
+       uint32_t *fc = t->stsc_first_chunk, *spc = t->stsc_samples_per_chunk;
+       uint32_t chunk1, chunk1samples, n, total, k;
+
+       for (k = 1, total = 0; k < t->stsc_entry_count; k++, total += n) {
+               n = (fc[k] - fc[k - 1]) * spc[k - 1]; /* number of samples */
+               if (sample < total + n)
+                       break;
+       }
+       chunk1 = fc[k - 1];
+       chunk1samples = spc[k - 1];
+       if (chunk1samples != 0)
+               *chunk = (sample - total) / chunk1samples + chunk1;
+       else
+               *chunk = 1;
+       return total + (*chunk - chunk1) * chunk1samples;
+}
+
+/**
+ * Compute the duration of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The number of milliseconds of the audio track. This function never
+ * fails.
+ */
+uint64_t mp4_get_duration(const struct mp4 *f)
+{
+       const struct mp4_track *t = &f->track;
+
+       return t->duration * 1000 / t->time_scale;
+}
+
+/**
+ * Reposition the read/write file offset.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The number of the sample to reposition to.
+ *
+ * The given sample number must be within range, i.e., strictly less than the
+ * value returned by \ref mp4_num_samples().
+ *
+ * \return Standard. The only possible error is an invalid sample number.
+ */
+int mp4_set_sample_position(struct mp4 *f, uint32_t sample)
+{
+       const struct mp4_track *t = &f->track;
+       int32_t offset, chunk, chunk_sample;
+       uint32_t n, srs; /* sample range size */
+
+       if (sample >= t->stsz_sample_count)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       chunk_sample = chunk_of_sample(f, sample, &chunk);
+       if (t->stsz_sample_size > 0)
+               srs = (sample - chunk_sample) * t->stsz_sample_size;
+       else {
+               for (srs = 0, n = chunk_sample; n < sample; n++)
+                       srs += t->stsz_table[n];
+       }
+       if (t->stco_entry_count > 0 && chunk > t->stco_entry_count)
+               offset = t->stco_chunk_offset[t->stco_entry_count - 1];
+       else if (t->stco_entry_count > 0)
+               offset = t->stco_chunk_offset[chunk - 1];
+       else
+               offset = 8;
+       set_position(f, offset + srs);
+       return 1;
+}
+
+/**
+ * Look up and return the size of the given sample in the stsz table.
+ *
+ * \param f See \ref mp4_close().
+ * \param sample The sample number of interest.
+ * \param result Sample size is returned here.
+ *
+ * For the sample argument the restriction mentioned in the documentation of
+ * \ref mp4_set_sample_position() applies as well.
+ *
+ * \return Standard. Like for \ref mp4_set_sample_position(), EINVAL is the
+ * only possible error.
+ */
+int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result)
+{
+       const struct mp4_track *t = &f->track;
+
+       if (sample >= t->stsz_sample_count)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       if (t->stsz_sample_size != 0)
+               *result = t->stsz_sample_size;
+       else
+               *result = t->stsz_table[sample];
+       return 1;
+}
+
+/**
+ * Return the sample rate stored in the stsd atom.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The sample rate is a property of the audio track of the mp4 file and is thus
+ * independent of the sample number.
+ *
+ * \return The function always returns a positive value because the open
+ * operation fails if the sample rate happens to be zero. A typical value is
+ * 44100.
+ */
+uint16_t mp4_get_sample_rate(const struct mp4 *f)
+{
+       return f->track.sample_rate;
+}
+
+/**
+ * Return the number of channels of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The returned channel count is guaranteed to be positive because the
+ * open operation fails if the mp4a atom is missing or contains a zero channel
+ * count.
+ */
+uint16_t mp4_get_channel_count(const struct mp4 *f)
+{
+       return f->track.channel_count;
+}
+
+/**
+ * Return the number of samples of the audio track.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * \return The sample count is read from the stsz atom during open.
+ */
+uint32_t mp4_num_samples(const struct mp4 *f)
+{
+       return f->track.stsz_sample_count;
+}
+
+/**
+ * Open an mp4 file in metadata-only mode.
+ *
+ * \param cb See \ref mp4_open().
+ * \param result See \ref mp4_open().
+ *
+ * This is similar to \ref mp4_open() but is cheaper because it only parses the
+ * metadata of the mp4 file. The only functions that can subsequently be called
+ * with the file handle returned here are \ref mp4_get_meta() and \ref
+ * mp4_update_meta().
+ *
+ * \return Standard.
+ *
+ * \sa \ref mp4_open(). The comment about ->write() and ->truncate() applies to
+ * this function as well.
+ */
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result)
+{
+       struct mp4 *f;
+       int ret = open_file(cb, true, &f);
+
+       if (ret < 0)
+               return ret;
+       *result = f;
+       return 1;
+}
+
+/**
+ * Return the metadata of an mp4 file.
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The caller is allowed to add, delete or modify the entries of the returned
+ * structure with the intention to pass the modified version to \ref
+ * mp4_update_meta().
+ *
+ * \return This never returns NULL, even if the file contains no metadata tag
+ * items. However, the meta count will be zero and the ->tags pointer NULL in
+ * this case.
+ */
+struct mp4_metadata *mp4_get_meta(struct mp4 *f)
+{
+       return &f->meta;
+}
+
+/** Total length of an on-disk metadata tag. */
+#define TAG_LEN(_len) (24 + (_len))
+static void create_ilst(const struct mp4_metadata *meta, uint8_t *out)
+{
+       for (unsigned n = 0; n < meta->count; n++) {
+               struct mp4_tag *tag = meta->tags + n;
+               unsigned len = strlen(tag->value);
+               const char *atom_name;
+
+               if (!strcasecmp(tag->item, "title"))
+                       atom_name = "\xA9" "nam";
+               else if (!strcasecmp(tag->item, "artist"))
+                       atom_name = "\xA9" "ART";
+               else if (!strcasecmp(tag->item, "album"))
+                       atom_name = "\xA9" "alb";
+               else if (!strcasecmp(tag->item, "date"))
+                       atom_name = "\xA9" "day";
+               else if (!strcasecmp(tag->item, "comment"))
+                       atom_name = "\xA9" "cmt";
+               else
+                       assert(false);
+               write_u32_be(out, TAG_LEN(len));
+               memcpy(out + 4, atom_name, 4);
+               write_u32_be(out + 8, 8 /* data atom header */
+                       + 8 /* flags + reserved */
+                       + len);
+               memcpy(out + 12, "data", 4);
+               write_u32_be(out + 16, 1); /* flags */
+               write_u32_be(out + 20, 0); /* reserved */
+               memcpy(out + 24, tag->value, len);
+               out += TAG_LEN(len);
+       }
+}
+
+static void *modify_moov(struct mp4 *f, uint32_t *out_size)
+{
+       int ret;
+       uint64_t total_base = f->moov_offset + 8;
+       uint32_t total_size = f->moov_size - 8;
+       uint32_t new_ilst_size = 0;
+       void *out_buffer;
+       uint8_t *p_out;
+       int32_t size_delta;
+       uint32_t tmp;
+
+       for (unsigned n = 0; n < f->meta.count; n++)
+               new_ilst_size += TAG_LEN(strlen(f->meta.tags[n].value));
+       size_delta = new_ilst_size - (f->ilst_size - 8);
+       *out_size = total_size + size_delta;
+       out_buffer = alloc(*out_size);
+       p_out = out_buffer;
+       set_position(f, total_base);
+       ret = read_data(f, p_out, f->udta_offset - total_base);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->udta_offset - total_base;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       ret = read_data(f, p_out, f->meta_offset - f->udta_offset - 8);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->meta_offset - f->udta_offset - 8;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       ret = read_data(f, p_out, f->ilst_offset - f->meta_offset - 8);
+       if (ret <= 0)
+               return NULL;
+       p_out += f->ilst_offset - f->meta_offset - 8;
+       ret = read_int32(f, &tmp);
+       if (ret <= 0)
+               return NULL;
+       write_u32_be(p_out, tmp + size_delta);
+       p_out += 4;
+       ret = read_data(f, p_out, 4);
+       if (ret <= 0)
+               return NULL;
+       p_out += 4;
+       create_ilst(&f->meta, p_out);
+       p_out += new_ilst_size;
+       set_position(f, f->ilst_offset + f->ilst_size);
+       ret = read_data(f, p_out, total_size - (f->ilst_offset - total_base)
+               - f->ilst_size);
+       if (ret <= 0)
+               return NULL;
+       return out_buffer;
+}
+
+static int write_data(struct mp4 *f, void *data, size_t size)
+{
+       while (size > 0) {
+               ssize_t ret = f->cb->write(f->cb->user_data, data, size);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -ERRNO_TO_PARA_ERROR(errno);
+               }
+               size -= ret;
+       }
+       return 1;
+}
+
+/**
+ * Write back the modified metadata items to the mp4 file.
+ *
+ * This is the only public function which modifies the contents of an mp4 file.
+ * This is achieved by calling the ->write() and ->truncate() methods of the
+ * callback structure passed to \ref mp4_open() or \ref mp4_open_meta().
+ *
+ * \param f See \ref mp4_close().
+ *
+ * The modified metadata structure does not need to be supplied to this
+ * function because it is part of the mp4 structure.
+ *
+ * \return Standard.
+ */
+int mp4_update_meta(struct mp4 *f)
+{
+       void *new_moov_data;
+       uint32_t new_moov_size;
+       uint8_t buf[8] = "----moov";
+       int ret;
+
+       set_position(f, 0);
+       new_moov_data = modify_moov(f, &new_moov_size);
+       if (!new_moov_data ) {
+               mp4_close(f);
+               return 0;
+       }
+       if (f->last_atom != ATOM_MOOV) {
+               set_position(f, f->moov_offset + 4);
+               ret = write_data(f, "free", 4); /* rename old moov to free */
+               if (ret < 0)
+                       goto free_moov;
+               /* write new moov atom at EOF */
+               f->cb->seek(f->cb->user_data, 0, SEEK_END);
+       } else /* overwrite old moov atom */
+               set_position(f, f->moov_offset);
+       write_u32_be(buf, new_moov_size + 8);
+       ret = write_data(f, buf, sizeof(buf));
+       if (ret < 0)
+               goto free_moov;
+       ret = write_data(f, new_moov_data, new_moov_size);
+       if (ret < 0)
+               goto free_moov;
+       ret = f->cb->truncate(f->cb->user_data);
+       if (ret < 0)
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+free_moov:
+       free(new_moov_data);
+       return ret;
+}
+
+/**
+ * Return the value of the given tag item.
+ *
+ * \param f See \ref mp4_close().
+ * \param item "artist", "title", "album", "comment", or "date".
+ *
+ * \return The function returns NULL if the given item is not in the above
+ * list. Otherwise, if the file does not contain a tag for the given item, the
+ * function also returns NULL. Otherwise a copy of the tag value is returned
+ * and the caller should free this memory when it is no longer needed.
+ */
+__malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item)
+{
+       for (unsigned n = 0; n < f->meta.count; n++)
+               if (!strcasecmp(f->meta.tags[n].item, item))
+                       return para_strdup(f->meta.tags[n].value);
+       return NULL;
+}
diff --git a/mp4.h b/mp4.h
new file mode 100644 (file)
index 0000000..c36a1f8
--- /dev/null
+++ b/mp4.h
@@ -0,0 +1,87 @@
+/** \file mp4.h Public API of the mp4 parser. */
+
+/**
+ * Callbacks provided by the user of the mp4 parsing API.
+ *
+ * A pointer to this structure is passed to the two public open functions. If
+ * the file is opened in read-only mode, the ->write() and ->truncate() methods
+ * won't be called and may thus be NULL. The ->read() and ->seek() methods
+ * must be supplied for either open type.
+ *
+ * All methods are supposed to work like their corresponding system calls.
+ * That is, they should return non-negative for success and -1 on failure. In
+ * the error case errno is expected to be set accordingly.
+ *
+ * \sa \ref mp4_open(), \ref mp4_open_meta().
+ */
+struct mp4_callback {
+       /** This pointer is propagated to each call of all methods. */
+       void *user_data;
+       /**
+        * This should return the number of bytes read on success. Short reads
+        * are OK: the function may return less than length.
+        */
+       ssize_t (*read)(void *user_data, void *buffer, size_t length);
+       /**
+        * This method is assumed to succeed. The implementation should simply
+        * abort on errors. Note that offsets beyond EOF must not be regarded
+        * as invalid arguments.
+        */
+       off_t (*seek)(void *user_data, off_t offset, int whence);
+       /**
+        * Like the write() system call, this should return the number of bytes
+        * written. Short writes are OK: the function may return less than
+        * count.
+        */
+       ssize_t (*write)(void *user_data, void *buffer, size_t count);
+       /**
+        * Unlike the truncate system call, this function does not receive an
+        * offset. The method is expected to truncate the file to the offset
+        * given by the current file position instead.
+        */
+       int (*truncate)(void *user_data);
+};
+
+/** Specifies one metadata tag. Both fields are 0-terminated strings. */
+struct mp4_tag {
+       /** The item name: "artist", "title", "album", "comment", or "date". */
+       char *item;
+       /** An arbitrary string value. */
+       char *value;
+};
+
+/**
+ * An array of name/value pairs.
+ *
+ * This structure is initialized when the mp4 file is opened in either mode.
+ * If the file contains metadata items other than the standard five, those
+ * non-standard items are not included in the array. After a successful open, a
+ * pointer to the metadata structure can be obtained via \ref mp4_get_meta().
+ */
+struct mp4_metadata {
+       /** It's OK to change this, for example by calling realloc(). */
+       struct mp4_tag *tags;
+       /** The number of entries of the array. */
+       unsigned count;
+};
+
+/**
+ * The mp4 file handle.
+ *
+ * A pointer to this opaque structure is returned by the two open functions.
+ * All other functions of the mp4 API receive a pointer of this type.
+ */
+struct mp4;
+
+int mp4_set_sample_position(struct mp4 *f, uint32_t sample);
+int mp4_open(const struct mp4_callback *cb, struct mp4 **result);
+void mp4_close(struct mp4 *f);
+int mp4_get_sample_size(const struct mp4 *f, uint32_t sample, uint32_t *result);
+uint16_t mp4_get_sample_rate(const struct mp4 *f);
+uint16_t mp4_get_channel_count(const struct mp4 *f);
+uint32_t mp4_num_samples(const struct mp4 *f);
+uint64_t mp4_get_duration(const struct mp4 *f);
+int mp4_open_meta(const struct mp4_callback *cb, struct mp4 **result);
+struct mp4_metadata *mp4_get_meta(struct mp4 *f);
+int mp4_update_meta(struct mp4 *f);
+__malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item);
diff --git a/net.c b/net.c
index e1951e5e8d87501b1ef9096d3f3df64117e904f0..9b3624428d5e8a0f63df7d814783df20afa54385 100644 (file)
--- a/net.c
+++ b/net.c
 #include "list.h"
 #include "fd.h"
 
 #include "list.h"
 #include "fd.h"
 
+/* Whether the given address conforms to the IPv4 address format. */
+static inline bool is_valid_ipv4_address(const char *address)
+{
+       struct in_addr test_it;
+       return inet_pton(AF_INET, address, &test_it) != 0;
+}
+
 /**
  * Parse and validate IPv4 address/netmask string.
  *
 /**
  * Parse and validate IPv4 address/netmask string.
  *
@@ -58,13 +65,6 @@ failed:
        return NULL;
 }
 
        return NULL;
 }
 
-
-/**
- * Match string as a candidate IPv4 address.
- *
- * \param address The string to match.
- * \return True if \a address has "dot-quad" format.
- */
 static bool is_v4_dot_quad(const char *address)
 {
        bool result;
 static bool is_v4_dot_quad(const char *address)
 {
        bool result;
@@ -77,6 +77,13 @@ static bool is_v4_dot_quad(const char *address)
        return result;
 }
 
        return result;
 }
 
+/* Whether a string conforms to IPv6 address format (RFC 4291). */
+static inline bool is_valid_ipv6_address(const char *address)
+{
+       struct in6_addr test_it;
+       return inet_pton(AF_INET6, address, &test_it) != 0;
+}
+
 /**
  * Perform basic syntax checking on the host-part of an URL:
  *
 /**
  * Perform basic syntax checking on the host-part of an URL:
  *
@@ -183,7 +190,7 @@ failed:
  * \return In all cases the returned string is a allocated with malloc(3) and
  * has to be freed by the caller.
  */
  * \return In all cases the returned string is a allocated with malloc(3) and
  * has to be freed by the caller.
  */
-char *format_url(const char *url, int default_port)
+__malloc char *format_url(const char *url, int default_port)
 {
        char host[MAX_HOSTLEN];
        int url_port;
 {
        char host[MAX_HOSTLEN];
        int url_port;
@@ -205,7 +212,7 @@ char *format_url(const char *url, int default_port)
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
  *
  * \param transport Transport protocol name (e.g. "udp", "tcp"), or NULL.
  * \return Pointer to static result buffer.
  *
- * \sa getservent(3), services(5), nsswitch.conf(5).
+ * \sa getservbyport(3), services(5), nsswitch.conf(5).
  */
 const char *stringify_port(int port, const char *transport)
 {
  */
 const char *stringify_port(int port, const char *transport)
 {
@@ -224,12 +231,13 @@ const char *stringify_port(int port, const char *transport)
        return service;
 }
 
        return service;
 }
 
-/**
- * Determine the socket type for a given layer-4 protocol.
- *
- * \param l4type The symbolic name of the transport-layer protocol.
- *
- * \sa ip(7), socket(2).
+#ifndef SOCK_DCCP
+#define SOCK_DCCP 6 /**< Linux socket type. */
+#endif
+
+/*
+ * Determine the socket type, given the symbolic name of the transport-layer
+ * protocol. See ip(7), socket(2).
  */
 static inline int sock_type(const unsigned l4type)
 {
  */
 static inline int sock_type(const unsigned l4type)
 {
@@ -241,9 +249,7 @@ static inline int sock_type(const unsigned l4type)
        return -1;              /* not supported here */
 }
 
        return -1;              /* not supported here */
 }
 
-/**
- * Pretty-print transport-layer name.
- */
+/* Pretty-print transport-layer name. */
 static const char *layer4_name(const unsigned l4type)
 {
        switch (l4type) {
 static const char *layer4_name(const unsigned l4type)
 {
        switch (l4type) {
@@ -273,7 +279,12 @@ struct pre_conn_opt {
        struct list_head node;          /**< FIFO, as sockopt order matters. */
 };
 
        struct list_head node;          /**< FIFO, as sockopt order matters. */
 };
 
-/** FIFO list of pre-connection socket options to be set */
+/**
+ * List of pre-connection socket options to be set.
+ *
+ * This list contains transport-layer independent encapsulation of socket
+ * options that need to be registered prior to setting up a connection.
+ */
 struct flowopts {
        struct list_head sockopts;
 };
 struct flowopts {
        struct list_head sockopts;
 };
@@ -286,7 +297,7 @@ struct flowopts {
  */
 struct flowopts *flowopt_new(void)
 {
  */
 struct flowopts *flowopt_new(void)
 {
-       struct flowopts *new = para_malloc(sizeof(*new));
+       struct flowopts *new = alloc(sizeof(*new));
 
        init_list_head(&new->sockopts);
        return new;
 
        init_list_head(&new->sockopts);
        return new;
@@ -307,7 +318,7 @@ struct flowopts *flowopt_new(void)
 void flowopt_add(struct flowopts *fo, int lev, int opt,
                const char *name, const void *val, int len)
 {
 void flowopt_add(struct flowopts *fo, int lev, int opt,
                const char *name, const void *val, int len)
 {
-       struct pre_conn_opt *new = para_malloc(sizeof(*new));
+       struct pre_conn_opt *new = alloc(sizeof(*new));
 
        new->sock_option = opt;
        new->sock_level  = lev;
 
        new->sock_option = opt;
        new->sock_level  = lev;
@@ -317,7 +328,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
                new->opt_val = NULL;
                new->opt_len = 0;
        } else {
                new->opt_val = NULL;
                new->opt_len = 0;
        } else {
-               new->opt_val = para_malloc(len);
+               new->opt_val = alloc(len);
                new->opt_len = len;
                memcpy(new->opt_val, val, len);
        }
                new->opt_len = len;
                memcpy(new->opt_val, val, len);
        }
@@ -325,7 +336,7 @@ void flowopt_add(struct flowopts *fo, int lev, int opt,
        list_add_tail(&new->node, &fo->sockopts);
 }
 
        list_add_tail(&new->node, &fo->sockopts);
 }
 
-/** Set the entire bunch of pre-connection options at once. */
+/* Set the entire bunch of pre-connection options at once. */
 static void flowopt_setopts(int sockfd, struct flowopts *fo)
 {
        struct pre_conn_opt *pc;
 static void flowopt_setopts(int sockfd, struct flowopts *fo)
 {
        struct pre_conn_opt *pc;
@@ -509,7 +520,7 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
        if (ai)
                freeaddrinfo(ai);
        if (ret < 0) {
        if (ai)
                freeaddrinfo(ai);
        if (ret < 0) {
-               PARA_ERROR_LOG("can not create %s socket %s#%d.\n",
+               PARA_NOTICE_LOG("can not create %s socket %s#%d.\n",
                layer4_name(l4type), host? host : (passive?
                "[loopback]" : "[localhost]"), port_number);
        }
                layer4_name(l4type), host? host : (passive?
                "[loopback]" : "[localhost]"), port_number);
        }
@@ -571,11 +582,7 @@ int para_listen_simple(unsigned l4type, uint16_t port)
        return para_listen(l4type, NULL, port);
 }
 
        return para_listen(l4type, NULL, port);
 }
 
-/**
- * Determine IPv4/v6 socket address length.
- * \param sa Container of IPv4 or IPv6 address.
- * \return Address-family dependent address length.
- */
+/* Compute the address-family dependent address length of an IPv4/v6 socket. */
 static socklen_t salen(const struct sockaddr *sa)
 {
        assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
 static socklen_t salen(const struct sockaddr *sa)
 {
        assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
@@ -585,7 +592,7 @@ static socklen_t salen(const struct sockaddr *sa)
                : sizeof(struct sockaddr_in);
 }
 
                : sizeof(struct sockaddr_in);
 }
 
-/** True if @ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
+/* True if ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */
 static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
 {
        const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
 static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
 {
        const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss;
@@ -593,10 +600,10 @@ static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss)
        return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr);
 }
 
        return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr);
 }
 
-/**
+/*
  * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
  * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address.
- * \param ss Container of IPv4/6 address.
- * \return Pointer to normalized address (may be static storage).
+ * ss: Container of IPv4/6 address.
+ * Returns: Pointer to normalized address (may be static storage).
  *
  * \sa RFC 3493.
  */
  *
  * \sa RFC 3493.
  */
@@ -617,7 +624,7 @@ normalize_ip_address(const struct sockaddr_storage *ss)
        return (const struct sockaddr *)ss;
 }
 
        return (const struct sockaddr *)ss;
 }
 
-/**
+/*
  * Generic/fallback MTU values
  *
  * These are taken from RFC 1122, RFC 2460, and RFC 5405.
  * Generic/fallback MTU values
  *
  * These are taken from RFC 1122, RFC 2460, and RFC 5405.
@@ -632,7 +639,7 @@ static inline int generic_mtu(const int af_type)
        return af_type == AF_INET6 ? 1280 : 576;
 }
 
        return af_type == AF_INET6 ? 1280 : 576;
 }
 
-/** Crude approximation of IP header overhead - neglecting options. */
+/* Crude approximation of IP header overhead - neglecting options. */
 static inline int estimated_header_overhead(const int af_type)
 {
        return af_type == AF_INET6 ? 40 : 20;
 static inline int estimated_header_overhead(const int af_type)
 {
        return af_type == AF_INET6 ? 40 : 20;
@@ -801,25 +808,21 @@ int recv_buffer(int fd, char *buf, size_t size)
  * Wrapper around the accept system call.
  *
  * \param fd The listening socket.
  * Wrapper around the accept system call.
  *
  * \param fd The listening socket.
- * \param rfds An optional fd_set pointer.
  * \param addr Structure which is filled in with the address of the peer socket.
  * \param size Should contain the size of the structure pointed to by \a addr.
  * \param new_fd Result pointer.
  *
  * \param addr Structure which is filled in with the address of the peer socket.
  * \param size Should contain the size of the structure pointed to by \a addr.
  * \param new_fd Result pointer.
  *
- * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is
- * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept().
+ * Accept incoming connections on addr, retry if interrupted.
  *
  * \return Negative on errors, zero if no connections are present to be accepted,
  * one otherwise.
  *
  * \sa accept(2).
  */
  *
  * \return Negative on errors, zero if no connections are present to be accepted,
  * one otherwise.
  *
  * \sa accept(2).
  */
-int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
+int para_accept(int fd, void *addr, socklen_t size, int *new_fd)
 {
        int ret;
 
 {
        int ret;
 
-       if (rfds && !FD_ISSET(fd, rfds))
-               return 0;
        do
                ret = accept(fd, (struct sockaddr *) addr, &size);
        while (ret < 0 && errno == EINTR);
        do
                ret = accept(fd, (struct sockaddr *) addr, &size);
        while (ret < 0 && errno == EINTR);
@@ -833,6 +836,10 @@ int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
        return -ERRNO_TO_PARA_ERROR(errno);
 }
 
+#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
+#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
+#endif
+
 /**
  * Probe the list of DCCP CCIDs configured on this host.
  * \param ccid_array Pointer to return statically allocated array in.
 /**
  * Probe the list of DCCP CCIDs configured on this host.
  * \param ccid_array Pointer to return statically allocated array in.
@@ -847,7 +854,7 @@ int dccp_available_ccids(uint8_t **ccid_array)
        socklen_t nccids = sizeof(ccids);
        int ret, fd;
 
        socklen_t nccids = sizeof(ccids);
        int ret, fd;
 
-       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       ret = fd = makesock(IPPROTO_DCCP, true /* passive */, NULL, 0, NULL);
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
 
@@ -865,6 +872,18 @@ int dccp_available_ccids(uint8_t **ccid_array)
        return nccids;
 }
 
        return nccids;
 }
 
+/**
+ * The buffer size of the sun_path component of struct sockaddr_un.
+ *
+ * While glibc doesn't define UNIX_PATH_MAX, it documents it has being limited
+ * to 108 bytes. On NetBSD it is only 104 bytes though. We trust UNIX_PATH_MAX
+ * if it is defined and use the size of the ->sun_path member otherwise. This
+ * should be safe everywhere.
+ */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
+#endif
+
 /*
  * Prepare a structure for AF_UNIX socket addresses.
  *
 /*
  * Prepare a structure for AF_UNIX socket addresses.
  *
diff --git a/net.h b/net.h
index 2256f376497b89d3382e2bf80ce09725ef64ab02..33acfc890495087991c964397371e4edf9b574f1 100644 (file)
--- a/net.h
+++ b/net.h
 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 /** \file net.h exported symbols from net.c */
 
 /* Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 /** \file net.h exported symbols from net.c */
 
-/**
- * The buffer size of the sun_path component of struct sockaddr_un.
- *
- * While glibc doesn't define \p UNIX_PATH_MAX, it documents it has being
- * limited to 108 bytes. On NetBSD it is only 104 bytes though. We trust \p
- * UNIX_PATH_MAX if it is defined and use the size of the ->sun_path member
- * otherwise. This should be safe everywhere.
- */
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path))
-#endif
-
 /* Userland defines for Linux DCCP support. */
 
 /* Userland defines for Linux DCCP support. */
 
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33 /**< IANA assigned value. */
-#endif
-
-#ifndef SOCK_DCCP
-#define SOCK_DCCP 6 /**< Linux socket type. */
-#endif
-
-#ifndef DCCP_SOCKOPT_RX_CCID
-/** Per-connection CCID support (set/get the RX CCID, since v2.6.30-rc1). */
-#define DCCP_SOCKOPT_RX_CCID 15
-#endif
-
 #ifndef SOL_DCCP
 #define SOL_DCCP 269 /**< Linux socket level. */
 #endif
 
 #ifndef SOL_DCCP
 #define SOL_DCCP 269 /**< Linux socket level. */
 #endif
 
-#ifndef DCCP_SOCKOPT_GET_CUR_MPS
-#define DCCP_SOCKOPT_GET_CUR_MPS  5 /**< Max packet size, RFC 4340, 14. */
-#endif
-
-#ifndef DCCP_SOCKOPT_AVAILABLE_CCIDS
-#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12 /**< List of supported CCIDs. */
-#endif
-
-#ifndef DCCP_SOCKOPT_CCID
-#define DCCP_SOCKOPT_CCID 13 /**< Sets both TX/RX CCID. */
-#endif
-
-#ifndef DCCP_SOCKOPT_TX_CCID
-#define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
-#endif
-
 /** The maximum length of the host component in an URL. */
 #define MAX_HOSTLEN 256
 
 /** The maximum length of the host component in an URL. */
 #define MAX_HOSTLEN 256
 
-/**
- * Flowopts: Transport-layer independent encapsulation of socket options
- *           that need to be registered prior to setting up a connection.
- */
+/* Opaque, only known to net.c. */
 struct flowopts;
 
 struct flowopts;
 
-extern struct flowopts *flowopt_new(void);
-extern void flowopt_add(struct flowopts *fo, int level, int opt,
+struct flowopts *flowopt_new(void);
+void flowopt_add(struct flowopts *fo, int level, int opt,
                const char *name, const void *val, int len);
 void flowopt_cleanup(struct flowopts *fo);
                const char *name, const void *val, int len);
 void flowopt_cleanup(struct flowopts *fo);
-/** Flowopt shortcut macros */
-#define OPT_ADD(fo, lev, opt, val, len)        flowopt_add(fo, lev, opt, #opt, val, len)
 
 /**
  * Functions to parse and validate (parts of) URLs.
  */
 
 /**
  * Functions to parse and validate (parts of) URLs.
  */
-extern char *parse_cidr(const char *cidr,
-                       char *addr, ssize_t addrlen, int32_t *netmask);
-extern char *parse_url(const char *url,
-                      char *host, ssize_t hostlen, int32_t *port);
-char *format_url(const char *url, int default_port);
-extern const char *stringify_port(int port, const char *transport);
-/**
- * Ensure that string conforms to the IPv4 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if \a address conforms to the IPv4 address format, else 0.
- */
-_static_inline_ bool is_valid_ipv4_address(const char *address)
-{
-       struct in_addr test_it;
-
-       return inet_pton(AF_INET, address, &test_it) != 0;
-}
-
-/**
- * Ensure that string conforms to IPv6 address format.
- *
- * \param address The address string to check.
- *
- * \return 1 if string has a valid IPv6 address syntax, 0 if not.
- * \sa RFC 4291.
- */
-_static_inline_ bool is_valid_ipv6_address(const char *address)
-{
-       struct in6_addr test_it;
-
-       return inet_pton(AF_INET6, address, &test_it) != 0;
-}
+char *parse_cidr(const char *cidr,
+               char *addr, ssize_t addrlen, int32_t *netmask);
+char *parse_url(const char *url,
+               char *host, ssize_t hostlen, int32_t *port);
+__malloc char *format_url(const char *url, int default_port);
+const char *stringify_port(int port, const char *transport);
 
 int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result);
 
 int lookup_address(unsigned l4type, bool passive, const char *host,
                int port_number, struct addrinfo **result);
@@ -114,10 +40,9 @@ int makesock(unsigned l4type, bool passive, const char *host,
 int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo);
 
 int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                struct flowopts *fo);
 
-static inline int para_connect_simple(unsigned l4type,
-                                     const char *host, uint16_t port)
+static inline int para_connect(unsigned l4type, const char *host, uint16_t port)
 {
 {
-       return makesock(l4type, 0, host, port, NULL);
+       return makesock(l4type, false, host, port, NULL);
 }
 
 void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
 }
 
 void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia);
@@ -133,17 +58,17 @@ int para_listen(unsigned l4type, const char *addr, uint16_t port);
 int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
 int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
-extern char *remote_name(int sockfd);
+char *remote_name(int sockfd);
 
 /**
  * Determining maximum payload (packet) size
  */
 
 /**
  * Determining maximum payload (packet) size
  */
-extern int generic_max_transport_msg_size(int sockfd);
+int generic_max_transport_msg_size(int sockfd);
 
 int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
 
 
 int recv_bin_buffer(int fd, char *buf, size_t size);
 int recv_buffer(int fd, char *buf, size_t size);
 
-int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd);
+int para_accept(int fd, void *addr, socklen_t size, int *new_fd);
 int create_local_socket(const char *name);
 int connect_local_socket(const char *name);
 int recv_cred_buffer(int, char *, size_t);
 int create_local_socket(const char *name);
 int connect_local_socket(const char *name);
 int recv_cred_buffer(int, char *, size_t);
@@ -152,8 +77,6 @@ ssize_t send_cred_buffer(int, char*);
 /**
  * Functions and definitions to support \p IPPROTO_DCCP
  */
 /**
  * Functions and definitions to support \p IPPROTO_DCCP
  */
-/** Estimated worst-case length of a DCCP header including options. */
-#define DCCP_MAX_HEADER                128
 /** Hardcoded maximum number of separate CCID modules compiled into a host. */
 #define DCCP_MAX_HOST_CCIDS    20
 /** Hardcoded maximum number of separate CCID modules compiled into a host. */
 #define DCCP_MAX_HOST_CCIDS    20
-extern int dccp_available_ccids(uint8_t **ccid_array);
+int dccp_available_ccids(uint8_t **ccid_array);
index 3e36bdd5b1e9d3615695f975541bf69c0cc05093..0a27a4acebc1652da03dea1573bf337eaf9c52a9 100644 (file)
@@ -167,7 +167,7 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
        PARA_INFO_LOG("%" PRIu32 " seconds, %d frames/chunk\n",
                afhi->seconds_total, frames_per_chunk);
        ct_size = 250;
        PARA_INFO_LOG("%" PRIu32 " seconds, %d frames/chunk\n",
                afhi->seconds_total, frames_per_chunk);
        ct_size = 250;
-       afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(ct_size, sizeof(uint32_t));
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
        oss.returned = afhi->header_len;
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
        oss.returned = afhi->header_len;
@@ -179,9 +179,9 @@ int oac_get_file_info(char *map, size_t numbytes, struct afh_info *afhi,
                        j++;
                        if (j >= ct_size) {
                                ct_size *= 2;
                        j++;
                        if (j >= ct_size) {
                                ct_size *= 2;
-                               afhi->chunk_table = para_realloc(
+                               afhi->chunk_table = arr_realloc(
                                        afhi->chunk_table,
                                        afhi->chunk_table,
-                                       ct_size * sizeof(uint32_t));
+                                       ct_size, sizeof(uint32_t));
                        }
                        afhi->chunk_table[j] = oss.returned;
                }
                        }
                        afhi->chunk_table[j] = oss.returned;
                }
@@ -365,9 +365,9 @@ struct oac_custom_header {
  *
  * \sa \ref oac_custom_header_init().
  */
  *
  * \sa \ref oac_custom_header_init().
  */
-struct oac_custom_header *oac_custom_header_new(void)
+__malloc struct oac_custom_header *oac_custom_header_new(void)
 {
 {
-       return para_calloc(sizeof(struct oac_custom_header));
+       return zalloc(sizeof(struct oac_custom_header));
 }
 
 /**
 }
 
 /**
index e0cf2d40c3ffb825c9f19338aa792558cb1c8742..03bf88b5794b0374fba493a79e502e4a89a686ca 100644 (file)
@@ -5,7 +5,7 @@
  * handlers that use the ogg container format.
  */
 
  * handlers that use the ogg container format.
  */
 
-struct oac_custom_header *oac_custom_header_new(void);
+__malloc struct oac_custom_header *oac_custom_header_new(void);
 void oac_custom_header_init(int serial, struct oac_custom_header *h);
 int oac_custom_header_append(ogg_packet *op, struct oac_custom_header *h);
 void oac_custom_header_flush(struct oac_custom_header *h);
 void oac_custom_header_init(int serial, struct oac_custom_header *h);
 int oac_custom_header_append(ogg_packet *op, struct oac_custom_header *h);
 void oac_custom_header_flush(struct oac_custom_header *h);
index 708a27e52b68c1c2add01a3b3fe39f721485e39f..b1aec4bc2c4cb0049ed942d3b7e7708b2de84688 100644 (file)
@@ -88,7 +88,7 @@ static const ov_callbacks ovc = {
 
 static void ogg_open(struct filter_node *fn)
 {
 
 static void ogg_open(struct filter_node *fn)
 {
-       fn->private_data = para_calloc(sizeof(struct private_oggdec_data));
+       fn->private_data = zalloc(sizeof(struct private_oggdec_data));
        fn->min_iqs = 8000;
 }
 
        fn->min_iqs = 8000;
 }
 
@@ -121,7 +121,7 @@ static int ogg_init(struct filter_node *fn)
        struct btr_node *btrn = fn->btrn;
        int ret, oret;
        size_t iqs;
        struct btr_node *btrn = fn->btrn;
        int ret, oret;
        size_t iqs;
-       struct OggVorbis_File *vf = para_malloc(sizeof(*vf));
+       struct OggVorbis_File *vf = alloc(sizeof(*vf));
 
        PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n",
                btr_get_input_queue_size(btrn), fn->min_iqs);
 
        PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n",
                btr_get_input_queue_size(btrn), fn->min_iqs);
@@ -178,13 +178,13 @@ out:
 
 /**
   * Allocate chunks of this size and produce at most one chunk of output per
 
 /**
   * Allocate chunks of this size and produce at most one chunk of output per
-  * ->post_select() invocation. If the buffer could only be filled partially
+  * ->post_monitor() invocation. If the buffer could only be filled partially
   * due to insufficient input being available, it is shrunk to the real output
   * size and the resized buffer is fed into the output queue.
   */
 #define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
 
   * due to insufficient input being available, it is shrunk to the real output
   * size and the resized buffer is fed into the output queue.
   */
 #define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
 
-static void ogg_pre_select(struct sched *s, void *context)
+static void ogg_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
@@ -201,7 +201,7 @@ static void ogg_pre_select(struct sched *s, void *context)
        sched_min_delay(s);
 }
 
        sched_min_delay(s);
 }
 
-static int ogg_post_select(__a_unused struct sched *s, void *context)
+static int ogg_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_oggdec_data *pod = fn->private_data;
@@ -211,7 +211,7 @@ static int ogg_post_select(__a_unused struct sched *s, void *context)
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
-               if (ret != -E_BTR_EOF) /* fatal error */
+               if (ret != -E_EOF) /* fatal error */
                        goto out;
                if (fn->min_iqs == 0 && !pod->have_more) /* EOF */
                        goto out;
                        goto out;
                if (fn->min_iqs == 0 && !pod->have_more) /* EOF */
                        goto out;
@@ -228,7 +228,7 @@ static int ogg_post_select(__a_unused struct sched *s, void *context)
                goto out;
        }
        have = 0;
                goto out;
        }
        have = 0;
-       buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
+       buf = alloc(OGGDEC_OUTPUT_CHUNK_SIZE);
        for (;;) {
                ret = ov_read(pod->vf, buf + have, OGGDEC_OUTPUT_CHUNK_SIZE - have,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
        for (;;) {
                ret = ov_read(pod->vf, buf + have, OGGDEC_OUTPUT_CHUNK_SIZE - have,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
@@ -262,7 +262,7 @@ out:
 const struct filter lsg_filter_cmd_com_oggdec_user_data = {
        .open = ogg_open,
        .close = ogg_close,
 const struct filter lsg_filter_cmd_com_oggdec_user_data = {
        .open = ogg_open,
        .close = ogg_close,
-       .pre_select = ogg_pre_select,
-       .post_select = ogg_post_select,
+       .pre_monitor = ogg_pre_monitor,
+       .post_monitor = ogg_post_monitor,
        .execute = oggdec_execute
 };
        .execute = oggdec_execute
 };
index 32891cbb012b66c0eaf5b8ca56f2c4288cd3bb1b..f696cd9e83606bc4e6bdd89d666f0885575f1d9f 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -11,6 +11,7 @@
 #include <openssl/sha.h>
 #include <openssl/bn.h>
 #include <openssl/aes.h>
 #include <openssl/sha.h>
 #include <openssl/bn.h>
 #include <openssl/aes.h>
+#include <openssl/evp.h>
 
 #include "para.h"
 #include "error.h"
 
 #include "para.h"
 #include "error.h"
@@ -36,12 +37,8 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
 }
 
 /*
 }
 
 /*
- * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
- * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
- * not readable, the function calls exit().
- *
- * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
- * random(3), \ref para_random().
+ * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Then seed the
+ * PRNG used by random(3) with a random seed obtained from SSL.
  */
 void crypt_init(void)
 {
  */
 void crypt_init(void)
 {
@@ -100,7 +97,7 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
        return bnsize + 4;
 }
 
        return bnsize + 4;
 }
 
-static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
+static int read_public_key(const unsigned char *blob, int blen, RSA **result)
 {
        int ret;
        RSA *rsa;
 {
        int ret;
        RSA *rsa;
@@ -152,7 +149,7 @@ bio_free:
        return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
 }
 
        return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
 }
 
-static int read_private_rsa_params(const unsigned char *blob,
+static int read_openssh_private_key(const unsigned char *blob,
                const unsigned char *end, RSA **result)
 {
        int ret;
                const unsigned char *end, RSA **result)
 {
        int ret;
@@ -219,11 +216,11 @@ static int read_private_rsa_params(const unsigned char *blob,
        rsa->n = n;
        rsa->e = e;
        rsa->d = d;
        rsa->n = n;
        rsa->e = e;
        rsa->d = d;
+       rsa->iqmp = iqmp;
        rsa->p = p;
        rsa->q = q;
        rsa->dmp1 = dmp1;
        rsa->dmq1 = dmq1;
        rsa->p = p;
        rsa->q = q;
        rsa->dmp1 = dmp1;
        rsa->dmq1 = dmq1;
-       rsa->iqmp = iqmp;
 #endif
        *result = rsa;
        ret = 1;
 #endif
        *result = rsa;
        ret = 1;
@@ -270,7 +267,7 @@ static int get_private_key(const char *path, RSA **rsa)
                if (ret < 0)
                        goto free_blob;
                PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
                if (ret < 0)
                        goto free_blob;
                PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
-               ret = read_private_rsa_params(blob + ret, end, rsa);
+               ret = read_openssh_private_key(blob + ret, end, rsa);
        } else
                ret = read_pem_private_key(path, rsa);
 free_blob:
        } else
                ret = read_pem_private_key(path, rsa);
 free_blob:
@@ -283,34 +280,34 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        unsigned char *blob;
        size_t decoded_size;
        int ret;
        unsigned char *blob;
        size_t decoded_size;
        int ret;
-       struct asymmetric_key *key = para_malloc(sizeof(*key));
+       struct asymmetric_key *pub = alloc(sizeof(*pub));
 
        ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                goto out;
 
        ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                goto out;
-       ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
+       ret = read_public_key(blob + ret, decoded_size - ret, &pub->rsa);
        if (ret < 0)
                goto free_blob;
        if (ret < 0)
                goto free_blob;
-       ret = RSA_size(key->rsa);
+       ret = RSA_size(pub->rsa);
        assert(ret > 0);
        assert(ret > 0);
-       *result = key;
+       *result = pub;
 free_blob:
        free(blob);
 out:
        if (ret < 0) {
 free_blob:
        free(blob);
 out:
        if (ret < 0) {
-               free(key);
+               free(pub);
                *result = NULL;
                PARA_ERROR_LOG("can not load key %s\n", key_file);
        }
        return ret;
 }
 
                *result = NULL;
                PARA_ERROR_LOG("can not load key %s\n", key_file);
        }
        return ret;
 }
 
-void apc_free_pubkey(struct asymmetric_key *key)
+void apc_free_pubkey(struct asymmetric_key *pub)
 {
 {
-       if (!key)
+       if (!pub)
                return;
                return;
-       RSA_free(key->rsa);
-       free(key);
+       RSA_free(pub->rsa);
+       free(pub);
 }
 
 int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
 }
 
 int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
@@ -324,7 +321,7 @@ int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
                return ret;
        if (inlen < 0)
                return -E_RSA;
                return ret;
        if (inlen < 0)
                return -E_RSA;
-       priv = para_malloc(sizeof(*priv));
+       priv = alloc(sizeof(*priv));
        ret = get_private_key(key_file, &priv->rsa);
        if (ret < 0) {
                free(priv);
        ret = get_private_key(key_file, &priv->rsa);
        if (ret < 0) {
                free(priv);
@@ -366,7 +363,7 @@ struct stream_cipher {
 
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
 
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
-       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+       struct stream_cipher *sc = alloc(sizeof(*sc));
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        sc->aes = EVP_CIPHER_CTX_new();
 
        assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
        sc->aes = EVP_CIPHER_CTX_new();
@@ -390,7 +387,7 @@ static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
 
        *dst = (typeof(*dst)) {
                /* Add one for the terminating zero byte. */
 
        *dst = (typeof(*dst)) {
                /* Add one for the terminating zero byte. */
-               .iov_base = para_malloc(inlen + 1),
+               .iov_base = alloc(inlen + 1),
                .iov_len = inlen
        };
        ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
                .iov_len = inlen
        };
        ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
@@ -409,16 +406,24 @@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
 
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
 
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
-       SHA_CTX c;
-       SHA1_Init(&c);
-       SHA1_Update(&c, data, len);
-       SHA1_Final(hash, &c);
+       EVP_MD_CTX *c = EVP_MD_CTX_new();
+       int ret = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
+       assert(ret != 0);
+       ret = EVP_DigestUpdate(c, data, len);
+       assert(ret != 0);
+       ret = EVP_DigestFinal_ex(c, hash, NULL);
+       assert(ret != 0);
+       EVP_MD_CTX_free(c);
 }
 
 void hash2_function(const char *data, unsigned long len, unsigned char *hash)
 {
 }
 
 void hash2_function(const char *data, unsigned long len, unsigned char *hash)
 {
-       SHA256_CTX c;
-       SHA256_Init(&c);
-       SHA256_Update(&c, data, len);
-       SHA256_Final(hash, &c);
+       EVP_MD_CTX *c = EVP_MD_CTX_new();
+       int ret = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
+       assert(ret != 0);
+       ret = EVP_DigestUpdate(c, data, len);
+       assert(ret != 0);
+       ret = EVP_DigestFinal_ex(c, hash, NULL);
+       assert(ret != 0);
+       EVP_MD_CTX_free(c);
 }
 }
index dca6cfbad1ce3ff8cdf6d820b01bf33bf9a02110..0a291bb16c18b6705b70ff0260e97b502068f500 100644 (file)
@@ -179,7 +179,7 @@ static size_t opus_make_meta_packet(struct taginfo *tags, char **result)
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
-       buf = p = para_malloc(sz + 1);
+       buf = p = alloc(sz + 1);
        memcpy(p, OPUS_COMMENT_HEADER, strlen(OPUS_COMMENT_HEADER));
        p += strlen(OPUS_COMMENT_HEADER);
        write_u32(p, comment_sz);
        memcpy(p, OPUS_COMMENT_HEADER, strlen(OPUS_COMMENT_HEADER));
        p += strlen(OPUS_COMMENT_HEADER);
        write_u32(p, comment_sz);
index 10ed394d295072d909441fe211fbcdc695ff62d0..f36990faf43a4110c8e7f7dc2f822f5a8e09046c 100644 (file)
@@ -86,7 +86,7 @@ static int opusdec_execute(struct btr_node *btrn, const char *cmd,
 
 static void opusdec_open(struct filter_node *fn)
 {
 
 static void opusdec_open(struct filter_node *fn)
 {
-       struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
+       struct opusdec_context *ctx = zalloc(sizeof(*ctx));
 
        ogg_sync_init(&ctx->oy);
        fn->private_data = ctx;
 
        ogg_sync_init(&ctx->oy);
        fn->private_data = ctx;
@@ -153,7 +153,7 @@ static void opusdec_add_output(short *pcm, int frames_available,
 
        if (tmp_skip > 0) {
                short *in = pcm + ctx->channels * tmp_skip;
 
        if (tmp_skip > 0) {
                short *in = pcm + ctx->channels * tmp_skip;
-               short *out = para_malloc(bytes);
+               short *out = alloc(bytes);
                memcpy(out, in, bytes);
                free(pcm);
                pcm = out;
                memcpy(out, in, bytes);
                free(pcm);
                pcm = out;
@@ -193,7 +193,7 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
        /* don't care for anything except opus eos */
        if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
                ctx->eos = true;
        /* don't care for anything except opus eos */
        if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
                ctx->eos = true;
-       output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
+       output = arr_alloc(sizeof(short) * ctx->channels, MAX_FRAME_SIZE);
        ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
                op->bytes, output, MAX_FRAME_SIZE, 0);
        if (ret < 0) {
        ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
                op->bytes, output, MAX_FRAME_SIZE, 0);
        if (ret < 0) {
@@ -207,7 +207,7 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
 
 #define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
 
 
 #define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
 
-static int opusdec_post_select(__a_unused struct sched *s, void *context)
+static int opusdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
@@ -217,7 +217,7 @@ static int opusdec_post_select(__a_unused struct sched *s, void *context)
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0) {
-               if (ret != -E_BTR_EOF) /* fatal error */
+               if (ret != -E_EOF) /* fatal error */
                        goto out;
                if (!ctx->have_more) /* EOF */
                        goto out;
                        goto out;
                if (!ctx->have_more) /* EOF */
                        goto out;
@@ -269,7 +269,7 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-static void opusdec_pre_select(struct sched *s, void *context)
+static void opusdec_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
 {
        struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
@@ -286,7 +286,7 @@ static void opusdec_pre_select(struct sched *s, void *context)
 const struct filter lsg_filter_cmd_com_opusdec_user_data = {
        .open = opusdec_open,
        .close = opusdec_close,
 const struct filter lsg_filter_cmd_com_opusdec_user_data = {
        .open = opusdec_open,
        .close = opusdec_close,
-       .pre_select = opusdec_pre_select,
-       .post_select = opusdec_post_select,
+       .pre_monitor = opusdec_pre_monitor,
+       .post_monitor = opusdec_post_monitor,
        .execute = opusdec_execute,
 };
        .execute = opusdec_execute,
 };
index f80301e9df282c13ef0e7d8ab2a025dd61624ba7..0814336fdc20fcebce15f344910c658726be831c 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -56,7 +56,7 @@ static int oss_mix_open(const char *dev, struct mixer_handle **handle)
                PARA_ERROR_LOG("could not open %s\n", dev);
                return ret;
        }
                PARA_ERROR_LOG("could not open %s\n", dev);
                return ret;
        }
-       h = para_malloc(sizeof(*h));
+       h = alloc(sizeof(*h));
        h->fd = ret;
        *handle = h;
        return 1;
        h->fd = ret;
        *handle = h;
        return 1;
index 0565167c256a43bfe86a6442f195a320e1458db8..4ea85afa73f45023c8f22ffe54e875343ea3d13e 100644 (file)
@@ -61,7 +61,7 @@ static int get_oss_format(enum sample_format sf)
        }
 }
 
        }
 }
 
-static void oss_pre_select(struct sched *s, void *context)
+static void oss_pre_monitor(struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
@@ -71,7 +71,7 @@ static void oss_pre_select(struct sched *s, void *context)
                return;
        if (ret < 0 || !powd)
                return sched_min_delay(s);
                return;
        if (ret < 0 || !powd)
                return sched_min_delay(s);
-       para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
+       sched_monitor_writefd(powd->fd, s);
 }
 
 static void oss_close(struct writer_node *wn)
 }
 
 static void oss_close(struct writer_node *wn)
@@ -101,7 +101,7 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
 {
        int ret, format;
        unsigned ch, rate;
 {
        int ret, format;
        unsigned ch, rate;
-       struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+       struct private_oss_write_data *powd = zalloc(sizeof(*powd));
        const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
 
        PARA_INFO_LOG("opening %s\n", dev);
        const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
 
        PARA_INFO_LOG("opening %s\n", dev);
@@ -178,7 +178,7 @@ err_free:
        return ret;
 }
 
        return ret;
 }
 
-static int oss_post_select(__a_unused struct sched *s, void *context)
+static int oss_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
 {
        struct writer_node *wn = context;
        struct private_oss_write_data *powd = wn->private_data;
@@ -218,11 +218,11 @@ static int oss_post_select(__a_unused struct sched *s, void *context)
        bytes = btr_next_buffer(btrn, &data);
        frames = bytes / powd->bytes_per_frame;
        if (!frames) { /* eof and less than a single frame available */
        bytes = btr_next_buffer(btrn, &data);
        frames = bytes / powd->bytes_per_frame;
        if (!frames) { /* eof and less than a single frame available */
-               ret = -E_WRITE_COMMON_EOF;
+               ret = -E_EOF;
                goto out;
        }
        ret = 0;
                goto out;
        }
        ret = 0;
-       if (!FD_ISSET(powd->fd, &s->wfds))
+       if (!sched_write_ok(powd->fd, s))
                goto out;
        /* get maximal number of bytes that can be written */
        ret = ioctl(powd->fd, SNDCTL_DSP_GETOSPACE, &abi);
                goto out;
        /* get maximal number of bytes that can be written */
        ret = ioctl(powd->fd, SNDCTL_DSP_GETOSPACE, &abi);
@@ -245,7 +245,7 @@ out:
 }
 
 const struct writer lsg_write_cmd_com_oss_user_data = {
 }
 
 const struct writer lsg_write_cmd_com_oss_user_data = {
-       .pre_select = oss_pre_select,
-       .post_select = oss_post_select,
+       .pre_monitor = oss_pre_monitor,
+       .post_monitor = oss_post_monitor,
        .close = oss_close,
 };
        .close = oss_close,
 };
diff --git a/para.h b/para.h
index b406818bd52a27bfc1794e0c95d8f9c0e9ef12a0..280c282323db369e7d0e84173324016aa425a92d 100644 (file)
--- a/para.h
+++ b/para.h
@@ -20,6 +20,8 @@
 #include <stdbool.h>
 #include <inttypes.h>
 #include <sys/uio.h>
 #include <stdbool.h>
 #include <inttypes.h>
 #include <sys/uio.h>
+#include <poll.h>
+
 #include "gcc-compat.h"
 
 /** used in various contexts */
 #include "gcc-compat.h"
 
 /** used in various contexts */
@@ -44,7 +46,6 @@
        typeof(x) _x = (x); \
        _x > 0? _x : -_x; })
 
        typeof(x) _x = (x); \
        _x > 0? _x : -_x; })
 
-
 extern __printf_2_3 void (*para_log)(int, const char*, ...);
 /**
  * Define a standard log function that always writes to stderr.
 extern __printf_2_3 void (*para_log)(int, const char*, ...);
 /**
  * Define a standard log function that always writes to stderr.
@@ -221,6 +222,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 
+/** \cond status_items */
 #define STATUS_ITEMS \
        STATUS_ITEM(basename) \
        STATUS_ITEM(status) \
 #define STATUS_ITEMS \
        STATUS_ITEM(basename) \
        STATUS_ITEM(status) \
@@ -268,6 +270,7 @@ enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
 enum status_items {STATUS_ITEMS NUM_STAT_ITEMS};
 #undef STATUS_ITEM
 #define STATUS_ITEM(_name) #_name,
 enum status_items {STATUS_ITEMS NUM_STAT_ITEMS};
 #undef STATUS_ITEM
 #define STATUS_ITEM(_name) #_name,
+/** \endcond status items */
 
 extern const char *status_item_list[];
 /** Loop over each status item. */
 
 extern const char *status_item_list[];
 /** Loop over each status item. */
diff --git a/play.c b/play.c
index 14fac42fd7b6e92566815d4c56b1f4b6b23c88e2..bd183b6b8dd48906e08cf142d7ff8c1e05aad48a 100644 (file)
--- a/play.c
+++ b/play.c
@@ -7,6 +7,7 @@
 #include <lopsub.h>
 
 #include "recv_cmd.lsg.h"
 #include <lopsub.h>
 
 #include "recv_cmd.lsg.h"
+#include "filter_cmd.lsg.h"
 #include "play_cmd.lsg.h"
 #include "write_cmd.lsg.h"
 #include "play.lsg.h"
 #include "play_cmd.lsg.h"
 #include "write_cmd.lsg.h"
 #include "play.lsg.h"
 #include "write.h"
 #include "fd.h"
 
 #include "write.h"
 #include "fd.h"
 
-/**
- * Besides playback tasks which correspond to the receiver/filter/writer nodes,
- * para_play creates two further tasks: The play task and the i9e task. It is
- * important whether a function can be called in the context of para_play or
- * i9e or both. As a rule, all command handlers are called only in i9e context via
- * the line handler (input mode) or the key handler (command mode) below.
- *
- * Playlist handling is done exclusively in play context.
- */
-
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
@@ -50,7 +41,7 @@ static struct lls_parse_result *play_lpr;
  * Describes a request to change the state of para_play.
  *
  * There is only one variable of this type: \a rq of the global play task
  * Describes a request to change the state of para_play.
  *
  * There is only one variable of this type: \a rq of the global play task
- * structure. Command handlers only set this variable and the post_select()
+ * structure. Command handlers only set this variable and the post_monitor()
  * function of the play task investigates its value during each iteration of
  * the scheduler run and performs the actual work.
  */
  * function of the play task investigates its value during each iteration of
  * the scheduler run and performs the actual work.
  */
@@ -117,7 +108,7 @@ INIT_STDERR_LOGGING(loglevel);
 
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
 
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
-static struct sched sched = {.max_fileno = 0};
+static struct sched sched;
 static struct play_task play_task, *pt = &play_task;
 
 #define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
 static struct play_task play_task, *pt = &play_task;
 
 #define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
@@ -192,6 +183,7 @@ static char get_playback_state(void)
        assert(false);
 };
 
        assert(false);
 };
 
+/* returns number of milliseconds */
 static long unsigned get_play_time(void)
 {
        char state = get_playback_state();
 static long unsigned get_play_time(void)
 {
        char state = get_playback_state();
@@ -201,16 +193,16 @@ static long unsigned get_play_time(void)
                return 0;
        if (pt->num_chunks == 0 || pt->seconds == 0)
                return 0;
                return 0;
        if (pt->num_chunks == 0 || pt->seconds == 0)
                return 0;
-       /* where the stream started (in seconds) */
-       result = pt->start_chunk * pt->seconds / pt->num_chunks;
+       /* where the stream started (in milliseconds) */
+       result = 1000ULL * pt->start_chunk * pt->seconds / pt->num_chunks;
        if (pt->wn.btrn) { /* Add the uptime of the writer node */
                struct timeval diff = {.tv_sec = 0}, wstime;
                btr_get_node_start(pt->wn.btrn, &wstime);
                if (wstime.tv_sec > 0)
                        tv_diff(now, &wstime, &diff);
        if (pt->wn.btrn) { /* Add the uptime of the writer node */
                struct timeval diff = {.tv_sec = 0}, wstime;
                btr_get_node_start(pt->wn.btrn, &wstime);
                if (wstime.tv_sec > 0)
                        tv_diff(now, &wstime, &diff);
-               result += diff.tv_sec;
+               result += tv2ms(&diff);
        }
        }
-       result = PARA_MIN(result, pt->seconds);
+       result = PARA_MIN(result, pt->seconds * 1000);
        result = PARA_MAX(result, 0UL);
        return result;
 }
        result = PARA_MAX(result, 0UL);
        return result;
 }
@@ -238,8 +230,7 @@ static int get_playback_error(void)
                return 0;
        if (task_status(pt->rn.task) >= 0)
                return 0;
                return 0;
        if (task_status(pt->rn.task) >= 0)
                return 0;
-       if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF
-                       || err == -E_WRITE_COMMON_EOF)
+       if (err == -E_EOF)
                return 1;
        return err;
 }
                return 1;
        return err;
 }
@@ -265,6 +256,7 @@ static int eof_cleanup(void)
        if (decoder->close)
                decoder->close(&pt->fn);
        btr_remove_node(&pt->fn.btrn);
        if (decoder->close)
                decoder->close(&pt->fn);
        btr_remove_node(&pt->fn.btrn);
+       lls_free_parse_result(pt->fn.lpr, FILTER_CMD(pt->fn.filter_num));
        free(pt->fn.conf);
        memset(&pt->fn, 0, sizeof(struct filter_node));
 
        free(pt->fn.conf);
        memset(&pt->fn, 0, sizeof(struct filter_node));
 
@@ -288,7 +280,7 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b)
 static void init_shuffle_map(void)
 {
        unsigned n, num_inputs = lls_num_inputs(play_lpr);
 static void init_shuffle_map(void)
 {
        unsigned n, num_inputs = lls_num_inputs(play_lpr);
-       shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+       shuffle_map = arr_alloc(num_inputs, sizeof(unsigned));
        for (n = 0; n < num_inputs; n++)
                shuffle_map[n] = n;
        if (!OPT_GIVEN(RANDOMIZE))
        for (n = 0; n < num_inputs; n++)
                shuffle_map[n] = n;
        if (!OPT_GIVEN(RANDOMIZE))
@@ -405,16 +397,16 @@ static int load_file(void)
        pt->rn.task = task_register(
                &(struct task_info) {
                        .name = lls_command_name(AFH_RECV_CMD),
        pt->rn.task = task_register(
                &(struct task_info) {
                        .name = lls_command_name(AFH_RECV_CMD),
-                       .pre_select = AFH_RECV->pre_select,
-                       .post_select = AFH_RECV->post_select,
+                       .pre_monitor = AFH_RECV->pre_monitor,
+                       .post_monitor = AFH_RECV->post_monitor,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
        pt->fn.task = task_register(
                &(struct task_info) {
                        .name = buf,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
        pt->fn.task = task_register(
                &(struct task_info) {
                        .name = buf,
-                       .pre_select = decoder->pre_select,
-                       .post_select = decoder->post_select,
+                       .pre_monitor = decoder->pre_monitor,
+                       .post_monitor = decoder->post_monitor,
                        .context = &pt->fn
                }, &sched);
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
                        .context = &pt->fn
                }, &sched);
        register_writer_node(&pt->wn, pt->fn.btrn, &sched);
@@ -591,7 +583,7 @@ static char *get_user_key_map_seq(int key)
        if (!p)
                return NULL;
        len = p - kma;
        if (!p)
                return NULL;
        len = p - kma;
-       result = para_malloc(len + 1);
+       result = alloc(len + 1);
        memcpy(result, kma, len);
        result[len] = '\0';
        return result;
        memcpy(result, kma, len);
        result[len] = '\0';
        return result;
@@ -611,7 +603,7 @@ static char *get_key_map_seq_safe(int key)
 
        if (len == 1 && isprint(*seq))
                return seq;
 
        if (len == 1 && isprint(*seq))
                return seq;
-       sseq = para_malloc(2 + 2 * len + 1);
+       sseq = alloc(2 + 2 * len + 1);
        sseq[0] = '0';
        sseq[1] = 'x';
        for (n = 0; n < len; n++) {
        sseq[0] = '0';
        sseq[1] = 'x';
        for (n = 0; n < len; n++) {
@@ -651,7 +643,7 @@ static char **get_mapped_keyseqs(void)
        char **result;
        int i;
 
        char **result;
        int i;
 
-       result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
+       result = arr_alloc(NUM_MAPPED_KEYS + 1, sizeof(char *));
        FOR_EACH_MAPPED_KEY(i) {
                char *seq = get_key_map_seq(i);
                result[i] = seq;
        FOR_EACH_MAPPED_KEY(i) {
                char *seq = get_key_map_seq(i);
                result[i] = seq;
@@ -840,20 +832,21 @@ EXPORT_PLAY_CMD_HANDLER(play);
 static int com_pause(__a_unused struct lls_parse_result *lpr)
 {
        char state;
 static int com_pause(__a_unused struct lls_parse_result *lpr)
 {
        char state;
-       long unsigned seconds, ss;
+       uint64_t ms;
+       unsigned long cn; /* chunk num */
 
        state = get_playback_state();
        pt->playing = false;
        if (state != 'P')
                return 0;
 
        state = get_playback_state();
        pt->playing = false;
        if (state != 'P')
                return 0;
-       seconds = get_play_time();
+       ms = get_play_time();
        pt->playing = false;
        pt->playing = false;
-       ss = 0;
+       cn = 0;
        if (pt->seconds > 0)
        if (pt->seconds > 0)
-               ss = seconds * pt->num_chunks / pt->seconds + 1;
-       ss = PARA_MAX(ss, 0UL);
-       ss = PARA_MIN(ss, pt->num_chunks);
-       pt->start_chunk = ss;
+               cn = ms * pt->num_chunks / pt->seconds / 1000 + 1;
+       cn = PARA_MIN(cn, pt->num_chunks);
+       pt->start_chunk = cn;
+       pt->rq = CRT_REPOS;
        kill_stream();
        return 0;
 }
        kill_stream();
        return 0;
 }
@@ -952,7 +945,7 @@ static int com_ff(struct lls_parse_result *lpr)
                return ret;
        if (pt->playing && !pt->fn.btrn)
                return 0;
                return ret;
        if (pt->playing && !pt->fn.btrn)
                return 0;
-       seconds += get_play_time();
+       seconds += (get_play_time() + 500) / 1000;
        seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
        seconds = PARA_MAX(seconds, 0);
        pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
        seconds = PARA_MIN(seconds, (typeof(seconds))pt->seconds - 4);
        seconds = PARA_MAX(seconds, 0);
        pt->start_chunk = pt->num_chunks * seconds / pt->seconds;
@@ -1055,9 +1048,9 @@ static void session_open(void)
                char *dot_para = make_message("%s/.paraslash", home);
 
                free(home);
                char *dot_para = make_message("%s/.paraslash", home);
 
                free(home);
-               ret = para_mkdir(dot_para, 0777);
+               ret = para_mkdir(dot_para);
                /* warn, but otherwise ignore mkdir error */
                /* warn, but otherwise ignore mkdir error */
-               if (ret < 0 && ret != -ERRNO_TO_PARA_ERROR(EEXIST))
+               if (ret < 0)
                        PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
                                para_strerror(-ret));
                history_file = make_message("%s/play.history", dot_para);
                        PARA_WARNING_LOG("Can not create %s: %s\n", dot_para,
                                para_strerror(-ret));
                history_file = make_message("%s/play.history", dot_para);
@@ -1074,7 +1067,7 @@ static void session_open(void)
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGWINCH, &act, NULL);
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGWINCH, &act, NULL);
-       sched.select_function = i9e_select;
+       sched.poll_function = i9e_poll;
 
        ici.bound_keyseqs = get_mapped_keyseqs();
        pt->btrn = ici.producer = btr_new_node(&(struct btr_node_description)
 
        ici.bound_keyseqs = get_mapped_keyseqs();
        pt->btrn = ici.producer = btr_new_node(&(struct btr_node_description)
@@ -1108,16 +1101,16 @@ static void session_update_time_string(char *str, unsigned len)
 /*
  * If we are about to die we must call i9e_close() to reset the terminal.
  * However, i9e_close() must be called in *this* context, i.e. from
 /*
  * If we are about to die we must call i9e_close() to reset the terminal.
  * However, i9e_close() must be called in *this* context, i.e. from
- * play_task.post_select() rather than from i9e_post_select(), because
+ * play_task.post_monitor() rather than from i9e_post_monitor(), because
  * otherwise i9e would access freed memory upon return. So the play task must
  * stay alive until the i9e task terminates.
  *
  * We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch()
  * otherwise i9e would access freed memory upon return. So the play task must
  * stay alive until the i9e task terminates.
  *
  * We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch()
- * and reschedule. In the next iteration, i9e->post_select returns an error and
+ * and reschedule. In the next iteration, i9e->post_monitor returns an error and
  * terminates. Subsequent calls to i9e_get_error() then return negative and we
  * are allowed to call i9e_close() and terminate as well.
  */
  * terminates. Subsequent calls to i9e_get_error() then return negative and we
  * are allowed to call i9e_close() and terminate as well.
  */
-static int session_post_select(__a_unused struct sched *s)
+static int session_post_monitor(__a_unused struct sched *s)
 {
        int ret;
 
 {
        int ret;
 
@@ -1140,11 +1133,11 @@ static int session_post_select(__a_unused struct sched *s)
 
 #else /* HAVE_READLINE */
 
 
 #else /* HAVE_READLINE */
 
-static int session_post_select(struct sched *s)
+static int session_post_monitor(struct sched *s)
 {
        char c;
 
 {
        char c;
 
-       if (!FD_ISSET(STDIN_FILENO, &s->rfds))
+       if (!sched_read_ok(STDIN_FILENO, s))
                return 0;
        if (read(STDIN_FILENO, &c, 1))
                do_nothing;
                return 0;
        if (read(STDIN_FILENO, &c, 1))
                do_nothing;
@@ -1163,11 +1156,11 @@ static void session_update_time_string(char *str, __a_unused unsigned len)
 }
 #endif /* HAVE_READLINE */
 
 }
 #endif /* HAVE_READLINE */
 
-static void play_pre_select(struct sched *s, __a_unused void *context)
+static void play_pre_monitor(struct sched *s, __a_unused void *context)
 {
        char state;
 
 {
        char state;
 
-       para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(STDIN_FILENO, s);
        state = get_playback_state();
        if (state == 'R' || state == 'F' || state == 'X')
                return sched_min_delay(s);
        state = get_playback_state();
        if (state == 'R' || state == 'F' || state == 'X')
                return sched_min_delay(s);
@@ -1187,7 +1180,7 @@ static unsigned get_time_string(char **result)
        length = pt->seconds;
        if (length == 0)
                return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
        length = pt->seconds;
        if (length == 0)
                return xasprintf(result, "0:00 [0:00] (0%%/0:00)");
-       seconds = get_play_time();
+       seconds = (get_play_time() + 500) / 1000;
        return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
                pt->current_file,
                seconds / 60,
        return xasprintf(result, "#%u: %d:%02d [%d:%02d] (%d%%/%d:%02d) %s",
                pt->current_file,
                seconds / 60,
@@ -1201,7 +1194,7 @@ static unsigned get_time_string(char **result)
        );
 }
 
        );
 }
 
-static int play_post_select(struct sched *s, __a_unused void *context)
+static int play_post_monitor(struct sched *s, __a_unused void *context)
 {
        int ret;
 
 {
        int ret;
 
@@ -1210,7 +1203,7 @@ static int play_post_select(struct sched *s, __a_unused void *context)
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
                pt->rq = CRT_TERM_RQ;
                return 0;
        }
-       ret = session_post_select(s);
+       ret = session_post_monitor(s);
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
        if (ret < 0)
                goto out;
        if (!pt->wn.btrn && !pt->fn.btrn) {
@@ -1244,28 +1237,43 @@ out:
 /**
  * The main function of para_play.
  *
 /**
  * The main function of para_play.
  *
- * \param argc Standard.
- * \param argv Standard.
+ * \param argc See man page.
+ * \param argv See man page.
+ *
+ * para_play distributes its work by submitting various tasks to the paraslash
+ * scheduler. The receiver, filter and writer tasks, which are used to play an
+ * audio file, require one task each to maintain their underlying buffer tree
+ * node. These tasks only exist when an audio file is playing.
+ *
+ * The i9 task, which is submitted and maintained by the i9e subsystem, reads
+ * an input line and calls the corresponding command handler such as com_stop()
+ * which is implemented in this file. The command handlers typically write a
+ * request to the global play_task structure, whose contents are read and acted
+ * upon by another task, the play task.
+ *
+ * As a rule, playlist handling is performed exclusively in play context, i.e.
+ * in the post-monitor step of the play task, while command handlers are only
+ * called in i9e context.
  *
  *
- * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
+ * \return EXIT_FAILURE or EXIT_SUCCESS.
  */
 int main(int argc, char *argv[])
 {
        int ret;
        unsigned num_inputs;
 
  */
 int main(int argc, char *argv[])
 {
        int ret;
        unsigned num_inputs;
 
-       sched.default_timeout.tv_sec = 5;
+       sched.default_timeout = 5000;
        parse_config_or_die(argc, argv);
        session_open();
        num_inputs = lls_num_inputs(play_lpr);
        init_shuffle_map();
        parse_config_or_die(argc, argv);
        session_open();
        num_inputs = lls_num_inputs(play_lpr);
        init_shuffle_map();
-       pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
+       pt->invalid = arr_zalloc(num_inputs, sizeof(*pt->invalid));
        pt->rq = CRT_FILE_CHANGE;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
        pt->rq = CRT_FILE_CHANGE;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
-               .pre_select = play_pre_select,
-               .post_select = play_post_select,
+               .pre_monitor = play_pre_monitor,
+               .post_monitor = play_post_monitor,
                .context = pt,
        }, &sched);
        ret = schedule(&sched);
                .context = pt,
        }, &sched);
        ret = schedule(&sched);
index 5f83b0fe0a35c9e5c31c97828bfa7af2bebc6208..c145b0fd80ce0520bda58f0949c46211eeee5fdd 100644 (file)
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
 
 /** \file playlist.c Functions for loading and saving playlists. */
 
-/** Structure used for adding entries to a playlist. */
-struct playlist_info {
+/**
+ * The state of a playlist instance.
+ *
+ * A structure of this type is allocated and initialized at playlist load time.
+ */
+struct playlist_instance {
        /** The name of the playlist. */
        char *name;
        /** The number of entries currently in the playlist. */
        unsigned length;
        /** The name of the playlist. */
        char *name;
        /** The number of entries currently in the playlist. */
        unsigned length;
+       /** Contains all valid paths of the playlist. */
+       struct osl_table *score_table;
 };
 };
-static struct playlist_info current_playlist;
+static struct playlist_instance current_playlist;
 
 /**
  * Re-insert an audio file into the tree of admissible files.
 
 /**
  * Re-insert an audio file into the tree of admissible files.
@@ -38,7 +44,7 @@ static int playlist_update_audio_file(const struct osl_row *aft_row)
 
 static int add_playlist_entry(char *path, void *data)
 {
 
 static int add_playlist_entry(char *path, void *data)
 {
-       struct playlist_info *playlist = data;
+       struct playlist_instance *pi = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
@@ -46,73 +52,43 @@ static int add_playlist_entry(char *path, void *data)
                PARA_NOTICE_LOG("%s: %s\n", path, para_strerror(-ret));
                return 1;
        }
                PARA_NOTICE_LOG("%s: %s\n", path, para_strerror(-ret));
                return 1;
        }
-       ret = score_add(aft_row, -playlist->length);
+       ret = score_add(aft_row, -pi->length, pi->score_table);
        if (ret < 0) {
                PARA_ERROR_LOG("failed to add %s: %s\n", path, para_strerror(-ret));
                return ret;
        }
        if (ret < 0) {
                PARA_ERROR_LOG("failed to add %s: %s\n", path, para_strerror(-ret));
                return ret;
        }
-       playlist->length++;
-       return 1;
-}
-
-/* returns -E_PLAYLIST_LOADED on _success_ to terminate the loop */
-static int load_playlist(struct osl_row *row, void *data)
-{
-       struct playlist_info *playlist = data;
-       struct osl_object playlist_def;
-       char *playlist_name;
-       int ret;
-
-       ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
-       if (ret < 0)
-               goto err;
-       playlist->length = 0;
-       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
-               playlist_def.size, add_playlist_entry, playlist);
-       osl_close_disk_object(&playlist_def);
-       if (ret < 0)
-               goto err;
-       ret = -E_PLAYLIST_EMPTY;
-       if (!playlist->length)
-               goto err;
-       playlist->name = para_strdup(playlist_name);
-       PARA_NOTICE_LOG("loaded playlist %s (%u files)\n", playlist->name,
-               playlist->length);
-       return -E_PLAYLIST_LOADED;
-err:
-       if (ret != -E_DUMMY_ROW)
-               PARA_NOTICE_LOG("unable to load playlist (%s)\n",
-                       para_strerror(-ret));
+       pi->length++;
        return 1;
 }
 
 static int check_playlist_path(char *path, void *data)
 {
        return 1;
 }
 
 static int check_playlist_path(char *path, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
        if (ret < 0)
        struct osl_row *aft_row;
        int ret = aft_get_row_of_path(path, &aft_row);
 
        if (ret < 0)
-               para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+               afs_error(aca, "%s: %s\n", path, para_strerror(-ret));
        return 1; /* do not fail the loop on bad paths */
 }
 
 static int check_playlist(struct osl_row *row, void *data)
 {
        return 1; /* do not fail the loop on bad paths */
 }
 
 static int check_playlist(struct osl_row *row, void *data)
 {
-       struct para_buffer *pb = data;
+       struct afs_callback_arg *aca = data;
+       struct para_buffer *pb = &aca->pbout;
        struct osl_object playlist_def;
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
        if (ret < 0) { /* log error, but continue */
        struct osl_object playlist_def;
        char *playlist_name;
        int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
 
        if (ret < 0) { /* log error, but continue */
-               para_printf(pb, "failed to get playlist data: %s\n",
+               afs_error(aca, "failed to get playlist data: %s\n",
                        para_strerror(-ret));
                return 1;
        }
        if (*playlist_name) { /* skip dummy row */
                para_printf(pb, "checking playlist %s...\n", playlist_name);
                for_each_line(FELF_READ_ONLY, playlist_def.data,
                        para_strerror(-ret));
                return 1;
        }
        if (*playlist_name) { /* skip dummy row */
                para_printf(pb, "checking playlist %s...\n", playlist_name);
                for_each_line(FELF_READ_ONLY, playlist_def.data,
-                       playlist_def.size, check_playlist_path, pb);
+                       playlist_def.size, check_playlist_path, aca);
        }
        osl_close_disk_object(&playlist_def);
        return 1;
        }
        osl_close_disk_object(&playlist_def);
        return 1;
@@ -129,49 +105,110 @@ static int check_playlist(struct osl_row *row, void *data)
 int playlist_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking playlists...\n");
 int playlist_check_callback(struct afs_callback_arg *aca)
 {
        para_printf(&aca->pbout, "checking playlists...\n");
-       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+       return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, aca,
                check_playlist));
 }
 
 /**
                check_playlist));
 }
 
 /**
- * Close the current playlist.
+ * Free all resources of the given/current playlist.
  *
  *
- * \sa \ref playlist_open().
+ * \param pi NULL means to unload the current playlist.
  */
  */
-void playlist_close(void)
+void playlist_unload(struct playlist_instance *pi)
 {
 {
+       if (pi) {
+               score_close(pi->score_table);
+               free(pi->name);
+               free(pi);
+               return;
+       }
        if (!current_playlist.name)
                return;
        if (!current_playlist.name)
                return;
+       score_clear();
        free(current_playlist.name);
        current_playlist.name = NULL;
        free(current_playlist.name);
        current_playlist.name = NULL;
+       current_playlist.length = 0;
 }
 
 /**
 }
 
 /**
- * Open the given playlist.
+ * Populate the score table from the paths of a playlist database object.
  *
  *
- * \param name The name of the playlist to open.
+ * This loads the blob object which corresponds to the given name from the
+ * playlist table. Each line of the blob is regarded as a path which is looked
+ * up in the audio file table. If the path lookup succeeds, a reference to the
+ * corresponding row of the audio file table is added to the score table.
  *
  *
- * Files which are listed in the playlist, but not contained in the database
- * are ignored.  This is not considered an error.
+ * \param name The name of the playlist to load.
+ * \param result Opaque, refers to the underlying score table.
+ * \param msg Error message or playlist info is returned here.
  *
  *
- * \return Standard.
+ * \return The length of the loaded playlist on success, negative error code
+ * else. Files which are listed in the playlist, but are not contained in the
+ * database are ignored. This is not considered an error.
  */
  */
-int playlist_open(const char *name)
+int playlist_load(const char *name, struct playlist_instance **result, char **msg)
 {
 {
-       struct osl_object obj;
        int ret;
        int ret;
-       struct osl_row *row;
+       struct playlist_instance *pi;
+       struct osl_object playlist_def;
 
 
-       obj.data = (char *)name;
-       obj.size = strlen(obj.data);
-       ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
-       if (ret < 0) {
-               PARA_NOTICE_LOG("failed to load playlist %s\n", name);
-               return ret;
+       if (!name || !*name) {
+               if (msg)
+                       *msg = make_message("empty playlist name\n");
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
+       }
+       ret = pl_get_def_by_name(name, &playlist_def);
+       if (ret < 0)
+               goto err;
+       pi = zalloc(sizeof(*pi));
+       if (result)
+               score_open(&pi->score_table);
+       ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+               playlist_def.size, add_playlist_entry, pi);
+       osl_close_disk_object(&playlist_def);
+       if (ret < 0)
+               goto close_score_table;
+       ret = -E_PLAYLIST_EMPTY;
+       if (pi->length == 0)
+               goto close_score_table;
+       /* success */
+       if (msg)
+               *msg = make_message("loaded playlist %s (%u files)\n", name,
+                       pi->length);
+       pi->name = para_strdup(name);
+       if (result)
+               *result = pi;
+       else {
+               playlist_unload(NULL);
+               current_playlist = *pi;
        }
        }
-       playlist_close();
-       ret = load_playlist(row, &current_playlist);
-       return (ret == -E_PLAYLIST_LOADED)? current_playlist.length : ret;
+       return pi->length;
+close_score_table:
+       if (result)
+               score_close(pi->score_table);
+       free(pi);
+err:
+       PARA_NOTICE_LOG("unable to load playlist %s\n", name);
+       if (msg)
+               *msg = make_message("unable to load playlist %s\n", name);
+       return ret;
+}
+
+/**
+ * Iterate over all admissible audio files of a playlist instance.
+ *
+ * This wrapper around \ref score_loop() is the playlist counterpart of \ref
+ * mood_loop().
+ *
+ * \param pi Determines the score table to iterate. Must not be NULL.
+ * \param func See \ref score_loop().
+ * \param data See \ref score_loop().
+ *
+ * \return See \ref score_loop(), \ref mood_loop().
+ */
+int playlist_loop(struct playlist_instance *pi, osl_rbtree_loop_func *func, void *data)
+{
+       return score_loop(func, pi->score_table, data);
 }
 
 static int search_path(char *path, void *data)
 }
 
 static int search_path(char *path, void *data)
@@ -183,17 +220,14 @@ static int search_path(char *path, void *data)
 
 static int handle_audio_file_event(enum afs_events event, void *data)
 {
 
 static int handle_audio_file_event(enum afs_events event, void *data)
 {
-       int ret, was_admissible = 0, is_admissible;
+       int ret;
+       bool was_admissible = false, is_admissible;
        struct osl_object playlist_def;
        char *new_path;
        const struct osl_row *row = data;
 
        struct osl_object playlist_def;
        char *new_path;
        const struct osl_row *row = data;
 
-       if (event == AUDIO_FILE_RENAME) {
-               ret = row_belongs_to_score_table(row, NULL);
-               if (ret < 0)
-                       return ret;
-               was_admissible = ret;
-       }
+       if (event == AUDIO_FILE_RENAME)
+               was_admissible = row_belongs_to_score_table(row);
        ret = get_audio_file_path_of_row(row, &new_path);
        if (ret < 0)
                return ret;
        ret = get_audio_file_path_of_row(row, &new_path);
        if (ret < 0)
                return ret;
@@ -214,7 +248,7 @@ static int handle_audio_file_event(enum afs_events event, void *data)
        }
        /* !was_admissible && is_admissible */
        current_playlist.length++;
        }
        /* !was_admissible && is_admissible */
        current_playlist.length++;
-       return score_add(row, 0); /* play it immediately */
+       return score_add(row, 0, NULL); /* play it immediately */
 }
 
 /**
 }
 
 /**
@@ -229,7 +263,6 @@ static int handle_audio_file_event(enum afs_events event, void *data)
 int playlists_event_handler(enum afs_events event,
        __a_unused struct para_buffer *pb, void *data)
 {
 int playlists_event_handler(enum afs_events event,
        __a_unused struct para_buffer *pb, void *data)
 {
-       int ret;
        struct afsi_change_event_data *aced = data;
 
        if (!current_playlist.name)
        struct afsi_change_event_data *aced = data;
 
        if (!current_playlist.name)
@@ -241,10 +274,7 @@ int playlists_event_handler(enum afs_events event,
        case AUDIO_FILE_ADD:
                return handle_audio_file_event(event, data);
        case AUDIO_FILE_REMOVE:
        case AUDIO_FILE_ADD:
                return handle_audio_file_event(event, data);
        case AUDIO_FILE_REMOVE:
-               ret = row_belongs_to_score_table(data, NULL);
-               if (ret < 0)
-                       return ret;
-               if (!ret)
+               if (!row_belongs_to_score_table(data))
                        return 1;
                current_playlist.length--;
                return score_delete(data);
                        return 1;
                current_playlist.length--;
                return score_delete(data);
index 9a801900c157e1da2da979c92d7bd60d03e21b9c..28b710108d435bd40d5970ca117b103d6fb0b1a3 100644 (file)
@@ -22,7 +22,7 @@ struct private_prebuffer_data {
        struct timeval barrier;
 };
 
        struct timeval barrier;
 };
 
-static void prebuffer_pre_select(struct sched *s, void *context)
+static void prebuffer_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -50,7 +50,7 @@ static void prebuffer_close(struct filter_node *fn)
        free(fn->private_data);
 }
 
        free(fn->private_data);
 }
 
-static int prebuffer_post_select(__a_unused struct sched *s, void *context)
+static int prebuffer_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -80,13 +80,13 @@ fail:
 
 static void prebuffer_open(struct filter_node *fn)
 {
 
 static void prebuffer_open(struct filter_node *fn)
 {
-       struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
+       struct private_prebuffer_data *ppd = zalloc(sizeof(*ppd));
        fn->private_data = ppd;
 }
 
 const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
        .open = prebuffer_open,
        .close = prebuffer_close,
        fn->private_data = ppd;
 }
 
 const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
        .open = prebuffer_open,
        .close = prebuffer_close,
-       .pre_select = prebuffer_pre_select,
-       .post_select = prebuffer_post_select,
+       .pre_monitor = prebuffer_pre_monitor,
+       .post_monitor = prebuffer_post_monitor,
 };
 };
diff --git a/recv.c b/recv.c
index 10d55d218071ccf6a314fc87d4e5f78001012db4..68417187348b058b375993b9a74eb6c2ca7d826b 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -97,13 +97,12 @@ int main(int argc, char *argv[])
        stdout_task_register(&sot, &s);
 
        ti.name = lls_command_name(cmd);
        stdout_task_register(&sot, &s);
 
        ti.name = lls_command_name(cmd);
-       ti.pre_select = r->pre_select;
-       ti.post_select = r->post_select;
+       ti.pre_monitor = r->pre_monitor;
+       ti.post_monitor = r->post_monitor;
        ti.context = &rn;
        rn.task = task_register(&ti, &s);
 
        ti.context = &rn;
        rn.task = task_register(&ti, &s);
 
-       s.default_timeout.tv_sec = 1;
-       s.default_timeout.tv_usec = 0;
+       s.default_timeout = 1000;
        ret = schedule(&s);
        sched_shutdown(&s);
        r->close(&rn);
        ret = schedule(&s);
        sched_shutdown(&s);
        r->close(&rn);
diff --git a/recv.h b/recv.h
index 36b0f1db62e348c9ab7ccdfc3b32ba656d698c5f..391395b241d67abf12eb70996a10ee7950519087 100644 (file)
--- a/recv.h
+++ b/recv.h
@@ -21,11 +21,11 @@ struct receiver_node {
        /**
         * The file descriptor to receive the stream.
         *
        /**
         * The file descriptor to receive the stream.
         *
-        * The pre_select function of the receiver adds this file descriptor to
+        * The pre_monitor function of the receiver adds this file descriptor to
         * the set of file descriptors which are watched for readability or
         * writability, depending on the state of the connection (if any).
         *
         * the set of file descriptors which are watched for readability or
         * writability, depending on the state of the connection (if any).
         *
-        * If \a fd is readable, the post_select function of the receiver reads
+        * If \a fd is readable, the post_monitor function of the receiver reads
         * data from this fd into the buffer pool area of \a btrp.
         *
         * \sa \ref receiver.
         * data from this fd into the buffer pool area of \a btrp.
         *
         * \sa \ref receiver.
@@ -34,9 +34,18 @@ struct receiver_node {
 };
 
 /**
 };
 
 /**
- * Describes one supported paraslash receiver.
+ * Describes a possible data source for audio streams.
  *
  *
- * \sa \ref http_recv.c, \ref udp_recv.c.
+ * A paraslash receiver is a modular piece of software which is capable of
+ * receiving an audio data stream from a data source. Received audio data is
+ * fed to consumers through the buffer tree mechanism.
+ *
+ * This structure contains the methods which have to be implemented by each
+ * receiver.
+ *
+ * \sa \ref http_recv.c, \ref udp_recv.c, \ref dccp_recv.c, \ref afh_recv.c,
+ * struct \ref receiver_node, struct \ref filter, struct \ref writer, struct
+ * \ref sched.
  */
 struct receiver {
        /**
  */
 struct receiver {
        /**
@@ -45,8 +54,6 @@ struct receiver {
         * This should allocate the output buffer of the given receiver node
         * and prepare it for retrieving the audio stream according to the
         * configuration stored in rn->lpr.
         * This should allocate the output buffer of the given receiver node
         * and prepare it for retrieving the audio stream according to the
         * configuration stored in rn->lpr.
-        *
-        * \sa struct \ref receiver_node.
         */
        int (*open)(struct receiver_node *rn);
        /**
         */
        int (*open)(struct receiver_node *rn);
        /**
@@ -58,31 +65,10 @@ struct receiver {
         * \sa \ref receiver_node.
         */
        void (*close)(struct receiver_node *rn);
         * \sa \ref receiver_node.
         */
        void (*close)(struct receiver_node *rn);
-       /**
-        * Add file descriptors to fd_sets and compute timeout for select(2).
-        *
-        * If this is not NULL, the function is called in each iteration of the
-        * scheduler's select loop. The receiver may define it to add file
-        * descriptors to the file descriptor sets given by s. Those will be
-        * monitored in the subsequent call to select(2). The function may also
-        * lower the timeout value of s to make select(2) return earlier if no
-        * file descriptors are ready for I/O.
-        *
-        * \sa select(2), \ref time.c, struct \ref sched.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Evaluate the result from select(2).
-        *
-        * This is called after the call to select(2). It should check all file
-        * descriptors which were added to any of the fd sets in the previous
-        * call to ->pre_select() and perform (non-blocking) I/O operations on
-        * those fds which are ready for I/O, for example in order to establish
-        * a connection or to receive a part of the audio stream.
-        *
-        * \sa select(2), struct \ref receiver.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Ask the scheduler to monitor receive fds. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Receive data and make it available to consumers. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Answer a buffer tree query.
         *
        /**
         * Answer a buffer tree query.
         *
@@ -110,4 +96,4 @@ struct receiver {
 
 int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
 void print_receiver_helps(bool detailed);
 
 int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
 void print_receiver_helps(bool detailed);
-int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
+int generic_recv_pre_monitor(struct sched *s, struct receiver_node *rn);
index 31fd81f1ec52c3d0b6204e8b3663a7d36e14fbb3..1939300a7c946d68d1b324ee6beb8c0bf146893b 100644 (file)
@@ -41,7 +41,7 @@ int check_receiver_arg(const char *ra, struct lls_parse_result **lprp)
        *lprp = NULL;
        if (!ra || !*ra) {
                argc = 1;
        *lprp = NULL;
        if (!ra || !*ra) {
                argc = 1;
-               argv = para_malloc(2 * sizeof(char*));
+               argv = alloc(2 * sizeof(char*));
                argv[0] = para_strdup("http");
                argv[1] = NULL;
        } else {
                argv[0] = para_strdup("http");
                argv[1] = NULL;
        } else {
@@ -98,19 +98,19 @@ void print_receiver_helps(bool detailed)
 }
 
 /**
 }
 
 /**
- * Simple pre-select hook, used by all receivers.
+ * Request a minimal timeout in case of buffer tree errors.
  *
  *
- * \param s Scheduler info.
- * \param rn The receiver node.
+ * \param s The scheduler instance.
+ * \param rn The buffer tree node is derived from this.
  *
  *
- * This requests a minimal delay from the scheduler if the status of the buffer
- * tree node indicates an error/eof condition. No file descriptors are added to
- * the fd sets of \a s.
+ * If the buffer tree node of the given receiver node is in error or EOF state,
+ * a minimal I/O timeout is requested from the scheduler. Otherwise, the
+ * function does nothing. No file descriptors are asked to be monitored.
  *
  *
- * \return The status of the btr node of the receiver node, i.e. the return
- * value of the underlying call to \ref btr_node_status().
+ * \return The status of of the receiver node's buffer tree node. That is, the
+ * return value of the underlying call to \ref btr_node_status().
  */
  */
-int generic_recv_pre_select(struct sched *s, struct receiver_node *rn)
+int generic_recv_pre_monitor(struct sched *s, struct receiver_node *rn)
 {
        int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT);
 
 {
        int ret = btr_node_status(rn->btrn, 0, BTR_NT_ROOT);
 
index bbdda51c525630c6d1411dfa547193f8ccdae9ce..72cb3f62ea766ee7ed94e3a68ed5a17591f2f133 100644 (file)
@@ -51,7 +51,7 @@ static void resample_close(struct filter_node *fn)
 
 static void resample_open(struct filter_node *fn)
 {
 
 static void resample_open(struct filter_node *fn)
 {
-       struct resample_context *ctx = para_calloc(sizeof(*ctx));
+       struct resample_context *ctx = zalloc(sizeof(*ctx));
        struct btr_node *btrn = fn->btrn;
        struct wav_params wp;
 
        struct btr_node *btrn = fn->btrn;
        struct wav_params wp;
 
@@ -62,7 +62,7 @@ static void resample_open(struct filter_node *fn)
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
 
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
 
-static void resample_pre_select(struct sched *s, void *context)
+static void resample_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
 {
        struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
@@ -70,7 +70,7 @@ static void resample_pre_select(struct sched *s, void *context)
 
        if (ret != 0)
                return sched_min_delay(s);
 
        if (ret != 0)
                return sched_min_delay(s);
-       check_wav_pre_select(s, ctx->cwc);
+       check_wav_pre_monitor(s, ctx->cwc);
 }
 
 static int get_btr_val(const char *what, struct btr_node *btrn)
 }
 
 static int get_btr_val(const char *what, struct btr_node *btrn)
@@ -167,10 +167,10 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
        data.output_frames = num_frames * ctx->ratio + 1;
        out_samples = data.output_frames * ctx->channels;
 
        data.output_frames = num_frames * ctx->ratio + 1;
        out_samples = data.output_frames * ctx->channels;
 
-       in_float = para_malloc(num_samples * sizeof(float));
+       in_float = arr_alloc(num_samples, sizeof(float));
        src_short_to_float_array(in, in_float, num_samples);
        data.data_in = in_float;
        src_short_to_float_array(in, in_float, num_samples);
        data.data_in = in_float;
-       data.data_out = para_malloc(out_samples * sizeof(float));
+       data.data_out = arr_alloc(out_samples, sizeof(float));
        ret = src_process(ctx->src_state, &data);
        free(in_float);
        if (ret != 0) {
        ret = src_process(ctx->src_state, &data);
        free(in_float);
        if (ret != 0) {
@@ -179,7 +179,7 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
                return -E_LIBSAMPLERATE;
        }
        out_samples = data.output_frames_gen * ctx->channels;
                return -E_LIBSAMPLERATE;
        }
        out_samples = data.output_frames_gen * ctx->channels;
-       out = para_malloc(out_samples * sizeof(short));
+       out = arr_alloc(out_samples, sizeof(short));
        src_float_to_short_array(data.data_out, out, out_samples);
        free(data.data_out);
        *result = out;
        src_float_to_short_array(data.data_out, out, out_samples);
        free(data.data_out);
        *result = out;
@@ -187,7 +187,7 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
        return data.input_frames_used;
 }
 
        return data.input_frames_used;
 }
 
-static int resample_post_select(__a_unused struct sched *s, void *context)
+static int resample_post_monitor(__a_unused struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
 {
        int ret;
        struct filter_node *fn = context;
@@ -197,7 +197,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
        size_t in_bytes, num_frames;
        bool have_more;
 
        size_t in_bytes, num_frames;
        bool have_more;
 
-       ret = check_wav_post_select(ctx->cwc);
+       ret = check_wav_post_monitor(ctx->cwc);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0)
                goto out;
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -220,7 +220,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
        }
        btr_merge(btrn, fn->min_iqs);
        in_bytes = btr_next_buffer(btrn, (char **)&in);
        }
        btr_merge(btrn, fn->min_iqs);
        in_bytes = btr_next_buffer(btrn, (char **)&in);
-       ret = -E_RESAMPLE_EOF;
+       ret = -E_EOF;
        num_frames = in_bytes / 2 / ctx->channels;
        if (num_frames == 0)
                goto out;
        num_frames = in_bytes / 2 / ctx->channels;
        if (num_frames == 0)
                goto out;
@@ -236,7 +236,7 @@ out:
        if (ret < 0) {
                btr_remove_node(&fn->btrn);
                /* This releases the check_wav btr node */
        if (ret < 0) {
                btr_remove_node(&fn->btrn);
                /* This releases the check_wav btr node */
-               check_wav_post_select(ctx->cwc);
+               check_wav_post_monitor(ctx->cwc);
        }
        return ret;
 }
        }
        return ret;
 }
@@ -277,8 +277,8 @@ static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
 const struct filter lsg_filter_cmd_com_resample_user_data = {
        .setup = resample_setup,
        .open = resample_open,
 const struct filter lsg_filter_cmd_com_resample_user_data = {
        .setup = resample_setup,
        .open = resample_open,
-       .pre_select = resample_pre_select,
-       .post_select = resample_post_select,
+       .pre_monitor = resample_pre_monitor,
+       .post_monitor = resample_post_monitor,
        .close = resample_close,
        .teardown = resample_teardown,
        .execute = resample_execute
        .close = resample_close,
        .teardown = resample_teardown,
        .execute = resample_execute
index 76e2d7afc8ab7caea1207a36c37c8b6452af2816..0e706f0f1ad6e906732f3bc95e8b653da53f9a6d 100644 (file)
@@ -41,8 +41,8 @@ struct ringbuffer
  */
 struct ringbuffer *ringbuffer_new(unsigned size)
 {
  */
 struct ringbuffer *ringbuffer_new(unsigned size)
 {
-       struct ringbuffer *rb = para_calloc(sizeof(struct ringbuffer));
-       rb->entries = para_calloc(size * sizeof(void *));
+       struct ringbuffer *rb = zalloc(sizeof(struct ringbuffer));
+       rb->entries = zalloc(size * sizeof(void *));
        rb->size = size;
        return rb;
 }
        rb->size = size;
        return rb;
 }
diff --git a/sched.c b/sched.c
index aac8efed1bcf8d897e6aef4d07973f2babc8d4f4..20822038b81aff04d8ee8c21f7f8d7773831ca37 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -17,9 +17,9 @@
  * The possible states of a task.
  *
  * In addition to the states listed here, a task may also enter zombie state.
  * The possible states of a task.
  *
  * In addition to the states listed here, a task may also enter zombie state.
- * This happens when its ->post_select function returns negative, the ->status
+ * This happens when its ->post_monitor function returns negative, the ->status
  * field is then set to this return value. Such tasks are not scheduled any
  * field is then set to this return value. Such tasks are not scheduled any
- * more (i.e. ->pre_select() and ->post_select() are no longer called), but
+ * more (i.e. ->pre_monitor() and ->post_monitor() are no longer called), but
  * they stay on the scheduler task list until \ref task_reap() or
  * \ref sched_shutdown() is called.
  */
  * they stay on the scheduler task list until \ref task_reap() or
  * \ref sched_shutdown() is called.
  */
@@ -46,7 +46,7 @@ struct task {
 static struct timeval now_struct;
 const struct timeval *now = &now_struct;
 
 static struct timeval now_struct;
 const struct timeval *now = &now_struct;
 
-static void sched_preselect(struct sched *s)
+static void sched_pre_monitor(struct sched *s)
 {
        struct task *t, *tmp;
 
 {
        struct task *t, *tmp;
 
@@ -55,46 +55,42 @@ static void sched_preselect(struct sched *s)
                        continue;
                if (t->notification != 0)
                        sched_min_delay(s);
                        continue;
                if (t->notification != 0)
                        sched_min_delay(s);
-               if (t->info.pre_select)
-                       t->info.pre_select(s, t->info.context);
+               if (t->info.pre_monitor)
+                       t->info.pre_monitor(s, t->info.context);
        }
 }
 
 static void unlink_and_free_task(struct task *t)
 {
        }
 }
 
 static void unlink_and_free_task(struct task *t)
 {
-       PARA_INFO_LOG("freeing task %s (%s)\n", t->name, t->status < 0?
-               para_strerror(-t->status) :
-               (t->status == TS_DEAD? "[dead]" : "[running]"));
-
        list_del(&t->node);
        free(t->name);
        free(t);
 }
 
 //#define SCHED_DEBUG 1
        list_del(&t->node);
        free(t->name);
        free(t);
 }
 
 //#define SCHED_DEBUG 1
-static inline void call_post_select(struct sched *s, struct task *t)
+static inline void call_post_monitor(struct sched *s, struct task *t)
 {
        int ret;
 
 #ifndef SCHED_DEBUG
 {
        int ret;
 
 #ifndef SCHED_DEBUG
-       ret = t->info.post_select(s, t->info.context);
+       ret = t->info.post_monitor(s, t->info.context);
 #else
        struct timeval t1, t2, diff;
        unsigned long pst;
 
        clock_get_realtime(&t1);
 #else
        struct timeval t1, t2, diff;
        unsigned long pst;
 
        clock_get_realtime(&t1);
-       ret = t->info.post_select(s, t->info.context);
+       ret = t->info.post_monitor(s, t->info.context);
        clock_get_realtime(&t2);
        tv_diff(&t1, &t2, &diff);
        pst = tv2ms(&diff);
        if (pst > 50)
        clock_get_realtime(&t2);
        tv_diff(&t1, &t2, &diff);
        pst = tv2ms(&diff);
        if (pst > 50)
-               PARA_WARNING_LOG("%s: post_select time: %lums\n",
+               PARA_WARNING_LOG("%s: post_monitor time: %lums\n",
                        t->name, pst);
 #endif
        t->status = ret < 0? ret : TS_RUNNING;
 }
 
                        t->name, pst);
 #endif
        t->status = ret < 0? ret : TS_RUNNING;
 }
 
-static unsigned sched_post_select(struct sched *s)
+static unsigned sched_post_monitor(struct sched *s)
 {
        struct task *t, *tmp;
        unsigned num_running_tasks = 0;
 {
        struct task *t, *tmp;
        unsigned num_running_tasks = 0;
@@ -103,7 +99,7 @@ static unsigned sched_post_select(struct sched *s)
                if (t->status == TS_DEAD) /* task has been reaped */
                        unlink_and_free_task(t);
                else if (t->status == TS_RUNNING) {
                if (t->status == TS_DEAD) /* task has been reaped */
                        unlink_and_free_task(t);
                else if (t->status == TS_RUNNING) {
-                       call_post_select(s, t); /* sets t->status */
+                       call_post_monitor(s, t); /* sets t->status */
                        t->notification = 0;
                        if (t->status == TS_RUNNING)
                                num_running_tasks++;
                        t->notification = 0;
                        if (t->status == TS_RUNNING)
                                num_running_tasks++;
@@ -117,13 +113,13 @@ static unsigned sched_post_select(struct sched *s)
  *
  * \param s Pointer to the scheduler struct.
  *
  *
  * \param s Pointer to the scheduler struct.
  *
- * This function updates the global \a now pointer, calls all registered
- * pre_select hooks which may set the timeout and add any file descriptors to
- * the fd sets of \a s.  Next, it calls para_select() and makes the result available
- * to the registered tasks by calling their post_select hook.
+ * This function updates the global now pointer, calls all registered
+ * pre_monitor hooks which may set the timeout and add any file descriptors to
+ * the pollfd array. Next, it calls the poll function and makes the result
+ * available to the registered tasks by calling their post_monitor hook.
  *
  * \return Zero if no more tasks are left in the task list, negative if the
  *
  * \return Zero if no more tasks are left in the task list, negative if the
- * select function returned an error.
+ * poll function returned an error.
  *
  * \sa \ref now.
  */
  *
  * \sa \ref now.
  */
@@ -132,31 +128,20 @@ int schedule(struct sched *s)
        int ret;
        unsigned num_running_tasks;
 
        int ret;
        unsigned num_running_tasks;
 
-       if (!s->select_function)
-               s->select_function = para_select;
+       if (!s->poll_function)
+               s->poll_function = xpoll;
 again:
 again:
-       FD_ZERO(&s->rfds);
-       FD_ZERO(&s->wfds);
-       s->select_timeout = s->default_timeout;
-       s->max_fileno = -1;
+       s->num_pfds = 0;
+       if (s->pidx)
+               memset(s->pidx, 0xff, s->pidx_array_len * sizeof(unsigned));
+       s->timeout = s->default_timeout;
        clock_get_realtime(&now_struct);
        clock_get_realtime(&now_struct);
-       sched_preselect(s);
-       ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
-               &s->select_timeout);
+       sched_pre_monitor(s);
+       ret = s->poll_function(s->pfd, s->num_pfds, s->timeout);
        if (ret < 0)
                return ret;
        if (ret < 0)
                return ret;
-       if (ret == 0) {
-               /*
-                * APUE: Be careful not to check the descriptor sets on return
-                * unless the return value is greater than zero. The return
-                * state of the descriptor sets is implementation dependent if
-                * either a signal is caught or the timer expires.
-                */
-               FD_ZERO(&s->rfds);
-               FD_ZERO(&s->wfds);
-       }
        clock_get_realtime(&now_struct);
        clock_get_realtime(&now_struct);
-       num_running_tasks = sched_post_select(s);
+       num_running_tasks = sched_post_monitor(s);
        if (num_running_tasks == 0)
                return 0;
        goto again;
        if (num_running_tasks == 0)
                return 0;
        goto again;
@@ -194,10 +179,11 @@ int task_reap(struct task **tptr)
        if (t->status >= 0)
                return 0;
        ret = t->status;
        if (t->status >= 0)
                return 0;
        ret = t->status;
+       PARA_INFO_LOG("reaping %s: %s\n", t->name, para_strerror(-ret));
        /*
         * With list_for_each_entry_safe() it is only safe to remove the
         * _current_ list item. Since we are being called from the loop in
        /*
         * With list_for_each_entry_safe() it is only safe to remove the
         * _current_ list item. Since we are being called from the loop in
-        * schedule() via some task's ->post_select() function, freeing the
+        * schedule() via some task's ->post_monitor() function, freeing the
         * given task here would result in use-after-free bugs in schedule().
         * So we only set the task status to TS_DEAD which tells schedule() to
         * free the task in the next iteration of its loop.
         * given task here would result in use-after-free bugs in schedule().
         * So we only set the task status to TS_DEAD which tells schedule() to
         * free the task in the next iteration of its loop.
@@ -226,6 +212,8 @@ void sched_shutdown(struct sched *s)
                                t->name);
                unlink_and_free_task(t);
        }
                                t->name);
                unlink_and_free_task(t);
        }
+       free(s->pfd);
+       free(s->pidx);
 }
 
 /**
 }
 
 /**
@@ -239,9 +227,9 @@ void sched_shutdown(struct sched *s)
  */
 struct task *task_register(struct task_info *info, struct sched *s)
 {
  */
 struct task *task_register(struct task_info *info, struct sched *s)
 {
-       struct task *t = para_malloc(sizeof(*t));
+       struct task *t = alloc(sizeof(*t));
 
 
-       assert(info->post_select);
+       assert(info->post_monitor);
 
        if (!s->task_list.next)
                init_list_head(&s->task_list);
 
        if (!s->task_list.next)
                init_list_head(&s->task_list);
@@ -288,14 +276,14 @@ char *get_task_list(struct sched *s)
  * \param err A positive error code.
  *
  * Tasks which honor notifications are supposed to call \ref
  * \param err A positive error code.
  *
  * Tasks which honor notifications are supposed to call \ref
- * task_get_notification() in their post_select function and act on the
+ * task_get_notification() in their post_monitor function and act on the
  * returned notification value.
  *
  * returned notification value.
  *
- * If the scheduler detects during its pre_select loop that at least one task
- * has been notified, the loop terminates, and the post_select methods of all
+ * If the scheduler detects during its pre_monitor loop that at least one task
+ * has been notified, the loop terminates, and the post_monitor methods of all
  * taks are immediately called again.
  *
  * taks are immediately called again.
  *
- * The notification for a task is reset after the call to its post_select
+ * The notification for a task is reset after the call to its post_monitor
  * method.
  *
  * \sa \ref task_get_notification().
  * method.
  *
  * \sa \ref task_get_notification().
@@ -316,7 +304,7 @@ void task_notify(struct task *t, int err)
  *
  * \return The notification value. If this is negative, the task has been
  * notified by another task. Tasks are supposed to check for notifications by
  *
  * \return The notification value. If this is negative, the task has been
  * notified by another task. Tasks are supposed to check for notifications by
- * calling this function from their post_select method.
+ * calling this function from their post_monitor method.
  *
  * \sa \ref task_notify().
  */
  *
  * \sa \ref task_notify().
  */
@@ -362,43 +350,43 @@ void task_notify_all(struct sched *s, int err)
 }
 
 /**
 }
 
 /**
- * Set the select timeout to the minimal possible value.
+ * Set the I/O timeout to the minimal possible value.
  *
  * \param s Pointer to the scheduler struct.
  *
  *
  * \param s Pointer to the scheduler struct.
  *
- * This causes the next select() call to return immediately.
+ * This causes the next poll() call to return immediately.
  */
 void sched_min_delay(struct sched *s)
 {
  */
 void sched_min_delay(struct sched *s)
 {
-       s->select_timeout.tv_sec = s->select_timeout.tv_usec = 0;
+       s->timeout = 0;
 }
 
 /**
 }
 
 /**
- * Impose an upper bound for the timeout of the next select() call.
+ * Impose an upper bound for the I/O timeout.
  *
  * \param to Maximal allowed timeout.
  * \param s Pointer to the scheduler struct.
  *
  *
  * \param to Maximal allowed timeout.
  * \param s Pointer to the scheduler struct.
  *
- * If the current scheduler timeout is already smaller than \a to, this
- * function does nothing. Otherwise the timeout for the next select() call is
- * set to the given value.
+ * If the current I/O timeout is already smaller than to, this function does
+ * nothing. Otherwise the timeout is set to the given value.
  *
  * \sa \ref sched_request_timeout_ms().
  */
 void sched_request_timeout(struct timeval *to, struct sched *s)
 {
  *
  * \sa \ref sched_request_timeout_ms().
  */
 void sched_request_timeout(struct timeval *to, struct sched *s)
 {
-       if (tv_diff(&s->select_timeout, to, NULL) > 0)
-               s->select_timeout = *to;
+       long unsigned ms = tv2ms(to);
+       if (s->timeout > ms)
+               s->timeout = ms;
 }
 
 /**
 }
 
 /**
- * Force the next select() call to return before the given amount of milliseconds.
+ * Bound the I/O timeout to at most the given amount of milliseconds.
  *
  * \param ms The maximal allowed timeout in milliseconds.
  * \param s Pointer to the scheduler struct.
  *
  *
  * \param ms The maximal allowed timeout in milliseconds.
  * \param s Pointer to the scheduler struct.
  *
- * Like sched_request_timeout() this imposes an upper bound on the timeout
- * value for the next select() call.
+ * Like \ref sched_request_timeout() this imposes an upper bound on the I/O
+ * timeout.
  */
 void sched_request_timeout_ms(long unsigned ms, struct sched *s)
 {
  */
 void sched_request_timeout_ms(long unsigned ms, struct sched *s)
 {
@@ -408,13 +396,13 @@ void sched_request_timeout_ms(long unsigned ms, struct sched *s)
 }
 
 /**
 }
 
 /**
- * Force the next select() call to return before the given future time.
+ * Bound the I/O timeout by an absolute time in the future.
  *
  *
- * \param barrier Absolute time before select() should return.
+ * \param barrier Defines the upper bound for the timeout.
  * \param s Pointer to the scheduler struct.
  *
  * \param s Pointer to the scheduler struct.
  *
- * \return If \a barrier is in the past, this function does nothing and returns
- * zero. Otherwise it returns one.
+ * \return If the barrier is in the past, this function does nothing and
+ * returns zero. Otherwise it returns one.
  *
  * \sa \ref sched_request_barrier_or_min_delay().
  */
  *
  * \sa \ref sched_request_barrier_or_min_delay().
  */
@@ -429,12 +417,12 @@ int sched_request_barrier(struct timeval *barrier, struct sched *s)
 }
 
 /**
 }
 
 /**
- * Force the next select() call to return before the given time.
+ * Bound the I/O timeout or request a minimal delay.
  *
  *
- * \param barrier Absolute time before select() should return.
+ * \param barrier Absolute time as in \ref sched_request_barrier().
  * \param s Pointer to the scheduler struct.
  *
  * \param s Pointer to the scheduler struct.
  *
- * \return If \a barrier is in the past, this function requests a minimal
+ * \return If the barrier is in the past, this function requests a minimal
  * timeout and returns zero. Otherwise it returns one.
  *
  * \sa \ref sched_min_delay(), \ref sched_request_barrier().
  * timeout and returns zero. Otherwise it returns one.
  *
  * \sa \ref sched_min_delay(), \ref sched_request_barrier().
@@ -450,3 +438,126 @@ int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s)
        sched_request_timeout(&diff, s);
        return 1;
 }
        sched_request_timeout(&diff, s);
        return 1;
 }
+
+static void add_pollfd(int fd, struct sched *s, short events)
+{
+       assert(fd >= 0);
+#if 0
+       {
+               int flags = fcntl(fd, F_GETFL);
+               if (!(flags & O_NONBLOCK)) {
+                       PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
+                       exit(EXIT_FAILURE);
+               }
+       }
+#endif
+       if (s->pidx_array_len > fd) { /* is fd already registered? */
+               if (s->pidx[fd] < s->pfd_array_len) { /* yes, it is */
+                       assert(s->pfd[s->pidx[fd]].fd == fd);
+                       s->pfd[s->pidx[fd]].events |= events;
+                       return;
+               }
+       } else { /* need to extend the index array */
+               unsigned old_len = s->pidx_array_len;
+               while (s->pidx_array_len <= fd)
+                       s->pidx_array_len = s->pidx_array_len * 2 + 1;
+               PARA_INFO_LOG("pidx array len: %u\n", s->pidx_array_len);
+               s->pidx = para_realloc(s->pidx,
+                       s->pidx_array_len * sizeof(unsigned));
+               memset(s->pidx + old_len, 0xff,
+                       (s->pidx_array_len - old_len) * sizeof(unsigned));
+       }
+       /*
+        * The given fd is not part of the pfd array yet. Initialize pidx[fd]
+        * to point at the next unused slot of this array and initialize the
+        * slot.
+        */
+       s->pidx[fd] = s->num_pfds;
+       if (s->pfd_array_len <= s->num_pfds) {
+               unsigned old_len = s->pfd_array_len;
+               s->pfd_array_len = old_len * 2 + 1;
+               PARA_INFO_LOG("pfd array len: %u\n", s->pfd_array_len);
+               s->pfd = para_realloc(s->pfd,
+                       s->pfd_array_len * sizeof(struct pollfd));
+               memset(s->pfd + old_len, 0,
+                       (s->pfd_array_len - old_len) * sizeof(struct pollfd));
+       }
+       s->pfd[s->num_pfds].fd = fd;
+       s->pfd[s->num_pfds].events = events;
+       s->pfd[s->num_pfds].revents = 0;
+       s->num_pfds++;
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for reading.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_writefd().
+ */
+void sched_monitor_readfd(int fd, struct sched *s)
+{
+       add_pollfd(fd, s, POLLIN);
+}
+
+/**
+ * Instruct the scheduler to monitor an fd for readiness for writing.
+ *
+ * \param fd The file descriptor.
+ * \param s The scheduler.
+ *
+ * \sa \ref sched_monitor_readfd().
+ */
+void sched_monitor_writefd(int fd, struct sched *s)
+{
+       add_pollfd(fd, s, POLLOUT);
+}
+
+static int get_revents(int fd, const struct sched *s)
+{
+       if (fd < 0)
+               return 0;
+       if (fd >= s->pidx_array_len)
+               return 0;
+       if (s->pidx[fd] >= s->num_pfds)
+               return 0;
+       if (s->pfd[s->pidx[fd]].fd != fd)
+               return 0;
+       assert((s->pfd[s->pidx[fd]].revents & POLLNVAL) == 0);
+       return s->pfd[s->pidx[fd]].revents;
+}
+
+/**
+ * Check whether there is data to read on the given fd.
+ *
+ * To be called from the ->post_monitor() method of a task.
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_readfd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for reading, false otherwise.
+ * If fd is negative, or has not been monitored in the current iteration of the
+ * scheduler's main loop, the function also returns false.
+ *
+ * \sa \ref sched_write_ok().
+ */
+bool sched_read_ok(int fd, const struct sched *s)
+{
+       return get_revents(fd, s) & (POLLIN | POLLERR | POLLHUP);
+}
+
+/**
+ * Check whether writing is possible (i.e., does not block).
+ *
+ * \param fd Should have been monitored with \ref sched_monitor_writefd().
+ * \param s The scheduler instance.
+ *
+ * \return True if the file descriptor is ready for writing, false otherwise.
+ * The comment in \ref sched_read_ok() about invalid file descriptors applies
+ * to this function as well.
+ */
+bool sched_write_ok(int fd, const struct sched *s)
+{
+       return get_revents(fd, s) & (POLLOUT | POLLERR | POLLHUP);
+}
diff --git a/sched.h b/sched.h
index 35e2503e383be3611fc302f5a732e45cf322d506..ede5e67ea2ac042c5765a92e6100bc00ab47e103 100644 (file)
--- a/sched.h
+++ b/sched.h
@@ -7,24 +7,30 @@
  * Paraslash's scheduler.
  *
  * Designed with KISS in mind. It maintains a list of task structures which is
  * Paraslash's scheduler.
  *
  * Designed with KISS in mind. It maintains a list of task structures which is
- * extended when a new task is registered. Each task may define a pre_select
+ * extended when a new task is registered. Each task may define a pre_monitor
  * function which is called from the scheduler main loop before it calls
  * function which is called from the scheduler main loop before it calls
- * select(). Similarly, each task must define a post_select function which is
- * called after the select call.
+ * poll(2). Similarly, each task must define a post_monitor function which is
+ * called after poll(2) returns.
+ *
+ * \sa select(2), poll(2).
  */
 struct sched {
  */
 struct sched {
-       /** Initial value before any pre_select call. */
-       struct timeval default_timeout;
-       /** The current timeout for the upcoming select call. */
-       struct timeval select_timeout;
-       /** fds that should be watched for readability. */
-       fd_set rfds;
-       /** fds that should be watched for writability. */
-       fd_set wfds;
-       /** Highest numbered file descriptor in any of the above fd sets. */
-       int max_fileno;
-       /** If non-NULL, use this function instead of para_select. */
-       int (*select_function)(int, fd_set *, fd_set *, struct timeval *);
+       /** Initial value (in milliseconds) before any pre_monitor call. */
+       int default_timeout;
+       /** The timeout (also in milliseconds) for the next iteration. */
+       int timeout;
+       /** Passed to poll(2). */
+       struct pollfd *pfd;
+       /** Number of elements in the above array, passed to poll(2). */
+       unsigned pfd_array_len;
+       /** Number of fds registered for montitoring so far. */
+       unsigned num_pfds;
+       /** Maps fds to indices of the pfd array. */
+       unsigned *pidx;
+       /** Mumber of elements in the above pidx array. */
+       unsigned pidx_array_len;
+       /** If non-NULL, use this function instead of \ref xpoll(). */
+       int (*poll_function)(struct pollfd *fds, nfds_t nfds, int timeout);
        /** Tasks which have been registered to the scheduler. */
        struct list_head task_list;
 };
        /** Tasks which have been registered to the scheduler. */
        struct list_head task_list;
 };
@@ -36,23 +42,32 @@ struct task_info {
        /** Used for log messages and by \ref get_task_list(). */
        const char *name;
        /**
        /** Used for log messages and by \ref get_task_list(). */
        const char *name;
        /**
-        * The optional pre select method.
+        * Configure watch fds and impose an upper bound on the I/O timeout.
+        *
+        * If this is not NULL, the function is called at each iteration of the
+        * scheduler's main loop. Its purpose is to tell the scheduler that
+        * certain file descriptors should be monitored for readiness for I/O.
+        * The function may also lower the scheduler's timeout value (but shall
+        * never increase it) to impose an upper bound on the waiting time in
+        * case no file descriptors happen to be ready.
         *
         *
-        * Its purpose is to add file descriptors to the fd sets of the
-        * scheduler and to decrease the select timeout if necessary.
+        * \sa \ref time.c.
         */
         */
-       void (*pre_select)(struct sched *s, void *context);
+       void (*pre_monitor)(struct sched *s, void *context);
        /**
        /**
-        * The mandatory post select method.
+        * Perform I/O on file descriptors which are ready for I/O.
+        *
+        * This mandatory hook is called after the system call which monitors
+        * file descriptors returns. The function should perform non-blocking
+        * I/O on those file descriptors which are reported as being ready.
         *
         *
-        * Its purpose is to evaluate and act upon the results of the previous
-        * select call. If this function returns a negative value, the
-        * scheduler unregisters the task.
+        * If this function returns a negative value, the scheduler unregisters
+        * the task.
         */
         */
-       int (*post_select)(struct sched *s, void *context);
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * This pointer is saved when the task is registered. It is passed to
        /**
         * This pointer is saved when the task is registered. It is passed to
-        * ->pre_select() and ->post_select(). Usually this is a pointer to the
+        * ->pre_monitor() and ->post_monitor(). Usually this is a pointer to the
         * struct owned by the caller which contains the task pointer.
         */
        void *context;
         * struct owned by the caller which contains the task pointer.
         */
        void *context;
@@ -80,3 +95,7 @@ void sched_request_timeout(struct timeval *to, struct sched *s);
 void sched_request_timeout_ms(long unsigned ms, struct sched *s);
 int sched_request_barrier(struct timeval *barrier, struct sched *s);
 int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s);
 void sched_request_timeout_ms(long unsigned ms, struct sched *s);
 int sched_request_barrier(struct timeval *barrier, struct sched *s);
 int sched_request_barrier_or_min_delay(struct timeval *barrier, struct sched *s);
+void sched_monitor_readfd(int fd, struct sched *s);
+void sched_monitor_writefd(int fd, struct sched *s);
+bool sched_read_ok(int fd, const struct sched *s);
+bool sched_write_ok(int fd, const struct sched *s);
diff --git a/score.c b/score.c
index 894e8ca3deae39f21552a6cd331237551e9c7a1b..c03e3472da306a1d535b201ea2d23c90ccf8f90a 100644 (file)
--- a/score.c
+++ b/score.c
@@ -76,23 +76,11 @@ static struct osl_table_description score_table_desc = {
        .column_descriptions = score_cols
 };
 
        .column_descriptions = score_cols
 };
 
-/**
- * Compute the number of files in score table.
- *
- * \param num Result is returned here.
- *
- * \return Positive on success, negative on errors.
- */
-int get_num_admissible_files(unsigned *num)
-{
-       return osl(osl_get_num_rows(score_table, num));
-}
-
 /* On errors (negative return value) the content of score is undefined. */
 /* On errors (negative return value) the content of score is undefined. */
-static int get_score_of_row(void *score_row, long *score)
+static int get_score_of_row(struct osl_table *t, void *score_row, long *score)
 {
        struct osl_object obj;
 {
        struct osl_object obj;
-       int ret = osl(osl_get_object(score_table, score_row, SCORECOL_SCORE, &obj));
+       int ret = osl(osl_get_object(t, score_row, SCORECOL_SCORE, &obj));
 
        if (ret >= 0)
                *score = *(long *)obj.data;
 
        if (ret >= 0)
                *score = *(long *)obj.data;
@@ -100,14 +88,15 @@ static int get_score_of_row(void *score_row, long *score)
 }
 
 /**
 }
 
 /**
- * Add an entry to the table of admissible files.
+ * Add a (row, score) pair to the score table.
  *
  *
- * \param aft_row The audio file to be added.
- * \param score The score for this file.
+ * \param aft_row Identifies the audio file to be added.
+ * \param score The score value of the audio file.
+ * \param t NULL means to operate on the currently active table.
  *
  * \return The return value of the underlying call to osl_add_row().
  */
  *
  * \return The return value of the underlying call to osl_add_row().
  */
-int score_add(const struct osl_row *aft_row, long score)
+int score_add(const struct osl_row *aft_row, long score, struct osl_table *t)
 {
        int ret;
        struct osl_object score_objs[NUM_SCORE_COLUMNS];
 {
        int ret;
        struct osl_object score_objs[NUM_SCORE_COLUMNS];
@@ -119,12 +108,12 @@ int score_add(const struct osl_row *aft_row, long score)
        score_objs[SCORECOL_AFT_ROW].size = size;
 
        size = score_table_desc.column_descriptions[SCORECOL_SCORE].data_size;
        score_objs[SCORECOL_AFT_ROW].size = size;
 
        size = score_table_desc.column_descriptions[SCORECOL_SCORE].data_size;
-       score_objs[SCORECOL_SCORE].data = para_malloc(size);
+       score_objs[SCORECOL_SCORE].data = alloc(size);
        score_objs[SCORECOL_SCORE].size = size;
        *(long *)(score_objs[SCORECOL_SCORE].data) = score;
 
 //     PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
        score_objs[SCORECOL_SCORE].size = size;
        *(long *)(score_objs[SCORECOL_SCORE].data) = score;
 
 //     PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
-       ret = osl(osl_add_row(score_table, score_objs));
+       ret = osl(osl_add_row(t? t : score_table, score_objs));
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                free(score_objs[SCORECOL_SCORE].data);
        if (ret < 0) {
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                free(score_objs[SCORECOL_SCORE].data);
@@ -132,16 +121,6 @@ int score_add(const struct osl_row *aft_row, long score)
        return ret;
 }
 
        return ret;
 }
 
-static int get_nth_score(unsigned n, long *score)
-{
-       struct osl_row *row;
-       int ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, n, &row));
-
-       if (ret < 0)
-               return ret;
-       return get_score_of_row(row, score);
-}
-
 /**
  * Replace a row of the score table.
  *
 /**
  * Replace a row of the score table.
  *
@@ -156,7 +135,7 @@ static int get_nth_score(unsigned n, long *score)
  */
 int score_update(const struct osl_row *aft_row, long percent)
 {
  */
 int score_update(const struct osl_row *aft_row, long percent)
 {
-       struct osl_row *row;
+       struct osl_row *row, *rrow; /* score row, reference row */
        long new_score;
        unsigned n, new_pos;
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
        long new_score;
        unsigned n, new_pos;
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
@@ -167,16 +146,19 @@ int score_update(const struct osl_row *aft_row, long percent)
                return 1;
        if (ret < 0)
                return ret;
                return 1;
        if (ret < 0)
                return ret;
-       ret = get_num_admissible_files(&n);
+       ret = osl(osl_get_num_rows(score_table, &n));
        if (ret < 0)
                return ret;
        new_pos = 1 + (n - 1) * percent / 100;
        if (ret < 0)
                return ret;
        new_pos = 1 + (n - 1) * percent / 100;
-       ret = get_nth_score(new_pos, &new_score);
+       ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
+       if (ret < 0)
+               return ret;
+       ret = get_score_of_row(score_table, rrow, &new_score);
        if (ret < 0)
                return ret;
        new_score--;
        obj.size = sizeof(long);
        if (ret < 0)
                return ret;
        new_score--;
        obj.size = sizeof(long);
-       obj.data = para_malloc(obj.size);
+       obj.data = alloc(obj.size);
        *(long *)obj.data = new_score;
        PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
        return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj));
        *(long *)obj.data = new_score;
        PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
        return osl(osl_update_object(score_table, row, SCORECOL_SCORE, &obj));
@@ -195,7 +177,7 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score,
                struct osl_row **aft_row)
 {
        struct osl_object obj;
                struct osl_row **aft_row)
 {
        struct osl_object obj;
-       int ret = get_score_of_row(score_row, score);
+       int ret = get_score_of_row(score_table, score_row, score);
 
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
@@ -206,28 +188,28 @@ int get_score_and_aft_row(struct osl_row *score_row, long *score,
        return 1;
 }
 
        return 1;
 }
 
-static int get_score_row_from_aft_row(const struct osl_row *aft_row,
-               struct osl_row **score_row)
+static int get_score_row_from_aft_row(struct osl_table *t,
+               const struct osl_row *aft_row, struct osl_row **score_row)
 {
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
                .size = sizeof(aft_row)};
 {
        struct osl_object obj = {.data = (struct osl_row *)aft_row,
                .size = sizeof(aft_row)};
-       return osl(osl_get_row(score_table, SCORECOL_AFT_ROW, &obj, score_row));
+       return osl(osl_get_row(t, SCORECOL_AFT_ROW, &obj, score_row));
 }
 
 /**
 }
 
 /**
- * Loop over all files in the score table.
- *
- * \param data A pointer to arbitrary data.
- * \param func Function to be called for each admissible file.
+ * Call the given function for each row of the score table.
  *
  *
- * \return The return value of the underlying call to osl_rbtree_loop().
+ * \param func Callback, called once per row.
+ * \param t NULL means to use the currently active score table.
+ * \param data Passed verbatim to the callback.
  *
  *
- * This is used for the ls command. The \a data parameter is passed as the
- * second argument to \a func.
+ * \return The return value of the underlying call to osl_rbtree_loop(). The
+ * loop terminates early if the callback returns negative.
  */
  */
-int admissible_file_loop(void *data, osl_rbtree_loop_func *func)
+int score_loop(osl_rbtree_loop_func *func, struct osl_table *t, void *data)
 {
 {
-       return osl(osl_rbtree_loop(score_table, SCORECOL_SCORE, data, func));
+       return osl(osl_rbtree_loop(t? t : score_table, SCORECOL_SCORE, data,
+               func));
 }
 
 /**
 }
 
 /**
@@ -250,7 +232,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
        if (ret < 0)
                return ret;
        *aft_row = obj.data;
        if (ret < 0)
                return ret;
        *aft_row = obj.data;
-       return get_score_of_row(row, score);
+       return get_score_of_row(score_table, row, score);
 }
 
 /**
 }
 
 /**
@@ -265,7 +247,7 @@ int score_get_best(struct osl_row **aft_row, long *score)
 int score_delete(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
 int score_delete(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
-       int ret = get_score_row_from_aft_row(aft_row, &score_row);
+       int ret = get_score_row_from_aft_row(score_table, aft_row, &score_row);
 
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
@@ -276,67 +258,72 @@ int score_delete(const struct osl_row *aft_row)
  * Find out whether an audio file is contained in the score table.
  *
  * \param aft_row The row of the audio file table.
  * Find out whether an audio file is contained in the score table.
  *
  * \param aft_row The row of the audio file table.
- * \param rank Result pointer
  *
  *
- * \return Positive, if \a aft_row belongs to the audio file table,
- * zero if not, negative on errors. If \a aft_row was found, and \a rank
- * is not \p NULL, the rank of \a aft_row is returned in \a rank.
+ * \return If the lookup operation fails for any other reason than "not found",
+ * the function aborts the current process (afs), since this is considered a
+ * fatal error that should never happen.
  */
  */
-int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
+bool row_belongs_to_score_table(const struct osl_row *aft_row)
 {
        struct osl_row *score_row;
 {
        struct osl_row *score_row;
-       int ret = get_score_row_from_aft_row(aft_row, &score_row);
+       int ret = get_score_row_from_aft_row(score_table, aft_row, &score_row);
 
        if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
 
        if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
-               return 0;
-       if (ret < 0)
-               return ret;
-       if (!rank)
-               return 1;
-       ret = osl(osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank));
-       if (ret < 0)
-               return ret;
-       return 1;
+               return false;
+       assert(ret >= 0);
+       return true;
 }
 
 }
 
-static void score_close(void)
+/**
+ * Free all volatile objects, then close the table.
+ *
+ * \param t As returned from \ref score_open().
+ *
+ * This either succeeds or terminates the calling process.
+ */
+void score_close(struct osl_table *t)
 {
 {
-       osl_close_table(score_table, OSL_FREE_VOLATILE);
-       score_table = NULL;
+       assert(osl_close_table(t? t : score_table, OSL_FREE_VOLATILE) >= 0);
 }
 
 }
 
-static int score_open(__a_unused const char *dir)
+static void close_global_table(void)
 {
 {
-       score_table_desc.dir = NULL; /* this table has only volatile columns */
-       return osl(osl_open_table(&score_table_desc, &score_table));
+       score_close(NULL);
 }
 
 }
 
-/**
- * Remove all entries from the score table, but keep the table open.
- *
- * \return Standard.
- */
-int clear_score_table(void)
+static int open_global_table(__a_unused const char *dir)
 {
 {
-       score_close();
-       return score_open(NULL);
+       assert(osl(osl_open_table(&score_table_desc, &score_table)) >= 0);
+       return 1;
 }
 
 }
 
-static int score_event_handler(__a_unused enum afs_events event,
-               __a_unused struct para_buffer *pb, __a_unused void *data)
+/**
+ * Allocate a score table instance.
+ *
+ * \param result NULL means to open the currently active score table.
+ *
+ * Since the score table does no filesystem I/O, this function always succeeds.
+ * \sa \ref score_close().
+ */
+void score_open(struct osl_table **result)
 {
 {
-       return 1;
+       if (result)
+               assert(osl(osl_open_table(&score_table_desc, result)) >= 0);
+       else
+               open_global_table(NULL);
 }
 
 /**
 }
 
 /**
- * Initialize the scoring subsystem.
- *
- * \param t The members of \a t are filled in by the function.
+ * Remove all entries from the score table, but keep the table open.
  */
  */
-void score_init(struct afs_table *t)
+void score_clear(void)
 {
 {
-       t->name = score_table_desc.name;
-       t->open = score_open;
-       t->close = score_close;
-       t->event_handler = score_event_handler;
+       close_global_table();
+       open_global_table(NULL);
 }
 }
+
+/** The score table stores (aft row, score) pairs in memory. */
+const struct afs_table_operations score_ops = {
+       .open = open_global_table,
+       .close = close_global_table,
+};
diff --git a/send.h b/send.h
index f6aafbb41a2bf9b089f2ddcc9968278dea734fa1..3407bc5c28a876f1fb5aca6e748368deb58afe0b 100644 (file)
--- a/send.h
+++ b/send.h
@@ -76,27 +76,10 @@ struct sender {
        void (*send)(long unsigned current_chunk, long unsigned chunks_sent,
                const char *buf, size_t len, const char *header_buf,
                size_t header_len);
        void (*send)(long unsigned current_chunk, long unsigned chunks_sent,
                const char *buf, size_t len, const char *header_buf,
                size_t header_len);
-       /**
-        * Add file descriptors to fd_sets.
-        *
-        * The pre_select function of each supported sender is called just before
-        * para_server enters its main select loop. Each sender may add its own
-        * file descriptors to the \a rfds or the \a wfds set.
-        *
-        * If a file descriptor was added, \a max_fileno must be increased by
-        * this function, if necessary.
-        *
-        * \sa select(2).
-        */
-       void (*pre_select)(int *max_fileno, fd_set *rfds, fd_set *wfds);
-       /**
-        * Handle the file descriptors which are ready for I/O.
-        *
-        * If the pre_select hook added one ore more file descriptors to the
-        * read or write set, this is the hook to check the result and do any
-        * I/O on those descriptors which are ready for reading/writing.
-        */
-       void (*post_select)(fd_set *rfds, fd_set *wfds);
+       /** Ask the scheduler to monitor file descriptors. */
+       void (*pre_monitor)(struct sched *s);
+       /** Perform I/O on the file descriptors which are ready. */
+       void (*post_monitor)(struct sched *s);
        /**
         * Terminate all connected clients.
         *
        /**
         * Terminate all connected clients.
         *
@@ -213,7 +196,7 @@ void init_sender_status(struct sender_status *ss,
                const struct lls_opt_result *listen_address_opt_result,
                int default_port, int max_clients, int default_deny);
 void free_sender_status(const struct sender_status *ss);
                const struct lls_opt_result *listen_address_opt_result,
                int default_port, int max_clients, int default_deny);
 void free_sender_status(const struct sender_status *ss);
-char *generic_sender_status(struct sender_status *ss, const char *name);
+__malloc char *generic_sender_status(struct sender_status *ss, const char *name);
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
 void generic_com_deny(struct sender_command_data *scd,
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
 void generic_com_deny(struct sender_command_data *scd,
@@ -221,7 +204,7 @@ void generic_com_deny(struct sender_command_data *scd,
 void generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_acl_deplete(struct list_head *acl);
 void generic_com_off(struct sender_status *ss);
 void generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_acl_deplete(struct list_head *acl);
 void generic_com_off(struct sender_status *ss);
-char *generic_sender_help(void);
-struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds);
+__malloc char *generic_sender_help(void);
+struct sender_client *accept_sender_client(struct sender_status *ss);
 int send_queued_chunks(int fd, struct chunk_queue *cq);
 int parse_fec_url(const char *arg, struct sender_command_data *scd);
 int send_queued_chunks(int fd, struct chunk_queue *cq);
 int parse_fec_url(const char *arg, struct sender_command_data *scd);
index 90242d5c9b5ccb125617004e16fbf9b64d5484f6..8dc82e9cba42aecf027761c7682289fb0a9b6797 100644 (file)
 #include "afs.h"
 #include "server.h"
 #include "acl.h"
 #include "afs.h"
 #include "server.h"
 #include "acl.h"
+#include "sched.h"
 #include "send.h"
 #include "close_on_fork.h"
 #include "chunk_queue.h"
 #include "send.h"
 #include "close_on_fork.h"
 #include "chunk_queue.h"
-#include "sched.h"
 #include "vss.h"
 
 /** Clients will be kicked if there are more than that many bytes pending. */
 #include "vss.h"
 
 /** Clients will be kicked if there are more than that many bytes pending. */
@@ -120,14 +120,14 @@ void init_sender_status(struct sender_status *ss,
 
        if (n == 0) {
                ss->num_listen_fds = 1;
 
        if (n == 0) {
                ss->num_listen_fds = 1;
-               ss->listen_addresses = para_malloc(sizeof(char *));
+               ss->listen_addresses = alloc(sizeof(char *));
                ss->listen_addresses[0] = NULL;
                ss->listen_addresses[0] = NULL;
-               ss->listen_fds = para_malloc(sizeof(int));
+               ss->listen_fds = alloc(sizeof(int));
                ss->listen_fds[0] = -1;
        } else {
                ss->num_listen_fds = n;
                ss->listen_fds[0] = -1;
        } else {
                ss->num_listen_fds = n;
-               ss->listen_addresses = para_malloc(n * sizeof(char *));
-               ss->listen_fds = para_malloc(n * sizeof(int));
+               ss->listen_addresses = alloc(n * sizeof(char *));
+               ss->listen_fds = alloc(n * sizeof(int));
                FOR_EACH_LISTEN_FD(i, ss) {
                        ss->listen_addresses[i] = para_strdup(lls_string_val(i,
                                listen_address_opt_result));
                FOR_EACH_LISTEN_FD(i, ss) {
                        ss->listen_addresses[i] = para_strdup(lls_string_val(i,
                                listen_address_opt_result));
@@ -181,7 +181,7 @@ void free_sender_status(const struct sender_status *ss)
  *
  * \return The string printed in the "si" command.
  */
  *
  * \return The string printed in the "si" command.
  */
-char *generic_sender_status(struct sender_status *ss, const char *name)
+__malloc char *generic_sender_status(struct sender_status *ss, const char *name)
 {
        char *clnts = NULL, *ret, *addr = NULL;
        struct sender_client *sc, *tmp_sc;
 {
        char *clnts = NULL, *ret, *addr = NULL;
        struct sender_client *sc, *tmp_sc;
@@ -343,7 +343,6 @@ void generic_com_off(struct sender_status *ss)
  * Accept a connection on the socket(s) this server is listening on.
  *
  * \param ss The sender whose listening fd is ready for reading.
  * Accept a connection on the socket(s) this server is listening on.
  *
  * \param ss The sender whose listening fd is ready for reading.
- * \param rfds Passed to para_accept(),
  *
  * This accepts incoming connections on any of the listening sockets of the
  * server. If there is a connection pending, the function
  *
  * This accepts incoming connections on any of the listening sockets of the
  * server. If there is a connection pending, the function
@@ -367,7 +366,7 @@ void generic_com_off(struct sender_status *ss)
  * \sa \ref para_accept(), \ref mark_fd_nonblocking(), \ref acl_check_access(),
  * \ref cq_new(), \ref add_close_on_fork_list().
  */
  * \sa \ref para_accept(), \ref mark_fd_nonblocking(), \ref acl_check_access(),
  * \ref cq_new(), \ref add_close_on_fork_list().
  */
-struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds)
+struct sender_client *accept_sender_client(struct sender_status *ss)
 {
        struct sender_client *sc;
        int fd, ret;
 {
        struct sender_client *sc;
        int fd, ret;
@@ -376,7 +375,7 @@ struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfd
        FOR_EACH_LISTEN_FD(n, ss) {
                if (ss->listen_fds[n] < 0)
                        continue;
        FOR_EACH_LISTEN_FD(n, ss) {
                if (ss->listen_fds[n] < 0)
                        continue;
-               ret = para_accept(ss->listen_fds[n], rfds, NULL, 0, &fd);
+               ret = para_accept(ss->listen_fds[n], NULL, 0, &fd);
                if (ret < 0)
                        goto warn;
                if (ret == 0)
                if (ret < 0)
                        goto warn;
                if (ret == 0)
@@ -391,7 +390,7 @@ struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfd
                if (ret < 0)
                        goto close_fd_and_warn;
                ss->num_clients++;
                if (ret < 0)
                        goto close_fd_and_warn;
                ss->num_clients++;
-               sc = para_calloc(sizeof(*sc));
+               sc = zalloc(sizeof(*sc));
                sc->fd = fd;
                sc->name = para_strdup(remote_name(fd));
                sc->cq = cq_new(MAX_CQ_BYTES);
                sc->fd = fd;
                sc->name = para_strdup(remote_name(fd));
                sc->cq = cq_new(MAX_CQ_BYTES);
@@ -414,7 +413,7 @@ warn:
  * \return A dynamically allocated string containing the help text for
  * a paraslash sender.
  */
  * \return A dynamically allocated string containing the help text for
  * a paraslash sender.
  */
-char *generic_sender_help(void)
+__malloc char *generic_sender_help(void)
 {
        return make_message(
                "usage: {on|off}\n"
 {
        return make_message(
                "usage: {on|off}\n"
index cb32d4d962e129ccf3589fa2ba3db8eb72e18984..ea9cc9c003616762a96aa546c381d54417fd34a2 100644 (file)
--- a/server.c
+++ b/server.c
@@ -24,8 +24,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "config.h"
 #include "close_on_fork.h"
 #include "vss.h"
 #include "config.h"
 #include "close_on_fork.h"
@@ -172,6 +172,7 @@ static void init_ipc_or_die(void)
        mmd->active_connections = 0;
        mmd->vss_status_flags = VSS_NEXT;
        mmd->new_vss_status_flags = VSS_NEXT;
        mmd->active_connections = 0;
        mmd->vss_status_flags = VSS_NEXT;
        mmd->new_vss_status_flags = VSS_NEXT;
+       mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL);
        return;
 destroy_mmd_mutex:
        mutex_destroy(mmd_mutex);
        return;
 destroy_mmd_mutex:
        mutex_destroy(mmd_mutex);
@@ -180,6 +181,9 @@ err_out:
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
+/** Get a reference to the supercommand of para_server. */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
 /**
  * (Re-)read the server configuration files.
  *
 /**
  * (Re-)read the server configuration files.
  *
@@ -205,7 +209,7 @@ void parse_config_or_die(bool reload)
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
                        para_strerror(-ret));
                exit(EXIT_FAILURE);
        }
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        if (OPT_GIVEN(LOGFILE)) {
                daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        if (OPT_GIVEN(LOGFILE)) {
                daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
@@ -250,14 +254,14 @@ static void handle_sighup(void)
                kill(afs_pid, SIGHUP);
 }
 
                kill(afs_pid, SIGHUP);
 }
 
-static int signal_post_select(struct sched *s, __a_unused void *context)
+static int signal_post_monitor(struct sched *s, __a_unused void *context)
 {
        int ret, signum;
 
        ret = task_get_notification(signal_task->task);
        if (ret < 0)
                return ret;
 {
        int ret, signum;
 
        ret = task_get_notification(signal_task->task);
        if (ret < 0)
                return ret;
-       signum = para_next_signal(&s->rfds);
+       signum = para_next_signal();
        switch (signum) {
        case 0:
                return 0;
        switch (signum) {
        case 0:
                return 0;
@@ -313,20 +317,20 @@ static void init_signal_task(void)
        add_close_on_fork_list(signal_task->fd);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
        add_close_on_fork_list(signal_task->fd);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
-               .pre_select = signal_pre_select,
-               .post_select = signal_post_select,
+               .pre_monitor = signal_pre_monitor,
+               .post_monitor = signal_post_monitor,
                .context = signal_task,
 
        }, &sched);
 }
 
                .context = signal_task,
 
        }, &sched);
 }
 
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
 {
        unsigned n;
        struct server_command_task *sct = context;
 
        for (n = 0; n < sct->num_listen_fds; n++)
 {
        unsigned n;
        struct server_command_task *sct = context;
 
        for (n = 0; n < sct->num_listen_fds; n++)
-               para_fd_set(sct->listen_fds[n], &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(sct->listen_fds[n], s);
 }
 
 static int command_task_accept(unsigned listen_idx, struct sched *s,
 }
 
 static int command_task_accept(unsigned listen_idx, struct sched *s,
@@ -337,7 +341,7 @@ static int command_task_accept(unsigned listen_idx, struct sched *s,
        pid_t child_pid;
        uint32_t *chunk_table;
 
        pid_t child_pid;
        uint32_t *chunk_table;
 
-       ret = para_accept(sct->listen_fds[listen_idx], &s->rfds, NULL, 0, &new_fd);
+       ret = para_accept(sct->listen_fds[listen_idx], NULL, 0, &new_fd);
        if (ret <= 0)
                goto out;
        mmd->num_connects++;
        if (ret <= 0)
                goto out;
        mmd->num_connects++;
@@ -383,13 +387,13 @@ static int command_task_accept(unsigned listen_idx, struct sched *s,
        /*
         * After we return, the scheduler calls server_select() with a minimal
         * timeout value, because the remaining tasks have a notification
        /*
         * After we return, the scheduler calls server_select() with a minimal
         * timeout value, because the remaining tasks have a notification
-        * pending. Next it calls the ->post_select method of these tasks,
+        * pending. Next it calls the ->post_monitor method of these tasks,
         * which will return negative in view of the notification. This causes
         * schedule() to return as there are no more runnable tasks.
         *
         * Note that semaphores are not inherited across a fork(), so we don't
         * which will return negative in view of the notification. This causes
         * schedule() to return as there are no more runnable tasks.
         *
         * Note that semaphores are not inherited across a fork(), so we don't
-        * hold the lock at this point. Since server_select() drops the lock
-        * prior to calling para_select(), we need to acquire it here.
+        * hold the lock at this point. Since server_poll() drops the lock
+        * prior to calling poll(), we need to acquire it here.
         */
        mutex_lock(mmd_mutex);
        return -E_CHILD_CONTEXT;
         */
        mutex_lock(mmd_mutex);
        return -E_CHILD_CONTEXT;
@@ -399,7 +403,7 @@ out:
        return 0;
 }
 
        return 0;
 }
 
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
 {
        struct server_command_task *sct = context;
        unsigned n;
 {
        struct server_command_task *sct = context;
        unsigned n;
@@ -407,15 +411,16 @@ static int command_post_select(struct sched *s, void *context)
 
        ret = task_get_notification(sct->task);
        if (ret < 0)
 
        ret = task_get_notification(sct->task);
        if (ret < 0)
-               return ret;
+               goto fail;
        for (n = 0; n < sct->num_listen_fds; n++) {
                ret = command_task_accept(n, s, sct);
        for (n = 0; n < sct->num_listen_fds; n++) {
                ret = command_task_accept(n, s, sct);
-               if (ret < 0) {
-                       free(sct->listen_fds);
-                       return ret;
-               }
+               if (ret < 0)
+                       goto fail;
        }
        return 0;
        }
        return 0;
+fail:
+       free(sct->listen_fds);
+       return ret;
 }
 
 static void init_server_command_task(struct server_command_task *sct,
 }
 
 static void init_server_command_task(struct server_command_task *sct,
@@ -431,14 +436,14 @@ static void init_server_command_task(struct server_command_task *sct,
        sct->argv = argv;
        if (!OPT_GIVEN(LISTEN_ADDRESS)) {
                sct->num_listen_fds = 1;
        sct->argv = argv;
        if (!OPT_GIVEN(LISTEN_ADDRESS)) {
                sct->num_listen_fds = 1;
-               sct->listen_fds = para_malloc(sizeof(int));
+               sct->listen_fds = alloc(sizeof(int));
                ret = para_listen_simple(IPPROTO_TCP, port);
                if (ret < 0)
                        goto err;
                sct->listen_fds[0] = ret;
        } else {
                sct->num_listen_fds = OPT_GIVEN(LISTEN_ADDRESS);
                ret = para_listen_simple(IPPROTO_TCP, port);
                if (ret < 0)
                        goto err;
                sct->listen_fds[0] = ret;
        } else {
                sct->num_listen_fds = OPT_GIVEN(LISTEN_ADDRESS);
-               sct->listen_fds = para_malloc(sct->num_listen_fds * sizeof(int));
+               sct->listen_fds = alloc(sct->num_listen_fds * sizeof(int));
                for (n = 0; n < OPT_GIVEN(LISTEN_ADDRESS); n++) {
                        const char *arg;
                        arg = lls_string_val(n, OPT_RESULT(LISTEN_ADDRESS));
                for (n = 0; n < OPT_GIVEN(LISTEN_ADDRESS); n++) {
                        const char *arg;
                        arg = lls_string_val(n, OPT_RESULT(LISTEN_ADDRESS));
@@ -458,8 +463,8 @@ static void init_server_command_task(struct server_command_task *sct,
 
        sct->task = task_register(&(struct task_info) {
                .name = "server command",
 
        sct->task = task_register(&(struct task_info) {
                .name = "server command",
-               .pre_select = command_pre_select,
-               .post_select = command_post_select,
+               .pre_monitor = command_pre_monitor,
+               .post_monitor = command_post_monitor,
                .context = sct,
        }, &sched);
        /*
                .context = sct,
        }, &sched);
        /*
@@ -542,7 +547,7 @@ static void server_init(int argc, char **argv, struct server_command_task *sct)
        if (ret < 0)
                goto fail;
        server_lpr = cmdline_lpr;
        if (ret < 0)
                goto fail;
        server_lpr = cmdline_lpr;
-       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("server", OPT_GIVEN(VERSION));
        daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
                OPT_STRING_VAL(GROUP));
        version_handle_flag("server", OPT_GIVEN(VERSION));
@@ -616,14 +621,14 @@ out:
        killpg(0, SIGUSR1);
 }
 
        killpg(0, SIGUSR1);
 }
 
-static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
-               struct timeval *timeout_tv)
+static int server_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
        int ret;
 
 {
        int ret;
 
+       daemon_set_loglevel(mmd->loglevel);
        status_refresh();
        mutex_unlock(mmd_mutex);
        status_refresh();
        mutex_unlock(mmd_mutex);
-       ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
+       ret = xpoll(fds, nfds, timeout);
        mutex_lock(mmd_mutex);
        return ret;
 }
        mutex_lock(mmd_mutex);
        return ret;
 }
@@ -657,15 +662,15 @@ int main(int argc, char *argv[])
        struct server_command_task server_command_task_struct,
                *sct = &server_command_task_struct;
 
        struct server_command_task server_command_task_struct,
                *sct = &server_command_task_struct;
 
-       sched.default_timeout.tv_sec = 1;
-       sched.select_function = server_select;
+       sched.default_timeout = 1000;
+       sched.poll_function = server_poll;
 
        server_init(argc, argv, sct);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        /*
 
        server_init(argc, argv, sct);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        /*
-        * We hold the mmd lock: it was re-acquired in server_select()
-        * after the select call.
+        * We hold the mmd lock: it was re-acquired in server_poll()
+        * after the poll(2) call.
         */
        mutex_unlock(mmd_mutex);
        sched_shutdown(&sched);
         */
        mutex_unlock(mmd_mutex);
        sched_shutdown(&sched);
@@ -678,12 +683,13 @@ int main(int argc, char *argv[])
                deplete_close_on_fork_list();
                if (ret < 0)
                        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                deplete_close_on_fork_list();
                if (ret < 0)
                        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               vss_shutdown();
        } else {
        } else {
+               vss_shutdown();
                alarm(ALARM_TIMEOUT);
                close_listed_fds();
                ret = handle_connect(sct->child_fd);
        }
                alarm(ALARM_TIMEOUT);
                close_listed_fds();
                ret = handle_connect(sct->child_fd);
        }
-       vss_shutdown();
        shm_detach(mmd);
        user_list_deplete();
        free_lpr();
        shm_detach(mmd);
        user_list_deplete();
        free_lpr();
index da75d86bdf191b130d02da12f49172ac5e0482d7..10bb6172860ddb467c3f9fd26ef6ce9e3cb8aab1 100644 (file)
--- a/server.h
+++ b/server.h
@@ -73,6 +73,8 @@ struct misc_meta_data {
        char afs_mode_string[MAXLINE];
        /** Used by the sender command. */
        struct sender_command_data sender_cmd_data;
        char afs_mode_string[MAXLINE];
        /** Used by the sender command. */
        struct sender_command_data sender_cmd_data;
+       /** Set by the ll command. */
+       int loglevel;
        /** Describes the current audio file. */
        struct audio_file_data afd;
 };
        /** Describes the current audio file. */
        struct audio_file_data afd;
 };
@@ -80,15 +82,6 @@ struct misc_meta_data {
 extern pid_t afs_pid;
 extern struct lls_parse_result *server_lpr;
 
 extern pid_t afs_pid;
 extern struct lls_parse_result *server_lpr;
 
-/**
- * Get a reference to the supercommand of para_server.
- *
- * This is needed for parsing the command line and for the ENUM_STRING_VAL()
- * macro below. The latter macro is used in command.c, so CMD_PTR() can not
- * be made local to server.c.
- */
-#define CMD_PTR (lls_cmd(0, server_suite))
-
 /** Get the parse result of an option to para_server. */
 #define OPT_RESULT(_name) (lls_opt_result( \
                LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
 /** Get the parse result of an option to para_server. */
 #define OPT_RESULT(_name) (lls_opt_result( \
                LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
@@ -105,10 +98,6 @@ extern struct lls_parse_result *server_lpr;
 /** The (first) argument to a server option of type int32. */
 #define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
 
 /** The (first) argument to a server option of type int32. */
 #define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
 
-/** Get the string which corresponds to an enum constant. */
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
-       lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
-
 int handle_connect(int fd);
 void parse_config_or_die(bool reload);
 char *server_get_tasks(void);
 int handle_connect(int fd);
 void parse_config_or_die(bool reload);
 char *server_get_tasks(void);
index ed7867a1c37027d7a49d1047bd349c5976d0e8d7..d4876234a192a956f112c48f871ed1fb0d5321b7 100644 (file)
@@ -37,7 +37,7 @@ struct sb_context {
 struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
                void *trafo_context)
 {
 struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
                void *trafo_context)
 {
-       struct sb_context *c = para_calloc(sizeof(*c));
+       struct sb_context *c = zalloc(sizeof(*c));
 
        c->max_size = max_size;
        c->trafo = t;
 
        c->max_size = max_size;
        c->trafo = t;
@@ -62,7 +62,7 @@ struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
 struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
                sb_transformation t, void *trafo_context)
 {
 struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
                sb_transformation t, void *trafo_context)
 {
-       struct sb_context *c = para_calloc(sizeof(*c));
+       struct sb_context *c = zalloc(sizeof(*c));
        struct iovec src, dst, *srcp, *dstp;
 
        assert(sbb);
        struct iovec src, dst, *srcp, *dstp;
 
        assert(sbb);
@@ -266,7 +266,7 @@ int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
         */
        if (sbb->iov.iov_len == (size_t)-1)
                return -E_SB_PACKET_SIZE;
         */
        if (sbb->iov.iov_len == (size_t)-1)
                return -E_SB_PACKET_SIZE;
-       sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
+       sbb->iov.iov_base = alloc(sbb->iov.iov_len + 1);
        return 0; /* ready to read body */
 success:
        *result = c->sbb;
        return 0; /* ready to read body */
 success:
        *result = c->sbb;
index 32d6ab6624e5f493ac393b630a603c0968f11497..d9a6aa37057415117c32a6a18c33d81977499392 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -27,7 +27,7 @@ static int signal_pipe[2];
  * signal arrives, the signal handler writes the number of the signal received
  * to one end of the signal pipe. The application can test for pending signals
  * by checking if the file descriptor of the other end of the signal pipe is
  * signal arrives, the signal handler writes the number of the signal received
  * to one end of the signal pipe. The application can test for pending signals
  * by checking if the file descriptor of the other end of the signal pipe is
- * ready for reading, see select(2).
+ * ready for reading.
  *
  * \return This function either succeeds or calls exit(3) to terminate the
  * current process. On success, a signal task structure is returned.
  *
  * \return This function either succeeds or calls exit(3) to terminate the
  * current process. On success, a signal task structure is returned.
@@ -48,7 +48,7 @@ struct signal_task *signal_init_or_die(void)
        ret = mark_fd_nonblocking(signal_pipe[1]);
        if (ret < 0)
                goto err_out;
        ret = mark_fd_nonblocking(signal_pipe[1]);
        if (ret < 0)
                goto err_out;
-       st = para_calloc(sizeof(*st));
+       st = zalloc(sizeof(*st));
        st->fd = signal_pipe[0];
        return st;
 err_out:
        st->fd = signal_pipe[0];
        return st;
 err_out:
@@ -72,10 +72,13 @@ static void generic_signal_handler(int s)
                errno = save_errno;
                return;
        }
                errno = save_errno;
                return;
        }
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", strerror(errno));
-       else
-               PARA_EMERG_LOG("short write to signal pipe\n");
+       /*
+        * This is a fatal error which should never happen. We must not call
+        * PARA_XXX_LOG() here because this might acquire the log mutex which
+        * is already taken by the main program if the interrupt occurs while a
+        * log message is being printed. The mutex will not be released as long
+        * as this signal handler is running, so a deadlock ensues.
+        */
        exit(EXIT_FAILURE);
 }
 
        exit(EXIT_FAILURE);
 }
 
@@ -202,16 +205,14 @@ void para_unblock_signal(int sig)
 /**
  * Return the number of the next pending signal.
  *
 /**
  * Return the number of the next pending signal.
  *
- * \param rfds The fd_set containing the signal pipe.
- *
  * \return On success, the number of the received signal is returned. If there
  * is no signal currently pending, the function returns zero. On read errors
  * from the signal pipe, the process is terminated.
  */
  * \return On success, the number of the received signal is returned. If there
  * is no signal currently pending, the function returns zero. On read errors
  * from the signal pipe, the process is terminated.
  */
-int para_next_signal(fd_set *rfds)
+int para_next_signal(void)
 {
        size_t n;
 {
        size_t n;
-       int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
+       int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), &n);
 
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
 
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
index e5532ded5a9378619f7951708b49c5b38bf52d18..d9e98e78a635930577038d6090c9d5260772ddec 100644 (file)
--- a/signal.h
+++ b/signal.h
@@ -13,32 +13,31 @@ struct signal_task {
 };
 
 /**
 };
 
 /**
- * A generic pre-select method for signal tasks.
+ * Monitor the signal fd for reading.
  *
  *
- * \param s Passed to para_fd_set().
+ * \param s The scheduler instance.
  * \param context Signal task pointer.
  *
  * This convenience helper is called from several programs which need to handle
  * \param context Signal task pointer.
  *
  * This convenience helper is called from several programs which need to handle
- * signals, including para_server and para_audiod. These programs define a
- * signal task structure and set its ->pre_select method to this function which
- * adds the file descriptor of the signal task to the set of descriptors to be
- * watched in the next select() call.
+ * signals, including para_server and para_audiod. These programs set up a
+ * signal pipe and a signal task structure, and use this function to tell the
+ * scheduler to monitor the read end of the pipe.
  *
  * Although the second parameter must be in fact a pointer to a signal_task
  *
  * Although the second parameter must be in fact a pointer to a signal_task
- * structure, the parameter is specified as void * here to match the
- * ->pre_select method of struct task.
+ * structure, the parameter is specified as void * here to match the signature
+ * declared in struct \ref task_info.
  */
  */
-_static_inline_ void signal_pre_select(struct sched *s, void *context)
+_static_inline_ void signal_pre_monitor(struct sched *s, void *context)
 {
        struct signal_task *st = context;
 {
        struct signal_task *st = context;
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(st->fd, s);
 }
 
 struct signal_task *signal_init_or_die(void);
 void para_sigaction(int sig, void (*handler)(int));
 void para_install_sighandler(int);
 int para_reap_child(pid_t *pid);
 }
 
 struct signal_task *signal_init_or_die(void);
 void para_sigaction(int sig, void (*handler)(int));
 void para_install_sighandler(int);
 int para_reap_child(pid_t *pid);
-int para_next_signal(fd_set *rfds);
+int para_next_signal(void);
 void signal_shutdown(struct signal_task *st);
 void para_block_signal(int sig);
 void para_unblock_signal(int sig);
 void signal_shutdown(struct signal_task *st);
 void para_block_signal(int sig);
 void para_unblock_signal(int sig);
index caeacb1921341b04b26f117d5de6607a80ab95e9..cd3b7cde907388465f04ead39af10bd125d154ca 100644 (file)
--- a/spx_afh.c
+++ b/spx_afh.c
@@ -195,7 +195,7 @@ static size_t spx_make_meta_packet(struct taginfo *tags, char **result)
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
        }
        PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
        /* terminating zero byte for the last sprintf() */
-       buf = p = para_malloc(sz + 1);
+       buf = p = alloc(sz + 1);
        write_u32(p, comment_sz);
        p += 4;
        strcpy(p, tags->comment);
        write_u32(p, comment_sz);
        p += 4;
        strcpy(p, tags->comment);
index 7be817ddaa49bf97e5d4025c43be63b42da9cd4c..08eac02a557b2d503646666edd7ffb35e1c5c5f7 100644 (file)
@@ -81,7 +81,7 @@ struct private_spxdec_data {
 
 static void spxdec_open(struct filter_node *fn)
 {
 
 static void spxdec_open(struct filter_node *fn)
 {
-       struct private_spxdec_data *psd = para_calloc(sizeof(*psd));
+       struct private_spxdec_data *psd = zalloc(sizeof(*psd));
 
        fn->private_data = psd;
        fn->min_iqs = 200;
 
        fn->private_data = psd;
        fn->min_iqs = 200;
@@ -171,7 +171,7 @@ static int speexdec_write_frames(int packet_no,
                if (new_frame_size <= 0)
                        continue;
                samples = new_frame_size * psd->shi.channels;
                if (new_frame_size <= 0)
                        continue;
                samples = new_frame_size * psd->shi.channels;
-               btr_output = para_malloc(2 * samples);
+               btr_output = arr_alloc(samples, 2);
                for (i = 0; i < samples; i++)
                        btr_output[i] = read_u16(output + i + skip_idx);
                btr_add_output((char *)btr_output, samples * 2, btrn);
                for (i = 0; i < samples; i++)
                        btr_output[i] = read_u16(output + i + skip_idx);
                btr_add_output((char *)btr_output, samples * 2, btrn);
@@ -246,7 +246,7 @@ static int compute_skip_samples(ogg_page *og, struct private_spxdec_data *psd)
        return ret;
 }
 
        return ret;
 }
 
-static int speexdec_post_select(__a_unused struct sched *s, void *context)
+static int speexdec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct private_spxdec_data *psd = fn->private_data;
 {
        struct filter_node *fn = context;
        struct private_spxdec_data *psd = fn->private_data;
@@ -305,7 +305,7 @@ fail:
 const struct filter lsg_filter_cmd_com_spxdec_user_data = {
        .open = spxdec_open,
        .close = speexdec_close,
 const struct filter lsg_filter_cmd_com_spxdec_user_data = {
        .open = spxdec_open,
        .close = speexdec_close,
-       .pre_select = generic_filter_pre_select,
-       .post_select = speexdec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = speexdec_post_monitor,
        .execute = speexdec_execute,
 };
        .execute = speexdec_execute,
 };
diff --git a/stdin.c b/stdin.c
index 9408235a045a6767e2acf443208d95196990d379..d025b949e8e8d2566abde3f2f214db7998ba44ff 100644 (file)
--- a/stdin.c
+++ b/stdin.c
 #include "string.h"
 
 /*
 #include "string.h"
 
 /*
- * If there is space left in the buffer of the stdin task add STDIN_FILENO to
- * the read fd set of s.
+ * If there is space left in the buffer of the stdin task, ask the scheduler to
+ * monitor STDIN_FILENO.
  */
  */
-static void stdin_pre_select(struct sched *s, void *context)
+static void stdin_pre_monitor(struct sched *s, void *context)
 {
        struct stdin_task *sit = context;
        int ret;
 {
        struct stdin_task *sit = context;
        int ret;
@@ -28,16 +28,15 @@ static void stdin_pre_select(struct sched *s, void *context)
        if (ret <= 0)
                return;
        if (btr_pool_unused(sit->btrp) > 0)
        if (ret <= 0)
                return;
        if (btr_pool_unused(sit->btrp) > 0)
-               return para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
+               return sched_monitor_readfd(STDIN_FILENO, s);
        sched_request_timeout_ms(100, s);
 }
 
 /*
        sched_request_timeout_ms(100, s);
 }
 
 /*
- * This function checks if STDIN_FILENO was included by in the read fd set of s
- * during the previous pre_select call. If so, and if STDIN_FILENO is readable,
- * data is read from stdin and fed into the buffer tree.
+ * Feed data from stdin into the buffer tree if STDIN_FILENO is ready for
+ * reading.
  */
  */
-static int stdin_post_select(struct sched *s, void *context)
+static int stdin_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct stdin_task *sit = context;
        ssize_t ret;
 {
        struct stdin_task *sit = context;
        ssize_t ret;
@@ -64,7 +63,7 @@ static int stdin_post_select(struct sched *s, void *context)
         * reference can not be freed, we're stuck.
         */
        sz = PARA_MIN(sz, btr_pool_size(sit->btrp) / 2);
         * reference can not be freed, we're stuck.
         */
        sz = PARA_MIN(sz, btr_pool_size(sit->btrp) / 2);
-       ret = read_nonblock(STDIN_FILENO, buf, sz, &s->rfds, &n);
+       ret = read_nonblock(STDIN_FILENO, buf, sz, &n);
        if (n > 0)
                btr_add_output_pool(sit->btrp, n, sit->btrn);
        if (ret >= 0)
        if (n > 0)
                btr_add_output_pool(sit->btrp, n, sit->btrn);
        if (ret >= 0)
@@ -91,8 +90,8 @@ void stdin_task_register(struct stdin_task *sit, struct sched *s)
        int ret;
        struct task_info ti = {
                .name = "stdin",
        int ret;
        struct task_info ti = {
                .name = "stdin",
-               .pre_select = stdin_pre_select,
-               .post_select = stdin_post_select,
+               .pre_monitor = stdin_pre_monitor,
+               .post_monitor = stdin_post_monitor,
                .context = sit,
        };
 
                .context = sit,
        };
 
index 1f7791094a33a961c193c273a4c09e9b16f2be07..ba5f19670fce6c3c04d256a2056477064f5df7d3 100644 (file)
--- a/stdout.c
+++ b/stdout.c
 #include "stdout.h"
 #include "buffer_tree.h"
 
 #include "stdout.h"
 #include "buffer_tree.h"
 
-/* Add STDOUT_FILENO to the write fd set if there is input data available. */
-static void stdout_pre_select(struct sched *s, void *context)
+/* Monitor STDOUT_FILENO if there is input data available. */
+static void stdout_pre_monitor(struct sched *s, void *context)
 {
        struct stdout_task *sot = context;
        int ret;
 
        ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
        if (ret > 0)
 {
        struct stdout_task *sot = context;
        int ret;
 
        ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
        if (ret > 0)
-               para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(STDOUT_FILENO, s);
        else if (ret < 0)
                sched_min_delay(s);
 }
 
 /*
        else if (ret < 0)
                sched_min_delay(s);
 }
 
 /*
- * This function writes input data from the buffer tree to stdout if
- * STDOUT_FILENO is writable.
+ * If input from the buffer tree is available and STDOUT_FILENO is ready, write
+ * as much as possible.
  */
  */
-static int stdout_post_select(struct sched *s, void *context)
+static int stdout_post_monitor(struct sched *s, void *context)
 {
        struct stdout_task *sot = context;
        struct btr_node *btrn = sot->btrn;
 {
        struct stdout_task *sot = context;
        struct btr_node *btrn = sot->btrn;
@@ -40,7 +40,7 @@ static int stdout_post_select(struct sched *s, void *context)
                goto out;
        if (ret == 0)
                return 0;
                goto out;
        if (ret == 0)
                return 0;
-       if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
+       if (!sched_write_ok(STDOUT_FILENO, s))
                return 0;
 
        if (sot->must_set_nonblock_flag) {
                return 0;
 
        if (sot->must_set_nonblock_flag) {
@@ -79,8 +79,8 @@ void stdout_task_register(struct stdout_task *sot, struct sched *s)
 {
        int ret;
        struct task_info ti = {
 {
        int ret;
        struct task_info ti = {
-               .pre_select = stdout_pre_select,
-               .post_select = stdout_post_select,
+               .pre_monitor = stdout_pre_monitor,
+               .post_monitor = stdout_post_monitor,
                .context = sot,
                .name = "stdout",
        };
                .context = sot,
                .name = "stdout",
        };
index 198e9f1d286ee2228638a2a7994fe9e65f41ccac..d8bd027b7010a149be59b8883b3e5ee685fb3c01 100644 (file)
--- a/string.c
+++ b/string.c
 #include "error.h"
 
 /**
 #include "error.h"
 
 /**
- * Paraslash's version of realloc().
+ * Reallocate an array, abort on failure or bugs.
  *
  *
- * \param p Pointer to the memory block, may be \p NULL.
- * \param size The desired new size.
+ * \param ptr Pointer to the memory block, may be NULL.
+ * \param nmemb Number of elements.
+ * \param size The size of one element in bytes.
  *
  *
- * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
- * i.e. there is no need to check the return value in the caller.
+ * A wrapper for realloc(3) which aborts on invalid arguments or integer
+ * overflow. The wrapper also terminates the current process on allocation
+ * errors, so the caller does not need to check for failure.
  *
  * \return A pointer to newly allocated memory which is suitably aligned for
  *
  * \return A pointer to newly allocated memory which is suitably aligned for
- * any kind of variable and may be different from \a p.
+ * any kind of variable and may be different from ptr.
  *
  * \sa realloc(3).
  */
  *
  * \sa realloc(3).
  */
-__must_check void *para_realloc(void *p, size_t size)
+__must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size)
 {
 {
+       size_t pr;
+
+       assert(size > 0);
+       assert(nmemb > 0);
+       assert(!__builtin_mul_overflow(nmemb, size, &pr));
+       assert(pr != 0);
+       ptr = realloc(ptr, pr);
+       assert(ptr);
+       return ptr;
+}
+
+/**
+ * Allocate an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * Like \ref arr_realloc(), this aborts on invalid arguments, integer overflow
+ * and allocation errors.
+ *
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable.
+ *
+ * \sa See \ref arr_realloc().
+ */
+__must_check __malloc void *arr_alloc(size_t nmemb, size_t size)
+{
+       return arr_realloc(NULL, nmemb, size);
+}
+
+/**
+ * Allocate and initialize an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * This calls \ref arr_alloc() and zeroes-out the array.
+ *
+ * \return See \ref arr_alloc().
+ */
+__must_check __malloc void *arr_zalloc(size_t nmemb, size_t size)
+{
+       void *ptr = arr_alloc(nmemb, size);
+
        /*
        /*
-        * No need to check for NULL pointers: If p is NULL, the call
-        * to realloc is equivalent to malloc(size)
+        * This multiplication can not overflow because the above call to \ref
+        * arr_alloc() aborts on overflow.
         */
         */
-       assert(size);
-       if (!(p = realloc(p, size))) {
-               PARA_EMERG_LOG("realloc failed (size = %zu), aborting\n",
-                       size);
-               exit(EXIT_FAILURE);
-       }
-       return p;
+       memset(ptr, 0, nmemb * size);
+       return ptr;
 }
 
 /**
 }
 
 /**
- * Paraslash's version of malloc().
+ * Allocate and initialize memory.
  *
  * \param size The desired new size.
  *
  *
  * \param size The desired new size.
  *
- * A wrapper for malloc(3) which exits on errors.
- *
- * \return A pointer to the allocated memory, which is suitably aligned for any
- * kind of variable.
+ * \return A pointer to the allocated and zeroed-out memory, which is suitably
+ * aligned for any kind of variable.
  *
  *
- * \sa malloc(3).
+ * \sa \ref alloc(), calloc(3).
  */
  */
-__must_check __malloc void *para_malloc(size_t size)
+__must_check void *zalloc(size_t size)
 {
 {
-       void *p;
-
-       assert(size);
-       p = malloc(size);
-       if (!p) {
-               PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n",
-                       size);
-               exit(EXIT_FAILURE);
-       }
-       return p;
+       return arr_zalloc(1, size);
 }
 
 /**
 }
 
 /**
- * Paraslash's version of calloc().
+ * Paraslash's version of realloc().
  *
  *
+ * \param p Pointer to the memory block, may be \p NULL.
  * \param size The desired new size.
  *
  * \param size The desired new size.
  *
- * A wrapper for calloc(3) which exits on errors.
+ * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
+ * i.e. there is no need to check the return value in the caller.
  *
  *
- * \return A pointer to the allocated and zeroed-out memory, which is suitably
- * aligned for any kind of variable.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from \a p.
  *
  *
- * \sa calloc(3)
+ * \sa realloc(3).
  */
  */
-__must_check __malloc void *para_calloc(size_t size)
+__must_check void *para_realloc(void *p, size_t size)
 {
 {
-       void *ret = para_malloc(size);
+       return arr_realloc(p, 1, size);
+}
 
 
-       memset(ret, 0, size);
-       return ret;
+/**
+ * Paraslash's version of malloc().
+ *
+ * \param size The desired new size.
+ *
+ * A wrapper for malloc(3) which exits on errors.
+ *
+ * \return A pointer to the allocated memory, which is suitably aligned for any
+ * kind of variable.
+ *
+ * \sa malloc(3).
+ */
+__must_check __malloc void *alloc(size_t size)
+{
+       return arr_alloc(1, size);
 }
 
 /**
 }
 
 /**
@@ -94,22 +140,23 @@ __must_check __malloc void *para_calloc(size_t size)
  *
  * \param s The string to be duplicated.
  *
  *
  * \param s The string to be duplicated.
  *
- * A wrapper for strdup(3). It calls \p exit(EXIT_FAILURE) on errors, i.e.
- * there is no need to check the return value in the caller.
+ * A strdup(3)-like function which aborts if insufficient memory was available
+ * to allocate the duplicated string, absolving the caller from the
+ * responsibility to check for failure.
  *
  *
- * \return A pointer to the duplicated string. If \a s was the \p NULL pointer,
- * an pointer to an empty string is returned.
+ * \return A pointer to the duplicated string. Unlike strdup(3), the caller may
+ * pass NULL, in which case the function returns a pointer to an empty string.
+ * Regardless of whether or not NULL was passed, the returned string is
+ * allocated on the heap and has to be freed by the caller.
  *
  *
- * \sa strdup(3)
+ * \sa strdup(3).
  */
 __must_check __malloc char *para_strdup(const char *s)
 {
  */
 __must_check __malloc char *para_strdup(const char *s)
 {
-       char *ret;
+       char *dupped_string = strdup(s? s: "");
 
 
-       if ((ret = strdup(s? s: "")))
-               return ret;
-       PARA_EMERG_LOG("strdup failed, aborting\n");
-       exit(EXIT_FAILURE);
+       assert(dupped_string);
+       return dupped_string;
 }
 
 /**
 }
 
 /**
@@ -137,7 +184,7 @@ __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
        size_t size = 150;
        va_list aq;
 
        size_t size = 150;
        va_list aq;
 
-       *result = para_malloc(size + 1);
+       *result = alloc(size + 1);
        va_copy(aq, ap);
        ret = vsnprintf(*result, size, fmt, aq);
        va_end(aq);
        va_copy(aq, ap);
        ret = vsnprintf(*result, size, fmt, aq);
        va_end(aq);
@@ -245,56 +292,6 @@ __must_check __malloc char *para_strcat(char *a, const char *b)
        return tmp;
 }
 
        return tmp;
 }
 
-/**
- * Paraslash's version of dirname().
- *
- * \param name Pointer to the full path.
- *
- * Compute the directory component of \p name.
- *
- * \return If \a name is \p NULL or the empty string, return \p NULL.
- * Otherwise, Make a copy of \a name and return its directory component. Caller
- * is responsible to free the result.
- */
-__must_check __malloc char *para_dirname(const char *name)
-{
-       char *p, *ret;
-
-       if (!name || !*name)
-               return NULL;
-       ret = para_strdup(name);
-       p = strrchr(ret, '/');
-       if (!p)
-               *ret = '\0';
-       else
-               *p = '\0';
-       return ret;
-}
-
-/**
- * Paraslash's version of basename().
- *
- * \param name Pointer to the full path.
- *
- * Compute the filename component of \a name.
- *
- * \return \p NULL if (a) \a name is the empty string or \p NULL, or (b) name
- * ends with a slash.  Otherwise, a pointer within \a name is returned.  Caller
- * must not free the result.
- */
-__must_check char *para_basename(const char *name)
-{
-       char *ret;
-
-       if (!name || !*name)
-               return NULL;
-       ret = strrchr(name, '/');
-       if (!ret)
-               return (char *)name;
-       ret++;
-       return ret;
-}
-
 /**
  * Get the logname of the current user.
  *
 /**
  * Get the logname of the current user.
  *
@@ -311,15 +308,32 @@ __must_check __malloc char *para_logname(void)
 }
 
 /**
 }
 
 /**
- * Get the home directory of the current user.
+ * Get the home directory of the calling user.
  *
  * \return A dynamically allocated string that must be freed by the caller. If
  *
  * \return A dynamically allocated string that must be freed by the caller. If
- * the home directory could not be found, this function returns "/tmp".
+ * no entry is found which matches the UID of the calling process, or any other
+ * error occurs, the function prints an error message and aborts.
+ *
+ * \sa getpwuid(3), getuid(2).
  */
 __must_check __malloc char *para_homedir(void)
 {
  */
 __must_check __malloc char *para_homedir(void)
 {
-       struct passwd *pw = getpwuid(getuid());
-       return para_strdup(pw? pw->pw_dir : "/tmp");
+       struct passwd *pw;
+
+       /*
+        * To distinguish between the error case and the "not found" case we
+        * have to check errno after getpwuid(3). The manual page recommends to
+        * set it to zero before the call.
+        */
+       errno = 0;
+       pw = getpwuid(getuid());
+       if (pw)
+               return para_strdup(pw->pw_dir);
+       if (errno != 0)
+               PARA_EMERG_LOG("getpwuid error: %s\n", strerror(errno));
+       else
+               PARA_EMERG_LOG("no pw entry for uid %u\n", (unsigned)getuid());
+       exit(EXIT_FAILURE);
 }
 
 /**
 }
 
 /**
@@ -389,7 +403,7 @@ int for_each_line(unsigned flags, char *buf, size_t size,
                if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
                        if (flags & FELF_READ_ONLY) {
                                size_t s = end - start;
                if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
                        if (flags & FELF_READ_ONLY) {
                                size_t s = end - start;
-                               char *b = para_malloc(s + 1);
+                               char *b = alloc(s + 1);
                                memcpy(b, start, s);
                                b[s] = '\0';
                                ret = line_handler(b, private_data);
                                memcpy(b, start, s);
                                b[s] = '\0';
                                ret = line_handler(b, private_data);
@@ -493,7 +507,7 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...)
        int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0;
 
        if (!b->buf) {
        int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0;
 
        if (!b->buf) {
-               b->buf = para_malloc(128);
+               b->buf = alloc(128);
                b->size = 128;
                b->offset = 0;
        }
                b->size = 128;
                b->offset = 0;
        }
@@ -602,37 +616,6 @@ int para_atoi32(const char *str, int32_t *value)
        return 1;
 }
 
        return 1;
 }
 
-static inline int loglevel_equal(const char *arg, const char * const ll)
-{
-       return !strncasecmp(arg, ll, strlen(ll));
-}
-
-/**
- * Compute the loglevel number from its name.
- *
- * \param txt The name of the loglevel (debug, info, ...).
- *
- * \return The numeric representation of the loglevel name.
- */
-int get_loglevel_by_name(const char *txt)
-{
-       if (loglevel_equal(txt, "debug"))
-               return LL_DEBUG;
-       if (loglevel_equal(txt, "info"))
-               return LL_INFO;
-       if (loglevel_equal(txt, "notice"))
-               return LL_NOTICE;
-       if (loglevel_equal(txt, "warning"))
-               return LL_WARNING;
-       if (loglevel_equal(txt, "error"))
-               return LL_ERROR;
-       if (loglevel_equal(txt, "crit"))
-               return LL_CRIT;
-       if (loglevel_equal(txt, "emerg"))
-               return LL_EMERG;
-       return -E_BAD_LL;
-}
-
 static int get_next_word(const char *buf, const char *delim, char **word)
 {
        enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
 static int get_next_word(const char *buf, const char *delim, char **word)
 {
        enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
@@ -641,7 +624,7 @@ static int get_next_word(const char *buf, const char *delim, char **word)
        char *out;
        int ret, state = 0;
 
        char *out;
        int ret, state = 0;
 
-       out = para_malloc(strlen(buf) + 1);
+       out = alloc(strlen(buf) + 1);
        *out = '\0';
        *word = out;
        for (in = buf; *in; in++) {
        *out = '\0';
        *word = out;
        for (in = buf; *in; in++) {
@@ -773,19 +756,17 @@ void free_argv(char **argv)
 static int create_argv_offset(int offset, const char *buf, const char *delim,
                char ***result)
 {
 static int create_argv_offset(int offset, const char *buf, const char *delim,
                char ***result)
 {
-       char *word, **argv = para_malloc((offset + 1) * sizeof(char *));
+       char *word, **argv = arr_zalloc(offset + 1, sizeof(char *));
        const char *p;
        int i, ret;
 
        const char *p;
        int i, ret;
 
-       for (i = 0; i < offset; i++)
-               argv[i] = NULL;
-       for (p = buf; p && *p; p += ret, i++) {
+       for (p = buf, i = offset; p && *p; p += ret, i++) {
                ret = get_next_word(p, delim, &word);
                if (ret < 0)
                        goto err;
                if (!ret)
                        break;
                ret = get_next_word(p, delim, &word);
                if (ret < 0)
                        goto err;
                if (!ret)
                        break;
-               argv = para_realloc(argv, (i + 2) * sizeof(char*));
+               argv = arr_realloc(argv, i + 2, sizeof(char*));
                argv[i] = word;
        }
        argv[i] = NULL;
                argv[i] = word;
        }
        argv[i] = NULL;
@@ -840,27 +821,6 @@ int create_shifted_argv(const char *buf, const char *delim, char ***result)
        return create_argv_offset(1, buf, delim, result);
 }
 
        return create_argv_offset(1, buf, delim, result);
 }
 
-/**
- * Find out if the given string is contained in the arg vector.
- *
- * \param arg The string to look for.
- * \param argv The array to search.
- *
- * \return The first index whose value equals \a arg, or \p -E_ARG_NOT_FOUND if
- * arg was not found in \a argv.
- */
-int find_arg(const char *arg, char **argv)
-{
-       int i;
-
-       if (!argv)
-               return -E_ARG_NOT_FOUND;
-       for (i = 0; argv[i]; i++)
-               if (strcmp(arg, argv[i]) == 0)
-                       return i;
-       return -E_ARG_NOT_FOUND;
-}
-
 /**
  * Compile a regular expression.
  *
 /**
  * Compile a regular expression.
  *
@@ -881,7 +841,7 @@ int para_regcomp(regex_t *preg, const char *regex, int cflags)
        if (ret == 0)
                return 1;
        size = regerror(ret, preg, NULL, 0);
        if (ret == 0)
                return 1;
        size = regerror(ret, preg, NULL, 0);
-       buf = para_malloc(size);
+       buf = alloc(size);
        regerror(ret, preg, buf, size);
        PARA_ERROR_LOG("%s\n", buf);
        free(buf);
        regerror(ret, preg, buf, size);
        PARA_ERROR_LOG("%s\n", buf);
        free(buf);
@@ -907,7 +867,7 @@ char *safe_strdup(const char *src, size_t len)
        char *p;
 
        assert(len < (size_t)-1);
        char *p;
 
        assert(len < (size_t)-1);
-       p = para_malloc(len + 1);
+       p = alloc(len + 1);
        if (len > 0)
                memcpy(p, src, len);
        p[len] = '\0';
        if (len > 0)
                memcpy(p, src, len);
        p[len] = '\0';
@@ -1061,7 +1021,7 @@ __must_check int strwidth(const char *s, size_t *result)
                return -ERRNO_TO_PARA_ERROR(errno);
        if (num_wchars == 0)
                return 0;
                return -ERRNO_TO_PARA_ERROR(errno);
        if (num_wchars == 0)
                return 0;
-       dest = para_malloc((num_wchars + 1) * sizeof(*dest));
+       dest = arr_alloc(num_wchars + 1, sizeof(*dest));
        src = s;
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
        src = s;
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
@@ -1116,7 +1076,7 @@ __must_check int sanitize_str(const char *src, size_t max_width,
        num_wchars = mbsrtowcs(NULL, &src, 0, &state);
        if (num_wchars == (size_t)-1)
                return -ERRNO_TO_PARA_ERROR(errno);
        num_wchars = mbsrtowcs(NULL, &src, 0, &state);
        if (num_wchars == (size_t)-1)
                return -ERRNO_TO_PARA_ERROR(errno);
-       wcs = para_malloc((num_wchars + 1) * sizeof(*wcs));
+       wcs = arr_alloc(num_wchars + 1, sizeof(*wcs));
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
        assert(num_wchars != (size_t)-1);
        memset(&state, 0, sizeof(state));
        num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
        assert(num_wchars != (size_t)-1);
@@ -1127,7 +1087,7 @@ __must_check int sanitize_str(const char *src, size_t max_width,
        }
        wcs[n] = L'\0';
        n = wcstombs(NULL, wcs, 0) + 1;
        }
        wcs[n] = L'\0';
        n = wcstombs(NULL, wcs, 0) + 1;
-       *result = para_malloc(n);
+       *result = alloc(n);
        num_wchars = wcstombs(*result, wcs, n);
        assert(num_wchars != (size_t)-1);
        free(wcs);
        num_wchars = wcstombs(*result, wcs, n);
        assert(num_wchars != (size_t)-1);
        free(wcs);
index 10379a0e83098f392e8653467b826925376eda15..d773600fbf0a0168584235a5098beabb110a3416 100644 (file)
--- a/string.h
+++ b/string.h
@@ -67,28 +67,27 @@ int for_each_line(unsigned flags, char *buf, size_t size,
 } \
 )
 
 } \
 )
 
+__must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size);
 __must_check void *para_realloc(void *p, size_t size);
 __must_check void *para_realloc(void *p, size_t size);
-__must_check __malloc void *para_malloc(size_t size);
-__must_check __malloc void *para_calloc(size_t size);
+__must_check __malloc void *alloc(size_t size);
+__must_check __malloc void *zalloc(size_t size);
+__must_check __malloc void *arr_alloc(size_t nmemb, size_t size);
+__must_check __malloc void *arr_zalloc(size_t nmemb, size_t size);
 __must_check __malloc char *para_strdup(const char *s);
 
 __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap);
 __printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...);
 __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
 __must_check __malloc char *para_strcat(char *a, const char *b);
 __must_check __malloc char *para_strdup(const char *s);
 
 __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap);
 __printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...);
 __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
 __must_check __malloc char *para_strcat(char *a, const char *b);
-__must_check __malloc char *para_dirname(const char *name);
-__must_check char *para_basename(const char *name);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
 int para_atoi64(const char *str, int64_t *result);
 int para_atoi32(const char *str, int32_t *value);
 __must_check __malloc char *para_logname(void);
 __must_check __malloc char *para_homedir(void);
 __malloc char *para_hostname(void);
 __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
 int para_atoi64(const char *str, int64_t *result);
 int para_atoi32(const char *str, int32_t *value);
-int get_loglevel_by_name(const char *txt);
 int read_size_header(const char *buf);
 int create_argv(const char *buf, const char *delim, char ***result);
 int create_shifted_argv(const char *buf, const char *delim, char ***result);
 int read_size_header(const char *buf);
 int create_argv(const char *buf, const char *delim, char ***result);
 int create_shifted_argv(const char *buf, const char *delim, char ***result);
-int find_arg(const char *arg, char **argv);
 void free_argv(char **argv);
 int para_regcomp(regex_t *preg, const char *regex, int cflags);
 void freep(void *arg);
 void free_argv(char **argv);
 int para_regcomp(regex_t *preg, const char *regex, int cflags);
 void freep(void *arg);
index 8e9ff2c5de79a6385b6472453f3dec417b9ac5cc..20db1b1d1136066a7a12b88b0f4b8cc539daea62 100644 (file)
@@ -102,7 +102,7 @@ static void sync_open(struct filter_node *fn)
        unsigned buddy_given;
        const struct lls_opt_result *r_b;
 
        unsigned buddy_given;
        const struct lls_opt_result *r_b;
 
-       ctx = fn->private_data = para_calloc(sizeof(*ctx));
+       ctx = fn->private_data = zalloc(sizeof(*ctx));
        init_list_head(&ctx->buddies);
 
        /* create socket to listen for incoming packets */
        init_list_head(&ctx->buddies);
 
        /* create socket to listen for incoming packets */
@@ -148,7 +148,7 @@ static void sync_open(struct filter_node *fn)
                        close(fd);
                        goto fail;
                }
                        close(fd);
                        goto fail;
                }
-               buddy = para_malloc(sizeof(*buddy));
+               buddy = alloc(sizeof(*buddy));
                buddy->fd = fd;
                buddy->sbi = sbi + i;
                buddy->ping_received = false;
                buddy->fd = fd;
                buddy->sbi = sbi + i;
                buddy->ping_received = false;
@@ -176,12 +176,12 @@ static void *sync_setup(const struct lls_parse_result *lpr)
 
        r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
        n = lls_opt_given(r_b);
 
        r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
        n = lls_opt_given(r_b);
-       sbi = para_malloc(n * sizeof(*sbi));
+       sbi = arr_alloc(n, sizeof(*sbi));
        PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
        for (i = 0; i < n; i++) {
                const char *url = lls_string_val(i, r_b);
                size_t len = strlen(url);
        PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
        for (i = 0; i < n; i++) {
                const char *url = lls_string_val(i, r_b);
                size_t len = strlen(url);
-               char *host = para_malloc(len + 1);
+               char *host = alloc(len + 1);
                int port;
                struct addrinfo *ai;
 
                int port;
                struct addrinfo *ai;
 
@@ -248,7 +248,7 @@ static void sync_set_timeout(struct sync_filter_context *ctx,
        tv_add(now, &to, &ctx->timeout);
 }
 
        tv_add(now, &to, &ctx->timeout);
 }
 
-static void sync_pre_select(struct sched *s, void *context)
+static void sync_pre_monitor(struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
 {
        int ret;
        struct filter_node *fn = context;
@@ -261,7 +261,7 @@ static void sync_pre_select(struct sched *s, void *context)
        ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
        if (ret < 0)
                return sched_min_delay(s);
        ret = btr_node_status(fn->btrn, 0, BTR_NT_INTERNAL);
        if (ret < 0)
                return sched_min_delay(s);
-       para_fd_set(ctx->listen_fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(ctx->listen_fd, s);
        if (ret == 0)
                return;
        if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
        if (ret == 0)
                return;
        if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
@@ -284,7 +284,7 @@ static struct sync_buddy *sync_find_buddy(struct sockaddr *addr,
        return NULL;
 }
 
        return NULL;
 }
 
-static int sync_post_select(__a_unused struct sched *s, void *context)
+static int sync_post_monitor(__a_unused struct sched *s, void *context)
 {
        int ret;
        struct filter_node *fn = context;
 {
        int ret;
        struct filter_node *fn = context;
@@ -324,7 +324,7 @@ static int sync_post_select(__a_unused struct sched *s, void *context)
                }
                ctx->ping_sent = true;
        }
                }
                ctx->ping_sent = true;
        }
-       if (FD_ISSET(ctx->listen_fd, &s->rfds)) {
+       if (sched_read_ok(ctx->listen_fd, s)) {
                char c;
                for (;;) {
                        struct sockaddr src_addr;
                char c;
                for (;;) {
                        struct sockaddr src_addr;
@@ -365,7 +365,7 @@ success:
        ret = -E_SYNC_COMPLETE; /* success */
        goto out;
 fail:
        ret = -E_SYNC_COMPLETE; /* success */
        goto out;
 fail:
-       if (ret != -E_BTR_EOF)
+       if (ret != -E_EOF)
                PARA_WARNING_LOG("%s\n", para_strerror(-ret));
 out:
        sync_close_buddies(ctx);
                PARA_WARNING_LOG("%s\n", para_strerror(-ret));
 out:
        sync_close_buddies(ctx);
@@ -377,8 +377,8 @@ out:
 const struct filter lsg_filter_cmd_com_sync_user_data = {
        .setup = sync_setup,
        .open = sync_open,
 const struct filter lsg_filter_cmd_com_sync_user_data = {
        .setup = sync_setup,
        .open = sync_open,
-       .pre_select = sync_pre_select,
-       .post_select = sync_post_select,
+       .pre_monitor = sync_pre_monitor,
+       .post_monitor = sync_post_monitor,
        .close = sync_close,
        .teardown = sync_teardown
 };
        .close = sync_close,
        .teardown = sync_teardown
 };
diff --git a/t/audio_files/short-44100-2.mp3 b/t/audio_files/short-44100-2.mp3
new file mode 100644 (file)
index 0000000..917d59d
Binary files /dev/null and b/t/audio_files/short-44100-2.mp3 differ
index 03957464e17e2a50596149e59627cd7584424fe9..f7a407dc9e4bd99a71a3b3e364a6386fc55f1d44 100755 (executable)
@@ -24,7 +24,8 @@ get_audio_file_paths ogg
 declare -a oggs=($result)
 declare -a oggs_base=(${oggs[@]##*/})
 
 declare -a oggs=($result)
 declare -a oggs_base=(${oggs[@]##*/})
 
-declare -a commands=() cmdline=() required_objects=() good=() bad=()
+declare -a commands=() cmdline=() required_objects=() good=() bad=() \
+       expect_failure=()
 i=0
 commands[$i]="help"
 cmdline[$i]="help -l"
 i=0
 commands[$i]="help"
 cmdline[$i]="help -l"
@@ -36,6 +37,18 @@ cmdline[$i]="init"
 good[$i]='^successfully'
 bad[$i]='!^successfully'
 
 good[$i]='^successfully'
 bad[$i]='!^successfully'
 
+let i++
+commands[$i]='add_dir'
+required_objects[$i]='ogg_afh'
+cmdline[$i]="add -v $test_audio_file_dir"
+good[$i]='^adding'
+
+let i++
+commands[$i]='rm'
+required_objects[$i]='ogg_afh'
+cmdline[$i]="rm -v $test_audio_file_dir/*"
+good[$i]='^removing'
+
 let i++
 commands[$i]="add_ogg"
 required_objects[$i]='ogg_afh'
 let i++
 commands[$i]="add_ogg"
 required_objects[$i]='ogg_afh'
@@ -72,6 +85,15 @@ required_objects[$i]='ogg_afh'
 cmdline[$i]="ls -l=v ${oggs[@]}"
 good[$i]='^attributes_txt: 33'
 
 cmdline[$i]="ls -l=v ${oggs[@]}"
 good[$i]='^attributes_txt: 33'
 
+let i++
+commands[$i]='addmood'
+cmdline[$i]="addmood test-mood"
+
+let i++
+commands[$i]='empty-mood-parameter'
+cmdline[$i]="select m/"
+expect_failure[$i]='true'
+
 let i++
 commands[$i]="term"
 cmdline[$i]="term"
 let i++
 commands[$i]="term"
 cmdline[$i]="term"
@@ -137,14 +159,19 @@ for ((i=0; i < ${#commands[@]}; i++)); do
                        continue
                fi
        fi
                        continue
                fi
        fi
-       test_expect_success "$command" "
+       if [[ -n "${expect_failure[$i]}" ]]; then
+               f=test_expect_failure
+       else
+               f=test_expect_success
+       fi
+       $f "$command" "
        $PARA_CLIENT \
                --loglevel $loglevel \
                --server-port $port \
                --key-file $privkey \
                --config-file /dev/null \
                -- \
        $PARA_CLIENT \
                --loglevel $loglevel \
                --server-port $port \
                --key-file $privkey \
                --config-file /dev/null \
                -- \
-               ${cmdline[$i]} > $command.out &&
+               ${cmdline[$i]} > $command.out < /dev/null &&
                { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out &&
                { [[ -z \"${bad[$i]}\" ]]  || ! grep \"${bad[$i]}\"; } < $command.out
        "
                { [[ -z \"${good[$i]}\" ]] || grep \"${good[$i]}\"; } < $command.out &&
                { [[ -z \"${bad[$i]}\" ]]  || ! grep \"${bad[$i]}\"; } < $command.out
        "
index 75249fe32b0b0e159b4b6ad1773efa2673b2cd22..1ba70632a95d585de16eb5b0e961291fe4ca83f1 100644 (file)
@@ -17,9 +17,12 @@ get_audio_file_paths()
 
 say_color()
 {
 
 say_color()
 {
+       local severity=$1
+
+       shift
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                export TERM=$ORIGINAL_TERM
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                export TERM=$ORIGINAL_TERM
-               case "$1" in
+               case "$severity" in
                        error) tput $C_BOLD; tput $C_SETAF 1;;
                        skip)  tput $C_SETAF 5;;
                        ok)
                        error) tput $C_BOLD; tput $C_SETAF 1;;
                        skip)  tput $C_SETAF 5;;
                        ok)
@@ -32,8 +35,11 @@ say_color()
                                tput $C_SETAF 6;;
                esac
        fi
                                tput $C_SETAF 6;;
                esac
        fi
-       shift
-       printf "%s\n" "$*"
+       if [[ "$severity" == 'error' ]]; then
+               printf "%s\n" "$*" 1>&2
+       else
+               printf "%s\n" "$*"
+       fi
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                tput $C_SGR0
                export TERM=dumb
        if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
                tput $C_SGR0
                export TERM=dumb
@@ -279,7 +285,7 @@ fixup_dirs()
        [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
        [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
 
        [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
        [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
 
-       # we want alsolute paths because relative paths become invalid
+       # we want absolute paths because relative paths become invalid
        # after changing to the trash dir
        [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
        [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
        # after changing to the trash dir
        [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
        [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
index 58d45ab43d6508619d34186b3f4a23f6d455ecc7..f98a9664aec2d1ee3d8129e70442468423fa79a8 100644 (file)
 #include "net.h"
 #include "fd.h"
 
 #include "net.h"
 #include "fd.h"
 
-static void udp_recv_pre_select(struct sched *s, void *context)
+static void udp_recv_pre_monitor(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
 
 {
        struct receiver_node *rn = context;
 
-       if (generic_recv_pre_select(s, rn) <= 0)
+       if (generic_recv_pre_monitor(s, rn) <= 0)
                return;
                return;
-       para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
+       sched_monitor_readfd(rn->fd, s);
 }
 
 static int udp_check_eof(size_t sz, struct iovec iov[2])
 }
 
 static int udp_check_eof(size_t sz, struct iovec iov[2])
@@ -40,17 +40,17 @@ static int udp_check_eof(size_t sz, struct iovec iov[2])
                if (memcmp(iov[0].iov_base, FEC_EOF_PACKET,
                                FEC_EOF_PACKET_LEN) != 0)
                        return 0;
                if (memcmp(iov[0].iov_base, FEC_EOF_PACKET,
                                FEC_EOF_PACKET_LEN) != 0)
                        return 0;
-               return -E_RECV_EOF;
+               return -E_EOF;
        }
        if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, iov[0].iov_len) != 0)
                return 0;
        if (memcmp(iov[1].iov_base, &FEC_EOF_PACKET[iov[0].iov_len],
                        FEC_EOF_PACKET_LEN - iov[0].iov_len) != 0)
                return 0;
        }
        if (memcmp(iov[0].iov_base, FEC_EOF_PACKET, iov[0].iov_len) != 0)
                return 0;
        if (memcmp(iov[1].iov_base, &FEC_EOF_PACKET[iov[0].iov_len],
                        FEC_EOF_PACKET_LEN - iov[0].iov_len) != 0)
                return 0;
-       return -E_RECV_EOF;
+       return -E_EOF;
 }
 
 }
 
-static int udp_recv_post_select(__a_unused struct sched *s, void *context)
+static int udp_recv_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
 {
        struct receiver_node *rn = context;
        struct btr_node *btrn = rn->btrn;
@@ -68,7 +68,7 @@ static int udp_recv_post_select(__a_unused struct sched *s, void *context)
        ret = -E_UDP_OVERRUN;
        if (iovcnt == 0)
                goto out;
        ret = -E_UDP_OVERRUN;
        if (iovcnt == 0)
                goto out;
-       ret = readv_nonblock(rn->fd, iov, iovcnt, &s->rfds, &num_bytes);
+       ret = readv_nonblock(rn->fd, iov, iovcnt, &num_bytes);
        if (num_bytes == 0)
                goto out;
        readv_ret = ret;
        if (num_bytes == 0)
                goto out;
        readv_ret = ret;
@@ -168,7 +168,7 @@ static int udp_recv_open(struct receiver_node *rn)
        uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
        uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
+       ret = makesock(IPPROTO_UDP, true /* passive */, host, port, NULL);
        if (ret < 0)
                return ret;
        rn->fd = ret;
        if (ret < 0)
                return ret;
        rn->fd = ret;
@@ -189,6 +189,6 @@ err:
 const struct receiver lsg_recv_cmd_com_udp_user_data = {
        .open = udp_recv_open,
        .close = udp_recv_close,
 const struct receiver lsg_recv_cmd_com_udp_user_data = {
        .open = udp_recv_open,
        .close = udp_recv_close,
-       .pre_select = udp_recv_pre_select,
-       .post_select = udp_recv_post_select,
+       .pre_monitor = udp_recv_pre_monitor,
+       .post_monitor = udp_recv_post_monitor,
 };
 };
index 68d75e3c3ef87dc089a3a1eec4d59fb647e73e44..fe001025bc1ad6c73c0e64c8589977db1e3203af 100644 (file)
@@ -21,8 +21,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "portable_io.h"
 #include "fd.h"
 #include "vss.h"
 #include "portable_io.h"
 #include "fd.h"
@@ -187,7 +187,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
                return ret;
        port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
                return ret;
        port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
+       ret = para_connect(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
 
@@ -326,8 +326,8 @@ static int udp_com_add(struct sender_command_data *scd)
                                sc->name);
                return -E_TARGET_EXISTS;
        }
                                sc->name);
                return -E_TARGET_EXISTS;
        }
-       ut = para_calloc(sizeof(*ut));
-       sc = para_calloc(sizeof(*sc));
+       ut = zalloc(sizeof(*ut));
+       sc = zalloc(sizeof(*sc));
        ut->fcp.slices_per_group      = scd->slices_per_group;
        ut->fcp.data_slices_per_group = scd->data_slices_per_group;
        ut->fcp.init_fec              = udp_init_fec;
        ut->fcp.slices_per_group      = scd->slices_per_group;
        ut->fcp.data_slices_per_group = scd->data_slices_per_group;
        ut->fcp.init_fec              = udp_init_fec;
@@ -336,7 +336,7 @@ static int udp_com_add(struct sender_command_data *scd)
 
        sc->private_data = ut;
        sc->fd = -1;
 
        sc->private_data = ut;
        sc->fd = -1;
-       ret = para_connect_simple(IPPROTO_UDP, scd->host, scd->port);
+       ret = para_connect(IPPROTO_UDP, scd->host, scd->port);
        if (ret < 0)
                goto err;
        sc->fd = ret;
        if (ret < 0)
                goto err;
        sc->fd = ret;
index 32a4309d4360fa73a8e7d0bbef622a7928001bb0..46770edf701e983d424e8869e7c9ccd4730c3cf9 100644 (file)
@@ -91,7 +91,7 @@ void user_list_init(const char *user_list_file)
                        continue;
                if (strcmp(w, "user"))
                        continue;
                        continue;
                if (strcmp(w, "user"))
                        continue;
-               PARA_DEBUG_LOG("found entry for user %s\n", n);
+               PARA_INFO_LOG("loading pubkey %s for user %s\n", k, n);
                ret = apc_get_pubkey(k, &pubkey);
                if (ret < 0) {
                        PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
                ret = apc_get_pubkey(k, &pubkey);
                if (ret < 0) {
                        PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
@@ -110,7 +110,7 @@ void user_list_init(const char *user_list_file)
                        apc_free_pubkey(pubkey);
                        continue;
                }
                        apc_free_pubkey(pubkey);
                        continue;
                }
-               u = para_malloc(sizeof(*u));
+               u = alloc(sizeof(*u));
                u->name = para_strdup(n);
                u->pubkey = pubkey;
                u->perms = 0;
                u->name = para_strdup(n);
                u->pubkey = pubkey;
                u->perms = 0;
diff --git a/vss.c b/vss.c
index f9bf57b5575a7348fa5b5bb8a2db3c446d9dd92c..cd55851c366cec61c0bf7bc9501609fc2fa8a6c5 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -28,8 +28,8 @@
 #include "net.h"
 #include "server.h"
 #include "list.h"
 #include "net.h"
 #include "server.h"
 #include "list.h"
-#include "send.h"
 #include "sched.h"
 #include "sched.h"
+#include "send.h"
 #include "vss.h"
 #include "ipc.h"
 #include "fd.h"
 #include "vss.h"
 #include "ipc.h"
 #include "fd.h"
@@ -43,7 +43,7 @@ const struct sender * const senders[] = {
 enum afs_socket_status {
        /** Socket is inactive. */
        AFS_SOCKET_READY,
 enum afs_socket_status {
        /** Socket is inactive. */
        AFS_SOCKET_READY,
-       /** Socket fd was included in the write fd set for select(). */
+       /** Socket fd was monitored for writing. */
        AFS_SOCKET_CHECK_FOR_WRITE,
        /** vss wrote a request to the socket and waits for reply from afs. */
        AFS_SOCKET_AFD_PENDING
        AFS_SOCKET_CHECK_FOR_WRITE,
        /** vss wrote a request to the socket and waits for reply from afs. */
        AFS_SOCKET_AFD_PENDING
@@ -335,10 +335,10 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
        ret = fec_new(k, n, &fc->parms);
        if (ret < 0)
                return ret;
        ret = fec_new(k, n, &fc->parms);
        if (ret < 0)
                return ret;
-       fc->src_data = para_malloc(k * sizeof(char *));
+       fc->src_data = arr_alloc(k, sizeof(char *));
        for (i = 0; i < k; i++)
        for (i = 0; i < k; i++)
-               fc->src_data[i] = para_malloc(fc->mps);
-       fc->enc_buf = para_malloc(fc->mps);
+               fc->src_data[i] = alloc(fc->mps);
+       fc->enc_buf = alloc(fc->mps);
 
        fc->state = FEC_STATE_READY_TO_RUN;
        fc->next_header_time.tv_sec = 0;
 
        fc->state = FEC_STATE_READY_TO_RUN;
        fc->next_header_time.tv_sec = 0;
@@ -671,7 +671,7 @@ size_t vss_get_fec_eof_packet(const char **buf)
 struct fec_client *vss_add_fec_client(struct sender_client *sc,
                                      struct fec_client_parms *fcp)
 {
 struct fec_client *vss_add_fec_client(struct sender_client *sc,
                                      struct fec_client_parms *fcp)
 {
-       struct fec_client *fc = para_calloc(sizeof(*fc));
+       struct fec_client *fc = zalloc(sizeof(*fc));
 
        fc->sc  = sc;
        fc->fcp = fcp;
 
        fc->sc  = sc;
        fc->fcp = fcp;
@@ -827,7 +827,7 @@ static void vss_compute_timeout(struct sched *s, struct vss_task *vsst)
        if (sched_request_barrier(&vsst->data_send_barrier, s) == 1)
                return;
        /*
        if (sched_request_barrier(&vsst->data_send_barrier, s) == 1)
                return;
        /*
-        * Compute the select timeout as the minimal time until the next
+        * Compute the I/O timeout as the minimal time until the next
         * chunk/slice is due for any client.
         */
        compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
         * chunk/slice is due for any client.
         */
        compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
@@ -892,21 +892,21 @@ static void set_mmd_offset(void)
        mmd->offset = tv2ms(&offset);
 }
 
        mmd->offset = tv2ms(&offset);
 }
 
-static void vss_pre_select(struct sched *s, void *context)
+static void vss_pre_monitor(struct sched *s, void *context)
 {
        int i;
        struct vss_task *vsst = context;
 
        if (need_to_request_new_audio_file(vsst)) {
                PARA_DEBUG_LOG("ready and playing, but no audio file\n");
 {
        int i;
        struct vss_task *vsst = context;
 
        if (need_to_request_new_audio_file(vsst)) {
                PARA_DEBUG_LOG("ready and playing, but no audio file\n");
-               para_fd_set(vsst->afs_socket, &s->wfds, &s->max_fileno);
+               sched_monitor_writefd(vsst->afs_socket, s);
                vsst->afsss = AFS_SOCKET_CHECK_FOR_WRITE;
        } else
                vsst->afsss = AFS_SOCKET_CHECK_FOR_WRITE;
        } else
-               para_fd_set(vsst->afs_socket, &s->rfds, &s->max_fileno);
+               sched_monitor_readfd(vsst->afs_socket, s);
        FOR_EACH_SENDER(i) {
        FOR_EACH_SENDER(i) {
-               if (!senders[i]->pre_select)
+               if (!senders[i]->pre_monitor)
                        continue;
                        continue;
-               senders[i]->pre_select(&s->max_fileno, &s->rfds, &s->wfds);
+               senders[i]->pre_monitor(s);
        }
        vss_compute_timeout(s, vsst);
 }
        }
        vss_compute_timeout(s, vsst);
 }
@@ -950,13 +950,13 @@ static int recv_afs_msg(int afs_socket, int *fd, uint32_t *code, uint32_t *data)
 #define MAP_POPULATE 0
 #endif
 
 #define MAP_POPULATE 0
 #endif
 
-static void recv_afs_result(struct vss_task *vsst, fd_set *rfds)
+static void recv_afs_result(struct vss_task *vsst, const struct sched *s)
 {
        int ret, passed_fd, shmid;
        uint32_t afs_code = 0, afs_data = 0;
        struct stat statbuf;
 
 {
        int ret, passed_fd, shmid;
        uint32_t afs_code = 0, afs_data = 0;
        struct stat statbuf;
 
-       if (!FD_ISSET(vsst->afs_socket, rfds))
+       if (!sched_read_ok(vsst->afs_socket, s))
                return;
        ret = recv_afs_msg(vsst->afs_socket, &passed_fd, &afs_code, &afs_data);
        if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
                return;
        ret = recv_afs_msg(vsst->afs_socket, &passed_fd, &afs_code, &afs_data);
        if (ret == -ERRNO_TO_PARA_ERROR(EAGAIN))
@@ -1014,13 +1014,8 @@ err:
 }
 
 /**
 }
 
 /**
- * Main sending function.
- *
- * This function gets called from vss_post_select(). It checks whether the next
- * chunk of data should be pushed out. It obtains a pointer to the data to be
- * sent out as well as its length from mmd->afd.afhi. This information is then
- * passed to each supported sender's send() function as well as to the send()
- * functions of each registered fec client.
+ * If the next chunk needs to be sent, pass a pointer to the chunk data to all
+ * registered fec clients and to each sender's ->send() method.
  */
 static void vss_send(struct vss_task *vsst)
 {
  */
 static void vss_send(struct vss_task *vsst)
 {
@@ -1087,7 +1082,7 @@ static void vss_send(struct vss_task *vsst)
        mmd->current_chunk++;
 }
 
        mmd->current_chunk++;
 }
 
-static int vss_post_select(struct sched *s, void *context)
+static int vss_post_monitor(struct sched *s, void *context)
 {
        int ret, i;
        struct vss_task *vsst = context;
 {
        int ret, i;
        struct vss_task *vsst = context;
@@ -1137,8 +1132,8 @@ static int vss_post_select(struct sched *s, void *context)
                mmd->sender_cmd_data.cmd_num = -1;
        }
        if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)
                mmd->sender_cmd_data.cmd_num = -1;
        }
        if (vsst->afsss != AFS_SOCKET_CHECK_FOR_WRITE)
-               recv_afs_result(vsst, &s->rfds);
-       else if (FD_ISSET(vsst->afs_socket, &s->wfds)) {
+               recv_afs_result(vsst, s);
+       else if (sched_write_ok(vsst->afs_socket, s)) {
                PARA_INFO_LOG("requesting new fd from afs\n");
                ret = write_buffer(vsst->afs_socket, "new");
                if (ret < 0)
                PARA_INFO_LOG("requesting new fd from afs\n");
                ret = write_buffer(vsst->afs_socket, "new");
                if (ret < 0)
@@ -1147,9 +1142,9 @@ static int vss_post_select(struct sched *s, void *context)
                        vsst->afsss = AFS_SOCKET_AFD_PENDING;
        }
        FOR_EACH_SENDER(i) {
                        vsst->afsss = AFS_SOCKET_AFD_PENDING;
        }
        FOR_EACH_SENDER(i) {
-               if (!senders[i]->post_select)
+               if (!senders[i]->post_monitor)
                        continue;
                        continue;
-               senders[i]->post_select(&s->rfds, &s->wfds);
+               senders[i]->post_monitor(s);
        }
        if ((vss_playing() && !(mmd->vss_status_flags & VSS_PLAYING)) ||
                        (vss_next() && vss_playing()))
        }
        if ((vss_playing() && !(mmd->vss_status_flags & VSS_PLAYING)) ||
                        (vss_next() && vss_playing()))
@@ -1194,8 +1189,8 @@ void vss_init(int afs_socket, struct sched *s)
        }
        vsst->task = task_register(&(struct task_info) {
                .name = "vss",
        }
        vsst->task = task_register(&(struct task_info) {
                .name = "vss",
-               .pre_select = vss_pre_select,
-               .post_select = vss_post_select,
+               .pre_monitor = vss_pre_monitor,
+               .post_monitor = vss_post_monitor,
                .context = vsst,
        }, s);
 }
                .context = vsst,
        }, s);
 }
index e749160d3337e87dc7c908c023ea100c955590e4..de4a3e6aafc76b83de2d9e2f4112c39c6e038d06 100644 (file)
@@ -53,12 +53,12 @@ static void wav_open(struct filter_node *fn)
 {
        int *bof;
 
 {
        int *bof;
 
-       fn->private_data = para_malloc(sizeof(int));
+       fn->private_data = alloc(sizeof(int));
        bof = fn->private_data;
        *bof = 1;
 }
 
        bof = fn->private_data;
        *bof = 1;
 }
 
-static void wav_pre_select(struct sched *s, void *context)
+static void wav_pre_monitor(struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        size_t iqs = btr_get_input_queue_size(fn->btrn);
 {
        struct filter_node *fn = context;
        size_t iqs = btr_get_input_queue_size(fn->btrn);
@@ -68,7 +68,7 @@ static void wav_pre_select(struct sched *s, void *context)
        sched_min_delay(s);
 }
 
        sched_min_delay(s);
 }
 
-static int wav_post_select(__a_unused struct sched *s, void *context)
+static int wav_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
 {
        struct filter_node *fn = context;
        struct btr_node *btrn = fn->btrn;
@@ -78,7 +78,7 @@ static int wav_post_select(__a_unused struct sched *s, void *context)
        int32_t rate, ch;
 
        if (iqs == 0) {
        int32_t rate, ch;
 
        if (iqs == 0) {
-               ret = -E_WAV_EOF;
+               ret = -E_EOF;
                if (btr_no_parent(btrn))
                        goto err;
                return 0;
                if (btr_no_parent(btrn))
                        goto err;
                return 0;
@@ -101,7 +101,7 @@ static int wav_post_select(__a_unused struct sched *s, void *context)
        free(buf);
        if (ret < 0)
                goto err;
        free(buf);
        if (ret < 0)
                goto err;
-       header = para_malloc(WAV_HEADER_LEN);
+       header = alloc(WAV_HEADER_LEN);
        make_wav_header(ch, rate, header);
        btr_add_output(header, WAV_HEADER_LEN, btrn);
        ret = -E_WAV_SUCCESS;
        make_wav_header(ch, rate, header);
        btr_add_output(header, WAV_HEADER_LEN, btrn);
        ret = -E_WAV_SUCCESS;
@@ -118,6 +118,6 @@ err:
 const struct filter lsg_filter_cmd_com_wav_user_data = {
        .close = wav_close,
        .open = wav_open,
 const struct filter lsg_filter_cmd_com_wav_user_data = {
        .close = wav_close,
        .open = wav_open,
-       .pre_select = wav_pre_select,
-       .post_select = wav_post_select,
+       .pre_monitor = wav_pre_monitor,
+       .post_monitor = wav_post_monitor,
 };
 };
index 78834a4d48be70f178dba5ffed88f5db8761745c..b5329ea07f3d8a2f9553f384c9129c4ad1a262e3 100644 (file)
@@ -309,8 +309,8 @@ repository with
                git clone git://git.tuebingen.mpg.de/lopsub
 
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
                git clone git://git.tuebingen.mpg.de/lopsub
 
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
-[clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
-supported. Clang version 1.1 or newer should work as well.
+[clang](http://clang.llvm.org). All gcc versions >= 5.4 are currently
+supported. Moderately recent versions of clang should work as well.
 
 - [gnu make](ftp://ftp.gnu.org/pub/gnu/make) is also shipped with the
 disto. On BSD systems the gnu make executable is often called gmake.
 
 - [gnu make](ftp://ftp.gnu.org/pub/gnu/make) is also shipped with the
 disto. On BSD systems the gnu make executable is often called gmake.
index 63e49677b0f1b85c13c17160a7b249531c1fbcd4..8bff7cfcaa98f8ae3bdfb00f87545c70c7927d94 100644 (file)
--- a/wma_afh.c
+++ b/wma_afh.c
@@ -195,7 +195,7 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size
        size_t ct_size = 250;
        int ret, count = 0, num_frames, num_superframes;
 
        size_t ct_size = 250;
        int ret, count = 0, num_frames, num_superframes;
 
-       afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
+       afhi->chunk_table = arr_alloc(ct_size, sizeof(uint32_t));
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
 
        afhi->chunk_table[0] = 0;
        afhi->chunk_table[1] = afhi->header_len;
 
@@ -318,7 +318,7 @@ static int convert_utf8_to_utf16(char *src, char **dst)
        int ret;
 
        if (!src || !*src) {
        int ret;
 
        if (!src || !*src) {
-               *dst = para_calloc(2);
+               *dst = zalloc(2);
                return 0;
        }
        /*
                return 0;
        }
        /*
@@ -334,7 +334,7 @@ static int convert_utf8_to_utf16(char *src, char **dst)
        /* even though src is in UTF-8, strlen() should DTRT */
        inbytes = inbytesleft = strlen(src);
        outbytes = outbytesleft = 4 * inbytes + 2; /* hope that's enough */
        /* even though src is in UTF-8, strlen() should DTRT */
        inbytes = inbytesleft = strlen(src);
        outbytes = outbytesleft = 4 * inbytes + 2; /* hope that's enough */
-       *dst = outbuf = para_malloc(outbytes);
+       *dst = outbuf = alloc(outbytes);
        sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
        if (sz == (size_t)-1) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
        sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
        if (sz == (size_t)-1) {
                ret = -ERRNO_TO_PARA_ERROR(errno);
@@ -411,7 +411,7 @@ static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
        result->size = 16 + 8 + 5 * 2 + title_bytes + artist_bytes
                + orig_cr_bytes + comment_bytes + orig_rating_bytes;
        PARA_DEBUG_LOG("cdo is %zu bytes\n", (size_t)result->size);
        result->size = 16 + 8 + 5 * 2 + title_bytes + artist_bytes
                + orig_cr_bytes + comment_bytes + orig_rating_bytes;
        PARA_DEBUG_LOG("cdo is %zu bytes\n", (size_t)result->size);
-       p = result->ptr = para_malloc(result->size);
+       p = result->ptr = alloc(result->size);
        memcpy(p, content_description_header, 16);
        p += 16;
        write_u64(p, result->size);
        memcpy(p, content_description_header, 16);
        p += 16;
        write_u64(p, result->size);
@@ -469,7 +469,7 @@ static int make_ecdo(struct taginfo *tags, struct asf_object *result)
        result->size += 2 + sizeof(album_tag_header) + 2 + 2 + 2 + album_bytes;
        result->size += 2 + sizeof(year_tag_header) + 2 + 2 + 2 + year_bytes;
 
        result->size += 2 + sizeof(album_tag_header) + 2 + 2 + 2 + album_bytes;
        result->size += 2 + sizeof(year_tag_header) + 2 + 2 + 2 + year_bytes;
 
-       p = result->ptr = para_malloc(result->size);
+       p = result->ptr = alloc(result->size);
        memcpy(p, extended_content_header, 16);
        p += 16;
        write_u64(p, result->size);
        memcpy(p, extended_content_header, 16);
        p += 16;
        write_u64(p, result->size);
@@ -622,7 +622,7 @@ static int wma_rewrite_tags(const char *map, size_t mapsize,
        if (top.reserved2 != 2)
                return -E_NO_WMA;
        p++; /* objects start at p */
        if (top.reserved2 != 2)
                return -E_NO_WMA;
        p++; /* objects start at p */
-       top.objects = para_malloc(top.num_objects * sizeof(struct asf_object));
+       top.objects = arr_alloc(top.num_objects, sizeof(struct asf_object));
        ret = read_asf_objects(p, top.size - (p - map), top.num_objects,
                top.objects, &ton);
        if (ret < 0)
        ret = read_asf_objects(p, top.size - (p - map), top.num_objects,
                top.objects, &ton);
        if (ret < 0)
index edf50cb0b3834d8971fe9602f6528e2f2bd86e5d..f2ca273cd08979f33b42ab0a7f84b533fe29a583 100644 (file)
@@ -16,7 +16,6 @@
 
 #include <math.h>
 #include <regex.h>
 
 #include <math.h>
 #include <regex.h>
-#include <sys/select.h>
 
 #include "para.h"
 #include "error.h"
 
 #include "para.h"
 #include "error.h"
@@ -160,8 +159,8 @@ static void init_coef_vlc(struct private_wmadec_data *pwd, int sidx, int didx)
        int i, l, j, k, level, n = src->n;
 
        init_vlc(dst, VLCBITS, n, src->huffbits, src->huffcodes, 4);
        int i, l, j, k, level, n = src->n;
 
        init_vlc(dst, VLCBITS, n, src->huffbits, src->huffcodes, 4);
-       pwd->run_table[didx] = para_malloc(n * sizeof(uint16_t));
-       pwd->level_table[didx] = para_malloc(n * sizeof(uint16_t));
+       pwd->run_table[didx] = arr_alloc(n, sizeof(uint16_t));
+       pwd->level_table[didx] = arr_alloc(n, sizeof(uint16_t));
        i = 2;
        level = 1;
        k = 0;
        i = 2;
        level = 1;
        k = 0;
@@ -428,7 +427,7 @@ static int wma_decode_init(char *initial_buf, int len, struct private_wmadec_dat
        int ret, i;
 
        PARA_NOTICE_LOG("initial buf: %d bytes\n", len);
        int ret, i;
 
        PARA_NOTICE_LOG("initial buf: %d bytes\n", len);
-       pwd = para_calloc(sizeof(*pwd));
+       pwd = zalloc(sizeof(*pwd));
        ret = read_asf_header(initial_buf, len, &pwd->ahi);
        if (ret <= 0) {
                free(pwd);
        ret = read_asf_header(initial_buf, len, &pwd->ahi);
        if (ret <= 0) {
                free(pwd);
@@ -1159,7 +1158,7 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result)
 
 #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
 
 
 #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
 
-static int wmadec_post_select(__a_unused struct sched *s, void *context)
+static int wmadec_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct filter_node *fn = context;
        int ret, converted, out_size;
 {
        struct filter_node *fn = context;
        int ret, converted, out_size;
@@ -1177,7 +1176,7 @@ next_buffer:
                return 0;
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &in);
                return 0;
        btr_merge(btrn, fn->min_iqs);
        len = btr_next_buffer(btrn, &in);
-       ret = -E_WMADEC_EOF;
+       ret = -E_EOF;
        if (len < fn->min_iqs)
                goto err;
        if (!pwd) {
        if (len < fn->min_iqs)
                goto err;
        if (!pwd) {
@@ -1197,7 +1196,7 @@ next_buffer:
        if (fn->min_iqs > len)
                goto success;
        out_size = WMA_OUTPUT_BUFFER_SIZE;
        if (fn->min_iqs > len)
                goto success;
        out_size = WMA_OUTPUT_BUFFER_SIZE;
-       out = para_malloc(out_size);
+       out = alloc(out_size);
        ret = wma_decode_superframe(pwd, out, &out_size,
                (uint8_t *)in + WMA_FRAME_SKIP);
        if (ret < 0) {
        ret = wma_decode_superframe(pwd, out, &out_size,
                (uint8_t *)in + WMA_FRAME_SKIP);
        if (ret < 0) {
@@ -1229,6 +1228,6 @@ const struct filter lsg_filter_cmd_com_wmadec_user_data = {
        .open = wmadec_open,
        .close = wmadec_close,
        .execute = wmadec_execute,
        .open = wmadec_open,
        .close = wmadec_close,
        .execute = wmadec_execute,
-       .pre_select = generic_filter_pre_select,
-       .post_select = wmadec_post_select,
+       .pre_monitor = generic_filter_pre_monitor,
+       .post_monitor = wmadec_post_monitor,
 };
 };
diff --git a/write.c b/write.c
index acfb94605b53d259a89ea41e75507b81e2cb2ca4..cb32d391eaa0f856414aa23318a7573def0d9eef 100644 (file)
--- a/write.c
+++ b/write.c
@@ -54,16 +54,16 @@ struct write_task {
        struct check_wav_context *cwc;
 };
 
        struct check_wav_context *cwc;
 };
 
-static void write_pre_select(struct sched *s, void *context)
+static void write_pre_monitor(struct sched *s, void *context)
 {
        struct write_task *wt = context;
 {
        struct write_task *wt = context;
-       check_wav_pre_select(s, wt->cwc);
+       check_wav_pre_monitor(s, wt->cwc);
 }
 
 }
 
-static int write_post_select(__a_unused struct sched *s, void *context)
+static int write_post_monitor(__a_unused struct sched *s, void *context)
 {
        struct write_task *wt = context;
 {
        struct write_task *wt = context;
-       return check_wav_post_select(wt->cwc);
+       return check_wav_post_monitor(wt->cwc);
 }
 
 static int setup_and_schedule(struct lls_parse_result *lpr)
 }
 
 static int setup_and_schedule(struct lls_parse_result *lpr)
@@ -83,21 +83,20 @@ static int setup_and_schedule(struct lls_parse_result *lpr)
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
        wt.task = task_register(&(struct task_info) {
                .name = "write",
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
        wt.task = task_register(&(struct task_info) {
                .name = "write",
-               .pre_select = write_pre_select,
-               .post_select = write_post_select,
+               .pre_monitor = write_pre_monitor,
+               .post_monitor = write_post_monitor,
                .context = &wt,
        }, &s);
 
        n = writer_given? writer_given : 1;
                .context = &wt,
        }, &s);
 
        n = writer_given? writer_given : 1;
-       wns = para_calloc(n * sizeof(*wns));
+       wns = arr_zalloc(n, sizeof(*wns));
        for (i = 0; i < n; i++) {
                const char *arg = i < writer_given?
                        lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
                wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
                register_writer_node(wns + i, cw_btrn, &s);
        }
        for (i = 0; i < n; i++) {
                const char *arg = i < writer_given?
                        lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
                wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
                register_writer_node(wns + i, cw_btrn, &s);
        }
-       s.default_timeout.tv_sec = 10;
-       s.default_timeout.tv_usec = 50000;
+       s.default_timeout = 10500;
        ret = schedule(&s);
        if (ret >= 0) {
                int j, ts;
        ret = schedule(&s);
        if (ret >= 0) {
                int j, ts;
@@ -105,7 +104,7 @@ static int setup_and_schedule(struct lls_parse_result *lpr)
                        struct writer_node *wn = wns + j;
                        ts = task_status(wn->task);
                        assert(ts < 0);
                        struct writer_node *wn = wns + j;
                        ts = task_status(wn->task);
                        assert(ts < 0);
-                       if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) {
+                       if (ts != -E_EOF) {
                                const char *name = writer_name(wn->wid);
                                PARA_ERROR_LOG("%s: %s\n", name,
                                        para_strerror(-ts));
                                const char *name = writer_name(wn->wid);
                                PARA_ERROR_LOG("%s: %s\n", name,
                                        para_strerror(-ts));
diff --git a/write.h b/write.h
index 833cb69a5cb6373bae819d5b7c323e1d567c6104..35a8d29f1a3f7dd493b65c394bc8ce63aa7f9f8f 100644 (file)
--- a/write.h
+++ b/write.h
@@ -20,21 +20,23 @@ struct writer_node {
        size_t min_iqs;
 };
 
        size_t min_iqs;
 };
 
-/** Describes one supported writer. */
+/**
+ * Describes a data sink for audio streams.
+ *
+ * A paraslash writer obtains data via the buffer tree mechanism from its
+ * parent node. It consumes data without producing output on its own.
+ *
+ * This structure contains the methods which have to be implemented by each
+ * writer.
+ *
+ * \sa struct \ref writer_node, struct \ref receiver, struct \ref filter,
+ * struct \ref sched.
+ */
 struct writer {
 struct writer {
-       /**
-        * Prepare the fd sets for select.
-        *
-        * This is called from scheduler. It may use the sched pointer to add
-        * any file descriptors or to decrease the select timeout.
-        */
-       void (*pre_select)(struct sched *s, void *context);
-       /**
-        * Write audio data.
-        *
-        * Called from the post_select function of the writer node's task.
-        */
-       int (*post_select)(struct sched *s, void *context);
+       /** Ask the scheduler to check whether data can be written. */
+       void (*pre_monitor)(struct sched *s, void *context);
+       /** Write audio data. */
+       int (*post_monitor)(struct sched *s, void *context);
        /**
         * Close one instance of the writer.
         *
        /**
         * Close one instance of the writer.
         *
index 41c3eb23728ab11cde71f837d4c58a1ef6908d24..9a13090541895df5b7bc7ee8f42fbc2fda37a9f2 100644 (file)
@@ -85,7 +85,7 @@ int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp)
        if (!wa || !*wa) {
                writer_num = default_writer_id();
                cmd = WRITE_CMD(writer_num);
        if (!wa || !*wa) {
                writer_num = default_writer_id();
                cmd = WRITE_CMD(writer_num);
-               argv = para_malloc(2 * sizeof(char *));
+               argv = alloc(2 * sizeof(char *));
                argc = 1;
                argv[0] = para_strdup(lls_command_name(cmd));
                argv[1] = NULL;
                argc = 1;
                argv[0] = para_strdup(lls_command_name(cmd));
                argv[1] = NULL;
@@ -139,8 +139,8 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                .handler = w->execute, .context = wn));
        wn->task = task_register(&(struct task_info) {
                .name = writer_name(wn->wid),
                .handler = w->execute, .context = wn));
        wn->task = task_register(&(struct task_info) {
                .name = writer_name(wn->wid),
-               .pre_select = w->pre_select,
-               .post_select = w->post_select,
+               .pre_monitor = w->pre_monitor,
+               .post_monitor = w->post_monitor,
                .context = wn,
        }, s);
 }
                .context = wn,
        }, s);
 }
diff --git a/yy/mp.y b/yy/mp.y
index 06d76101daf42550ea192d1c963c32d5da6635d2..8df4f20ea04ebd2ea23b77ad77451e23e0022dbc 100644 (file)
--- a/yy/mp.y
+++ b/yy/mp.y
@@ -59,7 +59,7 @@ enum semantic_types {
 
 static struct mp_ast_node *ast_node_raw(int id)
 {
 
 static struct mp_ast_node *ast_node_raw(int id)
 {
-       struct mp_ast_node *node = para_malloc(sizeof(struct mp_ast_node));
+       struct mp_ast_node *node = alloc(sizeof(struct mp_ast_node));
        node->id = id;
        return node;
 }
        node->id = id;
        return node;
 }
@@ -76,7 +76,7 @@ static struct mp_ast_node *ast_node_new_unary(int id, struct mp_ast_node *child)
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 1;
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 1;
-       node->children = para_malloc(sizeof(struct mp_ast_node *));
+       node->children = alloc(sizeof(struct mp_ast_node *));
        node->children[0] = child;
        return node;
 }
        node->children[0] = child;
        return node;
 }
@@ -86,7 +86,7 @@ static struct mp_ast_node *ast_node_new_binary(int id, struct mp_ast_node *left,
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 2;
 {
        struct mp_ast_node *node = ast_node_raw(id);
        node->num_children = 2;
-       node->children = para_malloc(2 * sizeof(struct mp_ast_node *));
+       node->children = arr_alloc(2, sizeof(struct mp_ast_node *));
        node->children[0] = left;
        node->children[1] = right;
        return node;
        node->children[0] = left;
        node->children[1] = right;
        return node;