From e9dd7df880c68a2d664ea10d167e6667707f9fd5 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 24 Jun 2016 21:38:24 +0200 Subject: [PATCH] Convert para_server to lopsub. This is the last executable to be converted to lopsub. acl_init() is removed from acl.c, and the initialization of the access control list is moved to init_sender_status() of send_common.c. It now takes an lls_opt_result argument and calls acl_add_entry() for each argument given. The latter function needs to be made public. In dccp_send.c we check the FEC parameters and change the gengetopt config structure to contain the default values if invalid FEC parameters were given. With lopsub this is no longer possible as the lls_parse_result structure is opaque. We copy the FEC parameters to a dccp_fec_client structure anyway, so is it easiest to move the check there. The patch removes the unused gengetopt option files but leaves the gengetopt infrastructure of the build system in place. The build system will be cleaned up in subsequent patches. --- Makefile.real | 4 +- acl.c | 23 +-- acl.h | 2 +- afs.c | 14 +- command.c | 4 +- configure.ac | 4 +- dccp_send.c | 39 ++-- http_send.c | 11 +- m4/gengetopt/color.m4 | 34 ---- m4/gengetopt/config_file.m4 | 14 -- m4/gengetopt/daemon.m4 | 10 - m4/gengetopt/group.m4 | 12 -- m4/gengetopt/header.m4 | 1 - m4/gengetopt/log_timing.m4 | 12 -- m4/gengetopt/logfile.m4 | 11 -- m4/gengetopt/loglevel.m4 | 22 --- m4/gengetopt/priority.m4 | 16 -- m4/gengetopt/server.m4 | 379 ------------------------------------ m4/gengetopt/user.m4 | 21 -- m4/lls/server.suite.m4 | 358 ++++++++++++++++++++++++++++++++++ send.h | 6 +- send_common.c | 24 ++- server.c | 226 ++++++++++++--------- server.h | 34 +++- udp_send.c | 22 ++- vss.c | 10 +- 26 files changed, 598 insertions(+), 715 deletions(-) delete mode 100644 m4/gengetopt/color.m4 delete mode 100644 m4/gengetopt/config_file.m4 delete mode 100644 m4/gengetopt/daemon.m4 delete mode 100644 m4/gengetopt/group.m4 delete mode 100644 m4/gengetopt/header.m4 delete mode 100644 m4/gengetopt/log_timing.m4 delete mode 100644 m4/gengetopt/logfile.m4 delete mode 100644 m4/gengetopt/loglevel.m4 delete mode 100644 m4/gengetopt/priority.m4 delete mode 100644 m4/gengetopt/server.m4 delete mode 100644 m4/gengetopt/user.m4 create mode 100644 m4/lls/server.suite.m4 diff --git a/Makefile.real b/Makefile.real index d4562018..2e2af483 100644 --- a/Makefile.real +++ b/Makefile.real @@ -43,7 +43,7 @@ all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \ $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \ $(write_objs) $(afh_objs) $(play_objs)) deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d))) -converted_executables := audioc client fade play recv write filter gui afh audiod +converted_executables := audioc client fade play recv write filter gui afh audiod server unconverted_executables := $(filter-out $(converted_executables), $(executables)) afh_objs += afh.lsg.o @@ -56,7 +56,7 @@ filter_objs += filter_cmd.lsg.o filter.lsg.o gui_objs += gui.lsg.o play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o recv_objs += recv_cmd.lsg.o recv.lsg.o -server_objs += server_cmd.lsg.o +server_objs += server_cmd.lsg.o server.lsg.o write_objs += write_cmd.lsg.o write.lsg.o m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(unconverted_executables))) diff --git a/acl.c b/acl.c index 560ff999..62677711 100644 --- a/acl.c +++ b/acl.c @@ -81,7 +81,7 @@ no_match: * \param addr The address to add. * \param netmask The netmask to use for this entry. */ -static void acl_add_entry(struct list_head *acl, char *addr, int netmask) +void acl_add_entry(struct list_head *acl, char *addr, int netmask) { struct access_info *ai = para_malloc(sizeof(struct access_info)); @@ -139,27 +139,6 @@ char *acl_get_contents(struct list_head *acl) return ret; } -/** - * Initialize an access control list. - * - * \param acl The list to initialize. - * \param acl_info An array of strings of the form ip/netmask. - * \param num The number of strings in \a acl_info. - */ -void acl_init(struct list_head *acl, char * const *acl_info, int num) -{ - char addr[16]; - int mask, i; - - INIT_LIST_HEAD(acl); - for (i = 0; i < num; i++) - if (parse_cidr(acl_info[i], addr, sizeof(addr), &mask) == NULL) - PARA_CRIT_LOG("ACL syntax error: %s, ignoring\n", - acl_info[i]); - else - acl_add_entry(acl, addr, mask); -} - /** * Check whether the peer name of a given fd is allowed by an acl. * diff --git a/acl.h b/acl.h index 5bfa39f2..91efa600 100644 --- a/acl.h +++ b/acl.h @@ -6,7 +6,7 @@ /** \file acl.h Exported functions of acl.c. */ -void acl_init(struct list_head *acl, char * const *acl_info, int num); +void acl_add_entry(struct list_head *acl, char *addr, int netmask); char *acl_get_contents(struct list_head *acl); int acl_check_access(int fd, struct list_head *acl, int default_deny); void acl_allow(char *addr, int mask, struct list_head *acl, int default_deny); diff --git a/afs.c b/afs.c index fbcffdde..3a752aca 100644 --- a/afs.c +++ b/afs.c @@ -18,8 +18,8 @@ #include #include +#include "server.lsg.h" #include "server_cmd.lsg.h" -#include "server.cmdline.h" #include "para.h" #include "error.h" #include "crypt.h" @@ -238,7 +238,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query, *(uint32_t *)buf = afs_socket_cookie; *(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid; - ret = connect_local_socket(conf.afs_socket_arg); + ret = connect_local_socket(OPT_STRING_VAL(AFS_SOCKET)); if (ret < 0) goto out; fd = ret; @@ -614,7 +614,7 @@ static int com_select(struct command_context *cc, struct lls_parse_result *lpr) } EXPORT_SERVER_CMD_HANDLER(select); -static void init_admissible_files(char *arg) +static void init_admissible_files(const char *arg) { if (activate_mood_or_playlist(arg, NULL) < 0) activate_mood_or_playlist(NULL, NULL); /* always successful */ @@ -623,7 +623,7 @@ static void init_admissible_files(char *arg) static int setup_command_socket_or_die(void) { int ret, socket_fd; - char *socket_name = conf.afs_socket_arg; + const char *socket_name = OPT_STRING_VAL(AFS_SOCKET); unlink(socket_name); ret = create_local_socket(socket_name, 0); @@ -655,8 +655,8 @@ static char *database_dir; static void get_database_dir(void) { if (!database_dir) { - if (conf.afs_database_dir_given) - database_dir = para_strdup(conf.afs_database_dir_arg); + if (OPT_GIVEN(AFS_DATABASE_DIR)) + database_dir = para_strdup(OPT_STRING_VAL(AFS_DATABASE_DIR)); else { char *home = para_homedir(); database_dir = make_message( @@ -997,7 +997,7 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd) goto out_close; PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n", server_socket, (unsigned) cookie); - init_admissible_files(conf.afs_initial_mode_arg); + init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE)); register_command_task(cookie, &s); s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 999 * 1000; diff --git a/command.c b/command.c index 89a6aeaf..fcae6da5 100644 --- a/command.c +++ b/command.c @@ -17,12 +17,12 @@ #include #include +#include "server.lsg.h" #include "para.h" #include "error.h" #include "crypt.h" #include "sideband.h" #include "command.h" -#include "server.cmdline.h" #include "string.h" #include "afh.h" #include "afs.h" @@ -402,7 +402,7 @@ static int com_si(struct command_context *cc, mmd->active_connections, mmd->num_commands, mmd->num_connects, - conf.loglevel_arg, + ENUM_STRING_VAL(LOGLEVEL), AUDIO_FORMAT_HANDLERS ); mutex_unlock(mmd_mutex); diff --git a/configure.ac b/configure.ac index a290c22b..6a50eacd 100644 --- a/configure.ac +++ b/configure.ac @@ -409,7 +409,6 @@ UNSTASH_FLAGS if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then build_server="yes" executables="$executables server" - server_cmdline_objs="server" server_errlist_objs=" server afh_common @@ -448,7 +447,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then wma_common sideband version - ggo " if test "$CRYPTOLIB" = openssl; then server_errlist_objs="$server_errlist_objs crypt" @@ -463,7 +461,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then if test $HAVE_FAAD = yes && test $HAVE_MP4V2 = yes; then server_errlist_objs="$server_errlist_objs aac_afh aac_common" fi - server_objs="add_cmdline($server_cmdline_objs) $server_errlist_objs" + server_objs="$server_errlist_objs" AC_SUBST(server_objs, add_dot_o($server_objs)) else build_server="no" diff --git a/dccp_send.c b/dccp_send.c index 92dd9333..61d42126 100644 --- a/dccp_send.c +++ b/dccp_send.c @@ -18,7 +18,9 @@ #include #include #include +#include +#include "server.lsg.h" #include "para.h" #include "error.h" #include "string.h" @@ -32,7 +34,6 @@ #include "fd.h" #include "close_on_fork.h" #include "chunk_queue.h" -#include "server.cmdline.h" #include "acl.h" static struct sender_status dccp_sender_status, *dss = &dccp_sender_status; @@ -91,6 +92,7 @@ static int dccp_init_fec(struct sender_client *sc) { int mps, ret; socklen_t ml = sizeof(mps); + uint32_t mss; /* max slize size */ /* If call fails, return some sensible minimum value */ ret = getsockopt(sc->fd, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &mps, &ml); @@ -100,8 +102,9 @@ static int dccp_init_fec(struct sender_client *sc) } PARA_INFO_LOG("current MPS = %d bytes\n", mps); assert(mps > 0); - if (conf.dccp_max_slice_size_arg > 0) - mps = PARA_MIN(mps, conf.dccp_max_slice_size_arg); + mss = OPT_UINT32_VAL(DCCP_MAX_SLICE_SIZE); + if (mss > 0 && mss <= INT_MAX) + mps = PARA_MIN(mps, (int)mss); return mps; } @@ -118,6 +121,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds) struct sender_client *sc; struct dccp_fec_client *dfc; int tx_ccid; + uint32_t k, n; sc = accept_sender_client(dss, rfds); if (!sc) @@ -143,8 +147,16 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds) } dfc = para_calloc(sizeof(*dfc)); sc->private_data = dfc; - dfc->fcp.data_slices_per_group = conf.dccp_data_slices_per_group_arg; - dfc->fcp.slices_per_group = conf.dccp_slices_per_group_arg; + k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP); + n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP); + if (k == 0 || n == 0 || k >= n) { + PARA_WARNING_LOG("invalid FEC parameters, using defaults\n"); + dfc->fcp.data_slices_per_group = 3; + dfc->fcp.slices_per_group = 4; + } else { + dfc->fcp.data_slices_per_group = k; + dfc->fcp.slices_per_group = n; + } dfc->fcp.init_fec = dccp_init_fec; dfc->fcp.send_fec = dccp_send_fec; dfc->fcp.need_periodic_header = false; @@ -216,7 +228,7 @@ static char *dccp_status(void) */ void dccp_send_init(struct sender *s) { - int ret, k, n; + int ret; s->status = dccp_status; s->send = NULL; @@ -232,18 +244,9 @@ void dccp_send_init(struct sender *s) s->client_cmds[SENDER_add] = NULL; s->client_cmds[SENDER_delete] = NULL; - k = conf.dccp_data_slices_per_group_arg; - n = conf.dccp_slices_per_group_arg; - - if (k <= 0 || n <= 0 || k >= n) { - PARA_WARNING_LOG("invalid FEC parameters, using defaults\n"); - conf.dccp_data_slices_per_group_arg = 3; - conf.dccp_slices_per_group_arg = 4; - } - - init_sender_status(dss, conf.dccp_access_arg, conf.dccp_access_given, - conf.dccp_port_arg, conf.dccp_max_clients_arg, - conf.dccp_default_deny_given); + init_sender_status(dss, OPT_RESULT(DCCP_ACCESS), + OPT_UINT32_VAL(DCCP_PORT), OPT_UINT32_VAL(DCCP_MAX_CLIENTS), + OPT_GIVEN(DCCP_DEFAULT_DENY)); ret = generic_com_on(dss, IPPROTO_DCCP); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); diff --git a/http_send.c b/http_send.c index 9d0f49ae..26536d07 100644 --- a/http_send.c +++ b/http_send.c @@ -13,11 +13,12 @@ #include #include #include +#include +#include "server.lsg.h" #include "para.h" #include "error.h" #include "string.h" -#include "server.cmdline.h" #include "afh.h" #include "server.h" #include "http.h" @@ -263,10 +264,10 @@ void http_send_init(struct sender *s) s->client_cmds[SENDER_add] = NULL; s->client_cmds[SENDER_delete] = NULL; - init_sender_status(hss, conf.http_access_arg, conf.http_access_given, - conf.http_port_arg, conf.http_max_clients_arg, - conf.http_default_deny_given); - if (conf.http_no_autostart_given) + init_sender_status(hss, OPT_RESULT(HTTP_ACCESS), + OPT_UINT32_VAL(HTTP_PORT), OPT_UINT32_VAL(HTTP_MAX_CLIENTS), + OPT_GIVEN(HTTP_DEFAULT_DENY)); + if (OPT_GIVEN(HTTP_NO_AUTOSTART)) return; ret = generic_com_on(hss, IPPROTO_TCP); if (ret < 0) diff --git a/m4/gengetopt/color.m4 b/m4/gengetopt/color.m4 deleted file mode 100644 index eb081786..00000000 --- a/m4/gengetopt/color.m4 +++ /dev/null @@ -1,34 +0,0 @@ - - -option "color" C -#~~~~~~~~~~~~~~~ -"activate color output" -enum typestr="when" -values = "yes","no","auto" -default = "auto" -optional - -option "log-color" - -#~~~~~~~~~~~~~~~~~~~ -"select a color for one type of log message" -string typestr="color_spec" -multiple -optional -details=" - The format of \"color_spec\" is [fg [bg]] [attr]. - - Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\", - \"red\", \"green\", \"yellow\", \"blue\", \"magenta\", - \"cyan\", and \"white\". - - The \"attr\" value must be one of \"bold\", \"dim\", \"ul\", - \"blink\", \"reverse\". - - Examples: - - --log-color \"debug:green\" - --log-color \"info:yellow bold\" - --log-color \"notice:white red bold\" -" - - diff --git a/m4/gengetopt/config_file.m4 b/m4/gengetopt/config_file.m4 deleted file mode 100644 index 29f66b44..00000000 --- a/m4/gengetopt/config_file.m4 +++ /dev/null @@ -1,14 +0,0 @@ - -option "config-file" c -#~~~~~~~~~~~~~~~~~~~~~ -"(default='DEFAULT_CONFIG_FILE')" -string typestr="filename" -optional -details=" - CURRENT_PROGRAM reads its config file right after parsing - the options that were given at the command line. If an - option is given both at the command line and in the - config file, the value that was specified at the command line - takes precedence. -" - diff --git a/m4/gengetopt/daemon.m4 b/m4/gengetopt/daemon.m4 deleted file mode 100644 index ebead6a8..00000000 --- a/m4/gengetopt/daemon.m4 +++ /dev/null @@ -1,10 +0,0 @@ - -option "daemon" d -#~~~~~~~~~~~~~~~~ -"run as background daemon" -flag off -details = " - If this option is given and no logfile was specified, all - messages go to /dev/null. -" - diff --git a/m4/gengetopt/group.m4 b/m4/gengetopt/group.m4 deleted file mode 100644 index 2a59ad9a..00000000 --- a/m4/gengetopt/group.m4 +++ /dev/null @@ -1,12 +0,0 @@ -option "group" g -#~~~~~~~~~~~~~~~ -"set group id" -string typestr="group" -optional -details=" - This option sets the group id according to 'group'. This option - is silently ignored if EUID != 0. Otherwise, real/effective - GID and the saved set-group ID are all set to the GID given by - 'group'. Must not be given in the config file. -" - diff --git a/m4/gengetopt/header.m4 b/m4/gengetopt/header.m4 deleted file mode 100644 index c4231879..00000000 --- a/m4/gengetopt/header.m4 +++ /dev/null @@ -1 +0,0 @@ -changequote(,) diff --git a/m4/gengetopt/log_timing.m4 b/m4/gengetopt/log_timing.m4 deleted file mode 100644 index ac0ea841..00000000 --- a/m4/gengetopt/log_timing.m4 +++ /dev/null @@ -1,12 +0,0 @@ - -option "log-timing" T -#~~~~~~~~~~~~~~~~~~~~ -"show milliseconds in log messages" -flag off -details = " - Selecting this option causes milliseconds to be included in - the log message output. This allows to measure the interval - between log messages in milliseconds which is useful for - identifying timing problems. -" - diff --git a/m4/gengetopt/logfile.m4 b/m4/gengetopt/logfile.m4 deleted file mode 100644 index 070d736b..00000000 --- a/m4/gengetopt/logfile.m4 +++ /dev/null @@ -1,11 +0,0 @@ - -option "logfile" L -#~~~~~~~~~~~~~~~~~ -"where to write log output" -string typestr="filename" -optional -details=" - If this option is not given, CURRENT_PROGRAM writes the log - messages to stderr. -" - diff --git a/m4/gengetopt/loglevel.m4 b/m4/gengetopt/loglevel.m4 deleted file mode 100644 index 162d030b..00000000 --- a/m4/gengetopt/loglevel.m4 +++ /dev/null @@ -1,22 +0,0 @@ - -option "loglevel" l -#~~~~~~~~~~~~~~~~~~ -"set loglevel" -string typestr="level" -values = "debug","info","notice","warning","error","crit","emerg" -default="warning" -optional -details=" - Log only messages with severity greater or equal the given - value. - - debug: Produces really noisy output. - info: Still noisy, but won't fill up the disk quickly. - notice: Indicates normal, but significant event. - warning: Unexpected events that can be handled. - error: Unhandled error condition. - crit: System might be unreliable. - emerg: Last message before exit. -" - - diff --git a/m4/gengetopt/priority.m4 b/m4/gengetopt/priority.m4 deleted file mode 100644 index 0b37dc06..00000000 --- a/m4/gengetopt/priority.m4 +++ /dev/null @@ -1,16 +0,0 @@ -option "priority" - -#~~~~~~~~~~~~~~~~~~ -"adjust scheduling priority" -int typestr = "prio" -default = "0" -optional -details = " - The priority (also known as nice value) is a value in the range -20 - to 19. Lower priorities cause more favorable scheduling. Since only - privileged processes may request a negative priority, specifying - a negative value works only if the daemon is started with root - privileges. - - Failure to set the given priority value is not considered an error - but a log message is printed in this case. -" diff --git a/m4/gengetopt/server.m4 b/m4/gengetopt/server.m4 deleted file mode 100644 index 48e7a1f4..00000000 --- a/m4/gengetopt/server.m4 +++ /dev/null @@ -1,379 +0,0 @@ -args "--conf-parser --no-handle-version --no-handle-help" - -purpose "Manage and stream audio files" - -include(header.m4) -define(CURRENT_PROGRAM,para_server) -define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf) - - -######################### -section "General options" -######################### - - -include(loglevel.m4) -include(log_timing.m4) -include(color.m4) -include(daemon.m4) -include(user.m4) -include(group.m4) -include(priority.m4) - - -option "port" p -#~~~~~~~~~~~~~~ -"listening port" -int typestr="portnumber" -default="2990" -optional -details=" - para_server listens on this tcp port for incoming connections - from clients such as para_client. If the default port is - changed, the corresponding option of para_client must be used - to connect to para_server. -" - -############################# -section "Configuration files" -############################# - - -include(logfile.m4) -include(config_file.m4) - - -option "user-list" - -#~~~~~~~~~~~~~~~~~~~ -"(default='~/.paraslash/server.users')" - -string typestr="filename" -optional - - -################################## -section "virtual streaming system" -################################## - - -option "autoplay" a -#~~~~~~~~~~~~~~~~~~ -"start playing on startup" -flag off - -option "autoplay-delay" - -#~~~~~~~~~~~~~~~~~~~~~~~~ -"time to wait before streaming" -int typestr="ms" -default="0" -optional -dependon="autoplay" -details=" - If para_server is started with the autoplay option, this option - may be used to set up a delay before para_server streams its - first audio file. This is useful for example if para_server - and para_audiod are started during system startup. The delay - time should be choosen large enough so that para_audiod is - already up when para_server starts to stream. Of course, this - option depends on the autoplay option. -" -option "announce-time" A -#~~~~~~~~~~~~~~~~~~~~~~~ -"grace time for clients" - -int typestr="ms" -default="300" -optional -details=" - Clients such as para_audiod connect to para_server and execute - the stat command to find out whether an audio stream is - currently available. This sets the delay betweeen announcing - the stream via the output of the stat command and sending - the first chunk of data. -" - -############################# -section "audio file selector" -############################# - -option "afs-database-dir" D -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"location of the database" -string typestr="path" -optional -details=" - Where para_server should look for the osl database of the audio - file selector. The default is '~/.paraslash/afs_database-0.4'. -" - -option "afs-socket" s -#~~~~~~~~~~~~~~~~~~~~ -"Command socket for afs" -string typestr="path" -default="/var/paraslash/afs_command_socket-0.4" -optional -details=" - For each server command that is handled by the audio file - selector, the child process of para_server connects to the - audio file selector via a local socket. This option specifies - the location of that socket in the file system. -" -option "afs-initial-mode" i -#~~~~~~~~~~~~~~~~~~~~~~~~~~ - -"Mood or playlist to load on startup." -string typestr="/" -optional - -details=" - The argument of this option must be prefixed with either 'p/' - or 'm/' to indicate whether a playlist or a mood should be - loaded. Example: - --afs-initial-mode p/foo - loads the playlist named 'foo'. -" - -##################### -section "http sender" -##################### - - -option "http-port" - -#~~~~~~~~~~~~~~~~~~~ -"tcp port for http streaming" -int typestr="portnumber" -default="8000" -optional -details=" - The http sender of para_server listens on this port for - incoming connections. Clients are expected to send the usual - http request message such as 'GET / HTTP/'. -" - -option "http-default-deny" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"make the http ACL a whitelist" -flag off -details=" - The default is to use blacklists instead, i.e. connections - to the http sender are allowed unless the connecting host - matches a pattern given by a http-access option. This allows - to use access control the other way round: Connections are - denied from hosts which are not explicitly allowed by one or - more http-access options. -" - -option "http-access" - -#~~~~~~~~~~~~~~~~~~~~~ -"add an entry to the http ACL" -string typestr="a.b.c.d/n" -optional -multiple -details=" - Add given host/network to access control list (whitelist if - http-default-deny was given, blacklist otherwise) before - opening the tcp port. This option can be given multiple - times. Example: '192.168.0.0/24' whitelists/blacklists the - 256 hosts 192.168.0.x -" - -option "http-no-autostart" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"do not open tcp port on startup" -flag off -details=" - If this option is given, the http sender does not listen on - its tcp port. It may be instructed to open this port at a - later time by using the sender command. -" - -option "http-max-clients" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"maximal number of connections" -int typestr="number" -default="-1" -optional -details=" - The http sender will refuse connections if already that number - of clients are currently connected. A non-positive value - (the default) allows an unlimited number of simultaneous - connections. -" - -##################### -section "dccp sender" -##################### - - -option "dccp-port" - -#~~~~~~~~~~~~~~~~~~~ -"port for dccp streaming" -int typestr="portnumber" -default="8000" -optional -details=" - See http-port for details. -" - -option "dccp-default-deny" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"make the dccp ACL a whitelist" -flag off -details=" - See http-default-deny for details. -" - -option "dccp-access" - -#~~~~~~~~~~~~~~~~~~~~~ -"add an entry to the dccp ACL" -string typestr="a.b.c.d/n" -optional -multiple -details=" - See http-access for details. -" - -option "dccp-max-clients" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"maximal number of connections" -int typestr="number" -default="-1" -optional -details=" - See http-max-clients for details. -" - -option "dccp-max-slice-size" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"Upper bound for the FEC slice size" -int typestr = "size" -optional -default = "0" -details = " - If this value is non-positive (the default) the dccp sender - uses the maximum packet size (MPS) of the connection as the - slice size. The MPS is a network parameter and depends on - the path maximum transmission unit (path MTU) of an incoming - connection, i.e. on the largest packet size that can be - transmitted without causing fragmentation. - - This option allows to use a value less than the MPS in order - to fine-tune application performance. Values greater than - the MPS of an incoming connection can not be set. -" - -option "dccp-data-slices-per-group" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"The number of non-redundant slices per FEC group" -int typestr = "num" -optional -default = "3" -details = " - This determines the number of slices in each FEC group that are - necessary to decode the group. The given number must be smaller - than the value of the dccp-slices-per-group option below. - - Note that the duration of a FEC group is proportional to the - product dccp-max-slice-size * dccp-data-slices-per-group. -" - -option "dccp-slices-per-group" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"The total number of slices per FEC group" -int typestr = "num" -optional -default = "4" -details = " - This value must be larger than the value of the argument to - --dccp-data-slices-per-group. The difference of the two values is - the number of redundant slices, that is, the number of slices which - may be lost without causing interruptions in the audio stream. - - Increase this value if you are on a lossy network. -" - -#################### -section "udp sender" -#################### - -option "udp-target" - -#~~~~~~~~~~~~~~~~~~~~ -"add udp target with optional port" -string typestr="host[:port]" -optional -multiple -details=" - Add given host/port to the list of targets. The 'host' argument - can be either an IPv4/v6 address or hostname (RFC 3986 syntax). - The 'port' argument is an optional port number. If the 'port' - part is absent, the 'udp-default-port' value is used. - - The following examples are possible targets: - '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port); - '224.0.1.38:1500' (IPv4 multicast); 'localhost:8001' (hostname - with port); '[::1]:8001' (IPv6 localhost); '[badc0de::1]' (IPv6 - host with default port); '[FF00::beef]:1500' (IPv6 multicast). - - This option can be given multiple times, for multiple targets. -" - -option "udp-no-autostart" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"do not start sending" -flag off -details=" - If this option is given, udp streaming may be activated at - a later time by using the sender command. -" - -option "udp-default-port" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"udp port to send to" -int typestr="port" -default="8000" -optional - -option "udp-mcast-iface" - -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"outgoing udp multicast interface" -string -optional - -option "udp-header-interval" H -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -"duration for sending header" -int typestr = "ms" -default = "2000" -optional -details = " - As the udp sender has no idea about connected clients it - sends the audio file header periodically if necessary. This - option specifies the duration between subsequent headers are - sent. Smaller values decrease the average time clients have - to wait before starting playback, larger values decrease - network traffic. - - Note that this affects only ogg/* and wma streams. Other - audio formats, including mp3, don't need an audio file header. -" - -option "udp-ttl" t -#~~~~~~~~~~~~~~~~~ -"set time to live value" -int typestr="num" -default="-1" -optional -details=" - This option applies exclusively to multicast UDPv4/v6 streaming. - - For the sending UDPv4 socket it sets the multicast Time-To-Live - value to \"num\". Traditional TTL scope values are: 0=host, - 1=network, 32=same site, 64=same region, 128=same continent, - 255=unrestricted. Please note however that this scoping is not - a good solution: RFC 2365 e.g. presents a better alternative. - - When using UDPv6 multicasting, the option sets the number of - multicast hops (as described in RFC 3493); a value of -1 - allows the kernel to auto-select the hop value. -" - diff --git a/m4/gengetopt/user.m4 b/m4/gengetopt/user.m4 deleted file mode 100644 index 1bd5c59a..00000000 --- a/m4/gengetopt/user.m4 +++ /dev/null @@ -1,21 +0,0 @@ - -option "user" u -#~~~~~~~~~~~~~~ -"run as the given user" -string typestr="name" -optional -details=" - CURRENT_PROGRAM does not need any special privileges. - - If started as root (EUID == 0) this option must - be given at the command line (not in the configuration - file) so that CURRENT_PROGRAM can drop the root - privileges right after parsing the command line options, - but before parsing the configuration file. In this case, - real/effective/saved UID are all set to the UID of 'name'. As - the configuration file is read afterwards, those options that - have a default value depending on the UID (e.g. the directory - for the configuration file) are computed by using the uid of - 'name'. This option has no effect if CURRENT_PROGRAM - is started as a non-root user (i.e. EUID != 0). -" diff --git a/m4/lls/server.suite.m4 b/m4/lls/server.suite.m4 new file mode 100644 index 00000000..5bba85d4 --- /dev/null +++ b/m4/lls/server.suite.m4 @@ -0,0 +1,358 @@ +m4_define(PROGRAM, para_server) +m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/server.conf) +[suite server] +version-string = GIT_VERSION() +[supercommand para_server] + purpose = manage and stream audio files + [description] + para_server streams audio files over a local or remote network. It + is controlled by para_client(1), which connects para_server through + the paraslash control service. + + On startup the server spawns a second process, the audio file selector, + which maintains the database of all known audio files. This database + contains file format, duration and tag information of each known file + and statistics such as last-played time, and the number of times each + file was streamed. Lyrics and cover art may be added to the database + and associated with one or more audio files. + + Besides ordinary playlists the audio file selector supports so-called + moods. Moods instruct the server to determine the files to be streamed + and their order in terms of properties stored in the database. + [/description] + m4_include(common-option-section.m4) + m4_include(help.m4) + m4_include(detailed-help.m4) + m4_include(version.m4) + m4_include(config-file.m4) + m4_include(priority.m4) + m4_include(daemon.m4) + m4_include(logfile.m4) + m4_include(user.m4) + m4_include(group.m4) + m4_include(loglevel.m4) + m4_include(log-timing.m4) + m4_include(color.m4) + m4_include(per-command-options-section.m4) + [option port] + short_opt = p + summary = listening port of the paraslash control service + arg_info = required_arg + arg_type = uint32 + typestr = portnumber + default_val = 2990 + [help] + para_server listens on this TCP port for incoming connections + from clients such as para_client. If the default port is changed, + the corresponding option of para_client must be used to connect + to para_server. + [/help] + [option user-list] + summary = file which contains user names and credentials + arg_info = required_arg + arg_type = string + typestr = path + [help] + This file contains one line per user of the form + + user + + See the manual for more information. + [/help] + [option vss] + summary = Options for the virtual streaming system + flag ignored + [option autoplay] + summary = start streaming on startup + short_opt = a + [help] + The default is to defer streaming until para_client connects and + executes the "play" command. + [/help] + [option autoplay-delay] + summary = time to wait before streaming + arg_info = required_arg + arg_type = uint32 + typestr = milliseconds + default_val = 0 + [help] + This option is ignored if --autplay is not given. Otherwise, its + argument defines for how long streaming is delayed at startup. + + This is useful in init scripts to set the delay large enough to make + sure para_audiod is up when para_server starts to stream. + [/help] + [option announce-time] + short_opt = A + summary = grace time for data connections + arg_info = required_arg + arg_type = uint32 + typestr = milliseconds + default_val = 300 + [help] + para_server tells para_audiod through the control service connection + whether an audio stream is currently available. This option defines + the delay between announcing the stream and sending the first chunk + of audio data. + [/help] + [option afs] + summary = Options for the audio file selector + flag ignored + [option afs-database-dir] + summary = location of the afs database + short_opt = D + arg_info = required_arg + arg_type = string + typestr = directory + [help] + The directory which contains the database for the audio file + selector. The default is ~/.paraslash/afs_database-0.4. + + If no database was found, the "init" command must be executed to + initialize the database. Once initialized, audio files may added with + the "add" command. + [/help] + [option afs-socket] + summary = socket for afs connections + short_opt = s + arg_info = required_arg + arg_type = string + typestr = path + default_val = /var/paraslash/afs_command_socket-0.4 + [help] + Server commands communicate with the audio file selector, via a + local socket. This option specifies the location of the socket in + the file system. + [/help] + [option afs-initial-mode] + summary = mood or playlist to load on startup + short_opt = i + arg_info = required_arg + arg_type = string + typestr = specifier/name + [help] + The argument of this option consists of a prefix, either 'm/' or + 'p/', to indicate whether a mood or a playlist should be loaded, + followed by the name of the mood or playlist. Example: + + --afs-initial-mode p/foo + + loads the playlist named "foo". + + If this option is not given, the dummy mood is loaded at startup. + [/help] + [option http] + summary = Options for the http sender + flag ignored + [option http-port] + summary = TCP port for http streaming + arg_info = required_arg + arg_type = uint32 + typestr = portnumber + default_val = 8000 + [help] + The http sender of para_server listens on this port for incoming + connections. Clients are expected to send the usual http request + message such as 'GET / HTTP/'. + [/help] + [option http-default-deny] + summary = make the http access control list a whitelist + [help] + The default is to use blacklists, i.e. connections to the http sender + are allowed unless the connecting host matches a pattern given by a + http-access option. This allows to use access control the other way + round: Connections are denied from hosts which are not explicitly + allowed by one or more http-access options. + [/help] + [option http-access] + summary = add an entry to the http access control list + arg_info = required_arg + arg_type = string + typestr = a.b.c.d/n + flag multiple + [help] + Add the given host/network to access control list (whitelist if + http-default-deny was given, blacklist otherwise) before opening + the tcp port. This option can be given multiple times. Example: + + --http-access 192.168.0.0/24 + + whitelists/blacklists the 256 hosts 192.168.0.x. + + This option may be given multiple times to blacklist/whitelist any + number of hosts or networks. + [/help] + [option http-no-autostart] + summary = do not open TCP port for http streaming on startup + [help] + If this option is given, the http sender does not listen on its TCP + port until the "sender" command is executed to open the port. + [/help] + [option http-max-clients] + summary = maximal number of simultaneous http connections + arg_info = required_arg + arg_type = int32 + typestr = number + default_val = -1 + [help] + The http sender will refuse connections if already that number of + clients are currently connected. A non-positive value (the default) + allows for an unlimited number of simultaneous connections. + [/help] + [option dccp] + summary = Options for the dccp sender + flag ignored + [option dccp-port] + summary = port for dccp streaming + arg_info = required_arg + arg_type = uint32 + typestr = portnumber + default_val = 8000 + [help] + See --http-port for details. + [/help] + [option dccp-default-deny] + summary = make the dccp access control list a whitelist + [help] + See http-default-deny for details. + [/help] + [option dccp-access] + summary = add an entry to the dccp access control list + arg_info = required_arg + arg_type = string + typestr = a.b.c.d/n + flag multiple + [help] + See --http-access for details. + [/help] + [option dccp-max-clients] + summary = maximal number of simultaneous dccp connections + arg_info = required_arg + arg_type = int32 + typestr = number + default_val = -1 + [help] + See --http-max-clients for details. + [/help] + [option dccp-max-slice-size] + summary = upper bound for the FEC slice size + arg_info = required_arg + arg_type = uint32 + typestr = bytes + default_val = 0 + [help] + If this value is zero (the default) the dccp sender uses the maximum + packet size (MPS) of the connection as the slice size. The MPS is a + network parameter and depends on the path maximum transmission unit + (path MTU) of an incoming connection, i.e. on the largest packet size + that can be transmitted without causing fragmentation. + + This option allows to use a value less than the MPS in order to + fine-tune application performance. Values greater than the MPS of an + incoming connection can not be set. + [/help] + [option dccp-data-slices-per-group] + summary = the number of non-redundant slices per FEC group + arg_info = required_arg + arg_type = uint32 + typestr = count + default_val = 3 + [help] + This determines the number of slices in each FEC group which are + necessary to decode the group. The given number must be smaller than + the argument to the --dccp-slices-per-group option below. + + Note that the duration of a FEC group is proportional to the + product dccp-max-slice-size * dccp-data-slices-per-group. + [/help] + [option dccp-slices-per-group] + summary = the total number of slices per FEC group + arg_info = required_arg + arg_type = uint32 + typestr = count + default_val = 4 + [help] + This value must be larger than the value of the argument to + --dccp-data-slices-per-group. The difference of the two values is + the number of redundant slices, that is, the number of slices which + may be lost without causing interruptions in the audio stream. + + Increase this value if you are on a lossy network. + [/help] + [option udp] + summary = Options for the udp sender + flag ignored + [option udp-target] + summary = add udp target with optional port + arg_info = required_arg + arg_type = string + typestr = host[:port] + flag multiple + [help] + Add the given host/port to the list of targets. The "host" argument + can be either an IPv4/v6 address or hostname (RFC 3986 syntax). The + "port" argument is an optional port number. If the "port" part is + absent, the "--udp-default-port" value (see below) is used. + + The following examples are possible targets: "10.10.1.2:8000" + (host:port); "10.10.1.2" (with default port); "224.0.1.38:1500" + (IPv4 multicast); "localhost:8001" (hostname with port); "[::1]:8001" + (IPv6 localhost); "[badc0de::1]" (IPv6 host with default port); + "[FF00::beef]:1500" (IPv6 multicast). + + This option can be given multiple times, for multiple targets. + [/help] + [option udp-default-port] + summary = default port for udp targets + arg_info = required_arg + arg_type = uint32 + typestr = portnumber + default_val = 8000 + [option udp-no-autostart] + summary = do not send the audio stream to UDP targets + [help] + If this option is given, udp streaming may be activated at a later + time by executing the "sender" command. + [/help] + [option udp-mcast-iface] + summary = outgoing udp multicast interface + arg_info = required_arg + arg_type = string + typestr = interface + [option udp-header-interval] + short_opt = H + summary = duration for sending header + arg_info = required_arg + arg_type = uint32 + typestr = milliseconds + default_val = 2000 + [help] + As the udp sender has no idea about connected clients it sends the + audio file header periodically if necessary. This option specifies the + duration between subsequent headers are sent. Smaller values decrease + the average time clients have to wait before starting playback, + larger values decrease network traffic. + + Note that this affects only ogg/* and wma streams. Other audio formats, + including mp3, don't need an audio file header. + [/help] + [option udp-ttl] + short_opt = t + summary = set time to live value + arg_info = required_arg + arg_type = int32 + typestr = num + default_val = -1 + [help] + This option applies exclusively to multicast UDPv4/v6 streaming. + + For the sending UDPv4 socket it sets the multicast Time-To-Live value + to "num". Traditional TTL scope values are: 0=host, 1=network, 32=same + site, 64=same region, 128=same continent, 255=unrestricted. Please + note however that this scoping is not a good solution: RFC 2365 + e.g. presents a better alternative. + + When using UDPv6 multicasting, the option sets the number of multicast + hops (as described in RFC 3493); a value of -1 allows the kernel to + auto-select the hop value. + [/help] diff --git a/send.h b/send.h index 0c74f0ea..54205234 100644 --- a/send.h +++ b/send.h @@ -182,10 +182,10 @@ struct sender_status { void shutdown_client(struct sender_client *sc, struct sender_status *ss); void shutdown_clients(struct sender_status *ss); -void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args, - int port, int max_clients, int default_deny); +void init_sender_status(struct sender_status *ss, + const struct lls_opt_result *acl_opt_result, int port, + int max_clients, int default_deny); char *generic_sender_status(struct sender_status *ss, const char *name); - void generic_com_allow(struct sender_command_data *scd, struct sender_status *ss); void generic_com_deny(struct sender_command_data *scd, diff --git a/send_common.c b/send_common.c index a9df02a9..988e8d48 100644 --- a/send_common.c +++ b/send_common.c @@ -134,19 +134,33 @@ int send_queued_chunks(int fd, struct chunk_queue *cq) * Initialize a struct sender status. * * \param ss The struct to initialize. - * \param access_arg The array of access arguments given at the command line. - * \param num_access_args The number of elements in \a access_arg. + * \param acl_opt_result Contains array of --{http|dccp}-access arguments. * \param port The tcp or dccp port to listen on. * \param max_clients The maximal number of simultaneous connections. * \param default_deny Whether a blacklist should be used for access control. */ -void init_sender_status(struct sender_status *ss, char **access_arg, - int num_access_args, int port, int max_clients, int default_deny) +void init_sender_status(struct sender_status *ss, + const struct lls_opt_result *acl_opt_result, int port, + int max_clients, int default_deny) { + int i; + ss->listen_fd = -1; INIT_LIST_HEAD(&ss->client_list); ss->port = port; - acl_init(&ss->acl, access_arg, num_access_args); + + /* Initialize an access control list */ + INIT_LIST_HEAD(&ss->acl); + for (i = 0; i < lls_opt_given(acl_opt_result); i++) { + const char *arg = lls_string_val(i, acl_opt_result); + char addr[16]; + int mask; + if (!parse_cidr(arg, addr, sizeof(addr), &mask)) + PARA_WARNING_LOG("ACL syntax error: %s, ignoring\n", + arg); + else + acl_add_entry(&ss->acl, addr, mask); + } ss->num_clients = 0; ss->max_clients = max_clients; ss->default_deny = default_deny; diff --git a/server.c b/server.c index a0eb50e2..43ede2a9 100644 --- a/server.c +++ b/server.c @@ -42,10 +42,10 @@ #include #include +#include "server.lsg.h" #include "para.h" #include "error.h" #include "crypt.h" -#include "server.cmdline.h" #include "afh.h" #include "string.h" #include "afs.h" @@ -63,7 +63,6 @@ #include "signal.h" #include "user_list.h" #include "color.h" -#include "ggo.h" #include "version.h" /** Array of error strings. */ @@ -81,12 +80,15 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log; struct misc_meta_data *mmd; /** - * The configuration of para_server + * The active value for all config options of para_server. * - * It also contains the options for the audio file selector, audio format - * handler and all supported senders. + * It is computed by merging the parse result of the command line options with + * the parse result of the config file. */ -struct server_args_info conf; +struct lls_parse_result *server_lpr = NULL; + +/* Command line options (no config file options). Used in handle_sighup(). */ +static struct lls_parse_result *cmdline_lpr; /** A random value used in child context for authentication. */ uint32_t afs_socket_cookie; @@ -94,9 +96,6 @@ uint32_t afs_socket_cookie; /** The mutex protecting the shared memory area containing the mmd struct. */ int mmd_mutex; -/** The file containing user information (public key, permissions). */ -static char *user_list_file = NULL; - static struct sched sched; static struct signal_task *signal_task; @@ -163,71 +162,97 @@ err_out: /** * (Re-)read the server configuration files. * - * \param override Passed to gengetopt to activate the override feature. + * \param reload Whether config file overrides command line. * - * This function also re-opens the logfile and sets the global \a - * user_list_file variable. + * This function also re-opens the logfile and the user list. On SIGHUP it is + * called from both server and afs context. */ -void parse_config_or_die(int override) +void parse_config_or_die(bool reload) { - char *home = para_homedir(); int ret; - char *cf; + char *cf = NULL, *errctx = NULL, *user_list_file = NULL; + void *map; + size_t sz; + int cf_argc; + char **cf_argv; + struct lls_parse_result *cf_lpr, *merged_lpr; + char *home = para_homedir(); daemon_close_log(); - if (conf.config_file_given) - cf = para_strdup(conf.config_file_arg); + if (OPT_GIVEN(CONFIG_FILE)) + cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE)); else cf = make_message("%s/.paraslash/server.conf", home); - free(user_list_file); - if (!conf.user_list_given) - user_list_file = make_message("%s/.paraslash/server.users", home); - else - user_list_file = para_strdup(conf.user_list_arg); - ret = file_exists(cf); - if (conf.config_file_given && !ret) { - ret = -1; - PARA_EMERG_LOG("can not read config file %s\n", cf); - goto out; + if (!mmd || getpid() != mmd->afs_pid) { + if (OPT_GIVEN(USER_LIST)) + user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST)); + else + user_list_file = make_message("%s/.paraslash/server.users", home); } - if (ret) { - int tmp = conf.daemon_given; - struct server_cmdline_parser_params params = { - .override = override, - .initialize = 0, - .check_required = 1, - .check_ambiguity = 0, - .print_errors = !conf.daemon_given - }; - server_cmdline_parser_config_file(cf, &conf, ¶ms); - daemon_set_loglevel(conf.loglevel_arg); - conf.daemon_given = tmp; + free(home); + ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); + if (ret < 0) { + if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) + goto free_cf; + if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE)) + goto free_cf; + ret = 0; + server_lpr = cmdline_lpr; + goto success; } - if (conf.logfile_given) { - daemon_set_logfile(conf.logfile_arg); + ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx)); + para_munmap(map, sz); + if (ret < 0) + goto free_cf; + cf_argc = ret; + ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx)); + lls_free_argv(cf_argv); + if (ret < 0) + goto free_cf; + if (reload) /* config file overrides command line */ + ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr, + &errctx)); + else /* command line options overrride config file options */ + ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr, + &errctx)); + lls_free_parse_result(cf_lpr, CMD_PTR); + if (ret < 0) + goto free_cf; + if (server_lpr != cmdline_lpr) + lls_free_parse_result(server_lpr, CMD_PTR); + server_lpr = merged_lpr; +success: + daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL)); + if (OPT_GIVEN(LOGFILE)) { + daemon_set_logfile(OPT_STRING_VAL(LOGFILE)); daemon_open_log_or_die(); } - - if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no, - conf.logfile_given)) { + if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO, + COLOR_NO, OPT_GIVEN(LOGFILE))) { int i; - for (i = 0; i < conf.log_color_given; i++) - daemon_set_log_color_or_die(conf.log_color_arg[i]); + for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++) + daemon_set_log_color_or_die(lls_string_val(i, + OPT_RESULT(LOG_COLOR))); } daemon_set_flag(DF_LOG_PID); daemon_set_flag(DF_LOG_LL); daemon_set_flag(DF_LOG_TIME); - if (conf.log_timing_given) + if (OPT_GIVEN(LOG_TIMING)) daemon_set_flag(DF_LOG_TIMING); + daemon_set_priority(OPT_UINT32_VAL(PRIORITY)); + if (user_list_file) + init_user_list(user_list_file); ret = 1; -out: +free_cf: free(cf); - free(home); - if (ret > 0) - return; free(user_list_file); - user_list_file = NULL; - exit(EXIT_FAILURE); + if (ret < 0) { + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); + } } /* @@ -235,9 +260,9 @@ out: */ static void handle_sighup(void) { + PARA_NOTICE_LOG("SIGHUP\n"); - parse_config_or_die(1); /* reopens log */ - init_user_list(user_list_file); /* reload user list */ + parse_config_or_die(true); if (mmd->afs_pid) kill(mmd->afs_pid, SIGHUP); } @@ -370,7 +395,8 @@ static int command_post_select(struct sched *s, void *context) */ for (i = sct->argc - 1; i >= 0; i--) memset(sct->argv[i], 0, strlen(sct->argv[i])); - sprintf(sct->argv[0], "para_server (serving %s)", peer_name); + i = sct->argc - 1 - lls_num_inputs(cmdline_lpr); + sprintf(sct->argv[i], "para_server (serving %s)", peer_name); handle_connect(new_fd, peer_name); /* never reached*/ out: @@ -388,7 +414,7 @@ static void init_server_command_task(int argc, char **argv) PARA_NOTICE_LOG("initializing tcp command socket\n"); sct->argc = argc; sct->argv = argv; - ret = para_listen_simple(IPPROTO_TCP, conf.port_arg); + ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT)); if (ret < 0) goto err; sct->listen_fd = ret; @@ -427,7 +453,8 @@ static int init_afs(int argc, char **argv) for (i = argc - 1; i >= 0; i--) memset(argv[i], 0, strlen(argv[i])); - sprintf(argv[0], "para_server (afs)"); + i = argc - lls_num_inputs(cmdline_lpr) - 1; + sprintf(argv[i], "para_server (afs)"); close(afs_server_socket[0]); afs_init(afs_socket_cookie, afs_server_socket[1]); } @@ -446,45 +473,46 @@ static int init_afs(int argc, char **argv) return afs_server_socket[0]; } -__noreturn static void print_help_and_die(void) +static void handle_help_flags(void) { - struct ggo_help h = DEFINE_GGO_HELP(server); - bool d = conf.detailed_help_given; + char *help; + bool d = OPT_GIVEN(DETAILED_HELP); - ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); - exit(0); + if (d) + help = lls_long_help(CMD_PTR); + else if (OPT_GIVEN(HELP)) + help = lls_short_help(CMD_PTR); + else + return; + printf("%s\n", help); + free(help); + exit(EXIT_SUCCESS); } static void server_init(int argc, char **argv) { - struct server_cmdline_parser_params params = { - .override = 0, - .initialize = 1, - .check_required = 0, - .check_ambiguity = 0, - .print_errors = 1 - }; - int afs_socket, daemon_pipe = -1; + int ret, afs_socket, daemon_pipe = -1; + char *errctx; valid_fd_012(); - init_random_seed_or_die(); /* parse command line options */ - server_cmdline_parser_ext(argc, argv, &conf, ¶ms); - daemon_set_loglevel(conf.loglevel_arg); - version_handle_flag("server", conf.version_given); - if (conf.help_given || conf.detailed_help_given) - print_help_and_die(); - daemon_set_priority(conf.priority_arg); - daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg); - /* parse config file, open log and set defaults */ - parse_config_or_die(0); + ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx)); + if (ret < 0) + goto fail; + server_lpr = cmdline_lpr; + daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL)); + daemon_drop_privileges_or_die(OPT_STRING_VAL(USER), + OPT_STRING_VAL(GROUP)); + version_handle_flag("server", OPT_GIVEN(VERSION)); + handle_help_flags(); + parse_config_or_die(false); + /* become daemon */ + if (OPT_GIVEN(DAEMON)) + daemon_pipe = daemonize(true /* parent waits for SIGTERM */); + init_random_seed_or_die(); daemon_log_welcome("server"); init_ipc_or_die(); /* init mmd struct and mmd->lock */ daemon_set_start_time(); - init_user_list(user_list_file); - /* become daemon */ - if (conf.daemon_given) - daemon_pipe = daemonize(true /* parent waits for us */); PARA_NOTICE_LOG("initializing audio format handlers\n"); afh_init(); @@ -492,16 +520,14 @@ static void server_init(int argc, char **argv) * Although afs uses its own signal handling we must ignore SIGUSR1 * _before_ the afs child process gets born by init_afs() below. It's * racy to do this in the child because the parent might send SIGUSR1 - * before the child gets a chance to ignore this signal -- only the - * good die young. - */ - para_sigaction(SIGUSR1, SIG_IGN); - /* - * We have to block SIGCHLD before the afs process is being forked off. - * Otherwise, para_server does not notice if afs dies before the + * before the child gets a chance to ignore this signal. + * + * We also have to block SIGCHLD before the afs process is created + * because otherwise para_server does not notice if afs dies before the * SIGCHLD handler has been installed for the parent process by * init_signal_task() below. */ + para_sigaction(SIGUSR1, SIG_IGN); para_block_signal(SIGCHLD); PARA_NOTICE_LOG("initializing the audio file selector\n"); afs_socket = init_afs(argc, argv); @@ -518,6 +544,13 @@ static void server_init(int argc, char **argv) close(daemon_pipe); } PARA_NOTICE_LOG("server init complete\n"); + return; +fail: + assert(ret < 0); + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); } static void status_refresh(void) @@ -573,9 +606,10 @@ int main(int argc, char *argv[]) mutex_lock(mmd_mutex); ret = schedule(&sched); sched_shutdown(&sched); - if (ret < 0) { + lls_free_parse_result(server_lpr, CMD_PTR); + if (server_lpr != cmdline_lpr) + lls_free_parse_result(cmdline_lpr, CMD_PTR); + if (ret < 0) PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); + exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/server.h b/server.h index 8de691ca..68836e93 100644 --- a/server.h +++ b/server.h @@ -89,9 +89,37 @@ struct misc_meta_data { struct audio_file_data afd; }; -/** Command line options for para_server. */ -extern struct server_args_info conf; +extern struct lls_parse_result *server_lpr; + +/** + * Get a reference to the supercommand of para_server. + * + * This is needed for parsing the command line and for the ENUM_STRING_VAL() + * macro below. The latter macro is used in command.c, so CMD_PTR() can not + * be made local to server.c. + */ +#define CMD_PTR (lls_cmd(0, server_suite)) + +/** Get the parse result of an option to para_server. */ +#define OPT_RESULT(_name) (lls_opt_result( \ + LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr)) + +/** How many times a server option was given. */ +#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name))) + +/** The (first) argument to a server option of type string. */ +#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name))) + +/** The (first) argument to a server option of type uint32. */ +#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name))) + +/** The (first) argument to a server option of type int32. */ +#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name))) + +/** Get the string which corresponds to an enum constant. */ +#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \ + lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR))) __noreturn void handle_connect(int fd, const char *peername); -void parse_config_or_die(int override); +void parse_config_or_die(bool reload); char *server_get_tasks(void); diff --git a/udp_send.c b/udp_send.c index 425118a1..8c9eebc9 100644 --- a/udp_send.c +++ b/udp_send.c @@ -15,8 +15,9 @@ #include #include #include +#include -#include "server.cmdline.h" +#include "server.lsg.h" #include "para.h" #include "error.h" #include "string.h" @@ -94,11 +95,11 @@ static int mcast_sender_setup(struct sender_client *sc) { struct sockaddr_storage ss; socklen_t sslen = sizeof(ss); - int ttl = conf.udp_ttl_arg, id = 0; + int ttl = OPT_INT32_VAL(UDP_TTL), id = 0; const int on = 1; - if (conf.udp_mcast_iface_given) { - char *iface = conf.udp_mcast_iface_arg; + if (OPT_GIVEN(UDP_MCAST_IFACE)) { + const char *iface = OPT_STRING_VAL(UDP_MCAST_IFACE); id = if_nametoindex(iface); if (id == 0) @@ -174,7 +175,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd) ret = parse_fec_url(url, scd); if (ret) return ret; - port = scd->port > 0 ? scd->port : conf.udp_default_port_arg; + port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT); ret = para_connect_simple(IPPROTO_UDP, scd->host, port); if (ret < 0) @@ -372,7 +373,7 @@ static char *udp_status(void) "port: %s\n" "targets: %s\n", (sender_status == SENDER_on)? "on" : "off", - stringify_port(conf.udp_default_port_arg, "udp"), + stringify_port(OPT_UINT32_VAL(UDP_DEFAULT_PORT), "udp"), tgts? tgts : "(none)" ); free(tgts); @@ -385,10 +386,11 @@ static void udp_init_target_list(void) int i; INIT_LIST_HEAD(&targets); - for (i = 0; i < conf.udp_target_given; i++) { - if (udp_resolve_target(conf.udp_target_arg[i], &scd) < 0) + for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) { + const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET)); + if (udp_resolve_target(arg, &scd) < 0) PARA_CRIT_LOG("not adding requested target '%s'\n", - conf.udp_target_arg[i]); + arg); else udp_com_add(&scd); } @@ -438,7 +440,7 @@ void udp_send_init(struct sender *s) s->client_cmds[SENDER_delete] = udp_com_delete; sender_status = SENDER_off; udp_init_target_list(); - if (!conf.udp_no_autostart_given) + if (!OPT_GIVEN(UDP_NO_AUTOSTART)) sender_status = SENDER_on; PARA_DEBUG_LOG("udp sender init complete\n"); } diff --git a/vss.c b/vss.c index 72eb53a3..d1cf5383 100644 --- a/vss.c +++ b/vss.c @@ -21,6 +21,7 @@ #include #include +#include "server.lsg.h" #include "para.h" #include "error.h" #include "portable_io.h" @@ -30,7 +31,6 @@ #include "afs.h" #include "server.h" #include "net.h" -#include "server.cmdline.h" #include "list.h" #include "send.h" #include "sched.h" @@ -1167,10 +1167,8 @@ void init_vss_task(int afs_socket, struct sched *s) static struct vss_task vss_task_struct, *vsst = &vss_task_struct; int i; char *hn = para_hostname(), *home = para_homedir(); - long unsigned announce_time = conf.announce_time_arg > 0? - conf.announce_time_arg : 300, - autoplay_delay = conf.autoplay_delay_arg > 0? - conf.autoplay_delay_arg : 0; + long unsigned announce_time = OPT_UINT32_VAL(ANNOUNCE_TIME), + autoplay_delay = OPT_UINT32_VAL(AUTOPLAY_DELAY); vsst->header_interval.tv_sec = 5; /* should this be configurable? */ vsst->afs_socket = afs_socket; ms2tv(announce_time, &vsst->announce_tv); @@ -1183,7 +1181,7 @@ void init_vss_task(int afs_socket, struct sched *s) free(hn); free(home); mmd->sender_cmd_data.cmd_num = -1; - if (conf.autoplay_given) { + if (OPT_GIVEN(AUTOPLAY)) { struct timeval tmp; mmd->vss_status_flags |= VSS_PLAYING; mmd->new_vss_status_flags |= VSS_PLAYING; -- 2.39.2