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

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

209
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.
*/
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;
if (wdir.empty())
return;
std::string dir = wcs2string(wdir);
gethostname(hostname, HOSTNAME_LEN);
std::string name;
name.append(dir);
name.append("/");
name.append(FILE);
name.append(hostname);
bool result = false;
debug(4, L"Open file for %s: '%s'",
save?"saving":"loading",
name.c_str());
path.c_str());
/* 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)
int fd = open(path.c_str(), save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600);
if (fd >= 0)
{
debug(1, L"Could not open load/save file. No previous saves?");
wperror(L"open");
return;
}
debug(4, L"File open on fd %d", c.fd);
/* Success */
result = true;
connection_t c = {};
connection_init(&c, fd);
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(identifier);
return name;
}
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;
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)
{
/* 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);
}
}
}
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