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

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