genode/repos/base-linux/src/include/base/internal/socket_descriptor_registry.h

151 lines
3.3 KiB
C++

/*
* \brief Linux-specific socket-descriptor registry
* \author Norman Feske
* \date 2012-07-26
*
* We use the names of Unix-domain sockets as keys to uniquely identify
* entrypoints. When receiving a socket descriptor as IPC payload, we first
* lookup the corresponding entrypoint ID. If we already possess a socket
* descriptor pointing to the same entrypoint, we close the received one and
* use the already known descriptor instead.
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_
#define _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_
#include <base/lock.h>
namespace Genode
{
template <unsigned MAX_FDS>
class Socket_descriptor_registry;
typedef Socket_descriptor_registry<100> Ep_socket_descriptor_registry;
/**
* Return singleton instance of registry for tracking entrypoint sockets
*/
Ep_socket_descriptor_registry *ep_sd_registry();
}
template <unsigned MAX_FDS>
class Genode::Socket_descriptor_registry
{
public:
class Limit_reached { };
class Aliased_global_id { };
private:
struct Entry
{
int fd;
int global_id;
/**
* Default constructor creates empty entry
*/
Entry() : fd(-1), global_id(-1) { }
Entry(int fd, int global_id) : fd(fd), global_id(global_id) { }
bool is_free() const { return fd == -1; }
void mark_as_free() { fd = -1; }
};
Entry _entries[MAX_FDS];
Genode::Lock mutable _lock;
Entry &_find_free_entry()
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].is_free())
return _entries[i];
throw Limit_reached();
}
Entry &_find_entry_by_fd(int fd)
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].fd == fd)
return _entries[i];
throw Limit_reached();
}
/**
* Lookup file descriptor that belongs to specified global ID
*
* \return file descriptor or -1 if lookup failed
*/
int _lookup_fd_by_global_id(int global_id) const
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].global_id == global_id)
return _entries[i].fd;
return -1;
}
public:
void disassociate(int sd)
{
Genode::Lock::Guard guard(_lock);
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].fd == sd) {
_entries[i].mark_as_free();
return;
}
}
/**
* Try to associate socket descriptor with corresponding ID
*
* \return socket descriptor associated with the ID
* \throw Limit_reached
*
* If the ID was already associated, the return value is the originally
* registered socket descriptor. In this case, the caller should drop
* the new socket descriptor and use the one returned by this function.
*/
int try_associate(int sd, int global_id)
{
/* ignore invalid capabilities */
if (sd == -1)
return sd;
/* ignore invalid capabilities */
if (sd == -1 || global_id == -1)
return sd;
Genode::Lock::Guard guard(_lock);
int const existing_sd = _lookup_fd_by_global_id(global_id);
if (existing_sd < 0) {
Entry &entry = _find_free_entry();
entry = Entry(sd, global_id);
return sd;
} else {
return existing_sd;
}
}
};
#endif /* _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_ */