1 /*
2 * Copyright 1999-2006 University of Chicago
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
18 /** @file globus_service_engine_register_session.c Register Service Engine Session
19 */
20
21 #include "globus_i_service_engine.h"
22 #include "globus_i_soap_message.h"
23 #include "globus_xio_tcp_driver.h"
24 #include "globus_xio_gsi.h"
25 #include "globus_xio_http.h"
26 #include "gssapi.h"
27 #include "openssl/rand.h"
28 #include <syslog.h>
29
30 #define SOAP_ACTION_HEADER "SOAPAction"
31 #define CONTENT_TYPE_HEADER "Content-Type"
32
33 static
34 void
35 globus_l_service_engine_accept_callback(
36 globus_xio_server_t server,
37 globus_xio_handle_t new_handle,
38 globus_result_t result,
39 void * arg);
40
41 static
42 void
43 globus_l_service_engine_open_callback(
44 globus_xio_handle_t handle,
45 globus_result_t result,
46 void * arg);
47
48 static
49 globus_result_t
50 globus_l_service_engine_service_module_copy(
51 void ** ne,
52 const void * e);
53
54 static
55 void
56 globus_l_service_engine_service_module_destroy(
57 void * e);
58 #endif /* GLOBUS_DONT_DOCUMENT_INTERNAL */
59
60 /**
61 * Register a new service session
62 * @ingroup globus_service_engine
63 * @internal
64 *
65 * Accepts a new SOAP connection and associates a session with it. When the
66 * session is ready to be processed, the @a callback function is called.
67 *
68 * @param engine
69 * Service engine reference to accept the new session from.
70 * @param callback
71 * Function to call when the session has been accepted.
72 * @param arg
73 * Application-specific parameter to @a callback.
74 *
75 * @retval GLOBUS_SUCCESS
76 * Session accept registered successfully.
77 * @retval GLOBUS_SOAP_MESSAGE_ERROR_TYPE_NULL_PARAM
78 * One of @a engine or @a callback is NULL.
79 * @retval GLOBUS_SOAP_MESSAGE_ERROR_TYPE_FAILED_TRANSPORT
80 * XIO accept failed.
81 *
82 * @see globus_service_engine_register_process()
83 */
84 globus_result_t
85 globus_service_engine_register_session(
86 globus_service_engine_t engine,
87 globus_service_session_callback_func_t
88 callback,
89 void * arg)
90 5861 {
91 5861 globus_i_session_callback_handle_t *callback_handle = NULL;
92 5861 globus_result_t result = GLOBUS_SUCCESS;
93 GlobusFuncName(globus_service_engine_register_session);
94 5861 GlobusServiceEngineDebugEnter();
95
96 5861 if (engine == NULL || callback == NULL)
97 {
98 0 result = GlobusSoapMessageErrorNullParam;
99
100 0 goto out;
101 }
102 5861 callback_handle = calloc(1, sizeof(globus_i_session_callback_handle_t));
103 5861 if(!callback_handle)
104 {
105 0 result = GlobusServiceEngineErrorOutOfMemory;
106
107 0 goto out;
108 }
109
110 5861 callback_handle->callback = callback;
111 5861 callback_handle->args = arg;
112 5861 callback_handle->engine = engine;
113 5861 callback_handle->faulting = GLOBUS_FALSE;
114
115 5861 result = globus_xio_server_register_accept(
116 engine->server,
117 globus_l_service_engine_accept_callback,
118 callback_handle);
119 5861 if(result != GLOBUS_SUCCESS)
120 {
121 0 result = GlobusSoapMessageErrorFailedTransport(
122 result,
123 "XIO Accept failed");
124
125 0 goto free_callback_handle_out;
126 }
127
128 5861 if (result != GLOBUS_SUCCESS)
129 {
130 0 free_callback_handle_out:
131 0 free(callback_handle);
132 }
133 5861 out:
134
135 5861 GlobusServiceEngineDebugExit();
136 5861 return result;
137 }
138 /* globus_service_engine_register_session() */
139
140 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
141 /**
142 * Session accept complete callback
143 *
144 * This callback is called after the XIO accept function has completed.
145 * If successful, an XIO open will be initiated; otherwise, the session
146 * callback will be called with a
147 * GLOBUS_SERVICE_ENGINE_ERROR_TYPE_REQUEST_FAILED error result.
148 *
149 * @param server
150 * XIO server associated with the engine that the new session is being
151 * accepted on.
152 * @param new_handle
153 * New handle created by XIO as a result of the accept. This will be NULL
154 * if the accept failed.
155 * @param result
156 * Result of the accept operation. If not GLOBUS_SUCCESS, the value of
157 * @a new_handle is NULL.
158 * @param arg
159 * The @a globus_i_session_callback_handle_t associated with the new
160 * session.
161 */
162 static
163 void
164 globus_l_service_engine_accept_callback(
165 globus_xio_server_t server,
166 globus_xio_handle_t new_handle,
167 globus_result_t result,
168 void * arg)
169 5843 {
170 5843 char * peer_contact = NULL;
171 globus_xio_attr_t attr;
172 5843 globus_soap_message_handle_t message = NULL;
173 globus_i_session_callback_handle_t *callback_handle;
174 GlobusFuncName(globus_l_service_engine_accept_callback);
175 5843 GlobusServiceEngineDebugEnter();
176
177 5843 callback_handle = arg;
178
179 5843 if(result != GLOBUS_SUCCESS)
180 {
181 290 result = GlobusServiceEngineErrorFailedRequest(result);
182 290 goto out;
183 }
184
185 5553 result = globus_soap_message_handle_init(&message, new_handle);
186 5553 if(result != GLOBUS_SUCCESS)
187 {
188 0 result = GlobusServiceEngineErrorFailedRequest(result);
189 0 goto out;
190 }
191
192 5553 GlobusServiceEngineLock(callback_handle->engine);
193
194 5553 result = globus_soap_message_handle_set_attrs(
195 message,
196 callback_handle->engine->attrs);
197 5553 if(result != GLOBUS_SUCCESS)
198 {
199 0 goto unlock_and_destroy_message_out;
200 }
201 5553 result = globus_soap_message_handle_set_attr(
202 message,
203 GLOBUS_I_SESSION_HANDLE_KEY,
204 NULL,
205 NULL,
206 callback_handle);
207 5553 if(result != GLOBUS_SUCCESS)
208 {
209 0 result = GlobusServiceEngineErrorFailedRequest(result);
210 0 goto unlock_and_destroy_message_out;
211 }
212
213 5553 result = globus_xio_attr_init(&attr);
214 5553 if(result != GLOBUS_SUCCESS)
215 {
216 0 result = GlobusServiceEngineErrorFailedRequest(result);
217 0 goto unlock_and_destroy_message_out;
218 }
219
220 5553 result = globus_xio_handle_cntl(
221 new_handle, globus_i_soap_message_tcp_driver,
222 GLOBUS_XIO_TCP_GET_REMOTE_CONTACT,
223 &peer_contact);
224 5553 if(result != GLOBUS_SUCCESS)
225 {
226 0 result = globus_xio_handle_cntl(
227 new_handle, globus_i_soap_message_tcp_driver,
228 GLOBUS_XIO_TCP_GET_REMOTE_NUMERIC_CONTACT,
229 &peer_contact);
230
231 0 if (result != GLOBUS_SUCCESS)
232 {
233 0 result = GlobusServiceEngineErrorFailedRequest(result);
234 0 goto destroy_attr_out;
235 }
236 }
237 5553 if (callback_handle->engine->logger != NULL)
238 {
239 unsigned char randbytes;
240
241 0 RAND_pseudo_bytes(&randbytes, sizeof(randbytes));
242 0 sprintf(callback_handle->id, "%hu", randbytes);
243
244 0 globus_logging_write(
245 callback_handle->engine->logger,
246 LOG_INFO,
247 "event=globus_service_engine.session.start "
248 "engine_id=%s "
249 "session_id=%s "
250 "client=%s\n",
251 callback_handle->engine->id,
252 callback_handle->id,
253 peer_contact);
254 }
255
256 5553 result = globus_xio_register_open(
257 new_handle, peer_contact, attr,
258 globus_l_service_engine_open_callback, callback_handle);
259 5553 if(result != GLOBUS_SUCCESS)
260 {
261 0 result = GlobusServiceEngineErrorFailedRequest(result);
262 0 goto destroy_attr_out;
263 }
264
265 5553 callback_handle->message = message;
266 5553 GlobusServiceEngineUnlock(callback_handle->engine);
267
268 5553 destroy_attr_out:
269 5553 globus_xio_attr_destroy(attr);
270 5553 if(result != GLOBUS_SUCCESS)
271 {
272 0 unlock_and_destroy_message_out:
273 0 GlobusServiceEngineUnlock(callback_handle->engine);
274 0 globus_soap_message_handle_destroy(message);
275 0 message = NULL;
276
277 290 out:
278 290 callback_handle->callback(result, callback_handle->engine,
279 message, callback_handle->args);
280 290 free(callback_handle);
281 }
282
283 5843 GlobusServiceEngineDebugExit();
284 5843 }
285 /* globus_l_service_engine_accept_callback() */
286
287 /**
288 * Session open callback
289 *
290 * This callback is called after the XIO open function has completed.
291 * If successful, an XIO read will be initiated to retrieve the HTTP headers;
292 * otherwise, the SOAP message handle (and XIO handle) associated with this
293 * session callback will be destroyed and the new session callback will be
294 * called with a * GLOBUS_SERVICE_ENGINE_ERROR_TYPE_REQUEST_FAILED error
295 * result.
296 *
297 * @param handle
298 * XIO handle associated with the session that the new session is being
299 * opened on.
300 * @param result
301 * Result of the open operation.
302 * @param arg
303 * The @a globus_i_session_callback_handle_t associated with the new
304 * session.
305 */
306 static
307 void
308 globus_l_service_engine_open_callback(
309 globus_xio_handle_t handle,
310 globus_result_t result,
311 void * arg)
312 5553 {
313 5553 globus_i_session_callback_handle_t *callback_handle = NULL;
314 static globus_byte_t buffer[1];
315 GlobusFuncName(globus_l_service_engine_open_callback);
316 5553 GlobusServiceEngineDebugEnter();
317
318 5553 callback_handle = arg;
319
320 5553 GlobusServiceEngineLock(callback_handle->engine);
321 5553 if(result == GLOBUS_SUCCESS)
322 {
323 5553 if (callback_handle->engine->https)
324 {
325 2280 gss_name_t peer_name = NULL;
326
327 /* Set Security attribute related to our peer */
328 2280 result = globus_xio_handle_cntl(
329 handle,
330 globus_i_soap_message_gsi_driver,
331 GLOBUS_XIO_GSI_GET_PEER_NAME,
332 &peer_name);
333
334 2280 if (result != GLOBUS_SUCCESS)
335 {
336 0 goto cleanup;
337 }
338 2280 result = globus_soap_message_handle_set_attr(
339 callback_handle->message,
340 GLOBUS_SOAP_MESSAGE_PEER_IDENTITY_KEY,
341 NULL,
342 NULL,
343 (void *) peer_name);
344 2280 if (result != GLOBUS_SUCCESS)
345 {
346 0 goto cleanup;
347 }
348 }
349 5553 result = globus_xio_register_read(handle,
350 buffer,
351 0,
352 0,
353 NULL,
354 globus_i_service_engine_request_ready_callback,
355 callback_handle);
356 }
357
358 5553 cleanup:
359 5553 GlobusServiceEngineUnlock(callback_handle->engine);
360
361 5553 if (result != GLOBUS_SUCCESS)
362 {
363 0 globus_soap_message_handle_destroy(callback_handle->message);
364 0 callback_handle->message = NULL;
365 0 callback_handle->callback(
366 result, callback_handle->engine,
367 NULL, callback_handle->args);
368 }
369 5553 GlobusServiceEngineDebugExit();
370 5553 }
371 /* globus_l_service_engine_open_callback() */
372
373 /**
374 * HTTP Header read callback
375 *
376 * This callback is invoked when the HTTP headers are read from an XIO
377 * handle associated with a SOAP message handle for a new session, or for
378 * a secondary read on an existing session when persistent connections are
379 * used. No data is read into the buffer, but the data descriptor has HTTP
380 * request header information associated with it. The service is loaded
381 * if it has not already been so based on the service URI. If this is a new
382 * session, then the register_session callback will be invoked to let the
383 * user decide when to process it; otherwise, it will continue to be processed.
384 *
385 * @param handle
386 * XIO handle associated with the SOAP message handle.
387 * @param result
388 * Result of the read operation.
389 * @param buffer
390 * IGNORED
391 * @param len
392 * IGNORED
393 * @param nbytes
394 * IGNORED
395 * @param data_desc
396 * Data descriptor handle. This will be queried to determine the HTTP
397 * header information.
398 * @param arg
399 * The @a globus_i_session_callback_handle_t associated with the new
400 * session.
401 */
402 void
403 globus_i_service_engine_request_ready_callback(
404 globus_xio_handle_t handle,
405 globus_result_t result,
406 globus_byte_t * buffer,
407 globus_size_t len,
408 globus_size_t nbytes,
409 globus_xio_data_descriptor_t data_desc,
410 void * arg)
411 16521 {
412 16521 globus_xio_http_header_t * soap_action = NULL;
413 16521 globus_xio_http_header_t * content_type = NULL;
414 16521 globus_xio_http_header_t * connection = NULL;
415 16521 char * method = NULL;
416 16521 char * uri = NULL;
417 globus_xio_http_version_t http_version;
418 globus_hashtable_t headers;
419 char * svcinfo;
420
421 16521 globus_i_session_callback_handle_t *callback_handle = arg;
422 GlobusFuncName(globus_i_service_engine_request_ready_callback);
423 16521 GlobusServiceEngineDebugEnter();
424
425 16521 if (result == GLOBUS_SUCCESS)
426 {
427 11051 result = globus_xio_data_descriptor_cntl(
428 data_desc,
429 globus_i_soap_message_http_driver,
430 GLOBUS_XIO_HTTP_GET_REQUEST,
431 &method,
432 &uri,
433 &http_version,
434 &headers);
435 11051 if (callback_handle->engine->logger)
436 {
437 0 globus_logging_write(
438 callback_handle->engine->logger,
439 LOG_INFO,
440 "event=globus_service_engine.process.start "
441 "session_id=%s "
442 "method=%s "
443 "uri=%s\n",
444 callback_handle->id,
445 method ? method : "NULL",
446 uri ? uri : "NULL");
447 }
448 }
449
450 22059 if (method == NULL || result != GLOBUS_SUCCESS)
451 {
452 5538 result = GlobusServiceEngineErrorMethodNotAllowed(method);
453 5538 if (callback_handle->process_callback != NULL)
454 {
455 /* This handle has been used before. Error will be ignored */
456 5538 callback_handle->state = GLOBUS_L_SERVICE_ENGINE_RESPONSE_CLOSE;
457 5538 globus_soap_message_handle_set_attr(
458 callback_handle->message,
459 GLOBUS_SOAP_MESSAGE_CONNECTION_KEY,
460 globus_soap_message_attr_copy_string,
461 globus_libc_free,
462 (void *) "close");
463 }
464 }
465 10983 else if(!strcmp(method, "POST"))
466 {
467 globus_service_descriptor_t * service_desc;
468
469 10983 soap_action = globus_hashtable_lookup(&headers, SOAP_ACTION_HEADER);
470 10983 if(!soap_action)
471 {
472 0 GlobusServiceEngineDebugPrintf(
473 GLOBUS_L_SERVICE_ENGINE_DEBUG_DEBUG,
474 ("Service request: %s does not "
475 "have a SOAPAction header entry\n",
476 uri));
477 }
478
479 10983 content_type = globus_hashtable_lookup(&headers, CONTENT_TYPE_HEADER);
480 10983 if(!content_type || !content_type->value ||
481 !strstr(content_type->value, "text/xml"))
482 {
483 0 result = GlobusServiceEngineErrorUnsupportedMediaType(
484 (content_type && content_type->value)
485 ? content_type->value
486 : "(null)");
487 0 goto exit;
488 }
489 10983 if (http_version != GLOBUS_XIO_HTTP_VERSION_1_0)
490 {
491 /*
492 * Assume persistent connection with HTTP/1.1 unless told
493 * otherwise
494 */
495 10983 connection = globus_hashtable_lookup(
496 &headers,
497 (void *) "Connection");
498 10983 if (connection && connection->value &&
499 strstr(connection->value, "close"))
500 {
501 0 result = globus_soap_message_handle_set_attr(
502 callback_handle->message,
503 GLOBUS_SOAP_MESSAGE_CONNECTION_KEY,
504 globus_soap_message_attr_copy_string,
505 globus_libc_free,
506 (void *) "close");
507
508 0 if (result != GLOBUS_SUCCESS)
509 {
510 0 result = GlobusServiceEngineErrorFailedRequest(result);
511
512 0 goto exit;
513 }
514 }
515 }
516 else
517 {
518 0 result = globus_soap_message_handle_set_attr(
519 callback_handle->message,
520 GLOBUS_SOAP_MESSAGE_CONNECTION_KEY,
521 globus_soap_message_attr_copy_string,
522 globus_libc_free,
523 (void *) "close");
524 }
525 10983 if (result != GLOBUS_SUCCESS)
526 {
527 0 goto exit;
528 }
529
530 10983 result = globus_i_service_engine_get_descriptor(
531 uri,
532 callback_handle->message,
533 &service_desc);
534
535 10983 if (result != GLOBUS_SUCCESS)
536 {
537 3 goto exit;
538 }
539
540 10980 svcinfo = globus_hashtable_lookup(
541 &callback_handle->engine->services,
542 uri);
543 10980 if (!svcinfo)
544 {
545 10980 char prefix[] = "/wsrf/services/";
546
547 10980 if (strncmp(uri, prefix, sizeof(prefix)-1) == 0)
548 {
549 10980 svcinfo = globus_libc_strdup(uri + sizeof(prefix)-1);
550 }
551 else
552 {
553 0 svcinfo = globus_libc_strdup(uri);
554 }
555
556 10980 globus_hashtable_insert(
557 &callback_handle->engine->services,
558 svcinfo,
559 svcinfo);
560 }
561 10980 if(soap_action)
562 {
563 10980 result = globus_soap_message_handle_set_attr(
564 callback_handle->message,
565 GLOBUS_SOAP_MESSAGE_SOAP_ACTION_KEY,
566 globus_soap_message_attr_copy_string,
567 globus_libc_free,
568 (void *)soap_action->value);
569 10980 if(result != GLOBUS_SUCCESS)
570 {
571 0 result = GlobusServiceEngineErrorReadRequestFailed(result);
572 0 goto exit;
573 }
574 }
575 }
576 /* TODO: Add support for GET of svc?WSDL */
577 else
578 {
579 0 result = GlobusServiceEngineErrorMethodNotAllowed(method);
580 0 goto exit;
581 }
582
583 16521 exit:
584 /* Reused handle, call session processing callback, not the new session
585 * callback
586 */
587 16521 if (callback_handle->process_callback)
588 {
589 10968 globus_i_service_session_callback(callback_handle);
590 }
591 else
592 {
593 5553 if(result != GLOBUS_SUCCESS)
594 {
595 3 callback_handle->result = result;
596 3 globus_soap_message_handle_set_attr(
597 callback_handle->message,
598 GLOBUS_SOAP_MESSAGE_CONNECTION_KEY,
599 globus_soap_message_attr_copy_string,
600 globus_libc_free,
601 (void *) "close");
602 }
603
604 /*
605 * Session started callback, which may register a new accept and then
606 * kick off process session for this session
607 */
608 5553 callback_handle->callback(
609 result, callback_handle->engine,
610 callback_handle->message, callback_handle->args);
611 }
612
613 16521 GlobusServiceEngineDebugExit();
614 16521 }
615 /* globus_i_service_engine_request_ready_callback() */
616
617 /**
618 * Get the service descriptor related to an service URI.
619 */
620 globus_result_t
621 globus_i_service_engine_get_descriptor(
622 char * uri,
623 globus_soap_message_handle_t message,
624 globus_service_descriptor_t ** service_desc_p)
625 10983 {
626 10983 globus_result_t result = GLOBUS_SUCCESS;
627 char * uriref;
628 char * service_path;
629 10983 globus_service_descriptor_t * service_desc = NULL;
630 globus_extension_handle_t ext;
631 10983 globus_bool_t free_service_path = GLOBUS_TRUE;
632
633 10983 uriref = uri;
634 32949 while (*uriref == '/')
635 {
636 10983 uriref++;
637 }
638 10983 service_path = globus_common_create_string(
639 GLOBUS_SERVICE_ENGINE_MODULE_PATH_PREFIX "/%s", uriref);
640
641 10983 if (service_path == NULL)
642 {
643 0 result = GlobusSoapMessageErrorOutOfMemory;
644
645 0 goto out;
646 }
647 10983 service_desc = globus_extension_lookup(
648 &ext,
649 GLOBUS_SERVICE_REGISTRY,
650 service_path);
651 10983 if(!service_desc)
652 {
653 int res;
654
655 277 res = globus_extension_activate(service_path);
656
657 277 if(res != GLOBUS_SUCCESS)
658 {
659 3 globus_object_t * tmp_err = globus_error_get((globus_result_t) res);
660 3 if(GlobusServiceEngineDebug(GLOBUS_L_SERVICE_ENGINE_DEBUG_DEBUG))
661 {
662 char * serror = globus_error_print_chain(
663 0 tmp_err);
664 0 GlobusServiceEngineDebugPrintf(
665 GLOBUS_L_SERVICE_ENGINE_DEBUG_DEBUG,
666 ("Module Activation Failed:\n\n%s\n",
667 serror));
668 0 globus_free(serror);
669 }
670
671 3 result = GlobusServiceEngineErrorServiceLoadFailed(uri, tmp_err);
672 3 goto free_service_path_out;
673 }
674
675 274 GlobusServiceEngineExtensionLock();
676 274 globus_list_insert(&globus_i_service_engine_extensions,
677 service_path);
678 274 free_service_path = GLOBUS_FALSE;
679 274 GlobusServiceEngineExtensionUnlock();
680
681 274 service_desc = globus_extension_lookup(
682 &ext,
683 GLOBUS_SERVICE_REGISTRY,
684 service_path);
685 274 if(!service_desc)
686 {
687 0 result = GlobusServiceEngineErrorServiceLoadFailed(uri, NULL);
688 0 goto remove_extension_from_list_out;
689 }
690 }
691
692 10980 result = globus_soap_message_handle_set_attr(
693 message,
694 GLOBUS_SOAP_MESSAGE_SERVICE_ENDPOINT_KEY,
695 globus_soap_message_attr_copy_string,
696 free,
697 uri);
698 10980 if(result != GLOBUS_SUCCESS)
699 {
700 0 result = GlobusServiceEngineErrorReadRequestFailed(result);
701
702 0 goto remove_extension_from_list_out;
703 }
704
705 10980 result = globus_soap_message_handle_set_attr(
706 message,
707 GLOBUS_I_SERVICE_DESCRIPTOR_KEY,
708 globus_l_service_engine_service_module_copy,
709 globus_l_service_engine_service_module_destroy,
710 (void *)ext);
711
712 10980 if(result != GLOBUS_SUCCESS)
713 {
714 globus_list_t * tmp;
715 0 result = GlobusServiceEngineErrorReadRequestFailed(result);
716
717 0 globus_soap_message_handle_remove_attr(
718 message,
719 GLOBUS_SOAP_MESSAGE_SERVICE_ENDPOINT_KEY);
720
721 0 remove_extension_from_list_out:
722 0 GlobusServiceEngineExtensionLock();
723 0 tmp = globus_list_search(globus_i_service_engine_extensions,
724 service_path);
725
726 0 if (tmp != NULL)
727 {
728 0 globus_list_remove(&globus_i_service_engine_extensions, tmp);
729 }
730 0 GlobusServiceEngineExtensionUnlock();
731 0 globus_extension_deactivate(service_path);
732 }
733 10983 free_service_path_out:
734 10983 if (result != GLOBUS_SUCCESS || free_service_path)
735 {
736 10709 free(service_path);
737 }
738 10983 out:
739 10983 *service_desc_p = service_desc;
740
741 10983 return result;
742 }
743 /* globus_i_service_engine_get_descriptor() */
744
745 static
746 globus_result_t
747 globus_l_service_engine_service_module_copy(
748 void ** ne,
749 const void * e)
750 10980 {
751 10980 globus_extension_reference((globus_extension_handle_t)e);
752 10980 *ne = (void *) e;
753 10980 return GLOBUS_SUCCESS;
754 }
755
756 static
757 void
758 globus_l_service_engine_service_module_destroy(
759 void * e)
760 10980 {
761 10980 globus_extension_release((globus_extension_handle_t)e);
762 10980 }
763
764