text: /*
   * Copyright © 2017 Pekka Paalanen <pq@iki.fi>
   * Copyright © 2018 Zodiac Inflight Innovations
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice (including the
   * next paragraph) shall be included in all copies or substantial
   * portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  #include "config.h"
  
  #include <libweston/weston-log.h>
  #include "helpers.h"
  #include <libweston/libweston.h>
  
  #include "weston-log-internal.h"
  #include "weston-debug-server-protocol.h"
  
  #include <assert.h>
  #include <unistd.h>
  #include <stdarg.h>
  #include <string.h>
  #include <errno.h>
  #include <sys/time.h>
  
  /**
   * @defgroup log Public Logging/Debugging API
   * @defgroup internal-log Private/Internal Logging/Debugging API
   * @defgroup debug-protocol weston-debug protocol specific
   */
  
  /** Main weston-log context
   *
   * One per weston_compositor. Stores list of scopes created and a list pending
   * subscriptions.
   *
   * A pending subscription is a subscription to a scope which hasn't been
   * created. When the scope is finally created the pending subscription will be
   * removed from the pending subscription list, but not before was added in the
   * scope's subscription list and that of the subscriber list.
   *
   * Pending subscriptions only make sense for other types of streams, other than
   * those created by weston-debug protocol. In the case of the weston-debug
   * protocol, the subscription processes is done automatically whenever a client
   * connects and subscribes to a scope which was previously advertised by the
   * compositor.
   *
   * @ingroup internal-log
   */
  struct weston_log_context {
  	struct wl_global *global;
  	struct wl_list scope_list; /**< weston_log_scope::compositor_link */
  	struct wl_list pending_subscription_list; /**< weston_log_subscription::source_link */
  };
  
  /** weston-log message scope
   *
   * This is used for scoping logging/debugging messages. Clients can subscribe
   * to only the scopes they are interested in. A scope is identified by its name
   * (also referred to as debug stream name).
   *
   * @ingroup log
   */
  struct weston_log_scope {
  	char *name;
  	char *desc;
  	weston_log_scope_cb new_subscriber;
  	void *user_data;
  	struct wl_list compositor_link;
  	struct wl_list subscription_list;  /**< weston_log_subscription::source_link */
  };
  
  /** Ties a subscriber to a scope
   *
   * A subscription is created each time we'd want to subscribe to a scope. From
   * the stream type we can retrieve the subscriber and from the subscriber we
   * reach each of the streams callbacks. See also weston_log_subscriber object.
   *
   * When a subscription has been created we store it in the scope's subscription
   * list and in the subscriber's subscription list. The subscription might be a
   * pending subscription until the scope for which there's was a subscribe has
   * been created. The scope creation will take of looking through the pending
   * subscription list.
   *
   * A subscription can reached from a subscriber subscription list by using the
   * streams base class.
   *
   * @ingroup internal-log
   */
  struct weston_log_subscription {
  	struct weston_log_subscriber *owner;
  	struct wl_list owner_link;      /**< weston_log_subscriber::subscription_list */
  
  	char *scope_name;
  	struct weston_log_scope *source;
  	struct wl_list source_link;     /**< weston_log_scope::subscription_list  or
  					  weston_log_context::pending_subscription_list */
  };
  
  static struct weston_log_subscription *
  find_pending_subscription(struct weston_log_context *log_ctx,
  			  const char *scope_name)
  {
  	struct weston_log_subscription *sub;
  
  	wl_list_for_each(sub, &log_ctx->pending_subscription_list, source_link)
  		if (!strncmp(sub->scope_name, scope_name, strlen(scope_name)))
  			return sub;
  
  	return NULL;
  }
  
  /** Create a pending subscription and add it the list of pending subscriptions
   *
   * @param owner a subscriber represented by weston_log_subscriber object
   * @param scope_name the name of the scope (which we don't have in the list of scopes)
   * @param log_ctx the logging context used to add the pending subscription
   *
   * @memberof weston_log_subscription
   */
  static void
  weston_log_subscription_create_pending(struct weston_log_subscriber *owner,
  				       const char *scope_name,
  				       struct weston_log_context *log_ctx)
  {
  	assert(owner);
  	assert(scope_name);
  	struct weston_log_subscription *sub = zalloc(sizeof(*sub));
  
  	if (!sub)
  		return;
  
  	sub->scope_name = strdup(scope_name);
  	sub->owner = owner;
  
  	wl_list_insert(&log_ctx->pending_subscription_list, &sub->source_link);
  }
  
  /** Destroys the pending subscription created previously with
   * weston_log_subscription_create_pending()
   *
   * @param sub the weston_log_subscription object to remove from the list
   * of subscriptions and to destroy the subscription
   *
   * @memberof weston_log_subscription
   */
  static void
  weston_log_subscription_destroy_pending(struct weston_log_subscription *sub)
  {
  	assert(sub);
  	/* pending subsriptions do not have a source */
  	wl_list_remove(&sub->source_link);
  	free(sub->scope_name);
  	free(sub);
  }
  
  /** Write to the stream's subscription
   *
   * @memberof weston_log_subscription
   */
  static void
  weston_log_subscription_write(struct weston_log_subscription *sub,
  			      const char *data, size_t len)
  {
  	if (sub->owner && sub->owner->write)
  		sub->owner->write(sub->owner, data, len);
  }
  
  /** Write a formatted string to the stream's subscription
   *
   * @memberof weston_log_subscription
   */
  static void
  weston_log_subscription_vprintf(struct weston_log_subscription *sub,
  				const char *fmt, va_list ap)
  {
  	static const char oom[] = "Out of memory";
  	char *str;
  	int len;
  
  	if (!weston_log_scope_is_enabled(sub->source))
  		return;
  
  	len = vasprintf(&str, fmt, ap);
  	if (len >= 0) {
  		weston_log_subscription_write(sub, str, len);
  		free(str);
  	} else {
  		weston_log_subscription_write(sub, oom, sizeof oom - 1);
  	}
  }
  
  /** Creates a new subscription using the subscriber by \c owner.
   *
   * The subscription created is added to the \c owner subscription list.
   * Destroying the subscription using weston_log_subscription_destroy() will
   * remove the link from the subscription list and free storage alloc'ed.
   *
   * Note that this adds the subscription to the scope's subscription list
   * hence the \c scope required argument.
   *
   * @param owner the subscriber owner, must be created before creating a
   * subscription
   * @param scope the scope in order to add the subscription to the scope's
   * subscription list
   * @returns a weston_log_subscription object in case of success, or NULL
   * otherwise
   *
   * @sa weston_log_subscription_destroy, weston_log_subscription_remove,
   * weston_log_subscription_add
   * @memberof weston_log_subscription
   */
  void
  weston_log_subscription_create(struct weston_log_subscriber *owner,
  			       struct weston_log_scope *scope)
  {
  	struct weston_log_subscription *sub;
  	assert(owner);
  	assert(scope);
  	assert(scope->name);
  
  	sub = zalloc(sizeof(*sub));
  	if (!sub)
  		return;
  
  	sub->owner = owner;
  	sub->scope_name = strdup(scope->name);
  
  	wl_list_insert(&sub->owner->subscription_list, &sub->owner_link);
  
  	weston_log_subscription_add(scope, sub);
  	weston_log_run_cb_new_subscriber(sub);
  }
  
  /** Destroys the subscription
   *
   * Removes the subscription from the scopes subscription list and from
   * subscriber's subscription list. It destroys the subscription afterwads.
   *
   * @memberof weston_log_subscription
   */
  void
  weston_log_subscription_destroy(struct weston_log_subscription *sub)
  {
  	assert(sub);
  	if (sub->owner)
  		wl_list_remove(&sub->owner_link);
  
  	weston_log_subscription_remove(sub);
  	free(sub->scope_name);
  	free(sub);
  }
  
  /** Retrieve a subscription by using the subscriber
   *
   * This is useful when trying to find a subscription from the subscriber by
   * having only access to the stream.
   *
   * @param subscriber the subscriber in question
   * @returns a weston_log_subscription object
   *
   * @memberof weston_log_subscription
   */
  struct weston_log_subscription *
  weston_log_subscriber_get_only_subscription(struct weston_log_subscriber *subscriber)
  {
  	struct weston_log_subscription *sub;
  	/* unlikely, but can happen */
  	if (wl_list_length(&subscriber->subscription_list) == 0)
  		return NULL;
  
  	assert(wl_list_length(&subscriber->subscription_list) == 1);
  
  	return wl_container_of(subscriber->subscription_list.prev,
  			       sub, owner_link);
  }
  
  /** Adds the subscription \c sub to the subscription list of the
   * scope.
   *
   * This should used when the scope has been created, and the subscription \c
   * sub has be created before calling this function.
   *
   * @param scope the scope
   * @param sub the subscription, it must be created before, see
   * weston_log_subscription_create()
   *
   * @memberof weston_log_subscription
   */
  void
  weston_log_subscription_add(struct weston_log_scope *scope,
  			    struct weston_log_subscription *sub)
  {
  	assert(scope);
  	assert(sub);
  	/* don't allow subscriptions to have a source already! */
  	assert(!sub->source);
  
  	sub->source = scope;
  	wl_list_insert(&scope->subscription_list, &sub->source_link);
  }
  
  /** Removes the subscription from the scope's subscription list
   *
   * @memberof weston_log_subscription
   */
  void
  weston_log_subscription_remove(struct weston_log_subscription *sub)
  {
  	assert(sub);
  	if (sub->source)
  		wl_list_remove(&sub->source_link);
  	sub->source = NULL;
  }
  
  /** Look-up the scope from the scope list  stored in the log context, by
   * matching against the \c name.
   *
   * @param log_ctx
   * @param name the scope name, see weston_compositor_add_log_scope()
   * @returns NULL if none found, or a pointer to a weston_log_scope
   *
   * @ingroup internal-log
   */
  struct weston_log_scope *
  weston_log_get_scope(struct weston_log_context *log_ctx, const char *name)
  {
  	struct weston_log_scope *scope;
  	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
  		if (strcmp(name, scope->name) == 0)
  			return scope;
  	return NULL;
  }
  
  /** Wrapper to invoke the weston_log_scope_cb. Allows to call the cb new_subscriber of
   * a log scope.
   *
   * @ingroup internal-log
   */
  void
  weston_log_run_cb_new_subscriber(struct weston_log_subscription *sub)
  {
  	if (sub->source->new_subscriber)
  		sub->source->new_subscriber(sub, sub->source->user_data);
  }
  
  /** Advertise the log scope name and the log scope description
   *
   * This is only used by the weston-debug protocol!
   *
   * @ingroup internal-log
   */
  void
  weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx,
  				       struct wl_resource *res)
  {
  	struct weston_log_scope *scope;
  	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
  		weston_debug_v1_send_available(res, scope->name, scope->desc);
  }
  
  /**
   * Connect weston_compositor structure to weston_log_context structure.
   *
   * \param compositor
   * \param log_ctx
   * \return 0 on success, -1 on failure
   *
   * Sets weston_compositor::weston_log_ctx.
   *
   * @ingroup log
   */
  int
  weston_log_ctx_compositor_setup(struct weston_compositor *compositor,
  			      struct weston_log_context *log_ctx)
  {
  	assert(!compositor->weston_log_ctx);
  	assert(log_ctx);
  
  	compositor->weston_log_ctx = log_ctx;
  	return 0;
  }
  
  /** Creates  weston_log_context structure
   *
   * \return NULL in case of failure, or a weston_log_context object in case of
   * success
   *
   * weston_log_context is a singleton for each weston_compositor.
   * @ingroup log
   *
   */
  WL_EXPORT struct weston_log_context *
  weston_log_ctx_compositor_create(void)
  {
  	struct weston_log_context *log_ctx;
  
  	log_ctx = zalloc(sizeof *log_ctx);
  	if (!log_ctx)
  		return NULL;
  
  	wl_list_init(&log_ctx->scope_list);
  	wl_list_init(&log_ctx->pending_subscription_list);
  
  	return log_ctx;
  }
  
  /** Destroy weston_log_context structure
   *
   * \param compositor The libweston compositor whose weston-debug to tear down.
   *
   * Clears weston_compositor::weston_log_ctx.
   * @ingroup log
   *
   */
  WL_EXPORT void
  weston_log_ctx_compositor_destroy(struct weston_compositor *compositor)
  {
  	struct weston_log_context *log_ctx = compositor->weston_log_ctx;
  	struct weston_log_scope *scope;
  	struct weston_log_subscription *pending_sub, *pending_sub_tmp;
  
  	if (log_ctx->global)
  		wl_global_destroy(log_ctx->global);
  
  	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
  		fprintf(stderr, "Internal warning: debug scope '%s' has not been destroyed.\n",
  			   scope->name);
  
  	/* Remove head to not crash if scope removed later. */
  	wl_list_remove(&log_ctx->scope_list);
  
  	/* Remove any pending subscription(s) which nobody subscribed to */
  	wl_list_for_each_safe(pending_sub, pending_sub_tmp,
  			      &log_ctx->pending_subscription_list, source_link) {
  		weston_log_subscription_destroy_pending(pending_sub);
  	}
  
  	/* pending_subscription_list should be empty at this point */
  
  	free(log_ctx);
  
  	compositor->weston_log_ctx = NULL;
  }
  
  /** Enable weston-debug protocol extension
   *
   * \param compositor The libweston compositor where to enable.
   *
   * This enables the weston_debug_v1 Wayland protocol extension which any client
   * can use to get debug messages from the compositor.
   *
   * WARNING: This feature should not be used in production. If a client
   * provides a file descriptor that blocks writes, it will block the whole
   * compositor indefinitely.
   *
   * There is no control on which client is allowed to subscribe to debug
   * messages. Any and all clients are allowed.
   *
   * The debug extension is disabled by default, and once enabled, cannot be
   * disabled again.
   *
   * @ingroup debug-protocol
   */
  WL_EXPORT void
  weston_compositor_enable_debug_protocol(struct weston_compositor *compositor)
  {
  	struct weston_log_context *log_ctx = compositor->weston_log_ctx;
  	assert(log_ctx);
  	if (log_ctx->global)
  		return;
  
  	log_ctx->global = wl_global_create(compositor->wl_display,
  				       &weston_debug_v1_interface, 1,
  				       log_ctx, weston_log_bind_weston_debug);
  	if (!log_ctx->global)
  		return;
  
  	fprintf(stderr, "WARNING: debug protocol has been enabled. "
  		   "This is a potential denial-of-service attack vector and "
  		   "information leak.\n");
  }
  
  /** Determine if the debug protocol has been enabled
   *
   * \param wc The libweston compositor to verify if debug protocol has been
   * enabled
   *
   * @ingroup debug-protocol
   */
  WL_EXPORT bool
  weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc)
  {
  	return wc->weston_log_ctx->global != NULL;
  }
  
  /** Register a new stream name, creating a log scope.
   *
   * @param log_ctx The weston_log_context where to add.
   * @param name The debug stream/scope name; must not be NULL.
   * @param description The log scope description for humans; must not be NULL.
   * @param new_subscriber Optional callback when a client subscribes to this
   * scope.
   * @param user_data Optional user data pointer for the callback.
   * @returns A valid pointer on success, NULL on failure.
   *
   * This function is used to create a log scope. All debug message printing
   * happens for a scope, which allows clients to subscribe to the kind of debug
   * messages they want by \c name. For the weston-debug protocol,
   * subscription for the scope will happen automatically but for other types of
   * streams, weston_log_subscribe() should be called as to create a subscription
   * and tie it to the scope created by this function.
   *
   * \p name must be unique in the weston_compositor instance. \p name
   * and \p description must both be provided. In case of the weston-debug
   * protocol, the description is printed when a client asks for a list of
   * supported log scopes.
   *
   * \p new_subscriber, if not NULL, is called when a client subscribes to the log
   * scope creating a debug stream. This is for log scopes that need to print
   * messages as a response to a client appearing, e.g. printing a list of
   * windows on demand or a static preamble. The argument \p user_data is
   * passed in to the callback and is otherwise unused.
   *
   * For one-shot debug streams, \c new_subscriber should finally call
   * weston_log_subscription_complete() to close the stream and tell the client
   * the printing is complete. Otherwise the client expects more data to be
   * written.  The complete callback in weston_log_subscriber should be installed
   * to trigger it and it is set-up automatically for the weston-debug protocol.
   *
   * As subscription can take place before creating the scope, any pending
   * subscriptions to scope added by weston_log_subscribe(), will be checked
   * against the scope being created and if found will be added to the scope's
   * subscription list.
   *
   * The log scope must be destroyed using weston_compositor_log_scope_destroy()
   * before destroying the weston_compositor.
   *
   * @memberof weston_log_scope
   * @sa weston_log_scope_cb, weston_log_subscribe
   */
  WL_EXPORT struct weston_log_scope *
  weston_compositor_add_log_scope(struct weston_log_context *log_ctx,
  				const char *name,
  				const char *description,
  				weston_log_scope_cb new_subscriber,
  				void *user_data)
  {
  	struct weston_log_scope *scope;
  	struct weston_log_subscription *pending_sub = NULL;
  
  	if (!name || !description) {
  		fprintf(stderr, "Error: cannot add a debug scope without name or description.\n");
  		return NULL;
  	}
  
  	if (!log_ctx) {
  		fprintf(stderr, "Error: cannot add debug scope '%s', infra not initialized.\n",
  			   name);
  		return NULL;
  	}
  
  	if (weston_log_get_scope(log_ctx, name)){
  		fprintf(stderr, "Error: debug scope named '%s' is already registered.\n",
  			   name);
  		return NULL;
  	}
  
  	scope = zalloc(sizeof *scope);
  	if (!scope) {
  		fprintf(stderr, "Error adding debug scope '%s': out of memory.\n",
  			   name);
  		return NULL;
  	}
  
  	scope->name = strdup(name);
  	scope->desc = strdup(description);
  	scope->new_subscriber = new_subscriber;
  	scope->user_data = user_data;
  	wl_list_init(&scope->subscription_list);
  
  	if (!scope->name || !scope->desc) {
  		fprintf(stderr, "Error adding debug scope '%s': out of memory.\n",
  			   name);
  		free(scope->name);
  		free(scope->desc);
  		free(scope);
  		return NULL;
  	}
  
  	wl_list_insert(log_ctx->scope_list.prev, &scope->compositor_link);
  
  	/* check if there are any pending subscriptions to this scope */
  	while ((pending_sub = find_pending_subscription(log_ctx, scope->name)) != NULL) {
  		weston_log_subscription_create(pending_sub->owner, scope);
  
  		/* remove it from pending */
  		weston_log_subscription_destroy_pending(pending_sub);
  	}
  
  
  	return scope;
  }
  
  /** Destroy a log scope
   *
   * @param scope The log scope to destroy; may be NULL.
   *
   * Destroys the log scope, calling each stream's destroy callback if one was
   * installed/created.
   *
   * @memberof weston_log_scope
   */
  WL_EXPORT void
  weston_compositor_log_scope_destroy(struct weston_log_scope *scope)
  {
  	struct weston_log_subscription *sub, *sub_tmp;
  
  	if (!scope)
  		return;
  
  	wl_list_for_each_safe(sub, sub_tmp, &scope->subscription_list, source_link) {
  		/* destroy each subscription */
  		if (sub->owner->destroy)
  			sub->owner->destroy(sub->owner);
  
  		weston_log_subscription_destroy(sub);
  	}
  
  	wl_list_remove(&scope->compositor_link);
  	free(scope->name);
  	free(scope->desc);
  	free(scope);
  }
  
  /** Are there any active subscriptions to the scope?
   *
   * \param scope The log scope to check; may be NULL.
   * \return True if any streams are open for this scope, false otherwise.
   *
   * As printing some debugging messages may be relatively expensive, one
   * can use this function to determine if there is a need to gather the
   * debugging information at all. If this function returns false, all
   * printing for this scope is dropped, so gathering the information is
   * pointless.
   *
   * The return value of this function should not be stored, as new clients
   * may subscribe to the debug scope later.
   *
   * If the given scope is NULL, this function will always return false,
   * making it safe to use in teardown or destroy code, provided the
   * scope is initialized to NULL before creation and set to NULL after
   * destruction.
   *
   * \memberof weston_log_scope
   */
  WL_EXPORT bool
  weston_log_scope_is_enabled(struct weston_log_scope *scope)
  {
  	if (!scope)
  		return false;
  
  	return !wl_list_empty(&scope->subscription_list);
  }
  
  /** Close the stream's complete callback if one was installed/created.
   *
   * @ingroup log
   */
  WL_EXPORT void
  weston_log_subscription_complete(struct weston_log_subscription *sub)
  {
  	if (sub->owner && sub->owner->complete)
  		sub->owner->complete(sub->owner);
  }
  
  /** Close the log scope.
   *
   * @param scope The log scope to complete; may be NULL.
   *
   * Complete the log scope, calling each stream's complete callback if one was
   * installed/created. This can be useful to signal the reading end that the
   * data has been transmited and should no longer expect that written over the
   * stream. Particularly useful for the weston-debug protocol.
   *
   * @memberof weston_log_scope
   * @sa weston_compositor_add_log_scope, weston_compositor_log_scope_destroy
   */
  WL_EXPORT void
  weston_log_scope_complete(struct weston_log_scope *scope)
  {
  	struct weston_log_subscription *sub;
  
  	if (!scope)
  		return;
  
  	wl_list_for_each(sub, &scope->subscription_list, source_link)
  		weston_log_subscription_complete(sub);
  }
  
  /** Write log data for a scope
   *
   * \param scope The debug scope to write for; may be NULL, in which case
   *              nothing will be written.
   * \param[in] data Pointer to the data to write.
   * \param len Number of bytes to write.
   *
   * Writes the given data to all subscribed clients' streams.
   *
   * \memberof weston_log_scope
   */
  WL_EXPORT void
  weston_log_scope_write(struct weston_log_scope *scope,
  		       const char *data, size_t len)
  {
  	struct weston_log_subscription *sub;
  
  	if (!scope)
  		return;
  
  	wl_list_for_each(sub, &scope->subscription_list, source_link)
  		weston_log_subscription_write(sub, data, len);
  }
  
  /** Write a formatted string for a scope (varargs)
   *
   * \param scope The log scope to write for; may be NULL, in which case
   *              nothing will be written.
   * \param fmt Printf-style format string.
   * \param ap Formatting arguments.
   *
   * Writes to formatted string to all subscribed clients' streams.
   *
   * The behavioral details for each stream are the same as for
   * weston_debug_stream_write().
   *
   * \memberof weston_log_scope
   */
  WL_EXPORT int
  weston_log_scope_vprintf(struct weston_log_scope *scope,
  			 const char *fmt, va_list ap)
  {
  	static const char oom[] = "Out of memory";
  	char *str;
  	int len = 0;
  
  	if (!weston_log_scope_is_enabled(scope))
  		return len;
  
  	len = vasprintf(&str, fmt, ap);
  	if (len >= 0) {
  		weston_log_scope_write(scope, str, len);
  		free(str);
  	} else {
  		weston_log_scope_write(scope, oom, sizeof oom - 1);
  	}
  
  	return len;
  }
  
  /** Write a formatted string for a scope
   *
   * \param scope The log scope to write for; may be NULL, in which case
   *              nothing will be written.
   * \param fmt Printf-style format string and arguments.
   *
   * Writes to formatted string to all subscribed clients' streams.
   *
   * The behavioral details for each stream are the same as for
   * weston_debug_stream_write().
   *
   * \memberof weston_log_scope
   */
  WL_EXPORT int
  weston_log_scope_printf(struct weston_log_scope *scope,
  			  const char *fmt, ...)
  {
  	va_list ap;
  	int len;
  
  	va_start(ap, fmt);
  	len = weston_log_scope_vprintf(scope, fmt, ap);
  	va_end(ap);
  
  	return len;
  }
  
  /** Write a formatted string for a subscription
   *
   * \param sub The subscription to write for; may be NULL, in which case
   *              nothing will be written.
   * \param fmt Printf-style format string and arguments.
   *
   * Writes to formatted string to the stream that created the subscription.
   *
   * @ingroup log
   */
  WL_EXPORT void
  weston_log_subscription_printf(struct weston_log_subscription *sub,
  			       const char *fmt, ...)
  {
  	va_list ap;
  
  	va_start(ap, fmt);
  	weston_log_subscription_vprintf(sub, fmt, ap);
  	va_end(ap);
  }
  
  /** Write debug scope name and current time into string
   *
   * \param[in] scope debug scope; may be NULL
   * \param[out] buf Buffer to store the string.
   * \param len Available size in the buffer in bytes.
   * \return \c buf
   *
   * Reads the current local wall-clock time and formats it into a string.
   * and append the debug scope name to it, if a scope is available.
   * The string is NUL-terminated, even if truncated.
   *
   * @memberof weston_log_scope
   */
  WL_EXPORT char *
  weston_log_scope_timestamp(struct weston_log_scope *scope,
  			   char *buf, size_t len)
  {
  	struct timeval tv;
  	struct tm *bdt;
  	char string[128];
  	size_t ret = 0;
  
  	gettimeofday(&tv, NULL);
  
  	bdt = localtime(&tv.tv_sec);
  	if (bdt)
  		ret = strftime(string, sizeof string,
  			       "%Y-%m-%d %H:%M:%S", bdt);
  
  	if (ret > 0) {
  		snprintf(buf, len, "[%s.%03ld][%s]", string,
  			 tv.tv_usec / 1000,
  			 (scope) ? scope->name : "no scope");
  	} else {
  		snprintf(buf, len, "[?][%s]",
  			 (scope) ? scope->name : "no scope");
  	}
  
  	return buf;
  }
  
  /** Subscribe to a scope
   *
   * Creates a subscription which is used to subscribe the \p subscriber
   * to the scope \c scope_name.
   *
   * If \c scope_name has already been created (using
   * weston_compositor_add_log_scope) the subscription will take place
   * immediately, otherwise we store the subscription into a pending list. See
   * also weston_compositor_add_log_scope().
   *
   * @param log_ctx the log context, used for accessing pending list
   * @param subscriber the subscriber, which has to be created before
   * @param scope_name the scope name. In case the scope is not created
   * we temporarily store the subscription in the pending list.
   *
   * @ingroup log
   */
  WL_EXPORT void
  weston_log_subscribe(struct weston_log_context *log_ctx,
  		     struct weston_log_subscriber *subscriber,
  		     const char *scope_name)
  {
  	assert(log_ctx);
  	assert(subscriber);
  	assert(scope_name);
  
  	struct weston_log_scope *scope;
  
  	scope = weston_log_get_scope(log_ctx, scope_name);
  	if (scope)
  		weston_log_subscription_create(subscriber, scope);
  	else
  		/*
  		 * if we don't have already as scope for it, add it to pending
  		 * subscription list
  		 */
  		weston_log_subscription_create_pending(subscriber, scope_name, log_ctx);
  }
  