root/openwsman/trunk/src/server/wsmand-listener.c

Revision 2079, 27.5 kB (checked in by houliang, 3 years ago)

initial cim indication listener implementation

Line 
1 /*******************************************************************************
2  * Copyright (C) 2004-2006 Intel Corp. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  - Redistributions of source code must retain the above copyright notice,
8  *    this list of conditions and the following disclaimer.
9  *
10  *  - Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  *  - Neither the name of Intel Corp. nor the names of its
15  *    contributors may be used to endorse or promote products derived from this
16  *    software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL Intel Corp. OR THE CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *******************************************************************************/
30
31 /**
32  * @author Anas Nashif
33  * @author Vadim Revyakin
34  * @author Liang Hou
35  */
36
37 #define _GNU_SOURCE
38 #ifdef HAVE_CONFIG_H
39 #include "wsman_config.h"
40 #endif
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <sys/stat.h>
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #ifndef WIN32
53 #include <dlfcn.h>
54 #endif
55
56 #include "u/libu.h"
57 #include "wsman-xml-api.h"
58 #include "wsman-soap.h"
59 #include "wsman-soap-envelope.h"
60
61 #include "wsman-xml.h"
62 #include "wsman-xml-serializer.h"
63 #include "wsman-dispatcher.h"
64
65 #define OPENWSMAN
66 #include "shttpd.h"
67
68 #include "wsman-plugins.h"
69 #include "wsmand-listener.h"
70 #include "wsmand-daemon.h"
71 #include "wsmand-auth.h"
72 #include "wsman-server.h"
73 #include "wsman-plugins.h"
74 #ifdef ENABLE_EVENTING_SUPPORT
75 #include "wsman-event-pool.h"
76 #include "wsman-cimxmllistener-path.h"
77 #include "wsman-cimindication-processor.h"
78 #endif
79
80 #define MULTITHREADED_SERVER
81
82 #ifdef MULTITHREADED_SERVER
83 #ifdef HAVE_PTHREAD_H
84 #include <pthread.h>
85 #endif
86 #include <sys/socket.h>
87
88 static pthread_mutex_t shttpd_mutex;
89 static pthread_cond_t shttpd_cond;
90 static list_t *request_list;
91 static int num_threads = 0;
92 static int min_threads = 2;
93 static int idle_threads = 0;
94 static int max_threads = 4;
95
96 #endif
97
98
99 int continue_working = 1;
100 static int (*basic_auth_callback) (char *, char *) = NULL;
101
102
103 static int
104 digest_auth_callback(char *realm, char *method, struct digest *dig)
105 {
106         WSmanAuthDigest wsdig;
107         char *filename = wsmand_options_get_digest_password_file();
108
109         if (filename == NULL) {
110                 debug("Could not get digest password file name");
111                 return 0;
112         }
113         wsdig.request_method = method;
114         wsdig.username = dig->user;
115         wsdig.realm = realm;
116         wsdig.digest_uri = dig->uri;
117         wsdig.nonce = dig->nonce;
118         wsdig.cnonce = dig->cnonce;
119         wsdig.qop = dig->qop;
120         strncpy(wsdig.nonce_count, dig->nc, sizeof(wsdig.nonce_count));
121         wsdig.digest_response = dig->resp;
122
123         return ws_authorize_digest(filename, &wsdig);
124 }
125 /*
126 static char *shttp_reason_phrase(int status)
127 {
128         if (status == WSMAN_STATUS_OK) {
129                 return "OK";
130         }
131         return "Error";
132 }
133 */
134
135 typedef struct {
136         char *response;
137         int length;
138         int ind;
139 } ShttpMessage;
140
141
142
143 static int server_callback(struct shttpd_arg_t *arg)
144 {
145         const char *method;
146         const char *content_type;
147 //    char *default_path;
148 //    const char *path;
149         char *encoding = NULL;
150         int status = WSMAN_STATUS_OK;
151         char *fault_reason = NULL;
152
153         ShttpMessage *shttp_msg = (ShttpMessage *) arg->state;
154         int n = 0;
155         int k;
156
157         debug("Server callback started %s. len = %d, sent = %d",
158               (shttp_msg == NULL) ? "initialy" : "continue",
159               arg->buflen, (shttp_msg == NULL) ? 0 : shttp_msg->ind);
160         if (shttp_msg != NULL) {
161                 // We already have the response, but server
162                 // output buffer is smaller then it.
163                 // Some part of resopnse have already sent.
164                 // Just continue to send it to server
165                 goto CONTINUE;
166         }
167         // Here we must handle the initial request
168         WsmanMessage *wsman_msg = wsman_soap_message_new();
169
170         // Check HTTP headers
171
172         method = shttpd_get_env(arg, "REQUEST_METHOD");
173         if (strncmp(method, "POST", 4)) {
174                 debug("Unsupported method %s", method);
175                 status = WSMAN_STATUS_METHOD_NOT_ALLOWED;
176                 fault_reason = "POST method supported only";
177         }
178
179
180         content_type = shttpd_get_header(arg, "Content-Type");
181         if (content_type && strncmp(content_type,
182                                     SOAP_CONTENT_TYPE,
183                                     strlen(SOAP_CONTENT_TYPE)) != 0) {
184                 status = WSMAN_STATUS_UNSUPPORTED_MEDIA_TYPE;
185                 fault_reason = "Unsupported content type";
186                 goto DONE;
187         }
188         if(content_type) {
189                 char *p = strstr(content_type, "charset");
190                 if(p) {
191                         p += strlen("charset");
192                         p++;
193                         wsman_msg->charset = u_strdup(p);
194                         encoding = u_strdup(p);
195                 }
196         }
197         SoapH soap = (SoapH) arg->user_data;
198         wsman_msg->status.fault_code = WSMAN_RC_OK;
199         wsman_msg->http_headers = shttpd_get_all_headers(arg);
200
201         // Get request from http server
202         size_t length = shttpd_get_post_query_len(arg);
203         char *body = shttpd_get_post_query(arg);
204         if (body == NULL) {
205                 status = WSMAN_STATUS_BAD_REQUEST;
206                 fault_reason = "No request body";
207                 error("NULL request body. len = %d", length);
208         }
209 #if 0   
210         if(strcmp(wsman_msg->charset, "UTF-8")) {
211                 iconv_t cd = iconv_open("UTF-8", wsman_msg->charset);
212                 if(cd == -1) {
213                         status = WSMAN_STATUS_INTERNAL_SERVER_ERROR;
214                         fault_reason = "Specified encoding unsupported";
215                         goto DONE;
216                 }
217                 char *mbbuf = u_zalloc(length);
218                 size_t outbuf_len = length;
219                 size_t inbuf_len = length;
220                 char *inbuf = body;
221                 char *outbuf = mbbuf;
222                 size_t coverted = iconv(cd, &inbuf, &inbuf_len, &outbuf, &outbuf_len);
223                 if( coverted == -1) {
224                         status = WSMAN_STATUS_INTERNAL_SERVER_ERROR;
225                         fault_reason = "Cannot covert specified encoding";
226                         goto DONE;
227                 }
228                 iconv_close(cd);
229 //              u_buf_construct(wsman_msg->request, mbbuf, length - outbuf_len, length - outbuf_len);
230                 debug("***coverted = %d***", length - outbuf_len);
231                
232         }
233 #endif         
234         u_buf_construct(wsman_msg->request, body, length, length);
235         debug("Posted request: %s, wsman_msg len = %d", u_buf_ptr(wsman_msg->request),
236                         u_buf_len(wsman_msg->request));
237
238         // some plugins can use credentials for its
239         // own authentication
240         shttpd_get_credentials(arg, &wsman_msg->auth_data.username,
241                                &wsman_msg->auth_data.password);
242
243
244         // Call dispatcher. Real request handling
245         if (status == WSMAN_STATUS_OK) {
246                 // dispatch if we didn't find out the error
247                 dispatch_inbound_call(soap, wsman_msg, NULL);
248                 status = wsman_msg->http_code;
249         }
250
251
252         if (wsman_msg->request) {
253                 // we don't need request any more
254 //              if(strcmp(wsman_msg->charset, "UTF-8") == 0)
255                         (void) u_buf_steal(wsman_msg->request);
256                 u_buf_free(wsman_msg->request);
257                 wsman_msg->request = NULL;
258         }
259         // here we start to handle the response
260
261         shttp_msg = (ShttpMessage *) malloc(sizeof(ShttpMessage));
262         if (shttp_msg == NULL) {
263                 status = WSMAN_STATUS_INTERNAL_SERVER_ERROR;
264                 fault_reason = "No memory";
265                 goto DONE;
266         }
267
268
269         shttp_msg->length = u_buf_len(wsman_msg->response);
270         debug("message len = %d", shttp_msg->length);
271         shttp_msg->response = u_buf_steal(wsman_msg->response);
272         shttp_msg->ind = 0;
273       DONE:
274          wsman_soap_message_destroy(wsman_msg);
275 /*      u_free(wsman_msg->charset);
276         if (wsman_msg->response) {
277                 u_buf_free(wsman_msg->response);
278                 wsman_msg->response = NULL;
279         }
280         //   wsman_soap_message_destroy(wsman_msg);
281         if (wsman_msg->http_headers) {
282                 hash_free(wsman_msg->http_headers);
283         }
284         u_free(wsman_msg);
285         if (fault_reason == NULL) {
286                 fault_reason = shttp_reason_phrase(status);
287         }
288 */      debug("Response (status) %d (%s)", status, fault_reason);
289
290         // Here we begin to create the http response.
291         // Create the headers at first.
292         // We consider output buffer of server is large enough to hold all headers.
293
294         n += snprintf(arg->buf + n, arg->buflen - n, "HTTP/1.1 %d %s\r\n",
295                       status, fault_reason);
296         n += snprintf(arg->buf + n, arg->buflen - n, "Server: %s/%s\r\n",
297                       PACKAGE, VERSION);
298 /*
299     if (status != WSMAN_STATUS_OK) {
300         n += snprintf(arg->buf + n, arg->buflen -n, "\r\n%d %s\r\n",
301                 status, fault_reason);
302         arg->last = 1;
303         u_free(shttp_msg);
304         return n;
305     }
306 */
307         if (!shttp_msg || shttp_msg->length == 0) {
308                 // can't send the body of response or nothing to send
309                 n += snprintf(arg->buf + n, arg->buflen - n, "\r\n");
310                 arg->last = 1;
311                 u_free(shttp_msg);
312                 return n;
313         }
314
315         n += snprintf(arg->buf + n, arg->buflen - n,
316                       "Content-Type: application/soap+xml;charset=%s\r\n", encoding);
317         n += snprintf(arg->buf + n, arg->buflen - n,
318                       "Content-Length: %d\r\n", shttp_msg->length);
319         n += snprintf(arg->buf + n, arg->buflen - n, "\r\n");
320         u_free(encoding);
321         // add response body to output buffer
322       CONTINUE:
323         k = arg->buflen - n;
324         if (k <= shttp_msg->length - shttp_msg->ind) {
325                 // not enogh room for all message. transfer only part
326                 memcpy(arg->buf + n, shttp_msg->response + shttp_msg->ind,
327                        k);
328                 shttp_msg->ind += k;
329                 arg->state = shttp_msg;
330                 return n + k;
331         }
332         // Enough room for all response body
333         memcpy(arg->buf + n, shttp_msg->response + shttp_msg->ind,
334                shttp_msg->length - shttp_msg->ind);
335         n += shttp_msg->length - shttp_msg->ind;
336         if (n + 4 > arg->buflen) {
337                 // not enough room for empty lines at the end of the message
338                 arg->state = shttp_msg;
339                 shttp_msg->ind = shttp_msg->length;
340                 return n;
341         }
342         // here we can complete
343         n += snprintf(arg->buf + n, arg->buflen - n, "\r\n\r\n");
344         debug("%s", arg->buf);
345         u_free(shttp_msg->response);
346         u_free(shttp_msg);
347        
348         arg->last = 1;
349         arg->state = NULL;
350         return n;
351 }
352
353 #ifdef ENABLE_EVENTING_SUPPORT
354
355 static int cimxml_listener_callback(struct shttpd_arg_t *arg)
356 {
357         debug("in cimxml_listener_callback");
358         const char *method;
359         const char *content_type;
360         int status = WSMAN_STATUS_OK;
361         char *fault_reason = NULL;
362         cimxml_context *cntx = NULL;
363         ShttpMessage *shttp_msg = (ShttpMessage *) arg->state;
364         int n = 0;
365         int k;
366
367         debug("CIM Indication Listener callback started %s. len = %d, sent = %d",
368               (shttp_msg == NULL) ? "initialy" : "continue",
369               arg->buflen, (shttp_msg == NULL) ? 0 : shttp_msg->ind);
370         if (shttp_msg != NULL) {
371                 // We already have the response, but server
372                 // output buffer is smaller then it.
373                 // Some part of resopnse have already sent.
374                 // Just continue to send it to server
375                 goto CONTINUE;
376         }
377         // Here we must handle the initial request
378         WsmanMessage *wsman_msg = wsman_soap_message_new();
379
380         // Check HTTP headers
381
382         method = shttpd_get_env(arg, "REQUEST_METHOD");
383         if (strncmp(method, "POST", 4)) {
384                 debug("Unsupported method %s", method);
385                 status = WSMAN_STATUS_METHOD_NOT_ALLOWED;
386                 fault_reason = "POST method supported only";
387         }
388
389
390         content_type = shttpd_get_header(arg, "Content-Type");
391         if (content_type && strncmp(content_type,
392                                     CIMXML_CONTENT_TYPE,
393                                     strlen(CIMXML_CONTENT_TYPE)) != 0) {
394                 status = WSMAN_STATUS_UNSUPPORTED_MEDIA_TYPE;
395                 fault_reason = "Unsupported content type";
396                 goto DONE;
397         }
398        
399         if(strncmp(shttpd_get_header(arg, "CIMExport"), "MethodRequest", strlen("MethodRequest")) ||
400                 strncmp(shttpd_get_header(arg, "CIMExportMethod"), "ExportIndication", strlen("ExportIndication"))) {
401                 status = CIMXML_STATUS_UNSUPPORTED_OPERATION;
402                 fault_reason = "Unsupported operation";
403                 goto DONE;
404         }
405         cntx = (cimxml_context *) arg->user_data;
406         wsman_msg->status.fault_code = WSMAN_RC_OK;
407         wsman_msg->http_headers = shttpd_get_all_headers(arg);
408
409         // Get request from http server
410         size_t length = shttpd_get_post_query_len(arg);
411         char *body = shttpd_get_post_query(arg);
412         if (body == NULL) {
413                 status = CIMXML_STATUS_REQUEST_NOT_WELL_FORMED;
414                 fault_reason = "No request body";
415                 error("NULL request body. len = %d", length);
416         }
417         else {
418                 debug("Posted request: %s", body);
419         }
420         u_buf_construct(wsman_msg->request, body, length, length);
421
422         // some plugins can use credentials for its
423         // own authentication
424         shttpd_get_credentials(arg, &wsman_msg->auth_data.username,
425                                &wsman_msg->auth_data.password);
426
427
428         // Call dispatcher. Real request handling
429         if (status == WSMAN_STATUS_OK) {
430                 CIM_Indication_call(cntx, wsman_msg, NULL);
431                 status = wsman_msg->http_code;
432         }
433
434
435         if (wsman_msg->request) {
436                 // we don't need request any more
437                 (void) u_buf_steal(wsman_msg->request);
438                 u_buf_free(wsman_msg->request);
439                 wsman_msg->request = NULL;
440         }
441         // here we start to handle the response
442
443         shttp_msg = (ShttpMessage *) malloc(sizeof(ShttpMessage));
444         if (shttp_msg == NULL) {
445                 status = WSMAN_STATUS_INTERNAL_SERVER_ERROR;
446                 fault_reason = "No memory";
447                 goto DONE;
448         }
449
450
451         shttp_msg->length = u_buf_len(wsman_msg->response);
452         debug("message len = %d", shttp_msg->length);
453         shttp_msg->response = u_buf_steal(wsman_msg->response);
454         shttp_msg->ind = 0;
455
456     DONE:
457          wsman_soap_message_destroy(wsman_msg);
458          if(cntx) {
459                 u_free(cntx->servicepath);
460                 u_free(cntx);
461          }
462          if (fault_reason == NULL) {
463                 fault_reason = shttp_reason_phrase(status);
464         }
465         debug("Response (status) %d (%s)", status, fault_reason);
466
467         // Here we begin to create the http response.
468         // Create the headers at first.
469         // We consider output buffer of server is large enough to hold all headers.
470
471         n += snprintf(arg->buf + n, arg->buflen - n, "HTTP/1.1 %d %s\r\n",
472                       status, fault_reason);
473         n += snprintf(arg->buf + n, arg->buflen - n, "Server: %s/%s\r\n",
474                       PACKAGE, VERSION);
475          if (status != WSMAN_STATUS_OK) {
476                 n += snprintf(arg->buf + n, arg->buflen -n, "\r\n%d %s\r\n",
477                 status, fault_reason);
478                 arg->last = 1;
479                 u_free(shttp_msg);
480                 return n;
481         }
482         if (!shttp_msg || shttp_msg->length == 0) {
483                 // can't send the body of response or nothing to send
484                 n += snprintf(arg->buf + n, arg->buflen - n, "\r\n");
485                 arg->last = 1;
486                 u_free(shttp_msg);
487                 return n;
488         }
489         n += snprintf(arg->buf + n, arg->buflen - n,
490                       "Content-Type: application/xml; charset=\"utf-8\"\r\n");
491         n += snprintf(arg->buf + n, arg->buflen - n,
492                       "Content-Length: %d\r\n", shttp_msg->length);
493         n += snprintf(arg->buf + n, arg->buflen - n,
494                         "CIMExport: MethodResponse\r\n");
495         n += snprintf(arg->buf + n, arg->buflen - n, "\r\n");
496         // add response body to output buffer
497       CONTINUE:
498         k = arg->buflen - n;
499         if (k <= shttp_msg->length - shttp_msg->ind) {
500                 // not enogh room for all message. transfer only part
501                 memcpy(arg->buf + n, shttp_msg->response + shttp_msg->ind,
502                        k);
503                 shttp_msg->ind += k;
504                 arg->state = shttp_msg;
505                 return n + k;
506         }
507         // Enough room for all response body
508         memcpy(arg->buf + n, shttp_msg->response + shttp_msg->ind,
509                shttp_msg->length - shttp_msg->ind);
510         n += shttp_msg->length - shttp_msg->ind;
511         if (n + 4 > arg->buflen) {
512                 // not enough room for empty lines at the end of the message
513                 arg->state = shttp_msg;
514                 shttp_msg->ind = shttp_msg->length;
515                 return n;
516         }
517         // here we can complete
518         n += snprintf(arg->buf + n, arg->buflen - n, "\r\n\r\n");
519 //      debug("************\n%s\n***********", arg->buf);
520         u_free(shttp_msg->response);
521         u_free(shttp_msg);
522        
523         arg->last = 1;
524         arg->state = NULL;
525         return n;
526 }
527 #endif
528
529 static void wsmand_start_notification_manager(WsContextH cntx, SubsRepositoryEntryH entry, int subsNum)
530 {
531         WsmanMessage *wsman_msg = wsman_soap_message_new();
532         if(wsman_msg == NULL) return;
533         unsigned char *strdoc = entry->strdoc;
534         u_buf_construct(wsman_msg->request, strdoc, entry->len, entry->len);
535         dispatch_inbound_call(cntx->soap, wsman_msg, NULL);
536         wsman_soap_message_destroy(wsman_msg);
537         if(list_count(cntx->soap->subscriptionMemList) > subsNum) {
538                 lnode_t *node = list_last(cntx->soap->subscriptionMemList);
539                 WsSubscribeInfo *subs = (WsSubscribeInfo *)node->list_data;
540                 //Delete new subscription file coz in fact we've got it
541 //              cntx->soap->subscriptionOpSet->delete_subscription(cntx->soap->uri_subsRepository, subs->subsId);
542                 //Update UUID in the memory
543                 strncpy(subs->subsId, entry->uuid+5, EUIDLEN);
544         }
545 }
546
547 static int wsmand_clean_subsrepository(SoapH soap, SubsRepositoryEntryH entry)
548 {
549         int retVal = 0;
550         WsXmlDocH doc = ws_xml_read_memory( (char *)entry->strdoc, entry->len, "UTF-8", 0);
551
552         if(doc) {
553                 WsXmlNodeH node = ws_xml_get_soap_body(doc);
554                 if(node) {
555                         node = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_SUBSCRIBE);
556                         node = ws_xml_get_child(node, 0, XML_NS_EVENTING, WSEVENT_EXPIRES);
557                         if(node == NULL) { //No specified expiration, delete it
558                                 debug("subscription %s deleted from the repository", entry->uuid);
559                                 soap->subscriptionOpSet->delete_subscription(soap->uri_subsRepository, entry->uuid+5);
560                                 retVal = 1;
561                         }
562                 }
563                 ws_xml_destroy_doc(doc);
564         }
565         return retVal;
566 }
567
568 static void listener_shutdown_handler(void *p)
569 {
570         int *a = (int *) p;
571         debug("listener_shutdown_handler started");
572         *a = 0;
573 }
574
575
576 static struct shttpd_ctx *create_shttpd_context(SoapH soap)
577 {
578         struct shttpd_ctx *ctx;
579         if (wsmand_options_get_use_ssl()) {
580                 message("Using SSL");
581                 ctx = shttpd_init(NULL,
582                                   "ssl_certificate",
583                                   wsmand_options_get_ssl_cert_file(),
584                                   "ssl_priv_key",
585                                   wsmand_options_get_ssl_key_file(),
586                                   "auth_realm", AUTHENTICATION_REALM,
587                                   "debug",
588                                   wsmand_options_get_debug_level() >
589                                   0 ? "1" : "0", NULL);
590         } else {
591                 ctx = shttpd_init(NULL,
592                                   "auth_realm", AUTHENTICATION_REALM,
593                                   "debug",
594                                   wsmand_options_get_debug_level() >
595                                   0 ? "1" : "0", NULL);
596         }
597         if (ctx == NULL) {
598                 return NULL;
599         }
600         shttpd_register_url(ctx, wsmand_options_get_service_path(),
601                             server_callback, 0, (void *) soap);
602 #ifdef ENABLE_EVENTING_SUPPORT
603          list_t *listenerpath = get_cimxml_listener_path();
604          if(listenerpath) {
605                lnode_t *node = list_first(listenerpath);
606                while(node) {
607                        char *path = (char *)node->list_data;
608                           cimxml_context *cimcntx = u_malloc(sizeof(cimxml_context));
609                           cimcntx->servicepath = u_strdup(path);
610                           cimcntx->soap = soap;
611                        shttpd_register_url(ctx, path, cimxml_listener_callback,
612                                        0, (void *)cimcntx);
613                           debug("********registered service path: %s*********", path);
614                        node = list_next(listenerpath, node);
615                }
616        }
617 #endif
618         if (wsmand_options_get_digest_password_file()) {
619                 shttpd_register_dauth_callback(ctx, digest_auth_callback);
620                 debug("Using Digest Authorization");
621         }
622         if (basic_auth_callback) {
623                 shttpd_register_bauth_callback(ctx, basic_auth_callback);
624                 debug("Using Basic Authorization %s",
625                       wsmand_option_get_basic_authenticator()?
626                       wsmand_option_get_basic_authenticator() :
627                       wsmand_default_basic_authenticator());
628         }
629
630         return ctx;
631 }
632
633 #ifdef MULTITHREADED_SERVER
634
635 static void handle_socket(int sock, SoapH soap)
636 {
637         struct shttpd_ctx *ctx;
638
639         debug("Thread %d handles sock %d", pthread_self(), sock);
640
641         ctx = create_shttpd_context(soap);
642         if (ctx == NULL) {
643                 (void) shutdown(sock, 2);
644                 close(sock);
645                 return;
646         }
647         shttpd_add(ctx, sock);
648         while (shttpd_active(ctx) && continue_working) {
649                 shttpd_poll(ctx, 100);
650         }
651         shttpd_fini(ctx);
652         debug("Thread %d processed sock %d", pthread_self(), sock);
653 }
654
655
656 static void *service_connection(void *arg)
657 {
658         lnode_t *node;
659         int sock;
660         SoapH soap = (SoapH) arg;
661
662         debug("shttpd thread %d started. num_threads = %d",
663               pthread_self(), num_threads);
664         pthread_mutex_lock(&shttpd_mutex);
665         while (continue_working) {
666                 if (list_isempty(request_list)) {
667                         // no sockets to serve
668                         if (num_threads > min_threads) {
669                                 debug("we have too many threads %d > %d"
670                                       " Thread %d is being completed",
671                                       num_threads, min_threads,
672                                       pthread_self());
673                                 break;
674                         } else {
675                                 idle_threads++;
676                                 debug("Thread %d goes to idle state",
677                                       pthread_self());
678                                 (void) pthread_cond_wait(&shttpd_cond,
679                                                          &shttpd_mutex);
680                                 idle_threads--;
681                                 continue;
682                         }
683                 }
684                 node = list_del_first(request_list);
685                 sock = (int) ((char *) lnode_get(node) - (char *) NULL);
686                 pthread_mutex_unlock(&shttpd_mutex);
687                 lnode_destroy(node);
688                 handle_socket(sock, soap);
689                 pthread_mutex_lock(&shttpd_mutex);
690         }
691         num_threads--;
692         debug("shttpd thread %d completed. num_threads = %d",
693               pthread_self(), num_threads);
694         if (num_threads == 0 && continue_working == 0) {
695                 debug("last thread completed");
696                 pthread_cond_broadcast(&shttpd_cond);
697         }
698         pthread_mutex_unlock(&shttpd_mutex);
699         return NULL;
700 }
701
702 #endif
703
704
705 static int initialize_basic_authenticator(void)
706 {
707         char *auth;
708         char *arg;
709         void *hnd;
710         int (*init) (char *);
711         char *name;
712         int should_return = 0;
713         int res = 0;
714
715         if (wsmand_options_get_basic_password_file() != NULL) {
716                 if ((wsmand_option_get_basic_authenticator() &&
717                      (strcmp(wsmand_default_basic_authenticator(),
718                              wsmand_option_get_basic_authenticator()))) ||
719                     wsmand_option_get_basic_authenticator_arg()) {
720                         fprintf(stderr,
721                                 "basic authentication is ambigious in config file\n");
722                         return 1;
723                 }
724                 auth = wsmand_default_basic_authenticator();
725                 arg = wsmand_options_get_basic_password_file();
726         } else {
727                 auth = wsmand_option_get_basic_authenticator();
728                 arg = wsmand_option_get_basic_authenticator_arg();
729         }
730
731         if (auth == NULL) {
732                 // No basic authenticationame
733                 return 0;
734         }
735
736         if (auth[0] == '/') {
737                 name = auth;
738         } else {
739                 name = u_strdup_printf("%s/%s", PACKAGE_AUTH_DIR, auth);
740                 should_return = 1;
741         }
742
743         hnd = dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
744         if (hnd == NULL) {
745                 fprintf(stderr, "Could not dlopen %s\n", name);
746                 res = 1;
747                 goto DONE;
748         }
749         basic_auth_callback = dlsym(hnd, "authorize");
750         if (basic_auth_callback == NULL) {
751                 fprintf(stderr, "Could not resolve authorize() in %s\n",
752                         name);
753                 res = 1;
754                 goto DONE;
755         }
756
757         init = dlsym(hnd, "initialize");
758         if (init != NULL) {
759                 res = init(arg);
760         }
761       DONE:
762         if (should_return) {
763                 u_free(name);
764         }
765         return res;
766 }
767
768
769 WsManListenerH *wsmand_start_server(dictionary * ini)
770 {
771         WsManListenerH *listener = wsman_dispatch_list_new();
772         listener->config = ini;
773         WsContextH cntx = wsman_init_plugins(listener);
774 #ifdef ENABLE_EVENTING_SUPPORT
775         SubsRepositoryOpSetH ops = wsman_init_subscription_repository(cntx, wsmand_options_get_subscription_repository_uri());
776         list_t *subs_list = list_create(-1);
777         debug("subscription_repository_uri = %s", wsmand_options_get_subscription_repository_uri());
778         if(ops->load_subscription(wsmand_options_get_subscription_repository_uri(), subs_list) == 0) {
779                 lnode_t *node = list_first(subs_list);
780                 while(node) {
781                         SubsRepositoryEntryH entry = (SubsRepositoryEntryH)node->list_data;
782                         if(wsmand_clean_subsrepository(cntx->soap, entry) == 0) {
783                                 debug("load subscription %s", entry->uuid);
784                                 wsmand_start_notification_manager(cntx, entry, list_count(cntx->soap->subscriptionMemList));
785                         }
786                         u_free(entry->uuid);
787                         u_free(entry);
788                         list_delete(subs_list, node);
789                         lnode_destroy(node);
790                         node = list_first(subs_list);
791                 }
792         }
793         list_destroy(subs_list);
794         wsman_init_event_pool(cntx, NULL);
795 #endif
796 #ifdef MULTITHREADED_SERVER
797         int r;
798         int sock;
799         lnode_t *node;
800         pthread_t thr_id;
801         pthread_t notificationManager_id;
802         pthread_attr_t pattrs;
803         struct timespec timespec;
804 #else
805         struct shttpd_ctx *ctx;
806
807 #endif
808
809         if (cntx == NULL) {
810                 return listener;
811         }
812 #ifndef HAVE_SSL
813         if (wsmand_options_get_use_ssl()) {
814                 error("Server configured without SSL support");
815                 return listener;
816         }
817 #endif
818         SoapH soap = ws_context_get_runtime(cntx);
819         ws_set_context_enumIdleTimeout(cntx,
820                                        wsmand_options_get_enumIdleTimeout
821                                        ());
822         if (initialize_basic_authenticator()) {
823                 return listener;
824         }
825
826         int lsn;
827         int port;
828
829
830         if (wsmand_options_get_use_ssl()) {
831                 message("Using SSL");
832                 if (wsmand_options_get_ssl_cert_file() &&
833                     wsmand_options_get_ssl_key_file() &&
834                     (wsmand_options_get_server_ssl_port() > 0)) {
835                         port = wsmand_options_get_server_ssl_port();
836                 } else {
837                         error("Not enough data to use SSL port");
838                         return listener;
839                 }
840         } else {
841                 port = wsmand_options_get_server_port();
842         }
843
844         message("     Working on port %d", port);
845         if (wsmand_options_get_digest_password_file()) {
846                 message("Using Digest Authorization");
847         }
848         if (basic_auth_callback) {
849                 message("Using Basic Authorization %s",
850                         wsmand_option_get_basic_authenticator()?
851                         wsmand_option_get_basic_authenticator() :
852                         wsmand_default_basic_authenticator());
853         }
854
855         if ((wsmand_options_get_digest_password_file() == NULL) &&
856             (basic_auth_callback == NULL)) {
857                 error("Server does not work without authentication");
858                 return listener;
859         }
860
861         wsmand_shutdown_add_handler(listener_shutdown_handler,
862                                     &continue_working);
863
864         lsn = shttpd_open_port(port);
865
866 #ifdef MULTITHREADED_SERVER
867         if ((r = pthread_cond_init(&shttpd_cond, NULL)) != 0) {
868                 debug("pthread_cond_init failed = %d", r);
869                 return listener;
870         }
871         if ((r = pthread_mutex_init(&shttpd_mutex, NULL)) != 0) {
872                 debug("pthread_mutex_init failed = %d", r);
873                 return listener;
874         }
875
876         if ((r = pthread_attr_init(&pattrs)) != 0) {
877                 debug("pthread_attr_init failed = %d", r);
878                 return listener;
879         }
880
881         if ((r = pthread_attr_setdetachstate(&pattrs,
882                                      PTHREAD_CREATE_DETACHED)) != 0) {
883                 debug("pthread_attr_setdetachstate = %d", r);
884                 return listener;
885         }
886
887         request_list = list_create(LISTCOUNT_T_MAX);
888
889         min_threads = wsmand_options_get_min_threads();
890         max_threads = wsmand_options_get_max_threads();
891
892         pthread_create(&thr_id, &pattrs,
893                        wsman_server_auxiliary_loop_thread, cntx);
894 #ifdef ENABLE_EVENTING_SUPPORT
895         pthread_create(&notificationManager_id, &pattrs, wsman_notification_manager, cntx);
896 #endif
897         while (continue_working) {
898                 if ((sock = shttpd_accept(lsn, 1000)) == -1) {
899                         continue;
900                 }
901                 debug("Sock %d accepted", sock);
902                 node = lnode_create((void *) ((char *) NULL + sock));
903                 if (node == NULL) {
904                         debug("lnode_create == NULL");
905                         (void) shutdown(sock, 2);
906                         close(sock);
907                         continue;
908                 }
909
910                 pthread_mutex_lock(&shttpd_mutex);
911
912                 list_append(request_list, node);
913                 if (idle_threads > 0) {
914                         // we have idle thread waiting for a request
915                         debug("using idle thread. idle_threads = %d",
916                               idle_threads);
917                         pthread_cond_signal(&shttpd_cond);
918                         pthread_mutex_unlock(&shttpd_mutex);
919                         continue;
920                 }
921                 if (num_threads >= max_threads) {
922                         // we have enough threads to serve requests
923                         debug("Using existing thread. %d > %d",
924                               num_threads, max_threads);
925                         pthread_mutex_unlock(&shttpd_mutex);
926                         continue;
927                 }
928                 debug("Creating new thread. Old num_threads = %d",
929                       num_threads);
930                 r = pthread_create(&thr_id, &pattrs, service_connection,
931                                    soap);
932                 if (r == 0) {
933                         num_threads++;
934                         debug("Thread %d created", thr_id);
935                         pthread_mutex_unlock(&shttpd_mutex);
936                         continue;
937                 }
938                 debug("pthread_create failed = %d. num_threads = %d",
939                       r, num_threads);
940                 if (num_threads > 0) {
941                         // we have threads to serve request
942                         debug
943                             ("we have threads to serve request. num_threads = %d",
944                              num_threads);
945                         pthread_mutex_unlock(&shttpd_mutex);
946                         continue;
947                 }
948
949                 // So we couldn't create even one thread. Serve the request here
950                 debug("Serve on main thread");
951                 node = list_delete(request_list, node);
952                 if (node) {
953                         lnode_destroy(node);
954                 } else {
955                         error("Coundn't find node in a list");
956                 }
957
958                 pthread_mutex_unlock(&shttpd_mutex);
959
960                 handle_socket(sock, soap);
961         }
962
963         pthread_mutex_lock(&shttpd_mutex);
964         while (num_threads > 0) {
965                 pthread_cond_broadcast(&shttpd_cond);
966                 timespec.tv_sec = 1;
967                 timespec.tv_nsec = 0;
968                 pthread_cond_timedwait(&shttpd_cond, &shttpd_mutex,
969                                        &timespec);
970         }
971         pthread_mutex_unlock(&shttpd_mutex);
972
973 #else
974         ctx = create_shttpd_context(soap);
975         if (ctx == NULL) {
976                 error("Could not create shttpd context");
977                 return listener;
978         }
979         shttpd_listen(ctx, lsn);
980         while (continue_working) {
981                 shttpd_poll(ctx, 1000);
982         }
983         shttpd_fini(ctx);
984
985 #endif
986         debug("shttpd_poll loop canceled");
987
988         return listener;
989 }