libasyncd
ad_server.c
Go to the documentation of this file.
1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <strings.h>
4 #include <arpa/inet.h>
5 #include <netinet/in.h>
6 #include <errno.h>
7 #include <assert.h>
8 #include <sys/un.h>
9 #include <sys/socket.h>
10 #include <sys/eventfd.h>
11 #include <event2/event.h>
12 #include <event2/bufferevent.h>
13 #include <event2/bufferevent_ssl.h>
14 #include <event2/thread.h>
15 #include <event2/listener.h>
16 #include <openssl/ssl.h>
17 #include <openssl/rand.h>
18 #include <openssl/conf.h>
19 #include <openssl/engine.h>
20 #include <openssl/err.h>
21 #include "macro.h"
22 #include "qlibc/qlibc.h"
23 #include "ad_server.h"
24 
25 /**
26  * Main core logic of the server implementation.
27  *
28  * @file ad_server.c
29  */
30 
31 #ifndef _DOXYGEN_SKIP
32 /*
33  * User callback hook container.
34  */
35 typedef struct ad_conn_s ad_conn_t;
36 struct ad_hook_s {
37  char *method;
38  ad_callback cb;
39  void *userdata;
40 };
41 
42 /*
43  * Local functions.
44  */
45 static int notify_loopexit(ad_server_t *server);
46 static void notify_cb(struct bufferevent *buffer, void *userdata);
47 static void *server_loop(void *instance);
48 static void close_server(ad_server_t *server);
49 static void libevent_log_cb(int severity, const char *msg);
50 static int set_undefined_options(ad_server_t *server);
51 static SSL_CTX *init_ssl(const char *cert_path, const char *pkey_path);
52 static void listener_cb(struct evconnlistener *listener,
53  evutil_socket_t evsocket, struct sockaddr *sockaddr,
54  int socklen, void *userdata);
55 static ad_conn_t *conn_new(ad_server_t *server, struct bufferevent *buffer);
56 static void conn_reset(ad_conn_t *conn);
57 static void conn_free(ad_conn_t *conn);
58 static void conn_read_cb(struct bufferevent *buffer, void *userdata) ;
59 static void conn_write_cb(struct bufferevent *buffer, void *userdata);
60 static void conn_event_cb(struct bufferevent *buffer, short what, void *userdata);
61 static void conn_cb(ad_conn_t *conn, int event);
62 static int call_hooks(short event, ad_conn_t *conn);
63 static void *set_userdata(ad_conn_t *conn, int index, const void *userdata, ad_userdata_free_cb free_cb);
64 static void *get_userdata(ad_conn_t *conn, int index);
65 
66 /*
67  * Local variables.
68  */
69 static bool initialized = false;
70 #endif
71 
72 /*
73  * Global variables.
74  */
76 
77 /**
78  * Set debug output level.
79  *
80  * @param debug_level debug output level. 0 to disable.
81  *
82  * @return previous debug level.
83  *
84  * @note
85  * debug_level:
86  * AD_LOG_DISABLE
87  * AD_LOG_ERROR
88  * AD_LOG_WARN (default)
89  * AD_LOG_INFO
90  * AD_LOG_DEBUG
91  * AD_LOG_DEBUG2
92  */
93 enum ad_log_e ad_log_level(enum ad_log_e log_level) {
94  int prev = _ad_log_level;
95  _ad_log_level = log_level;
96  return prev;
97 }
98 
99 /**
100  * Create a server object.
101  */
103  if (initialized) {
104  initialized = true;
105  //evthread_use_pthreads();
106  }
107 
108  ad_server_t *server = NEW_OBJECT(ad_server_t);
109  if (server == NULL) {
110  return NULL;
111  }
112 
113  // Initialize instance.
114  server->options = qhashtbl(0, 0);
115  server->stats = qhashtbl(100, QHASHTBL_THREADSAFE);
116  server->hooks = qlist(0);
117  if (server->options == NULL || server->stats == NULL || server->hooks == NULL) {
118  ad_server_free(server);
119  return NULL;
120  }
121 
122  DEBUG("Created a server object.");
123  return server;
124 }
125 
126 /**
127  * Start server.
128  *
129  * @return 0 if successful, otherwise -1.
130  */
132  DEBUG("Starting a server.");
133 
134  // Set default options that were not set by user..
135  set_undefined_options(server);
136 
137  // Hookup libevent's log message.
138  if (_ad_log_level >= AD_LOG_DEBUG) {
139  event_set_log_callback(libevent_log_cb);
140  if (_ad_log_level >= AD_LOG_DEBUG2) {
141  event_enable_debug_mode();
142  }
143  }
144 
145  // Parse addr
146  int port = ad_server_get_option_int(server, "server.port");
147  char *addr = ad_server_get_option(server, "server.addr");
148  struct sockaddr *sockaddr = NULL;
149  size_t sockaddr_len = 0;
150  if (addr[0] == '/') { // Unix socket.
151  struct sockaddr_un unixaddr;
152  bzero((void *) &unixaddr, sizeof(struct sockaddr_un));
153  if (strlen(addr) >= sizeof(unixaddr.sun_path)) {
154  errno = EINVAL;
155  DEBUG("Too long unix socket name. '%s'", addr);
156  return -1;
157  }
158  unixaddr.sun_family = AF_UNIX;
159  strcpy(unixaddr.sun_path, addr); // no need of strncpy()
160  sockaddr = (struct sockaddr *) &unixaddr;
161  sockaddr_len = sizeof(unixaddr);
162  } else if (strstr(addr, ":")) { // IPv6
163  struct sockaddr_in6 ipv6addr;
164  bzero((void *) &ipv6addr, sizeof(struct sockaddr_in6));
165  ipv6addr.sin6_family = AF_INET6;
166  ipv6addr.sin6_port = htons(port);
167  evutil_inet_pton(AF_INET6, addr, &ipv6addr.sin6_addr);
168  sockaddr = (struct sockaddr *) &ipv6addr;
169  sockaddr_len = sizeof(ipv6addr);
170  } else { // IPv4
171  struct sockaddr_in ipv4addr;
172  bzero((void *) &ipv4addr, sizeof(struct sockaddr_in));
173  ipv4addr.sin_family = AF_INET;
174  ipv4addr.sin_port = htons(port);
175  ipv4addr.sin_addr.s_addr =
176  (IS_EMPTY_STR(addr)) ? INADDR_ANY : inet_addr(addr);
177  sockaddr = (struct sockaddr *) &ipv4addr;
178  sockaddr_len = sizeof(ipv4addr);
179  }
180 
181  // SSL
182  if (ad_server_get_option_int(server, "server.enable_ssl")) {
183  char *cert_path = ad_server_get_option(server, "server.ssl_cert");
184  char *pkey_path = ad_server_get_option(server, "server.ssl_pkey");
185  server->sslctx = init_ssl(cert_path, pkey_path);
186  if (server->sslctx == NULL) {
187  ERROR("Couldn't load certificate file(%s) or private key file(%s).",
188  cert_path, pkey_path);
189  return -1;
190  }
191  DEBUG("SSL Initialized.");
192  }
193 
194  // Bind
195  if (! server->evbase) {
196  server->evbase = event_base_new();
197  if (! server->evbase) {
198  ERROR("Failed to create a new event base.");
199  return -1;
200  }
201  }
202 
203  // Create a eventfd for notification channel.
204  int notifyfd = eventfd(0, 0);
205  server->notify_buffer = bufferevent_socket_new(server->evbase, notifyfd, BEV_OPT_CLOSE_ON_FREE);
206  bufferevent_setcb(server->notify_buffer, NULL, notify_cb, NULL, server);
207 
208  if (! server->listener) {
209  server->listener = evconnlistener_new_bind(
210  server->evbase, listener_cb, (void *)server,
211  LEV_OPT_THREADSAFE | LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
212  ad_server_get_option_int(server, "server.backlog"),
213  sockaddr, sockaddr_len);
214  if (! server->listener) {
215  ERROR("Failed to bind on %s:%d", addr, port);
216  return -1;
217  }
218  }
219 
220  // Listen
221  INFO("Listening on %s:%d%s", addr, port, ((server->sslctx) ? " (SSL)" : ""));
222 
223  int exitstatus = 0;
224  if (ad_server_get_option_int(server, "server.daemon")) {
225  DEBUG("Launching server as a thread.")
226  server->thread = NEW_OBJECT(pthread_t);
227  pthread_create(server->thread, NULL, &server_loop, (void *)server);
228  //pthread_detach(server->thread);
229  } else {
230  int *retval = server_loop(server);
231  exitstatus = *retval;
232  free(retval);
233 
234  close_server(server);
235  if (ad_server_get_option_int(server, "server.free_on_stop")) {
236  ad_server_free(server);
237  }
238  }
239 
240  return exitstatus;
241 }
242 
243 /**
244  * Stop server.
245  *
246  * This call is be used to stop a server from different thread.
247  *
248  * @return 0 if successful, otherwise -1.
249  */
251  DEBUG("Send loopexit notification.");
252  notify_loopexit(server);
253  sleep(1);
254 
255  if (ad_server_get_option_int(server, "server.daemon")) {
256  close_server(server);
257  if (ad_server_get_option_int(server, "server.free_on_stop")) {
258  ad_server_free(server);
259  }
260  }
261 }
262 
263 /**
264  * Release server object and all the resources.
265  */
267  if (server == NULL) return;
268 
269  int daemon = ad_server_get_option_int(server, "server.daemon");
270  if (daemon && server->thread) {
271  notify_loopexit(server);
272  sleep(1);
273  close_server(server);
274  }
275 
276  if (server->evbase) {
277  event_base_free(server->evbase);
278  }
279 
280  if (server->sslctx) {
281  SSL_CTX_free(server->sslctx);
282  ERR_clear_error();
283  ERR_remove_state(0);
284  }
285 
286  if (server->options) {
287  server->options->free(server->options);
288  }
289  if (server->stats) {
290  server->stats->free(server->stats);
291  }
292  if (server->hooks) {
293  qlist_t *tbl = server->hooks;
294  ad_hook_t *hook;
295  while ((hook = tbl->popfirst(tbl, NULL))) {
296  if (hook->method) free(hook->method);
297  free(hook);
298  }
299  server->hooks->free(server->hooks);
300  }
301  free(server);
302  DEBUG("Server terminated.");
303 }
304 
305 /**
306  * Clean up all the global objects.
307  *
308  * This will make memory-leak checkers happy.
309  * There are globally shared resources in libevent and openssl and
310  * it's usually not a problem since they don't grow but having these
311  * can confuse some debugging tools into thinking as memory leak.
312  * If you need to make sure that libasyncd has released all internal
313  * library-global data structures, call this.
314  */
316  // Libevent related.
317  //libevent_global_shutdown(); // From libevent v2.1
318 
319  // OpenSSL related.
320  ENGINE_cleanup();
321  CONF_modules_free();
322  ERR_free_strings();
323  EVP_cleanup();
324  CRYPTO_cleanup_all_ex_data();
325  sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
326 }
327 
328 /**
329  * Set server option.
330  *
331  * @see AD_SERVER_OPTIONS
332  */
333 void ad_server_set_option(ad_server_t *server, const char *key, const char *value) {
334  server->options->putstr(server->options, key, value);
335 }
336 
337 /**
338  * Retrieve server option.
339  */
340 char *ad_server_get_option(ad_server_t *server, const char *key) {
341  return server->options->getstr(server->options, key, false);
342 }
343 
344 /**
345  * Retrieve server option in integer format.
346  */
347 int ad_server_get_option_int(ad_server_t *server, const char *key) {
348  char *value = ad_server_get_option(server, key);
349  return (value) ? atoi(value) : 0;
350 }
351 
352 /**
353  * Get OpenSSL SSL_CTX object.
354  *
355  * @return SSL_CTX object, NULL if not enabled.
356  *
357  * @note
358  * Libasyncd initializes SSL_CTX with minimum default with
359  * "SSLv23_server_method" which will make the server understand
360  * SSLv2, SSLv3, and TLSv1 protocol. Use returned SSL_CTX object
361  * to set any custom options before starting the server.
362  *
363  * @code
364  * SSL_CTX *sslctx = ad_server_get_ssl_ctx(server);
365  * SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2);
366  * SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER);
367  * ad_server_start(server);
368  * @endcode
369  */
371  return server->sslctx;
372 }
373 
374 /**
375  * Return internal statistic counter map.
376  */
377 qhashtbl_t *ad_server_get_stats(ad_server_t *server, const char *key) {
378  return server->stats;
379 }
380 
381 /**
382  * Register user hook.
383  */
384 void ad_server_register_hook(ad_server_t *server, ad_callback cb, void *userdata) {
385  ad_server_register_hook_on_method(server, NULL, cb, userdata);
386 }
387 
388 /**
389  * Register user hook on method name.
390  */
391 void ad_server_register_hook_on_method(ad_server_t *server, const char *method, ad_callback cb, void *userdata) {
392  ad_hook_t hook;
393  bzero((void *)&hook, sizeof(ad_hook_t));
394  hook.method = (method) ? strdup(method) : NULL;
395  hook.cb = cb;
396  hook.userdata = userdata;
397 
398  server->hooks->addlast(server->hooks, (void *)&hook, sizeof(ad_hook_t));
399 }
400 
401 /**
402  * Attach userdata into the connection.
403  *
404  * @return previous userdata;
405  */
406 void *ad_conn_set_userdata(ad_conn_t *conn, const void *userdata, ad_userdata_free_cb free_cb) {
407  return set_userdata(conn, 0, userdata, free_cb);
408 }
409 
410 /**
411  * Get userdata attached in the connection.
412  *
413  * @return previous userdata;
414  */
416  return get_userdata(conn, 0);
417 }
418 
419 /**
420  * Set extra userdata into the connection.
421  *
422  * @return previous userdata;
423  *
424  * @note
425  * Extra userdata is for default protocol handler such as ad_http_handler to
426  * provide higher abstraction. End users should always use only ad_conn_set_userdata()
427  * to avoid any conflict with default handlers.
428  */
429 void *ad_conn_set_extra(ad_conn_t *conn, const void *extra, ad_userdata_free_cb free_cb) {
430  return set_userdata(conn, 1, extra, free_cb);
431 }
432 
433 /**
434  * Get extra userdata attached in this connection.
435  */
437  return get_userdata(conn, 1);
438 }
439 
440 /**
441  * Set method name on this connection.
442  *
443  * Once the method name is set, hooks registered by ad_server_register_hook_on_method()
444  * will be called if method name is matching as registered.
445  *
446  * @see ad_server_register_hook_on_method()
447  */
448 char *ad_conn_set_method(ad_conn_t *conn, char *method) {
449  char *prev = conn->method;
450  if (conn->method) {
451  free(conn->method);
452  }
453  conn->method = strdup(method);
454  return prev;
455 }
456 
457 /******************************************************************************
458  * Private internal functions.
459  *****************************************************************************/
460 #ifndef _DOXYGEN_SKIP
461 
462 /**
463  * If there's no event, loopbreak or loopexit call won't work until one more
464  * event arrived. So we use eventfd as a internal notification channel to let
465  * server get out of the loop without waiting for an event.
466  */
467 static int notify_loopexit(ad_server_t *server) {
468  uint64_t x = 0;
469  return bufferevent_write(server->notify_buffer, &x, sizeof(uint64_t));
470 }
471 
472 static void notify_cb(struct bufferevent *buffer, void *userdata) {
473  ad_server_t *server = (ad_server_t *)userdata;
474  event_base_loopexit(server->evbase, NULL);
475  DEBUG("Existing loop.");
476 }
477 
478 static void *server_loop(void *instance) {
479  ad_server_t *server = (ad_server_t *)instance;
480 
481  int *retval = NEW_OBJECT(int);
482  DEBUG("Loop start");
483  event_base_loop(server->evbase, 0);
484  DEBUG("Loop finished");
485  *retval = (event_base_got_break(server->evbase)) ? -1 : 0;
486 
487  return retval;
488 }
489 
490 static void close_server(ad_server_t *server) {
491  DEBUG("Closing server.");
492 
493  if (server->notify_buffer) {
494  bufferevent_free(server->notify_buffer);
495  server->notify_buffer = NULL;
496  }
497 
498  if (server->listener) {
499  evconnlistener_free(server->listener);
500  server->listener = NULL;
501  }
502 
503  if (server->thread) {
504  void *retval = NULL;
505  DEBUG("Waiting server's last loop to finish.");
506  pthread_join(*(server->thread), &retval);
507  free(retval);
508  free(server->thread);
509  server->thread = NULL;
510  }
511  INFO("Server closed.");
512 }
513 
514 static void libevent_log_cb(int severity, const char *msg) {
515  switch(severity) {
516  case _EVENT_LOG_MSG : {
517  INFO("%s", msg);
518  break;
519  }
520  case _EVENT_LOG_WARN : {
521  WARN("%s", msg);
522  break;
523  }
524  case _EVENT_LOG_ERR : {
525  ERROR("%s", msg);
526  break;
527  }
528  default : {
529  DEBUG("%s", msg);
530  break;
531  }
532  }
533 }
534 
535 // Set default options that were not set by user..
536 static int set_undefined_options(ad_server_t *server) {
537  int newentries = 0;
538  char *default_options[][2] = AD_SERVER_OPTIONS;
539  for (int i = 0; ! IS_EMPTY_STR(default_options[i][0]); i++) {
540  if (! ad_server_get_option(server, default_options[i][0])) {
541  ad_server_set_option(server, default_options[i][0], default_options[i][1]);
542  newentries++;
543  }
544  DEBUG("%s=%s", default_options[i][0], ad_server_get_option(server, default_options[i][0]));
545  }
546  return newentries;
547 }
548 
549 static SSL_CTX *init_ssl(const char *cert_path, const char *pkey_path) {
550  // Initialize OpenSSL library.
551  SSL_load_error_strings();
552  SSL_library_init();
553  RAND_poll();
554 
555  SSL_CTX *sslctx = SSL_CTX_new(SSLv23_server_method());
556  if (! SSL_CTX_use_certificate_file(sslctx, cert_path, SSL_FILETYPE_PEM) ||
557  ! SSL_CTX_use_PrivateKey_file(sslctx, pkey_path, SSL_FILETYPE_PEM)) {
558  return NULL;
559  }
560 
561  return sslctx;
562 }
563 
564 static void listener_cb(struct evconnlistener *listener, evutil_socket_t socket,
565  struct sockaddr *sockaddr, int socklen, void *userdata) {
566  DEBUG("New connection.");
567  ad_server_t *server = (ad_server_t *)userdata;
568 
569  // Create a new buffer.
570  struct bufferevent *buffer = NULL;
571  if (server->sslctx) {
572  buffer = bufferevent_openssl_socket_new(server->evbase, socket,
573  SSL_new(server->sslctx),
574  BUFFEREVENT_SSL_ACCEPTING,
575  BEV_OPT_CLOSE_ON_FREE);
576  } else {
577  buffer = bufferevent_socket_new(server->evbase, socket, BEV_OPT_CLOSE_ON_FREE);
578  }
579  if (buffer == NULL) goto error;
580 
581  // Set read timeout.
582  int timeout = ad_server_get_option_int(server, "server.timeout");
583  if (timeout > 0) {
584  struct timeval tm;
585  bzero((void *)&tm, sizeof(struct timeval));
586  tm.tv_sec = timeout;
587  bufferevent_set_timeouts(buffer, &tm, NULL);
588  }
589 
590  // Create a connection.
591  void *conn = conn_new(server, buffer);
592  if (! conn) goto error;
593 
594  return;
595 
596  error:
597  if (buffer) bufferevent_free(buffer);
598  ERROR("Failed to create a connection handler.");
599  event_base_loopbreak(server->evbase);
600  server->errcode = ENOMEM;
601 }
602 
603 static ad_conn_t *conn_new(ad_server_t *server, struct bufferevent *buffer) {
604  if (server == NULL || buffer == NULL) {
605  return NULL;
606  }
607 
608  // Create a new connection container.
609  ad_conn_t *conn = NEW_OBJECT(ad_conn_t);
610  if (conn == NULL) return NULL;
611 
612  // Initialize with default values.
613  conn->server = server;
614  conn->buffer = buffer;
615  conn->in = bufferevent_get_input(buffer);
616  conn->out = bufferevent_get_output(buffer);
617  conn_reset(conn);
618 
619  // Bind callback
620  bufferevent_setcb(buffer, conn_read_cb, conn_write_cb, conn_event_cb, (void *)conn);
621  bufferevent_setwatermark(buffer, EV_WRITE, 0, 0);
622  bufferevent_enable(buffer, EV_WRITE);
623  bufferevent_enable(buffer, EV_READ);
624 
625  // Run callbacks with AD_EVENT_INIT event.
626  conn->status = call_hooks(AD_EVENT_INIT | AD_EVENT_WRITE, conn);
627 
628  return conn;
629 }
630 
631 static void conn_reset(ad_conn_t *conn) {
632  conn->status = AD_OK;
633 
634  for(int i = 0; i < AD_NUM_USERDATA; i++) {
635  if (conn->userdata[i]) {
636  if (conn->userdata_free_cb[i] != NULL) {
637  conn->userdata_free_cb[i](conn, conn->userdata[i]);
638  } else {
639  WARN("Found unreleased userdata.");
640  }
641  conn->userdata[i] = NULL;
642  }
643  }
644 
645  if (conn->method) {
646  free(conn->method);
647  conn->method = NULL;
648  }
649 }
650 
651 static void conn_free(ad_conn_t *conn) {
652  if (conn) {
653  if (conn->status != AD_CLOSE) {
654  call_hooks(AD_EVENT_CLOSE | AD_EVENT_SHUTDOWN , conn);
655  }
656  conn_reset(conn);
657  if (conn->buffer) {
658  if (conn->server->sslctx) {
659  int sslerr = bufferevent_get_openssl_error(conn->buffer);
660  if (sslerr) {
661  char errmsg[256];
662  ERR_error_string_n(sslerr, errmsg, sizeof(errmsg));
663  ERROR("SSL %s (err:%d)", errmsg, sslerr);
664  }
665  }
666  bufferevent_free(conn->buffer);
667  }
668  free(conn);
669  }
670 }
671 
672 #define DRAIN_EVBUFFER(b) evbuffer_drain(b, evbuffer_get_length(b))
673 static void conn_read_cb(struct bufferevent *buffer, void *userdata) {
674  DEBUG("read_cb");
675  ad_conn_t *conn = userdata;
676  conn_cb(conn, AD_EVENT_READ);
677 }
678 
679 static void conn_write_cb(struct bufferevent *buffer, void *userdata) {
680  DEBUG("write_cb");
681  ad_conn_t *conn = userdata;
682  conn_cb(conn, AD_EVENT_WRITE);
683 }
684 
685 static void conn_event_cb(struct bufferevent *buffer, short what, void *userdata) {
686  DEBUG("event_cb 0x%x", what);
687  ad_conn_t *conn = userdata;
688 
689  if (what & BEV_EVENT_EOF || what & BEV_EVENT_ERROR || what & BEV_EVENT_TIMEOUT) {
690  conn->status = AD_CLOSE;
691  conn_cb(conn, AD_EVENT_CLOSE | ((what & BEV_EVENT_TIMEOUT) ? AD_EVENT_TIMEOUT : 0));
692  }
693 }
694 
695 static void conn_cb(ad_conn_t *conn, int event) {
696  DEBUG("conn_cb: status:0x%x, event:0x%x", conn->status, event)
697  if(conn->status == AD_OK || conn->status == AD_TAKEOVER) {
698  int status = call_hooks(event, conn);
699  // Update status only when it's higher then before.
700  if (! (conn->status == AD_CLOSE || (conn->status == AD_DONE && conn->status >= status))) {
701  conn->status = status;
702  }
703  }
704 
705  if(conn->status == AD_DONE) {
706  if (ad_server_get_option_int(conn->server, "server.request_pipelining")) {
707  call_hooks(AD_EVENT_CLOSE , conn);
708  conn_reset(conn);
709  call_hooks(AD_EVENT_INIT , conn);
710  } else {
711  // Do nothing but drain input buffer.
712  if (event == AD_EVENT_READ) {
713  DEBUG("Draining in-buffer. %d", conn->status);
714  DRAIN_EVBUFFER(conn->in);
715  }
716  }
717  return;
718  } else if(conn->status == AD_CLOSE) {
719  if (evbuffer_get_length(conn->out) <= 0) {
720  int newevent = (event & AD_EVENT_CLOSE) ? event : AD_EVENT_CLOSE;
721  call_hooks(newevent, conn);
722  conn_free(conn);
723  DEBUG("Connection closed.");
724  return;
725  }
726  }
727 }
728 
729 static int call_hooks(short event, ad_conn_t *conn) {
730  DEBUG("call_hooks: event 0x%x", event);
731  qlist_t *hooks = conn->server->hooks;
732 
733  qdlobj_t obj;
734  bzero((void *)&obj, sizeof(qdlobj_t));
735  while (hooks->getnext(hooks, &obj, false) == true) {
736  ad_hook_t *hook = (ad_hook_t *)obj.data;
737  if (hook->cb) {
738  if (hook->method && conn->method && strcmp(hook->method, conn->method)) {
739  continue;
740  }
741  int status = hook->cb(event, conn, hook->userdata);
742  if (status != AD_OK) {
743  return status;
744  }
745  }
746  }
747  return AD_OK;
748 }
749 
750 static void *set_userdata(ad_conn_t *conn, int index, const void *userdata, ad_userdata_free_cb free_cb) {
751  void *prev = conn->userdata;
752  conn->userdata[index] = (void *)userdata;
753  conn->userdata_free_cb[index] = free_cb;
754  return prev;
755 }
756 
757 static void *get_userdata(ad_conn_t *conn, int index) {
758  return conn->userdata[index];
759 }
760 
761 #endif _DOXYGEN_SKIP