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:
ridiculousfish 2013-01-08 02:39:22 -08:00
parent f8e01628b2
commit 552d8f394e
8 changed files with 206 additions and 58 deletions

View file

@ -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);

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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
View file

@ -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 */);
}
/**

View file

@ -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

View file

@ -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;