1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2006 Lennart Poettering
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <errno.h>
28
29#include <pulsecore/module.h>
30#include <pulsecore/iochannel.h>
31#include <pulsecore/cli.h>
32#include <pulsecore/sioman.h>
33#include <pulsecore/log.h>
34#include <pulsecore/modargs.h>
35#include <pulsecore/macro.h>
36#include <pulsecore/core-util.h>
37
38PA_MODULE_AUTHOR("Lennart Poettering");
39PA_MODULE_DESCRIPTION("Command line interface");
40PA_MODULE_VERSION(PACKAGE_VERSION);
41PA_MODULE_LOAD_ONCE(true);
42PA_MODULE_USAGE("exit_on_eof=<exit daemon after EOF?>");
43
44static const char* const valid_modargs[] = {
45    "exit_on_eof",
46    NULL
47};
48
49static void eof_and_unload_cb(pa_cli*c, void *userdata) {
50    pa_module *m = userdata;
51
52    pa_assert(c);
53    pa_assert(m);
54
55    pa_module_unload_request(m, true);
56}
57
58static void eof_and_exit_cb(pa_cli*c, void *userdata) {
59    pa_module *m = userdata;
60
61    pa_assert(c);
62    pa_assert(m);
63
64    pa_core_exit(m->core, false, 0);
65}
66
67int pa__init(pa_module*m) {
68    pa_iochannel *io;
69    pa_modargs *ma;
70    bool exit_on_eof = false;
71#ifndef OS_IS_WIN32
72    int fd;
73#endif
74
75    pa_assert(m);
76
77    if (m->core->running_as_daemon) {
78        pa_log_info("Running as daemon, refusing to load this module.");
79        return 0;
80    }
81
82    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
83        pa_log("failed to parse module arguments.");
84        goto fail;
85    }
86
87    if (pa_modargs_get_value_boolean(ma, "exit_on_eof", &exit_on_eof) < 0) {
88        pa_log("exit_on_eof= expects boolean argument.");
89        goto fail;
90    }
91
92    if (pa_stdio_acquire() < 0) {
93        pa_log("STDIN/STDOUT already in use.");
94        goto fail;
95    }
96
97    /* We try to open the controlling tty anew here. This has the
98     * benefit of giving us a new fd that doesn't share the O_NDELAY
99     * flag with fds 0, 1, or 2. Since pa_iochannel_xxx needs O_NDELAY
100     * on its fd using those fds directly could set O_NDELAY which
101     * fprintf() doesn't really like, resulting in truncated output
102     * of log messages, particularly because if stdout and stderr are
103     * dup'ed they share the same O_NDELAY, too. */
104
105#ifndef OS_IS_WIN32
106    if ((fd = pa_open_cloexec("/dev/tty", O_RDWR|O_NONBLOCK, 0)) >= 0) {
107        io = pa_iochannel_new(m->core->mainloop, fd, fd);
108        pa_log_debug("Managed to open /dev/tty.");
109    }
110    else
111#endif
112    {
113        io = pa_iochannel_new(m->core->mainloop, STDIN_FILENO, STDOUT_FILENO);
114        pa_iochannel_set_noclose(io, true);
115        pa_log_debug("Failed to open /dev/tty, using stdin/stdout fds instead.");
116    }
117
118    m->userdata = pa_cli_new(m->core, io, m);
119    pa_cli_set_eof_callback(m->userdata, exit_on_eof ? eof_and_exit_cb : eof_and_unload_cb, m);
120
121    pa_modargs_free(ma);
122
123    return 0;
124
125fail:
126
127    if (ma)
128        pa_modargs_free(ma);
129
130    return -1;
131}
132
133void pa__done(pa_module*m) {
134    pa_assert(m);
135
136    if (m->userdata) {
137        pa_cli_free(m->userdata);
138        pa_stdio_release();
139    }
140}
141