blob: 8b9771406cd94d16ed4d7314776e573f353e69f5 [file] [log] [blame]
Marius Vlad69e75712019-06-25 13:29:57 +03001/*
2 * Copyright © 2019 Collabora Ltd
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include "config.h"
27
Marius Vladc901e892019-06-21 22:49:18 +030028#include <libweston/weston-log.h>
Pekka Paalanenc232f8d2019-04-05 16:09:45 +030029#include "shared/helpers.h"
Marius Vlad69e75712019-06-25 13:29:57 +030030#include <libweston/libweston.h>
31
32#include "weston-log-internal.h"
33#include "weston-debug-server-protocol.h"
34
35#include <assert.h>
36#include <unistd.h>
37#include <stdarg.h>
38#include <string.h>
39#include <errno.h>
40#include <sys/time.h>
41
42/** A debug stream created by a client
43 *
Marius Vlad967a6c22019-06-21 21:13:15 +030044 * A client provides a file descriptor for the server to write debug messages
45 * into. A weston_log_debug_wayland is associated to one weston_log_scope via the
46 * scope name, and the scope provides the messages. There can be several
47 * streams for the same scope, all streams getting the same messages.
48 *
49 * The following is specific to weston-debug protocol.
50 * Subscription/unsubscription takes place in the stream_create(), respectively
51 * in stream_destroy().
Marius Vlad69e75712019-06-25 13:29:57 +030052 */
Marius Vlad967a6c22019-06-21 21:13:15 +030053struct weston_log_debug_wayland {
Marius Vlad69e75712019-06-25 13:29:57 +030054 struct weston_log_subscriber base;
55 int fd; /**< client provided fd */
56 struct wl_resource *resource; /**< weston_debug_stream_v1 object */
57};
58
Marius Vlad967a6c22019-06-21 21:13:15 +030059static struct weston_log_debug_wayland *
60to_weston_log_debug_wayland(struct weston_log_subscriber *sub)
Marius Vlad69e75712019-06-25 13:29:57 +030061{
Emmanuel Gil Peyroteff793a2021-07-31 17:25:41 +020062 return container_of(sub, struct weston_log_debug_wayland, base);
Marius Vlad69e75712019-06-25 13:29:57 +030063}
64
65static void
Marius Vlad967a6c22019-06-21 21:13:15 +030066stream_close_unlink(struct weston_log_debug_wayland *stream)
Marius Vlad69e75712019-06-25 13:29:57 +030067{
68 if (stream->fd != -1)
69 close(stream->fd);
70 stream->fd = -1;
71}
72
73static void WL_PRINTF(2, 3)
Marius Vlad967a6c22019-06-21 21:13:15 +030074stream_close_on_failure(struct weston_log_debug_wayland *stream,
Marius Vlad69e75712019-06-25 13:29:57 +030075 const char *fmt, ...)
76{
77 char *msg;
78 va_list ap;
79 int ret;
80
81 stream_close_unlink(stream);
82
83 va_start(ap, fmt);
84 ret = vasprintf(&msg, fmt, ap);
85 va_end(ap);
86
87 if (ret > 0) {
88 weston_debug_stream_v1_send_failure(stream->resource, msg);
89 free(msg);
90 } else {
91 weston_debug_stream_v1_send_failure(stream->resource,
92 "MEMFAIL");
93 }
94}
95
96/** Write data into a specific debug stream
97 *
98 * \param sub The subscriber's stream to write into; must not be NULL.
99 * \param[in] data Pointer to the data to write.
100 * \param len Number of bytes to write.
101 *
102 * Writes the given data (binary verbatim) into the debug stream.
103 * If \c len is zero or negative, the write is silently dropped.
104 *
105 * Writing is continued until all data has been written or
106 * a write fails. If the write fails due to a signal, it is re-tried.
107 * Otherwise on failure, the stream is closed and
108 * \c weston_debug_stream_v1.failure event is sent to the client.
109 *
Marius Vlad967a6c22019-06-21 21:13:15 +0300110 * \memberof weston_log_debug_wayland
Marius Vlad69e75712019-06-25 13:29:57 +0300111 */
112static void
Marius Vlad967a6c22019-06-21 21:13:15 +0300113weston_log_debug_wayland_write(struct weston_log_subscriber *sub,
114 const char *data, size_t len)
Marius Vlad69e75712019-06-25 13:29:57 +0300115{
116 ssize_t len_ = len;
117 ssize_t ret;
118 int e;
Marius Vlad967a6c22019-06-21 21:13:15 +0300119 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
Marius Vlad69e75712019-06-25 13:29:57 +0300120
121 if (stream->fd == -1)
122 return;
123
124 while (len_ > 0) {
125 ret = write(stream->fd, data, len_);
126 e = errno;
127 if (ret < 0) {
128 if (e == EINTR)
129 continue;
130
131 stream_close_on_failure(stream,
132 "Error writing %zd bytes: %s (%d)",
133 len_, strerror(e), e);
134 break;
135 }
136
137 len_ -= ret;
138 data += ret;
139 }
140}
141
142/** Close the debug stream and send success event
143 *
144 * \param sub Subscriber's stream to close.
145 *
146 * Closes the debug stream and sends \c weston_debug_stream_v1.complete
147 * event to the client. This tells the client the debug information dump
148 * is complete.
149 *
Marius Vlad967a6c22019-06-21 21:13:15 +0300150 * \memberof weston_log_debug_wayland
Marius Vlad69e75712019-06-25 13:29:57 +0300151 */
152static void
Marius Vlad967a6c22019-06-21 21:13:15 +0300153weston_log_debug_wayland_complete(struct weston_log_subscriber *sub)
Marius Vlad69e75712019-06-25 13:29:57 +0300154{
Marius Vlad967a6c22019-06-21 21:13:15 +0300155 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
Marius Vlad69e75712019-06-25 13:29:57 +0300156
157 stream_close_unlink(stream);
158 weston_debug_stream_v1_send_complete(stream->resource);
159}
160
161static void
Marius Vlad967a6c22019-06-21 21:13:15 +0300162weston_log_debug_wayland_to_destroy(struct weston_log_subscriber *sub)
Marius Vlad69e75712019-06-25 13:29:57 +0300163{
Marius Vlad967a6c22019-06-21 21:13:15 +0300164 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
Leandro Ribeirod65483e2020-02-03 23:45:59 -0300165
166 if (stream->fd != -1)
167 stream_close_on_failure(stream, "debug name removed");
Marius Vlad69e75712019-06-25 13:29:57 +0300168}
169
Marius Vlad967a6c22019-06-21 21:13:15 +0300170static struct weston_log_debug_wayland *
Marius Vlad69e75712019-06-25 13:29:57 +0300171stream_create(struct weston_log_context *log_ctx, const char *name,
172 int32_t streamfd, struct wl_resource *stream_resource)
173{
Marius Vlad967a6c22019-06-21 21:13:15 +0300174 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300175 struct weston_log_scope *scope;
Marius Vlad69e75712019-06-25 13:29:57 +0300176
177 stream = zalloc(sizeof *stream);
178 if (!stream)
179 return NULL;
180
181 stream->fd = streamfd;
182 stream->resource = stream_resource;
183
Marius Vlad967a6c22019-06-21 21:13:15 +0300184 stream->base.write = weston_log_debug_wayland_write;
Leandro Ribeiro1ded6612020-02-06 16:43:51 -0300185 stream->base.destroy = NULL;
Leandro Ribeiro8c02ea12020-02-06 15:41:32 -0300186 stream->base.destroy_subscription = weston_log_debug_wayland_to_destroy;
Marius Vlad967a6c22019-06-21 21:13:15 +0300187 stream->base.complete = weston_log_debug_wayland_complete;
Marius Vlad69e75712019-06-25 13:29:57 +0300188 wl_list_init(&stream->base.subscription_list);
189
Marius Vlad69e75712019-06-25 13:29:57 +0300190 scope = weston_log_get_scope(log_ctx, name);
191 if (scope) {
Marius Vlad5ae0e622019-07-11 17:44:50 +0300192 weston_log_subscription_create(&stream->base, scope);
Marius Vlad69e75712019-06-25 13:29:57 +0300193 } else {
194 stream_close_on_failure(stream,
195 "Debug stream name '%s' is unknown.",
196 name);
197 }
198
199 return stream;
200}
201
202static void
203stream_destroy(struct wl_resource *stream_resource)
204{
Marius Vlad967a6c22019-06-21 21:13:15 +0300205 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300206 stream = wl_resource_get_user_data(stream_resource);
207
Leandro Ribeirod65483e2020-02-03 23:45:59 -0300208 stream_close_unlink(stream);
209 weston_log_subscriber_release(&stream->base);
Marius Vlad69e75712019-06-25 13:29:57 +0300210 free(stream);
211}
212
213static void
214weston_debug_stream_destroy(struct wl_client *client,
215 struct wl_resource *stream_resource)
216{
217 wl_resource_destroy(stream_resource);
218}
219
220static const struct weston_debug_stream_v1_interface
221 weston_debug_stream_impl = {
222 weston_debug_stream_destroy
223};
224
225static void
226weston_debug_destroy(struct wl_client *client,
227 struct wl_resource *global_resource)
228{
229 wl_resource_destroy(global_resource);
230}
231
232static void
233weston_debug_subscribe(struct wl_client *client,
234 struct wl_resource *global_resource,
235 const char *name,
236 int32_t streamfd,
237 uint32_t new_stream_id)
238{
239 struct weston_log_context *log_ctx;
240 struct wl_resource *stream_resource;
241 uint32_t version;
Marius Vlad967a6c22019-06-21 21:13:15 +0300242 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300243
244 log_ctx = wl_resource_get_user_data(global_resource);
245 version = wl_resource_get_version(global_resource);
246
247 stream_resource = wl_resource_create(client,
248 &weston_debug_stream_v1_interface,
249 version, new_stream_id);
250 if (!stream_resource)
251 goto fail;
252
253 stream = stream_create(log_ctx, name, streamfd, stream_resource);
254 if (!stream)
255 goto fail;
256
257 wl_resource_set_implementation(stream_resource,
258 &weston_debug_stream_impl,
259 stream, stream_destroy);
260 return;
261
262fail:
263 close(streamfd);
264 wl_client_post_no_memory(client);
265}
266
267static const struct weston_debug_v1_interface weston_debug_impl = {
268 weston_debug_destroy,
269 weston_debug_subscribe
270};
271
272void
273weston_log_bind_weston_debug(struct wl_client *client,
274 void *data, uint32_t version, uint32_t id)
275{
276 struct weston_log_context *log_ctx = data;
277 struct wl_resource *resource;
278
279 resource = wl_resource_create(client,
280 &weston_debug_v1_interface,
281 version, id);
282 if (!resource) {
283 wl_client_post_no_memory(client);
284 return;
285 }
286 wl_resource_set_implementation(resource, &weston_debug_impl,
287 log_ctx, NULL);
288
289 weston_debug_protocol_advertise_scopes(log_ctx, resource);
290}