Anklang-0.3.0.dev835+g24d8ae08 anklang-0.3.0.dev835+g24d8ae08
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
webui.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "webui.hh"
3#include "logging.hh"
4#include "platform.hh"
5#include "strings.hh"
6#include <cerrno>
7#include <fcntl.h>
8#include <sys/wait.h>
9#include <sys/prctl.h>
10#include <sys/stat.h>
11#include <filesystem>
12#include <dirent.h>
13#include "path.hh"
14
15#define WDEBUG(...) Ase::debug ("webui", __VA_ARGS__)
16
17namespace Ase {
18
19String // check errno
20webui_create_auth_redirect (const std::string &executable, unsigned port, const std::string &token, const std::string &snapmode)
21{
22 String basedir = Path::cache_home() + "/" + executable;
23 /* The canonical path for the redirect is $XDG_CACHE_HOME/<executable>/<executable>-<port>.html
24 * But snap based browsers cannot read from ~/.* $XDG_RUNTIME_DIR ~/snap/… /var/tmp or /tmp/.
25 * This means we must find a path under ~/[^.]* or ~/<subdir>/.something that comes close.
26 * In any case, we only do this on systems with snapd.
27 */
28 bool snap_workaround = true; // __linux__
29 if (!Path::check ("/tmp/snap-private-tmp/", "d") ||
30 (snapmode == "htmlgui" && Path::check (anklang_runpath (RPath::ELECTRONDIR, "htmlgui"), "x")))
31 snap_workaround = false; // not starting a snap browser
32 if (snap_workaround) {
33 basedir = Path::xdg_dir ("DOWNLOAD") + "/." + executable;
34 const auto readme =
35 "This directory only exists to enable temporary file exchange with\n"
36 "snap packages which are unable to read from ~/.cache/.\n"
37 "Feel free to remove this directory in its entirety.\n";
38 if (!Path::check (basedir, "dw") &&
39 !Path::stringwrite (basedir + "/README", readme, true)) {
40 errno = errno ? errno : EIO;
41 return basedir;
42 }
43 }
45 const String link = string_format ("http://localhost:%u/~auth", port);
46 const String query = token.empty() ? "" : "?token=" + token;
47 const String ua = string_format ("%s-%u", string_capitalize (executable), port);
48 const String html_text =
49 string_format ("<!DOCTYPE html>\n"
50 "<html><!--@@TEMPFILE_PID=%d@@-->\n"
51 "<head><title>%s Authentication Redirect</title>\n" // 307 Temporary Redirect
52 "<meta http-equiv=\"refresh\" content=\"0; url=%s\">\n"
53 "</head>\n<body>\n"
54 "<h1>%s Authentication Redirect</h1>\n\n"
55 "<p>Redirecting to %s: <a href=\"%s\">%s</a></p>\n"
56 "<hr><address>%s</address>\n"
57 "<hr></body></html>\n",
58 getpid(), ua, link + query, ua, ua, link + query, link, ua);
59 const String html_file = string_format ("%s/%s-%u.html", basedir, executable, port);
60 if (!Path::stringwrite (html_file, html_text, true, 0600))
61 errno = errno ? errno : EIO;
62 else {
63 atquit_add_removal (html_file);
64 errno = 0;
65 }
66 return html_file;
67}
68
69ErrorReason
70webui_start_browser (const std::string &mode, LoopP loop, const std::string &url, const std::function<void()> &onclose, WebuiFlags flags)
71{
73 std::string browser_name;
74 int stdio_fd = -1;
75 std::vector<int> keep_fds;
76
77 if (!! (flags & WebuiFlags::STDIO_REDIRECT)) {
78 const String logdir = Path::cache_home() + "/anklang";
79 const String logfile = string_format ("%s/%s-%08x.log", logdir, "webui", gethostid());
80 if (!Path::mkdirs (logdir))
81 return { errno, "mkdirs " + logdir };
82 stdio_fd = open (logfile.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_NOFOLLOW, 0640);
83 if (stdio_fd < 0)
84 return { errno, "open " + logfile };
85 }
86
87 // Duplicate current stdout/stderr file descriptors for htmlgui console output
88 int console_stdout_fd = -1, console_stderr_fd = -1;
89 if (!! (flags & WebuiFlags::CONSOLE_LOGS)) {
90 console_stdout_fd = dup (STDOUT_FILENO);
91 if (console_stdout_fd < 0)
92 return { errno, "dup stdout" };
93 console_stderr_fd = dup (STDERR_FILENO);
94 if (console_stderr_fd < 0) {
95 close (console_stdout_fd);
96 return { errno, "dup stderr" };
97 }
98 keep_fds.push_back (console_stdout_fd);
99 keep_fds.push_back (console_stderr_fd);
100 }
101
102 if (mode == "chromium" || mode == "google-chrome")
103 {
104 browser_name = mode;
105 argv.push_back (browser_name);
106 std::string temp_dir = create_tempfile_dir();
107 if (temp_dir.empty())
108 return { errno, "mkdtemp" };
109 const std::string user_data_dir_arg = "--user-data-dir=" + temp_dir;
110 std::string app = "--app=";
111 app += url;
112 for (const auto arg : {
113 "--incognito", "--no-first-run", "--no-experiments",
114 "--no-default-browser-check", "--disable-extensions", "--disable-sync",
115 // "--auto-open-devtools-for-tabs",
116 "--bwsi", "--new-window" })
117 argv.push_back (arg);
118 if (!! (flags & WebuiFlags::HEADLESS))
119 argv.push_back ("--headless");
120 argv.push_back (user_data_dir_arg);
121 argv.push_back (app);
122 }
123 else if (mode == "htmlgui")
124 {
125 browser_name = mode;
126 argv.push_back (anklang_runpath (RPath::ELECTRONDIR, "htmlgui"));
127 argv.push_back ("--no-sandbox");
128 if (!! (flags & WebuiFlags::HEADLESS))
129 argv.push_back ("--headless");
130 if (console_stdout_fd >= 0 && console_stderr_fd >= 0)
131 argv.push_back (string_format ("--console-logs=%d,%d", console_stdout_fd, console_stderr_fd));
132 argv.push_back (url);
133 }
134 else if (mode == "none" or mode == "" or mode == "wait")
135 return { 0 }; // none
136 else
137 return { EINVAL, string_format ("unknown webui: %s", mode) };
138
139 pid_t child_pid = 0;
140 ErrorReason ereason = spawn_process (argv, &child_pid, SIGTERM, stdio_fd, keep_fds);
141 close (stdio_fd);
142 for (int fd : keep_fds)
143 if (fd >= 0)
144 close (fd);
145 if (ereason.error)
146 return ereason;
147 atquit_add_killl_pid (child_pid);
148 loop->exec_sigchld (child_pid,
149 [onclose] (pid_t pid, int status)
150 {
151 std::string state;
152 if (WIFEXITED (status))
153 state = string_format ("status=%d", WEXITSTATUS (status));
154 else if (WIFSIGNALED (status))
155 state = string_format ("signal=%d", WTERMSIG (status));
156 if (state.size()) {
157 info ("WebUI: child process pid=%d exited: %s", pid, state);
158 atquit_del_killl_pid (pid);
159 }
160 if (onclose)
161 onclose();
162 });
163 info ("WebUI: started %s pid=%d: %s", browser_name, child_pid, url);
164 return { 0, "" }; // Success
165}
166
167} // Ase
#define EIO
close
dup
T empty(T... args)
errno
gethostid
getpid
link
bool mkdirs(const String &dirpath, uint mode)
Create the directories in dirpath with mode, check errno on false returns.
Definition path.cc:197
bool check(const String &file, const String &mode)
Definition path.cc:625
String cache_home()
Get the $XDG_CACHE_HOME directory, see: https://specifications.freedesktop.org/basedir-spec/latest.
Definition path.cc:322
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
void atquit_add_removal(const std::string &filename)
Remove filename (or directory) when the program terminates.
Definition atquit.cc:107
String string_capitalize(const String &str, size_t maxn, bool rest_tolower)
Capitalize words, so the first letter is upper case, the rest lower case.
Definition strings.cc:186
std::string anklang_runpath(RPath rpath, const String &segment)
Retrieve various resource paths at runtime.
Definition platform.cc:58
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
ErrorReason spawn_process(const std::vector< std::string > &argv, pid_t *child_pid, int pdeathsig, int stdio_fd, const std::vector< int > &keep_fds)
Span a child process after cleaning up the environment.
Definition atquit.cc:180
void cleanup_orphaned_tempfiles(const std::string &directory)
Delete all files that contain @TEMPFILE_PID=d@ without a running pid_t d.
Definition atquit.cc:21
void atquit_add_killl_pid(int pid)
Kill pid when the program terminates.
Definition atquit.cc:166
std::string create_tempfile_dir(const std::string &basename)
Create temporary directory under /tmp, scheduled for removal atquit.
Definition atquit.cc:256
open
T size(T... args)
typedef pid_t