mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-10 13:29:58 -05:00
Reorganised the source tree. We now have src/ with a couple of subdirs:
* daemon/ - Contains the files specific to the polypaudio daemon. * modules/ - All loadable modules. * polyp/ - Files that are part of the public, application interface or are only used in libpolyp. * polypcore/ - All other shared files. * tests/ - Test programs. * utils/ - Utility programs. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@487 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
5b881e6228
commit
e205b25d65
246 changed files with 724 additions and 689 deletions
131
src/daemon/caps.c
Normal file
131
src/daemon/caps.c
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_SYS_CAPABILITY_H
|
||||
#include <sys/capability.h>
|
||||
#endif
|
||||
|
||||
#include <polypcore/log.h>
|
||||
#include "caps.h"
|
||||
|
||||
#ifdef HAVE_GETUID
|
||||
|
||||
/* Drop root rights when called SUID root */
|
||||
void pa_drop_root(void) {
|
||||
uid_t uid = getuid();
|
||||
|
||||
if (uid == 0 || geteuid() != 0)
|
||||
return;
|
||||
|
||||
pa_log_info(__FILE__": dropping root rights.\n");
|
||||
|
||||
#if defined(HAVE_SETRESUID)
|
||||
setresuid(uid, uid, uid);
|
||||
#elif defined(HAVE_SETREUID)
|
||||
setreuid(uid, uid);
|
||||
#else
|
||||
setuid(uid);
|
||||
seteuid(uid);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void pa_drop_root(void) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_CAPABILITY_H
|
||||
|
||||
/* Limit capabilities set to CAPSYS_NICE */
|
||||
int pa_limit_caps(void) {
|
||||
int r = -1;
|
||||
cap_t caps;
|
||||
cap_value_t nice_cap = CAP_SYS_NICE;
|
||||
|
||||
caps = cap_init();
|
||||
assert(caps);
|
||||
|
||||
cap_clear(caps);
|
||||
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET);
|
||||
cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET);
|
||||
|
||||
if (cap_set_proc(caps) < 0)
|
||||
goto fail;
|
||||
|
||||
pa_log_info(__FILE__": dropped capabilities successfully.\n");
|
||||
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
cap_free (caps);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drop all capabilities, effectively becoming a normal user */
|
||||
int pa_drop_caps(void) {
|
||||
cap_t caps;
|
||||
int r = -1;
|
||||
|
||||
caps = cap_init();
|
||||
assert(caps);
|
||||
|
||||
cap_clear(caps);
|
||||
|
||||
if (cap_set_proc(caps) < 0) {
|
||||
pa_log(__FILE__": failed to drop capabilities: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
fail:
|
||||
cap_free (caps);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* NOOPs in case capabilities are not available. */
|
||||
int pa_limit_caps(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_drop_caps(void) {
|
||||
pa_drop_root();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
29
src/daemon/caps.h
Normal file
29
src/daemon/caps.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef foocapshfoo
|
||||
#define foocapshfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
void pa_drop_root(void);
|
||||
int pa_limit_caps(void);
|
||||
int pa_drop_caps(void);
|
||||
|
||||
#endif
|
||||
300
src/daemon/cmdline.c
Normal file
300
src/daemon/cmdline.c
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "cmdline.h"
|
||||
#include <polypcore/util.h>
|
||||
#include <polypcore/strbuf.h>
|
||||
#include <polypcore/xmalloc.h>
|
||||
|
||||
/* Argument codes for getopt_long() */
|
||||
enum {
|
||||
ARG_HELP = 256,
|
||||
ARG_VERSION,
|
||||
ARG_DUMP_CONF,
|
||||
ARG_DUMP_MODULES,
|
||||
ARG_DAEMONIZE,
|
||||
ARG_FAIL,
|
||||
ARG_LOG_LEVEL,
|
||||
ARG_HIGH_PRIORITY,
|
||||
ARG_DISALLOW_MODULE_LOADING,
|
||||
ARG_EXIT_IDLE_TIME,
|
||||
ARG_MODULE_IDLE_TIME,
|
||||
ARG_SCACHE_IDLE_TIME,
|
||||
ARG_LOG_TARGET,
|
||||
ARG_LOAD,
|
||||
ARG_FILE,
|
||||
ARG_DL_SEARCH_PATH,
|
||||
ARG_RESAMPLE_METHOD,
|
||||
ARG_KILL,
|
||||
ARG_USE_PID_FILE,
|
||||
ARG_CHECK
|
||||
};
|
||||
|
||||
/* Tabel for getopt_long() */
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, ARG_HELP},
|
||||
{"version", 0, 0, ARG_VERSION},
|
||||
{"dump-conf", 0, 0, ARG_DUMP_CONF},
|
||||
{"dump-modules", 0, 0, ARG_DUMP_MODULES},
|
||||
{"daemonize", 2, 0, ARG_DAEMONIZE},
|
||||
{"fail", 2, 0, ARG_FAIL},
|
||||
{"verbose", 2, 0, ARG_LOG_LEVEL},
|
||||
{"log-level", 2, 0, ARG_LOG_LEVEL},
|
||||
{"high-priority", 2, 0, ARG_HIGH_PRIORITY},
|
||||
{"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING},
|
||||
{"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME},
|
||||
{"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
|
||||
{"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME},
|
||||
{"log-target", 1, 0, ARG_LOG_TARGET},
|
||||
{"load", 1, 0, ARG_LOAD},
|
||||
{"file", 1, 0, ARG_FILE},
|
||||
{"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
|
||||
{"resample-method", 1, 0, ARG_RESAMPLE_METHOD},
|
||||
{"kill", 0, 0, ARG_KILL},
|
||||
{"use-pid-file", 2, 0, ARG_USE_PID_FILE},
|
||||
{"check", 0, 0, ARG_CHECK},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
void pa_cmdline_help(const char *argv0) {
|
||||
const char *e;
|
||||
|
||||
if ((e = strrchr(argv0, '/')))
|
||||
e++;
|
||||
else
|
||||
e = argv0;
|
||||
|
||||
printf("%s [options]\n\n"
|
||||
"COMMANDS:\n"
|
||||
" -h, --help Show this help\n"
|
||||
" --version Show version\n"
|
||||
" --dump-conf Dump default configuration\n"
|
||||
" --dump-modules Dump list of available modules\n"
|
||||
" -k --kill Kill a running daemon\n"
|
||||
" --check Check for a running daemon\n\n"
|
||||
|
||||
"OPTIONS:\n"
|
||||
" -D, --daemonize[=BOOL] Daemonize after startup\n"
|
||||
" --fail[=BOOL] Quit when startup fails\n"
|
||||
" --high-priority[=BOOL] Try to set high process priority\n"
|
||||
" (only available as root)\n"
|
||||
" --disallow-module-loading[=BOOL] Disallow module loading after startup\n"
|
||||
" --exit-idle-time=SECS Terminate the daemon when idle and this\n"
|
||||
" time passed\n"
|
||||
" --module-idle-time=SECS Unload autoloaded modules when idle and\n"
|
||||
" this time passed\n"
|
||||
" --scache-idle-time=SECS Unload autoloaded samples when idle and\n"
|
||||
" this time passed\n"
|
||||
" --log-level[=LEVEL] Increase or set verbosity level\n"
|
||||
" -v Increase the verbosity level\n"
|
||||
" --log-target={auto,syslog,stderr} Specify the log target\n"
|
||||
" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
|
||||
" objects (plugins)\n"
|
||||
" --resample-method=[METHOD] Use the specified resampling method\n"
|
||||
" (one of src-sinc-medium-quality,\n"
|
||||
" src-sinc-best-quality,src-sinc-fastest\n"
|
||||
" src-zero-order-hold,src-linear,trivial)\n"
|
||||
" --use-pid-file[=BOOL] Create a PID file\n\n"
|
||||
|
||||
"STARTUP SCRIPT:\n"
|
||||
" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
|
||||
" the specified argument\n"
|
||||
" -F, --file=FILENAME Run the specified script\n"
|
||||
" -C Open a command line on the running TTY\n"
|
||||
" after startup\n\n"
|
||||
|
||||
" -n Don't load default script file\n", e);
|
||||
}
|
||||
|
||||
int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) {
|
||||
pa_strbuf *buf = NULL;
|
||||
int c;
|
||||
assert(conf && argc && argv);
|
||||
|
||||
buf = pa_strbuf_new();
|
||||
|
||||
if (conf->script_commands)
|
||||
pa_strbuf_puts(buf, conf->script_commands);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case ARG_HELP:
|
||||
case 'h':
|
||||
conf->cmd = PA_CMD_HELP;
|
||||
break;
|
||||
|
||||
case ARG_VERSION:
|
||||
conf->cmd = PA_CMD_VERSION;
|
||||
break;
|
||||
|
||||
case ARG_DUMP_CONF:
|
||||
conf->cmd = PA_CMD_DUMP_CONF;
|
||||
break;
|
||||
|
||||
case ARG_DUMP_MODULES:
|
||||
conf->cmd = PA_CMD_DUMP_MODULES;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case ARG_KILL:
|
||||
conf->cmd = PA_CMD_KILL;
|
||||
break;
|
||||
|
||||
case ARG_CHECK:
|
||||
conf->cmd = PA_CMD_CHECK;
|
||||
break;
|
||||
|
||||
case ARG_LOAD:
|
||||
case 'L':
|
||||
pa_strbuf_printf(buf, "load-module %s\n", optarg);
|
||||
break;
|
||||
|
||||
case ARG_FILE:
|
||||
case 'F':
|
||||
pa_strbuf_printf(buf, ".include %s\n", optarg);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
pa_strbuf_puts(buf, "load-module module-cli\n");
|
||||
break;
|
||||
|
||||
case ARG_DAEMONIZE:
|
||||
case 'D':
|
||||
if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(__FILE__": --daemonize expects boolean argument\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARG_FAIL:
|
||||
if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(__FILE__": --fail expects boolean argument\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
case ARG_LOG_LEVEL:
|
||||
|
||||
if (optarg) {
|
||||
if (pa_daemon_conf_set_log_level(conf, optarg) < 0) {
|
||||
pa_log(__FILE__": --log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).\n");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if (conf->log_level < PA_LOG_LEVEL_MAX-1)
|
||||
conf->log_level++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ARG_HIGH_PRIORITY:
|
||||
if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(__FILE__": --high-priority expects boolean argument\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARG_DISALLOW_MODULE_LOADING:
|
||||
if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(__FILE__": --disallow-module-loading expects boolean argument\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARG_USE_PID_FILE:
|
||||
if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
|
||||
pa_log(__FILE__": --use-pid-file expects boolean argument\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
case ARG_DL_SEARCH_PATH:
|
||||
pa_xfree(conf->dl_search_path);
|
||||
conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
pa_xfree(conf->default_script_file);
|
||||
conf->default_script_file = NULL;
|
||||
break;
|
||||
|
||||
case ARG_LOG_TARGET:
|
||||
if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
|
||||
pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n");
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARG_EXIT_IDLE_TIME:
|
||||
conf->exit_idle_time = atoi(optarg);
|
||||
break;
|
||||
|
||||
case ARG_MODULE_IDLE_TIME:
|
||||
conf->module_idle_time = atoi(optarg);
|
||||
break;
|
||||
|
||||
case ARG_SCACHE_IDLE_TIME:
|
||||
conf->scache_idle_time = atoi(optarg);
|
||||
break;
|
||||
|
||||
case ARG_RESAMPLE_METHOD:
|
||||
if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) {
|
||||
pa_log(__FILE__": Invalid resample method '%s'.\n", optarg);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
pa_xfree(conf->script_commands);
|
||||
conf->script_commands = pa_strbuf_tostring_free(buf);
|
||||
|
||||
if (!conf->script_commands) {
|
||||
pa_xfree(conf->script_commands);
|
||||
conf->script_commands = NULL;
|
||||
}
|
||||
|
||||
*d = optind;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (buf)
|
||||
pa_strbuf_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
35
src/daemon/cmdline.h
Normal file
35
src/daemon/cmdline.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef foocmdlinehfoo
|
||||
#define foocmdlinehfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include "daemon-conf.h"
|
||||
|
||||
/* Parese the command line and store its data in *c. Return the index
|
||||
* of the first unparsed argument in *d. */
|
||||
int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d);
|
||||
|
||||
/* Show the command line help. The command name is extracted from
|
||||
* argv[0] which should be passed in argv0. */
|
||||
void pa_cmdline_help(const char *argv0);
|
||||
|
||||
#endif
|
||||
236
src/daemon/cpulimit.c
Normal file
236
src/daemon/cpulimit.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public
|
||||
License along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "cpulimit.h"
|
||||
#include <polypcore/util.h>
|
||||
#include <polypcore/log.h>
|
||||
|
||||
#ifdef HAVE_SIGXCPU
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
/* This module implements a watchdog that makes sure that the current
|
||||
* process doesn't consume more than 70% CPU time for 10 seconds. This
|
||||
* is very useful when using SCHED_FIFO scheduling which effectively
|
||||
* disables multitasking. */
|
||||
|
||||
/* Method of operation: Using SIGXCPU a signal handler is called every
|
||||
* 10s process CPU time. That function checks if less than 14s system
|
||||
* time have passed. In that case, it tries to contact the main event
|
||||
* loop through a pipe. After two additional seconds it is checked
|
||||
* whether the main event loop contact was successful. If not, the
|
||||
* program is terminated forcibly. */
|
||||
|
||||
/* Utilize this much CPU time at maximum */
|
||||
#define CPUTIME_PERCENT 70
|
||||
|
||||
/* Check every 10s */
|
||||
#define CPUTIME_INTERVAL_SOFT (10)
|
||||
|
||||
/* Recheck after 2s */
|
||||
#define CPUTIME_INTERVAL_HARD (2)
|
||||
|
||||
/* Time of the last CPU load check */
|
||||
static time_t last_time = 0;
|
||||
|
||||
/* Pipe for communicating with the main loop */
|
||||
static int the_pipe[2] = {-1, -1};
|
||||
|
||||
/* Main event loop and IO event for the FIFO */
|
||||
static pa_mainloop_api *api = NULL;
|
||||
static pa_io_event *io_event = NULL;
|
||||
|
||||
/* Saved sigaction struct for SIGXCPU */
|
||||
static struct sigaction sigaction_prev;
|
||||
|
||||
/* Nonzero after pa_cpu_limit_init() */
|
||||
static int installed = 0;
|
||||
|
||||
/* The current state of operation */
|
||||
static enum {
|
||||
PHASE_IDLE, /* Normal state */
|
||||
PHASE_SOFT /* After CPU overload has been detected */
|
||||
} phase = PHASE_IDLE;
|
||||
|
||||
/* Reset the SIGXCPU timer to the next t seconds */
|
||||
static void reset_cpu_time(int t) {
|
||||
int r;
|
||||
long n;
|
||||
struct rlimit rl;
|
||||
struct rusage ru;
|
||||
|
||||
/* Get the current CPU time of the current process */
|
||||
r = getrusage(RUSAGE_SELF, &ru);
|
||||
assert(r >= 0);
|
||||
|
||||
n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t;
|
||||
|
||||
r = getrlimit(RLIMIT_CPU, &rl);
|
||||
assert(r >= 0);
|
||||
|
||||
rl.rlim_cur = n;
|
||||
r = setrlimit(RLIMIT_CPU, &rl);
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
/* A simple, thread-safe puts() work-alike */
|
||||
static void write_err(const char *p) {
|
||||
pa_loop_write(2, p, strlen(p));
|
||||
}
|
||||
|
||||
/* The signal handler, called on every SIGXCPU */
|
||||
static void signal_handler(int sig) {
|
||||
assert(sig == SIGXCPU);
|
||||
|
||||
if (phase == PHASE_IDLE) {
|
||||
time_t now;
|
||||
|
||||
#ifdef PRINT_CPU_LOAD
|
||||
char t[256];
|
||||
#endif
|
||||
|
||||
time(&now);
|
||||
|
||||
#ifdef PRINT_CPU_LOAD
|
||||
snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100);
|
||||
write_err(t);
|
||||
#endif
|
||||
|
||||
if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) {
|
||||
static const char c = 'X';
|
||||
|
||||
write_err("Soft CPU time limit exhausted, terminating.\n");
|
||||
|
||||
/* Try a soft cleanup */
|
||||
write(the_pipe[1], &c, sizeof(c));
|
||||
phase = PHASE_SOFT;
|
||||
reset_cpu_time(CPUTIME_INTERVAL_HARD);
|
||||
|
||||
} else {
|
||||
|
||||
/* Everything's fine */
|
||||
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
|
||||
last_time = now;
|
||||
}
|
||||
|
||||
} else if (phase == PHASE_SOFT) {
|
||||
write_err("Hard CPU time limit exhausted, terminating forcibly.\n");
|
||||
_exit(1); /* Forced exit */
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback for IO events on the FIFO */
|
||||
static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags f, void *userdata) {
|
||||
char c;
|
||||
assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]);
|
||||
read(the_pipe[0], &c, sizeof(c));
|
||||
m->quit(m, 1); /* Quit the main loop */
|
||||
}
|
||||
|
||||
/* Initializes CPU load limiter */
|
||||
int pa_cpu_limit_init(pa_mainloop_api *m) {
|
||||
struct sigaction sa;
|
||||
assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed);
|
||||
|
||||
time(&last_time);
|
||||
|
||||
/* Prepare the main loop pipe */
|
||||
if (pipe(the_pipe) < 0) {
|
||||
pa_log(__FILE__": pipe() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pa_make_nonblock_fd(the_pipe[0]);
|
||||
pa_make_nonblock_fd(the_pipe[1]);
|
||||
pa_fd_set_cloexec(the_pipe[0], 1);
|
||||
pa_fd_set_cloexec(the_pipe[1], 1);
|
||||
|
||||
api = m;
|
||||
io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL);
|
||||
|
||||
phase = PHASE_IDLE;
|
||||
|
||||
/* Install signal handler for SIGXCPU */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
|
||||
if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) {
|
||||
pa_cpu_limit_done();
|
||||
return -1;
|
||||
}
|
||||
|
||||
installed = 1;
|
||||
|
||||
reset_cpu_time(CPUTIME_INTERVAL_SOFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shutdown CPU load limiter */
|
||||
void pa_cpu_limit_done(void) {
|
||||
int r;
|
||||
|
||||
if (io_event) {
|
||||
assert(api);
|
||||
api->io_free(io_event);
|
||||
io_event = NULL;
|
||||
api = NULL;
|
||||
}
|
||||
|
||||
if (the_pipe[0] >= 0)
|
||||
close(the_pipe[0]);
|
||||
if (the_pipe[1] >= 0)
|
||||
close(the_pipe[1]);
|
||||
the_pipe[0] = the_pipe[1] = -1;
|
||||
|
||||
if (installed) {
|
||||
r = sigaction(SIGXCPU, &sigaction_prev, NULL);
|
||||
assert(r >= 0);
|
||||
installed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* HAVE_SIGXCPU */
|
||||
|
||||
int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_cpu_limit_done(void) {
|
||||
}
|
||||
|
||||
#endif
|
||||
34
src/daemon/cpulimit.h
Normal file
34
src/daemon/cpulimit.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef foocpulimithfoo
|
||||
#define foocpulimithfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include <polyp/mainloop-api.h>
|
||||
|
||||
/* This kills the polypaudio process if it eats more than 70% of the
|
||||
* CPU time. This is build around setrlimit() and SIGXCPU. It is handy
|
||||
* in case of using SCHED_FIFO which may freeze the whole machine */
|
||||
|
||||
int pa_cpu_limit_init(pa_mainloop_api *m);
|
||||
void pa_cpu_limit_done(void);
|
||||
|
||||
#endif
|
||||
297
src/daemon/daemon-conf.c
Normal file
297
src/daemon/daemon-conf.c
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "daemon-conf.h"
|
||||
#include <polypcore/util.h>
|
||||
#include <polypcore/xmalloc.h>
|
||||
#include <polypcore/strbuf.h>
|
||||
#include <polypcore/conf-parser.h>
|
||||
#include <polypcore/resampler.h>
|
||||
|
||||
#ifndef DEFAULT_CONFIG_DIR
|
||||
# ifndef OS_IS_WIN32
|
||||
# define DEFAULT_CONFIG_DIR "/etc/polypaudio"
|
||||
# else
|
||||
# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef OS_IS_WIN32
|
||||
# define PATH_SEP "/"
|
||||
#else
|
||||
# define PATH_SEP "\\"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR PATH_SEP "default.pa"
|
||||
#define DEFAULT_SCRIPT_FILE_USER ".polypaudio" PATH_SEP "default.pa"
|
||||
#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf"
|
||||
#define DEFAULT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "daemon.conf"
|
||||
|
||||
#define ENV_SCRIPT_FILE "POLYP_SCRIPT"
|
||||
#define ENV_CONFIG_FILE "POLYP_CONFIG"
|
||||
#define ENV_DL_SEARCH_PATH "POLYP_DLPATH"
|
||||
|
||||
static const pa_daemon_conf default_conf = {
|
||||
.cmd = PA_CMD_DAEMON,
|
||||
.daemonize = 0,
|
||||
.fail = 1,
|
||||
.high_priority = 0,
|
||||
.disallow_module_loading = 0,
|
||||
.exit_idle_time = -1,
|
||||
.module_idle_time = 20,
|
||||
.scache_idle_time = 20,
|
||||
.auto_log_target = 1,
|
||||
.script_commands = NULL,
|
||||
.dl_search_path = NULL,
|
||||
.default_script_file = NULL,
|
||||
.log_target = PA_LOG_SYSLOG,
|
||||
.log_level = PA_LOG_NOTICE,
|
||||
.resample_method = PA_RESAMPLER_SRC_SINC_FASTEST,
|
||||
.config_file = NULL,
|
||||
.use_pid_file = 1
|
||||
};
|
||||
|
||||
pa_daemon_conf* pa_daemon_conf_new(void) {
|
||||
FILE *f;
|
||||
pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
|
||||
|
||||
if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file)))
|
||||
fclose(f);
|
||||
|
||||
#ifdef DLSEARCHPATH
|
||||
c->dl_search_path = pa_xstrdup(DLSEARCHPATH);
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
void pa_daemon_conf_free(pa_daemon_conf *c) {
|
||||
assert(c);
|
||||
pa_xfree(c->script_commands);
|
||||
pa_xfree(c->dl_search_path);
|
||||
pa_xfree(c->default_script_file);
|
||||
pa_xfree(c->config_file);
|
||||
pa_xfree(c);
|
||||
}
|
||||
|
||||
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
|
||||
assert(c && string);
|
||||
|
||||
if (!strcmp(string, "auto"))
|
||||
c->auto_log_target = 1;
|
||||
else if (!strcmp(string, "syslog")) {
|
||||
c->auto_log_target = 0;
|
||||
c->log_target = PA_LOG_SYSLOG;
|
||||
} else if (!strcmp(string, "stderr")) {
|
||||
c->auto_log_target = 0;
|
||||
c->log_target = PA_LOG_STDERR;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) {
|
||||
uint32_t u;
|
||||
assert(c && string);
|
||||
|
||||
if (pa_atou(string, &u) >= 0) {
|
||||
if (u >= PA_LOG_LEVEL_MAX)
|
||||
return -1;
|
||||
|
||||
c->log_level = (pa_log_level_t) u;
|
||||
} else if (pa_startswith(string, "debug"))
|
||||
c->log_level = PA_LOG_DEBUG;
|
||||
else if (pa_startswith(string, "info"))
|
||||
c->log_level = PA_LOG_INFO;
|
||||
else if (pa_startswith(string, "notice"))
|
||||
c->log_level = PA_LOG_NOTICE;
|
||||
else if (pa_startswith(string, "warn"))
|
||||
c->log_level = PA_LOG_WARN;
|
||||
else if (pa_startswith(string, "err"))
|
||||
c->log_level = PA_LOG_ERROR;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
|
||||
int m;
|
||||
assert(c && string);
|
||||
|
||||
if ((m = pa_parse_resample_method(string)) < 0)
|
||||
return -1;
|
||||
|
||||
c->resample_method = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
|
||||
pa_daemon_conf *c = data;
|
||||
assert(filename && lvalue && rvalue && data);
|
||||
|
||||
if (pa_daemon_conf_set_log_target(c, rvalue) < 0) {
|
||||
pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
|
||||
pa_daemon_conf *c = data;
|
||||
assert(filename && lvalue && rvalue && data);
|
||||
|
||||
if (pa_daemon_conf_set_log_level(c, rvalue) < 0) {
|
||||
pa_log(__FILE__": [%s:%u] Invalid log level '%s'.\n", filename, line, rvalue);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) {
|
||||
pa_daemon_conf *c = data;
|
||||
assert(filename && lvalue && rvalue && data);
|
||||
|
||||
if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) {
|
||||
pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
|
||||
int r = -1;
|
||||
FILE *f = NULL;
|
||||
|
||||
pa_config_item table[] = {
|
||||
{ "daemonize", pa_config_parse_bool, NULL },
|
||||
{ "fail", pa_config_parse_bool, NULL },
|
||||
{ "high-priority", pa_config_parse_bool, NULL },
|
||||
{ "disallow-module-loading", pa_config_parse_bool, NULL },
|
||||
{ "exit-idle-time", pa_config_parse_int, NULL },
|
||||
{ "module-idle-time", pa_config_parse_int, NULL },
|
||||
{ "scache-idle-time", pa_config_parse_int, NULL },
|
||||
{ "dl-search-path", pa_config_parse_string, NULL },
|
||||
{ "default-script-file", pa_config_parse_string, NULL },
|
||||
{ "log-target", parse_log_target, NULL },
|
||||
{ "log-level", parse_log_level, NULL },
|
||||
{ "verbose", parse_log_level, NULL },
|
||||
{ "resample-method", parse_resample_method, NULL },
|
||||
{ "use-pid-file", pa_config_parse_bool, NULL },
|
||||
{ NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
table[0].data = &c->daemonize;
|
||||
table[1].data = &c->fail;
|
||||
table[2].data = &c->high_priority;
|
||||
table[3].data = &c->disallow_module_loading;
|
||||
table[4].data = &c->exit_idle_time;
|
||||
table[5].data = &c->module_idle_time;
|
||||
table[6].data = &c->scache_idle_time;
|
||||
table[7].data = &c->dl_search_path;
|
||||
table[8].data = &c->default_script_file;
|
||||
table[9].data = c;
|
||||
table[10].data = c;
|
||||
table[11].data = c;
|
||||
table[12].data = c;
|
||||
table[13].data = &c->use_pid_file;
|
||||
|
||||
pa_xfree(c->config_file);
|
||||
c->config_file = NULL;
|
||||
|
||||
f = filename ?
|
||||
fopen(c->config_file = pa_xstrdup(filename), "r") :
|
||||
pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file);
|
||||
|
||||
if (!f && errno != ENOENT) {
|
||||
pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0;
|
||||
|
||||
finish:
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int pa_daemon_conf_env(pa_daemon_conf *c) {
|
||||
char *e;
|
||||
|
||||
if ((e = getenv(ENV_DL_SEARCH_PATH))) {
|
||||
pa_xfree(c->dl_search_path);
|
||||
c->dl_search_path = pa_xstrdup(e);
|
||||
}
|
||||
if ((e = getenv(ENV_SCRIPT_FILE))) {
|
||||
pa_xfree(c->default_script_file);
|
||||
c->default_script_file = pa_xstrdup(e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const log_level_to_string[] = {
|
||||
[PA_LOG_DEBUG] = "debug",
|
||||
[PA_LOG_INFO] = "info",
|
||||
[PA_LOG_NOTICE] = "notice",
|
||||
[PA_LOG_WARN] = "warning",
|
||||
[PA_LOG_ERROR] = "error"
|
||||
};
|
||||
|
||||
char *pa_daemon_conf_dump(pa_daemon_conf *c) {
|
||||
pa_strbuf *s = pa_strbuf_new();
|
||||
|
||||
if (c->config_file)
|
||||
pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file);
|
||||
|
||||
assert(c->log_level <= PA_LOG_LEVEL_MAX);
|
||||
|
||||
pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize);
|
||||
pa_strbuf_printf(s, "fail = %i\n", !!c->fail);
|
||||
pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority);
|
||||
pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading);
|
||||
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
|
||||
pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
|
||||
pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
|
||||
pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : "");
|
||||
pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file);
|
||||
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
|
||||
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
|
||||
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
|
||||
pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file);
|
||||
|
||||
return pa_strbuf_tostring_free(s);
|
||||
}
|
||||
80
src/daemon/daemon-conf.h
Normal file
80
src/daemon/daemon-conf.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef foodaemonconfhfoo
|
||||
#define foodaemonconfhfoo
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include <polypcore/log.h>
|
||||
|
||||
/* The actual command to execute */
|
||||
typedef enum pa_daemon_conf_cmd {
|
||||
PA_CMD_DAEMON, /* the default */
|
||||
PA_CMD_HELP,
|
||||
PA_CMD_VERSION,
|
||||
PA_CMD_DUMP_CONF,
|
||||
PA_CMD_DUMP_MODULES,
|
||||
PA_CMD_KILL,
|
||||
PA_CMD_CHECK
|
||||
} pa_daemon_conf_cmd_t;
|
||||
|
||||
/* A structure containing configuration data for the Polypaudio server . */
|
||||
typedef struct pa_daemon_conf {
|
||||
pa_daemon_conf_cmd_t cmd;
|
||||
int daemonize,
|
||||
fail,
|
||||
high_priority,
|
||||
disallow_module_loading,
|
||||
exit_idle_time,
|
||||
module_idle_time,
|
||||
scache_idle_time,
|
||||
auto_log_target,
|
||||
use_pid_file;
|
||||
char *script_commands, *dl_search_path, *default_script_file;
|
||||
pa_log_target_t log_target;
|
||||
pa_log_level_t log_level;
|
||||
int resample_method;
|
||||
char *config_file;
|
||||
} pa_daemon_conf;
|
||||
|
||||
/* Allocate a new structure and fill it with sane defaults */
|
||||
pa_daemon_conf* pa_daemon_conf_new(void);
|
||||
void pa_daemon_conf_free(pa_daemon_conf*c);
|
||||
|
||||
/* Load configuration data from the specified file overwriting the
|
||||
* current settings in *c. If filename is NULL load the default daemon
|
||||
* configuration file */
|
||||
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename);
|
||||
|
||||
/* Pretty print the current configuration data of the daemon. The
|
||||
* returned string has to be freed manually. The output of this
|
||||
* function may be parsed with pa_daemon_conf_load(). */
|
||||
char *pa_daemon_conf_dump(pa_daemon_conf *c);
|
||||
|
||||
/* Load the configuration data from the process' environment
|
||||
* overwriting the current settings in *c. */
|
||||
int pa_daemon_conf_env(pa_daemon_conf *c);
|
||||
|
||||
/* Set these configuration variables in the structure by passing a string */
|
||||
int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
|
||||
int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
|
||||
int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
|
||||
|
||||
#endif
|
||||
99
src/daemon/dumpmodules.c
Normal file
99
src/daemon/dumpmodules.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ltdl.h>
|
||||
|
||||
#include "dumpmodules.h"
|
||||
#include <polypcore/modinfo.h>
|
||||
#include <polypcore/util.h>
|
||||
|
||||
#define PREFIX "module-"
|
||||
|
||||
static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) {
|
||||
assert(name && i);
|
||||
printf("%-40s%s\n", name, i->description ? i->description : "n/a");
|
||||
}
|
||||
|
||||
static void long_info(const char *name, const char *path, pa_modinfo *i) {
|
||||
static int nl = 0;
|
||||
assert(name && i);
|
||||
|
||||
if (nl)
|
||||
printf("\n");
|
||||
|
||||
nl = 1;
|
||||
|
||||
printf("Name: %s\n", name);
|
||||
|
||||
if (!i->description && !i->version && !i->author && !i->usage)
|
||||
printf("No module information available\n");
|
||||
else {
|
||||
if (i->version)
|
||||
printf("Version: %s\n", i->version);
|
||||
if (i->description)
|
||||
printf("Description: %s\n", i->description);
|
||||
if (i->author)
|
||||
printf("Author: %s\n", i->author);
|
||||
if (i->usage)
|
||||
printf("Usage: %s\n", i->usage);
|
||||
}
|
||||
|
||||
if (path)
|
||||
printf("Path: %s\n", path);
|
||||
}
|
||||
|
||||
static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) {
|
||||
pa_modinfo *i;
|
||||
|
||||
if ((i = pa_modinfo_get_by_name(path ? path : name))) {
|
||||
info(name, path, i);
|
||||
pa_modinfo_free(i);
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(const char *path, lt_ptr data) {
|
||||
const char *e;
|
||||
pa_daemon_conf *c = (data);
|
||||
|
||||
e = pa_path_get_filename(path);
|
||||
|
||||
if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1))
|
||||
show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) {
|
||||
if (argc > 0) {
|
||||
int i;
|
||||
for (i = 0; i < argc; i++)
|
||||
show_info(argv[i], NULL, long_info);
|
||||
} else
|
||||
lt_dlforeachfile(NULL, callback, c);
|
||||
}
|
||||
31
src/daemon/dumpmodules.h
Normal file
31
src/daemon/dumpmodules.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef foodumpmoduleshfoo
|
||||
#define foodumpmoduleshfoo
|
||||
|
||||
/* $Id*/
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#include "daemon-conf.h"
|
||||
|
||||
/* Dump all available modules to STDOUT. If argc > 0 print information
|
||||
* about the modules specified in argv[] instead. */
|
||||
void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]);
|
||||
|
||||
#endif
|
||||
472
src/daemon/main.c
Normal file
472
src/daemon/main.c
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
/* $Id$ */
|
||||
|
||||
/***
|
||||
This file is part of polypaudio.
|
||||
|
||||
polypaudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
polypaudio 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 Lesser General Public License
|
||||
along with polypaudio; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <ltdl.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <liboil/liboil.h>
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
#include <syslog.h>
|
||||
#include <tcpd.h>
|
||||
#endif
|
||||
|
||||
#include <polypcore/winsock.h>
|
||||
|
||||
#include <polypcore/core.h>
|
||||
#include <polypcore/memblock.h>
|
||||
#include <polyp/mainloop.h>
|
||||
#include <polypcore/module.h>
|
||||
#include <polyp/mainloop-signal.h>
|
||||
#include "cmdline.h"
|
||||
#include <polypcore/cli-command.h>
|
||||
#include <polypcore/util.h>
|
||||
#include <polypcore/sioman.h>
|
||||
#include <polypcore/xmalloc.h>
|
||||
#include "cpulimit.h"
|
||||
#include <polypcore/log.h>
|
||||
#include "daemon-conf.h"
|
||||
#include "dumpmodules.h"
|
||||
#include "caps.h"
|
||||
#include <polypcore/cli-text.h>
|
||||
#include <polypcore/pid.h>
|
||||
#include <polypcore/namereg.h>
|
||||
|
||||
#ifdef HAVE_LIBWRAP
|
||||
/* Only one instance of these variables */
|
||||
int allow_severity = LOG_INFO;
|
||||
int deny_severity = LOG_WARNING;
|
||||
#endif
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
|
||||
static void message_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
|
||||
MSG msg;
|
||||
|
||||
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
raise(SIGTERM);
|
||||
else {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) {
|
||||
pa_log_info(__FILE__": Got signal %s.\n", pa_strsignal(sig));
|
||||
|
||||
switch (sig) {
|
||||
#ifdef SIGUSR1
|
||||
case SIGUSR1:
|
||||
pa_module_load(userdata, "module-cli", NULL);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SIGUSR2
|
||||
case SIGUSR2:
|
||||
pa_module_load(userdata, "module-cli-protocol-unix", NULL);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SIGHUP
|
||||
case SIGHUP: {
|
||||
char *c = pa_full_status_string(userdata);
|
||||
pa_log_notice(c);
|
||||
pa_xfree(c);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
default:
|
||||
pa_log_info(__FILE__": Exiting.\n");
|
||||
m->quit(m, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_pipe(int p[2]) {
|
||||
if (p[0] != -1)
|
||||
close(p[0]);
|
||||
if (p[1] != -1)
|
||||
close(p[1]);
|
||||
p[0] = p[1] = -1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pa_core *c;
|
||||
pa_strbuf *buf = NULL;
|
||||
pa_daemon_conf *conf;
|
||||
pa_mainloop *mainloop;
|
||||
|
||||
char *s;
|
||||
int r, retval = 1, d = 0;
|
||||
int daemon_pipe[2] = { -1, -1 };
|
||||
int suid_root;
|
||||
int valid_pid_file = 0;
|
||||
|
||||
#ifdef HAVE_GETUID
|
||||
gid_t gid = (gid_t) -1;
|
||||
#endif
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
pa_defer_event *defer;
|
||||
#endif
|
||||
|
||||
pa_limit_caps();
|
||||
|
||||
#ifdef HAVE_GETUID
|
||||
suid_root = getuid() != 0 && geteuid() == 0;
|
||||
|
||||
if (suid_root && (pa_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) {
|
||||
pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'.\n");
|
||||
pa_drop_root();
|
||||
}
|
||||
#else
|
||||
suid_root = 0;
|
||||
#endif
|
||||
|
||||
LTDL_SET_PRELOADED_SYMBOLS();
|
||||
|
||||
r = lt_dlinit();
|
||||
assert(r == 0);
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
{
|
||||
WSADATA data;
|
||||
WSAStartup(MAKEWORD(2, 0), &data);
|
||||
}
|
||||
#endif
|
||||
|
||||
pa_log_set_ident("polypaudio");
|
||||
|
||||
conf = pa_daemon_conf_new();
|
||||
|
||||
if (pa_daemon_conf_load(conf, NULL) < 0)
|
||||
goto finish;
|
||||
|
||||
if (pa_daemon_conf_env(conf) < 0)
|
||||
goto finish;
|
||||
|
||||
if (pa_cmdline_parse(conf, argc, argv, &d) < 0) {
|
||||
pa_log(__FILE__": failed to parse command line.\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
pa_log_set_maximal_level(conf->log_level);
|
||||
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
|
||||
|
||||
if (conf->high_priority && conf->cmd == PA_CMD_DAEMON)
|
||||
pa_raise_priority();
|
||||
|
||||
pa_drop_caps();
|
||||
|
||||
if (suid_root)
|
||||
pa_drop_root();
|
||||
|
||||
if (conf->dl_search_path)
|
||||
lt_dlsetsearchpath(conf->dl_search_path);
|
||||
|
||||
switch (conf->cmd) {
|
||||
case PA_CMD_DUMP_MODULES:
|
||||
pa_dump_modules(conf, argc-d, argv+d);
|
||||
retval = 0;
|
||||
goto finish;
|
||||
|
||||
case PA_CMD_DUMP_CONF: {
|
||||
s = pa_daemon_conf_dump(conf);
|
||||
fputs(s, stdout);
|
||||
pa_xfree(s);
|
||||
retval = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
case PA_CMD_HELP :
|
||||
pa_cmdline_help(argv[0]);
|
||||
retval = 0;
|
||||
goto finish;
|
||||
|
||||
case PA_CMD_VERSION :
|
||||
printf(PACKAGE_NAME" "PACKAGE_VERSION"\n");
|
||||
retval = 0;
|
||||
goto finish;
|
||||
|
||||
case PA_CMD_CHECK: {
|
||||
pid_t pid;
|
||||
|
||||
if (pa_pid_file_check_running(&pid) < 0) {
|
||||
pa_log_info(__FILE__": daemon not running\n");
|
||||
} else {
|
||||
pa_log_info(__FILE__": daemon running as PID %u\n", pid);
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
goto finish;
|
||||
|
||||
}
|
||||
case PA_CMD_KILL:
|
||||
|
||||
if (pa_pid_file_kill(SIGINT, NULL) < 0)
|
||||
pa_log(__FILE__": failed to kill daemon.\n");
|
||||
else
|
||||
retval = 0;
|
||||
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
assert(conf->cmd == PA_CMD_DAEMON);
|
||||
}
|
||||
|
||||
if (conf->daemonize) {
|
||||
pid_t child;
|
||||
int tty_fd;
|
||||
|
||||
if (pa_stdio_acquire() < 0) {
|
||||
pa_log(__FILE__": failed to acquire stdio.\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
#ifdef HAVE_FORK
|
||||
if (pipe(daemon_pipe) < 0) {
|
||||
pa_log(__FILE__": failed to create pipe.\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if ((child = fork()) < 0) {
|
||||
pa_log(__FILE__": fork() failed: %s\n", strerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (child != 0) {
|
||||
/* Father */
|
||||
|
||||
close(daemon_pipe[1]);
|
||||
daemon_pipe[1] = -1;
|
||||
|
||||
if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) {
|
||||
pa_log(__FILE__": read() failed: %s\n", strerror(errno));
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
if (retval)
|
||||
pa_log(__FILE__": daemon startup failed.\n");
|
||||
else
|
||||
pa_log_info(__FILE__": daemon startup successful.\n");
|
||||
|
||||
goto finish;
|
||||
}
|
||||
|
||||
close(daemon_pipe[0]);
|
||||
daemon_pipe[0] = -1;
|
||||
#endif
|
||||
|
||||
if (conf->auto_log_target)
|
||||
pa_log_set_target(PA_LOG_SYSLOG, NULL);
|
||||
|
||||
#ifdef HAVE_SETSID
|
||||
setsid();
|
||||
#endif
|
||||
#ifdef HAVE_SETPGID
|
||||
setpgid(0,0);
|
||||
#endif
|
||||
|
||||
#ifndef OS_IS_WIN32
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
|
||||
open("/dev/null", O_RDONLY);
|
||||
open("/dev/null", O_WRONLY);
|
||||
open("/dev/null", O_WRONLY);
|
||||
#else
|
||||
FreeConsole();
|
||||
#endif
|
||||
|
||||
#ifdef SIGTTOU
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
#endif
|
||||
#ifdef SIGTTIN
|
||||
signal(SIGTTIN, SIG_IGN);
|
||||
#endif
|
||||
#ifdef SIGTSTP
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#ifdef TIOCNOTTY
|
||||
if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) {
|
||||
ioctl(tty_fd, TIOCNOTTY, (char*) 0);
|
||||
close(tty_fd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
chdir("/");
|
||||
|
||||
if (conf->use_pid_file) {
|
||||
if (pa_pid_file_create() < 0) {
|
||||
pa_log(__FILE__": pa_pid_file_create() failed.\n");
|
||||
#ifdef HAVE_FORK
|
||||
if (conf->daemonize)
|
||||
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
|
||||
#endif
|
||||
goto finish;
|
||||
}
|
||||
|
||||
valid_pid_file = 1;
|
||||
}
|
||||
|
||||
mainloop = pa_mainloop_new();
|
||||
assert(mainloop);
|
||||
|
||||
c = pa_core_new(pa_mainloop_get_api(mainloop));
|
||||
assert(c);
|
||||
|
||||
r = pa_signal_init(pa_mainloop_get_api(mainloop));
|
||||
assert(r == 0);
|
||||
pa_signal_new(SIGINT, signal_callback, c);
|
||||
pa_signal_new(SIGTERM, signal_callback, c);
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
defer = pa_mainloop_get_api(mainloop)->defer_new(pa_mainloop_get_api(mainloop), message_cb, NULL);
|
||||
assert(defer);
|
||||
#endif
|
||||
|
||||
if (conf->daemonize)
|
||||
c->running_as_daemon = 1;
|
||||
|
||||
#ifdef SIGUSR1
|
||||
pa_signal_new(SIGUSR1, signal_callback, c);
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
pa_signal_new(SIGUSR2, signal_callback, c);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
pa_signal_new(SIGHUP, signal_callback, c);
|
||||
#endif
|
||||
|
||||
oil_init();
|
||||
|
||||
r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop));
|
||||
assert(r == 0);
|
||||
|
||||
buf = pa_strbuf_new();
|
||||
assert(buf);
|
||||
if (conf->default_script_file)
|
||||
r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail);
|
||||
|
||||
if (r >= 0)
|
||||
r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
|
||||
pa_log(s = pa_strbuf_tostring_free(buf));
|
||||
pa_xfree(s);
|
||||
|
||||
if (r < 0 && conf->fail) {
|
||||
pa_log(__FILE__": failed to initialize daemon.\n");
|
||||
#ifdef HAVE_FORK
|
||||
if (conf->daemonize)
|
||||
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
|
||||
#endif
|
||||
} else if (!c->modules || pa_idxset_size(c->modules) == 0) {
|
||||
pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n");
|
||||
#ifdef HAVE_FORK
|
||||
if (conf->daemonize)
|
||||
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
|
||||
#endif
|
||||
} else {
|
||||
|
||||
retval = 0;
|
||||
#ifdef HAVE_FORK
|
||||
if (conf->daemonize)
|
||||
pa_loop_write(daemon_pipe[1], &retval, sizeof(retval));
|
||||
#endif
|
||||
|
||||
c->disallow_module_loading = conf->disallow_module_loading;
|
||||
c->exit_idle_time = conf->exit_idle_time;
|
||||
c->module_idle_time = conf->module_idle_time;
|
||||
c->scache_idle_time = conf->scache_idle_time;
|
||||
c->resample_method = conf->resample_method;
|
||||
|
||||
if (c->default_sink_name &&
|
||||
pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) {
|
||||
pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.\n", __FILE__, c->default_sink_name);
|
||||
retval = 1;
|
||||
} else {
|
||||
pa_log_info(__FILE__": Daemon startup complete.\n");
|
||||
if (pa_mainloop_run(mainloop, &retval) < 0)
|
||||
retval = 1;
|
||||
pa_log_info(__FILE__": Daemon shutdown initiated.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
pa_mainloop_get_api(mainloop)->defer_free(defer);
|
||||
#endif
|
||||
|
||||
pa_core_free(c);
|
||||
|
||||
pa_cpu_limit_done();
|
||||
pa_signal_done();
|
||||
pa_mainloop_free(mainloop);
|
||||
|
||||
pa_log_info(__FILE__": Daemon terminated.\n");
|
||||
|
||||
finish:
|
||||
|
||||
if (conf)
|
||||
pa_daemon_conf_free(conf);
|
||||
|
||||
if (valid_pid_file)
|
||||
pa_pid_file_remove();
|
||||
|
||||
close_pipe(daemon_pipe);
|
||||
|
||||
#ifdef OS_IS_WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
lt_dlexit();
|
||||
|
||||
return retval;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue