blob: d1fcbf5376cbe8f140a2d6a65a31aa4f2df6833a [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 *
44 * A client provides a file descriptor for the server to write debug
45 * messages into. A weston_debug_stream is associated to one
46 * weston_log_scope via the scope name, and the scope provides the messages.
47 * There can be several streams for the same scope, all streams getting the
48 * same messages.
49 */
50struct weston_debug_stream {
51 struct weston_log_subscriber base;
52 int fd; /**< client provided fd */
53 struct wl_resource *resource; /**< weston_debug_stream_v1 object */
54};
55
56static struct weston_debug_stream *
57to_weston_debug_stream(struct weston_log_subscriber *sub)
58{
59 return container_of(sub, struct weston_debug_stream, base);
60}
61
62static void
63stream_close_unlink(struct weston_debug_stream *stream)
64{
65 if (stream->fd != -1)
66 close(stream->fd);
67 stream->fd = -1;
68}
69
70static void WL_PRINTF(2, 3)
71stream_close_on_failure(struct weston_debug_stream *stream,
72 const char *fmt, ...)
73{
74 char *msg;
75 va_list ap;
76 int ret;
77
78 stream_close_unlink(stream);
79
80 va_start(ap, fmt);
81 ret = vasprintf(&msg, fmt, ap);
82 va_end(ap);
83
84 if (ret > 0) {
85 weston_debug_stream_v1_send_failure(stream->resource, msg);
86 free(msg);
87 } else {
88 weston_debug_stream_v1_send_failure(stream->resource,
89 "MEMFAIL");
90 }
91}
92
93/** Write data into a specific debug stream
94 *
95 * \param sub The subscriber's stream to write into; must not be NULL.
96 * \param[in] data Pointer to the data to write.
97 * \param len Number of bytes to write.
98 *
99 * Writes the given data (binary verbatim) into the debug stream.
100 * If \c len is zero or negative, the write is silently dropped.
101 *
102 * Writing is continued until all data has been written or
103 * a write fails. If the write fails due to a signal, it is re-tried.
104 * Otherwise on failure, the stream is closed and
105 * \c weston_debug_stream_v1.failure event is sent to the client.
106 *
107 * \memberof weston_debug_stream
108 */
109static void
110weston_debug_stream_write(struct weston_log_subscriber *sub,
111 const char *data, size_t len)
112{
113 ssize_t len_ = len;
114 ssize_t ret;
115 int e;
116 struct weston_debug_stream *stream = to_weston_debug_stream(sub);
117
118 if (stream->fd == -1)
119 return;
120
121 while (len_ > 0) {
122 ret = write(stream->fd, data, len_);
123 e = errno;
124 if (ret < 0) {
125 if (e == EINTR)
126 continue;
127
128 stream_close_on_failure(stream,
129 "Error writing %zd bytes: %s (%d)",
130 len_, strerror(e), e);
131 break;
132 }
133
134 len_ -= ret;
135 data += ret;
136 }
137}
138
139/** Close the debug stream and send success event
140 *
141 * \param sub Subscriber's stream to close.
142 *
143 * Closes the debug stream and sends \c weston_debug_stream_v1.complete
144 * event to the client. This tells the client the debug information dump
145 * is complete.
146 *
147 * \memberof weston_debug_stream
148 */
149static void
150weston_debug_stream_complete(struct weston_log_subscriber *sub)
151{
152 struct weston_debug_stream *stream = to_weston_debug_stream(sub);
153
154 stream_close_unlink(stream);
155 weston_debug_stream_v1_send_complete(stream->resource);
156}
157
158static void
159weston_debug_stream_to_destroy(struct weston_log_subscriber *sub)
160{
161 struct weston_debug_stream *stream = to_weston_debug_stream(sub);
162 stream_close_on_failure(stream, "debug name removed");
163}
164
165static struct weston_debug_stream *
166stream_create(struct weston_log_context *log_ctx, const char *name,
167 int32_t streamfd, struct wl_resource *stream_resource)
168{
169 struct weston_debug_stream *stream;
170 struct weston_log_scope *scope;
171 struct weston_log_subscription *sub;
172
173 stream = zalloc(sizeof *stream);
174 if (!stream)
175 return NULL;
176
177 stream->fd = streamfd;
178 stream->resource = stream_resource;
179
180 stream->base.write = weston_debug_stream_write;
181 stream->base.destroy = weston_debug_stream_to_destroy;
182 stream->base.complete = weston_debug_stream_complete;
183 wl_list_init(&stream->base.subscription_list);
184
185
186 scope = weston_log_get_scope(log_ctx, name);
187 if (scope) {
188 sub = weston_log_subscription_create(&stream->base, name);
189 weston_log_subscription_add(scope, sub);
190 weston_log_run_begin_cb(scope);
191 } 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{
203 struct weston_debug_stream *stream;
204 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);
212 weston_log_subscription_remove(sub);
213 weston_log_subscription_destroy(sub);
214
215 free(stream);
216}
217
218static void
219weston_debug_stream_destroy(struct wl_client *client,
220 struct wl_resource *stream_resource)
221{
222 wl_resource_destroy(stream_resource);
223}
224
225static const struct weston_debug_stream_v1_interface
226 weston_debug_stream_impl = {
227 weston_debug_stream_destroy
228};
229
230static void
231weston_debug_destroy(struct wl_client *client,
232 struct wl_resource *global_resource)
233{
234 wl_resource_destroy(global_resource);
235}
236
237static void
238weston_debug_subscribe(struct wl_client *client,
239 struct wl_resource *global_resource,
240 const char *name,
241 int32_t streamfd,
242 uint32_t new_stream_id)
243{
244 struct weston_log_context *log_ctx;
245 struct wl_resource *stream_resource;
246 uint32_t version;
247 struct weston_debug_stream *stream;
248
249 log_ctx = wl_resource_get_user_data(global_resource);
250 version = wl_resource_get_version(global_resource);
251
252 stream_resource = wl_resource_create(client,
253 &weston_debug_stream_v1_interface,
254 version, new_stream_id);
255 if (!stream_resource)
256 goto fail;
257
258 stream = stream_create(log_ctx, name, streamfd, stream_resource);
259 if (!stream)
260 goto fail;
261
262 wl_resource_set_implementation(stream_resource,
263 &weston_debug_stream_impl,
264 stream, stream_destroy);
265 return;
266
267fail:
268 close(streamfd);
269 wl_client_post_no_memory(client);
270}
271
272static const struct weston_debug_v1_interface weston_debug_impl = {
273 weston_debug_destroy,
274 weston_debug_subscribe
275};
276
277void
278weston_log_bind_weston_debug(struct wl_client *client,
279 void *data, uint32_t version, uint32_t id)
280{
281 struct weston_log_context *log_ctx = data;
282 struct wl_resource *resource;
283
284 resource = wl_resource_create(client,
285 &weston_debug_v1_interface,
286 version, id);
287 if (!resource) {
288 wl_client_post_no_memory(client);
289 return;
290 }
291 wl_resource_set_implementation(resource, &weston_debug_impl,
292 log_ctx, NULL);
293
294 weston_debug_protocol_advertise_scopes(log_ctx, resource);
295}