blob: 732439f2cce8fdd1745346a67b252aef60d88445 [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{
Marius Vlad967a6c22019-06-21 21:13:15 +030062 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);
Marius Vlad69e75712019-06-25 13:29:57 +0300165 stream_close_on_failure(stream, "debug name removed");
166}
167
Marius Vlad967a6c22019-06-21 21:13:15 +0300168static struct weston_log_debug_wayland *
Marius Vlad69e75712019-06-25 13:29:57 +0300169stream_create(struct weston_log_context *log_ctx, const char *name,
170 int32_t streamfd, struct wl_resource *stream_resource)
171{
Marius Vlad967a6c22019-06-21 21:13:15 +0300172 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300173 struct weston_log_scope *scope;
Marius Vlad69e75712019-06-25 13:29:57 +0300174
175 stream = zalloc(sizeof *stream);
176 if (!stream)
177 return NULL;
178
179 stream->fd = streamfd;
180 stream->resource = stream_resource;
181
Marius Vlad967a6c22019-06-21 21:13:15 +0300182 stream->base.write = weston_log_debug_wayland_write;
Leandro Ribeiro1ded6612020-02-06 16:43:51 -0300183 stream->base.destroy = NULL;
Leandro Ribeiro8c02ea12020-02-06 15:41:32 -0300184 stream->base.destroy_subscription = weston_log_debug_wayland_to_destroy;
Marius Vlad967a6c22019-06-21 21:13:15 +0300185 stream->base.complete = weston_log_debug_wayland_complete;
Marius Vlad69e75712019-06-25 13:29:57 +0300186 wl_list_init(&stream->base.subscription_list);
187
Marius Vlad69e75712019-06-25 13:29:57 +0300188 scope = weston_log_get_scope(log_ctx, name);
189 if (scope) {
Marius Vlad5ae0e622019-07-11 17:44:50 +0300190 weston_log_subscription_create(&stream->base, scope);
Marius Vlad69e75712019-06-25 13:29:57 +0300191 } else {
192 stream_close_on_failure(stream,
193 "Debug stream name '%s' is unknown.",
194 name);
195 }
196
197 return stream;
198}
199
200static void
201stream_destroy(struct wl_resource *stream_resource)
202{
Marius Vlad967a6c22019-06-21 21:13:15 +0300203 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300204 struct weston_log_subscription *sub = NULL;
205
206 stream = wl_resource_get_user_data(stream_resource);
207
208 if (stream->fd != -1)
209 close(stream->fd);
210
211 sub = weston_log_subscriber_get_only_subscription(&stream->base);
Marius Vlad5ae0e622019-07-11 17:44:50 +0300212
213 /* we can have a zero subscription if clients tried to subscribe
214 * to a non-existent scope */
215 if (sub)
216 weston_log_subscription_destroy(sub);
Marius Vlad69e75712019-06-25 13:29:57 +0300217
218 free(stream);
219}
220
221static void
222weston_debug_stream_destroy(struct wl_client *client,
223 struct wl_resource *stream_resource)
224{
225 wl_resource_destroy(stream_resource);
226}
227
228static const struct weston_debug_stream_v1_interface
229 weston_debug_stream_impl = {
230 weston_debug_stream_destroy
231};
232
233static void
234weston_debug_destroy(struct wl_client *client,
235 struct wl_resource *global_resource)
236{
237 wl_resource_destroy(global_resource);
238}
239
240static void
241weston_debug_subscribe(struct wl_client *client,
242 struct wl_resource *global_resource,
243 const char *name,
244 int32_t streamfd,
245 uint32_t new_stream_id)
246{
247 struct weston_log_context *log_ctx;
248 struct wl_resource *stream_resource;
249 uint32_t version;
Marius Vlad967a6c22019-06-21 21:13:15 +0300250 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300251
252 log_ctx = wl_resource_get_user_data(global_resource);
253 version = wl_resource_get_version(global_resource);
254
255 stream_resource = wl_resource_create(client,
256 &weston_debug_stream_v1_interface,
257 version, new_stream_id);
258 if (!stream_resource)
259 goto fail;
260
261 stream = stream_create(log_ctx, name, streamfd, stream_resource);
262 if (!stream)
263 goto fail;
264
265 wl_resource_set_implementation(stream_resource,
266 &weston_debug_stream_impl,
267 stream, stream_destroy);
268 return;
269
270fail:
271 close(streamfd);
272 wl_client_post_no_memory(client);
273}
274
275static const struct weston_debug_v1_interface weston_debug_impl = {
276 weston_debug_destroy,
277 weston_debug_subscribe
278};
279
280void
281weston_log_bind_weston_debug(struct wl_client *client,
282 void *data, uint32_t version, uint32_t id)
283{
284 struct weston_log_context *log_ctx = data;
285 struct wl_resource *resource;
286
287 resource = wl_resource_create(client,
288 &weston_debug_v1_interface,
289 version, id);
290 if (!resource) {
291 wl_client_post_no_memory(client);
292 return;
293 }
294 wl_resource_set_implementation(resource, &weston_debug_impl,
295 log_ctx, NULL);
296
297 weston_debug_protocol_advertise_scopes(log_ctx, resource);
298}