1 /*
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-log.h>
29 #include "shared/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 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().
52 */
53 struct weston_log_debug_wayland {
54 struct weston_log_subscriber base;
55 int fd; /**< client provided fd */
56 struct wl_resource *resource; /**< weston_debug_stream_v1 object */
57 };
58
59 static struct weston_log_debug_wayland *
to_weston_log_debug_wayland(struct weston_log_subscriber * sub)60 to_weston_log_debug_wayland(struct weston_log_subscriber *sub)
61 {
62 return container_of(sub, struct weston_log_debug_wayland, base);
63 }
64
65 static void
stream_close_unlink(struct weston_log_debug_wayland * stream)66 stream_close_unlink(struct weston_log_debug_wayland *stream)
67 {
68 if (stream->fd != -1)
69 close(stream->fd);
70 stream->fd = -1;
71 }
72
73 static void WL_PRINTF(2, 3)
stream_close_on_failure(struct weston_log_debug_wayland * stream,const char * fmt,...)74 stream_close_on_failure(struct weston_log_debug_wayland *stream,
75 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 *
110 * \memberof weston_log_debug_wayland
111 */
112 static void
weston_log_debug_wayland_write(struct weston_log_subscriber * sub,const char * data,size_t len)113 weston_log_debug_wayland_write(struct weston_log_subscriber *sub,
114 const char *data, size_t len)
115 {
116 ssize_t len_ = len;
117 ssize_t ret;
118 int e;
119 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
120
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 *
150 * \memberof weston_log_debug_wayland
151 */
152 static void
weston_log_debug_wayland_complete(struct weston_log_subscriber * sub)153 weston_log_debug_wayland_complete(struct weston_log_subscriber *sub)
154 {
155 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
156
157 stream_close_unlink(stream);
158 weston_debug_stream_v1_send_complete(stream->resource);
159 }
160
161 static void
weston_log_debug_wayland_to_destroy(struct weston_log_subscriber * sub)162 weston_log_debug_wayland_to_destroy(struct weston_log_subscriber *sub)
163 {
164 struct weston_log_debug_wayland *stream = to_weston_log_debug_wayland(sub);
165
166 if (stream->fd != -1)
167 stream_close_on_failure(stream, "debug name removed");
168 }
169
170 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)171 stream_create(struct weston_log_context *log_ctx, const char *name,
172 int32_t streamfd, struct wl_resource *stream_resource)
173 {
174 struct weston_log_debug_wayland *stream;
175 struct weston_log_scope *scope;
176
177 stream = zalloc(sizeof *stream);
178 if (!stream)
179 return NULL;
180
181 stream->fd = streamfd;
182 stream->resource = stream_resource;
183
184 stream->base.write = weston_log_debug_wayland_write;
185 stream->base.destroy = NULL;
186 stream->base.destroy_subscription = weston_log_debug_wayland_to_destroy;
187 stream->base.complete = weston_log_debug_wayland_complete;
188 wl_list_init(&stream->base.subscription_list);
189
190 scope = weston_log_get_scope(log_ctx, name);
191 if (scope) {
192 weston_log_subscription_create(&stream->base, scope);
193 } else {
194 stream_close_on_failure(stream,
195 "Debug stream name '%s' is unknown.",
196 name);
197 }
198
199 return stream;
200 }
201
202 static void
stream_destroy(struct wl_resource * stream_resource)203 stream_destroy(struct wl_resource *stream_resource)
204 {
205 struct weston_log_debug_wayland *stream;
206 stream = wl_resource_get_user_data(stream_resource);
207
208 stream_close_unlink(stream);
209 weston_log_subscriber_release(&stream->base);
210 free(stream);
211 }
212
213 static void
weston_debug_stream_destroy(struct wl_client * client,struct wl_resource * stream_resource)214 weston_debug_stream_destroy(struct wl_client *client,
215 struct wl_resource *stream_resource)
216 {
217 wl_resource_destroy(stream_resource);
218 }
219
220 static const struct weston_debug_stream_v1_interface
221 weston_debug_stream_impl = {
222 weston_debug_stream_destroy
223 };
224
225 static void
weston_debug_destroy(struct wl_client * client,struct wl_resource * global_resource)226 weston_debug_destroy(struct wl_client *client,
227 struct wl_resource *global_resource)
228 {
229 wl_resource_destroy(global_resource);
230 }
231
232 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)233 weston_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;
242 struct weston_log_debug_wayland *stream;
243
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
262 fail:
263 close(streamfd);
264 wl_client_post_no_memory(client);
265 }
266
267 static const struct weston_debug_v1_interface weston_debug_impl = {
268 weston_debug_destroy,
269 weston_debug_subscribe
270 };
271
272 void
weston_log_bind_weston_debug(struct wl_client * client,void * data,uint32_t version,uint32_t id)273 weston_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 }
291