• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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