/* * Copyright (c) 2005 Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* $Id$ */ #include #include #include /* should only be accessed from the registrar thread */ khm_size kmm_active_modules = 0; kmm_module_i * kmmint_get_module_i(wchar_t * name) { kmm_module_i * m; size_t sz; if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &sz))) return NULL; sz += sizeof(wchar_t); EnterCriticalSection(&cs_kmm); m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name); if(m == NULL) { m = PMALLOC(sizeof(kmm_module_i)); ZeroMemory(m, sizeof(kmm_module_i)); m->magic = KMM_MODULE_MAGIC; m->name = PMALLOC(sz); StringCbCopy(m->name, sz, name); m->state = KMM_MODULE_STATE_NONE; hash_add(hash_modules, (void *) m->name, (void *) m); LPUSH(&kmm_all_modules, m); } LeaveCriticalSection(&cs_kmm); return m; } kmm_module_i * kmmint_find_module_i(wchar_t * name) { kmm_module_i * m; EnterCriticalSection(&cs_kmm); m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name); LeaveCriticalSection(&cs_kmm); return m; } /* called with cs_kmm held */ void kmmint_free_module(kmm_module_i * m) { m->magic = 0; hash_del(hash_modules, m->name); LDELETE(&kmm_all_modules, m); if (m->name) PFREE(m->name); if (m->description) PFREE(m->description); if (m->path) PFREE(m->path); if (m->vendor) PFREE(m->vendor); if (m->support) PFREE(m->support); if (m->version_info) PFREE(m->version_info); PFREE(m); } KHMEXP khm_int32 KHMAPI kmm_hold_module(kmm_module module) { if(!kmm_is_module(module)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_kmm); kmm_module_from_handle(module)->refcount++; LeaveCriticalSection(&cs_kmm); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kmm_release_module(kmm_module vm) { kmm_module_i * m; if(!kmm_is_module(vm)) return KHM_ERROR_INVALID_PARAM; EnterCriticalSection(&cs_kmm); m = kmm_module_from_handle(vm); if(! --(m->refcount)) { /* note that a 0 ref count means that there are no active plugins */ kmmint_free_module(m); } LeaveCriticalSection(&cs_kmm); return KHM_ERROR_SUCCESS; } khm_int32 kmmint_check_api_version(DWORD v) { /* for now, we allow API versions in the range KH_VERSION_API_MINCOMPAT through KH_VERSION_API, inclusive. In the future when we are swamped with so much time that we don't know what to do with it, we can actually parse the apiversion.txt file and create a compatibility table which we can check against the functions used by the module and decide whether or not it is compatible. */ if (v < KH_VERSION_API_MINCOMPAT || v > KH_VERSION_API) return KHM_ERROR_INCOMPATIBLE; else return KHM_ERROR_SUCCESS; } struct lang_code { WORD language; WORD codepage; }; khm_int32 kmmint_read_module_info(kmm_module_i * m) { /* the only fields we can count on at this point are m->name and m->path */ DWORD t; size_t cb; WORD lang; khm_int32 rv = KHM_ERROR_SUCCESS; struct lang_code *languages; int n_languages; int i; wchar_t resname[256]; /* the resource names are a lot shorter */ wchar_t * r; VS_FIXEDFILEINFO *vff; assert(m->name); assert(m->path); t = TRUE; cb = GetFileVersionInfoSize(m->path, &t); /* if successful, cb gets the size in bytes of the version info structure and sets t to zero */ if (t) { return KHM_ERROR_NOT_FOUND; } else if (cb == 0) { _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path)); return KHM_ERROR_INVALID_PARAM; } if (m->version_info) { PFREE(m->version_info); m->version_info = NULL; } m->version_info = PMALLOC(cb); #ifdef DEBUG assert(m->version_info); #endif if(!GetFileVersionInfo(m->path, t, (DWORD) cb, m->version_info)) { rv = KHM_ERROR_NOT_FOUND; _report_mr1(KHERR_WARNING, MSG_RMI_NOT_FOUND, _dupstr(m->path)); _location(L"GetFileVersionInfo"); goto _cleanup; } if(!VerQueryValue(m->version_info, L"\\VarFileInfo\\Translation", (LPVOID*) &languages, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_NO_TRANS, _dupstr(m->path)); _location(L"VerQueryValue"); goto _cleanup; } n_languages = (int) (cb / sizeof(*languages)); /* Try searching for the user's default language first */ lang = GetUserDefaultLangID(); for (i = 0; i < n_languages; i++) { if(languages[i].language == lang) break; } /* If not, try the system default */ if (i >= n_languages) { lang = GetSystemDefaultLangID(); for (i=0; i= n_languages) { lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); for (i=0; i= n_languages) { lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); for (i=0; i= n_languages) { i = 0; } if (i >= n_languages) { rv = KHM_ERROR_INVALID_PARAM; _report_mr0(KHERR_WARNING, MSG_RMI_NO_LOCAL); goto _cleanup; } /* check module name */ StringCbPrintf(resname, sizeof(resname), L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_MODULE), languages[i].language, languages[i].codepage); if (!VerQueryValue(m->version_info, resname, (LPVOID *) &r, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(TEXT(NIMV_MODULE))); goto _cleanup; } if (cb > KMM_MAXCB_NAME || FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG, _cstr(TEXT(NIMV_MODULE))); goto _cleanup; } if (wcscmp(r, m->name)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr2(KHERR_WARNING, MSG_RMI_MOD_MISMATCH, _dupstr(r), _dupstr(m->name)); goto _cleanup; } /* check API version */ StringCbPrintf(resname, sizeof(resname), L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_APIVER), languages[i].language, languages[i].codepage); if (!VerQueryValue(m->version_info, resname, (LPVOID *) &r, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(TEXT(NIMV_APIVER))); goto _cleanup; } if (cb > KMM_MAXCB_NAME || FAILED(StringCbLength(r, KMM_MAXCB_NAME, &cb))) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG, _cstr(TEXT(NIMV_APIVER))); goto _cleanup; } t = wcstol(r, NULL, 10); rv = kmmint_check_api_version(t); if (KHM_FAILED(rv)) { _report_mr2(KHERR_WARNING, MSG_RMI_API_MISMATCH, _int32(t), _int32(KH_VERSION_API)); goto _cleanup; } /* Looks good. Now load the description, copyright, support URI and file versions */ if (m->description) { PFREE(m->description); m->description = NULL; } StringCbPrintf(resname, sizeof(resname), L"\\StringFileInfo\\%04x%04x\\FileDescription", languages[i].language, languages[i].codepage); if (!VerQueryValue(m->version_info, resname, (LPVOID *) &r, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(L"FileDescription")); goto _cleanup; } if (cb > KMM_MAXCB_DESC || FAILED(StringCbLength(r, KMM_MAXCB_DESC, &cb))) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG, _cstr(L"FileDescription")); goto _cleanup; } cb += sizeof(wchar_t); m->description = PMALLOC(cb); #ifdef DEBUG assert(m->description); #endif StringCbCopy(m->description, cb, r); /* on to the support URI */ if (m->support) { PFREE(m->support); m->support = NULL; } StringCbPrintf(resname, sizeof(resname), L"\\StringFileInfo\\%04x%04x\\" TEXT(NIMV_SUPPORT), languages[i].language, languages[i].codepage); if (!VerQueryValue(m->version_info, resname, (LPVOID *) &r, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(TEXT(NIMV_SUPPORT))); goto _cleanup; } if (cb > KMM_MAXCB_SUPPORT || FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG, _cstr(TEXT(NIMV_SUPPORT))); goto _cleanup; } cb += sizeof(wchar_t); m->support = PMALLOC(cb); #ifdef DEBUG assert(m->support); #endif StringCbCopy(m->support, cb, r); /* the vendor/copyright */ if (m->vendor) { PFREE(m->vendor); m->vendor = NULL; } StringCbPrintf(resname, sizeof(resname), L"\\StringFileInfo\\%04x%04x\\LegalCopyright", languages[i].language, languages[i].codepage); if (!VerQueryValue(m->version_info, resname, (LPVOID *) &r, &cb)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(L"LegalCopyright")); goto _cleanup; } if (cb > KMM_MAXCB_SUPPORT || FAILED(StringCbLength(r, KMM_MAXCB_SUPPORT, &cb))) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_TOO_LONG, _cstr(L"LegalCopyright")); goto _cleanup; } cb += sizeof(wchar_t); m->vendor = PMALLOC(cb); #ifdef DEBUG assert(m->vendor); #endif StringCbCopy(m->vendor, cb, r); if (!VerQueryValue(m->version_info, L"\\", (LPVOID *) &vff, &cb) || cb != sizeof(*vff)) { rv = KHM_ERROR_INVALID_PARAM; _report_mr1(KHERR_WARNING, MSG_RMI_RES_MISSING, _cstr(L"Fixed Version Info")); goto _cleanup; } m->file_version.major = HIWORD(vff->dwFileVersionMS); m->file_version.minor = LOWORD(vff->dwFileVersionMS); m->file_version.patch = HIWORD(vff->dwFileVersionLS); m->file_version.aux = LOWORD(vff->dwFileVersionLS); m->prod_version.major = HIWORD(vff->dwProductVersionMS); m->prod_version.minor = LOWORD(vff->dwProductVersionMS); m->prod_version.patch = HIWORD(vff->dwProductVersionLS); m->prod_version.aux = LOWORD(vff->dwProductVersionLS); rv = KHM_ERROR_SUCCESS; _cleanup: if (KHM_FAILED(rv)) { if (m->version_info) { PFREE(m->version_info); m->version_info = NULL; } } return rv; } KHMEXP khm_int32 KHMAPI kmm_load_module(wchar_t * modname, khm_int32 flags, kmm_module * result) { kmm_module_i * m = NULL; kmm_module_i * mi; size_t cbsize; khm_int32 rv = KHM_ERROR_SUCCESS; if(FAILED(StringCbLength(modname, KMM_MAXCB_NAME, &cbsize))) return KHM_ERROR_INVALID_PARAM; cbsize += sizeof(wchar_t); EnterCriticalSection(&cs_kmm); mi = kmmint_find_module_i(modname); if(mi != NULL) { kmm_hold_module(kmm_handle_from_module(mi)); /* check if the module has either failed to load either or if it has been terminated. If so, we try once again to load the module. */ if(!(flags & KMM_LM_FLAG_NOLOAD) && (mi->state < 0 || mi->state == KMM_MODULE_STATE_EXITED)) { mi->state = KMM_MODULE_STATE_PREINIT; } } LeaveCriticalSection(&cs_kmm); if(flags & KMM_LM_FLAG_NOLOAD) { if(result) *result = mi; else if(mi) kmm_release_module(kmm_handle_from_module(mi)); return (mi)? KHM_ERROR_SUCCESS: KHM_ERROR_NOT_FOUND; } if(mi) { m = mi; } else { m = kmmint_get_module_i(modname); m->state = KMM_MODULE_STATE_PREINIT; kmm_hold_module(kmm_handle_from_module(m)); } /* the module is already running or is already being worked on by the registrar */ if(m->state != KMM_MODULE_STATE_PREINIT) { if(result) *result = kmm_handle_from_module(m); else kmm_release_module(kmm_handle_from_module(m)); return KHM_ERROR_EXISTS; } kmmint_add_to_module_queue(); if(flags & KMM_LM_FLAG_SYNC) { kmm_hold_module(kmm_handle_from_module(m)); kmq_send_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_INIT_MODULE, (void*) m); if(m->state <= 0) { /* failed to load ? */ if(m->state == KMM_MODULE_STATE_FAIL_NOT_FOUND) rv = KHM_ERROR_NOT_FOUND; else if(m->state == KMM_MODULE_STATE_FAIL_SIGNATURE) rv = KHM_ERROR_INVALID_SIGNATURE; else rv = KHM_ERROR_UNKNOWN; kmm_release_module(kmm_handle_from_module(m)); if(result) *result = NULL; } else { if(result) *result = kmm_handle_from_module(m); else kmm_release_module(kmm_handle_from_module(m)); } } else { kmm_hold_module(kmm_handle_from_module(m)); kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_INIT_MODULE, (void*) m); if(result) *result = kmm_handle_from_module(m); else kmm_release_module(kmm_handle_from_module(m)); } return rv; } KHMEXP khm_int32 KHMAPI kmm_get_module_state(kmm_module m) { if(!kmm_is_module(m)) return KMM_MODULE_STATE_NONE; else return kmm_module_from_handle(m)->state; } KHMEXP khm_int32 KHMAPI kmm_get_module_info_i(kmm_module vm, kmm_module_info * info) { kmm_module_i * m; khm_int32 rv; EnterCriticalSection(&cs_kmm); if (!kmm_is_module(vm) || !info) rv = KHM_ERROR_INVALID_PARAM; else { m = kmm_module_from_handle(vm); ZeroMemory(info, sizeof(*info)); info->reg.name = m->name; info->reg.path = m->path; info->reg.vendor = m->vendor; info->reg.n_plugins = m->plugin_count; info->state = m->state; info->h_module = vm; info->file_version = m->file_version; info->product_version = m->prod_version; kmm_hold_module(vm); rv = KHM_ERROR_SUCCESS; } LeaveCriticalSection(&cs_kmm); return rv; } KHMEXP khm_int32 KHMAPI kmm_release_module_info_i(kmm_module_info * info) { if (info->h_module) kmm_release_module(info->h_module); ZeroMemory(info, sizeof(*info)); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kmm_unload_module(kmm_module module) { if(!kmm_is_module(module)) return KHM_ERROR_INVALID_PARAM; kmm_hold_module(module); kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_MODULE, (void *) kmm_module_from_handle(module)); return KHM_ERROR_SUCCESS; } KHMEXP khm_int32 KHMAPI kmm_load_default_modules(void) { khm_handle csm = NULL; khm_handle cs_mod = NULL; khm_int32 rv; wchar_t buf[KMM_MAXCCH_NAME]; khm_size s; rv = kmm_get_modules_config(0, &csm); if(KHM_FAILED(rv)) return rv; _begin_task(KHERR_CF_TRANSITIVE); _report_mr0(KHERR_NONE, MSG_LOAD_DEFAULT); _describe(); kmmint_add_to_module_queue(); while(KHM_SUCCEEDED(khc_enum_subspaces(csm, cs_mod, &cs_mod))) { s = sizeof(buf); if (KHM_FAILED(khc_get_config_space_name(cs_mod, buf, &s))) continue; /* check for schema subspace. This is not an actual module. */ if (!wcscmp(buf, L"_Schema")) continue; kmm_load_module(buf, 0, NULL); } kmmint_remove_from_module_queue(); if(csm) khc_close_space(csm); _end_task(); return rv; } #ifdef _WIN32 KHMEXP HMODULE KHMAPI kmm_get_hmodule(kmm_module m) { if(!kmm_is_module(m)) return NULL; else return kmm_module_from_handle(m)->h_module; } #endif