mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 12:23:09 +00:00
Make fishd base its variable files on the MAC address instead of hostname
Fixes https://github.com/fish-shell/fish-shell/issues/183
This commit is contained in:
parent
f8e01628b2
commit
552d8f394e
8 changed files with 206 additions and 58 deletions
|
@ -580,7 +580,7 @@ expression *test_parser::parse_args(const wcstring_list_t &args, wcstring &err)
|
|||
append_format(err, L"test: unexpected argument at index %lu: '%ls'\n", (unsigned long)result->range.end, args.at(result->range.end).c_str());
|
||||
}
|
||||
errored = true;
|
||||
|
||||
|
||||
delete result;
|
||||
result = NULL;
|
||||
}
|
||||
|
@ -802,14 +802,14 @@ int builtin_test(parser_t &parser, wchar_t **argv)
|
|||
/* The first argument should be the name of the command ('test') */
|
||||
if (! argv[0])
|
||||
return BUILTIN_TEST_FAIL;
|
||||
|
||||
|
||||
/* Whether we are invoked with bracket '[' or not */
|
||||
const bool is_bracket = ! wcscmp(argv[0], L"[");
|
||||
|
||||
size_t argc = 0;
|
||||
while (argv[argc + 1])
|
||||
argc++;
|
||||
|
||||
|
||||
/* If we're bracket, the last argument ought to be ]; we ignore it. Note that argc is the number of arguments after the command name; thus argv[argc] is the last argument. */
|
||||
if (is_bracket)
|
||||
{
|
||||
|
@ -825,7 +825,7 @@ int builtin_test(parser_t &parser, wchar_t **argv)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Collect the arguments into a list */
|
||||
const wcstring_list_t args(argv + 1, argv + 1 + argc);
|
||||
|
||||
|
|
|
@ -163,9 +163,8 @@ int fgetws2(wcstring *s, FILE *f)
|
|||
}
|
||||
|
||||
/**
|
||||
Converts the narrow character string \c in into it's wide
|
||||
equivalent, stored in \c out. \c out must have enough space to fit
|
||||
the entire string.
|
||||
Converts the narrow character string \c in into its wide
|
||||
equivalent, and return it
|
||||
|
||||
The string may contain embedded nulls.
|
||||
|
||||
|
|
|
@ -720,6 +720,7 @@ AC_CHECK_FUNCS( wcsdup wcsndup wcslen wcscasecmp wcsncasecmp fwprintf )
|
|||
AC_CHECK_FUNCS( futimes wcwidth wcswidth wcstok fputwc fgetwc )
|
||||
AC_CHECK_FUNCS( wcstol wcslcat wcslcpy lrand48_r killpg gettext )
|
||||
AC_CHECK_FUNCS( dcgettext backtrace backtrace_symbols sysconf )
|
||||
AC_CHECK_FUNCS( getifaddrs )
|
||||
|
||||
#
|
||||
# The Makefile also needs to know if we have gettext, so it knows if
|
||||
|
|
|
@ -276,7 +276,7 @@ start_conversion:
|
|||
/* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...)
|
||||
OS X and Linux this one: size_t iconv (iconv_t, char **...)
|
||||
AFAIK there's no single type that can be passed as both char ** and const char **.
|
||||
So we cast the function pointer instead (!)
|
||||
Hence this hack.
|
||||
*/
|
||||
nconv = hack_iconv(cd, &in, &in_len, &nout, &out_len);
|
||||
|
||||
|
|
|
@ -839,7 +839,7 @@ static bool run_test_test(int expected, const wcstring &str)
|
|||
copy(istream_iterator<wcstring, wchar_t, std::char_traits<wchar_t> >(iss),
|
||||
istream_iterator<wstring, wchar_t, std::char_traits<wchar_t> >(),
|
||||
back_inserter<vector<wcstring> >(lst));
|
||||
|
||||
|
||||
bool bracket = run_one_test_test(expected, lst, true);
|
||||
bool nonbracket = run_one_test_test(expected, lst, false);
|
||||
assert(bracket == nonbracket);
|
||||
|
@ -850,13 +850,13 @@ static void test_test_brackets()
|
|||
{
|
||||
// Ensure [ knows it needs a ]
|
||||
parser_t parser(PARSER_TYPE_GENERAL, true);
|
||||
|
||||
|
||||
const wchar_t *argv1[] = {L"[", L"foo", NULL};
|
||||
assert(builtin_test(parser, (wchar_t **)argv1) != 0);
|
||||
|
||||
|
||||
const wchar_t *argv2[] = {L"[", L"foo", L"]", NULL};
|
||||
assert(builtin_test(parser, (wchar_t **)argv2) == 0);
|
||||
|
||||
|
||||
const wchar_t *argv3[] = {L"[", L"foo", L"]", L"bar", NULL};
|
||||
assert(builtin_test(parser, (wchar_t **)argv3) != 0);
|
||||
|
||||
|
@ -918,11 +918,11 @@ static void test_test()
|
|||
/* We didn't properly handle multiple "just strings" either */
|
||||
assert(run_test_test(0, L"foo"));
|
||||
assert(run_test_test(0, L"foo -a bar"));
|
||||
|
||||
|
||||
/* These should be errors */
|
||||
assert(run_test_test(1, L"foo bar"));
|
||||
assert(run_test_test(1, L"foo bar baz"));
|
||||
|
||||
|
||||
/* This crashed */
|
||||
assert(run_test_test(1, L"1 = 1 -a = 1"));
|
||||
}
|
||||
|
@ -1029,13 +1029,13 @@ static void test_autosuggestion_combining()
|
|||
{
|
||||
say(L"Testing autosuggestion combining");
|
||||
assert(combine_command_and_autosuggestion(L"alpha", L"alphabeta") == L"alphabeta");
|
||||
|
||||
|
||||
// when the last token contains no capital letters, we use the case of the autosuggestion
|
||||
assert(combine_command_and_autosuggestion(L"alpha", L"ALPHABETA") == L"ALPHABETA");
|
||||
|
||||
|
||||
// when the last token contains capital letters, we use its case
|
||||
assert(combine_command_and_autosuggestion(L"alPha", L"alphabeTa") == L"alPhabeTa");
|
||||
|
||||
|
||||
// if autosuggestion is not longer than input, use the input's case
|
||||
assert(combine_command_and_autosuggestion(L"alpha", L"ALPHAA") == L"ALPHAA");
|
||||
assert(combine_command_and_autosuggestion(L"alpha", L"ALPHA") == L"alpha");
|
||||
|
|
225
fishd.cpp
225
fishd.cpp
|
@ -52,6 +52,7 @@ time the original barrier request was sent have been received.
|
|||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/un.h>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -98,6 +99,9 @@ time the original barrier request was sent have been received.
|
|||
#define MSG_DONTWAIT 0
|
||||
#endif
|
||||
|
||||
/* Length of a MAC address */
|
||||
#define MAC_ADDRESS_MAX_LEN 6
|
||||
|
||||
/**
|
||||
Small greeting to show that fishd is running
|
||||
*/
|
||||
|
@ -109,7 +113,7 @@ time the original barrier request was sent have been received.
|
|||
#define SAVE_MSG "# This file is automatically generated by the fishd universal variable daemon.\n# Do NOT edit it directly, your changes will be overwritten.\n"
|
||||
|
||||
/**
|
||||
The name of the save file. The hostname is appended to this.
|
||||
The name of the save file. The machine identifier is appended to this.
|
||||
*/
|
||||
#define FILE "fishd."
|
||||
|
||||
|
@ -223,9 +227,14 @@ static void sprint_rand_digits(char *str, int maxlen)
|
|||
and ignores errors returned by gettimeofday.
|
||||
Cast to unsigned so that wrapping occurs on overflow as per ANSI C.
|
||||
*/
|
||||
(void)gettimeofday(&tv, NULL);
|
||||
unsigned long long seed = tv.tv_sec + tv.tv_usec * 1000000ULL;
|
||||
srand((unsigned int)seed);
|
||||
static bool seeded = false;
|
||||
if (! seeded)
|
||||
{
|
||||
(void)gettimeofday(&tv, NULL);
|
||||
unsigned long long seed = tv.tv_sec + tv.tv_usec * 1000000ULL;
|
||||
srand((unsigned int)seed);
|
||||
seeded = true;
|
||||
}
|
||||
max = (int)(1 + (maxlen - 1) * (rand() / (RAND_MAX + 1.0)));
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
|
@ -235,6 +244,7 @@ static void sprint_rand_digits(char *str, int maxlen)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Generate a filename unique in an NFS namespace by creating a copy of str and
|
||||
appending .{hostname}.{pid} to it. If gethostname() fails then a pseudo-
|
||||
|
@ -264,6 +274,118 @@ static std::string gen_unique_nfs_filename(const char *filename)
|
|||
}
|
||||
|
||||
|
||||
/* Thanks to Jan Brittenson
|
||||
http://lists.apple.com/archives/xcode-users/2009/May/msg00062.html
|
||||
*/
|
||||
#ifdef SIOCGIFHWADDR
|
||||
|
||||
/* Linux */
|
||||
#include <net/if.h>
|
||||
static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const char *interface = "eth0")
|
||||
{
|
||||
bool result = false;
|
||||
const int dummy = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (dummy >= 0)
|
||||
{
|
||||
struct ifreq r;
|
||||
strncpy((char *)r.ifr_name, interface, sizeof r.ifr_name - 1);
|
||||
r.ifr_name[sizeof r.ifr_name - 1] = 0;
|
||||
if (ioctl(dummy, SIOCGIFHWADDR, &r) >= 0)
|
||||
{
|
||||
memcpy(macaddr, r.ifr_hwaddr.sa_data, MAC_ADDRESS_MAX_LEN);
|
||||
result = true;
|
||||
}
|
||||
close(dummy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_GETIFADDRS)
|
||||
|
||||
/* OS X and BSD */
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_dl.h>
|
||||
static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN], const char *interface = "en0")
|
||||
{
|
||||
// BSD, Mac OS X
|
||||
struct ifaddrs *ifap;
|
||||
bool ok = false;
|
||||
|
||||
if (getifaddrs(&ifap) == 0)
|
||||
{
|
||||
for (const ifaddrs *p = ifap; p; p = p->ifa_next)
|
||||
{
|
||||
if (p->ifa_addr->sa_family == AF_LINK)
|
||||
{
|
||||
if (p->ifa_name && p->ifa_name[0] &&
|
||||
! strcmp((const char*)p->ifa_name, interface))
|
||||
{
|
||||
|
||||
const sockaddr_dl& sdl = *(sockaddr_dl*)p->ifa_addr;
|
||||
|
||||
size_t alen = sdl.sdl_alen;
|
||||
if (alen > MAC_ADDRESS_MAX_LEN) alen = MAC_ADDRESS_MAX_LEN;
|
||||
memcpy(macaddr, sdl.sdl_data + sdl.sdl_nlen, alen);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Unsupported */
|
||||
static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Function to get an identifier based on the hostname */
|
||||
static bool get_hostname_identifier(std::string *result)
|
||||
{
|
||||
bool success = false;
|
||||
char hostname[HOSTNAME_LEN + 1] = {};
|
||||
if (gethostname(hostname, HOSTNAME_LEN) == 0)
|
||||
{
|
||||
result->assign(hostname);
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Get a sort of unique machine identifier. Prefer the MAC address; if that fails, fall back to the hostname; if that fails, pick something. */
|
||||
static std::string get_machine_identifier(void)
|
||||
{
|
||||
std::string result;
|
||||
unsigned char mac_addr[MAC_ADDRESS_MAX_LEN] = {};
|
||||
if (get_mac_address(mac_addr))
|
||||
{
|
||||
result.reserve(2 * MAC_ADDRESS_MAX_LEN);
|
||||
for (size_t i=0; i < MAC_ADDRESS_MAX_LEN; i++)
|
||||
{
|
||||
char buff[3];
|
||||
snprintf(buff, sizeof buff, "%02x", mac_addr[i]);
|
||||
result.append(buff);
|
||||
}
|
||||
}
|
||||
else if (get_hostname_identifier(&result))
|
||||
{
|
||||
/* Hooray */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fallback */
|
||||
result.assign("nohost");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
The number of milliseconds to wait between polls when attempting to acquire
|
||||
a lockfile
|
||||
|
@ -661,53 +783,76 @@ static wcstring fishd_get_config()
|
|||
/**
|
||||
Load or save all variables
|
||||
*/
|
||||
static void load_or_save(int save)
|
||||
static bool load_or_save_variables_at_path(bool save, const std::string &path)
|
||||
{
|
||||
const wcstring wdir = fishd_get_config();
|
||||
char hostname[HOSTNAME_LEN];
|
||||
connection_t c;
|
||||
int fd;
|
||||
bool result = false;
|
||||
|
||||
if (wdir.empty())
|
||||
return;
|
||||
debug(4, L"Open file for %s: '%s'",
|
||||
save?"saving":"loading",
|
||||
path.c_str());
|
||||
|
||||
std::string dir = wcs2string(wdir);
|
||||
/* OK to not use CLO_EXEC here because fishd is single threaded */
|
||||
int fd = open(path.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600);
|
||||
if (fd >= 0)
|
||||
{
|
||||
/* Success */
|
||||
result = true;
|
||||
connection_t c = {};
|
||||
connection_init(&c, fd);
|
||||
|
||||
gethostname(hostname, HOSTNAME_LEN);
|
||||
if (save)
|
||||
{
|
||||
/* Save to the file */
|
||||
write_loop(c.fd, SAVE_MSG, strlen(SAVE_MSG));
|
||||
enqueue_all(&c);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read from the file */
|
||||
read_message(&c);
|
||||
}
|
||||
|
||||
connection_destroy(&c);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static std::string get_variables_file_path(const std::string &dir, const std::string &identifier)
|
||||
{
|
||||
std::string name;
|
||||
name.append(dir);
|
||||
name.append("/");
|
||||
name.append(FILE);
|
||||
name.append(hostname);
|
||||
name.append(identifier);
|
||||
return name;
|
||||
}
|
||||
|
||||
debug(4, L"Open file for %s: '%s'",
|
||||
save?"saving":"loading",
|
||||
name.c_str());
|
||||
static bool load_or_save_variables(bool save)
|
||||
{
|
||||
const wcstring wdir = fishd_get_config();
|
||||
const std::string dir = wcs2string(wdir);
|
||||
if (dir.empty())
|
||||
return false;
|
||||
|
||||
/* OK to not use CLO_EXEC here because fishd is single threaded */
|
||||
fd = open(name.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600);
|
||||
|
||||
if (fd == -1)
|
||||
const std::string machine_id = get_machine_identifier();
|
||||
const std::string machine_id_path = get_variables_file_path(dir, machine_id);
|
||||
bool success = load_or_save_variables_at_path(save, machine_id_path);
|
||||
if (! success && ! save && errno == ENOENT)
|
||||
{
|
||||
debug(1, L"Could not open load/save file. No previous saves?");
|
||||
wperror(L"open");
|
||||
return;
|
||||
/* We failed to load, because the file was not found. Older fish used the hostname only. Try *moving* the filename based on the hostname into place; if that succeeds try again. Silently "upgraded." */
|
||||
std::string hostname_id;
|
||||
if (get_hostname_identifier(&hostname_id) && hostname_id != machine_id)
|
||||
{
|
||||
std::string hostname_path = get_variables_file_path(dir, hostname_id);
|
||||
if (0 == rename(hostname_path.c_str(), machine_id_path.c_str()))
|
||||
{
|
||||
/* We renamed - try again */
|
||||
success = load_or_save_variables_at_path(save, machine_id_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
debug(4, L"File open on fd %d", c.fd);
|
||||
|
||||
connection_init(&c, fd);
|
||||
|
||||
if (save)
|
||||
{
|
||||
|
||||
write_loop(c.fd, SAVE_MSG, strlen(SAVE_MSG));
|
||||
enqueue_all(&c);
|
||||
}
|
||||
else
|
||||
read_message(&c);
|
||||
|
||||
connection_destroy(&c);
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -715,7 +860,7 @@ static void load_or_save(int save)
|
|||
*/
|
||||
static void load()
|
||||
{
|
||||
load_or_save(0);
|
||||
load_or_save_variables(false /* load, not save */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -723,7 +868,7 @@ static void load()
|
|||
*/
|
||||
static void save()
|
||||
{
|
||||
load_or_save(1);
|
||||
load_or_save_variables(true /* save, not load */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
/* Define to 1 if you have the `fwprintf' function. */
|
||||
#define HAVE_FWPRINTF 1
|
||||
|
||||
/* Define to 1 if you have the `getifaddrs' function. */
|
||||
#define HAVE_GETIFADDRS 1
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#define HAVE_GETOPT_H 1
|
||||
|
||||
|
|
|
@ -509,7 +509,7 @@ static void reader_repaint()
|
|||
|
||||
// Combine the command and autosuggestion into one string
|
||||
wcstring full_line = combine_command_and_autosuggestion(data->command_line, data->autosuggestion);
|
||||
|
||||
|
||||
size_t len = full_line.size();
|
||||
if (len < 1)
|
||||
len = 1;
|
||||
|
|
Loading…
Reference in a new issue