[common] added missing file to the repository

This commit is contained in:
Geoffrey McRae 2019-11-04 21:10:21 +11:00
parent 01f5238a9d
commit 1dfa0ed218
2 changed files with 366 additions and 1 deletions

View File

@ -1 +1 @@
B1-25-gc382a5acb1+1 B1-26-g01f5238a9d+1

365
porthole/src/linux/client.c Normal file
View File

@ -0,0 +1,365 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "porthole/client.h"
#include "common/objectlist.h"
#include "common/debug.h"
#include "../phmsg.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
typedef struct
{
uint32_t id;
int fd;
int refcount;
uint8_t * map;
size_t size;
}
SharedFD;
typedef struct
{
SharedFD * sfd;
uint64_t addr;
uint32_t size;
}
Segment;
typedef struct
{
uint32_t id;
ObjectList segments;
size_t size;
}
Mapping;
struct PortholeClient
{
int socket;
PortholeMapEvent map_cb;
PortholeUnmapEvent unmap_cb;
PortholeDisconEvent discon_cb;
ObjectList fds;
ObjectList intmaps;
Mapping * current;
ObjectList maps;
bool running;
pthread_t thread;
bool thread_valid;
};
// forwards
static void * porthole_socket_thread(void * opaque);
static void porthole_free_map(Mapping * map);
static void porthole_sharedfd_free_handler(void * opaque);
static void porthole_intmaps_free_handler(void * opaque);
static void porthole_segment_free_handler(void * opaque);
static Mapping * porthole_intmap_new();
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd);
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size);
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type);
// implementation
bool porthole_client_open(
PortholeClient * handle,
const char * socket_path,
PortholeMapEvent map_cb,
PortholeUnmapEvent unmap_cb,
PortholeDisconEvent discon_cb)
{
assert(handle);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
DEBUG_ERROR("Failed to create a unix socket");
return false;
}
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (const struct sockaddr*)&addr, sizeof(addr)) == -1)
{
DEBUG_ERROR("Failed to connect to the socket");
close(fd);
return false;
}
*handle = (PortholeClient)calloc(sizeof(PortholeClient), 1);
(*handle)->socket = fd;
(*handle)->map_cb = map_cb;
(*handle)->unmap_cb = unmap_cb;
(*handle)->discon_cb = discon_cb;
(*handle)->fds = objectlist_new(porthole_sharedfd_free_handler);
(*handle)->intmaps = objectlist_new(porthole_intmaps_free_handler);
(*handle)->maps = objectlist_new(objectlist_free_item);
(*handle)->running = true;
if (pthread_create(&(*handle)->thread, NULL, porthole_socket_thread, *handle) != 0)
{
DEBUG_ERROR("Failed to create porthole socket thread");
porthole_client_close(handle);
return false;
}
(*handle)->thread_valid = true;
return true;
}
void porthole_client_close(PortholeClient * handle)
{
assert(handle && *handle);
if ((*handle)->thread_valid)
{
(*handle)->running = false;
pthread_join((*handle)->thread, NULL);
}
close((*handle)->socket);
if ((*handle)->current)
porthole_free_map((*handle)->current);
objectlist_free(&(*handle)->maps );
objectlist_free(&(*handle)->intmaps);
objectlist_free(&(*handle)->fds );
free(*handle);
*handle = NULL;
}
static void * porthole_socket_thread(void * opaque)
{
PortholeClient handle = (PortholeClient)opaque;
DEBUG_INFO("Porthole socket thread started");
while(handle->running)
{
PHMsg msg;
struct iovec io =
{
.iov_base = &msg,
.iov_len = sizeof(msg)
};
char buffer[256] = {0};
struct msghdr msghdr =
{
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = &buffer,
.msg_controllen = sizeof(buffer)
};
if (recvmsg(handle->socket, &msghdr, 0) < 0)
{
DEBUG_ERROR("Failed to recieve the message");
if (handle->discon_cb)
handle->discon_cb();
break;
}
switch(msg.msg)
{
case PH_MSG_MAP:
if (handle->current)
{
DEBUG_WARN("Started a new map before finishing the last one");
porthole_free_map(handle->current);
}
handle->current = porthole_intmap_new();
break;
case PH_MSG_FD:
{
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msghdr);
int * fds = (int *)CMSG_DATA(cmsg);
porthole_sharedfd_new(handle, msg.u.fd.id, fds[0]);
break;
}
case PH_MSG_SEGMENT:
{
if (!handle->current)
DEBUG_FATAL("Segment sent before map, this is a bug in the guest porthole device or driver");
porthole_segment_new(
handle->fds,
handle->current,
msg.u.segment.fd_id,
msg.u.segment.addr,
msg.u.segment.size
);
break;
}
case PH_MSG_FINISH:
if (!handle->current)
DEBUG_FATAL("Finished map before starting one");
handle->current->id = msg.u.finish.id;
objectlist_push(handle->intmaps, handle->current);
porthole_do_map(handle, handle->current, msg.u.finish.type);
handle->current = NULL;
break;
case PH_MSG_UNMAP:
/* TODO: remove the object from intmaps and maps */
handle->unmap_cb(msg.u.unmap.id);
break;
}
}
handle->running = false;
DEBUG_INFO("Porthole socket thread stopped");
return NULL;
}
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd)
{
struct stat st;
fstat(fd, &st);
SharedFD * sfd = (SharedFD *)calloc(sizeof(SharedFD), 1);
sfd->id = id;
sfd->fd = fd;
sfd->size = st.st_size;
DEBUG_INFO("Guest FD ID %u (FD:%d, Size:%lu)", sfd->id, sfd->fd, sfd->size);
objectlist_push(handle->fds, sfd);
}
static void porthole_sharedfd_inc_ref(SharedFD * sfd)
{
if (sfd->refcount == 0)
{
sfd->map = mmap(NULL, sfd->size, PROT_READ | PROT_WRITE, MAP_SHARED, sfd->fd, 0);
if(!sfd->map)
DEBUG_FATAL("Failed to map shared memory");
}
++sfd->refcount;
}
static void porthole_sharedfd_dec_ref(SharedFD * sfd)
{
if (sfd->refcount == 0)
return;
munmap(sfd->map, sfd->size);
sfd->map = NULL;
--sfd->refcount;
}
static void porthole_sharedfd_free_handler(void * opaque)
{
SharedFD * sfd = (SharedFD *)opaque;
if (sfd->map)
{
munmap(sfd->map, sfd->size);
sfd->map = NULL;
}
close(sfd->fd);
free(sfd);
}
static Mapping * porthole_intmap_new()
{
Mapping * map = (Mapping *)calloc(sizeof(Mapping), 1);
map->segments = objectlist_new(porthole_segment_free_handler);
return map;
}
static void porthole_free_map(Mapping * map)
{
objectlist_free(&map->segments);
free(map);
}
static void porthole_intmaps_free_handler(void * opaque)
{
porthole_free_map((Mapping *)opaque);
}
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size)
{
Segment * seg = calloc(sizeof(Segment), 1);
seg->addr = addr;
seg->size = size;
const unsigned int count = objectlist_count(fds);
for(unsigned int i = 0; i < count; ++i)
{
SharedFD *sfd = (SharedFD*)objectlist_at(fds, i);
if (sfd->id == fd_id)
{
seg->sfd = sfd;
break;
}
}
if (!seg->sfd)
DEBUG_FATAL("Unable to find the FD for the segment, this is a bug in the porthole device!");
map->size += size;
porthole_sharedfd_inc_ref(seg->sfd);
objectlist_push(map->segments, seg);
}
static void porthole_segment_free_handler(void * opaque)
{
Segment * seg = (Segment *)opaque;
porthole_sharedfd_dec_ref(seg->sfd);
free(seg);
}
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type)
{
const unsigned int count = objectlist_count(map->segments);
PortholeMap *m = calloc(sizeof(PortholeMap) + sizeof(PortholeSegment) * count, 1);
m->id = map->id;
m->size = map->size;
for(unsigned int i = 0; i < count; ++i)
{
Segment * seg = (Segment *)objectlist_at(map->segments, i);
m->segments[i].size = seg->size;
m->segments[i].data = seg->sfd->map + seg->addr;
}
objectlist_push(handle->maps, m);
handle->map_cb(type, m);
}