1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <unistd.h>
27#include <errno.h>
28
29#include <pulse/xmalloc.h>
30
31#include <pulsecore/i18n.h>
32#include <pulsecore/macro.h>
33#include <pulsecore/core-error.h>
34#include <pulsecore/log.h>
35#include <pulsecore/conf-parser.h>
36#include <pulsecore/core-util.h>
37#include <pulsecore/authkey.h>
38
39#include "client-conf.h"
40
41#ifdef HAVE_X11
42#include <pulse/client-conf-x11.h>
43#endif
44
45#define DEFAULT_CLIENT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "client.conf"
46#define DEFAULT_CLIENT_CONFIG_FILE_USER "client.conf"
47
48#define ENV_CLIENT_CONFIG_FILE "PULSE_CLIENTCONFIG"
49#define ENV_DEFAULT_SINK "PULSE_SINK"
50#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
51#define ENV_DEFAULT_SERVER "PULSE_SERVER"
52#define ENV_DAEMON_BINARY "PULSE_BINARY"
53#define ENV_COOKIE_FILE "PULSE_COOKIE"
54
55static const pa_client_conf default_conf = {
56    .daemon_binary = NULL,
57    .extra_arguments = NULL,
58    .default_sink = NULL,
59    .default_source = NULL,
60    .default_server = NULL,
61    .default_dbus_server = NULL,
62    .cookie_file_from_env = NULL,
63    .cookie_from_x11_valid = false,
64    .cookie_file_from_application = NULL,
65    .cookie_file_from_client_conf = NULL,
66    .autospawn = true,
67    .disable_shm = false,
68    .disable_memfd = false,
69    .shm_size = 0,
70    .auto_connect_localhost = false,
71    .auto_connect_display = false
72};
73
74pa_client_conf *pa_client_conf_new(void) {
75    pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf));
76
77    c->daemon_binary = pa_xstrdup(PA_BINARY);
78    c->extra_arguments = pa_xstrdup("--log-target=syslog");
79
80    return c;
81}
82
83void pa_client_conf_free(pa_client_conf *c) {
84    pa_assert(c);
85    pa_xfree(c->daemon_binary);
86    pa_xfree(c->extra_arguments);
87    pa_xfree(c->default_sink);
88    pa_xfree(c->default_source);
89    pa_xfree(c->default_server);
90    pa_xfree(c->default_dbus_server);
91    pa_xfree(c->cookie_file_from_env);
92    pa_xfree(c->cookie_file_from_application);
93    pa_xfree(c->cookie_file_from_client_conf);
94    pa_xfree(c);
95}
96
97static void load_env(pa_client_conf *c) {
98    char *e;
99
100    if ((e = getenv(ENV_DEFAULT_SINK))) {
101        pa_xfree(c->default_sink);
102        c->default_sink = pa_xstrdup(e);
103    }
104
105    if ((e = getenv(ENV_DEFAULT_SOURCE))) {
106        pa_xfree(c->default_source);
107        c->default_source = pa_xstrdup(e);
108    }
109
110    if ((e = getenv(ENV_DEFAULT_SERVER))) {
111        pa_xfree(c->default_server);
112        c->default_server = pa_xstrdup(e);
113
114        /* We disable autospawning automatically if a specific server was set */
115        c->autospawn = false;
116    }
117
118    if ((e = getenv(ENV_DAEMON_BINARY))) {
119        pa_xfree(c->daemon_binary);
120        c->daemon_binary = pa_xstrdup(e);
121    }
122
123    if ((e = getenv(ENV_COOKIE_FILE)) && *e) {
124        pa_xfree(c->cookie_file_from_env);
125        c->cookie_file_from_env = pa_xstrdup(e);
126    }
127}
128
129void pa_client_conf_load(pa_client_conf *c, bool load_from_x11, bool load_from_env) {
130    FILE *f = NULL;
131    char *fn = NULL;
132
133    /* Prepare the configuration parse table */
134    pa_config_item table[] = {
135        { "daemon-binary",          pa_config_parse_string,   &c->daemon_binary, NULL },
136        { "extra-arguments",        pa_config_parse_string,   &c->extra_arguments, NULL },
137        { "default-sink",           pa_config_parse_string,   &c->default_sink, NULL },
138        { "default-source",         pa_config_parse_string,   &c->default_source, NULL },
139        { "default-server",         pa_config_parse_string,   &c->default_server, NULL },
140        { "default-dbus-server",    pa_config_parse_string,   &c->default_dbus_server, NULL },
141        { "autospawn",              pa_config_parse_bool,     &c->autospawn, NULL },
142        { "cookie-file",            pa_config_parse_string,   &c->cookie_file_from_client_conf, NULL },
143        { "disable-shm",            pa_config_parse_bool,     &c->disable_shm, NULL },
144        { "enable-shm",             pa_config_parse_not_bool, &c->disable_shm, NULL },
145        { "enable-memfd",           pa_config_parse_not_bool, &c->disable_memfd, NULL },
146        { "shm-size-bytes",         pa_config_parse_size,     &c->shm_size, NULL },
147        { "auto-connect-localhost", pa_config_parse_bool,     &c->auto_connect_localhost, NULL },
148        { "auto-connect-display",   pa_config_parse_bool,     &c->auto_connect_display, NULL },
149        { NULL,                     NULL,                     NULL, NULL },
150    };
151
152    f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn);
153    if (f) {
154        pa_config_parse(fn, f, table, NULL, true, NULL);
155        pa_xfree(fn);
156        fclose(f);
157    }
158
159    if (load_from_x11) {
160#ifdef HAVE_X11
161        pa_client_conf_from_x11(c);
162#endif
163    }
164
165    if (load_from_env)
166        load_env(c);
167}
168
169int pa_client_conf_load_cookie(pa_client_conf *c, uint8_t *cookie, size_t cookie_length) {
170    int r;
171    char *fallback_path;
172
173    pa_assert(c);
174    pa_assert(cookie);
175    pa_assert(cookie_length > 0);
176
177    if (c->cookie_file_from_env) {
178        r = pa_authkey_load(c->cookie_file_from_env, true, cookie, cookie_length);
179        if (r >= 0)
180            return 0;
181
182        pa_log_warn("Failed to load cookie from %s (configured with environment variable PULSE_COOKIE): %s",
183                    c->cookie_file_from_env, pa_cstrerror(errno));
184    }
185
186    if (c->cookie_from_x11_valid) {
187        if (cookie_length == sizeof(c->cookie_from_x11)) {
188            memcpy(cookie, c->cookie_from_x11, cookie_length);
189            return 0;
190        }
191
192        pa_log_warn("Failed to load cookie from X11 root window property PULSE_COOKIE: size mismatch.");
193    }
194
195    if (c->cookie_file_from_application) {
196        r = pa_authkey_load(c->cookie_file_from_application, true, cookie, cookie_length);
197        if (r >= 0)
198            return 0;
199
200        pa_log_warn("Failed to load cookie from %s (configured by the application): %s", c->cookie_file_from_application,
201                    pa_cstrerror(errno));
202    }
203
204    if (c->cookie_file_from_client_conf) {
205        r = pa_authkey_load(c->cookie_file_from_client_conf, true, cookie, cookie_length);
206        if (r >= 0)
207            return 0;
208
209        pa_log_warn("Failed to load cookie from %s (configured in client.conf): %s", c->cookie_file_from_client_conf,
210                    pa_cstrerror(errno));
211    }
212
213    r = pa_authkey_load(PA_NATIVE_COOKIE_FILE, false, cookie, cookie_length);
214    if (r >= 0)
215        return 0;
216
217    if (pa_append_to_home_dir(PA_NATIVE_COOKIE_FILE_FALLBACK, &fallback_path) >= 0) {
218        r = pa_authkey_load(fallback_path, false, cookie, cookie_length);
219        pa_xfree(fallback_path);
220        if (r >= 0)
221            return 0;
222    }
223
224    r = pa_authkey_load(PA_NATIVE_COOKIE_FILE, true, cookie, cookie_length);
225    if (r >= 0)
226        return 0;
227
228    pa_log("Failed to load cookie file from %s: %s", PA_NATIVE_COOKIE_FILE, pa_cstrerror(errno));
229    memset(cookie, 0, cookie_length);
230
231    return -1;
232}
233
234void pa_client_conf_set_cookie_file_from_application(pa_client_conf *c, const char *cookie_file) {
235    pa_assert(c);
236    pa_assert(!cookie_file || *cookie_file);
237
238    pa_xfree(c->cookie_file_from_application);
239    c->cookie_file_from_application = pa_xstrdup(cookie_file);
240}
241