blob: 5dffabb1b5511b574bdbeee187eec0de39b2b562 [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
28#include <libweston/weston-debug.h>
29#include "helpers.h"
30#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;
174 struct weston_log_subscription *sub;
175
176 stream = zalloc(sizeof *stream);
177 if (!stream)
178 return NULL;
179
180 stream->fd = streamfd;
181 stream->resource = stream_resource;
182
Marius Vlad967a6c22019-06-21 21:13:15 +0300183 stream->base.write = weston_log_debug_wayland_write;
184 stream->base.destroy = weston_log_debug_wayland_to_destroy;
185 stream->base.complete = weston_log_debug_wayland_complete;
Marius Vlad69e75712019-06-25 13:29:57 +0300186 wl_list_init(&stream->base.subscription_list);
187
188
189 scope = weston_log_get_scope(log_ctx, name);
190 if (scope) {
191 sub = weston_log_subscription_create(&stream->base, name);
192 weston_log_subscription_add(scope, sub);
193 weston_log_run_begin_cb(scope);
194 } else {
195 stream_close_on_failure(stream,
196 "Debug stream name '%s' is unknown.",
197 name);
198 }
199
200 return stream;
201}
202
203static void
204stream_destroy(struct wl_resource *stream_resource)
205{
Marius Vlad967a6c22019-06-21 21:13:15 +0300206 struct weston_log_debug_wayland *stream;
Marius Vlad69e75712019-06-25 13:29:57 +0300207 struct weston_log_subscription *sub = NULL;
208
209 stream = wl_resource_get_user_data(stream_resource);
210
211 if (stream->fd != -1)
212 close(stream->fd);
213
214 sub = weston_log_subscriber_get_only_subscription(&stream->base);
215 weston_log_subscription_remove(sub);
216 weston_log_subscription_destroy(sub);
217
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}