weston/libweston/weston-log-wayland.c

291 lines
7.8 KiB
C
Raw Permalink Normal View History

/*
* Copyright © 2019 Collabora Ltd
*
* 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 "shared/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>
/** A debug stream created by a client
*
* A client provides a file descriptor for the server to write debug messages
* into. A weston_log_debug_wayland is associated to one weston_log_scope via the
* scope name, and the scope provides the messages. There can be several
* streams for the same scope, all streams getting the same messages.
*
* The following is specific to weston-debug protocol.
* Subscription/unsubscription takes place in the stream_create(), respectively
* in stream_destroy().
*/
struct weston_log_debug_wayland {
struct weston_log_subscriber base;
int fd; /**< client provided fd */
struct wl_resource *resource; /**< weston_debug_stream_v1 object */
};
static struct weston_log_debug_wayland *
to_weston_log_debug_wayland(struct weston_log_subscriber *sub)
{
return container_of(sub, struct weston_log_debug_wayland, base);
}
static void
stream_close_unlink(struct weston_log_debug_wayland *stream)
{
if (stream->fd != -1)
close(stream->fd);
stream->fd = -1;
}
static void WL_PRINTF(2, 3)
stream_close_on_failure(struct weston_log_debug_wayland *stream,
const char *fmt, ...)
{
char *msg;
va_list ap;
int ret;
stream_close_unlink(stream);
va_start(ap, fmt);
ret = vasprintf(&msg, fmt, ap);
va_end(ap);
if (ret > 0) {
weston_debug_stream_v1_send_failure(stream->resource, msg);
free(msg);
} else {
weston_debug_stream_v1_send_failure(stream->resource,
"MEMFAIL");
}
}
/** Write data into a specific debug stream
*
* \param sub The subscriber's stream to write into; must not be NULL.
* \param[in] data Pointer to the data to write.
* \param len Number of bytes to write.
*
* Writes the given data (binary verbatim) into the debug stream.
* If \c len is zero or negative, the write is silently dropped.
*
* Writing is continued until all data has been written or
* a write fails. If the write fails due to a signal, it is re-tried.
* Otherwise on failure, the stream is closed and
* \c weston_debug_stream_v1.failure event is sent to the client.
*
* \memberof weston_log_debug_wayland
*/
static void
weston_log_debug_wayland_write(struct weston_log_subscriber *sub,
const char *data, size_t len)
{
ssize_t len_ = len;
ssize_t ret;
int e;
struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
if (stream->fd == -1)
return;
while (len_ > 0) {
ret = write(stream->fd, data, len_);
e = errno;
if (ret < 0) {
if (e == EINTR)
continue;
stream_close_on_failure(stream,
"Error writing %zd bytes: %s (%d)",
len_, strerror(e), e);
break;
}
len_ -= ret;
data += ret;
}
}
/** Close the debug stream and send success event
*
* \param sub Subscriber's stream to close.
*
* Closes the debug stream and sends \c weston_debug_stream_v1.complete
* event to the client. This tells the client the debug information dump
* is complete.
*
* \memberof weston_log_debug_wayland
*/
static void
weston_log_debug_wayland_complete(struct weston_log_subscriber *sub)
{
struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
stream_close_unlink(stream);
weston_debug_stream_v1_send_complete(stream->resource);
}
static void
weston_log_debug_wayland_to_destroy(struct weston_log_subscriber *sub)
{
struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
if (stream->fd != -1)
stream_close_on_failure(stream, "debug name removed");
}
static struct weston_log_debug_wayland *
stream_create(struct weston_log_context *log_ctx, const char *name,
int32_t streamfd, struct wl_resource *stream_resource)
{
struct weston_log_debug_wayland *stream;
struct weston_log_scope *scope;
stream = zalloc(sizeof *stream);
if (!stream)
return NULL;
stream->fd = streamfd;
stream->resource = stream_resource;
stream->base.write = weston_log_debug_wayland_write;
stream->base.destroy = NULL;
stream->base.destroy_subscription = weston_log_debug_wayland_to_destroy;
stream->base.complete = weston_log_debug_wayland_complete;
wl_list_init(&stream->base.subscription_list);
scope = weston_log_get_scope(log_ctx, name);
if (scope) {
weston_log_subscription_create(&stream->base, scope);
} else {
stream_close_on_failure(stream,
"Debug stream name '%s' is unknown.",
name);
}
return stream;
}
static void
stream_destroy(struct wl_resource *stream_resource)
{
struct weston_log_debug_wayland *stream;
stream = wl_resource_get_user_data(stream_resource);
stream_close_unlink(stream);
weston_log_subscriber_release(&stream->base);
free(stream);
}
static void
weston_debug_stream_destroy(struct wl_client *client,
struct wl_resource *stream_resource)
{
wl_resource_destroy(stream_resource);
}
static const struct weston_debug_stream_v1_interface
weston_debug_stream_impl = {
weston_debug_stream_destroy
};
static void
weston_debug_destroy(struct wl_client *client,
struct wl_resource *global_resource)
{
wl_resource_destroy(global_resource);
}
static void
weston_debug_subscribe(struct wl_client *client,
struct wl_resource *global_resource,
const char *name,
int32_t streamfd,
uint32_t new_stream_id)
{
struct weston_log_context *log_ctx;
struct wl_resource *stream_resource;
uint32_t version;
struct weston_log_debug_wayland *stream;
log_ctx = wl_resource_get_user_data(global_resource);
version = wl_resource_get_version(global_resource);
stream_resource = wl_resource_create(client,
&weston_debug_stream_v1_interface,
version, new_stream_id);
if (!stream_resource)
goto fail;
stream = stream_create(log_ctx, name, streamfd, stream_resource);
if (!stream)
goto fail;
wl_resource_set_implementation(stream_resource,
&weston_debug_stream_impl,
stream, stream_destroy);
return;
fail:
close(streamfd);
wl_client_post_no_memory(client);
}
static const struct weston_debug_v1_interface weston_debug_impl = {
weston_debug_destroy,
weston_debug_subscribe
};
void
weston_log_bind_weston_debug(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
{
struct weston_log_context *log_ctx = data;
struct wl_resource *resource;
resource = wl_resource_create(client,
&weston_debug_v1_interface,
version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &weston_debug_impl,
log_ctx, NULL);
weston_debug_protocol_advertise_scopes(log_ctx, resource);
}