mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-10 01:11:11 -03:00
Compare commits
17 Commits
2.1.0
...
Integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95687a03fb | ||
|
|
b3aa187867 | ||
|
|
0986b6d991 | ||
|
|
7f4908b0db | ||
|
|
482e615fe0 | ||
|
|
9c78295a9a | ||
|
|
fd70ae0b61 | ||
|
|
3e2d68a059 | ||
|
|
78e2b7cc08 | ||
|
|
397249a8d5 | ||
|
|
b44b624ca5 | ||
|
|
b5cd21c337 | ||
|
|
4cb4fc3ef8 | ||
|
|
af14cf8f8b | ||
|
|
c0989dce2d | ||
|
|
8412c867a5 | ||
|
|
10642a34f1 |
@@ -1,3 +1,5 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
./add-shell /usr/local/bin/fish > /tmp/fish_postinstall_output.log
|
||||
killall fishd || true
|
||||
|
||||
|
||||
71
common.cpp
71
common.cpp
@@ -24,6 +24,7 @@ parts of fish.
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
@@ -2342,3 +2343,73 @@ char **make_null_terminated_array(const std::vector<std::string> &lst)
|
||||
{
|
||||
return make_null_terminated_array_helper(lst);
|
||||
}
|
||||
|
||||
/**
|
||||
Check, and create if necessary, a secure runtime path
|
||||
Derived from tmux.c in tmux (http://tmux.sourceforge.net/)
|
||||
*/
|
||||
static int check_runtime_path(const char * path)
|
||||
{
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
struct stat statpath;
|
||||
u_int uid = geteuid();
|
||||
|
||||
if (mkdir(path, S_IRWXU) != 0 && errno != EEXIST)
|
||||
return errno;
|
||||
if (lstat(path, &statpath) != 0)
|
||||
return errno;
|
||||
if (!S_ISDIR(statpath.st_mode)
|
||||
|| statpath.st_uid != uid
|
||||
|| (statpath.st_mode & (S_IRWXG|S_IRWXO)) != 0)
|
||||
return EACCES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return the path of an appropriate runtime data directory */
|
||||
std::string common_get_runtime_path()
|
||||
{
|
||||
const char *dir = getenv("XDG_RUNTIME_DIR");
|
||||
const char *uname = getenv("USER");
|
||||
std::string path;
|
||||
|
||||
if (uname == NULL)
|
||||
{
|
||||
const struct passwd *pw = getpwuid(getuid());
|
||||
uname = pw->pw_name;
|
||||
}
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
// /tmp/fish.user
|
||||
dir = "/tmp/fish.";
|
||||
path.reserve(strlen(dir) + strlen(uname));
|
||||
path.append(dir);
|
||||
path.append(uname);
|
||||
if (check_runtime_path(path.c_str()) != 0)
|
||||
{
|
||||
debug(0, L"Runtime path not available. Try deleting the directory %s and restarting fish.", path.c_str());
|
||||
path.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path.reserve(strlen(dir));
|
||||
path.append(dir);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
2
common.h
2
common.h
@@ -813,5 +813,7 @@ extern "C" {
|
||||
__attribute__((noinline)) void debug_thread_error(void);
|
||||
}
|
||||
|
||||
/** Return the path of an appropriate runtime data directory */
|
||||
std::string common_get_runtime_path();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -387,8 +387,9 @@ cursor, in a muted gray color (which can be changed with the
|
||||
<code>fish_color_autosuggestion</code> variable).
|
||||
|
||||
To accept the autosuggestion (replacing the command line contents),
|
||||
hit right arrow or Control-F. If the autosuggestion is not what you want,
|
||||
just ignore it: it won't execute unless you accept it.
|
||||
press right arrow or Control-F. To accept the first suggested word, press
|
||||
Alt-Right. If the autosuggestion is not what you want, just ignore it: it won't
|
||||
execute unless you accept it.
|
||||
|
||||
Autosuggestions are a powerful way to quickly summon frequently entered commands, by
|
||||
typing the first few characters. They are also an efficient technique for navigating
|
||||
@@ -1123,7 +1124,7 @@ Here are some of the commands available in the editor:
|
||||
- Home or Ctrl-A moves the cursor to the beginning of the line.
|
||||
- End or Ctrl-E moves to the end of line. If the cursor is already at the end of the line, and an autosuggestion is available, End or Ctrl-E accepts the autosuggestion.
|
||||
- Left (or Ctrl-B) and Right (or Ctrl-F) move the cursor left or right by one character. If the cursor is already at the end of the line, and an autosuggestion is available, the Right key and the Ctrl-F combination accept the suggestion.
|
||||
- Alt-Left and Alt-Right move the cursor one word left or right, or moves forward/backward in the directory history if the command line is empty.
|
||||
- Alt-Left and Alt-Right move the cursor one word left or right, or moves forward/backward in the directory history if the command line is empty. If the cursor is already at the end of the line, and an autosuggestion is available, Alt-Right accept the first word in the suggestion.
|
||||
- Up and Down search the command history for the previous/next command containing the string that was specified on the commandline before the search was started. If the commandline was empty when the search started, all commands match. See the <a href='#history'>history </a>section for more information on history searching.
|
||||
- Alt-Up and Alt-Down search the command history for the previous/next token containing the token under the cursor before the search was started. If the commandline was not on a token when the search started, all tokens match. See the <a href='#history'>history </a>section for more information on history searching.
|
||||
- Delete and Backspace removes one character forwards or backwards respectively.
|
||||
|
||||
@@ -1400,6 +1400,26 @@ POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
<P>
|
||||
|
||||
<h2>License for code derived from tmux</h2>
|
||||
|
||||
Fish contains code derived from
|
||||
<a href="http://tmux.sourceforge.net">tmux</a>, made available under an ISC
|
||||
license.
|
||||
<p>
|
||||
Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
<p>
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
<p>
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
\htmlonly </div> \endhtmlonly
|
||||
|
||||
@@ -349,7 +349,7 @@ And history too. Type a command once, and you can re-summon it by just typing a
|
||||
> <b>r</b><span class="suggest"><u>s</u>ync -avze ssh . myname@somelonghost.com:/some/long/path/doo/dee/doo/dee/doo</span>
|
||||
</pre>
|
||||
|
||||
To accept the autosuggestion, hit right arrow or Control-F. If the autosuggestion is not what you want, just ignore it.
|
||||
To accept the autosuggestion, hit right arrow or Control-F. To accept a single word of the autosuggestion, hit Alt+right arrow. If the autosuggestion is not what you want, just ignore it.
|
||||
|
||||
<h2 id="tut_tab_completions">Tab Completions</h2>
|
||||
|
||||
|
||||
7
env.cpp
7
env.cpp
@@ -57,7 +57,7 @@
|
||||
#include "complete.h"
|
||||
|
||||
/** Command used to start fishd */
|
||||
#define FISHD_CMD L"fishd ^ /tmp/fishd.log.%s"
|
||||
#define FISHD_CMD L"fishd ^ $__fish_runtime_dir/fishd.log.%s"
|
||||
|
||||
// Version for easier debugging
|
||||
//#define FISHD_CMD L"fishd"
|
||||
@@ -618,10 +618,11 @@ void env_init(const struct config_paths_t *paths /* or NULL */)
|
||||
env_set(L"version", version.c_str(), ENV_GLOBAL);
|
||||
env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL);
|
||||
|
||||
const env_var_t fishd_dir_wstr = env_get_string(L"FISHD_SOCKET_DIR");
|
||||
const env_var_t user_dir_wstr = env_get_string(L"USER");
|
||||
|
||||
wchar_t * fishd_dir = fishd_dir_wstr.missing()?NULL:const_cast<wchar_t*>(fishd_dir_wstr.c_str());
|
||||
std::string fishd_dir = common_get_runtime_path();
|
||||
env_set(L"__fish_runtime_dir", str2wcstring(fishd_dir).c_str(), ENV_GLOBAL | ENV_EXPORT);
|
||||
|
||||
wchar_t * user_dir = user_dir_wstr.missing()?NULL:const_cast<wchar_t*>(user_dir_wstr.c_str());
|
||||
|
||||
env_universal_init(fishd_dir , user_dir ,
|
||||
|
||||
@@ -61,7 +61,7 @@ static int get_socket_count = 0;
|
||||
#define DEFAULT_RETRY_COUNT 15
|
||||
#define DEFAULT_RETRY_DELAY 0.2
|
||||
|
||||
static wchar_t * path;
|
||||
static const char * path;
|
||||
static wchar_t *user;
|
||||
static void (*start_fishd)();
|
||||
static void (*external_callback)(fish_message_type_t type, const wchar_t *name, const wchar_t *val);
|
||||
@@ -82,48 +82,19 @@ static int try_get_socket_once(void)
|
||||
{
|
||||
int s;
|
||||
|
||||
wchar_t *wdir;
|
||||
wchar_t *wuname;
|
||||
char *dir = 0;
|
||||
|
||||
wdir = path;
|
||||
wuname = user;
|
||||
|
||||
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
wperror(L"socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wdir)
|
||||
dir = wcs2str(wdir);
|
||||
else
|
||||
dir = strdup("/tmp");
|
||||
|
||||
std::string uname;
|
||||
if (wuname)
|
||||
{
|
||||
uname = wcs2string(wuname);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (pw && pw->pw_name)
|
||||
{
|
||||
uname = pw->pw_name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
name.reserve(strlen(dir) + uname.size() + strlen(SOCK_FILENAME) + 2);
|
||||
name.append(dir);
|
||||
name.append("/");
|
||||
name.reserve(strlen(path) + strlen(SOCK_FILENAME) + 1);
|
||||
name.append(path);
|
||||
name.push_back('/');
|
||||
name.append(SOCK_FILENAME);
|
||||
name.append(uname);
|
||||
|
||||
free(dir);
|
||||
|
||||
debug(3, L"Connect to socket %s at fd %2", name.c_str(), s);
|
||||
debug(3, L"Connect to socket %s at fd %d", name.c_str(), s);
|
||||
|
||||
struct sockaddr_un local = {};
|
||||
local.sun_family = AF_UNIX;
|
||||
@@ -271,23 +242,29 @@ static void reconnect()
|
||||
}
|
||||
|
||||
|
||||
void env_universal_init(wchar_t * p,
|
||||
void env_universal_init(std::string p,
|
||||
wchar_t *u,
|
||||
void (*sf)(),
|
||||
void (*cb)(fish_message_type_t type, const wchar_t *name, const wchar_t *val))
|
||||
{
|
||||
path=p;
|
||||
path=p.c_str();
|
||||
user=u;
|
||||
start_fishd=sf;
|
||||
external_callback = cb;
|
||||
|
||||
env_universal_server.fd = get_socket();
|
||||
env_universal_common_init(&callback);
|
||||
env_universal_read_all();
|
||||
s_env_univeral_inited = true;
|
||||
if (env_universal_server.fd >= 0)
|
||||
if (p == "") {
|
||||
debug(1, L"Could not connect to universal variable server. You will not be able to share variable values between fish sessions.");
|
||||
}
|
||||
else
|
||||
{
|
||||
env_universal_barrier();
|
||||
env_universal_server.fd = get_socket();
|
||||
env_universal_common_init(&callback);
|
||||
env_universal_read_all();
|
||||
s_env_univeral_inited = true;
|
||||
if (env_universal_server.fd >= 0)
|
||||
{
|
||||
env_universal_barrier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ extern connection_t env_universal_server;
|
||||
/**
|
||||
Initialize the envuni library
|
||||
*/
|
||||
void env_universal_init(wchar_t * p,
|
||||
void env_universal_init(std::string p,
|
||||
wchar_t *u,
|
||||
void (*sf)(),
|
||||
void (*cb)(fish_message_type_t type, const wchar_t *name, const wchar_t *val));
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <locale.h>
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <map>
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
@@ -86,6 +85,13 @@
|
||||
*/
|
||||
#define ENV_UNIVERSAL_EOF 0x102
|
||||
|
||||
/**
|
||||
Maximum length of socket filename
|
||||
*/
|
||||
#ifndef UNIX_PATH_MAX
|
||||
#define UNIX_PATH_MAX 100
|
||||
#endif
|
||||
|
||||
/**
|
||||
A variable entry. Stores the value of a variable and whether it
|
||||
should be exported. Obviously, it needs to be allocated large
|
||||
@@ -417,7 +423,7 @@ void env_universal_common_init(void (*cb)(fish_message_type_t type, const wchar_
|
||||
}
|
||||
|
||||
/**
|
||||
Read one byte of date form the specified connection
|
||||
Read one byte of date from the specified connection
|
||||
*/
|
||||
static int read_byte(connection_t *src)
|
||||
{
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
|
||||
/**
|
||||
The filename to use for univeral variables. The username is appended
|
||||
The filename to use for univeral variables.
|
||||
*/
|
||||
#define SOCK_FILENAME "fishd.socket."
|
||||
#define SOCK_FILENAME "fishd.socket"
|
||||
|
||||
/**
|
||||
The different types of commands that can be sent between client/server
|
||||
@@ -133,6 +133,11 @@ void try_send_all(connection_t *c);
|
||||
*/
|
||||
message_t *create_message(fish_message_type_t type, const wchar_t *key, const wchar_t *val);
|
||||
|
||||
/**
|
||||
Constructs the fish socket filename
|
||||
*/
|
||||
std::string env_universal_common_get_socket_filename(void);
|
||||
|
||||
/**
|
||||
Init the library
|
||||
*/
|
||||
|
||||
@@ -1190,7 +1190,7 @@
|
||||
"DATADIR=L\\\"/usr/local/share\\\"",
|
||||
"SYSCONFDIR=L\\\"/usr/local/etc\\\"",
|
||||
"BINDIR=L\\\"/usr/local/bin\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.0\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.2\\\"",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
@@ -1342,7 +1342,7 @@
|
||||
"DATADIR=L\\\"/usr/local/share\\\"",
|
||||
"SYSCONFDIR=L\\\"/usr/local/etc\\\"",
|
||||
"BINDIR=L\\\"/usr/local/bin\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.0\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.2\\\"",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
@@ -1370,7 +1370,7 @@
|
||||
"DATADIR=L\\\"/usr/local/share\\\"",
|
||||
"SYSCONFDIR=L\\\"/usr/local/etc\\\"",
|
||||
"BINDIR=L\\\"/usr/local/bin\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.0\\\"",
|
||||
"FISH_BUILD_VERSION=\\\"2.1.2\\\"",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
|
||||
@@ -1032,8 +1032,8 @@ static void init(int mangle_descriptors, int out)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
env_universal_init(0, 0, 0, 0);
|
||||
std::string dir = common_get_runtime_path();
|
||||
env_universal_init(dir, 0, 0, 0);
|
||||
input_common_init(&interrupt_handler);
|
||||
output_set_writer(&pager_buffered_writer);
|
||||
|
||||
|
||||
73
fishd.cpp
73
fishd.cpp
@@ -158,6 +158,32 @@ static int quit=0;
|
||||
Constructs the fish socket filename
|
||||
*/
|
||||
static std::string get_socket_filename(void)
|
||||
{
|
||||
std::string dir = common_get_runtime_path();
|
||||
|
||||
if (dir == "") {
|
||||
debug(0, L"Cannot access desired socket path.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
name.reserve(dir.length() + strlen(SOCK_FILENAME) + 1);
|
||||
name.append(dir);
|
||||
name.push_back('/');
|
||||
name.append(SOCK_FILENAME);
|
||||
|
||||
if (name.size() >= UNIX_PATH_MAX)
|
||||
{
|
||||
debug(1, L"Filename too long: '%s'", name.c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
Constructs the legacy socket filename
|
||||
*/
|
||||
static std::string get_old_socket_filename(void)
|
||||
{
|
||||
const char *dir = getenv("FISHD_SOCKET_DIR");
|
||||
char *uname = getenv("USER");
|
||||
@@ -174,10 +200,9 @@ static std::string get_socket_filename(void)
|
||||
}
|
||||
|
||||
std::string name;
|
||||
name.reserve(strlen(dir)+ strlen(uname)+ strlen(SOCK_FILENAME) + 1);
|
||||
name.reserve(strlen(dir)+ strlen(uname)+ strlen("fishd.socket.") + 1);
|
||||
name.append(dir);
|
||||
name.push_back('/');
|
||||
name.append(SOCK_FILENAME);
|
||||
name.append("/fishd.socket.");
|
||||
name.append(uname);
|
||||
|
||||
if (name.size() >= UNIX_PATH_MAX)
|
||||
@@ -541,6 +566,7 @@ repeat:
|
||||
int exitcode = EXIT_FAILURE;
|
||||
struct sockaddr_un local;
|
||||
const std::string sock_name = get_socket_filename();
|
||||
const std::string old_sock_name = get_old_socket_filename();
|
||||
|
||||
/*
|
||||
Start critical section protected by lock
|
||||
@@ -598,6 +624,28 @@ repeat:
|
||||
doexit = 1;
|
||||
}
|
||||
|
||||
// Attempt to hardlink the old socket name so that old versions of fish keep working on upgrade
|
||||
// Not critical if it fails
|
||||
// On OS X, we use symlinks instead of hardlinks to work around a filesystem corruption bug http://openradar.appspot.com/19687545
|
||||
// On Linux, we use hardlinks instead of symlinks for compatibility with the Yama security model - see #1859
|
||||
int (*linkfunc)(const char *, const char *);
|
||||
#if __APPLE__
|
||||
linkfunc = symlink;
|
||||
#else
|
||||
linkfunc = link;
|
||||
#endif
|
||||
|
||||
if (unlink(old_sock_name.c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
debug(0, L"Could not create legacy socket path");
|
||||
wperror(L"unlink");
|
||||
}
|
||||
else if (linkfunc(sock_name.c_str(), old_sock_name.c_str()) != 0)
|
||||
{
|
||||
debug(0, L"Could not create legacy socket path");
|
||||
wperror(L"link");
|
||||
}
|
||||
|
||||
unlock:
|
||||
(void)unlink(lockfile.c_str());
|
||||
debug(4, L"Released lockfile: %s", lockfile.c_str());
|
||||
@@ -667,13 +715,14 @@ static void daemonize()
|
||||
setup_fork_guards();
|
||||
|
||||
/*
|
||||
Make fishd ignore the HUP signal.
|
||||
Make fishd ignore the HUP and PIPE signals.
|
||||
*/
|
||||
struct sigaction act;
|
||||
sigemptyset(& act.sa_mask);
|
||||
act.sa_flags=0;
|
||||
act.sa_handler=SIG_IGN;
|
||||
sigaction(SIGHUP, &act, 0);
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
/*
|
||||
Make fishd save and exit on the TERM signal.
|
||||
@@ -872,6 +921,18 @@ static void init()
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
Clean up behind ourselves
|
||||
*/
|
||||
static void cleanup()
|
||||
{
|
||||
if (unlink(get_old_socket_filename().c_str()) != 0)
|
||||
{
|
||||
debug(0, L"Could not remove legacy socket path");
|
||||
wperror(L"unlink");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Main function for fishd
|
||||
*/
|
||||
@@ -973,6 +1034,7 @@ int main(int argc, char ** argv)
|
||||
if (quit)
|
||||
{
|
||||
save();
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -982,6 +1044,7 @@ int main(int argc, char ** argv)
|
||||
if (errno != EINTR)
|
||||
{
|
||||
wperror(L"select");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -994,6 +1057,7 @@ int main(int argc, char ** argv)
|
||||
&t)) == -1)
|
||||
{
|
||||
wperror(L"accept");
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
@@ -1070,6 +1134,7 @@ int main(int argc, char ** argv)
|
||||
{
|
||||
debug(0, L"No more clients. Quitting");
|
||||
save();
|
||||
cleanup();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
#define PACKAGE_NAME "fish"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "fish 2.1.0"
|
||||
#define PACKAGE_STRING "fish 2.1.2"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "fish"
|
||||
@@ -192,7 +192,7 @@
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "2.1.0"
|
||||
#define PACKAGE_VERSION "2.1.2"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
@@ -12,6 +12,12 @@ function __fish_print_packages
|
||||
#Get the word 'Package' in the current language
|
||||
set -l package (_ Package)
|
||||
|
||||
# Set up cache directory
|
||||
if test -z "$XDG_CACHE_HOME"
|
||||
set XDG_CACHE_HOME $HOME/.cache
|
||||
end
|
||||
mkdir -m 700 -p $XDG_CACHE_HOME
|
||||
|
||||
if type -f apt-cache >/dev/null
|
||||
# Do not generate the cache as apparently sometimes this is slow.
|
||||
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=547550
|
||||
@@ -35,7 +41,7 @@ function __fish_print_packages
|
||||
|
||||
# If the cache is less than six hours old, we do not recalculate it
|
||||
|
||||
set cache_file /tmp/.yum-cache.$USER
|
||||
set cache_file $XDG_CACHE_HOME/.yum-cache.$USER
|
||||
if test -f $cache_file
|
||||
cat $cache_file
|
||||
set age (math (date +%s) - (stat -c '%Y' $cache_file))
|
||||
@@ -56,7 +62,7 @@ function __fish_print_packages
|
||||
|
||||
# If the cache is less than five minutes old, we do not recalculate it
|
||||
|
||||
set cache_file /tmp/.rpm-cache.$USER
|
||||
set cache_file $XDG_CACHE_HOME/.rpm-cache.$USER
|
||||
if test -f $cache_file
|
||||
cat $cache_file
|
||||
set age (math (date +%s) - (stat -c '%Y' $cache_file))
|
||||
|
||||
@@ -81,11 +81,7 @@ function funced --description 'Edit function definition'
|
||||
return 0
|
||||
end
|
||||
|
||||
set -q TMPDIR; or set -l TMPDIR /tmp
|
||||
set -l tmpname (printf "$TMPDIR/fish_funced_%d_%d.fish" %self (random))
|
||||
while test -f $tmpname
|
||||
set tmpname (printf "$TMPDIR/fish_funced_%d_%d.fish" %self (random))
|
||||
end
|
||||
set tmpname (mktemp -t fish_funced.XXXXXXXXXX)
|
||||
|
||||
if functions -q -- $funcname
|
||||
functions -- $funcname > $tmpname
|
||||
|
||||
@@ -45,21 +45,16 @@ function psub --description "Read from stdin into a file and output the filename
|
||||
return
|
||||
end
|
||||
|
||||
# Find unique file name for writing output to
|
||||
while true
|
||||
set filename /tmp/.psub.(echo %self).(random);
|
||||
if not test -e $filename
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
if test use_fifo = 1
|
||||
# Write output to pipe. This needs to be done in the background so
|
||||
# that the command substitution exits without needing to wait for
|
||||
# all the commands to exit
|
||||
set dir (mktemp -d /tmp/.psub.XXXXXXXXXX); or return
|
||||
set filename $dir/psub.fifo
|
||||
mkfifo $filename
|
||||
cat >$filename &
|
||||
else
|
||||
set filename (mktemp /tmp/.psub.XXXXXXXXXX)
|
||||
cat >$filename
|
||||
end
|
||||
|
||||
|
||||
@@ -556,7 +556,7 @@ function switch_tab(new_tab) {
|
||||
if (new_tab == 'tab_colors') {
|
||||
/* Keep track of whether this is the first element */
|
||||
var first = true
|
||||
run_get_request('/colors/', function(key_and_values){
|
||||
run_get_request('colors/', function(key_and_values){
|
||||
/* Result is name, description, value */
|
||||
var key = key_and_values[0]
|
||||
var description = key_and_values[1]
|
||||
@@ -577,7 +577,7 @@ function switch_tab(new_tab) {
|
||||
sample_prompts.length = 0
|
||||
/* Color the first one blue */
|
||||
var first = true;
|
||||
run_get_request('/sample_prompts/', function(sample_prompt){
|
||||
run_get_request('sample_prompts/', function(sample_prompt){
|
||||
var name = sample_prompt['name']
|
||||
sample_prompts[name] = sample_prompt
|
||||
var color = first ? '66F' : 'AAA'
|
||||
@@ -594,7 +594,7 @@ function switch_tab(new_tab) {
|
||||
} else if (new_tab == 'tab_functions') {
|
||||
/* Keep track of whether this is the first element */
|
||||
var first = true
|
||||
run_get_request('/functions/', function(contents){
|
||||
run_get_request('functions/', function(contents){
|
||||
var elem = create_master_element(contents, false/* description */, 'AAAAAA', '11pt', select_function_master_element)
|
||||
if (first) {
|
||||
/* It's the first element, so select it, so something gets selected */
|
||||
@@ -606,7 +606,7 @@ function switch_tab(new_tab) {
|
||||
$('#master_detail_table').show()
|
||||
wants_data_table = false
|
||||
} else if (new_tab == 'tab_variables') {
|
||||
run_get_request_with_bulk_handler('/variables/', function(json_contents){
|
||||
run_get_request_with_bulk_handler('variables/', function(json_contents){
|
||||
var rows = new Array()
|
||||
for (var i = 0; i < json_contents.length; i++) {
|
||||
var contents = json_contents[i]
|
||||
@@ -622,7 +622,7 @@ function switch_tab(new_tab) {
|
||||
} else if (new_tab == 'tab_history') {
|
||||
// Clear the history map
|
||||
history_element_map.length = 0
|
||||
run_get_request_with_bulk_handler('/history/', function(json_contents){
|
||||
run_get_request_with_bulk_handler('history/', function(json_contents){
|
||||
start = new Date().getTime()
|
||||
var rows = new Array()
|
||||
for (var i = 0; i < json_contents.length; i++) {
|
||||
@@ -757,7 +757,7 @@ function select_color_master_element(elem) {
|
||||
function select_function_master_element(elem) {
|
||||
select_master_element(elem)
|
||||
|
||||
run_post_request('/get_function/', {
|
||||
run_post_request('get_function/', {
|
||||
what: current_master_element_name()
|
||||
}, function(contents){
|
||||
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
||||
@@ -773,7 +773,7 @@ function select_sample_prompt_master_element(elem) {
|
||||
select_master_element(elem)
|
||||
var name = current_master_element_name()
|
||||
sample_prompt = sample_prompts[name]
|
||||
run_post_request('/get_sample_prompt/', {
|
||||
run_post_request('get_sample_prompt/', {
|
||||
what: sample_prompt['function']
|
||||
}, function(keys_and_values){
|
||||
var prompt_func = keys_and_values['function']
|
||||
@@ -788,7 +788,7 @@ function select_sample_prompt_master_element(elem) {
|
||||
function select_current_prompt_master_element(elem) {
|
||||
$('.prompt_save_button').hide()
|
||||
select_master_element(elem)
|
||||
run_get_request_with_bulk_handler('/current_prompt/', function(keys_and_values){
|
||||
run_get_request_with_bulk_handler('current_prompt/', function(keys_and_values){
|
||||
var prompt_func = keys_and_values['function']
|
||||
var prompt_demo = keys_and_values['demo']
|
||||
var prompt_font_size = keys_and_values['font_size']
|
||||
@@ -801,7 +801,7 @@ function select_current_prompt_master_element(elem) {
|
||||
function save_current_prompt() {
|
||||
var name = current_master_element_name()
|
||||
var sample_prompt = sample_prompts[name]
|
||||
run_post_request('/set_prompt/', {
|
||||
run_post_request('set_prompt/', {
|
||||
what: sample_prompt['function']
|
||||
}, function(contents){
|
||||
if (contents == "OK") {
|
||||
@@ -817,7 +817,7 @@ function post_style_to_server() {
|
||||
if (! style)
|
||||
return
|
||||
|
||||
run_post_request('/set_color/', {
|
||||
run_post_request('set_color/', {
|
||||
what: current_master_element_name(),
|
||||
color: style.color,
|
||||
background_color: style.background_color,
|
||||
@@ -1221,7 +1221,7 @@ function escape_HTML(foo) {
|
||||
function tell_fish_to_delete_element(idx) {
|
||||
var row_elem = $('#data_table_row_' + idx)
|
||||
var txt = history_element_map[idx]
|
||||
run_post_request('/delete_history_item/', {
|
||||
run_post_request('delete_history_item/', {
|
||||
what: txt
|
||||
}, function(contents){
|
||||
if (contents == "OK") {
|
||||
|
||||
@@ -17,7 +17,7 @@ else:
|
||||
from urllib.parse import parse_qs
|
||||
import webbrowser
|
||||
import subprocess
|
||||
import re, socket, os, sys, cgi, select, time, glob
|
||||
import re, socket, os, sys, cgi, select, time, glob, random, string, binascii
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
@@ -250,6 +250,16 @@ class FishVar:
|
||||
if self.exported: flags.append('exported')
|
||||
return [self.name, self.value, ', '.join(flags)]
|
||||
|
||||
class FishConfigTCPServer(SocketServer.TCPServer):
|
||||
"""TCPServer that only accepts connections from localhost (IPv4/IPv6)."""
|
||||
WHITELIST = set(['::1', '::ffff:127.0.0.1', '127.0.0.1'])
|
||||
|
||||
address_family = socket.AF_INET6
|
||||
|
||||
def verify_request(self, request, client_address):
|
||||
return client_address[0] in FishConfigTCPServer.WHITELIST
|
||||
|
||||
|
||||
class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
def write_to_wfile(self, txt):
|
||||
@@ -461,6 +471,14 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
# Ignore unreadable files, etc
|
||||
pass
|
||||
return result
|
||||
|
||||
def secure_startswith(self, haystack, needle):
|
||||
if len(haystack) < len(needle):
|
||||
return False
|
||||
bits = 0
|
||||
for x,y in zip(haystack, needle):
|
||||
bits |= ord(x) ^ ord(y)
|
||||
return bits == 0
|
||||
|
||||
def font_size_for_ansi_prompt(self, prompt_demo_ansi):
|
||||
width = ansi_prompt_line_width(prompt_demo_ansi)
|
||||
@@ -475,9 +493,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
else: font_size = '18pt'
|
||||
return font_size
|
||||
|
||||
|
||||
def do_GET(self):
|
||||
p = self.path
|
||||
|
||||
authpath = '/' + authkey
|
||||
if self.secure_startswith(p, authpath):
|
||||
p = p[len(authpath):]
|
||||
else:
|
||||
return self.send_error(403)
|
||||
self.path = p
|
||||
|
||||
if p == '/colors/':
|
||||
output = self.do_get_colors()
|
||||
elif p == '/functions/':
|
||||
@@ -509,6 +534,14 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
def do_POST(self):
|
||||
p = self.path
|
||||
|
||||
authpath = '/' + authkey
|
||||
if self.secure_startswith(p, authpath):
|
||||
p = p[len(authpath):]
|
||||
else:
|
||||
return self.send_error(403)
|
||||
self.path = p
|
||||
|
||||
if IS_PY2:
|
||||
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
|
||||
else: # Python 3
|
||||
@@ -572,7 +605,19 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
def log_request(self, code='-', size='-'):
|
||||
""" Disable request logging """
|
||||
pass
|
||||
|
||||
|
||||
redirect_template_html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL='%s'" />
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="%s">Start the Fish Web config</a></p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# find fish
|
||||
fish_bin_dir = os.environ.get('__fish_bin_dir')
|
||||
fish_bin_path = None
|
||||
@@ -608,12 +653,15 @@ initial_wd = os.getcwd()
|
||||
where = os.path.dirname(sys.argv[0])
|
||||
os.chdir(where)
|
||||
|
||||
# Generate a 16-byte random key as a hexadecimal string
|
||||
authkey = binascii.b2a_hex(os.urandom(16)).decode('ascii')
|
||||
|
||||
# Try to find a suitable port
|
||||
PORT = 8000
|
||||
while PORT <= 9000:
|
||||
try:
|
||||
Handler = FishConfigHTTPRequestHandler
|
||||
httpd = SocketServer.TCPServer(("", PORT), Handler)
|
||||
httpd = FishConfigTCPServer(("::", PORT), Handler)
|
||||
# Success
|
||||
break
|
||||
except socket.error:
|
||||
@@ -637,9 +685,36 @@ if len(sys.argv) > 1:
|
||||
initial_tab = '#' + tab
|
||||
break
|
||||
|
||||
url = 'http://localhost:%d/%s' % (PORT, initial_tab)
|
||||
print("Web config started at '%s'. Hit enter to stop." % url)
|
||||
webbrowser.open(url)
|
||||
url = 'http://localhost:%d/%s/%s' % (PORT, authkey, initial_tab)
|
||||
|
||||
# Create temporary file to hold redirect to real server
|
||||
# This prevents exposing the URL containing the authentication key on the command line
|
||||
# (see CVE-2014-2914 or https://github.com/fish-shell/fish-shell/issues/1438)
|
||||
if 'XDG_CACHE_HOME' in os.environ:
|
||||
dirname = os.path.expanduser(os.path.expandvars('$XDG_CACHE_HOME/fish/'))
|
||||
else:
|
||||
dirname = os.path.expanduser('~/.cache/fish/')
|
||||
|
||||
os.umask(0o0077)
|
||||
try:
|
||||
os.makedirs(dirname, 0o0700)
|
||||
except OSError as e:
|
||||
if e.errno == 17:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
|
||||
randtoken = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
|
||||
filename = dirname + 'web_config-%s.html' % randtoken
|
||||
|
||||
f = open(filename, 'w')
|
||||
f.write(redirect_template_html % (url, url))
|
||||
f.close()
|
||||
|
||||
# Open temporary file as URL
|
||||
fileurl = 'file://' + filename
|
||||
print("Web config started at '%s'. Hit enter to stop." % fileurl)
|
||||
webbrowser.open(fileurl)
|
||||
|
||||
# Select on stdin and httpd
|
||||
stdin_no = sys.stdin.fileno()
|
||||
@@ -656,3 +731,5 @@ try:
|
||||
except KeyboardInterrupt:
|
||||
print("\nShutting down.")
|
||||
|
||||
# Clean up temporary file
|
||||
os.remove(filename)
|
||||
|
||||
Reference in New Issue
Block a user