Linux: cleanup system-call bindings

This patch simplifies the system call bindings. The common syscall
bindings in 'src/platform/' have been reduced to the syscalls needed by
non-core programs. The additional syscalls that are needed solely by
core have been moved to 'src/core/include/core_linux_syscalls.h'.
Furthermore, the resource path is not used outside of core anymore.
Hence, we could get rid of the rpath library. The resource-path code has
been moved to 'src/core/include/resource_path.h'. The IPC-related parts
of 'src/platform' have been moved to the IPC library. So there is now a
clean separation between low-level syscall bindings (in 'src/platform')
and higher-level code.

The code for the socket-descriptor registry is now located in the
'src/base/ipc/socket_descriptor_registry.h' header. The interface is
separated from 'ipc.cc' because core needs to access the registry from
outside the ipc library.
This commit is contained in:
Norman Feske
2012-08-10 16:35:57 +02:00
parent 371b8fd12d
commit de69ee2e66
19 changed files with 738 additions and 714 deletions

View File

@ -34,14 +34,21 @@
#include <base/env.h>
#include <linux_cpu_session/linux_cpu_session.h>
/* local includes */
#include <socket_descriptor_registry.h>
/* Linux includes */
#include <linux_socket.h>
#include <linux_syscalls.h>
#include <sys/un.h>
#include <sys/socket.h>
using namespace Genode;
/******************************
** File-descriptor registry **
******************************/
Genode::Ep_socket_descriptor_registry *Genode::ep_sd_registry()
{
@ -50,6 +57,275 @@ Genode::Ep_socket_descriptor_registry *Genode::ep_sd_registry()
}
/********************************************
** Communication over Unix-domain sockets **
********************************************/
/**
* Utility: Return thread ID to which the given socket is directed to
*
* \return -1 if the socket is pointing to a valid entrypoint
*/
static int lookup_tid_by_client_socket(int sd)
{
sockaddr_un name;
socklen_t name_len = sizeof(name);
int ret = lx_getpeername(sd, (sockaddr *)&name, &name_len);
if (ret < 0)
return -1;
struct Prefix_len
{
size_t const len;
static int _init_len(char const *s)
{
char const * const pattern = "/ep-";
static size_t const pattern_len = Genode::strlen(pattern);
for (size_t i = 0; Genode::strlen(s + i) >= pattern_len; i++)
if (Genode::strcmp(s + i, pattern, pattern_len) == 0)
return i + pattern_len;
struct Unexpected_rpath_prefix { };
throw Unexpected_rpath_prefix();
}
Prefix_len(char const *s) : len(_init_len(s)) { }
};
/*
* The name of the Unix-domain socket has the form <rpath>-<uid>/ep-<tid>.
* We are only interested in the <tid> part. Hence, we determine the length
* of the <rpath>-<uid>/ep- portion only once and keep it in a static
* variable.
*/
static Prefix_len prefix_len(name.sun_path);
unsigned tid = 0;
if (Genode::ascii_to(name.sun_path + prefix_len.len, &tid) == 0) {
PRAW("Error: could not parse tid number");
return -1;
}
return tid;
}
namespace {
/**
* Message object encapsulating data for sendmsg/recvmsg
*/
struct Message
{
public:
enum { MAX_SDS_PER_MSG = Genode::Msgbuf_base::MAX_CAPS_PER_MSG };
private:
msghdr _msg;
sockaddr_un _addr;
iovec _iovec;
char _cmsg_buf[CMSG_SPACE(MAX_SDS_PER_MSG*sizeof(int))];
unsigned _num_sds;
public:
Message(void *buffer, size_t buffer_len) : _num_sds(0)
{
Genode::memset(&_msg, 0, sizeof(_msg));
/* initialize control message */
struct cmsghdr *cmsg;
_msg.msg_control = _cmsg_buf;
_msg.msg_controllen = sizeof(_cmsg_buf); /* buffer space available */
_msg.msg_flags |= MSG_CMSG_CLOEXEC;
cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(0);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
/* initialize iovec */
_msg.msg_iov = &_iovec;
_msg.msg_iovlen = 1;
_iovec.iov_base = buffer;
_iovec.iov_len = buffer_len;
}
msghdr * msg() { return &_msg; }
void marshal_socket(int sd)
{
*((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + _num_sds) = sd;
_num_sds++;
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(_num_sds*sizeof(int));
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
void accept_sockets(int num_sds)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
cmsg->cmsg_len = CMSG_LEN(num_sds*sizeof(int));
_msg.msg_controllen = cmsg->cmsg_len; /* actual cmsg length */
}
int socket_at_index(int index) const
{
return *((int *)CMSG_DATA((cmsghdr *)_cmsg_buf) + index);
}
unsigned num_sockets() const
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&_msg);
if (!cmsg)
return 0;
return (cmsg->cmsg_len - CMSG_ALIGN(sizeof(cmsghdr)))/sizeof(int);
}
};
} /* unnamed namespace */
/**
* Utility: Extract socket desriptors from SCM message into 'Genode::Msgbuf'
*/
static void extract_sds_from_message(unsigned start_index, Message const &msg,
Genode::Msgbuf_base &buf)
{
buf.reset_caps();
/* start at offset 1 to skip the reply channel */
for (unsigned i = start_index; i < msg.num_sockets(); i++) {
int const sd = msg.socket_at_index(i);
int const id = lookup_tid_by_client_socket(sd);
int const existing_sd = Genode::ep_sd_registry()->lookup_fd_by_global_id(id);
if (existing_sd >= 0) {
lx_close(sd);
buf.append_cap(existing_sd);
} else {
Genode::ep_sd_registry()->associate(sd, id);
buf.append_cap(sd);
}
}
}
/**
* Send request to server and wait for reply
*/
static inline void lx_call(int dst_sd,
Genode::Msgbuf_base &send_msgbuf, size_t send_msg_len,
Genode::Msgbuf_base &recv_msgbuf)
{
int ret;
Message send_msg(send_msgbuf.buf, send_msg_len);
/* create reply channel */
enum { LOCAL_SOCKET = 0, REMOTE_SOCKET = 1 };
int reply_channel[2];
ret = lx_socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, reply_channel);
if (ret < 0) {
PRAW("lx_socketpair failed with %d", ret);
throw Genode::Ipc_error();
}
/* assemble message */
/* marshal reply capability */
send_msg.marshal_socket(reply_channel[REMOTE_SOCKET]);
/* marshal capabilities contained in 'send_msgbuf' */
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
send_msg.marshal_socket(send_msgbuf.cap(i));
ret = lx_sendmsg(dst_sd, send_msg.msg(), 0);
if (ret < 0) {
PRAW("[%d] lx_sendmsg to sd %d failed with %d in lx_call()",
lx_getpid(), dst_sd, ret);
throw Genode::Ipc_error();
}
/* receive reply */
Message recv_msg(recv_msgbuf.buf, recv_msgbuf.size());
recv_msg.accept_sockets(Message::MAX_SDS_PER_MSG);
ret = lx_recvmsg(reply_channel[LOCAL_SOCKET], recv_msg.msg(), 0);
if (ret < 0) {
PRAW("[%d] lx_recvmsg failed with %d in lx_call()", lx_getpid(), ret);
throw Genode::Ipc_error();
}
extract_sds_from_message(0, recv_msg, recv_msgbuf);
/* destroy reply channel */
lx_close(reply_channel[LOCAL_SOCKET]);
lx_close(reply_channel[REMOTE_SOCKET]);
}
/**
* for request from client
*
* \return socket descriptor of reply capability
*/
static inline int lx_wait(Genode::Native_connection_state &cs,
Genode::Msgbuf_base &recv_msgbuf)
{
Message msg(recv_msgbuf.buf, recv_msgbuf.size());
msg.accept_sockets(Message::MAX_SDS_PER_MSG);
int ret = lx_recvmsg(cs.server_sd, msg.msg(), 0);
if (ret < 0) {
PRAW("lx_recvmsg failed with %d in lx_wait(), sd=%d", ret, cs.server_sd);
throw Genode::Ipc_error();
}
int const reply_socket = msg.socket_at_index(0);
extract_sds_from_message(1, msg, recv_msgbuf);
return reply_socket;
}
/**
* Send reply to client
*/
static inline void lx_reply(int reply_socket,
Genode::Msgbuf_base &send_msgbuf,
size_t msg_len)
{
Message msg(send_msgbuf.buf, msg_len);
/*
* Marshall capabilities to be transferred to the client
*/
for (unsigned i = 0; i < send_msgbuf.used_caps(); i++)
msg.marshal_socket(send_msgbuf.cap(i));
int ret = lx_sendmsg(reply_socket, msg.msg(), 0);
if (ret < 0)
PRAW("lx_sendmsg failed with %d in lx_reply()", ret);
lx_close(reply_socket);
}
/*****************
** Ipc_ostream **
*****************/

View File

@ -0,0 +1,132 @@
/*
* \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 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; }
};
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();
}
bool _is_registered(int global_id) const
{
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].global_id == global_id)
return true;
return false;
}
public:
/**
* Register association of socket descriptor and its corresponding ID
*
* \throw Limit_reached
* \throw Aliased_global_id if global ID is already registered
*/
void associate(int sd, int global_id)
{
Genode::Lock::Guard guard(_lock);
/* ignore invalid capabilities */
if (sd == -1 || global_id == -1)
return;
/*
* Check for potential aliasing
*
* We allow any global ID to be present in the registry only once.
*/
if (_is_registered(global_id))
throw Aliased_global_id();
Entry &entry = _find_free_entry();
entry = Entry(sd, global_id);
}
/**
* 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
{
Genode::Lock::Guard guard(_lock);
for (unsigned i = 0; i < MAX_FDS; i++)
if (_entries[i].global_id == global_id)
return _entries[i].fd;
return -1;
}
};
#endif /* _BASE__IPC__SOCKET_DESCRIPTOR_REGISTRY_H_ */