1 /* 2 Shmif- server library 3 Copyright (c) 2017-2018, Bjorn Stahl 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, 7 with or without modification, are permitted provided that the 8 following conditions are met: 9 10 1. Redistributions of source code must retain the above copyright notice, 11 this list of conditions and the following disclaimer. 12 13 2. Redistributions in binary form must reproduce the above copyright notice, 14 this list of conditions and the following disclaimer in the documentation 15 and/or other materials provided with the distribution. 16 17 3. Neither the name of the copyright holder nor the names of its contributors 18 may be used to endorse or promote products derived from this software without 19 specific prior written permission. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 26 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * This is used to be able to write headless servers for shmif, in order to 36 * easier facilitate testing, proxying and nesting of shmif connections. It 37 * (re-) uses the same setup, platform and similar code from the normal arcan 38 * build, but with different event-routing and polling. 39 */ 40 struct shmifsrv_client; 41 42 /* 43 * Used for setup when we need to launch an external program with working/ 44 * unique connection primitives from the start. If init_w and init_h are 0, the 45 * client needs to set its size on its own, or be told of it during the 46 * preroll- event stage. 47 */ 48 struct shmifsrv_envp { 49 char* path; 50 char** argv; 51 char** envv; 52 53 size_t init_w; 54 size_t init_h; 55 56 int detach; 57 }; 58 59 enum shmifsrv_status { 60 SHMIFSRV_OK = 1, 61 SHMIFSRV_INVALID_ARGUMENT = -1, 62 SHMIFSRV_OUT_OF_MEMORY = -2, 63 SHMIFSRV_EXEC_FAILED = -3 64 }; 65 66 /* 67 * Depending on underlying OS, there are a number of recoverable shared memory 68 * related errors that can occur while reading/writing, but it is typically a 69 * sign of a malicious client. To work around this, we rely on setjmp/longjmp 70 * like behavior. This is wrapped with these functions, like so: 71 * 72 * if (!shmifsrv_enter(conn)){ 73 * something_bad_happened, anything between enter and leave 74 * may be in a partial state, kill/clean. 75 * 76 * return; 77 * } 78 * 79 * sensitive functions (marked [CRITICAL]) 80 * shmifsrv_queuestatus 81 * shmifsrv_video 82 * shmifsrv_audio 83 * 84 * shmifsrv_leave(); 85 * 86 * only sensitive functions ([CRITICAL]) should be called within the 87 * _enter/_leave critical region. BEWARE that this function MAY temporarily 88 * modify the signal mask for some signals (e.g. SIGBUS). 89 */ 90 bool shmifsrv_enter(struct shmifsrv_client*); 91 void shmifsrv_leave(); 92 93 /* 94 * Wrap a shmifsrv client into an interface that can be used with the normal 95 * arcan_shmif_... class of functions. 96 */ 97 struct arcan_shmif_cont* 98 shmifsrv_client_wrap(struct shmifsrv_client* srv); 99 100 /* 101 * Allocate, prepare and transfer a new sub-segment to the frameserver 102 * referenced by [dst] (!NULL). 103 * 104 * [segid] specifies the type of the subsegment 105 * (MUST match REQID if it is in response to a SEGREQ event) 106 * 107 * [hints] are default render hints for the segment and should be set to 108 * what the client requested in a segment request, or left at 0. 109 * 110 * [idtok] is used as a reference in a client addressable namespace in 111 * order to set spatial hints. 112 */ 113 struct shmifsrv_client* 114 shmifsrv_send_subsegment(struct shmifsrv_client* dst, 115 int segid, int hints, size_t init_w, size_t init_h, int reqid, uint32_t idtok); 116 117 /* 118 * Allocate and prepare a frameserver connection point accessible via the name 119 * [name](!NULL) an (optional) authentication key and the permission mask of 120 * the target socket. The function returns a valid server context or NULL with 121 * the reason for failure in statuscode. 122 * 123 * Design / legacy quirk: 124 * The listening socket is contained within shmifsrv_client and should be 125 * extracted using shmifsrv_client_handle. If a client connects (see 126 * shmifsrv_poll), this handle with mutate internally to that of the accepted 127 * connection and it is the responsibility of the caller to either close 128 * the descriptor or use it as the [fd] argument to this function in order 129 * to re-open the connection point. 130 * 131 * Example use: 132 * struct shmifsrv_client* cl = 133 * shmifsrv_allocate_connpoint("demo", NULL, S_IRWXU, -1); 134 * 135 * if (!cl) 136 * error("couldn't allocate resources"); 137 138 * int server_fd = shmifsrv_client_handler(cl); 139 * while(cl){ 140 * ... poll / wait in activity on server_fd ... 141 * dispatch_client(cl); (have a thread/loop that runs shmifsrv_poll) 142 * cl = shmifsrv_allocate_connpoint("demo", NULL, S_IRWXU, server_fd); 143 * } 144 * 145 */ 146 struct shmifsrv_client* shmifsrv_allocate_connpoint( 147 const char* name, const char* key, mode_t permission, int fd); 148 149 /* 150 * Setup a connection-less frameserver, meaning that all primitives and 151 * allocation is passed through means that can be inherited via the client 152 * context itself, though it could also be used in the same process space. 153 * 154 * return NULL on failure, and, if provided, a shmifsrv_error status 155 * code. 156 */ 157 struct shmifsrv_client* shmifsrv_spawn_client( 158 struct shmifsrv_envp env, int* clsocket, int* statuscode, uint32_t idtok); 159 160 /* 161 * Setup a preconnected frameserver, meaning that the initial socket 162 * pairing has been performed via some other mechanism, and it is only 163 * the memory/synch primitives that need to be prepared 164 */ 165 struct shmifsrv_client* shmifsrv_inherit_connection(int sockin, int* sc); 166 167 /* 168 * Retrieve an I/O multiplexable handle for mixing into poll() rather 169 * than throwing a _poll() call in there whenever there's time. 170 */ 171 int shmifsrv_client_handle(struct shmifsrv_client*); 172 173 /* 174 * This should be invoked at a monotonic tickrate, the common default 175 * used here is 25Hz (% 50, 75, 90 as nominal video framerates) 176 * 177 * Internally, it verifies that the shared resources are in a healthy 178 * state, forwards client- managed timers and so on. 179 */ 180 bool shmifsrv_tick(struct shmifsrv_client*); 181 182 /* 183 * Polling routine for pumping synchronization actions and determining 184 * updating status. Should be called periodically, as part of the normal 185 * processing loop, at least once every shmifsrv_monotonic_tick()/ 186 */ 187 enum shmifsrv_client_status { 188 CLIENT_DEAD = -1, 189 CLIENT_NOT_READY = 0, 190 CLIENT_VBUFFER_READY = 1, 191 CLIENT_ABUFFER_READY = 2 192 }; 193 int shmifsrv_poll(struct shmifsrv_client*); 194 195 /* 196 * Free the resources associated with a shmifsrv_client. If this client has 197 * been created through shmifsrv_allocate_connpoint and is still in a listening 198 * state, the underlying descriptor won't be closed in order for the connpoint 199 * to be reused. 200 * 201 * If [full] is set to false, the client will not be directly told that the 202 * connection is dead via the normal 'dead-man-switch' handle, only through 203 * the event-queue - forcing the client to try and use some recovery mechanism. 204 */ 205 enum shmifsrv_action { 206 SHMIFSRV_FREE_FULL = 0, 207 SHMIFSRV_FREE_NO_DMS = 1, 208 SHMIFSRV_FREE_LOCAL = 2 209 }; 210 void shmifsrv_free(struct shmifsrv_client*, int mode); 211 212 /* 213 * [CRITICAL] 214 * Add an event to the outgoing event-queue, will return false if the queue has 215 * been saturated (non-responsive client), true otherwise. 216 * arcan_shmif_descrevent() can be used to verify if there's supposed to be a 217 * descriptor coupled with the event. 218 * 219 * As a rule, only srv->client events carry descriptors (the exception is 220 * accelerated buffer passing) as the client isn't expected to be in a context 221 * where it can easily create descriptors to shareable resources (sandboxing) 222 * and as a means of closing paths where a client may attempt to starve the 223 * parent by filling descriptor tables. The descriptors passed can safely be 224 * closed afterwards. 225 */ 226 bool shmifsrv_enqueue_event( 227 struct shmifsrv_client*, struct arcan_event*, int fd); 228 229 /* 230 * [CRITICAL] 231 * Attempt to dequeue up to [limit] events from the ingoing event queue. Will 232 * return the numbers of actual events dequeued. This will perform no additional 233 * tracking or management, only raw event access. For assistance with state 234 * tracking, feed the events through shmifsrv_process_event. 235 */ 236 size_t shmifsrv_dequeue_events( 237 struct shmifsrv_client*, struct arcan_event* newev, size_t limit); 238 239 /* 240 * Retrieve the currently registered type for a client 241 */ 242 enum ARCAN_SEGID shmifsrv_client_type(struct shmifsrv_client* cl); 243 244 /* 245 * Set the mask of permitted subprotocols, this applies to coming negotiations 246 * as this is initiated by the client and provided permissions do not revoke. 247 */ 248 void shmifsrv_client_protomask(struct shmifsrv_client* cl, unsigned mask); 249 250 /* 251 * Handle some of the normal state-tracking events (e.g. CLOCKREQ, 252 * BUFFERSTREAM, FLUSHAUD). Returns true if the event was consumed and no 253 * further action is needed. 254 */ 255 bool shmifsrv_process_event( 256 struct shmifsrv_client*, struct arcan_event* ev); 257 258 enum vbuffer_status { 259 VBUFFER_OUTPUT = -1, 260 VBUFFER_NODATA = 0, 261 VBUFFER_OKDATA, 262 VBUFFER_HANDLE 263 }; 264 265 struct shmifsrv_vbuffer { 266 int state; 267 union { 268 shmif_pixel* buffer; 269 uint8_t* buffer_bytes; 270 }; 271 272 struct { 273 bool origo_ll : 1; 274 bool ignore_alpha : 1; 275 bool subregion : 1; 276 bool srgb : 1; 277 bool hwhandles : 1; 278 bool tpack : 1; 279 bool compressed : 1; 280 } flags; 281 282 /* if compressed is set (requires the compressed buffer subprotocol to be 283 * provided) the fourcc code will match that of the video format frame in 284 * the buffer */ 285 uint8_t fourcc[4]; 286 287 union { 288 struct { 289 size_t w, h, pitch, stride; 290 }; 291 size_t buffer_sz; 292 }; 293 294 /* desired presentation time since connection epoch, hint */ 295 uint64_t vpts; 296 297 /* only usedated with subregion : true */ 298 struct arcan_shmif_region region; 299 300 /* only used with hwhandles : true */ 301 size_t formats[4]; 302 int planes[4]; 303 }; 304 305 /* 306 * [CRITICAL] 307 * access the currently active video buffer slot in the client. 308 * the buffer is returned to the client. The contents of the buffer are 309 * a reference to shared memory, and should thus be explicitly copied out 310 * before leaving critical. 311 * 312 * The [state] field of the returned structure will match vbuffer_status: 313 * VBUFFER_OUTPUT - segment is configured for output, don't use this function. 314 * VBUFFER_NODATA - nothing available. 315 * VBUFFER_OKDATA - buffer is updated. 316 * VBUFFER_HANDLE - accelerated opaque handle, descriptor field set. 317 * 318 * Video buffers can operate in two different modes, accelerated (opaque) 319 * handle passing and accelerated. This server library does not currently 320 * provide support functions for working with opaque handles. 321 */ 322 struct shmifsrv_vbuffer shmifsrv_video(struct shmifsrv_client*); 323 324 /* [CRITICAL] 325 * Forward that the last known video buffer is no longer interesting and 326 * signal a release to the client 327 */ 328 void shmifsrv_video_step(struct shmifsrv_client*); 329 330 /* [CRITICAL] 331 * Flush all pending buffers to the provided drain function. In contrast to 332 * _video_step this function has an implicit step stage so all known buffers 333 * will be flushed and a waiting client will be woken up. 334 * 335 * Returns false is the indices are out of negotiated bounds or corrupt. 336 */ 337 bool shmifsrv_audio(struct shmifsrv_client* cl, 338 void (*on_buffer)(shmif_asample* buf, 339 size_t n_samples, unsigned channels, unsigned rate, void* tag), void* tag); 340 341 /* 342 * [THREAD_UNSAFE] 343 * This is a helper function that returns the number of monotonic ticks 344 * since the last time this function was called. It is thus a global state 345 * shared by many clients, along with the optional time to next tick. The 346 * typical pattern is: 347 * [start] 348 * shmifsrv_monotonic_rebase(); 349 * 350 * [loop] 351 * num = shmifsrv_monotonic_tick(NULL); 352 * while (num-- > 0) 353 * each_client(cl){ shmifsrv_tick( cl ) 354 */ 355 int shmifsrv_monotonic_tick(int* left); 356 357 /* 358 * [THREAD_UNSAFE] 359 * Explicitly rebase the shared clock counter due to a large stall, 360 * pause, global suspend action and so on. 361 */ 362 void shmifsrv_monotonic_rebase(); 363