-# 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.
# 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
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.
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
# 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 =
-# 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
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
-# 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.
#
# 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.
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
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.
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.
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
# 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 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.
# 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
# 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.
# 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
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
# 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
-# - 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
#
# 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.
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 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
-# 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.
# 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
# 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 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.
# 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.
# 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.
# 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 =
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.
#
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
-# 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
# 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
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
#
# 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.
#
# 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.
# 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.
+# 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 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.
# 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 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.
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
#---------------------------------------------------------------------------
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.
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 =
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
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
# 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
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
#---------------------------------------------------------------------------
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.
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.
Installing lopsub
~~~~~~~~~~~~~~~~~
- git clone git://git.tuebingen.mpg.de/lopsub
+ git clone https://git.tuebingen.mpg.de/lopsub
cd lopsub && make && sudo make install
- (see http://people.tuebingen.mpg.de/maan/lopsub/)
+ (see https://people.tuebingen.mpg.de/maan/lopsub/)
Installing osl
~~~~~~~~~~~~~~
- git clone git://git.tuebingen.mpg.de/osl
+ git clone https://git.tuebingen.mpg.de/osl
cd osl && make && sudo make install
- (see http://people.tuebingen.mpg.de/maan/osl/)
+ (see https://people.tuebingen.mpg.de/maan/osl/)
Installing paraslash from tarball
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For details see the user manual:
- http://people.tuebingen.mpg.de/maan/paraslash/manual.html
+ https://people.tuebingen.mpg.de/maan/paraslash/manual.html
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)
CPPFLAGS += -I$(lls_suite_dir)
CPPFLAGS += -I$(yy_build_dir)
CPPFLAGS += $(lopsub_cppflags)
+CPPFLAGS += -Wunused-macros
STRICT_CFLAGS += -fno-strict-aliasing
STRICT_CFLAGS += -ftrapv
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 ($(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
- LDFLAGS += -Wl,--gc-sections
endif
cc-option = $(shell \
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
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)
define CC_CMD
$(call SAY, CC $<)
$(CC) -c -o $(object_dir)/$(*F).o -MMD -MF \
NEWS
====
-------------------------------------------
-0.7.2 (to be announced) "optical friction"
-------------------------------------------
+---------------------------------------------
+0.7.4 (to be announced) "genetic contraction"
+---------------------------------------------
+
+Downloads:
+[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
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-git.tar.xz)
+[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"
Distribution of paraslash is covered by the GNU GPL, version 2 unless
otherwise stated. See file COPYING.
-Web page: http://people.tuebingen.mpg.de/maan/paraslash/
-Gitweb: http://git.tuebingen.mpg.de/paraslash.git/
-Git URL: git://git.tuebingen.mpg.de/paraslash.git
+Web page: https://people.tuebingen.mpg.de/maan/paraslash/
+Gitweb: https://git.tuebingen.mpg.de/paraslash.git/
+Git URL: https://git.tuebingen.mpg.de/paraslash.git
Email: Andre Noll <maan@tuebingen.mpg.de>
Comments and bug reports are welcome.
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)
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++;
int ret;
char *msg;
- if (!arg) {
- ret = mood_load(NULL, &msg);
+ 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, &msg);
+ ret = playlist_load(arg + 2, NULL, &msg);
mode = PLAY_MODE_PLAYLIST;
} else if (!strncmp(arg, "m/", 2)) {
- ret = mood_load(arg + 2, &msg);
+ ret = mood_load(arg + 2, NULL, &msg);
mode = PLAY_MODE_MOOD;
} else {
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- msg = make_message("%s: parse error", arg);
+ msg = make_message("%s: parse error\n", arg);
}
if (pb)
para_printf(pb, "%s", msg);
{
int ret = activate_mood_or_playlist(arg, NULL);
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)
activate_mood_or_playlist(NULL, NULL);
}
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;
}
ret = schedule(&s);
sched_shutdown(&s);
- mood_unload();
+ mood_unload(NULL);
+ playlist_unload(NULL);
out_close:
close_afs_tables();
out:
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();
+ mood_unload(NULL);
else
- playlist_unload();
- ret = activate_mood_or_playlist(arg, &aca->pbout);
+ 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, &aca->pbout);
+ 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, &aca->pbout);
+ activate_mood_or_playlist(NULL, pbout);
free_lpr:
lls_free_parse_result(aca->lpr, cmd);
return ret;
send_errctx(cc, errctx);
return ret;
}
- return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
+ 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);
.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) {
/* score */
extern const struct afs_table_operations score_ops;
-int score_loop(osl_rbtree_loop_func *func, void *data);
+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_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_delete(const struct osl_row *aft_row);
void score_clear(void);
void free_status_items(void);
/* mood */
-int mood_load(const char *mood_name, char **msg);
-void mood_unload(void);
+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 */
-int playlist_load(const char *name, char **msg);
-void playlist_unload(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 */
#include <sys/mman.h>
#include <fnmatch.h>
#include <sys/shm.h>
+#include <dirent.h>
#include <osl.h>
#include <lopsub.h>
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;
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;
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",
if (ret < 0)
return ret;
if (!d->hash)
- d->hash = alloc(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)
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;
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;
- int i = 0, ret;
+ int ret;
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);
-
+ r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
- if (admissible_only(opts))
- ret = score_loop(prepare_ls_row, opts);
- 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;
- 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;
}
if (ret < 0)
goto out;
time(¤t_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);
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 {
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);
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;
/* 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;
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;
- 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);
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;
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 */
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 (frames > 0)
break;
/* eof and less than a single frame available */
- ret = -E_WRITE_COMMON_EOF;
+ ret = -E_EOF;
goto fail;
}
/*
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;
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);
*
* \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 */
*/
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;
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 */
/** 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.
+/*
+ * Add a status client to the global client list and increment num_clients.
*
- * Only those status items having the bit set in \a mask will be
- * sent to the client.
- *
- * \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;
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;
}
continue;
/* write error or short write */
close_stat_client(sc);
- dump_stat_client_list();
}
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 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};
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);
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;
i9e_close();
para_log = stderr_log;
out:
+ free(ici.history_file);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
/* 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;
return false;
for (int i = 0; ct->features[i]; i++)
if (strcmp(feature, ct->features[i]) == 0)
- return i;
+ return true;
return false;
}
goto out;
ct->challenge_hash = alloc(HASH2_SIZE);
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 {
- 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);
}
- 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;
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)
- ret = -E_BTR_EOF;
+ ret = -E_EOF;
}
if (ret < 0)
goto close1;
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);
- if (!file_exists(kf)) {
+ if (stat(kf, &statbuf) != 0) { /* assume file does not exist */
free(kf);
kf = make_message("%s/.ssh/id_rsa", home);
}
#include "signal.h"
#include "version.h"
+/** \cond server_cmd_aux_info */
#define SERVER_CMD_AUX_INFO(_arg) _arg,
static const unsigned server_command_perms[] = {LSG_SERVER_CMD_AUX_INFOS};
#undef SERVER_CMD_AUX_INFO
#define SERVER_CMD_AUX_INFO(_arg) #_arg,
static const char * const server_command_perms_txt[] = {LSG_SERVER_CMD_AUX_INFOS};
#undef SERVER_CMD_AUX_INFO
+/** \endcond server_cmd_aux_info */
/** Commands including options must be shorter than this. */
#define MAX_COMMAND_LEN 32768
}
EXPORT_SERVER_CMD_HANDLER(version);
-/** These status items are cleared if no audio file is currently open. */
+/** \cond empty_status_items */
+/* These status items are cleared if no audio file is currently open. */
#define EMPTY_STATUS_ITEMS \
ITEM(path) \
ITEM(directory) \
ITEM(amplification) \
ITEM(play_time) \
+/** \endcond empty_status_items */
+
/*
* Create a set of audio-file related status items with empty values. These are
* written to stat clients when no audio file is open.
{
long promille;
int i, ret;
- char c, *errctx;
+ char *errctx;
ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
if (ret < 0) {
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)
*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++) {
- 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;
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;
AC_PREREQ([2.61])
AC_INIT([paraslash], [m4_esyscmd_s(./GIT-VERSION-GEN)],
- [maan@tuebingen.mpg.de], [], [http://people.tuebingen.mpg.de/maan/paraslash/])
+ [maan@tuebingen.mpg.de], [], [https://people.tuebingen.mpg.de/maan/paraslash/])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
The lopsub library is required to build this software, but
the above checks indicate it is not installed on your system.
Run the following command to download a copy.
- git clone git://git.tuebingen.mpg.de/lopsub.git
+ git clone https://git.tuebingen.mpg.de/lopsub.git
Install the library, then run this configure script again.
])
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])
-
+ 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],
* \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);
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;
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(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(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(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_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(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(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(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(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(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(MISSING_COLON, "syntax error: missing colon"), \
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(MP4_READ, "mp4: read error or unexpected end of file"), \
PARA_ERROR(MP4_CORRUPT, "invalid/corrupt mp4 file"), \
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(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(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(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(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(WRITE_COMMON_EOF, "end of file"), \
/**
* This is temporarily defined to expand to its first argument (prefixed by
*/
_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);
- return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
+ return weak_osl_strerror(idx);
}
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))
- return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
- return para_errlist[num];
+ return strerror(idx);
+ return para_errlist[idx];
}
/**
}
/**
- * 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.
*
- * 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
- * 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().
*/
}
/**
- * Write all data to a file descriptor.
+ * Write to a file descriptor, fail on short writes.
*
* \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.
*/
}
/**
- * 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.
*
+ * 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().
+ *
+ * \sa fprintf(3), \ref xvasprintf().
*/
__printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
{
}
/**
- * Read a buffer and check its content for a pattern.
- *
- * \param fd The file descriptor to receive from.
- * \param pattern The expected pattern.
- * \param bufsize The size of the internal buffer.
+ * Read a buffer and compare its contents to a string, ignoring case.
*
- * 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)
+int read_and_compare(int fd, const char *expectation)
{
- size_t n, len;
- char *buf = alloc(bufsize + 1);
- int ret = read_nonblock(fd, buf, bufsize, &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;
+ buf[n] = '\0';
ret = 0;
if (n == 0)
goto out;
ret = -E_READ_PATTERN;
- len = strlen(pattern);
if (n < len)
goto out;
- if (strncasecmp(buf, pattern, len) != 0)
+ if (strncasecmp(buf, expectation, len) != 0)
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;
}
-/**
- * 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);
-}
-
/**
* Set a file descriptor to blocking mode.
*
}
/**
- * Wrapper for chdir(2).
- *
- * \param path The specified directory.
+ * Create a directory, don't fail if it already exists.
*
- * \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.
- *
- * \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);
}
/**
* \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)
{
- int err;
-
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);
}
/**
}
}
}
-
-/**
- * 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;
-}
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 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);
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);
void valid_fd_012(void);
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_pattern(int fd, const char *pattern, size_t bufsize);
+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 for_each_file_in_dir(const char *dirname,
- int (*func)(const char *, void *), void *private_data);
+
/**
* Write a \p NULL-terminated buffer.
*
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;
if (ret < 0)
goto free_argv;
f = filter_get(filter_num);
+ assert(f);
*conf = f->setup? f->setup(*lprp) : NULL;
ret = filter_num;
free_argv:
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;
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) {
struct asymmetric_key {
gcry_sexp_t sexp;
- int num_bytes;
};
static const char *gcrypt_strerror(gcry_error_t gret)
}
PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
key = alloc(sizeof(*key));
- key->num_bytes = ret;
key->sexp = sexp;
*result = key;
- ret = bits;
+ ret = bits / 8;
release_n:
gcry_mpi_release(n);
release_e:
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)
}
scroll_position = 0;
redraw_bot_win();
+ print_in_bar(COLOR_MSG, " ");
}
static void com_page_down(void)
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) {
return;
}
loglevel--;
- print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+ print_ll_msg();
}
static void com_ll_incr(void)
return;
}
loglevel++;
- print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
+ print_ll_msg();
}
static void com_reread_conf(void)
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 = "";
return 0;
}
if (phd->status == HTTP_SENT_GET_REQUEST) {
- ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
+ ret = read_and_compare(rn->fd, HTTP_OK_MSG);
if (ret < 0) {
PARA_ERROR_LOG("did not receive HTTP OK message\n");
goto out;
case HTTP_STREAMING: /* nothing to do */
break;
case HTTP_CONNECTED: /* need to recv get request */
- ret = read_pattern(sc->fd, HTTP_GET_MSG, MAXLINE);
+ ret = read_and_compare(sc->fd, HTTP_GET_MSG);
if (ret < 0)
phsd->status = HTTP_INVALID_GET_REQUEST;
else if (ret > 0) {
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;
goto rm_btrn;
}
if (ret == 0) {
- ret = -E_I9E_EOF;
+ ret = -E_EOF;
goto rm_btrn;
}
buf[1] = '\0';
{
int fd = open("/proc/sys/kernel/shmmax", O_RDONLY);
if (fd >= 0) {
- char buf[100] = "";
+ char buf[100];
int ret = read(fd, buf, sizeof(buf) - 1);
if (ret > 0) {
buf[ret] = '\0';
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 admissible]
short_opt = a
summary = list only admissible files
+ arg_type = string
+ arg_info = optional_arg
+ typestr = specifier/name
+ default_val = .
[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
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
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 (!m)
return;
mp_shutdown(m->parser_context);
+ if (m->score_table)
+ score_close(m->score_table);
free(m->name);
free(m);
}
}
static int add_to_score_table(const struct osl_row *aft_row,
- const struct afs_statistics *stats)
+ struct mood_instance *m)
{
long score;
struct afs_info afsi;
if (ret < 0)
return ret;
- score = compute_score(&afsi, stats);
- 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)
ret = add_afs_statistics(aft_row, ¤t_mood->stats);
if (ret < 0)
return ret;
- return add_to_score_table(aft_row, ¤t_mood->stats);
+ return add_to_score_table(aft_row, current_mood);
}
/* update score */
ret = get_afsi_of_row(aft_row, &afsi);
unsigned n = m->stats.num;
int mean_days, sigma_days;
+ 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)
+ int_sqrt(m->stats.num_played_qd / n),
+ 86400.0 * m->stats.last_played_correction /
+ m->stats.num_played_correction
);
}
-/** Free all resources of the current mood, if any. */
-void mood_unload(void)
+/**
+ * Free all resources of a mood instance.
+ *
+ * \param m As obtained by \ref mood_load(). If NULL, unload the current mood.
+ *
+ * It's OK to call this with m == NULL even if no current mood is loaded.
+ */
+void mood_unload(struct mood_instance *m)
{
+ if (m)
+ return destroy_mood(m);
destroy_mood(current_mood);
current_mood = NULL;
}
}
/**
- * Change the current mood.
+ * Populate a score table with admissible files for the given mood.
+ *
+ * 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.
*
* \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.
*
- * 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.
+ * If the mood name is NULL, the dummy mood is loaded. This mood regards every
+ * audio file as admissible.
+ *
+ * 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.
+ *
+ * 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.
*
- * \return The number of admissible files on success, negative on errors. It is
+ * \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 struct \ref afs_info::last_played, \ref mp_eval_row().
+ * \sa \ref mp_eval_row().
*/
-int mood_load(const char *mood_name, char **msg)
+int mood_load(const char *mood_name, struct mood_instance **result, char **msg)
{
int i, ret;
struct admissible_array aa = {.size = 0};
}
clock_get_realtime(&rnow);
compute_correction_factors(rnow.tv_sec, &aa.m->stats);
- if (aa.m->stats.num == 0) {
- if (msg)
- *msg = make_message("no admissible files\n");
- ret = 0;
- goto out;
- }
+ 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->stats);
+ ret = add_to_score_table(aa.array[i], aa.m);
if (ret < 0) {
if (msg)
*msg = make_message(
if (msg)
*msg = get_statistics(aa.m, rnow.tv_sec);
ret = aa.m->stats.num;
- mood_unload();
- current_mood = aa.m;
+ if (result)
+ *result = aa.m;
+ else {
+ mood_unload(NULL);
+ current_mood = aa.m;
+ }
+ ret = 1;
out:
free(aa.array);
- if (ret < 0)
+ if (ret <= 0) /* error, or no admissible files */
destroy_mood(aa.m);
return ret;
}
+/**
+ * Iterate over the admissible files of a mood instance.
+ *
+ * This wrapper around \ref score_loop() is the mood counterpart of \ref
+ * playlist_loop().
+ *
+ * \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 list of
- * admissible files useless, for example if an attribute is removed from the
- * attribute table.
+ * 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)
{
current_mood->name : "(dummy)");
if (current_mood->name)
mood_name = para_strdup(current_mood->name);
- mood_unload();
- ret = mood_load(mood_name, NULL);
+ mood_unload(NULL);
+ ret = mood_load(mood_name, NULL, NULL);
free(mood_name);
return ret;
}
unsigned int freq;
unsigned int padding;
unsigned int mode;
- unsigned int copyright;
- unsigned int original;
- unsigned int emphasis;
};
static const int frequencies[3][4] = {
+ 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;
(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;
}
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;
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;
return ret;
}
-/** A macro defining the atoms we care about. It gets expanded twice. */
+/** \cond atom_items */
+/* 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(META, 'm', 'e', 't', 'a') /* iTunes Metadata box */ \
ATOM_ITEM(DATA, 'd', 'a', 't', 'a') /* iTunes Metadata data box */ \
+/** \endcond atom_items */
+
/** 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. */
* 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.
*/
-char *mp4_get_tag_value(const struct mp4 *f, const char *item)
+__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))
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);
-char *mp4_get_tag_value(const struct mp4 *f, const char *item);
+__malloc char *mp4_get_tag_value(const struct mp4 *f, const char *item);
* \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 *addr, ssize_t addrlen, int32_t *netmask);
char *parse_url(const char *url,
char *host, ssize_t hostlen, int32_t *port);
-char *format_url(const char *url, int default_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,
*
* \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 zalloc(sizeof(struct oac_custom_header));
}
* 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);
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;
#include <openssl/sha.h>
#include <openssl/bn.h>
#include <openssl/aes.h>
+#include <openssl/evp.h>
#include "para.h"
#include "error.h"
}
/*
- * 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)
{
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;
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;
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->iqmp = iqmp;
#endif
*result = rsa;
ret = 1;
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:
unsigned char *blob;
size_t decoded_size;
int ret;
- struct asymmetric_key *key = alloc(sizeof(*key));
+ struct asymmetric_key *pub = alloc(sizeof(*pub));
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;
- ret = RSA_size(key->rsa);
+ ret = RSA_size(pub->rsa);
assert(ret > 0);
- *result = key;
+ *result = pub;
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;
}
-void apc_free_pubkey(struct asymmetric_key *key)
+void apc_free_pubkey(struct asymmetric_key *pub)
{
- if (!key)
+ if (!pub)
return;
- RSA_free(key->rsa);
- free(key);
+ RSA_free(pub->rsa);
+ free(pub);
}
int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
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)
{
- 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);
}
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;
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;
#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 "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;
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;
}
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));
char *result;
int len;
- if (!p)
- return NULL;
+ assert(p);
len = p - kma;
result = alloc(len + 1);
memcpy(result, kma, len);
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 */
- 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);
/**
* 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[])
{
/** \file playlist.c Functions for loading and saving playlists. */
-/** Structure used for adding entries to a playlist. */
+/**
+ * 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;
+ /** Contains all valid paths of the playlist. */
+ struct osl_table *score_table;
};
static struct playlist_instance current_playlist;
static int add_playlist_entry(char *path, void *data)
{
- struct playlist_instance *playlist = data;
+ struct playlist_instance *pi = data;
struct osl_row *aft_row;
int ret = aft_get_row_of_path(path, &aft_row);
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;
}
- playlist->length++;
+ pi->length++;
return 1;
}
check_playlist));
}
-/** Free all resources of the current playlist, if any. */
-void playlist_unload(void)
+/**
+ * Free all resources of the given/current playlist.
+ *
+ * \param pi NULL means to unload the current playlist.
+ */
+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;
+ score_clear();
free(current_playlist.name);
current_playlist.name = NULL;
current_playlist.length = 0;
* corresponding row of the audio file table is added to the score table.
*
* \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 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_load(const char *name, char **msg)
+int playlist_load(const char *name, struct playlist_instance **result, char **msg)
{
int ret;
- struct playlist_instance *playlist = ¤t_playlist;
+ struct playlist_instance *pi;
struct osl_object playlist_def;
- ret = pl_get_def_by_name(name, &playlist_def);
- if (ret < 0) {
- *msg = make_message("could not read playlist %s\n", name);
- return ret;
+ if (!name || !*name) {
+ if (msg)
+ *msg = make_message("empty playlist name\n");
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
}
- playlist_unload();
+ 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, playlist);
+ playlist_def.size, add_playlist_entry, pi);
osl_close_disk_object(&playlist_def);
if (ret < 0)
- goto err;
+ goto close_score_table;
ret = -E_PLAYLIST_EMPTY;
- if (!playlist->length)
- goto err;
- playlist->name = para_strdup(name);
- *msg = make_message("loaded playlist %s (%u files)\n", playlist->name,
- playlist->length);
+ if (pi->length == 0)
+ goto close_score_table;
/* success */
- return current_playlist.length;
+ 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;
+ }
+ 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);
- *msg = make_message("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)
{
if (strcmp(path, data))
}
/* !was_admissible && is_admissible */
current_playlist.length++;
- return score_add(row, 0); /* play it immediately */
+ return score_add(row, 0, NULL); /* play it immediately */
}
/**
}
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;
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);
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
};
/* 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;
- 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;
}
/**
- * 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().
*/
-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];
*(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);
ret = osl(osl_get_nth_row(score_table, SCORECOL_SCORE, new_pos, &rrow));
if (ret < 0)
return ret;
- ret = get_score_of_row(rrow, &new_score);
+ ret = get_score_of_row(score_table, rrow, &new_score);
if (ret < 0)
return ret;
new_score--;
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;
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)};
- 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));
}
/**
* Call the given function for each row of the score table.
*
* \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.
*
* \return The return value of the underlying call to osl_rbtree_loop(). The
* loop terminates early if the callback returns negative.
*/
-int score_loop(osl_rbtree_loop_func *func, void *data)
+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));
}
/**
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);
}
/**
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;
bool row_belongs_to_score_table(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 == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
return false;
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)
+{
+ assert(osl_close_table(t? t : score_table, OSL_FREE_VOLATILE) >= 0);
+}
+
+static void close_global_table(void)
{
- osl_close_table(score_table, OSL_FREE_VOLATILE);
- score_table = NULL;
+ score_close(NULL);
}
-static int score_open(__a_unused const char *dir)
+static int open_global_table(__a_unused const char *dir)
{
- assert(osl_open_table(&score_table_desc, &score_table) >= 0);
+ assert(osl(osl_open_table(&score_table_desc, &score_table)) >= 0);
return 1;
}
+/**
+ * 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)
+{
+ if (result)
+ assert(osl(osl_open_table(&score_table_desc, result)) >= 0);
+ else
+ open_global_table(NULL);
+}
+
/**
* Remove all entries from the score table, but keep the table open.
*/
void score_clear(void)
{
- score_close();
- score_open(NULL);
+ 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 = score_open,
- .close = score_close,
+ .open = open_global_table,
+ .close = close_global_table,
};
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_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);
+__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);
*
* \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;
* \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"
}
/**
- * 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
- * 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)
{
- 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);
}
/**
static int create_argv_offset(int offset, const char *buf, const char *delim,
char ***result)
{
- char *word, **argv = arr_alloc(offset + 1, sizeof(char *));
+ char *word, **argv = arr_zalloc(offset + 1, sizeof(char *));
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;
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);
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'
say_color()
{
+ local severity=$1
+
+ shift
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)
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
[[ -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"
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;
- return -E_RECV_EOF;
+ return -E_EOF;
}
static int udp_recv_post_monitor(__a_unused struct sched *s, void *context)
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,
int32_t rate, ch;
if (iqs == 0) {
- ret = -E_WAV_EOF;
+ ret = -E_EOF;
if (btr_no_parent(btrn))
goto err;
return 0;
<p> Author: Andre Noll, <a
href="mailto:maan@tuebingen.mpg.de">maan@tuebingen.mpg.de</a>,
Homepage: <a
-href="http://people.tuebingen.mpg.de/maan/">http://people.tuebingen.mpg.de/maan/</a>
+href="https://people.tuebingen.mpg.de/maan/">https://people.tuebingen.mpg.de/maan/</a>
</p>
<p> Comments and bug reports are welcome. Please provide the version
Clone the git repository by executing
<pre> <kbd>
- git clone git://git.tuebingen.mpg.de/paraslash.git
+ git clone https://git.tuebingen.mpg.de/paraslash.git
</kbd> </pre>
<p> The repository contains the full history of the
The
- <a href="http://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
+ <a href="https://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
page contains a snapshot link for each revision. This
allows getting a specific revision without downloading
In this chapter we give an [overview](#Overview) of the interactions
of the programs contained in the paraslash package, followed by
-[brief descriptions](#The.paraslash.executables) of all executables.
+[brief descriptions](#The-paraslash-executables) of all executables.
Overview
--------
including information found in (ID3) tags. Simple playlists are also
supported. It is possible to store images (album covers) and lyrics
in the database and associate these to the corresponding audio files.
-The section on the [audio file selector](#The.audio.file.selector)
+The section on the [audio file selector](#The-audio-file-selector)
discusses this topic in more detail.
Another component of para_server is the virtual streaming system,
------------
<h3> For the impatient </h3>
- git clone git://git.tuebingen.mpg.de/lopsub
+ git clone https://git.tuebingen.mpg.de/lopsub
cd lopsub && make && sudo make install
- git clone git://git.tuebingen.mpg.de/osl
+ git clone https://git.tuebingen.mpg.de/osl
cd osl && make && sudo make install && sudo ldconfig
sudo apt-get install autoconf libssl-dev m4 \
libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
file parsers for all paraslash executables. Clone the source code
repository with
- git clone git://git.tuebingen.mpg.de/lopsub
+ git clone https://git.tuebingen.mpg.de/lopsub
- [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
[clang](http://clang.llvm.org). All gcc versions >= 5.4 are currently
Optional:
-- [libosl](http://people.tuebingen.mpg.de/maan/osl/). The _object
+- [libosl](https://people.tuebingen.mpg.de/maan/osl/). The _object
storage layer_ library is used by para_server. To clone the source
code repository, execute
- git clone git://git.tuebingen.mpg.de/osl
+ git clone https://git.tuebingen.mpg.de/osl
- [openssl](https://www.openssl.org/) or
[libgcrypt](ftp://ftp.gnupg.org/gcrypt/libgcrypt/). At least one
on debian systems) as well.
- [flex](https://github.com/westes/flex) and
-[bison](https://www.gnu.org/software/bison) are needed to build the
+[bison](https://www.gnu.org/software/bison/) are needed to build the
mood parser of para_server. The build system will skip para_server
if these tools are not installed.
para_gui. Debian package: `libncurses-dev`.
- [GNU
-Readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html). If
+Readline](https://www.gnu.org/software/readline/). If
this library (`libreadline-dev`) is installed, para_client, para_audioc
and para_play support interactive sessions.
Since no playlist has been specified yet, the "dummy" mode which
selects all known audio files is activated automatically. See the
-section on the [audio file selector](#The.audio.file.selector) for how
+section on the [audio file selector](#The-audio-file-selector) for how
to use playlists and moods to specify which files should be streamed
in which order.
the AES stream cipher in integer counter mode.
In this chapter we briefly describe RSA and AES, and sketch the
-[authentication handshake](#Client-server.authentication)
+[authentication handshake](#Client-2d-server-authentication)
between para_client and para_server. User management is discussed
-in the section on [the user_list file](#The.user_list.file).
+in the section on [the user_list file](#The-user_list-file).
These sections are all about communication between the client and the
server. Connecting para_audiod is a different matter and is described
-in a [separate section](#Connecting.para_audiod).
+in a [separate section](#Connecting-para_audiod).
RSA and AES
-----------
Neither para_server or para_client create RSA keys on their
own. This has to be done once for each user as sketched in
-[Quick start](#Quick.start) and discussed in more detail
-[below](#The.user_list.file).
+[Quick start](#Quick-start) and discussed in more detail
+[below](#The-user_list-file).
The user_list file
------------------
that can be associated with one or more audio files.
In this chapter we sketch the setup of the [AFS
-process](#The.AFS.process) during server startup and proceed with the
-description of the [layout](#Database.layout) of the various database
-tables. The section on [playlists and moods](#Playlists.and.moods)
+process](#The-AFS-process) during server startup and proceed with the
+description of the [layout](#Database-layout) of the various database
+tables. The section on [playlists and moods](#Playlists-and-moods)
explains these two audio file selection mechanisms in detail
and contains practical examples. The way [file renames and content
-changes](#File.renames.and.content.changes) are detected is discussed
-briefly before the [Troubleshooting](#Troubleshooting) section
+changes](#File-renames-and-content-changes) are detected is discussed
+briefly before the [Troubleshooting](#Common-problems) section
concludes the chapter.
The AFS process
to the database.
Note that AFS employs
-[libosl](http://people.tuebingen.mpg.de/maan/osl/), the object
+[libosl](https://people.tuebingen.mpg.de/maan/osl/), the object
storage layer library, as the database backend. This library offers
functionality similar to a relational database, but is much more
lightweight than a full featured database management system.
It is possible to change the behaviour of the add command by using the
"-l" (lazy add) or the "-f" (force add) option.
-Troubleshooting
+Common problems
---------------
Use the debug loglevel (-l debug) to show debugging info. All paraslash
HTTP) can be chosen.
The chapter starts with the [control
-service](#The.paraslash.control.service), followed by a section
-on the various [streaming protocols](#Streaming.protocols)
+service](#The-paraslash-control-service), followed by a section
+on the various [streaming protocols](#Streaming-protocols)
in which the data connections are described. The way
audio file headers are embedded into the stream is discussed
-[briefly](#Streams.with.headers.and.headerless.streams) before the
-[example section](#Networking.examples) which illustrates typical
+[briefly](#Streams-with-headers-and-headerless-streams) before the
+[example section](#Networking-examples) which illustrates typical
commands for real-life scenarios.
Both IPv4 and IPv6 are supported.
fact, the child process can not change address space of server process.
The section on [client-server
-authentication](#Client-server.authentication) above described the
+authentication](#Client-2d-server-authentication) above described the
early connection establishment from the crypto point of view. Here
it is described what happens after the connection (including crypto
setup) has been established. There are four processes involved during
- UDP. Recommended for multicast LAN streaming.
-See the Appendix on [network protocols](#Network.protocols)
+See the Appendix on [network protocols](#Network-protocols)
for brief descriptions of the various protocols relevant for network
audio streaming with paraslash.
and may also be configured through the "sender" command. The FEC
parameters are encoded in the header of each network packet, so no
configuration is necessary on the receiver side. See the section on
-[FEC](#Forward.error.correction) below.
+[FEC](#Forward-error-correction) below.
Streams with headers and headerless streams
-------------------------------------------
Forward error correction
------------------------
-As already mentioned [earlier](#Streaming.protocols), paraslash
+As already mentioned [earlier](#Streaming-protocols), paraslash
uses forward error correction (FEC) for the unreliable UDP and
DCCP transports. FEC is a technique which was invented already in
1960 by Reed and Solomon and which is widely used for the parity
The "." and "," keys are used to switch between themes.
-Examples
---------
+Gui examples
+------------
-> Show server info:
paraslash project, some additional tools should be installed on a
developer machine.
-- [git](http://git.or.cz/). As described in more detail
-[below](#Git.branches), the git source code management tool is used for
+- [git](https://git-scm.com/). As described in more detail
+[below](#Git-branches), the git source code management tool is used for
paraslash development. It is necessary for cloning the git repository
and for getting updates.
written in the Markdown markup language and are translated into html
with the converter of the *Discount* package.
-- [doxygen](http://www.stack.nl/~dimitri/doxygen/). The documentation
+- [doxygen](https://www.doxygen.nl/). The documentation
of paraslash's C sources uses the doxygen documentation system. The
conventions for documenting the source code is described in the
[Doxygen section](#Doxygen).
References
==========
-Articles
---------
-- [Polynomial Codes over Certain Finite
-Fields](http://kom.aau.dk/~heb/kurser/NOTER/KOFA01.PDF) by Reed, Irving
-S.; Solomon, Gustave (1960), Journal of the Society for Industrial
-and Applied Mathematics (SIAM) 8 (2): 300-304, doi:10.1137/0108018)
-
RFCs
----
-- [RFC 768](http://www.ietf.org/rfc/rfc768.txt) (1980): User Datagram
+- [RFC 768](https://www.ietf.org/rfc/rfc768.txt) (1980): User Datagram
Protocol
-- [RFC 791](http://www.ietf.org/rfc/rfc791.txt) (1981): Internet
+- [RFC 791](https://www.ietf.org/rfc/rfc791.txt) (1981): Internet
Protocol
-- [RFC 2437](http://www.ietf.org/rfc/rfc2437.txt) (1998): RSA
+- [RFC 2437](https://www.ietf.org/rfc/rfc2437.txt) (1998): RSA
Cryptography Specifications
-- [RFC 4340](http://www.ietf.org/rfc/rfc4340.txt) (2006): Datagram
+- [RFC 4340](https://www.ietf.org/rfc/rfc4340.txt) (2006): Datagram
Congestion Control Protocol (DCCP)
-- [RFC 4341](http://www.ietf.org/rfc/rfc4341.txt) (2006): Congestion
+- [RFC 4341](https://www.ietf.org/rfc/rfc4341.txt) (2006): Congestion
Control ID 2: TCP-like Congestion Control
-- [RFC 4342](http://www.ietf.org/rfc/rfc4342.txt) (2006): Congestion
+- [RFC 4342](https://www.ietf.org/rfc/rfc4342.txt) (2006): Congestion
Control ID 3: TCP-Friendly Rate Control (TFRC)
-- [RFC 6716](http://www.ietf.org/rfc/rfc6716.txt) (2012): Definition
+- [RFC 6716](https://www.ietf.org/rfc/rfc6716.txt) (2012): Definition
of the Opus Audio Codec
Application web pages
---------------------
-- [paraslash](http://people.tuebingen.mpg.de/maan/paraslash/)
-- [xmms](https://xmms2.org/wiki/Main_Page)
+- [paraslash](https://people.tuebingen.mpg.de/maan/paraslash/)
- [mpg123](http://www.mpg123.de/)
- [gstreamer](https://gstreamer.freedesktop.org/)
-- [icecast](http://www.icecast.org/)
-- [Audio Compress](https://beesbuzz.biz/code/audiocompress.php)
+- [icecast](https://www.icecast.org/)
+- [Audio Compress](https://github.com/fluffy-critter/audiocompress)
External documentation
----------------------
-- [The mathematics of
-Raid6](https://www.kernel.org/pub/linux/kernel/people/hpa/raid6.pdf)
-by H. Peter Anvin
-
- [Effective Erasure Codes for reliable Computer Communication
Protocols](http://info.iet.unipi.it/~luigi/fec_ccr.ps.gz) by Luigi
Rizzo
font-family: sans-serif;
background-color: black;
color: #bbbbbb;
+ text-align: justify;
+ padding: 0px 40px 40px 40px;
margin: 20px;
}
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) {
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));