blob: 0f7b35916bdcc6dee67751c4199cd1f8d46012a6 [file] [log] [blame]
// Copyright 2015 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// 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.
#include "android/emulation/qemud/android_qemud_service.h"
#include "android/emulation/qemud/android_qemud_common.h"
#include <errno.h>
QemudService* qemud_service_new(const char* name,
int max_clients,
void* serv_opaque,
QemudServiceConnect serv_connect,
QemudServiceSave serv_save,
QemudServiceLoad serv_load,
QemudService** pservices) {
QemudService* s = static_cast<QemudService*>(android_alloc0(sizeof(*s)));
s->name = ASTRDUP(name);
s->max_clients = max_clients;
s->num_clients = 0;
s->clients = NULL;
s->serv_opaque = serv_opaque;
s->serv_connect = serv_connect;
s->serv_save = serv_save;
s->serv_load = serv_load;
s->next = *pservices;
*pservices = s;
return s;
}
void qemud_service_add_client(QemudService* s, QemudClient* c) {
c->service = s;
c->next_serv = s->clients;
s->clients = c;
s->num_clients += 1;
}
void qemud_service_remove_client(QemudService* s, QemudClient* c) {
QemudClient** pnode = &s->clients;
QemudClient* node;
/* remove from clients linked-list */
for (; ;) {
node = *pnode;
if (node == NULL) {
D("%s: could not find client for service '%s'",
__FUNCTION__, s->name);
return;
}
if (node == c)
break;
pnode = &node->next_serv;
}
*pnode = node->next_serv;
s->num_clients -= 1;
}
QemudClient* qemud_service_connect_client(QemudService* sv,
int channel_id,
const char* client_param) {
QemudClient* client =
sv->serv_connect(sv->serv_opaque, sv, channel_id, client_param);
if (client == NULL) {
D("%s: registration failed for '%s' service",
__FUNCTION__, sv->name);
return NULL;
}
D("%s: registered client channel %d for '%s' service",
__FUNCTION__, channel_id, sv->name);
return client;
}
QemudService* qemud_service_find(QemudService* service_list, const char* service_name) {
QemudService* sv = NULL;
for (sv = service_list; sv != NULL; sv = sv->next) {
if (!strcmp(sv->name, service_name)) {
break;
}
}
return sv;
}
void qemud_service_save_name(Stream* f, QemudService* s) {
int len = strlen(s->name) + 1; // include '\0' terminator
stream_put_be32(f, len);
stream_write(f, (const uint8_t*) s->name, len);
}
char* qemud_service_load_name(Stream* f) {
int ret;
int name_len = stream_get_be32(f);
char* service_name = (char*)android_alloc(name_len);
if ((ret = stream_read(f, (uint8_t*) service_name, name_len) != name_len)) {
D("%s: service name load failed: expected %d bytes, got %d\n",
__FUNCTION__, name_len, ret);
AFREE(service_name);
return NULL;
}
if (service_name[name_len - 1] != '\0') {
char last = service_name[name_len - 1];
service_name[name_len - 1] = '\0'; /* make buffer contents printable */
D("%s: service name load failed: expecting NULL-terminated string, but "
"last char is '%c' (buffer contents: '%s%c')\n",
__FUNCTION__, name_len, last, service_name, last);
AFREE(service_name);
return NULL;
}
return service_name;
}
void qemud_service_save(Stream* f, QemudService* s) {
qemud_service_save_name(f, s);
stream_put_be32(f, s->max_clients);
stream_put_be32(f, s->num_clients);
if (s->serv_save)
s->serv_save(f, s, s->serv_opaque);
}
int qemud_service_load(Stream* f, QemudService* current_services) {
char* service_name = qemud_service_load_name(f);
if (service_name == NULL)
return -EIO;
/* get current service instance */
QemudService* sv = qemud_service_find(current_services, service_name);
if (sv == NULL) {
D("%s: loading failed: service \"%s\" not available\n",
__FUNCTION__, service_name);
return -EIO;
}
/* reconfigure service as required */
sv->max_clients = stream_get_be32(f);
sv->num_clients = 0;
// NOTE: The number of clients saved cannot be verified now.
(void) stream_get_be32(f);
/* load service specific data */
int ret;
if (sv->serv_load) {
if ((ret = sv->serv_load(f, sv, sv->serv_opaque))) {
/* load failure */
return ret;
}
}
return 0;
}
void qemud_service_broadcast(QemudService* sv,
const uint8_t* msg,
int msglen) {
QemudClient* c;
for (c = sv->clients; c; c = c->next_serv)
qemud_client_send(c, msg, msglen);
}