summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-21 08:39:20 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2025-08-24 11:54:08 +0300
commit9a2d481fc397d1314d7bb6573f8727ef8c567c0f (patch)
tree61bea200079109292c9d712bab4497aa62f9e1f0
parentd5a0fce4cfd9eb7a3c75ac86257b6d7d9e2d7b7f (diff)
mu: add --listen option for server
Add a --listen option for the server Rework the option code to share the --listen / socket-path code between scm and server subcommands. Move option off the stack in mu.cc, seems it's too big, at least when using gdb.
-rw-r--r--man/mu-server.1.org12
-rw-r--r--mu/mu-cmd-server.cc77
-rw-r--r--mu/mu-options.cc28
-rw-r--r--mu/mu-options.hh16
-rw-r--r--mu/mu.cc26
5 files changed, 116 insertions, 43 deletions
diff --git a/man/mu-server.1.org b/man/mu-server.1.org
index cd401e0..3d90b8f 100644
--- a/man/mu-server.1.org
+++ b/man/mu-server.1.org
@@ -61,10 +61,16 @@ directly through the emacs process input/output. This is noticeably faster for
commands with a lot of output, esp. when the the temp-file uses a in-memory
file-system.
+** --listen
+If set, the server starts an SCM REPL as well, which listens on a Unix domain
+socket. This corresponds to the *--listen* options for *mu scm**.
+
+The store object (including the Xapian database) is shared between the server and the REPL.
+
* PERFORMANCE
As an indication for the relative performance, we can simulate something ~mu4e~
-does; we take overall time of 50 such requests:
+does. We take the overall time of 50 such requests:
#+begin_src sh
time build/mu/mu server --allow-temp-file --eval '(find :query "\"\"" :include-related t :threads t :maxnum 50000)' >/dev/null
@@ -78,7 +84,6 @@ time build/mu/mu server --allow-temp-file --eval '(find :query "\"\"" :include-r
| 1.10 | 5.7s |
| 1.11 (master) | 2.8s |
-
#+include: "muhome.inc" :minlevel 2
#+include: "common-options.inc" :minlevel 1
@@ -87,4 +92,5 @@ time build/mu/mu server --allow-temp-file --eval '(find :query "\"\"" :include-r
* SEE ALSO
-{{{man-link(mu,1)}}}
+{{{man-link(mu,1)}}},
+{{{man-link(mu-scm,1)}}}
diff --git a/mu/mu-cmd-server.cc b/mu/mu-cmd-server.cc
index 3a69456..280ec4e 100644
--- a/mu/mu-cmd-server.cc
+++ b/mu/mu-cmd-server.cc
@@ -1,5 +1,5 @@
/*
-** Copyright (C) 2020-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
+** Copyright (C) 2020-2025 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
@@ -29,6 +29,10 @@
#include "mu-cmd.hh"
#include "mu-server.hh"
+#if BUILD_SCM
+#include "scm/mu-scm.hh"
+#endif/*BUILD_SCM*/
+
#include "utils/mu-utils.hh"
#include "utils/mu-readline.hh"
@@ -60,6 +64,8 @@ install_sig_handler()
}
/*
+ * djcb@djcbsoftware.nl
+ *
* Markers for/after the length cookie that precedes the expression we write to
* output. We use octal 376, 377 (ie, 0xfe, 0xff) as they will never occur in
* utf8
@@ -102,6 +108,46 @@ report_error(const Mu::Error& err) noexcept
Server::OutputFlags::Flush);
}
+static void
+maybe_setup_readline(const Mu::Options& opts)
+{
+ tty = ::isatty(::fileno(stdout));
+
+ // Note, the readline stuff is inactive unless on a tty.
+ const auto histpath{opts.runtime_path(RuntimePath::Cache) + "/history"};
+ setup_readline(histpath, 50);
+}
+
+// get the SCM socket path we're listening on, or empty if not listening
+static std::string
+maybe_listen_path(const Mu::Store& store, const Mu::Options& opts)
+{
+#ifdef BUILD_SCM
+ if (!opts.scm.socket_path)
+ return {};
+ const auto res = Mu::Scm::run(store, opts, false/*!block*/);
+ if (!res) {
+ mu_warning("failed to start scm socket: {}", res.error().what());
+ return {};
+ } else
+ return *opts.scm.socket_path;
+#endif /*BUILD_SCM*/
+ return {};
+}
+
+static bool
+maybe_eval(Server& server, const Mu::Options& opts)
+{
+ const auto eval = std::string{opts.server.commands ?
+ "(help :full t)" : opts.server.eval};
+
+ if (!eval.empty()) {
+ server.invoke(eval);
+ return true;
+ } else
+ return false;
+}
+
Result<void>
Mu::mu_cmd_server(const Mu::Options& opts) try {
@@ -110,31 +156,23 @@ Mu::mu_cmd_server(const Mu::Options& opts) try {
if (!store)
return Err(store.error());
- Server::Options sopts{};
- sopts.allow_temp_file = opts.server.allow_temp_file;
+ // empty when we're not listening
+ const auto socket_path = maybe_listen_path(*store, opts);
+ Server::Options sopts{opts.server.allow_temp_file, socket_path};
Server server{*store, sopts, output_stdout};
- mu_message("created server with store @ {}; maildir @ {}; debug-mode {};"
- "readline: {}",
- store->path(), store->root_maildir(),
- opts.debug ? "yes" : "no",
- have_readline() ? "yes" : "no");
- tty = ::isatty(::fileno(stdout));
- const auto eval = std::string{opts.server.commands ? "(help :full t)" : opts.server.eval};
- if (!eval.empty()) {
- server.invoke(eval);
+ if (maybe_eval(server, opts))
return Ok();
- }
-
- // Note, the readline stuff is inactive unless on a tty.
- const auto histpath{opts.runtime_path(RuntimePath::Cache) + "/history"};
- setup_readline(histpath, 50);
+ maybe_setup_readline(opts);
install_sig_handler();
- mu_println(";; Welcome to the " PACKAGE_STRING " command-server{}\n"
- ";; Use (help) to get a list of commands, (quit) to quit.",
+
+ mu_println(";; Welcome to the " PACKAGE_STRING " command-server{}",
opts.debug ? " (debug-mode)" : "");
+ if (!socket_path.empty())
+ mu_println(";; SCM socket listening on {}", socket_path);
+ mu_println(";; Use (help) to get a list of commands, (quit) to quit.");
bool do_quit{};
while (!MuTerminate && !do_quit) {
@@ -151,7 +189,6 @@ Mu::mu_cmd_server(const Mu::Options& opts) try {
mu_message ("shutting down due to signal {}", MuTerminate.load());
shutdown_readline();
-
return Ok();
} catch (const Error& er) { /* note: user-level error, "OK" for mu */
diff --git a/mu/mu-options.cc b/mu/mu-options.cc
index b91138e..0db3bb6 100644
--- a/mu/mu-options.cc
+++ b/mu/mu-options.cc
@@ -616,6 +616,24 @@ sub_remove(CLI::App& sub, Options& opts)
->type_name("<files>");
}
+static std::string
+concoct_socket_path()
+{
+ return join_paths(g_get_user_runtime_dir(),
+ mu_format("mu-scm-{}.sock", ::getpid()));
+}
+
+static void
+add_listen_flag(CLI::App& sub, Options& opts)
+{
+ sub.add_flag("--listen", opts.scm.listen,
+ "Start SCM REPL on a domain socket");
+ sub.callback([&opts]{
+ if (opts.scm.listen)
+ opts.scm.socket_path = concoct_socket_path();
+ });
+}
+
static void
sub_server(CLI::App& sub, Options& opts)
{
@@ -627,18 +645,23 @@ sub_server(CLI::App& sub, Options& opts)
sub.add_flag("--allow-temp-file", opts.server.allow_temp_file,
"Allow for the temp-file optimization")
->excludes("--commands");
+
+#if BUILD_SCM
+ add_listen_flag(sub, opts);
+#endif/*BUILD_SCM*/
}
static void
sub_scm(CLI::App& sub, Options& opts)
{
- sub.add_flag("--listen", opts.scm.listen,
- "Start listening on a domain socket");
+ add_listen_flag(sub, opts);
+
sub.add_option("script-path", opts.scm.script_path, "Path to script")
->type_name("<path>")
->excludes("--listen");
sub.add_option("script-args", opts.scm.params, "Parameters for script")
->type_name("<parameters>");
+
}
static void
@@ -787,7 +810,6 @@ AssocPairs<SubCommand, CommandInfo, Options::SubCommandNum> SubCommandInfos= {{
}};
-
static ScriptInfos
add_scripts(CLI::App& app, Options& opts)
{
diff --git a/mu/mu-options.hh b/mu/mu-options.hh
index 245678d..e0a51fe 100644
--- a/mu/mu-options.hh
+++ b/mu/mu-options.hh
@@ -46,13 +46,13 @@ struct Options {
/*
* general options
*/
- bool quiet; /**< don't give any output */
- bool debug; /**< log debug-level info */
- bool version; /**< request mu version */
- bool log_stderr; /**< log to stderr */
- bool nocolor; /**< don't use use ansi-colors */
- bool verbose; /**< verbose output */
- std::string muhome; /**< alternative mu dir */
+ bool quiet; /**< don't give any output */
+ bool debug; /**< log debug-level info */
+ bool version; /**< request mu version */
+ bool log_stderr; /**< log to stderr */
+ bool nocolor; /**< don't use use ansi-colors */
+ bool verbose; /**< verbose output */
+ std::string muhome; /**< alternative mu dir */
/**
* Whether by default, we should show color
@@ -260,6 +260,8 @@ struct Options {
OptString script_path; /**< Path to script (optional) */
StringVec params; /**< Parameters for script (after "--") */
bool listen; /**< Whether to start listening on a socket */
+ Option<std::string> socket_path; /**< path for the '--listen'
+ * Unix domain socket */
} scm;
diff --git a/mu/mu.cc b/mu/mu.cc
index 69d3c48..0a580bc 100644
--- a/mu/mu.cc
+++ b/mu/mu.cc
@@ -77,6 +77,10 @@ handle_result(const Result<void>& res, const Mu::Options& opts)
return res.error().exit_code();
}
+namespace {
+Options opts; // it's big, don't want it on the stack
+}
+
int
main(int argc, char* argv[]) try
{
@@ -93,34 +97,36 @@ main(int argc, char* argv[]) try
/*
* read command-line options
*/
- const auto opts{Options::make(argc, argv)};
- if (!opts) {
- output_error(opts.error().what(), !Options::default_no_color());
- return opts.error().exit_code();
- } else if (!opts->sub_command) {
+ auto optsres{Options::make(argc, argv)};
+ if (!optsres) {
+ output_error(optsres.error().what(), !Options::default_no_color());
+ return optsres.error().exit_code();
+ } else if (!optsres->sub_command) {
// nothing more to do.
return 0;
}
+ opts = std::move(*optsres);
+
// setup logging
Logger::Options lopts{Logger::Options::None};
- if (opts->log_stderr)
+ if (opts.log_stderr)
lopts |= Logger::Options::StdOutErr;
- if (opts->debug)
+ if (opts.debug)
lopts |= Logger::Options::Debug;
if (!!g_getenv("MU_TEST"))
lopts |= Logger::Options::File;
- const auto logger{Logger::make(opts->runtime_path(RuntimePath::LogFile), lopts)};
+ const auto logger{Logger::make(opts.runtime_path(RuntimePath::LogFile), lopts)};
if (!logger) {
- output_error(logger.error().what(), !opts->nocolor);
+ output_error(logger.error().what(), !opts.nocolor);
return logger.error().exit_code();
}
/*
* handle sub command
*/
- return handle_result(mu_cmd_execute(*opts), *opts);
+ return handle_result(mu_cmd_execute(opts), opts);
// exceptions should have been handled earlier, but catch them here,
// just in case...