/* introspecting.c -- LabComm example of a twoway stacked introspection reader/writer. Copyright 2013 Anders Blomdell <anders.blomdell@control.lth.se> This file is part of LabComm. LabComm 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 3 of the License, or (at your option) any later version. LabComm 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, see <http://www.gnu.org/licenses/>. */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include "labcomm_private.h" #include "introspecting.h" #include "gen/introspecting_messages.h" struct introspecting_private { struct introspecting introspecting; struct labcomm_error_handler *error; struct labcomm_memory *memory; struct labcomm_scheduler *scheduler; struct labcomm_reader_action_context reader_action_context; struct labcomm_writer_action_context writer_action_context; LABCOMM_SIGNATURE_ARRAY_DEF(remote, struct remote { char *name; int size; uint8_t *signature; }); LABCOMM_SIGNATURE_ARRAY_DEF(local, struct local { enum introspecting_status status; struct labcomm_signature *signature; }); }; static struct local *get_local(struct introspecting_private *introspecting, int index, struct labcomm_signature *signature) { /* Called with data_lock held */ struct local *local; local = LABCOMM_SIGNATURE_ARRAY_REF(introspecting->memory, introspecting->local, struct local, index); if (local->signature == NULL) { local->signature = signature; local->status = introspecting_unknown; } if (local->status == introspecting_unknown) { int i; local->status = introspecting_unhandled; LABCOMM_SIGNATURE_ARRAY_FOREACH(introspecting->remote, struct remote, i) { struct remote *r; r = LABCOMM_SIGNATURE_ARRAY_REF(introspecting->memory, introspecting->remote, struct remote, i); if (r->name && strcmp(signature->name, r->name) == 0 && r->size == signature->size && memcmp(signature->signature, r->signature, signature->size) == 0) { local->status = introspecting_unregistered; break; } } } return local; } static void handles_signature( introspecting_messages_handles_signature *value, void * context) { struct introspecting_private *introspecting = context; struct remote *remote; labcomm_scheduler_data_lock(introspecting->scheduler); remote = LABCOMM_SIGNATURE_ARRAY_REF(introspecting->memory, introspecting->remote, struct remote, value->index); remote->name = strdup(value->name); remote->signature = malloc(value->signature.n_0); if (remote->signature) { int i; memcpy(remote->signature, value->signature.a, value->signature.n_0); remote->size = value->signature.n_0; LABCOMM_SIGNATURE_ARRAY_FOREACH(introspecting->local, struct local, i) { struct local *l; l = LABCOMM_SIGNATURE_ARRAY_REF(introspecting->memory, introspecting->local, struct local, i); if (l->signature && l->status == introspecting_unhandled && l->signature->name && strcmp(remote->name, l->signature->name) == 0 && remote->size == l->signature->size && memcmp(l->signature->signature, remote->signature, l->signature->size) == 0) { l->status = introspecting_unregistered; } } } labcomm_scheduler_data_unlock(introspecting->scheduler); } static int wrap_reader_alloc( struct labcomm_reader *r, struct labcomm_reader_action_context *action_context, char *labcomm_version) { struct introspecting_private *introspecting = action_context->context; labcomm_decoder_register_introspecting_messages_handles_signature( introspecting->introspecting.reader->decoder, handles_signature, introspecting); return labcomm_reader_alloc(r, action_context->next, labcomm_version); } struct handles_signature { struct introspecting_private *introspecting; int index; struct labcomm_signature *signature; }; static void send_handles_signature(void *arg) { struct handles_signature *h = arg; introspecting_messages_handles_signature handles_signature; handles_signature.index = h->index; handles_signature.name = h->signature->name; handles_signature.signature.n_0 = h->signature->size; handles_signature.signature.a = h->signature->signature; labcomm_encode_introspecting_messages_handles_signature( h->introspecting->introspecting.writer->encoder, &handles_signature); } static int wrap_reader_start( struct labcomm_reader *r, struct labcomm_reader_action_context *action_context, int local_index, int remote_index, struct labcomm_signature *signature, void *value) { struct introspecting_private *introspecting = action_context->context; if (value == NULL) { struct handles_signature *handles_signature; handles_signature = labcomm_memory_alloc(introspecting->memory, 1, sizeof(*handles_signature)); handles_signature->introspecting = introspecting; handles_signature->index = local_index; handles_signature->signature = signature; labcomm_scheduler_enqueue(introspecting->scheduler, 0, send_handles_signature, handles_signature); } return labcomm_reader_start(r, action_context->next, local_index, remote_index, signature, value); } void encode_handles_signature( struct labcomm_encoder *encoder, void *context) { struct labcomm_signature *signature = context; introspecting_messages_handles_signature handles_signature; int index = 0; handles_signature.index = index; handles_signature.name = signature->name; handles_signature.signature.n_0 = signature->size; handles_signature.signature.a = signature->signature; labcomm_encode_introspecting_messages_handles_signature( NULL, &handles_signature); } struct labcomm_reader_action introspecting_reader_action = { .alloc = wrap_reader_alloc, .free = NULL, .start = wrap_reader_start, .end = NULL, .fill = NULL, .ioctl = NULL }; static void register_encoder_signatures(void *context) { struct introspecting_private *introspecting = context; labcomm_encoder_register_introspecting_messages_handles_signature( introspecting->introspecting.writer->encoder); } static int wrap_writer_alloc( struct labcomm_writer *w, struct labcomm_writer_action_context *action_context, char *labcomm_version) { struct introspecting_private *introspecting = action_context->context; labcomm_scheduler_enqueue(introspecting->scheduler, 0, register_encoder_signatures, introspecting); return labcomm_writer_alloc(w, action_context->next, labcomm_version); } static int wrap_writer_start( struct labcomm_writer *w, struct labcomm_writer_action_context *action_context, int index, struct labcomm_signature *signature, void *value) { struct introspecting_private *introspecting = action_context->context; if (value == NULL) { struct local *local; labcomm_scheduler_data_lock(introspecting->scheduler); local = get_local(introspecting, index, signature); local->status = introspecting_registered; labcomm_scheduler_data_unlock(introspecting->scheduler); } return labcomm_writer_start(w, action_context->next, index, signature, value); } static int wrap_writer_ioctl( struct labcomm_writer *w, struct labcomm_writer_action_context *action_context, int index, struct labcomm_signature *signature, uint32_t ioctl_action, va_list args) { struct introspecting_private *introspecting = action_context->context; switch (ioctl_action) { case HAS_SIGNATURE: { struct local *local; int result; labcomm_scheduler_data_lock(introspecting->scheduler); local = get_local(introspecting, index, signature); result = local->status; labcomm_scheduler_data_unlock(introspecting->scheduler); return result; } default: { return labcomm_writer_ioctl(w, action_context->next, index, signature, ioctl_action, args); } break; } } struct labcomm_writer_action introspecting_writer_action = { .alloc = wrap_writer_alloc, .free = NULL, .start = wrap_writer_start, .end = NULL, .flush = NULL, .ioctl = wrap_writer_ioctl }; extern struct introspecting *introspecting_new( struct labcomm_reader *reader, struct labcomm_writer *writer, struct labcomm_error_handler *error, struct labcomm_memory *memory, struct labcomm_scheduler *scheduler) { struct introspecting_private *result; result = malloc(sizeof(*result)); if (result == NULL) { goto out_fail; } /* Wrap reader and writer */ result->reader_action_context.next = reader->action_context; result->reader_action_context.action = &introspecting_reader_action; result->reader_action_context.context = result; reader->action_context = &result->reader_action_context; result->writer_action_context.next = writer->action_context; result->writer_action_context.action = &introspecting_writer_action; result->writer_action_context.context = result; writer->action_context = &result->writer_action_context; /* Init visible result struct */ result->introspecting.reader = reader; result->introspecting.writer = writer; /* Init other fields */ result->error = error; result->memory = memory; result->scheduler = scheduler; LABCOMM_SIGNATURE_ARRAY_INIT(result->remote, struct remote); LABCOMM_SIGNATURE_ARRAY_INIT(result->local, struct local); goto out_ok; out_fail: return NULL; out_ok: return &result->introspecting; }