KFW Network Identity Manager (Beta 2)
[krb5.git] / src / windows / identity / kmm / kmm_registrar.c
1 /*\r
2  * Copyright (c) 2005 Massachusetts Institute of Technology\r
3  *\r
4  * Permission is hereby granted, free of charge, to any person\r
5  * obtaining a copy of this software and associated documentation\r
6  * files (the "Software"), to deal in the Software without\r
7  * restriction, including without limitation the rights to use, copy,\r
8  * modify, merge, publish, distribute, sublicense, and/or sell copies\r
9  * of the Software, and to permit persons to whom the Software is\r
10  * furnished to do so, subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be\r
13  * included in all copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
22  * SOFTWARE.\r
23  */\r
24 \r
25 /* $Id$ */\r
26 \r
27 #include<kmminternal.h>\r
28 \r
29 static LONG pending_modules = 0;\r
30 static LONG pending_plugins = 0;\r
31 static LONG startup_signal = 0;\r
32 static BOOL load_done = FALSE;\r
33 \r
34 void\r
35 kmmint_check_completion(void) {\r
36     if (pending_modules == 0 &&\r
37         pending_plugins == 0 &&\r
38         InterlockedIncrement(&startup_signal) == 1) {\r
39 \r
40         load_done = TRUE;\r
41 \r
42         /* TODO: check for orphaned plugins */\r
43 \r
44         kmq_post_message(KMSG_KMM, KMSG_KMM_I_DONE, 0, 0);\r
45     }\r
46 }\r
47 \r
48 void\r
49 kmmint_add_to_module_queue(void) {\r
50     InterlockedIncrement(&pending_modules);\r
51 }\r
52 \r
53 void\r
54 kmmint_remove_from_module_queue(void) {\r
55 \r
56     InterlockedDecrement(&pending_modules);\r
57 \r
58     kmmint_check_completion();\r
59 }\r
60 \r
61 void\r
62 kmmint_add_to_plugin_queue(void) {\r
63     InterlockedIncrement(&pending_plugins);\r
64 }\r
65 \r
66 void\r
67 kmmint_remove_from_plugin_queue(void) {\r
68     InterlockedDecrement(&pending_plugins);\r
69 \r
70     kmmint_check_completion();\r
71 }\r
72 \r
73 KHMEXP khm_boolean  KHMAPI\r
74 kmm_load_pending(void) {\r
75     return !load_done;\r
76 }\r
77 \r
78 /*! \internal\r
79   \brief Message handler for the registrar thread. */\r
80 khm_boolean KHMAPI kmm_reg_cb(\r
81     khm_int32 msg_type, \r
82     khm_int32 msg_sub_type, \r
83     khm_ui_4 uparam,\r
84     void *vparam)\r
85 {\r
86     /* we should only be getting <KMSG_KMM,KMSG_KMM_I_REG> anyway */\r
87     if(msg_type != KMSG_KMM || msg_sub_type != KMSG_KMM_I_REG)\r
88         return FALSE;\r
89 \r
90     switch(uparam) {\r
91         case KMM_REG_INIT_MODULE:\r
92             kmm_init_module((kmm_module_i *) vparam);\r
93             kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));\r
94             break;\r
95 \r
96         case KMM_REG_EXIT_MODULE:\r
97             kmm_exit_module((kmm_module_i *) vparam);\r
98             kmm_release_module(kmm_handle_from_module((kmm_module_i *) vparam));\r
99             break;\r
100 \r
101         case KMM_REG_INIT_PLUGIN:\r
102             kmm_init_plugin((kmm_plugin_i *) vparam);\r
103             kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam));\r
104             break;\r
105 \r
106         case KMM_REG_EXIT_PLUGIN:\r
107             kmm_exit_plugin((kmm_plugin_i *) vparam);\r
108             kmm_release_plugin(kmm_handle_from_plugin((kmm_plugin_i *) vparam));\r
109             break;\r
110     }\r
111     return TRUE;\r
112 }\r
113 \r
114 /*! \internal\r
115   \brief The registrar thread.\r
116 \r
117   The only thing this function does is to dispatch messages to the\r
118   callback routine ( kmm_reg_cb() ) */\r
119 DWORD WINAPI kmm_registrar(\r
120   LPVOID lpParameter\r
121 )\r
122 {\r
123     tid_registrar = GetCurrentThreadId();\r
124 \r
125     kmq_subscribe(KMSG_KMM, kmm_reg_cb);\r
126     kmq_subscribe(KMSG_SYSTEM, kmm_reg_cb);\r
127 \r
128     SetEvent(evt_startup);\r
129 \r
130     while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));\r
131 \r
132     ExitThread(0);\r
133     /* not reached */\r
134     return 0;\r
135 }\r
136 \r
137 /*! \internal\r
138   \brief Manages a plugin message thread.\r
139 \r
140   Each plugin gets its own plugin thread which is used to dispatch\r
141   messages to the plugin.  This acts as the thread function for the\r
142   plugin thread.*/\r
143 DWORD WINAPI kmm_plugin_broker(LPVOID lpParameter)\r
144 {\r
145     DWORD rv = 0;\r
146     kmm_plugin_i * p = (kmm_plugin_i *) lpParameter;\r
147 \r
148     TlsSetValue(tls_kmm, (LPVOID) p);\r
149 \r
150     kmm_hold_plugin(kmm_handle_from_plugin(p));\r
151 \r
152     p->tid_thread = GetCurrentThreadId();\r
153 \r
154     if (IsBadCodePtr(p->p.msg_proc)) {\r
155         rv = KHM_ERROR_INVALID_PARAM;\r
156     } else {\r
157         rv = (p->p.msg_proc(KMSG_SYSTEM, KMSG_SYSTEM_INIT, \r
158                             0, (void *) &(p->p)));\r
159     }\r
160 \r
161     /* if it fails to initialize, we exit the plugin */\r
162     if(KHM_FAILED(rv)) {\r
163         kmmint_remove_from_plugin_queue();\r
164         rv = 1;\r
165         goto _exit;\r
166     }\r
167 \r
168     /* subscribe to default message classes by plugin type */\r
169     if(p->p.type == KHM_PITYPE_CRED) {\r
170         kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc);\r
171         kmq_subscribe(KMSG_KCDB, p->p.msg_proc);\r
172         kmq_subscribe(KMSG_CRED, p->p.msg_proc);\r
173     } else if(p->p.type == KHM_PITYPE_IDENT) {\r
174         khm_handle h = NULL;\r
175 \r
176         kmq_subscribe(KMSG_SYSTEM, p->p.msg_proc);\r
177         kmq_subscribe(KMSG_KCDB, p->p.msg_proc);\r
178 \r
179         kmq_create_subscription(p->p.msg_proc, &h);\r
180         kcdb_identity_set_provider(h);\r
181         /* kcdb deletes the subscription when it's done with it */\r
182     } else if(p->p.type == KHM_PITYPE_CONFIG) {\r
183         /*TODO: subscribe to configuration provider messages here */\r
184     }\r
185 \r
186     p->state = KMM_PLUGIN_STATE_RUNNING;\r
187 \r
188     /* if there were any plugins that were waiting for this one to\r
189        start, we should start them too */\r
190     EnterCriticalSection(&cs_kmm);\r
191     do {\r
192         kmm_plugin_i * pd;\r
193         int i;\r
194 \r
195         for(i=0; i < p->n_dependants; i++) {\r
196             pd = p->dependants[i];\r
197 \r
198             pd->n_unresolved--;\r
199 \r
200             if(pd->n_unresolved == 0) {\r
201                 kmm_hold_plugin(kmm_handle_from_plugin(pd));\r
202                 kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_INIT_PLUGIN, (void *) pd);\r
203             }\r
204         }\r
205     } while(FALSE);\r
206     LeaveCriticalSection(&cs_kmm);\r
207 \r
208     kmmint_remove_from_plugin_queue();\r
209 \r
210     /* main message loop */\r
211     while(KHM_SUCCEEDED(kmq_dispatch(INFINITE)));\r
212 \r
213     /* unsubscribe from default message classes by plugin type */\r
214     if(p->p.type == KHM_PITYPE_CRED) {\r
215         kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc);\r
216         kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc);\r
217         kmq_unsubscribe(KMSG_CRED, p->p.msg_proc);\r
218     } else if (p->p.type == KHM_PITYPE_IDENT) {\r
219         kmq_unsubscribe(KMSG_KCDB, p->p.msg_proc);\r
220         kmq_unsubscribe(KMSG_SYSTEM, p->p.msg_proc);\r
221         kcdb_identity_set_provider(NULL);\r
222     } else if(p->p.type == KHM_PITYPE_CONFIG) {\r
223         /*TODO: unsubscribe from configuration provider messages here */\r
224     }\r
225 \r
226     p->p.msg_proc(KMSG_SYSTEM, KMSG_SYSTEM_EXIT, 0, (void *) &(p->p));\r
227 \r
228 _exit:\r
229     p->state = KMM_PLUGIN_STATE_EXITED;\r
230 \r
231     /* the following call will automatically release the plugin */\r
232     kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, \r
233                      KMM_REG_EXIT_PLUGIN, (void *) p);\r
234 \r
235     TlsSetValue(tls_kmm, (LPVOID) 0);\r
236 \r
237     ExitThread(rv);\r
238 \r
239     /* not reached */\r
240     return rv;\r
241 }\r
242 \r
243 /*! \internal\r
244   \brief Initialize a plugin\r
245 \r
246   \note If kmm_init_plugin() is called on a plugin, then kmm_exit_plugin()\r
247       \b must be called for the plugin.\r
248 \r
249   \note Should only be called from the context of the registrar thread */\r
250 void kmm_init_plugin(kmm_plugin_i * p) {\r
251     DWORD dummy;\r
252     khm_handle csp_plugin   = NULL;\r
253     khm_handle csp_plugins  = NULL;\r
254     khm_int32 t;\r
255 \r
256     /* the following will be undone in kmm_exit_plugin() */\r
257     kmm_hold_plugin(kmm_handle_from_plugin(p));\r
258 \r
259     EnterCriticalSection(&cs_kmm);\r
260     if(p->state != KMM_PLUGIN_STATE_REG &&\r
261         p->state != KMM_PLUGIN_STATE_HOLD)\r
262     {\r
263         LeaveCriticalSection(&cs_kmm);\r
264         goto _exit;\r
265     }\r
266 \r
267     _begin_task(0);\r
268     _report_mr1(KHERR_NONE, MSG_IP_TASK_DESC, _cstr(p->p.name));\r
269     _describe();\r
270 \r
271     if(p->state == KMM_PLUGIN_STATE_HOLD) {\r
272         /* if this plugin was held, then we already had a hold\r
273            from the initial attempt to start the plugin.  Undo\r
274            the hold we did a few lines earlier. */\r
275         kmm_release_plugin(kmm_handle_from_plugin(p));\r
276         /* same for the plugin count for the module. */\r
277         p->module->plugin_count--;\r
278     }\r
279 \r
280     p->state = KMM_PLUGIN_STATE_PREINIT;\r
281     LeaveCriticalSection(&cs_kmm);\r
282 \r
283     if(KHM_FAILED(kmm_get_plugins_config(0, &csp_plugins))) {\r
284         _report_mr0(KHERR_ERROR, MSG_IP_GET_CONFIG);\r
285 \r
286         p->state = KMM_PLUGIN_STATE_FAIL_UNKNOWN;\r
287         goto _exit;\r
288     }\r
289 \r
290     if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin)) ||\r
291         KHM_FAILED(khc_read_int32(csp_plugin, L"Flags", &t))) {\r
292         if(KHM_FAILED(kmm_register_plugin(&(p->p), 0))) {\r
293             _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED);\r
294 \r
295             p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED;\r
296             goto _exit;\r
297         }\r
298         \r
299         if(KHM_FAILED(kmm_get_plugin_config(p->p.name, 0, &csp_plugin))) {\r
300             _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED);\r
301 \r
302             p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED;\r
303             goto _exit;\r
304         }\r
305 \r
306         if(KHM_FAILED(khc_read_int32(csp_plugin, L"Flags", &t))) {\r
307             _report_mr0(KHERR_ERROR, MSG_IP_NOT_REGISTERED);\r
308 \r
309             p->state = KMM_PLUGIN_STATE_FAIL_NOT_REGISTERED;\r
310             goto _exit;\r
311         }\r
312     }\r
313 \r
314     if(t & KMM_PLUGIN_FLAG_DISABLED) {\r
315         _report_mr0(KHERR_ERROR, MSG_IP_DISABLED);\r
316 \r
317         p->state = KMM_PLUGIN_STATE_FAIL_DISABLED;\r
318         goto _exit;\r
319     }\r
320 \r
321 #if 0\r
322     /*TODO: check the failure count and act accordingly */\r
323     if(KHM_SUCCEEDED(khc_read_int32(csp_plugin, L"FailureCount", &t)) && (t > 0)) {\r
324     }\r
325 #endif\r
326 \r
327     EnterCriticalSection(&cs_kmm);\r
328 \r
329     p->n_depends = 0;\r
330     p->n_unresolved = 0;\r
331     \r
332     do {\r
333         wchar_t * deps = NULL;\r
334         wchar_t * d;\r
335         khm_size sz = 0;\r
336 \r
337         if(khc_read_multi_string(csp_plugin, L"Dependencies", \r
338                                  NULL, &sz) != KHM_ERROR_TOO_LONG)\r
339             break;\r
340 \r
341         deps = PMALLOC(sz);\r
342         if(KHM_FAILED(khc_read_multi_string(csp_plugin, L"Dependencies", \r
343                                             deps, &sz))) {\r
344             if(deps)\r
345                 PFREE(deps);\r
346             break;\r
347         }\r
348 \r
349         for(d = deps; d && *d; d = multi_string_next(d)) {\r
350             kmm_plugin_i * pd;\r
351             int i;\r
352 \r
353             pd = kmmint_get_plugin_i(d);\r
354 \r
355             if(pd->state == KMM_PLUGIN_STATE_NONE) {\r
356                 /* the dependant was not previously known */\r
357                 pd->state = KMM_PLUGIN_STATE_PLACEHOLDER;\r
358             }\r
359 \r
360             for(i=0; i < pd->n_dependants; i++) {\r
361                 if(pd->dependants[i] == p)\r
362                     break;\r
363             }\r
364 \r
365             if(i >= pd->n_dependants) {\r
366                 if( pd->n_dependants >= KMM_MAX_DEPENDANTS ) {\r
367                     /*TODO: handle this gracefully */\r
368                     RaiseException(1, EXCEPTION_NONCONTINUABLE, 0, NULL);\r
369                 }\r
370 \r
371                 /* released in kmmint_free_plugin() */\r
372                 kmm_hold_plugin(kmm_handle_from_plugin(p));\r
373                 pd->dependants[pd->n_dependants] = p;\r
374                 pd->n_dependants++;\r
375             }\r
376 \r
377             p->n_depends++;\r
378 \r
379             if(pd->state != KMM_PLUGIN_STATE_RUNNING) {\r
380                 p->n_unresolved++;\r
381             }\r
382         }\r
383 \r
384         if(p->n_unresolved > 0) {\r
385             p->state = KMM_PLUGIN_STATE_HOLD;\r
386         }\r
387 \r
388         PFREE(deps);\r
389 \r
390     } while(FALSE);\r
391     LeaveCriticalSection(&cs_kmm);\r
392 \r
393     EnterCriticalSection(&cs_kmm);\r
394     p->module->plugin_count++;\r
395     kmmint_delist_plugin(p);\r
396     kmmint_list_plugin(p);\r
397     LeaveCriticalSection(&cs_kmm);\r
398 \r
399     if(p->state == KMM_PLUGIN_STATE_HOLD) {\r
400         _report_mr1(KHERR_INFO, MSG_IP_HOLD, _dupstr(p->p.name));\r
401 \r
402         goto _exit_post;\r
403     }\r
404 \r
405     kmmint_add_to_plugin_queue();\r
406 \r
407     p->ht_thread = CreateThread(NULL,\r
408                                 0,\r
409                                 kmm_plugin_broker,\r
410                                 (LPVOID) p,\r
411                                 CREATE_SUSPENDED,\r
412                                 &dummy);\r
413 \r
414     p->state = KMM_PLUGIN_STATE_INIT;\r
415 \r
416     ResumeThread(p->ht_thread);\r
417 \r
418 _exit_post:\r
419     if(csp_plugin != NULL)\r
420         khc_close_space(csp_plugin);\r
421 \r
422     if(csp_plugins != NULL)\r
423         khc_close_space(csp_plugins);\r
424 \r
425     _report_mr2(KHERR_INFO, MSG_IP_STATE, \r
426                 _dupstr(p->p.name), _int32(p->state));\r
427 \r
428     _end_task();\r
429     \r
430     return;\r
431 \r
432     /* jump here if an error condition happens before the plugin\r
433        broker thread starts and the plugin should be unloaded */\r
434 \r
435 _exit:\r
436     if(csp_plugin != NULL)\r
437         khc_close_space(csp_plugin);\r
438     if(csp_plugins != NULL)\r
439         khc_close_space(csp_plugins);\r
440 \r
441     _report_mr2(KHERR_WARNING, MSG_IP_EXITING, \r
442                 _dupstr(p->p.name), _int32(p->state));\r
443     _end_task();\r
444 \r
445     kmm_hold_plugin(kmm_handle_from_plugin(p));\r
446 \r
447     kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_PLUGIN, (void *) p);\r
448 }\r
449 \r
450 /*! \internal\r
451   \brief Uninitialize a plugin\r
452 \r
453   In addition to terminating the thread, and removing p from the\r
454   linked list and hashtable, it also frees up p.\r
455    \r
456   \note Should only be called from the context of the registrar thread. */\r
457 void kmm_exit_plugin(kmm_plugin_i * p) {\r
458     int np;\r
459 \r
460     if(p->state == KMM_PLUGIN_STATE_RUNNING ||\r
461         p->state == KMM_PLUGIN_STATE_INIT)\r
462     {\r
463         kmq_post_thread_quit_message(p->tid_thread, 0, NULL);\r
464         /* when we post the quit message to the plugin thread, the plugin\r
465            broker terminates the plugin and posts a EXIT_PLUGIN message,\r
466            which calls this function again.  We just exit here because\r
467            the EXIT_PLUGIN message will end up calling us again momentarily */\r
468         return;\r
469     }\r
470 \r
471     if(p->ht_thread) {\r
472         /* wait for the thread to terminate */\r
473         WaitForSingleObject(p->ht_thread, INFINITE);\r
474         p->ht_thread = NULL;\r
475     }\r
476 \r
477     EnterCriticalSection(&cs_kmm);\r
478 \r
479     /* undo reference count done in kmm_init_plugin() */\r
480     if(p->state == KMM_PLUGIN_STATE_EXITED ||\r
481         p->state == KMM_PLUGIN_STATE_HOLD) \r
482     {\r
483         np = --(p->module->plugin_count);\r
484     } else {\r
485         /* the plugin was never active.  We can't base a module unload\r
486            decision on np */\r
487         np = TRUE;\r
488     }\r
489     LeaveCriticalSection(&cs_kmm);\r
490 \r
491     if(!np) {\r
492         /*  if this is the last plugin to exit, then notify the\r
493             registrar that the module should be removed as well */\r
494         kmm_hold_module(kmm_handle_from_module(p->module));\r
495         kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, KMM_REG_EXIT_MODULE, (void *) p->module);\r
496     }\r
497 \r
498     /* release the hold obtained in kmm_init_plugin() */\r
499     kmm_release_plugin(kmm_handle_from_plugin(p));\r
500 }\r
501 \r
502 /*! \internal\r
503   \brief Initialize a module\r
504 \r
505   \a m is not in the linked list yet.\r
506 \r
507   \note Should only be called from the context of the registrar thread. */\r
508 void kmm_init_module(kmm_module_i * m) {\r
509     HMODULE hm;\r
510     init_module_t p_init_module;\r
511     kmm_plugin_i * pi;\r
512     khm_int32 rv;\r
513     khm_handle csp_mod = NULL;\r
514     khm_handle csp_mods = NULL;\r
515     khm_size sz;\r
516     khm_int32 i;\r
517 \r
518     /* error condition handling */\r
519     BOOL exit_module = FALSE;\r
520     BOOL release_module = TRUE;\r
521     BOOL record_failure = FALSE;\r
522 \r
523     /* failure handling */\r
524     khm_int32 max_fail_count = 0;\r
525     khm_int64 fail_reset_time = 0;\r
526 \r
527     _begin_task(0);\r
528     _report_mr1(KHERR_NONE, MSG_INIT_MODULE, _cstr(m->name));\r
529     _describe();\r
530 \r
531     kmm_hold_module(kmm_handle_from_module(m));\r
532 \r
533     if(KHM_FAILED(kmm_get_modules_config(0, &csp_mods))) {\r
534         _report_mr0(KHERR_ERROR, MSG_IM_GET_CONFIG);\r
535         _location(L"kmm_get_modules_config()");\r
536 \r
537         m->state = KMM_MODULE_STATE_FAIL_UNKNOWN;\r
538         goto _exit;\r
539     }\r
540 \r
541     khc_read_int32(csp_mods, L"ModuleMaxFailureCount", &max_fail_count);\r
542     khc_read_int64(csp_mods, L"ModuleFailureCountResetTime", &fail_reset_time);\r
543 \r
544     /* If the module is not in the pre-init state, we can't\r
545        initialize it. */\r
546     if(m->state != KMM_MODULE_STATE_PREINIT) {\r
547         _report_mr1(KHERR_WARNING, MSG_IM_NOT_PREINIT, _int32(m->state));\r
548         goto _exit;\r
549     }\r
550 \r
551     if(KHM_FAILED(kmm_get_module_config(m->name, 0, &csp_mod))) {\r
552         _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED);\r
553 \r
554         m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED;\r
555         goto _exit;\r
556     }\r
557 \r
558     if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"Flags", &i)) &&\r
559        (i & KMM_MODULE_FLAG_DISABLED)) {\r
560 \r
561         _report_mr0(KHERR_ERROR, MSG_IM_DISABLED);\r
562 \r
563         m->state = KMM_MODULE_STATE_FAIL_DISABLED;\r
564         goto _exit;\r
565     }\r
566 \r
567     if(KHM_SUCCEEDED(khc_read_int32(csp_mod, L"FailureCount", &i))) {\r
568         khm_int64 tm;\r
569         khm_int64 ct;\r
570         khm_int32 last_reason = 0;\r
571 \r
572         /* reset the failure count if the failure count reset time\r
573            period has elapsed */\r
574         tm = 0;\r
575         khc_read_int64(csp_mod, L"FailureTime", &tm);\r
576         GetSystemTimeAsFileTime((LPFILETIME) &ct);\r
577         ct -= tm;\r
578 \r
579         if(tm > 0 && \r
580            FtIntervalToSeconds((LPFILETIME) &ct) > fail_reset_time) {\r
581 \r
582             i = 0;\r
583             khc_write_int32(csp_mod, L"FailureCount", 0);\r
584             khc_write_int64(csp_mod, L"FailureTime", 0);\r
585 \r
586         }\r
587 \r
588         khc_read_int32(csp_mod, L"FailureReason", &last_reason);\r
589 \r
590         /* did we exceed the max failure count?  However, we ignore\r
591            the max failure count if the reason why it didn't load the\r
592            last time was because the module wasn't found. */\r
593         if(i > max_fail_count && \r
594            last_reason != KMM_MODULE_STATE_FAIL_NOT_FOUND) {\r
595             /* failed too many times */\r
596             _report_mr0(KHERR_ERROR, MSG_IM_MAX_FAIL);\r
597 \r
598             m->state = KMM_MODULE_STATE_FAIL_MAX_FAILURE;\r
599             goto _exit;\r
600         }\r
601     }\r
602 \r
603     if(khc_read_string(csp_mod, L"ImagePath", NULL, &sz) == \r
604        KHM_ERROR_TOO_LONG) {\r
605         if(m->path)\r
606             PFREE(m->path);\r
607         m->path = PMALLOC(sz);\r
608         khc_read_string(csp_mod, L"ImagePath", m->path, &sz);\r
609     } else {\r
610         _report_mr0(KHERR_ERROR, MSG_IM_NOT_REGISTERED);\r
611 \r
612         m->state = KMM_MODULE_STATE_FAIL_NOT_REGISTERED;\r
613         goto _exit;\r
614     }\r
615 \r
616     rv = kmmint_read_module_info(m);\r
617 \r
618     if (KHM_FAILED(rv)) {\r
619         if (rv == KHM_ERROR_INCOMPATIBLE) {\r
620             _report_mr0(KHERR_ERROR, MSG_IM_INCOMPATIBLE);\r
621 \r
622             m->state = KMM_MODULE_STATE_FAIL_INCOMPAT;\r
623         } else if (rv == KHM_ERROR_NOT_FOUND) {\r
624             _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path));\r
625 \r
626             m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;\r
627         } else {\r
628             _report_mr0(KHERR_ERROR, MSG_IM_INVALID_MODULE);\r
629 \r
630             m->state = KMM_MODULE_STATE_FAIL_INV_MODULE;\r
631         }\r
632         goto _exit;\r
633     }\r
634 \r
635     /* check again */\r
636     if(m->state != KMM_MODULE_STATE_PREINIT) {\r
637         _report_mr0(KHERR_ERROR, MSG_IM_NOT_PREINIT);\r
638 \r
639         goto _exit;\r
640     }\r
641 \r
642     /* from this point on, we must record any failure codes */\r
643     record_failure = TRUE;\r
644 \r
645     hm = LoadLibrary(m->path);\r
646     if(!hm) {\r
647         m->h_module = NULL;\r
648         m->state = KMM_MODULE_STATE_FAIL_NOT_FOUND;\r
649 \r
650         _report_mr1(KHERR_ERROR, MSG_IM_NOT_FOUND, _dupstr(m->path));\r
651 \r
652         goto _exit;\r
653     }\r
654 \r
655     /* from this point on, we need to discard the module through\r
656        exit_module */\r
657     release_module = FALSE;\r
658     exit_module = TRUE;\r
659 \r
660     m->flags |= KMM_MODULE_FLAG_LOADED;\r
661     m->h_module = hm;\r
662 \r
663     /* TODO: check signatures */\r
664 \r
665     p_init_module = (init_module_t) GetProcAddress(hm, EXP_INIT_MODULE);\r
666 \r
667     if(!p_init_module) {\r
668         _report_mr1(KHERR_ERROR, MSG_IM_NO_ENTRY, _cstr(EXP_INIT_MODULE));\r
669 \r
670         m->state = KMM_MODULE_STATE_FAIL_INVALID;\r
671         goto _exit;\r
672     }\r
673 \r
674     m->state = KMM_MODULE_STATE_INIT;\r
675 \r
676     /* call init_module() */\r
677     rv = (*p_init_module)(kmm_handle_from_module(m));\r
678 \r
679     m->flags |= KMM_MODULE_FLAG_INITP;\r
680 \r
681     if(KHM_FAILED(rv)) {\r
682         _report_mr1(KHERR_ERROR, MSG_IM_INIT_FAIL, _int32(rv));\r
683 \r
684         m->state = KMM_MODULE_STATE_FAIL_LOAD;\r
685         goto _exit;\r
686     }\r
687 \r
688     if(!m->plugins) {\r
689         _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS);\r
690 \r
691         m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS;\r
692         record_failure = FALSE;\r
693         goto _exit;\r
694     }\r
695 \r
696     m->state = KMM_MODULE_STATE_INITPLUG;\r
697 \r
698     do {\r
699         LPOP(&(m->plugins), &pi);\r
700         if(pi) {\r
701             pi->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST;\r
702             kmm_init_plugin(pi);\r
703 \r
704             /* release the hold obtained in kmm_provide_plugin() */\r
705             kmm_release_plugin(kmm_handle_from_plugin(pi));\r
706         }\r
707     } while(pi);\r
708 \r
709     if(!m->plugin_count) {\r
710         _report_mr0(KHERR_ERROR, MSG_IM_NO_PLUGINS);\r
711 \r
712         m->state = KMM_MODULE_STATE_FAIL_NO_PLUGINS;\r
713         record_failure = FALSE;\r
714         goto _exit;\r
715     }\r
716 \r
717     m->state = KMM_MODULE_STATE_RUNNING;\r
718 \r
719     exit_module = FALSE;\r
720     record_failure = FALSE;\r
721 \r
722     ResetEvent(evt_exit);\r
723 \r
724  _exit:\r
725     if(csp_mod) {\r
726         if(record_failure) {\r
727             khm_int64 ct;\r
728 \r
729             i = 0;\r
730             khc_read_int32(csp_mod, L"FailureCount", &i);\r
731             i++;\r
732             khc_write_int32(csp_mod, L"FailureCount", i);\r
733 \r
734             if(i==1) { /* first fault */\r
735                 GetSystemTimeAsFileTime((LPFILETIME) &ct);\r
736                 khc_write_int64(csp_mod, L"FailureTime", ct);\r
737             }\r
738 \r
739             khc_write_int32(csp_mod, L"FailureReason", m->state);\r
740         }\r
741         khc_close_space(csp_mod);\r
742     }\r
743 \r
744     if(csp_mods)\r
745         khc_close_space(csp_mods);\r
746 \r
747     _report_mr2(KHERR_INFO, MSG_IM_MOD_STATE, \r
748                 _dupstr(m->name), _int32(m->state));\r
749 \r
750     kmmint_remove_from_module_queue();\r
751 \r
752     /* if something went wrong after init_module was called on the\r
753        module code, we need to call exit_module */\r
754     if(exit_module)\r
755         kmm_exit_module(m);\r
756 \r
757     if(release_module)\r
758         kmm_release_module(kmm_handle_from_module(m));\r
759 \r
760     if (kherr_is_error()) {\r
761         kherr_context * c;\r
762         kherr_event * err_e = NULL;\r
763         kherr_event * warn_e = NULL;\r
764         kherr_event * e;\r
765 \r
766         c = kherr_peek_context();\r
767         err_e = kherr_get_err_event(c);\r
768         for(e = kherr_get_first_event(c);\r
769             e;\r
770             e = kherr_get_next_event(e)) {\r
771             if (e != err_e &&\r
772                 e->severity == KHERR_WARNING) {\r
773                 warn_e = e;\r
774                 break;\r
775             }\r
776         }\r
777 \r
778         kherr_evaluate_event(err_e);\r
779         if (warn_e)\r
780             kherr_evaluate_event(warn_e);\r
781 \r
782         kherr_clear_error();\r
783 \r
784         e = kherr_report(KHERR_ERROR,\r
785                          (wchar_t *) MSG_IMERR_TITLE,\r
786                          KHERR_FACILITY,\r
787                          NULL,\r
788                          err_e->long_desc,\r
789                          ((warn_e)? (wchar_t *)MSG_IMERR_SUGGEST: NULL),\r
790                          KHERR_FACILITY_ID,\r
791                          KHERR_SUGGEST_NONE,\r
792                          _cstr(m->name),\r
793                          ((warn_e)? _cstr(warn_e->long_desc):0),\r
794                          0,0,\r
795                          KHERR_RF_MSG_SHORT_DESC |\r
796                          ((warn_e)? KHERR_RF_MSG_SUGGEST: 0),\r
797                          KHERR_HMODULE);\r
798 \r
799         kherr_evaluate_event(e);\r
800 \r
801         kherr_release_context(c);\r
802     }\r
803 \r
804     _end_task();\r
805 }\r
806 \r
807 \r
808 /*! \internal\r
809   \brief Uninitializes a module\r
810 \r
811   \note Should only be called from the context of the registrar\r
812   thread */\r
813 void kmm_exit_module(kmm_module_i * m) {\r
814     kmm_plugin_i * p;\r
815 \r
816     /*  exiting a module happens in two stages.  \r
817     \r
818         If the module state is running (there are active plugins) then\r
819         those plugins must be exited.  This has to be done from the\r
820         plugin threads.  The signal for the plugins to exit must be\r
821         issued from the registrar.  Therefore, we post messages to the\r
822         registrar for each plugin we want to remove and exit\r
823         kmm_exit_module().\r
824 \r
825         When the last plugin is exited, the plugin management code\r
826         automatically signalls the registrar to remove the module.\r
827         kmm_exit_module() gets called again.  This is the second\r
828         stage, where we call exit_module() for the module and start\r
829         unloading everything.\r
830     */\r
831 \r
832     EnterCriticalSection(&cs_kmm);\r
833 \r
834     /* get rid of any dangling uninitialized plugins */\r
835     LPOP(&(m->plugins), &p);\r
836     while(p) {\r
837         p->flags &= ~KMM_PLUGIN_FLAG_IN_MODLIST;\r
838         kmm_exit_plugin(p);\r
839 \r
840         /* release hold from kmm_provide_plugin() */\r
841         kmm_release_plugin(kmm_handle_from_plugin(p));\r
842 \r
843         LPOP(&(m->plugins), &p);\r
844     }\r
845 \r
846     if(m->state == KMM_MODULE_STATE_RUNNING) {\r
847         int np = 0;\r
848 \r
849         m->state = KMM_MODULE_STATE_EXITPLUG;\r
850 \r
851         p = kmm_listed_plugins;\r
852 \r
853         while(p) {\r
854             if(p->module == m) {\r
855                 kmm_hold_plugin(kmm_handle_from_plugin(p));\r
856                 kmq_post_message(KMSG_KMM, KMSG_KMM_I_REG, \r
857                                  KMM_REG_EXIT_PLUGIN, (void *) p);\r
858                 np++;\r
859             }\r
860 \r
861             p = LNEXT(p);\r
862         }\r
863 \r
864         if(np > 0) {\r
865             /*  we have to go back and wait for the plugins to exit.\r
866                 when the last plugin exits, it automatically posts\r
867                 EXIT_MODULE. We can pick up from there when this\r
868                 happens. */\r
869             LeaveCriticalSection(&cs_kmm);\r
870             return;\r
871         }\r
872     }\r
873 \r
874     if(m->flags & KMM_MODULE_FLAG_INITP)\r
875     {\r
876         exit_module_t p_exit_module;\r
877 \r
878         if(m->state > 0)\r
879             m->state = KMM_MODULE_STATE_EXIT;\r
880 \r
881         p_exit_module = \r
882             (exit_module_t) GetProcAddress(m->h_module, \r
883                                            EXP_EXIT_MODULE);\r
884         if(p_exit_module) {\r
885             LeaveCriticalSection(&cs_kmm);\r
886             p_exit_module(kmm_handle_from_module(m));\r
887             EnterCriticalSection(&cs_kmm);\r
888         }\r
889     }\r
890 \r
891     LeaveCriticalSection(&cs_kmm);\r
892 \r
893     if(m->state > 0)\r
894         m->state = KMM_MODULE_STATE_EXITED;\r
895 \r
896     if(m->h_module) {\r
897         FreeLibrary(m->h_module);\r
898     }\r
899 \r
900     if(m->h_resource && (m->h_resource != m->h_module)) {\r
901         FreeLibrary(m->h_resource);\r
902     }\r
903 \r
904     m->h_module = NULL;\r
905     m->h_resource = NULL;\r
906     m->flags = 0;\r
907 \r
908     /* release the hold obtained in kmm_init_module() */\r
909     kmm_release_module(kmm_handle_from_module(m));\r
910 }\r