mirror of https://github.com/dzavalishin/oskit/
381 lines
8.8 KiB
C
Executable File
381 lines
8.8 KiB
C
Executable File
/*
|
|
* Copyright (c) 1996, 1998, 1999 University of Utah and the Flux Group.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Flux OSKit. The OSKit is free software, also known
|
|
* as "open source;" you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License (GPL), version 2, as published by the Free
|
|
* Software Foundation (FSF). To explore alternate licensing terms, contact
|
|
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
*
|
|
* The OSKit 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 GPL for more details. You should have
|
|
* received a copy of the GPL along with the OSKit; see the file COPYING. If
|
|
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Generic COM interface registration module.
|
|
* Basically maintains a list of COM interface pointers
|
|
* for a particular interface GUID (IID).
|
|
* Given an IID, you can find all the registered COM interfaces with that IID.
|
|
* Interfaces will be returned in the order in which they were registered.
|
|
* It's harmless to register an interface multiple times;
|
|
* only a single entry in the table will be retained.
|
|
* Currently just does everything with simple lists;
|
|
* if we end up having lots of objects, we may need smarter algorithms.
|
|
*/
|
|
|
|
/*
|
|
* Locking! This is not thread safe, which is *going* to cause problems.
|
|
*/
|
|
|
|
#include <oskit/com.h>
|
|
#include <oskit/com/services.h>
|
|
#include <oskit/com/mem.h>
|
|
#include <oskit/c/stdlib.h>
|
|
#include <oskit/c/assert.h>
|
|
#include <oskit/c/string.h>
|
|
|
|
static struct oskit_services_ops services_ops;
|
|
|
|
/*
|
|
* One of these nodes represents each registered COM interface (object)
|
|
*/
|
|
struct objnode {
|
|
struct objnode *next;
|
|
oskit_iunknown_t *intf;
|
|
};
|
|
|
|
/*
|
|
* We keep one iidnode for each unique IID we see
|
|
*/
|
|
struct iidnode {
|
|
struct iidnode *next;
|
|
oskit_guid_t iid;
|
|
struct objnode *objs;
|
|
int objcount;
|
|
};
|
|
|
|
/*
|
|
* This is the interface object. Each services database is parameterized
|
|
* with a memory object.
|
|
*/
|
|
struct db {
|
|
oskit_services_t servi; /* COM interface */
|
|
int count; /* Reference count */
|
|
struct iidnode *iids; /* DB pointer */
|
|
oskit_mem_t *memi; /* Memory object to use */
|
|
};
|
|
|
|
static OSKIT_COMDECL
|
|
services_query(oskit_services_t *si,
|
|
const oskit_iid_t *iid, void **out_ihandle)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
|
|
if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 ||
|
|
memcmp(iid, &oskit_services_iid, sizeof(*iid)) == 0) {
|
|
*out_ihandle = &s->servi;
|
|
s->count++;
|
|
return 0;
|
|
}
|
|
|
|
*out_ihandle = 0;
|
|
return OSKIT_E_NOINTERFACE;
|
|
};
|
|
|
|
static OSKIT_COMDECL_U
|
|
services_addref(oskit_services_t *si)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
|
|
assert(s->count);
|
|
return ++s->count;
|
|
}
|
|
|
|
static OSKIT_COMDECL_U
|
|
services_release(oskit_services_t *si)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
|
|
assert(s->count);
|
|
|
|
if (--s->count == 0) {
|
|
oskit_mem_t *memi = s->memi;
|
|
struct iidnode *in, *nin;
|
|
struct objnode *on, *non;
|
|
|
|
/*
|
|
* Must free up all the registered interface objects.
|
|
*/
|
|
in = s->iids;
|
|
while (in) {
|
|
on = in->objs;
|
|
while (on) {
|
|
oskit_iunknown_release(on->intf);
|
|
non = on->next;
|
|
oskit_mem_free(memi,
|
|
(void *) on, sizeof(*on), 0);
|
|
on = non;
|
|
}
|
|
|
|
nin = in->next;
|
|
oskit_mem_free(memi, (void *) in, sizeof(*in), 0);
|
|
in = nin;
|
|
}
|
|
|
|
oskit_mem_free(memi, (void *)s, sizeof(*s), 0);
|
|
oskit_mem_release(memi);
|
|
return 0;
|
|
}
|
|
return s->count;
|
|
}
|
|
|
|
/*
|
|
* Register an interface in the services database.
|
|
* More than one interface can be registered for a particular IID.
|
|
*/
|
|
OSKIT_COMDECL
|
|
services_addservice(oskit_services_t *si,
|
|
const struct oskit_guid *iid, void *interface)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
oskit_iunknown_t *iu = (oskit_iunknown_t*)interface;
|
|
struct iidnode *in;
|
|
struct objnode *on, **onp;
|
|
|
|
/* Find or create the appropriate iidnode */
|
|
for (in = s->iids; ; in = in->next) {
|
|
if (in == NULL) {
|
|
in = oskit_mem_alloc(s->memi, sizeof(*in), 0);
|
|
if (in == NULL)
|
|
return OSKIT_E_OUTOFMEMORY;
|
|
in->iid = *iid;
|
|
in->objs = NULL;
|
|
in->objcount = 0;
|
|
in->next = s->iids;
|
|
s->iids = in;
|
|
break;
|
|
}
|
|
if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
|
|
break;
|
|
}
|
|
|
|
/* Make sure this interface isn't already registered */
|
|
for (onp = &in->objs; *onp; onp = &(*onp)->next) {
|
|
if ((*onp)->intf == interface)
|
|
return 0;
|
|
}
|
|
|
|
/* Create a new objnode for this interface */
|
|
on = oskit_mem_alloc(s->memi, sizeof(*on), 0);
|
|
if (on == NULL)
|
|
return OSKIT_E_OUTOFMEMORY;
|
|
on->next = NULL;
|
|
on->intf = iu; oskit_iunknown_addref(iu);
|
|
*onp = on;
|
|
in->objcount++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Unregister a previously registered interface.
|
|
*/
|
|
OSKIT_COMDECL
|
|
services_remservice(oskit_services_t *si,
|
|
const struct oskit_guid *iid, void *interface)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
struct iidnode *in;
|
|
struct objnode *on, **onp;
|
|
|
|
/* Find the appropriate iidnode */
|
|
for (in = s->iids; ; in = in->next) {
|
|
if (in == NULL)
|
|
return OSKIT_E_INVALIDARG;
|
|
if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
|
|
break;
|
|
}
|
|
|
|
/* Find and remove the objnode */
|
|
for (onp = &in->objs; ; onp = &on->next) {
|
|
on = *onp;
|
|
if (on == NULL)
|
|
return OSKIT_E_INVALIDARG;
|
|
if (on->intf == interface)
|
|
break;
|
|
}
|
|
*onp = on->next;
|
|
oskit_iunknown_release(on->intf);
|
|
oskit_mem_free(s->memi, (void *) on, sizeof(*on), 0);
|
|
in->objcount--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Obtain a list of all the registered interfaces with a specified IID.
|
|
*/
|
|
OSKIT_COMDECL
|
|
services_lookup(oskit_services_t *si,
|
|
const oskit_guid_t *iid, void ***out_interface_array)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
struct iidnode *in;
|
|
struct objnode *on;
|
|
void **arr;
|
|
int i;
|
|
|
|
/* Find the appropriate iidnode */
|
|
for (in = s->iids; ; in = in->next) {
|
|
if (in == NULL) {
|
|
*out_interface_array = NULL;
|
|
return 0;
|
|
}
|
|
if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
|
|
break;
|
|
}
|
|
if (in->objcount == 0) {
|
|
*out_interface_array = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Allocate an array to hold the interface pointers to return
|
|
* Note that we *do* use malloc here, since the caller is responsible
|
|
* for freeing up the array.
|
|
*/
|
|
arr = malloc(sizeof(*arr)*in->objcount);
|
|
if (arr == NULL)
|
|
return OSKIT_E_OUTOFMEMORY;
|
|
|
|
/* Fill it in */
|
|
for (i = 0, on = in->objs; i < in->objcount; i++, on = on->next) {
|
|
assert(on != NULL);
|
|
arr[i] = on->intf;
|
|
oskit_iunknown_addref(on->intf);
|
|
}
|
|
assert(on == NULL);
|
|
|
|
*out_interface_array = arr;
|
|
return in->objcount;
|
|
}
|
|
|
|
/*
|
|
* Lookup the first interface registered for a given IID.
|
|
* This is typically used to look up "the" instance of a service
|
|
*/
|
|
OSKIT_COMDECL
|
|
services_lookup_first(oskit_services_t *si,
|
|
const oskit_guid_t *iid, void **out_intf)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
struct iidnode *in;
|
|
oskit_iunknown_t *intf;
|
|
|
|
/* Find the appropriate iidnode */
|
|
for (in = s->iids; ; in = in->next) {
|
|
if (in == NULL) {
|
|
*out_intf = NULL;
|
|
return 0;
|
|
}
|
|
if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
|
|
break;
|
|
}
|
|
if (in->objcount == 0) {
|
|
*out_intf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
*out_intf = intf = in->objs->intf;
|
|
oskit_iunknown_addref(intf);
|
|
return 0;
|
|
}
|
|
|
|
OSKIT_COMDECL
|
|
services_clone(oskit_services_t *si, oskit_services_t **out_intf)
|
|
{
|
|
struct db *s = (struct db *) si;
|
|
struct db *ns;
|
|
struct iidnode *in;
|
|
struct objnode *on;
|
|
oskit_error_t rc;
|
|
|
|
ns = oskit_mem_alloc(s->memi, sizeof(*ns), 0);
|
|
if (ns == NULL)
|
|
return OSKIT_E_OUTOFMEMORY;
|
|
|
|
ns->count = 1;
|
|
ns->memi = s->memi;
|
|
ns->servi.ops = &services_ops;
|
|
ns->iids = 0;
|
|
oskit_mem_addref(ns->memi);
|
|
|
|
in = s->iids;
|
|
while (in) {
|
|
on = in->objs;
|
|
while (on) {
|
|
if ((rc = services_addservice(&ns->servi,
|
|
&in->iid, on->intf))
|
|
!= NULL) {
|
|
panic("services_clone");
|
|
}
|
|
on = on->next;
|
|
}
|
|
in = in->next;
|
|
}
|
|
|
|
*out_intf = &ns->servi;
|
|
return 0;
|
|
}
|
|
|
|
static struct oskit_services_ops services_ops = {
|
|
services_query,
|
|
services_addref,
|
|
services_release,
|
|
services_addservice,
|
|
services_remservice,
|
|
services_lookup,
|
|
services_lookup_first,
|
|
services_clone,
|
|
};
|
|
|
|
/*
|
|
* The memory object interface is optional, except when creating the
|
|
* initial global registry. Subsequent to the creation of the global
|
|
* registry, we can look there for a default memory object is one is
|
|
* not provided.
|
|
*/
|
|
oskit_error_t
|
|
oskit_services_create(oskit_mem_t *memi, oskit_services_t **out_intf)
|
|
{
|
|
struct db *s;
|
|
|
|
/*
|
|
* If a memory object was not provided, look in the global registry.
|
|
*/
|
|
if (!memi) {
|
|
oskit_lookup_first(&oskit_mem_iid, (void *) &memi);
|
|
|
|
if (!memi)
|
|
panic("oskit_services_create: Null memory object!");
|
|
}
|
|
|
|
s = oskit_mem_alloc(memi, sizeof(*s), 0);
|
|
if (s == NULL)
|
|
return OSKIT_E_OUTOFMEMORY;
|
|
|
|
s->count = 1;
|
|
s->memi = memi;
|
|
s->servi.ops = &services_ops;
|
|
s->iids = 0;
|
|
oskit_mem_addref(memi);
|
|
|
|
*out_intf = &s->servi;
|
|
|
|
return 0;
|
|
}
|