]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'master' into my-osx
authormaan <maan@Gumbo.local>
Thu, 15 Jun 2006 08:17:41 +0000 (10:17 +0200)
committermaan <maan@Gumbo.local>
Thu, 15 Jun 2006 08:17:41 +0000 (10:17 +0200)
31 files changed:
INSTALL
Makefile.in
README
README.mysql
audioc.c
audioc.ggo
audiod.c
client.c
client.h [new file with mode: 0644]
client_common.c [new file with mode: 0644]
command.c
configure.ac
dccp.c
dccp_recv.c
error.h
fd.c
filter.c
http_recv.c
net.c
net.h
recv.c
recv_common.c
sched.c
sched.h
skencil/overview.sk
stdin.c
stdin.h
stdout.c
stdout.h
string.c
write.c

diff --git a/INSTALL b/INSTALL
index 9e53e1f3dbeccf49b6ee8647673823ee54bef7c2..35914c81688be69720bcc8f6d3b6e8ba65fb6a0c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,18 +1,22 @@
-Paraslash install notes
-=======================
+INSTALL
+=======
 
+----
 Any knowledge of how to work with mouse and icons is not required.
 
+---------------------------
 Install all needed packages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+---------------------------
+
 See README for a list of required software. Don't be afraid of the long
 list of unusal libraries: Most of them are only needed for optional
 programs. Autoconf will detect what is installed on your system and
 will only build those executables that can be built with your setup.
 
-
+------------------------
 Install server and client
-~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------
+
 Install the package on all machines, you'd like this software to run on:
 
        (./configure && make) > /dev/null
@@ -22,8 +26,10 @@ software). Then, as root,
 
        make install
 
+-----------------------------------
 Setup user list and create rsa keys
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------------------
+
 If you already have your rsa keys, skip this step. If you are new
 to paraslash, you have to generate an rsa key pair for each user you
 want to allow to connect. You need at least one user.
@@ -62,10 +68,13 @@ server_host=client_host):
 
 Finally, tell para_client to connect to server_host:
 
-       echo 'hostname server_host' > ~/.paraslash/client.conf
+       conf=~/.paraslash/client.conf
+       echo 'hostname server_host' > $conf
 
+-----------------
 Start para_server
-~~~~~~~~~~~~~~~~~
+-----------------
+
 For the first try, we'll use the default audio file selector, the
 "random" selector which chooses audio files from the given directory
 by random. You have to tell para_server via the --random_dir command
@@ -82,8 +91,9 @@ example) and try
 
 to retrieve the list of available commands and some server info.
 
+------------------------
 Start streaming manually
-~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------
 
        para_client play
        para_client stat 2
@@ -95,7 +105,8 @@ You should now be able to receive and listen to the stream. To check
 this, try the following on client_host (assuming alsa and an mp3
 stream):
 
-       para_recv -r 'http -i server_host' > file.mp3 #interrupt after a few seconds
+       para_recv -r 'http -i server_host' > file.mp3
+       # (interrupt after a few seconds)
        ls -l file.mp3 # should not be empty
        para_filter -f mp3dec -f wav < file.mp3 > file.wav
        ls -l file.wav # should be much bigger than file.mp3
@@ -107,38 +118,41 @@ and para_write to increase verbosity.
 
 Next, put the pieces together:
 
-       para_recv -r 'http -i server_host' | para_filter -f mp3dec -f wav | para_write -w alsa
+       para_recv -r 'http -i server_host' \
+               | para_filter -f mp3dec -f wav \
+               | para_write -w alsa
        or
        mpg123 http://server_host:8000/
        or
        xmms http://server_host:8000/
 
+-----------------------------
 Choose an audio file selector
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------------
+
 paraslash has three different audio file selectors: random (default),
 playlist and mysql.
 
-       The random selector chooses files randomly from the given
-       directory.
-
-       The playlist selector allows to send a playlist to para_server
-       via the lpl (load playlist) command. para_server will choose
-       files from the loaded playlist in sequential order.
-
-       The mysql selector stores information about your audio
-       files in a mysql database. It is much more involved than
-       the other two selectors and lets you chose files in many
-       interesting ways. If you like to use the mysql selector,
-       read README.mysql and follow the instructions given there.
-       Return to this document when ready.
+       - The random selector chooses files randomly from the given
+         directory.
+       - The playlist selector allows to send a playlist to para_server
+         via the lpl (load playlist) command. para_server will choose
+         files from the loaded playlist in sequential order.
+       - The mysql selector stores information about your audio
+         files in a mysql database. It is much more involved than
+         the other two selectors and lets you chose files in many
+         interesting ways. If you like to use the mysql selector,
+         read README.mysql and follow the instructions given there.
+         Return to this document when ready.
 
 The current audio file selector can be changed at runtime via
 
        para_client chs new_selector
 
-
+---------------------
 Configure para_audiod
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
+
 In order to automatically start the right decoder at the right time
 and to offer to the clients some information on the current audio
 stream and on paraslash's internal state, you should run the local
@@ -180,31 +194,26 @@ That should dump some information to stdout. Other commands include
        para_audioc term
        para_audioc cycle
 
-
+--------------
 Start para_gui
-~~~~~~~~~~~~~~
+--------------
+
 para_gui reads the output of "para_audioc stat" and displays that
 information in a curses window. It also allows you to bind keys to
 arbitrary commands. There are several flavours of key-bindings:
 
-       o internal: These are the built-in commands that can not be
-       changed (help, quit, loglevel, version...).
-
-       o external: Shutdown curses before launching the given command.
-       Useful for starting other ncurses programs from within
-       para_gui, e.g. aumix or para_dbadm. Or, use
-
-               para_client mbox
-
-       to write a mailbox containing one mail for each file
-       in the mysql database and start mutt from within para_gui
-       to browse your collection!
-
-       o display: Launch the command and display its stdout in
-       para_gui's bottom window.
-
-       o para: Like display, but start "para_client <specified
-       command>" instead of "<specified command>".
+       - internal: These are the built-in commands that can not be
+         changed (help, quit, loglevel, version...).
+       - external: Shutdown curses before launching the given command.
+         Useful for starting other ncurses programs from within
+         para_gui, e.g. aumix or para_dbadm. Or, use "para_client
+         mbox" to write a mailbox containing one mail for each file
+         in the mysql database and start mutt from within para_gui
+         to browse your collection!
+       - display: Launch the command and display its stdout in
+         para_gui's bottom window.
+       - para: Like display, but start "para_client <specified
+         command>" instead of "<specified command>".
 
 
 That's all, congratulations. Check out all the other optional gimmics!
index bf571ef4d7d4b0c417abe5a051ad989c665e1deb..ed887fa4ba7ddaee7da86a7f809ec66177ff58b2 100644 (file)
@@ -141,7 +141,7 @@ grab_client.cmdline.h grab_client.cmdline.c: grab_client.ggo
                --func-name $(subst _write.ggo,,$<)_cmdline_parser < $<
 
 %.cmdline.h %.cmdline.c: %.ggo
-       case $< in client.ggo) O="--unamed-opts=command";; \
+       case $< in client.ggo) O="--unamed-opts=command --arg-struct-name=$(subst .ggo,,$<)_args_info";; \
                audioc.ggo) O="--unamed-opts=command";; \
        esac; \
        gengetopt $$O --conf-parser --file-name=$(*F).cmdline --set-package="para_$(subst .cmdline,,$(*F))" --set-version="$V"  < $<
diff --git a/README b/README
index 017bc0c31d71e4d984783d5d0a6a2e3a46a2fc14..a1e8adc1391103366d3b4fe1a481f6fe2566d6e9 100644 (file)
--- a/README
+++ b/README
-Paraslash README
-================
+README
+======
+
+----
 Paraslash is an acronym for
 
-       Play, archive, rate and stream large audio sets happily
+_Play, archive, rate and stream large audio sets happily_
 
 It contains the following programs:
 
-- para_server (obligatory):
+-----------------------
+para_server (obligatory)
+-----------------------
 
-       This server supports audio streaming of mp3, ogg vorbis and
-       aac files.  It listens on a tcp port and accepts commands
-       such as play, stop, pause, next from authenticated clients.
+This server supports audio streaming of mp3, ogg vorbis and aac files.
+It listens on a tcp port and accepts commands such as play, stop,
+pause, next from authenticated clients.
 
-       The audio stream may be sent over the network by three
-       different senders:
+The audio stream may be sent over the network by three different
+senders:
 
-       The http sender is recommended for public streams that can
-       be played by any player like mpg123, xmms, winamp...
+The http sender is recommended for public streams that can be played
+by any player like mpg123, xmms, winamp...
 
-       The dccp sender is experimental and requires kernel support
-       for the rather new datagram congestion control protocol.
+The dccp sender is experimental and requires kernel support for the
+rather new datagram congestion control protocol.
 
-       The ortp sender is recommended for LAN streaming and for
-       private streams that require authentication.
+The ortp sender is recommended for LAN streaming and for private
+streams that require authentication.
 
-       It is possible to activate more than one sender simultaneously.
+It is possible to activate more than one sender simultaneously.
 
-       para_server needs an "audio file selector" to work, mainly
-       to determine which song to stream next. There are three
-       selectors available: random, playlist and mysql. The former
-       chooses audio files randomly and  playlist can handle, well,
-       playlists. Both are always supported.
+para_server needs an "audio file selector" to work, mainly to determine
+which song to stream next. There are three selectors available:
+random, playlist and mysql. The former chooses audio files randomly
+and  playlist can handle, well, playlists. Both are always supported.
 
-       The optional mysql selector connects to a mysql server which
-       holds information on your audio files. It has several unusual
-       features, see README.mysql for details.
+The optional mysql selector connects to a mysql server which holds
+information on your audio files. It has several unusual features,
+see README.mysql for details.
 
-- para_client (obligatory):
+------------------------
+para_client (obligatory)
+-----------------------
 
-       The client program to connect to para_server.
+The client program to connect to para_server.
 
-- para_recv (optional)
+-------------------
+para_recv (optional)
+-------------------
 
-       A command line http/dccp/rtp stream grabber.
+A command line http/dccp/rtp stream grabber.
 
-- para_filter (optional)
+---------------------
+para_filter (optional)
+---------------------
 
-       A filter program that converts from stdin and writes to
-       stdout. This one is independent from the rest of paraslash,
-       so it might be useful also for different purposes.
+A filter program that converts from stdin and writes to stdout. This
+one is independent from the rest of paraslash, so it might be useful
+also for different purposes.
 
-       para_filter combines several decoders (mp3, oggvorbis, aac)
-       and a volume normalzer. New filters can be added easily due
-       to the modular design. If more than one filter is specified,
-       the given filters are 'piped' together in-memory, i.e. without
-       calling any of the read(2)/write(2)/select(2) etc. functions.
+para_filter combines several decoders (mp3, oggvorbis, aac) and a
+volume normalzer. New filters can be added easily due to the modular
+design. If more than one filter is specified, the given filters
+are 'piped' together in-memory, i.e. without calling any of the
+read(2)/write(2)/select(2) etc. functions.
 
-- para_write (obligatory)
+----------------------
+para_write (obligatory)
+----------------------
 
-       A modular audio stream writer. It supports a simple file
-       writer output plugin and an optional wav/raw player for alsa.
-       Debian package: libasound2-dev
+A modular audio stream writer. It supports a simple file writer output
+plugin and an optional wav/raw player for alsa.  Debian package:
+libasound2-dev
 
-- para_audiod (optional, but recommended):
+---------------------------------------
+para_audiod (optional, but recommended)
+---------------------------------------
 
-       The local daemon that collects information from para_server. It
-       starts an appropriate receiver, filter and player as soon
-       as para_server announces the availability (and the type) of
-       an audio stream. para_audiod listens on a local socket and
-       sends status information about para_server and para_audiod
-       to local clients on request.
+The local daemon that collects information from para_server. It
+starts an appropriate receiver, filter and player as soon as
+para_server announces the availability (and the type) of an audio
+stream. para_audiod listens on a local socket and sends status
+information about para_server and para_audiod to local clients
+on request.
 
-- para_audioc (optional, but recommended)
+--------------------------------------
+para_audioc (optional, but recommended)
+--------------------------------------
 
-       The client program which talks with para_audiod. Used to
-       control para_audiod, to receive status info, or to grab the
-       stream at any point in the filter chain.
+The client program which talks with para_audiod. Used to control
+para_audiod, to receive status info, or to grab the stream at any
+point in the filter chain.
 
-       para_audioc (hence para_audiod) is needed by para_gui,
-       para_sdl_gui and para_krell, see below.
+para_audioc (hence para_audiod) is needed by para_gui, para_sdl_gui
+and para_krell, see below.
 
-- para_gui (optional):
+-------------------
+para_gui (optional)
+-------------------
 
-       Themable ncurses-based gui. It calls para_audioc and presents
-       the obtained information in an ncurses window. para_gui
-       provides key-bindings for the most common commands and new
-       key-bindings can be added easily.
+Themable ncurses-based gui. It calls para_audioc and presents
+the obtained information in an ncurses window. para_gui provides
+key-bindings for the most common commands and new key-bindings can
+be added easily.
 
-- para_sdl_gui (optional):
+-----------------------
+para_sdl_gui (optional)
+-----------------------
 
-       SDL-based gui. Similar to para_gui but presents its output
-       in an X window (fullscreen mode is also available) and can
-       display jpg images on a per song basis. para_sdl_gui provides
-       an input prompt to enter arbitrary commands. However, it
-       can also be used non-interactively (e.g. as a screen saver)
-       via the -i switch.
+SDL-based gui. Similar to para_gui but presents its output in an X
+window (fullscreen mode is also available) and can display jpg images
+on a per song basis. para_sdl_gui provides an input prompt to enter
+arbitrary commands. However, it can also be used non-interactively
+(e.g. as a screen saver) via the -i switch.
 
-- para_krell (optional, only useful in conjunction with the mysql selector):
+-------------------------------------------------------------------------
+para_krell (optional, only useful in conjunction with the mysql selector)
+-------------------------------------------------------------------------
 
-       A plugin for gkrellm which shows small pictures of the
-       current song. It allows you to launch 27 different commands
-       by clicking in different areas of its picture (9 small squares
-       x 3 mouse buttons).
+A plugin for gkrellm which shows small pictures of the current song. It
+allows you to launch 27 different commands by clicking in different
+areas of its picture (9 small squares x 3 mouse buttons).
 
-- para_fade (optional):
+--------------------
+para_fade (optional)
+--------------------
 
-       A (Linux-only) alarm clock and volume-fader.
+A (Linux-only) alarm clock and volume-fader.
 
-- para_dbadm (optional, only useful in conjunction with the mysql selector):
+-------------------------------------------------------------------------
+para_dbadm (optional, only useful in conjunction with the mysql selector)
+-------------------------------------------------------------------------
 
-       Very simple curses-based frontend which uses libmenu. Useful
-       for quickly changing the attributes of the current song
-       (e.g. from para_gui as an external command).
+Very simple curses-based frontend which uses libmenu. Useful for
+quickly changing the attributes of the current song (e.g. from para_gui
+as an external command).
 
-- para_slider (optional, only useful in conjunction with the mysql selector):
+--------------------------------------------------------------------------
+para_slider (optional, only useful in conjunction with the mysql selector)
+--------------------------------------------------------------------------
 
-       A small X application which shows a scrollbar for each
-       attribute defined in the mysql database. It creates a stream
-       definition from the values of the scrollbars. This allows
-       to smoothly change the mood of the given stream without any
-       file editing.
+A small X application which shows a scrollbar for each attribute
+defined in the mysql database. It creates a stream definition from
+the values of the scrollbars. This allows to smoothly change the mood
+of the given stream without any file editing.
 
-- bash_completion (optional):
+--------------------------
+bash_completion (optional)
+--------------------------
 
-       A small bash script for inclusion in ~/.bashrc. It gives you
-       command line completion for some paraslash commands.
+A small bash script for inclusion in ~/.bashrc. It gives you command
+line completion for some paraslash commands.
+
+------------
+Requirements
+------------
 
-REQUIREMENTS:
-~~~~~~~~~~~~~
 In any case you need
 
        - gcc, the gnu compiler collection (shipped with distro): gcc-3.3
-       or newer is required.
-
+         or newer is required.
        - openssl (needed by server, client): usually shipped with
-       distro, but you might have to install the "development"
-       package as well
-
-               http://www.openssl.org/
-
+         distro, but you might have to install the "development"
+         package as well: http://www.openssl.org/
        - software mixing, e.g. ALSA and the direct mixing plugin (dmix)
 
 If you want to use the mysql-based audio file selector, you also need
@@ -150,70 +174,46 @@ If you want to use the mysql-based audio file selector, you also need
 These are usually shipped with the distro but probably not installed
 by default.
 
-The mp3 decoder of para_filter is based on libmad:
-
-               http://www.underbit.com/products/mad/
-
-If you prefer to use the libmad package provided by your distributor,
-make sure to install the corresponding development package as well.
+The mp3 decoder of para_filter is based on libmad: If you prefer to
+use the libmad package provided by your distributor, make sure to
+install the corresponding development package as well.  Otherwise,
+just download libmad from http://www.underbit.com/products/mad/
 
-If you want to stream ogg vorbis files you'll need:
-
-       - libogg, libvorbis, libvorbisfile, and a command line ogg vorbis
-       decoder, e.g. para_filter or ogg123.
-
-               http://www.xiph.org/downloads/
-
-Debian packages: libogg-dev libvorbis-dev
+For ogg vorbis streams you'll need libogg, libvorbis, libvorbisfile,
+and a command line ogg vorbis decoder, e.g. para_filter or ogg123.
+The corresponding Debian packages are called libogg-dev libvorbis-dev,
+other distributors chose similar names.  All of these are also
+available at http://www.xiph.org/downloads/
 
 For aac files (m4a) you'll need libfaad. Get it at
+http://www.audiocoding.com/modules/wiki/?page=AAC
 
-       http://www.audiocoding.com/modules/wiki/?page=AAC
-
-Note that para_audiod still works even if no audio file support was
-compiled in. You'll have to use the --no_default_filters option in this
-case (and e.g. "mpg123 -" as the stream write command for mp3 streams).
-
-If you intend to use the optional ortp streamer:
-
-       - libortp
-
-               http://www.linphone.org/ortp/
-
+If you intend to use the optional ortp streamer, you'll need libortp:
+http://www.linphone.org/ortp/
 
 For the optional SDL-based gui, the following packages must be installed:
 
-       - X (usually shipped with distro)
-
-               http://www.x.org/
-
-       - libSDL (usually shipped with distro)
-
-               http://www.libsdl.org/index.php
-
-       - SDL_image
+       - X (usually shipped with distro): http://www.x.org/
+       - libSDL (usually shipped with distro): http://www.libsdl.org/index.php
+       - SDL_image: http://www.libsdl.org/projects/SDL_image/
 
-               http://www.libsdl.org/projects/SDL_image/
-
-For para_slider, the zero memory widget library is neccessary. Get it at
-
-               http://www710.univ-lyon1.fr/~exco/ZMW/
+For para_slider, the zero memory widget library is neccessary:
+http://www710.univ-lyon1.fr/~exco/ZMW/
 
 Finally, para_krell needs
 
-       - gtk2
-
-               http://www.gtk.org/
+       - gtk2: http://www.gtk.org/
+       - gkrellm2: http://members.dslextreme.com/users/billw/gkrellm/gkrellm.html
 
-       - gkrellm2
+-------
+LICENSE
+-------
 
-               http://members.dslextreme.com/users/billw/gkrellm/gkrellm.html
-
-LICENSE:
-~~~~~~~~
 Distribution of paraslash is covered by the GNU GPL. See file COPYING.
 
-THE AUTHOR:
-~~~~~~~~~~~
-Author: Andre Noll <maan@systemlinux.org>
+----------
+THE AUTHOR
+----------
+
+Andre Noll <maan@systemlinux.org>
 Comments and bug reports are welcome.
index e619a814bc827a566fc5a2df766f7246fb53d64b..87ee27b71a2c5557f8e1adf9a77d60fbf40b90eb 100644 (file)
@@ -1,6 +1,8 @@
+============
 README.mysql
 ============
 
+----
 This file describes how to use the mysql audio file selector which
 comes with the paraslash package.
 
@@ -10,23 +12,20 @@ in INSTALL, so read README and INSTALL before proceeding.
 First of all, make sure that
 
        - mysqld is running
-
        - para_server is running and compiled with mysql support
-       (type "para_client si" to find out)
-
+         (type "para_client si" to find out)
        - the user who runs para_client has the paraslash DB_WRITE
-       and DB_READ permissions set in server.users
-
+         and DB_READ permissions set in server.users
        - the user who runs para_server has create privileges on the
-       mysql server.
+         mysql server.
 
 Remember: If something doesn't work as expected, look at the server
 log file and/or increase output verbosity by using the -l switch for
 server and client.
 
-
+------------------------------------
 Specify mysql data (port, passwd,...)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------------------
 
 Type
 
@@ -45,8 +44,9 @@ effect you'll need to do
 
 Or, restart the server.
 
+---------------------------------------
 Switch to the mysql audio file selector
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+---------------------------------------
 
 The command
 
@@ -71,8 +71,9 @@ options to specify the mysql path explicitly . Example:
                --enable-mysql-libs=/Library/MySQL/lib/mysql
 
 
+---------------------
 Create a new database
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
 
 Once the mysql selector is activated, create the database:
 
@@ -82,9 +83,9 @@ Once the mysql selector is activated, create the database:
 The second command forces para_server to re-init the mysql selector.
 Check the log. There should not be any warnings or errors.
 
-
+-------------------------------
 Fill your database with content
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------------
 
        para_client upd
 
@@ -102,21 +103,21 @@ The command
 prints the list of all files known by the mysql selector. If the list
 is empty, double check the mysql_audio_file_dir option.
 
+---------------------------------------
+Create a stream which selects all files
+---------------------------------------
 
-Create a stream which selects all songs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To keep it simple, let's only define the stream "all_songs". See below for
-advanced stream usage.
+To keep it simple, let's only define the stream "all_songs". See
+below for advanced stream usage.
 
        para_client stradd all_songs < /dev/null
        para_client sl 10 all_songs
 
 The latter command should show you ten filenames.
 
-
+------------------------------
 Change to the all_songs stream
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------------
 
        para_client cs all_songs
 
@@ -124,9 +125,9 @@ You should now be able to start streaming with
 
        para_client play
 
-
+---------------
 Attribute usage
-~~~~~~~~~~~~~~~
+---------------
 
 An attribute is simply a bit which can be set for each audio file
 individually. You may have as many attributes as you like. A new
@@ -154,8 +155,9 @@ and drop the test attribute entirely from the database with
 
        para_client da test
 
+------------
 Stream usage
-~~~~~~~~~~~~
+------------
 
 A stream is a pair of expressions in terms of attributes and other data
 contained in the database. The first, boolian, expression determines
@@ -180,18 +182,19 @@ the client rather than using temporary files. Like this:
 
 
 Example:
+~~~~~~~~
 
-       Assume you already have an attribute "test" and you'd like to
-       to restrict audio streaming to those files having the "test"
-       attribute set. Define a new stream "only_test" by
+Assume you already have an attribute "test" and you'd like to to
+restrict audio streaming to those files having the "test" attribute
+set. Define a new stream "only_test" by
 
-               echo 'accept: IS_SET(test)' | para_client stradd only_test
+       echo 'accept: IS_SET(test)' | para_client stradd only_test
 
-       Then, after switching to the "only_test" stream with
+Then, after switching to the "only_test" stream with
 
-               para_client cs only_test
+       para_client cs only_test
 
-       only the desired files are going to be streamed.
+only the desired files are going to be streamed.
 
 There is no need to keep the temporary files containing the stream
 definition since you can always use the strq command to get it back:
@@ -201,20 +204,20 @@ definition since you can always use the strq command to get it back:
 The accept/deny expressions are used to find out which songs are
 permitted. The following four cases are all possible and valid:
 
-       o Neither accept nor deny lines: This selects all songs.
+       - Neither accept nor deny lines: This selects all songs.
 
-       o Only accept lines: Songs that match at least one accept
-       expression are accepted, all others are denied:
+       - Only accept lines: Songs that match at least one accept
+         expression are accepted, all others are denied:
 
                accept_expr1 or accept_expr2 or ...
 
-       o Only deny lines: Songs that match at least one deny expression are
-       denied, all others are accepted:
+       - Only deny lines: Songs that match at least one deny expression are
+         denied, all others are accepted:
 
                not (deny_expr1 or deny_expr2 ...)
 
-       o Both accept and deny lines: A song is accepted if it matches
-       at least one accept expression, but no deny expression, i.e.
+       - Both accept and deny lines: A song is accepted if it matches
+         at least one accept expression, but no deny expression, i.e.
 
                (accept_expr1 or accept_expr2 or ..) and not
                        (deny_expr1 or deny_expr2 ..)
@@ -234,19 +237,15 @@ one IS_SET() macro as in the example above. Of course, IS_SET(foo)
 is true for a given audio file if and only if it has the attribute
 "foo" set.  Here are some more macros you can use:
 
-       o IS_N_SET(attr): True if attribute attr is not set
-
-       o NAME_LIKE(string): True if basename is like (in the sense
-       of mysql) "string"
-
-       o LASTPLAYED(): Expands to number of minutes that are gone
-       since this audio file has been played (by paraslash).
-
-       o NUMPLAYED(): Expands to number of times, the file has
-       been played.
-
-       o PICID(): Expands to the number of the picture which is
-       associated with this song.
+       - IS_N_SET(attr): True if attribute attr is not set
+       - NAME_LIKE(string): True if basename is like (in the sense
+         of mysql) "string"
+       - LASTPLAYED(): Expands to number of minutes that are gone
+         since this audio file has been played (by paraslash).
+       - NUMPLAYED(): Expands to number of times, the file has
+         been played.
+       - PICID(): Expands to the number of the picture which is
+         associated with this song.
 
 To give a real-life example, suppose you have already added the
 attributes "pop", "rock" with the "na" command. Assume also that you
@@ -275,8 +274,10 @@ Accept/deny lines affect only the set of admissible audio files,
 but not the order in which these are streamed. That's where the score
 expression comes into play.
 
+-------
 Scoring
-~~~~~~~
+-------
+
 You may put a single score line anywhere in the stream definition. If
 omitted, the default scoring rule specified in the configuration file
 applies. If there is no default scoring rule in the config file either,
@@ -316,9 +317,9 @@ band "bar" some extra points:
 
        score: 40 * IS_SET(foo) + 20 * NAME_LIKE(%bar%) + LASTPLAYED()/1440
 
-
-Pictures
-~~~~~~~~
+------
+Images
+------
 
 The mysql selector can also magage images that, when associated
 with one or more audio files, can be displayed by para_sdl_gui and
index 0b0d2fb7006e6b9c60a9c0526c3d3d0259028463..1723114fc37221bc18d1480dd4c375972955d9fc 100644 (file)
--- a/audioc.c
+++ b/audioc.c
@@ -46,10 +46,6 @@ void para_log(int ll, const char* fmt,...)
        va_end(argp);
 }
 
-/* audioc does not use encryption */
-void (*crypt_function_recv)(unsigned long len, const unsigned char *indata, unsigned char *outdata) = NULL;
-void (*crypt_function_send)(unsigned long len, const unsigned char *indata, unsigned char *outdata) = NULL;
-
 static char *concat_args(const int argc, char * const *argv)
 {
        int i; char *buf = NULL;
@@ -121,7 +117,6 @@ int main(int argc, char *argv[])
        ret = - E_AUDIOC_CONNECT;
        if (connect(fd, (struct sockaddr *)&unix_addr, UNIX_PATH_MAX) < 0)
                goto out;
-       fprintf(stderr, "loaded: %d\n", loaded);
        ret = send_cred_buffer(fd, args);
        if (ret < 0)
                goto out;
@@ -131,8 +126,6 @@ int main(int argc, char *argv[])
                fd_set rfd, wfd;
                FD_ZERO(&rfd);
                FD_ZERO(&wfd);
-               if (loaded && loaded > 10000)
-                       fprintf(stderr, "loaded: %d\n", loaded);
                if (loaded < conf.bufsize_arg)
                        para_fd_set(fd, &rfd, &max_fileno);
                if (loaded > 0) {
index f6f4b0529f7443ee051aa8f8d80a1c0d249a10de..be8ff62de9d99679f9a9e329d2384b5420df4eea 100644 (file)
@@ -1,6 +1,28 @@
-section "general options"
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "socket" s "well-known socket (default=/var/paraslash/audiod.socket.$HOSTNAME)" string typestr="filename" optional
-option "tmpdir" d "directory for temporary socket (default=~/.paraslash)" string typestr="dirname" optional
-option "timeout" t "maximum time in seconds to wait for server response before giving up" int typestr="seconds" default="1" optional
-option "bufsize" b "size of internal buffer" int typestr="bytes" default="8192" optional
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+"set loglevel (0-6)"
+       int typestr="level"
+       default="4"
+       optional
+
+
+option "socket" s
+#~~~~~~~~~~~~~~~~
+"well-known socket (default=/var/paraslash/audiod.socket.$HOSTNAME)"
+       string typestr="filename"
+       optional
+
+
+option "tmpdir" d
+#~~~~~~~~~~~~~~~~
+"directory for temporary socket (default=~/.paraslash)"
+       string typestr="dirname"
+       optional
+
+
+option "bufsize" b
+#~~~~~~~~~~~~~~~~~
+"size of internal buffer"
+       int typestr="bytes"
+       default="8192"
+       optional
index 032f9cd0743a85a492b5cb446ebc965dc3b2cb1f..8b9f181b80edfb74b31ec08aed23d61d74c8c19f 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -288,8 +288,7 @@ void __noreturn clean_exit(int status, const char *msg)
        PARA_EMERG_LOG("%s\n", msg);
        if (socket_name)
                unlink(socket_name);
-       if (stat_task->fd >= 0)
-               close_stat_pipe();
+       close_stat_pipe();
        exit(status);
 }
 
@@ -882,6 +881,7 @@ static int audiod_get_socket(void)
                exit(EXIT_FAILURE); /* do not unlink socket */
        }
        add_close_on_fork_list(fd);
+       mark_fd_nonblock(fd);
        return fd;
 }
 
@@ -894,6 +894,7 @@ static int open_stat_pipe(void)
                ret = fd[1];
                PARA_NOTICE_LOG("stat pipe opened, fd %d\n", ret);
                add_close_on_fork_list(ret);
+               mark_fd_nonblock(ret);
        } else
                clean_exit(EXIT_FAILURE, "failed to open status pipe");
        return ret;
@@ -1052,8 +1053,6 @@ int main(int argc, char *argv[])
        struct command_task command_task_struct, *cmd_task = &command_task_struct;
        struct task audiod_task_struct, *audiod_task = &audiod_task_struct;
 
-       init_sched();
-
        valid_fd_012();
        cmdline_parser(argc, argv, &conf);
        para_drop_privileges(conf.user_arg, conf.group_arg);
index a28be42f0ec3b9c3171fa4d8ba07debb07cf1d26..d2f4643372200465b0a576184b9fefe00761f01c 100644 (file)
--- a/client.c
+++ b/client.c
 /** \file client.c the client program used to connect to para_server */
 
 #include "para.h"
-#include "config.h"
+#include "list.h"
+#include "sched.h"
 #include "client.cmdline.h"
-#include "crypt.h"
-#include "rc4.h"
-#include <openssl/rc4.h>
-#include "net.h"
 #include "string.h"
+#include "stdin.h"
+#include "stdout.h"
+#include "client.h"
 #include "error.h"
 
-struct gengetopt_args_info args_info;
-
 INIT_CLIENT_ERRLISTS;
 
+static struct private_client_data *pcd;
+static struct stdin_task sit;
+static struct stdout_task sot;
+
 /*
  * client log function
  */
@@ -40,245 +42,60 @@ void para_log(int ll, const char* fmt,...)
        va_list argp;
 
        /* ignore log message if loglevel is not high enough */
-       if (ll < args_info.loglevel_arg)
+       if (pcd && ll < pcd->conf.loglevel_arg)
                return;
        va_start(argp, fmt);
        vfprintf(stderr, fmt, argp);
        va_end(argp);
 }
 
-static int get_options(int argc, char *argv[],
-       char **config_file, char **key_file)
+static void client_event_handler(struct task *t)
 {
-       char *home;
-       static char default_key_file[_POSIX_PATH_MAX] = "";
-       static char default_config_file[_POSIX_PATH_MAX] = "";
-       struct stat statbuf;
-       int ret;
+       struct private_client_data *p = t->private_data;
 
-       cmdline_parser(argc, argv, &args_info);
-       if (!args_info.user_given)
-               args_info.user_arg = para_logname();
-       if (!args_info.key_file_given) {
-               home = para_homedir();
-               sprintf(default_key_file, "%s/.paraslash/key.%s", home,
-                       args_info.user_arg);
-               free(home);
+       PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret));
+       if (t->ret != -E_HANDSHAKE_COMPLETE) {
+               unregister_task(t);
+               p->eof = 1;
+               return;
        }
-       if (!args_info.config_file_given) {
-               home = para_homedir();
-               sprintf(default_config_file, "%s/.paraslash/client.conf",
-                       home);
-               free(home);
+       if (p->status == CL_SENDING_STDIN) {
+               stdin_set_defaults(&sit);
+               sit.buf = para_malloc(sit.bufsize),
+               register_task(&sit.task);
+               p->inbuf = sit.buf;
+               p->in_loaded = &sit.loaded;
+               p->in_eof = &sit.eof;
+               return;
        }
-       if (!args_info.config_file_given)
-               *config_file = default_config_file;
-       else
-               *config_file = args_info.config_file_arg;
-       ret = stat(*config_file, &statbuf);
-       if (ret && args_info.config_file_given)
-               return -E_NO_CONFIG;
-       if (!ret)
-               cmdline_parser_configfile(*config_file, &args_info, 0, 0, 0);
-       if (!args_info.key_file_given)
-               *key_file = default_key_file;
-       else
-               *key_file = args_info.key_file_arg;
-       return 1;
+       stdout_set_defaults(&sot);
+       sot.buf = p->buf;
+       sot.loaded = &p->loaded;
+       sot.input_eof = &p->eof;
+       register_task(&sot.task);
 }
 
-static RC4_KEY rc4_recv_key;
-static RC4_KEY rc4_send_key;
-static unsigned char rc4_buf[2 * RC4_KEY_LEN];
-
-static void rc4_send(unsigned long len, const unsigned char *indata, unsigned char *outdata)
-{
-       RC4(&rc4_send_key, len, indata, outdata);
-}
-
-static void rc4_recv(unsigned long len, const unsigned char *indata, unsigned char *outdata)
-{
-       RC4(&rc4_recv_key, len, indata, outdata);
-}
-void (*crypt_function_recv)(unsigned long len, const unsigned char *indata, unsigned char *outdata);
-void (*crypt_function_send)(unsigned long len, const unsigned char *indata, unsigned char *outdata);
-
-
-static void append_str(char **data, const char* append)
-{
-       if (*data) {
-               char *tmp = make_message("%s\n%s", *data, append);
-               free(*data);
-               *data = tmp;
-       } else
-               *data = para_strdup(append);
-}
-
-
-static int send_stdin(int fd)
-{
-       char buf[8192];
-       int ret;
-
-       PARA_NOTICE_LOG("%s", "sending stdin\n");
-       for (;;) {
-               ret = read(STDIN_FILENO, buf, sizeof(buf));
-               if (ret <= 0)
-                       return ret;
-               ret = send_bin_buffer(fd, buf, ret);
-               if (ret < 0)
-                       return ret;
-       }
-       return 1;
-}
 /*
  * MAIN
  */
 int main(int argc, char *argv[])
 {
 
-       int sockfd = -1, numbytes, i, received, ret;
-       struct hostent *he;
-       struct sockaddr_in their_addr;
-       char *command = NULL;
-       char buf[8192];
-       char *auth_str;
-       char *key_file, *config_file;
-       long unsigned challenge_nr;
+       int ret;
+       struct sched s;
 
-       ret = get_options(argc, argv, &config_file, &key_file);
+       s.default_timeout.tv_sec = 1;
+       s.default_timeout.tv_usec = 0;
+       ret = client_parse_config(argc, argv, &pcd);
        if (ret < 0)
                goto out;
-       if (args_info.loglevel_arg <= NOTICE)
-               cmdline_parser_print_version();
-       PARA_INFO_LOG(
-               "current loglevel: %d\n"
-               "using config_file: %s\n"
-               "using key_file: %s\n"
-               "connecting to %s:%d\n",
-               args_info.loglevel_arg,
-               config_file,
-               key_file,
-               args_info.hostname_arg,
-               args_info.server_port_arg
-       );
-       ret = - E_CLIENT_SYNTAX;
-       if (!args_info.inputs_num)
-               goto out;
-       /* concat args */
-       for (i = 0; i < args_info.inputs_num; i++)
-               append_str(&command, args_info.inputs[i]);
-       crypt_function_recv = NULL;
-       crypt_function_send = NULL;
-       /* get the host info */
-       PARA_NOTICE_LOG("getting host info of %s\n",
-               args_info.hostname_arg);
-       ret = get_host_info(args_info.hostname_arg, &he);
+       pcd->task.event_handler = client_event_handler;
+       ret = client_open(pcd);
        if (ret < 0)
                goto out;
-       /* get new socket */
-       ret = get_socket();
-       if (ret < 0)
-               goto out;
-       sockfd = ret;
-       /* init their_addr */
-       init_sockaddr(&their_addr, args_info.server_port_arg, he);
-       /* connect */
-       PARA_NOTICE_LOG("connecting to %s\n", args_info.hostname_arg);
-       ret = para_connect(sockfd, &their_addr);
-       if (ret < 0)
-               goto out;
-       /* receive welcome message */
-       ret = recv_buffer(sockfd, buf, sizeof(buf));
-       if (ret < 0)
-               goto out;
-       /* send auth command */
-       auth_str = make_message("auth %s%s", args_info.plain_given?  "" : "rc4 ",
-               args_info.user_arg);
-       PARA_INFO_LOG("<-- %s--> %s\n", buf, auth_str);
-       ret = send_buffer(sockfd, auth_str);
-       if (ret < 0)
-               goto out;
-       /* receive challenge number */
-       ret = recv_buffer(sockfd, buf, sizeof(buf));
-       if (ret < 0)
-               goto out;
-       if (ret != 64) {
-               ret = -E_INVALID_CHALLENGE;
-               PARA_ERROR_LOG("received the following: %s\n", buf);
-               goto out;
-       }
-       PARA_INFO_LOG("%s", "<-- [challenge]\n");
-       /* decrypt challenge number */
-       ret = para_decrypt_challenge(key_file, &challenge_nr, (unsigned char *) buf, 64);
-       if (ret < 0)
-               goto out;
-       /* send decrypted challenge */
-       PARA_INFO_LOG("--> %lu\n", challenge_nr);
-       ret = send_va_buffer(sockfd, "%s%lu", CHALLENGE_RESPONSE_MSG, challenge_nr);
-       if (ret < 0)
-               goto out;
-       /* wait for approval */
-       PARA_NOTICE_LOG("%s", "waiting for approval from server\n");
-       ret = recv_buffer(sockfd, buf, sizeof(buf));
-       if (ret < 0)
-               goto out;
-       numbytes = ret;
-       PARA_INFO_LOG("++++ server info ++++\n%s\n++++ end of server "
-               "info ++++\n", buf);
-       /* check if server has sent "Proceed" message */
-       ret = -E_CLIENT_AUTH;
-       if (!strstr(buf, PROCEED_MSG))
-               goto out;
-       if (numbytes >= PROCEED_MSG_LEN + 32) {
-               PARA_INFO_LOG("%s", "decrypting session key\n");
-               ret = para_decrypt_buffer(key_file, rc4_buf,
-                       (unsigned char *)buf + PROCEED_MSG_LEN + 1,
-                       numbytes - PROCEED_MSG_LEN - 1);
-               if (ret < 0)
-                       goto out;
-               RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf);
-               RC4_set_key(&rc4_recv_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
-               PARA_INFO_LOG("rc4 encrytion activated: %x:%x:%x:%x\n",
-                       rc4_buf[0], rc4_buf[1], rc4_buf[2], rc4_buf[3]);
-               crypt_function_recv = rc4_recv;
-               crypt_function_send = rc4_send;
-       }
-       /* send command */
-       PARA_INFO_LOG("--> %s\n", command);
-       ret = send_buffer(sockfd, command);
-       if (ret < 0)
-               goto out;
-       free(command);
-       command = NULL;
-       ret = send_buffer(sockfd, EOC_MSG "\n");
-       if (ret < 0)
-               goto out;
-       PARA_NOTICE_LOG("%s", "command sent.\n");
-       received = 0;
-       for (;;) {
-               ret = recv_bin_buffer(sockfd, buf, sizeof(buf) - 1);
-               if (ret <= 0) {
-                       if (!ret)
-                               PARA_NOTICE_LOG("%s", "connection closed by peer\n");
-                       goto out;
-               }
-               buf[ret] = '\0';
-               numbytes = ret;
-               if (!received && strstr(buf, AWAITING_DATA_MSG)) {
-                       ret = send_stdin(sockfd);
-                       goto out;
-               }
-               received = 1;
-               ret = write(STDOUT_FILENO, buf, numbytes);
-               if (ret != numbytes) {
-                       ret = -E_SHORT_CLIENT_WRITE;
-                       goto out;
-               }
-       }
+       ret = sched(&s);
+       client_close(pcd);
 out:
-       if (sockfd >= 0)
-               close(sockfd);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
        return ret >= 0? EXIT_SUCCESS: EXIT_FAILURE;
diff --git a/client.h b/client.h
new file mode 100644 (file)
index 0000000..8f7b9a5
--- /dev/null
+++ b/client.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 1997-2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file client.h common client functions and exported symbols from client_common.c */
+
+#include <openssl/rc4.h>
+enum {
+       CL_CONNECTED,
+       CL_RECEIVED_WELCOME,
+       CL_SENT_AUTH,
+       CL_RECEIVED_CHALLENGE,
+       CL_SENT_CH_RESPONSE,
+       CL_RECEIVED_PROCEED,
+       CL_SENT_COMMAND,
+       CL_SENDING_STDIN,
+       CL_RECEIVING_SERVER_OUTPUT
+};
+
+#define CLIENT_BUFSIZE 8192
+
+struct private_client_data {
+       int status;
+       int fd;
+       struct client_args_info conf;
+       char *config_file;
+       char *key_file;
+       char *user;
+       RC4_KEY rc4_recv_key;
+       RC4_KEY rc4_send_key;
+       struct task task;
+       int eof;
+       char buf[CLIENT_BUFSIZE];
+       size_t loaded;
+       int check_r;
+       int check_w;
+       long unsigned challenge_nr;
+       /* only used if stdin gets sent to para_server */
+       char *inbuf;
+       size_t *in_loaded;
+       int *in_eof;
+};
+
+int client_open(struct private_client_data *pcd);
+void client_close(struct private_client_data *pcd);
+int client_parse_config(int argc, char *argv[],
+       struct private_client_data **pcd_ptr);
+void client_pre_select(struct sched *s, struct task *t);
+void client_post_select(struct sched *s, struct task *t);
diff --git a/client_common.c b/client_common.c
new file mode 100644 (file)
index 0000000..a0128ad
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 1997-2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file client_common.c common functions of para_client and para_audiod */
+
+#include "para.h"
+#include "list.h"
+#include "sched.h"
+#include "client.cmdline.h"
+#include "crypt.h"
+#include "rc4.h"
+#include "net.h"
+#include "fd.h"
+#include "string.h"
+#include "client.h"
+#include "error.h"
+
+void rc4_send(unsigned long len, const unsigned char *indata,
+               unsigned char *outdata, void *private_data)
+{
+       struct private_client_data *pcd = private_data;
+       RC4(&pcd->rc4_send_key, len, indata, outdata);
+}
+
+void rc4_recv(unsigned long len, const unsigned char *indata,
+               unsigned char *outdata, void *private_data)
+{
+       struct private_client_data *pcd = private_data;
+       RC4(&pcd->rc4_recv_key, len, indata, outdata);
+}
+
+
+void client_close(struct private_client_data *pcd)
+{
+       if (pcd)
+               return;
+       if (pcd->fd >= 0)
+               close(pcd->fd);
+       free(pcd->user);
+       free(pcd->config_file);
+       free(pcd->key_file);
+       free(pcd);
+}
+
+int client_parse_config(int argc, char *argv[],
+               struct private_client_data **pcd_ptr)
+{
+       char *home = para_homedir();
+       struct stat statbuf;
+       int ret;
+       struct private_client_data *pcd =
+               para_calloc(sizeof(struct private_client_data));
+
+       pcd->fd = -1;
+       cmdline_parser(argc, argv, &pcd->conf);
+       ret = - E_CLIENT_SYNTAX;
+       if (!pcd->conf.inputs_num)
+               goto out;
+       pcd->user = pcd->conf.user_given?
+               para_strdup(pcd->conf.user_arg) : para_logname();
+
+       pcd->key_file = pcd->conf.key_file_given?
+               para_strdup(pcd->conf.key_file_arg) :
+               make_message("%s/.paraslash/key.%s", home, pcd->user);
+
+       pcd->config_file = pcd->conf.config_file_given?
+               para_strdup(pcd->conf.config_file_arg) :
+               make_message("%s/.paraslash/client.conf", home);
+       ret = stat(pcd->config_file, &statbuf);
+       if (ret && pcd->conf.config_file_given) {
+               ret = -E_NO_CONFIG;
+               goto out;
+       }
+       if (!ret)
+               cmdline_parser_configfile(pcd->config_file, &pcd->conf, 0, 0, 0);
+       ret = 1;
+       *pcd_ptr = pcd;
+       PARA_INFO_LOG(
+               "current loglevel: %d\n"
+               "using config_file: %s\n"
+               "using key_file: %s\n"
+               "connecting to %s:%d\n" ,
+               pcd->conf.loglevel_arg,
+               pcd->config_file,
+               pcd->key_file,
+               pcd->conf.hostname_arg, pcd->conf.server_port_arg
+       );
+out:
+       free(home);
+       if (ret < 0)
+               client_close(pcd);
+       return ret;
+}
+
+void client_pre_select(struct sched *s, struct task *t)
+{
+       struct private_client_data *pcd = t->private_data;
+
+       PARA_INFO_LOG("status %d\n", pcd->status);
+       t->ret = 1;
+       pcd->check_r = 0;
+       pcd->check_w = 0;
+       if (pcd->fd < 0)
+               return;
+       switch (pcd->status) {
+       case CL_CONNECTED:
+       case CL_SENT_AUTH:
+       case CL_SENT_CH_RESPONSE:
+       case CL_SENT_COMMAND:
+               para_fd_set(pcd->fd, &s->rfds, &s->max_fileno);
+               pcd->check_r = 1;
+               return;
+
+       case CL_RECEIVED_WELCOME:
+       case CL_RECEIVED_CHALLENGE:
+       case CL_RECEIVED_PROCEED:
+               para_fd_set(pcd->fd, &s->wfds, &s->max_fileno);
+               pcd->check_w = 1;
+               return;
+
+       case CL_RECEIVING_SERVER_OUTPUT:
+               if (pcd->loaded < CLIENT_BUFSIZE - 1) {
+                       para_fd_set(pcd->fd, &s->rfds, &s->max_fileno);
+                       pcd->check_r = 1;
+               }
+               return;
+       case CL_SENDING_STDIN:
+               if (*pcd->in_loaded) {
+                       PARA_INFO_LOG("loaded: %d\n", *pcd->in_loaded);
+                       para_fd_set(pcd->fd, &s->wfds, &s->max_fileno);
+                       pcd->check_w = 1;
+               } else {
+                       if (*pcd->in_eof) {
+                               t->ret = -E_INPUT_EOF;
+                               s->timeout.tv_sec = 0;
+                               s->timeout.tv_usec = 1;
+                       }
+               }
+               return;
+       }
+}
+
+static ssize_t client_recv_buffer(struct private_client_data *pcd)
+{
+       ssize_t ret = recv_buffer(pcd->fd, pcd->buf + pcd->loaded,
+               CLIENT_BUFSIZE - pcd->loaded);
+       if (!ret)
+               return -E_SERVER_EOF;
+       if (ret > 0)
+               pcd->loaded += ret;
+       return ret;
+
+}
+
+void client_post_select(struct sched *s, struct task *t)
+{
+       struct private_client_data *pcd = t->private_data;
+
+       PARA_INFO_LOG("status %d\n", pcd->status);
+       t->ret = 1;
+       if (pcd->fd < 0)
+               return;
+       if (!pcd->check_r && !pcd->check_w)
+               return;
+       if (pcd->check_r && !FD_ISSET(pcd->fd, &s->rfds))
+               return;
+       if (pcd->check_w && !FD_ISSET(pcd->fd, &s->wfds))
+               return;
+       switch (pcd->status) {
+       case CL_CONNECTED: /* receive welcome message */
+               t->ret = client_recv_buffer(pcd);
+               if (t->ret > 0)
+                       pcd->status = CL_RECEIVED_WELCOME;
+               return;
+       case CL_RECEIVED_WELCOME: /* send auth command */
+               sprintf(pcd->buf, "auth %s%s", pcd->conf.plain_given?
+                       "" : "rc4 ", pcd->user);
+               PARA_INFO_LOG("--> %s\n", pcd->buf);
+               t->ret = send_buffer(pcd->fd, pcd->buf);
+               if (t->ret >= 0)
+                       pcd->status = CL_SENT_AUTH;
+               return;
+       case CL_SENT_AUTH: /* receive challenge number */
+               pcd->loaded = 0;
+               t->ret = client_recv_buffer(pcd);
+               if (t->ret < 0)
+                       return;
+               if (t->ret != 64) {
+                       t->ret = -E_INVALID_CHALLENGE;
+                       PARA_ERROR_LOG("received the following: %s\n", pcd->buf);
+                       return;
+               }
+               PARA_INFO_LOG("%s", "<-- [challenge]\n");
+               /* decrypt challenge number */
+               t->ret = para_decrypt_challenge(pcd->key_file, &pcd->challenge_nr,
+                       (unsigned char *) pcd->buf, 64);
+               if (t->ret > 0)
+                       pcd->status = CL_RECEIVED_CHALLENGE;
+               return;
+       case CL_RECEIVED_CHALLENGE: /* send decrypted challenge */
+               PARA_INFO_LOG("--> %lu\n", pcd->challenge_nr);
+               t->ret = send_va_buffer(pcd->fd, "%s%lu", CHALLENGE_RESPONSE_MSG,
+                       pcd->challenge_nr);
+               if (t->ret > 0)
+                       pcd->status = CL_SENT_CH_RESPONSE;
+               return;
+       case CL_SENT_CH_RESPONSE: /* read server response */
+               {
+               size_t bytes_received;
+               unsigned char rc4_buf[2 * RC4_KEY_LEN] = "";
+               pcd->loaded = 0;
+               t->ret = client_recv_buffer(pcd);
+               if (t->ret < 0)
+                       return;
+               bytes_received = t->ret;
+               PARA_INFO_LOG("++++ server info ++++\n%s\n++++ end of server "
+                       "info ++++\n", pcd->buf);
+               /* check if server has sent "Proceed" message */
+               t->ret = -E_CLIENT_AUTH;
+               if (!strstr(pcd->buf, PROCEED_MSG))
+                       return;
+               t->ret = 1;
+               pcd->status = CL_RECEIVED_PROCEED;
+               if (bytes_received < PROCEED_MSG_LEN + 32)
+                       return;
+               PARA_INFO_LOG("%s", "decrypting session key\n");
+               t->ret = para_decrypt_buffer(pcd->key_file, rc4_buf,
+                       (unsigned char *)pcd->buf + PROCEED_MSG_LEN + 1,
+                       bytes_received - PROCEED_MSG_LEN - 1);
+               if (t->ret < 0)
+                       return;
+               RC4_set_key(&pcd->rc4_send_key, RC4_KEY_LEN, rc4_buf);
+               RC4_set_key(&pcd->rc4_recv_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
+               enable_crypt(pcd->fd, rc4_recv, rc4_send, pcd);
+               }
+       case CL_RECEIVED_PROCEED: /* concat args and send command */
+               {
+               int i;
+               char *command = NULL;
+               for (i = 0; i < pcd->conf.inputs_num; i++) {
+                       char *tmp = command;
+                       command = make_message("%s\n%s", command?
+                               command : "", pcd->conf.inputs[i]);
+                       free(tmp);
+               }
+               command = para_strcat(command, EOC_MSG "\n");
+               PARA_INFO_LOG("--> %s\n", command);
+               t->ret = send_buffer(pcd->fd, command);
+               free(command);
+               if (t->ret > 0)
+                       pcd->status = CL_SENT_COMMAND;
+               return;
+               }
+       case CL_SENT_COMMAND:
+               pcd->loaded = 0;
+               t->ret = client_recv_buffer(pcd);
+               if (t->ret < 0)
+                       return;
+               t->ret = -E_HANDSHAKE_COMPLETE;
+               if (strstr(pcd->buf, AWAITING_DATA_MSG))
+                       pcd->status = CL_SENDING_STDIN;
+               else
+                       pcd->status = CL_RECEIVING_SERVER_OUTPUT;
+               return;
+       case CL_SENDING_STDIN: /* FIXME: might block */
+               PARA_INFO_LOG("loaded: %d\n", *pcd->in_loaded);
+               t->ret = send_bin_buffer(pcd->fd, pcd->inbuf, *pcd->in_loaded);
+               if (t->ret <= 0) {
+                       if (!t->ret)
+                               t->ret = 1;
+                       return;
+               }
+               *pcd->in_loaded = 0; /* FIXME: short writes */
+               return;
+       case CL_RECEIVING_SERVER_OUTPUT:
+               t->ret = client_recv_buffer(pcd);
+               return;
+       }
+
+}
+
+int client_open(struct private_client_data *pcd)
+{
+       int ret;
+       struct hostent *he;
+       struct sockaddr_in their_addr;
+
+       /* get the host info */
+       PARA_NOTICE_LOG("getting host info of %s\n",
+               pcd->conf.hostname_arg);
+       ret = get_host_info(pcd->conf.hostname_arg, &he);
+       if (ret < 0)
+               goto out;
+       /* get new socket */
+       ret = get_socket();
+       if (ret < 0)
+               goto out;
+       pcd->fd = ret;
+       /* init their_addr */
+       init_sockaddr(&their_addr, pcd->conf.server_port_arg, he);
+       /* connect */
+       PARA_NOTICE_LOG("connecting to %s\n", pcd->conf.hostname_arg);
+       ret = para_connect(pcd->fd, &their_addr);
+       if (ret < 0)
+               goto out;
+       pcd->status = CL_CONNECTED;
+       pcd->task.pre_select = client_pre_select;
+       pcd->task.post_select = client_post_select;
+       pcd->task.private_data = pcd;
+       sprintf(pcd->task.status, "client");
+       register_task(&pcd->task);
+       ret = 1;
+out:
+       return ret;
+}
index 531c776a3249e6d555c6570746c1e6036e125f7d..9098ad3c21d257c02b90e7a5ca1eee8c3d6b1759 100644 (file)
--- a/command.c
+++ b/command.c
 #include "daemon.h"
 #include "string.h"
 
-void (*crypt_function_recv)(unsigned long len, const unsigned char *indata,
-       unsigned char *outdata) = NULL;
-void (*crypt_function_send)(unsigned long len, const unsigned char *indata,
-       unsigned char *outdata) = NULL;
 static RC4_KEY rc4_recv_key;
 static RC4_KEY rc4_send_key;
 static unsigned char rc4_buf[2 * RC4_KEY_LEN];
@@ -1073,12 +1069,14 @@ static void init_rc4_keys(void)
        RC4_set_key(&rc4_send_key, RC4_KEY_LEN, rc4_buf + RC4_KEY_LEN);
 }
 
-static void rc4_recv(unsigned long len, const unsigned char *indata, unsigned char *outdata)
+static void rc4_recv(unsigned long len, const unsigned char *indata,
+               unsigned char *outdata, __a_unused void *private_data)
 {
        RC4(&rc4_recv_key, len, indata, outdata);
 }
 
-static void rc4_send(unsigned long len, const unsigned char *indata, unsigned char *outdata)
+static void rc4_send(unsigned long len, const unsigned char *indata,
+               unsigned char *outdata, __a_unused void *private_data)
 {
        RC4(&rc4_send_key, len, indata, outdata);
 }
@@ -1171,11 +1169,8 @@ int handle_connect(int fd, struct sockaddr_in *addr)
        ret = send_bin_buffer(fd, buf, numbytes);
        if (ret < 0)
                goto err_out;
-       if (use_rc4) {
-               crypt_function_recv = rc4_recv;
-               crypt_function_send = rc4_send;
-               PARA_INFO_LOG("%s", "rc4 encryption activated\n");
-       }
+       if (use_rc4)
+               enable_crypt(fd, rc4_recv, rc4_send, NULL);
        /* read command */
        while ((numbytes = recv_buffer(fd, buf, sizeof(buf))) > 0) {
 //             PARA_INFO_LOG("recvd: %s (%d)\n", buf, numbytes);
index 4dcf0f2b795161e5ee3e00f2a27e02948fed1a40..81d1cbba04e4ffc08341cee2528c0313f22bc7b2 100644 (file)
@@ -86,7 +86,7 @@ write_ldflags=""
 write_writers="file"
 
 client_cmdline_objs="client.cmdline"
-client_errlist_objs="client net string crypt"
+client_errlist_objs="client net string crypt fd sched stdin stdout client_common"
 client_ldflags=""
 
 ########################################################################### ssl
diff --git a/dccp.c b/dccp.c
index 147e46307676b60025561e4f81167a3a72b22b90..fa48976e72f44f0cc8b0bdac7d93bcf6368075ec 100644 (file)
--- a/dccp.c
+++ b/dccp.c
@@ -26,6 +26,7 @@
 #include "para.h"
 #include "error.h"
 #include "dccp.h"
+#include "fd.h"
 
 /** \cond some magic dccp constants */
 #define SOL_DCCP 269
index 335ad2e630598de4d98cafbb2257e5e07be144af..943b8283e6a0f135c3b0edbc3cd5de44e250cedb 100644 (file)
@@ -98,6 +98,7 @@ static int dccp_recv_open(struct receiver_node *rn)
        ret = -E_DCCP_CONNECT;
        if (connect(pdd->fd, ai->ai_addr, ai->ai_addrlen) < 0)
                goto err_out;
+       mark_fd_nonblock(pdd->fd);
        return 1;
 err_out:
        dccp_recv_close(rn);
diff --git a/error.h b/error.h
index a64348e342e2aeef66a09b9e2e808f57366b002d..89dbb0f69f1ffa984f740fa8b7a0e3b5d24d035b 100644 (file)
--- a/error.h
+++ b/error.h
@@ -20,6 +20,7 @@
 
 /** \cond list of all subsystems that support the shiny error facility */
 enum para_subsystem {
+       SS_CLIENT,
        SS_GUI,
        SS_TIME,
        SS_WAV,
@@ -31,7 +32,7 @@ enum para_subsystem {
        SS_RECV,
        SS_NET,
        SS_ORTP_RECV,
-       SS_CLIENT,
+       SS_CLIENT_COMMON,
        SS_AUDIOC,
        SS_SCHED,
        SS_AUDIOD,
@@ -78,6 +79,7 @@ enum para_subsystem {
 
 /* these do not need error handling (yet) */
 #define SERVER_ERRORS
+#define CLIENT_ERRORS
 #define WAV_ERRORS
 #define COMPRESS_ERRORS
 #define TIME_ERRORS
@@ -110,16 +112,22 @@ extern const char **para_errlist[];
        PARA_ERROR(AUDIOC_OVERRUN, "audioc buffer overrun"), \
 
 
-#define CLIENT_ERRORS \
+
+
+#define CLIENT_COMMON_ERRORS \
        PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
        PARA_ERROR(INVALID_CHALLENGE, "did not receive valid challenge"), \
-       PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
-       PARA_ERROR(SHORT_CLIENT_WRITE, "short client write"), \
        PARA_ERROR(NO_CONFIG, "config file not found"), \
+       PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
+       PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
+       PARA_ERROR(INPUT_EOF, "end of input"), \
+       PARA_ERROR(HANDSHAKE_COMPLETE, ""), /* not really an error */ \
+
 
 #define SCHED_ERRORS \
        PARA_ERROR(TASK_KILLED, "task killed"), \
        PARA_ERROR(NO_SUCH_TASK, "task not found"), \
+       PARA_ERROR(NOT_INITIALIZED, "scheduler not yet initialized"), \
 
 
 #define STDIN_ERRORS \
@@ -559,6 +567,7 @@ SS_ENUM(FILE_WRITER);
 SS_ENUM(OSX_WRITER);
 SS_ENUM(RINGBUFFER);
 SS_ENUM(CLIENT);
+SS_ENUM(CLIENT_COMMON);
 SS_ENUM(AUDIOC);
 /** \endcond */
 #undef PARA_ERROR
diff --git a/fd.c b/fd.c
index d1e0412d3f3f22174229eab26582b9a688707e85..b913009369ba6029fcdf4fa8378c9d926e30dd10 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -100,10 +100,20 @@ int mark_fd_nonblock(int fd)
 */
 void para_fd_set(int fd, fd_set *fds, int *max_fileno)
 {
+
        if (fd < 0 || fd >= FD_SETSIZE) {
                PARA_EMERG_LOG("fatal: tried to add invalid fd %d\n", fd);
                exit(EXIT_FAILURE);
        }
+#if 0
+       {
+               int flags = fcntl(fd, F_GETFL);
+               if (!(flags & O_NONBLOCK)) {
+                       PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd);
+                       exit(EXIT_FAILURE);
+               }
+       }
+#endif
        FD_SET(fd, fds);
        *max_fileno = PARA_MAX(*max_fileno, fd);
 }
index 608bb8b3bf5eedd8c1bac69d6454f857cb6efaeb..f49042f8c7ecfe901bcb01b5ef889d12e93b71b5 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -140,7 +140,6 @@ int main(int argc, char *argv[])
        int ret;
        struct sched s;
 
-       init_sched();
        stdin_set_defaults(sit);
        sit->buf = para_malloc(sit->bufsize),
 
index 3d72c7c7396ca3b8f96f63653aec9c021cb90846..096541679ea5ae30116f478436c2303c1aabf567 100644 (file)
@@ -189,9 +189,10 @@ static int http_recv_open(struct receiver_node *rn)
        if (!ret < 0)
                goto err_out;
        /* get new socket */
-       ret = -E_SOCKET;
-       if ((phd->fd = get_socket()) < 0)
+       ret = get_socket();
+       if (ret < 0)
                goto err_out;
+       phd->fd = ret;
        /* init their_addr */
        init_sockaddr(&their_addr, conf->port_arg, he);
        /* connect */
@@ -200,6 +201,7 @@ static int http_recv_open(struct receiver_node *rn)
        ret = para_connect(phd->fd, &their_addr);
        if (ret < 0)
                goto err_out;
+       mark_fd_nonblock(phd->fd);
        phd->status = HTTP_CONNECTED;
        return 1;
 err_out:
diff --git a/net.c b/net.c
index 582fa04dad3c3f61c98fd975d551424123759da8..28bc777d4dde6f56be5414d4d8d92aeb0d47ab6e 100644 (file)
--- a/net.c
+++ b/net.c
 #include "string.h"
 #include "error.h"
 
-extern void (*crypt_function_recv)(unsigned long len, const unsigned char *indata, unsigned char *outdata);
-extern void (*crypt_function_send)(unsigned long len, const unsigned char *indata, unsigned char *outdata);
+struct crypt_data {
+       crypt_function *recv;
+       crypt_function *send;
+       void *private_data;
+};
+
+static struct crypt_data *crypt_data_array;
+static unsigned cda_size = 0;
+
+void enable_crypt(int fd, crypt_function *recv, crypt_function *send,
+       void *private_data)
+{
+       if (fd + 1 > cda_size) {
+               crypt_data_array = para_realloc(crypt_data_array,
+                       (fd + 1) * sizeof(struct crypt_data));
+               memset(crypt_data_array + cda_size * sizeof(struct crypt_data), 0,
+                       (fd + 1 - cda_size) * sizeof(struct crypt_data));
+               cda_size = fd + 1;
+       }
+       crypt_data_array[fd].recv = recv;
+       crypt_data_array[fd].send = send;
+       crypt_data_array[fd].private_data = private_data;
+       PARA_INFO_LOG("rc4 encryption activated for fd %d\n", fd);
+}
+
+void disable_crypt(int fd)
+{
+       if (cda_size < fd + 1)
+               return;
+       crypt_data_array[fd].recv = NULL;
+       crypt_data_array[fd].send = NULL;
+       crypt_data_array[fd].private_data = NULL;
+}
 
 
 /**
@@ -95,12 +126,16 @@ static int sendall(int fd, const char *buf, size_t *len)
 int send_bin_buffer(int fd, const char *buf, size_t len)
 {
        int ret;
+       crypt_function *cf = NULL;
 
        if (!len)
                PARA_CRIT_LOG("%s", "len == 0\n");
-       if (crypt_function_send) {
+       if (fd + 1 <= cda_size)
+               cf = crypt_data_array[fd].send;
+       if (cf) {
+               void *private = crypt_data_array[fd].private_data;
                unsigned char *outbuf = para_malloc(len);
-               crypt_function_send(len, (unsigned char *)buf, outbuf);
+               (*cf)(len, (unsigned char *)buf, outbuf, private);
                ret = sendall(fd, (char *)outbuf, &len);
                free(outbuf);
        } else
@@ -159,12 +194,16 @@ __printf_2_3 int send_va_buffer(int fd, const char *fmt, ...)
 __must_check int recv_bin_buffer(int fd, char *buf, ssize_t size)
 {
        int n;
+       crypt_function *cf = NULL;
 
-       if (crypt_function_recv) {
+       if (fd + 1 <= cda_size)
+               cf = crypt_data_array[fd].recv;
+       if (cf) {
                unsigned char *tmp = para_malloc(size);
+               void *private = crypt_data_array[fd].private_data;
                n = recv(fd, tmp, size, 0);
                if (n > 0)
-                       crypt_function_recv(n, tmp, (unsigned char *)buf);
+                       (*cf)(n, tmp, (unsigned char *)buf, private);
                free(tmp);
        } else
                n = recv(fd, buf, size, 0);
@@ -199,8 +238,12 @@ int recv_buffer(int fd, char *buf, ssize_t size)
 /**
  * wrapper around gethostbyname
  *
- * @param host hostname or IPv4 address
- * \return The hostent structure or a NULL pointer if an error occurs
+ * \param host hostname or IPv4 address
+ * \param ret the hostent structure is returned here
+ *
+ * \return positive on success, negative on errors. On success, \a ret
+ * contains the return value of the underlying gethostbyname() call.
+ *
  * \sa gethostbyname(2)
  */
 int get_host_info(char *host, struct hostent **ret)
@@ -275,14 +318,6 @@ static int setserversockopts(int socket_fd)
        return 1;
 }
 
-static int do_bind(int socket_fd, struct sockaddr_in *my_addr)
-{
-       if (bind(socket_fd, (struct sockaddr *)my_addr,
-               sizeof(struct sockaddr)) == -1)
-               return -E_BIND;
-       return 1;
-}
-
 /**
  * prepare a structure for \p AF_UNIX socket addresses
  *
@@ -471,22 +506,30 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
  */
 int init_tcp_socket(int port)
 {
-       int sockfd, ret;
        struct sockaddr_in my_addr;
+       int fd, ret = get_socket();
 
-       if ((sockfd = get_socket()) < 0)
-               return sockfd;
-       ret = setserversockopts(sockfd);
        if (ret < 0)
                return ret;
-       init_sockaddr(&my_addr, port, NULL);
-       ret = do_bind(sockfd, &my_addr);
+       fd = ret;
+       ret = setserversockopts(fd);
        if (ret < 0)
-               return ret;
-       if (listen(sockfd, BACKLOG) == -1)
-               return -E_LISTEN;
-       PARA_INFO_LOG("listening on port %d, fd %d\n", port, sockfd);
-       return sockfd;
+               goto err;
+       init_sockaddr(&my_addr, port, NULL);
+       ret = -E_BIND;
+       if (bind(fd, (struct sockaddr *)&my_addr,
+                       sizeof(struct sockaddr)) == -1) {
+               PARA_CRIT_LOG("bind error: %s\n", strerror(errno));
+               goto err;
+       }
+       ret = -E_LISTEN;
+       if (listen(fd, BACKLOG) == -1)
+               goto err;
+       PARA_INFO_LOG("listening on port %d, fd %d\n", port, fd);
+       return fd;
+err:
+       close(fd);
+       return ret;
 }
 
 /**
diff --git a/net.h b/net.h
index 9f6af7528b146886092da4fe3bcffb9ce23b0d3d..58732c0cbb857ece77b456a4457920b5e1057a77 100644 (file)
--- a/net.h
+++ b/net.h
@@ -28,6 +28,9 @@
 #define UNIX_PATH_MAX 108
 #endif
 
+typedef void crypt_function(unsigned long len,
+       const unsigned char *indata, unsigned char *outdata, void *private_data);
+
 #include <netdb.h> /* hostent */
 int get_host_info(char *host, struct hostent **ret);
 int get_socket(void);
@@ -45,4 +48,7 @@ int recv_cred_buffer(int, char *, size_t);
 ssize_t send_cred_buffer(int, char*);
 int recv_pattern(int fd, const char *pattern, size_t bufsize);
 int init_tcp_socket(int port);
+void enable_crypt(int fd, crypt_function *recv, crypt_function *send,
+       void *private_data);
+void disable_crypt(int fd);
 
diff --git a/recv.c b/recv.c
index 8639ce2b63c654fa36595adcd79a2b68fbfa79e4..555b7c51d1c554ea02340443cd3d431fedcc3c63 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -72,7 +72,6 @@ int main(int argc, char *argv[])
        struct stdout_task sot;
        struct sched s;
 
-       init_sched();
        s.default_timeout.tv_sec = 1;
        s.default_timeout.tv_usec = 0;
 
index e39719da37dde6ab5524fc5d81880f0e4edb326b..0393d7641abbae85e9645e260417551d4e7fea8b 100644 (file)
@@ -25,9 +25,6 @@
 #include "recv.h"
 #include "string.h"
 
-void (*crypt_function_recv)(unsigned long len, const unsigned char *indata, unsigned char *outdata) = NULL;
-void (*crypt_function_send)(unsigned long len, const unsigned char *indata, unsigned char *outdata) = NULL;
-
 DEFINE_RECEIVER_ARRAY;
 static void *parse_receiver_args(int receiver_num, char *options)
 {
diff --git a/sched.c b/sched.c
index c5b2c5ea1b4badf8b5f2e02fe94a03c9cdb6421f..c2a0e64b16326c5ce4f28b20cb4a048ae6322d30 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file sched.c paraslash's scheduling functions */
+
 #include <sys/time.h>
 #include "para.h"
 #include "ipc.h"
@@ -7,8 +27,8 @@
 #include "string.h"
 #include "error.h"
 
-struct list_head pre_select_list;
-struct list_head post_select_list;
+static struct list_head pre_select_list, post_select_list;
+static int initialized;
 
 static struct timeval now_struct;
 struct timeval *now = &now_struct;
@@ -42,9 +62,23 @@ static void sched_post_select(struct sched *s)
        }
 }
 
+/**
+ * the core function for all paraslash programs
+ *
+ * Short and sweet. It updates the global \a now pointer, calls all registered
+ * pre_select hooks which may set the timeout and add any file descriptors to
+ * the fd sets of \a s.  Next, it calls para_select() and makes the result available
+ * to the registered tasks by calling their post_select hook.
+ *
+ * \return Zero if no more tasks are left in either of the two lists, negative
+ * if para_select returned an error.
+ *
+ * \sa task, now.
+ */
 int sched(struct sched *s)
 {
-
+       if (!initialized)
+               return -E_NOT_INITIALIZED;
        gettimeofday(now, NULL);
 again:
        FD_ZERO(&s->rfds);
@@ -63,8 +97,31 @@ again:
        goto again;
 }
 
-void *register_task(struct task *t)
+/**
+ * initialize the paraslash scheduler
+ */
+static void init_sched(void)
+{
+       PARA_INFO_LOG("%s", "initializing scheduler\n");
+       INIT_LIST_HEAD(&pre_select_list);
+       INIT_LIST_HEAD(&post_select_list);
+       initialized = 1;
+};
+
+/**
+ * add a task to the scheduler
+ *
+ * \param t the task to add
+ *
+ * If the pre_select pointer of \a t is not \p NULL, it is added to
+ * the pre_select list of the scheduler. Same goes for post_select.
+ *
+ * \sa task::pre_select, task::post_select
+ */
+void register_task(struct task *t)
 {
+       if (!initialized)
+               init_sched();
        PARA_INFO_LOG("registering %s (%p)\n", t->status, t);
        if (t->pre_select) {
                PARA_DEBUG_LOG("pre_select: %p\n", &t->pre_select);
@@ -74,11 +131,20 @@ void *register_task(struct task *t)
                PARA_DEBUG_LOG("post_select: %p\n", &t->pre_select);
                list_add(&t->post_select_node, &post_select_list);
        }
-       return t;
 }
 
+/**
+ * remove a task from the scheduler
+ *
+ * \param t the task to remove
+ *
+ * If the pre_select pointer of \a t is not \p NULL, it is removed from
+ * the pre_select list of the scheduler. Same goes for \a post_select.
+ */
 void unregister_task(struct task *t)
 {
+       if (!initialized)
+               return;
        PARA_INFO_LOG("unregistering %s (%p)\n", t->status, t);
        if (t->pre_select)
                list_del(&t->pre_select_node);
@@ -86,27 +152,42 @@ void unregister_task(struct task *t)
                list_del(&t->post_select_node);
 };
 
-void init_sched(void)
-{
-       INIT_LIST_HEAD(&pre_select_list);
-       INIT_LIST_HEAD(&post_select_list);
-};
-
+/**
+ * unregister all tasks
+ *
+ * This will cause \a sched() to return immediately because both the
+ * \a pre_select_list and the \a post_select_list are empty.
+ */
 void sched_shutdown(void)
 {
        struct task *t, *tmp;
 
+       if (!initialized)
+               return;
        list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node)
                unregister_task(t);
        /* remove tasks which do not have a pre_select hook */
        list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node)
                unregister_task(t);
+       initialized = 0;
 };
 
+/**
+ * get the list of all registered tasks.
+ *
+ * \return the task list
+ *
+ * Each entry of the list contains an identifier which is simply a hex number
+ * that may be used in \a kill_task() to terminate the task.
+ * The result ist dynamically allocated and must be freed by the caller.
+ */
 char *get_task_list(void)
 {
        struct task *t, *tmp;
        char *msg = NULL;
+
+       if (!initialized)
+               return NULL;
        list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
                char *tmp_msg;
                tmp_msg = make_message("%s%p\tpre\t%s\n", msg? msg : "", t, t->status);
@@ -125,10 +206,24 @@ char *get_task_list(void)
        return msg;
 }
 
+/**
+ * simulate an error for the given task
+ *
+ * \param id the task identifier
+ *
+ * Find the task identified by \a id, set the tasks' return value to
+ * \p -E_TASK_KILLED and call the event handler of the task.
+ *
+ * \return Positive on success, negative on errors (e.g. if \a id does not
+ * correspond to a registered task).
+ */
 int kill_task(char *id)
 {
        struct task *t, *tmp;
        char buf[20];
+
+       if (!initialized)
+               return -E_NOT_INITIALIZED;
        list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
                sprintf(buf, "%p", t);
                if (strcmp(id, buf))
diff --git a/sched.h b/sched.h
index bd1ad796dfb4fa015e0053c05d83eeb5f5150704..bdca15f828cb60c177b7d8fb0cb497c1eb99b375 100644 (file)
--- a/sched.h
+++ b/sched.h
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file sched.h sched and task structures and exported symbols from sched.c */
+
+
+/**
+ * paraslash's scheduler
+ *
+ * desinged with KISS in mind. It manages two lists of tasks.  The pre_select
+ * list contains pointers to functions that are called before calling select()
+ * from the main loop. Similarly, \a post_select_list is a list of function
+ * pointers each of which is called after the select call. Tasks add hooks to
+ * these lists by registering themselves to the scheduler.
+ */
 struct sched {
-       struct timeval now, timeout;
+       /** initial value before any pre_select call */
+       struct timeval default_timeout;
+       /** the current timeout for the upcoming select call */
+       struct timeval timeout;
+       /** fds that should be watched for readability */
+       fd_set rfds;
+       /** fds that should be watched for writability */
+       fd_set wfds;
+       /** highest numbered file descriptor in any of the above fd sets */
        int max_fileno;
-       fd_set rfds, wfds;
+       /** the return value of the previous select call */
        int select_ret;
-       struct timeval default_timeout;
 };
 
+/**
+ * paraslash's task structure
+ *
+ * before registering a task to the scheduler, the task structure must be
+ * filled in properly by the caller.
+ *
+ * If one of these functions return a negative value via \a t->ret the
+ * (optional) event_handler gets called (it may also be called in case another
+ * event happend). In many cases the only possible event is an error or an eof
+ * condition and the event handler simply unregisters the task from the
+ * scheduler.
+ *
+ * \sa struct sched
+ */
 struct task {
+       /** pointer to the struct this task is embedded in */
        void *private_data;
-       int ret;
+       /**
+        * the pre select hook of \a t
+        *
+        * Its purpose is to add file descriptors to the fd sets of the
+        * scheduler and to decrease the select timeout if neccessary.
+        */
        void (*pre_select)(struct sched *s, struct task *t);
+       /**
+        * the postselect hook of \a t
+        *
+        * evaluate and act upon the results of the previous select call.
+        */
        void (*post_select)(struct sched *s, struct task *t);
+       /** gets called if pre_select or post_select returned an error */
        void (*event_handler)(struct task *t);
+       /** pre_select() and post_select store their return value here */
+       int ret;
+       /** position of the task in the pre_select list of the scheduler */
        struct list_head pre_select_node;
+       /** position of the task in the post_select list of the scheduler */
        struct list_head post_select_node;
+       /** descriptive text and current status of the task */
        char status[MAXLINE];
 };
 
+/**
+ * This is set by the scheduler at the beginning of its main loop.  It may be
+ * used (read-only) from everywhere. As none of the functions called by the
+ * scheduler are allowed to block, this value should be accurate enough so that
+ * there is no need to call gettimeofday() directly.
+ */
 extern struct timeval *now;
 
-void *register_task(struct task *t);
+void register_task(struct task *t);
 void unregister_task(struct task *t);
 int sched(struct sched *s);
-void init_sched(void);
 char *get_task_list(void);
 int kill_task(char *id);
index f4427df203466de0a3ca2163b6d4592edd214981..e0da4d546176e0010514def36894d5ffb5642ae0 100644 (file)
@@ -128,14 +128,14 @@ G_()
 lw(0.992126)
 la2(([(-6.0, 3.0), (1.0, 0.0), (-6.0, -3.0)], 0))
 b()
-bs(498.256,150.683,0)
-bs(498.256,72.1559,0)
+bs(497.249,328.901,0)
+bs(497.249,212.112,0)
 fp((0,0,0))
 le()
 lw(1)
 Fn('Times-Roman')
 Fs(14)
-txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,481.11,134.249))
+txt('pcm data',(-1.83691e-16,-1,1,-1.83691e-16,507.289,295.35))
 lw(0.992126)
 ld((0.10000000000000001, 1.0))
 b()
@@ -156,7 +156,7 @@ fp((0,0,0))
 le()
 lw(1)
 Fn('Times-Italic')
-txt('client',(334.671,487.931))
+txt('client',(355.816,498))
 G()
 lw(0.992126)
 la2(([(-6.0, 3.0), (1.0, 0.0), (-6.0, -3.0)], 0))
@@ -210,13 +210,13 @@ Fs(24)
 txt('para_client',(186.458,489.996))
 G()
 lw(1)
-r(143.312,0,0,-28.2587,413.631,59.6823)
+r(143.312,0,0,-28.2587,408.597,189.57)
 fp((0,0,0))
 le()
 lw(1)
 Fn('Times-Roman')
 Fs(24)
-txt('sound_device',(419.299,39.9609))
+txt('sound_device',(414.265,169.849))
 G_()
 G()
 lw(1)
@@ -248,14 +248,6 @@ Fn('Times-Roman')
 Fs(24)
 txt('para_gui',(40.4306,169.678))
 G_()
-lw(1)
-r(114.044,0,0,-28.2587,441.89,188.865)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(24)
-txt('para_play',(448.5,169.679))
 G()
 G()
 lw(0.992126)
@@ -298,7 +290,7 @@ le()
 lw(1)
 Fn('Times-Bold')
 Fs(36)
-txt('0.2.9',(437.217,715.454))
+txt('0.2.13',(425.134,712.433))
 G()
 lw(1)
 r(517.739,0,0,-28.2587,33.4706,653.823)
@@ -320,34 +312,6 @@ Fs(24)
 txt('para_audiod',(320.419,355.601))
 G_()
 G()
-G()
-lw(0.992126)
-la2(([(-6.0, 3.0), (1.0, 0.0), (-6.0, -3.0)], 0))
-b()
-bs(514.857,324.515,0)
-bs(514.857,209.912,0)
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('wav data',(-1.83691e-16,-1,1,-1.83691e-16,517.895,292.288))
-G_()
-G()
-fp((0,0,0))
-le()
-lw(1)
-Fn('Times-Roman')
-Fs(14)
-txt('fork & exec',(-1.83691e-16,-1,1,-1.83691e-16,484.591,300.646))
-lw(0.992126)
-la2(([(-6.0, 3.0), (1.0, 0.0), (-6.0, -3.0)], 0))
-b()
-bs(481.553,324.515,0)
-bs(481.553,209.912,0)
-G_()
-G_()
-G()
 fp((0,0,0))
 le()
 lw(1)
diff --git a/stdin.c b/stdin.c
index d33be3588b9211c8efa4a98991c9a6bd1b1ce2ca..8b18c2e2cef6c54975cde07285fd68207334d63b 100644 (file)
--- a/stdin.c
+++ b/stdin.c
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file stdin.c functions that deal with reading from stdin */
+
 #include "para.h"
 #include "string.h"
 #include "list.h"
 #include "error.h"
 #include "stdin.h"
 
+/**
+ * the pre_select function of the stdin task
+ *
+ * \param s the scheduler this task was registered to
+ * \param t the task structure of the stdin task
+ *
+ * This function is always successful. If there is space left in the
+ * buffer of the stdin task, it adds \p STDIN_FILENO to the read fd set
+ * of \a s.
+ */
 void stdin_pre_select(struct sched *s, struct task *t)
 {
        struct stdin_task *sit = t->private_data;
-       if (sit->loaded < sit->bufsize)
-               para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
-       t->ret = 1; /* success */
+       t->ret = 1;
+       sit->check_fd = 0;
+       if (sit->loaded >= sit->bufsize)
+               return;
+       sit->check_fd = 1;
+       para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
 }
 
 static void stdin_default_event_handler(struct task *t)
@@ -20,13 +53,24 @@ static void stdin_default_event_handler(struct task *t)
        unregister_task(t);
 }
 
+/**
+ * the post select function of the stdin task
+ *
+ * \param s the scheduler this task was registered to
+ * \param t the task structure of the stdin task
+ *
+ * This function checks if \p STDIN_FILENO was included by in the read fd set
+ * of \a s during the previous pre_select call.  If yes, and \p STDIN_FILENO
+ * appeears to be readable, data is read from stdin into the buffer of the
+ * stdin task.
+ */
 void stdin_post_select(struct sched *s, struct task *t)
 {
        struct stdin_task *sit = t->private_data;
        ssize_t ret;
 
        t->ret = 1;
-       if (sit->loaded >= sit->bufsize)
+       if (!sit->check_fd)
                return;
        if (!FD_ISSET(STDIN_FILENO, &s->rfds))
                return;
@@ -42,6 +86,16 @@ void stdin_post_select(struct sched *s, struct task *t)
                sit->eof = 1;
 }
 
+/**
+ * initialize a stdin task structure with default values
+ *
+ * \param sit the stdin task structure
+ *
+ * This fills in the pre/post select function poinzters of the task structure
+ * given by \a sot. It also sets up a default error handler which unregisters
+ * the task. Moreover, \a loaded and \a eof are set to zero and \a bufsize is
+ * initialized to 16 KB (but no buffer is allocated).
+ */
 void stdin_set_defaults(struct stdin_task *sit)
 {
        sit->bufsize = 16 * 1024,
@@ -51,5 +105,6 @@ void stdin_set_defaults(struct stdin_task *sit)
        sit->task.post_select = stdin_post_select;
        sit->task.event_handler = stdin_default_event_handler;
        sit->task.private_data = sit;
+       mark_fd_nonblock(STDIN_FILENO);
        sprintf(sit->task.status, "stdin reader");
 }
diff --git a/stdin.h b/stdin.h
index cb6cbfb6b3797f0f954d99d2c39e8369663be0aa..ee03922e06bd06fe2bc21d4f447a72766fe13e9c 100644 (file)
--- a/stdin.h
+++ b/stdin.h
@@ -1,8 +1,38 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file stdin.h the stdin task structure and exported symbols from stdin.c */
+
+/**
+ * the task structure used for reading from stdin
+ */
 struct stdin_task {
+       /** input buffer */
        char *buf;
+       /** the size of \a buf */
        size_t bufsize;
+       /** number of bytes currently loaded in \a buf */
        size_t loaded;
+       /** whether \p STDIN_FILENO was included in the read fd set */
+       int check_fd;
+       /** the task structure */
        struct task task;
+       /** non-zero on read error, or if a read from stdin returned zero */
        int eof;
 };
 
index e9277d999681fd1108fe26b8ec257bf7fe972d87..2e86fe965a022ac099c6c2effe5f582d10611c5a 100644 (file)
--- a/stdout.c
+++ b/stdout.c
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file stdout.c functions that deal with writing to stdout */
+
 #include "para.h"
 #include "string.h"
 #include "list.h"
 #include "error.h"
 #include "stdout.h"
 
+/**
+ * the pre_select function of the stdout task
+ *
+ * \param s the scheduler this task was registered to
+ * \param t the task structure of the stdout task
+ *
+ * This function is always successful. If there is data available in the input
+ * buffer, it adds \p STDOUT_FILENO to the write fd set of \a s.
+ */
 void stdout_pre_select(struct sched *s, struct task *t)
 {
        struct stdout_task *sot = t->private_data;
 
        t->ret = 1;
        sot->check_fd = 0;
-       if (!*sot->loaded)
+       if (!*sot->loaded) {
+               if (*sot->input_eof) {
+                       t->ret = -E_STDOUT_EOF;
+                       s->timeout.tv_sec = 0;
+                       s->timeout.tv_usec = 1;
+               }
                return;
+       }
        sot->check_fd = 1;
        para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
 }
 
+/**
+ * the post select function of the stdout task
+ *
+ * \param s the scheduler this task was registered to
+ * \param t the task structure of the stdout task
+ *
+ * This function checks if \p STDOUT_FILENO was included by in the write fd set
+ * of \a s during the previous pre_select call.  If yes, and \p STDOUT_FILENO
+ * appeears to be writable, the data loaded in the input buffer is written to
+ * stdout.
+ */
 void stdout_post_select(struct sched *s, struct task *t)
 {
        struct stdout_task *sot = t->private_data;
@@ -39,13 +85,21 @@ void stdout_post_select(struct sched *s, struct task *t)
        t->ret = 1;
 }
 
-void stdout_default_event_handler(struct task *t)
+static void stdout_default_event_handler(struct task *t)
 {
        PARA_NOTICE_LOG("%p: %s\n", t, PARA_STRERROR(-t->ret));
        unregister_task(t);
 }
 
-
+/**
+ * initialize a stdout task structure with default values
+ *
+ * \param sot the stdout task structure
+ *
+ * This fills in the pre/post select function poinzters of the task structure
+ * given by \a sot. It also sets up a default error handler which unregisters
+ * the task on errors and clears the eof flag of \a sot.
+ */
 void stdout_set_defaults(struct stdout_task *sot)
 {
        sot->task.private_data = sot;
@@ -53,5 +107,6 @@ void stdout_set_defaults(struct stdout_task *sot)
        sot->task.post_select = stdout_post_select;
        sot->task.event_handler = stdout_default_event_handler;
        sot->eof = 0;
+       mark_fd_nonblock(STDOUT_FILENO);
        sprintf(sot->task.status, "stdout writer");
 }
index 4dafafdec596d3ff60fd70dbc2291482165f71d0..e8d9d255cf75e8baedef649d750af1b4e909c3ae 100644 (file)
--- a/stdout.h
+++ b/stdout.h
@@ -1,11 +1,40 @@
-/** \file stdout.h common code for uitlities that write to stdout */
+/*
+ * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/** \file stdout.h the stdout task structure and exported symbols from stdout.c */
+
+/**
+ * the task structure used for writing to stdout
+ */
 struct stdout_task {
+       /** pointer to the data buffer */
        char *buf;
+       /** the size of \a buf */
        size_t *bufsize;
+       /** number of bytes loaded in \a buf */
        size_t *loaded;
+       /** pointer to the eof flag of the feeding task */
        int *input_eof;
+       /** non-zero if a write error occured */
        int eof;
+       /** the task structure */
        struct task task;
+       /** whether \p STDOUT_FILENO was included in the write fd set */
        int check_fd;
 };
 
index 78b862c933ced61396aadb6df04f43d8185eee21..2bdc7e89687cfe31235efb63b9a2af77469d460b 100644 (file)
--- a/string.c
+++ b/string.c
@@ -389,14 +389,19 @@ __must_check unsigned split_args(char *args, char ***argv_ptr, const char *delim
        char **argv;
        size_t n = 0, i, j;
 
-       while ((i = strcspn(p, delim)) && (p += i)) {
-               p += strspn(p, delim);
+       p = args + strspn(args, delim);
+       for (;;) {
+               i = strcspn(p, delim);
+               if (!i)
+                       break;
+               p += i;
                n++;
+               p += strspn(p, delim);
        }
        *argv_ptr = para_malloc((n + 1) * sizeof(char *));
        argv = *argv_ptr;
        i = 0;
-       p = args;
+       p = args + strspn(args, delim);
        while (p) {
                argv[i] = p;
                j = strcspn(p, delim);
diff --git a/write.c b/write.c
index 56b4401a53e3c418a250505d2e188cf7ce61da19..66cdd7f0c6f15870e5af6752f67b9e395bb749be 100644 (file)
--- a/write.c
+++ b/write.c
@@ -214,7 +214,6 @@ int main(int argc, char *argv[])
 
        cmdline_parser(argc, argv, &conf);
        init_supported_writers();
-       init_sched();
 
        wng = check_args();
        if (!wng)