if (ll < conf.loglevel_arg)
return;
- if (!logfile && conf.logfile_given)
- logfile = open_log(conf.logfile_arg);
- if (!logfile && conf.daemon_given)
- return;
- if (!logfile) {
- if (ll < WARNING)
- outfd = stdout;
- else
- outfd = stderr;
- } else
- outfd = logfile;
+ outfd = logfile? logfile : stderr;
time(&t1);
tm = localtime(&t1);
strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
valid_fd_012();
hostname = para_hostname();
cmdline_parser(argc, argv, &conf);
- para_drop_privileges(conf.user_arg);
+ para_drop_privileges(conf.user_arg, conf.group_arg);
cf = configfile_exists();
if (cf) {
if (cmdline_parser_configfile(cf, &conf, 0, 0, 0)) {
- fprintf(stderr, "parse error in config file\n");
+ PARA_EMERG_LOG("%s", "parse error in config file\n");
exit(EXIT_FAILURE);
}
}
+ if (conf.logfile_given)
+ logfile = open_log(conf.logfile_arg);
log_welcome("para_audiod", conf.loglevel_arg);
i = init_stream_io();
if (i < 0) {
- fprintf(stderr, "init stream io error: %s\n",
- PARA_STRERROR(-i));
+ PARA_EMERG_LOG("init stream io error: %s\n", PARA_STRERROR(-i));
exit(EXIT_FAILURE);
}
server_uptime(UPTIME_SET);
section "general options"
-option "user" u "run as user 'name'. Read the output of 'para_server -h' for a detailed information on this option." string typestr="name" optional
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "daemon" d "run as background daemon" flag off
-option "force" F "force startup even if socket exits" flag off
-option "logfile" L "(default=stdout/stderr)" string typestr="filename" optional
-option "mode" m "mode to use on startup (on/off/sb)" string typestr="mode" default="on" optional
-option "socket" s "well-known socket to listen on (default=/var/paraslash/audiod_sock.<host_name>)" string typestr="filename" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~
+
+option "user" u
+#~~~~~~~~~~~~~~
+
+"run as user 'name'. Read the output of
+'para_server -h' for more information on this
+option."
+
+ string typestr="name"
+ optional
+
+option "group" g
+#~~~~~~~~~~~~~~~
+
+"set group id to 'group'. Read the output of
+'para_server -h' for more information on this
+option."
+
+ string typestr="group"
+ optional
+
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+
+"set loglevel (0-6)"
+
+ int typestr="level"
+ default="4"
+ optional
+
+option "daemon" d
+#~~~~~~~~~~~~~~~~
+
+"run as background daemon"
+
+ flag off
+
+
+option "force" F
+#~~~~~~~~~~~~~~~
+
+"force startup even if
+socket exists"
+
+ flag off
+
+option "logfile" L
+#~~~~~~~~~~~~~~~~~
+
+"(default=stdout/stderr)"
+
+ string typestr="filename"
+ optional
+
+option "mode" m
+#~~~~~~~~~~~~~~
+
+"mode to use on startup (on/off/sb)"
+
+ string typestr="mode"
+ default="on"
+ optional
+
+option "socket" s
+
+"well-known socket to listen on
+(default=/var/paraslash/audiod_sock.<host_name>)"
+
+ string typestr="filename"
+ optional
option "user_allow" -
+#~~~~~~~~~~~~~~~~~~~~
"allow this user to connect to para_audiod.
May be specified multiple times. If not
-specified at all, allow all users to
+specified at all, all users are allowed to
connect."
-int typestr="uid" default="-1" optional multiple
+ int typestr="uid"
+ default="-1"
+ optional
+ multiple
-section "stream i/o options."
-#################
-option "receiver" r "Select receiver.
-May be given multiple times, once for each
-supported audio format. receiver_spec
-consists of an audio format and the receiver
-name, separated by a colon, and any options
-for that receiver, seperated by whitespace.
-If any receiver options are present, the
-whole receiver argument must be quoted.
+section "stream i/o options."
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+option "receiver" r
+#~~~~~~~~~~~~~~~~~~
+
+"Select receiver. May be given multiple
+times, once for each supported audio format.
+'receiver_spec' consists of an audio format and
+the receiver name, separated by a colon, and
+any options for that receiver, seperated by
+whitespace. If any receiver options are
+present, the whole receiver argument must be
+quoted.
Example:
-r 'mp3:http -i www.paraslash.org -p 8009'
"
-string typestr="receiver_spec" default="http" optional multiple
+ string typestr="receiver_spec"
+ default="http"
+ optional
+ multiple
+
-#################
-option "no_default_filters" D "Configure filters manually.
+option "no_default_filters" D
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If (and only if) this option is set, the
---filter options take effect. Otherwise, the
-compiled-in default filters mp3dec (oggdec)
-and wav are activated for mp3 (ogg) streams."
+"Configure filters manually. If (and only
+if) this option is set, the --filter options
+take effect. Otherwise, the compiled-in
+default filters mp3dec (oggdec) and wav are
+activated for mp3 (ogg) streams."
-flag off
-#################
+ flag off
-option "filter" f "Select filter(s) manually.
-May be given multiple times. filter_spec
-consists of an audio format, the name of the
-filter, and any options for that filter.
+option "filter" f
+#~~~~~~~~~~~~~~~~
+
+"Select filter(s) manually. May be given
+multiple times. filter_spec consists of an
+audio format, the name of the filter, and any
+options for that filter.
Examples:
- -f mp3:mp3dec
+ -f 'mp3:mp3dec'
-f 'mp3:compress --inertia 5 --damp 2'
Note that these options are ignored by default,
string typestr="filter_spec" optional multiple
-#################
option "stream_write_cmd" w
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
"Specify stream writer.
at runtime by the stream start time announced
by para_server, plus any offsets."
-string typestr="format:command" optional multiple
+ string typestr="format:command"
+ optional
+ multiple
-#################
option "stream_delay" -
+#~~~~~~~~~~~~~~~~~~~~~~
"Time to add to para_server's start_time.
START_TIME() was given. Useful for
syncronizing the audio output of clients."
-int typestr="milliseconds" default="200" no
-
-#################
+ int typestr="milliseconds"
+ default="200"
+ optional
option "stream_timeout" -
+#~~~~~~~~~~~~~~~~~~~~~~~~
-"Deactivate slot if idle for that many seconds"
+"Deactivate slot if idle for that many
+seconds"
-int typestr="seconds" default="30" optional
+ int typestr="seconds"
+ default="30"
+ optional
#include "para.h"
#include "daemon.h"
#include <pwd.h>
+
+/* getgrnam() */
+#include <sys/types.h>
+#include <grp.h>
+
#include "string.h"
/**
*
* Either calls exit() or returns a valid file handle.
*/
-/* might be called from para_log, so we must not use para_log */
FILE *open_log(const char *logfile_name)
{
FILE *logfile;
if (!logfile_name)
return NULL;
- if (!(logfile = fopen(logfile_name, "a")))
+ if (!(logfile = fopen(logfile_name, "a"))) {
+ PARA_EMERG_LOG("can not open %s, uid: %d\n", logfile_name,
+ getuid());
exit(EXIT_FAILURE);
+ }
setlinebuf(logfile);
return logfile;
}
/**
* give up superuser privileges
*
- * This function returns immediately if not invoked with EUID zero. Otherwise,
- * it tries to obtain the UID for the user specified by \a username and exits
- * if this user was not found. On success, effective and real UID and the saved
- * set-user-ID are all set to the UID of \a username.
+ * This function returns immediately if not invoked with EUID zero. Otherwise,
+ * it tries to obtain the GID of \a groupname and the UID of \a username. On
+ * success, effective and real GID/UID and the saved set-group-ID/set-user-ID
+ * are all set accordingly. On errors, an appropriate message is logged and exit()
+ * is called to terminate the process.
*
- * \sa getpwnam(3), getuid(2), setuid(2)
+ * \sa getpwnam(3), getuid(2), setuid(2), getgrnam(2), setgid(2)
*/
-void para_drop_privileges(const char *username)
+void para_drop_privileges(const char *username, const char *groupname)
{
struct passwd *p;
char *tmp;
if (geteuid())
return;
+ if (groupname) {
+ struct group *g = getgrnam(groupname);
+ if (!g) {
+ PARA_EMERG_LOG("failed to get group %s\n", groupname);
+ exit(EXIT_FAILURE);
+ }
+ if (setgid(g->gr_gid) < 0) {
+ PARA_EMERG_LOG("failed to set group id %d (%s)\n",
+ g->gr_gid, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
if (!username) {
PARA_EMERG_LOG("%s", "root privileges, but no user option given\n");
exit(EXIT_FAILURE);
PARA_EMERG_LOG("%s", "no such user\n");
exit(EXIT_FAILURE);
}
- PARA_NOTICE_LOG("%s", "dropping root privileges\n");
+ PARA_INFO_LOG("%s", "dropping root privileges\n");
setuid(p->pw_uid);
PARA_DEBUG_LOG("uid: %d, euid: %d\n", getuid(), geteuid());
setuid(p->pw_uid);
return make_message("%li:%02li:%02li", t / 86400,
(t / 3600) % 24, (t / 60) % 60);
}
-
FILE *open_log(const char *logfile_name);
void close_log(FILE* logfile);
void log_welcome(const char *whoami, int loglevel);
-void para_drop_privileges(const char *username);
+void para_drop_privileges(const char *username, const char *groupname);
/** used for server_uptime() */
enum uptime {UPTIME_SET, UPTIME_GET};
time_t server_uptime(enum uptime set_or_get);
if (ll < conf.loglevel_arg)
return;
- if (!logfile) {
- if (ll < WARNING)
- outfd = stdout;
- else
- outfd = stderr;
- } else
- outfd = logfile;
- if (conf.daemon_given && !logfile)
- return;
+ outfd = logfile? logfile : stderr;
time(&t1);
tm = localtime(&t1);
strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
init_random_seed();
/* parse command line options */
cmdline_parser(argc, argv, &conf);
- para_drop_privileges(conf.user_arg);
+ para_drop_privileges(conf.user_arg, conf.group_arg);
/* parse config file, open log and set defaults */
parse_config(0);
log_welcome("para_server", conf.loglevel_arg);
section "General options"
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "port" p "port to listen on" int typestr="portnumber" default="2990" optional
-option "daemon" d "run as background daemon" flag off
-option "user" u "run as user 'name'. para_server 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 para_server 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 'username'. As the configuration file is read afterwards, those options that have a default value depending on the UID (e.g. the home directory for the configuration file) are computed by using the uid of 'username'. This option has no effect if para_server ist started as a non-root user (i.e. EUID != 0)" string typestr="name" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~
+
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+
+"set loglevel (0-6)"
+
+ int typestr="level"
+ default="4"
+ optional
+
+option "port" p
+#~~~~~~~~~~~~~~
+
+"listening port"
+
+ int typestr="portnumber"
+ default="2990"
+ optional
+
+option "daemon" d
+#~~~~~~~~~~~~~~~~
+
+"run as background daemon"
+
+ flag off
+
+option "user" u
+#~~~~~~~~~~~~~~
+
+"run as user 'name'. para_server 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 para_server 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 'username'. 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 'username'.
+This option has no effect if para_server is
+started as a non-root user (i.e. EUID != 0)"
+
+ string typestr="name"
+ optional
+
+
+option "group" g
+#~~~~~~~~~~~~~~~
+
+"set group id to 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."
+
+ string typestr="groupname"
+ optional
+
+
section "Configuration files"
-option "logfile" L "(default=stdout/stderr)" string typestr="filename" optional
-option "config_file" c "(default='~/.paraslash/server.conf'" string typestr="filename" optional
-option "user_list" - "(default='~/.paraslash/server.users')" string typestr="filename" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+option "logfile" L
+#~~~~~~~~~~~~~~~~~
+
+"(default=stdout/stderr)"
+
+ string typestr="filename"
+ optional
+
+option "config_file" c
+#~~~~~~~~~~~~~~~~~~~~~
+
+"(default='~/.paraslash/server.conf'"
+
+ string typestr="filename"
+ optional
+
+option "user_list" -
+#~~~~~~~~~~~~~~~~~~~
+
+"(default='~/.paraslash/server.users')"
+
+ string typestr="filename"
+ optional
+
+
section "audio file sender"
-option "autoplay" a "start playing on startup" flag off
-option "announce_time" A "Delay betweeen announcing the stream and sending data" int typestr="milliseconds" default="300" optional
-option "selector" S "(default=random)" string typestr="name" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+option "autoplay" a
+#~~~~~~~~~~~~~~~~~~
+
+"start playing on startup"
+
+ flag off
+
+option "announce_time" A
+#~~~~~~~~~~~~~~~~~~~~~~~
+
+"Delay betweeen announcing the stream and sending data"
+
+ int typestr="milliseconds"
+ default="300"
+ optional
+
+
+option "selector" S
+#~~~~~~~~~~~~~~~~~~
+
+"(default=random)"
+
+ string typestr="name"
+ optional
+
+
section "mysql selector:"
-option "mysql_host" - "mysql server" string default="localhost" optional
-option "mysql_port" - "where mysql is listening" int default="3306" optional
-option "mysql_user" - "default value: username from /etc/passwd" string optional
-option "mysql_passwd" - "(required)" string optional
-option "mysql_database" - "name of mysql database" string default="paraslash" optional
-option "mysql_audio_file_dir" - "dir to search for audio files (required)" string optional
-option "mysql_default_score" - "scoring rule to use if stream definition does not contain explicit score definition" string default="(LASTPLAYED() / 1440 - 1000 / (LASTPLAYED() + 1) - sqrt(NUMPLAYED()))" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+option "mysql_host" -
+#~~~~~~~~~~~~~~~~~~~~
+
+"mysql server"
+
+ string typestr="ip or hostname"
+ default="localhost"
+ optional
+
+option "mysql_port" -
+#~~~~~~~~~~~~~~~~~~~~
+
+"where mysql is listening"
+
+ int typestr="portnumber"
+ default="3306"
+ optional
+
+
+option "mysql_user" -
+#~~~~~~~~~~~~~~~~~~~~
+
+"default value: username from /etc/passwd"
+
+ string typestr="username"
+ optional
+
+option "mysql_passwd" -
+#~~~~~~~~~~~~~~~~~~~~~~
+
+"(required)"
+
+ string
+ optional
+
+option "mysql_database" -
+#~~~~~~~~~~~~~~~~~~~~~~~~
+
+"name of mysql database"
+
+ string
+ default="paraslash"
+ optional
+
+option "mysql_audio_file_dir" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"dir to search for audio files (required)"
+
+ string typestr="dirname"
+ optional
+
+option "mysql_default_score" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"scoring rule to use if stream definition
+does not contain explicit score definition"
+
+ string
+ default="(LASTPLAYED() / 1440 - 1000 / (LASTPLAYED() + 1) - sqrt(NUMPLAYED()))"
+ optional
+
+
+
section "random selector"
-option "random_dir" - "dir to search for audio files" string default="/home/music" optional
+#~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+option "random_dir" -
+#~~~~~~~~~~~~~~~~~~~~
+
+"dir to search for audio files"
+ string typestr="dirname"
+ default="/home/music"
+ optional
+
+
+
section "http sender"
-option "http_port" - "tcp port for http streaming" int typestr="portnumber" default="8000" optional
-option "http_default_deny" - "deny connections from hosts which are not explicitly allowed" flag off
-option "http_access" - "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" string typestr="a.b.c.d/n" optional multiple
-option "http_no_autostart" - "do not open tcp port on server startup" flag off
-option "http_max_clients" - "maximal simultaneous connections, non-positive value means unlimited" int typestr="number" default="-1" optional
+#~~~~~~~~~~~~~~~~~~~~
+
+
+option "http_port" -
+#~~~~~~~~~~~~~~~~~~~
+
+"tcp port for http streaming"
+
+ int typestr="portnumber"
+ default="8000"
+ optional
+
+option "http_default_deny" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"deny connections from hosts which are not
+explicitly allowed"
+
+ flag off
+
+option "http_access" -
+#~~~~~~~~~~~~~~~~~~~~~
+
+"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"
+
+ string typestr="a.b.c.d/n"
+ optional
+ multiple
+
+option "http_no_autostart" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"do not open tcp port on server startup"
+
+ flag off
+
+option "http_max_clients" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"maximal simultaneous connections,
+non-positive value means unlimited"
+
+ int typestr="number"
+ default="-1"
+ optional
+
+
+
section "dccp sender"
-option "dccp_port" - "port for dccp streaming" int typestr="portnumber" default="5001" optional
+#~~~~~~~~~~~~~~~~~~~~
+
+
+option "dccp_port" -
+#~~~~~~~~~~~~~~~~~~~
+
+"port for dccp streaming"
+
+ int typestr="portnumber"
+ default="5001"
+ optional
+
+
+
section "ortp sender"
-option "ortp_target" - "Add given host/port to the list of targets. This option can be given multiple times. Example: '224.0.1.38:1500' instructs the ortp sender to send to udp port 1500 on host 224.0.1.38 (unassigned ip in the Local Network Control Block 224.0.0/24). This is useful for LAN-streaming." string typestr="a.b.c.d:p" optional multiple
-option "ortp_no_autostart" - "do not start to send automatically" flag off
-option "ortp_default_port" - "default udp port if not specified" int typestr="portnumber" default="1500" optional
-option "ortp_header_interval" H "time between extra header sends" int typestr="milliseconds" default="2000" optional
-option "ortp_jitter_compensation" j "non-zero values enable ortp's adaptive jitter compensation" int typestr="milliseconds" default="400" optional
+#~~~~~~~~~~~~~~~~~~~~
+
+option "ortp_target" -
+#~~~~~~~~~~~~~~~~~~~~~
+
+"Add given host/port to the list of targets.
+This option can be given multiple times.
+Example: '224.0.1.38:1500' instructs the ortp
+sender to send to udp port 1500 on host
+224.0.1.38 (unassigned ip in the Local
+Network Control Block 224.0.0/24). This is
+useful for LAN-streaming."
+
+ string typestr="a.b.c.d:p"
+ optional
+ multiple
+
+option "ortp_no_autostart" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"do not start to send automatically"
+
+ flag off
+
+option "ortp_default_port" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"default udp port if not specified"
+
+ int typestr="portnumber"
+ default="1500"
+ optional
+
+option "ortp_header_interval" H
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"time between extra header sends"
+
+ int typestr="milliseconds"
+ default="2000"
+ optional
+
+option "ortp_jitter_compensation" j
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"non-zero values enable ortp's adaptive
+jitter compensation"
+
+ int typestr="milliseconds"
+ default="400"
+ optional