c232f8d934
When all shared/ headers are included in the same way, we can drop unnecessary include seach paths from the compiler. This include style was chosen because it is prevalent in the code base. Doing anything different would have been a bigger patch. This also means that we need to keep the project root directory in the include search path, which means that one could accidentally include private headers with #include "libweston/dbus.h" or even #include <libweston/dbus.h> IMO such problem is smaller than the churn caused by any of the alternatives, and we should be able to catch those in review. We might even be able to catch those with grep in CI if necessary. The "bad" include style was found with: $ for h in shared/*.h; do git grep -F $(basename $h); done | grep -vF '"shared/' Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
298 lines
8.0 KiB
C
298 lines
8.0 KiB
C
/*
|
|
* 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);
|
|
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 = 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;
|
|
struct weston_log_subscription *sub = NULL;
|
|
|
|
stream = wl_resource_get_user_data(stream_resource);
|
|
|
|
if (stream->fd != -1)
|
|
close(stream->fd);
|
|
|
|
sub = weston_log_subscriber_get_only_subscription(&stream->base);
|
|
|
|
/* we can have a zero subscription if clients tried to subscribe
|
|
* to a non-existent scope */
|
|
if (sub)
|
|
weston_log_subscription_destroy(sub);
|
|
|
|
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);
|
|
}
|