xcb_conn.c revision b9526c6a
1/* Copyright (C) 2001-2004 Bart Massey and Jamey Sharp. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a 4 * copy of this software and associated documentation files (the "Software"), 5 * to deal in the Software without restriction, including without limitation 6 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 * and/or sell copies of the Software, and to permit persons to whom the 8 * Software is furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 17 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 * 20 * Except as contained in this notice, the names of the authors or their 21 * institutions shall not be used in advertising or otherwise to promote the 22 * sale, use or other dealings in this Software without prior written 23 * authorization from the authors. 24 */ 25 26/* Connection management: the core of XCB. */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include <assert.h> 33#include <string.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <fcntl.h> 37#include <errno.h> 38#include <limits.h> 39 40#include "xcb.h" 41#include "xcbint.h" 42#if USE_POLL 43#include <poll.h> 44#elif !defined _WIN32 45#include <sys/select.h> 46#endif 47 48#ifdef _WIN32 49#include "xcb_windefs.h" 50#include <io.h> 51#else 52#include <unistd.h> 53#include <sys/socket.h> 54#include <netinet/in.h> 55#endif /* _WIN32 */ 56 57/* SHUT_RDWR is fairly recent and is not available on all platforms */ 58#if !defined(SHUT_RDWR) 59#define SHUT_RDWR 2 60#endif 61 62typedef struct { 63 uint8_t status; 64 uint8_t pad0[5]; 65 uint16_t length; 66} xcb_setup_generic_t; 67 68static const xcb_setup_t xcb_error_setup = { 69 0, /* status: failed (but we wouldn't have a xcb_setup_t in this case) */ 70 0, /* pad0 */ 71 0, 0, /* protocol version, should be 11.0, but isn't */ 72 0, /* length, invalid value */ 73 0, /* release_number */ 74 0, 0, /* resource_id_{base,mask} */ 75 0, /* motion_buffer_size */ 76 0, /* vendor_len */ 77 0, /* maximum_request_length */ 78 0, /* roots_len */ 79 0, /* pixmap_formats_len */ 80 0, /* image_byte_order */ 81 0, /* bitmap_format_bit_order */ 82 0, /* bitmap_format_scanline_unit */ 83 0, /* bitmap_format_scanline_pad */ 84 0, 0, /* {min,max}_keycode */ 85 { 0, 0, 0, 0 } /* pad1 */ 86}; 87 88/* Keep this list in sync with is_static_error_conn()! */ 89static const int xcb_con_error = XCB_CONN_ERROR; 90static const int xcb_con_closed_mem_er = XCB_CONN_CLOSED_MEM_INSUFFICIENT; 91static const int xcb_con_closed_parse_er = XCB_CONN_CLOSED_PARSE_ERR; 92static const int xcb_con_closed_screen_er = XCB_CONN_CLOSED_INVALID_SCREEN; 93 94static int is_static_error_conn(xcb_connection_t *c) 95{ 96 return c == (xcb_connection_t *) &xcb_con_error || 97 c == (xcb_connection_t *) &xcb_con_closed_mem_er || 98 c == (xcb_connection_t *) &xcb_con_closed_parse_er || 99 c == (xcb_connection_t *) &xcb_con_closed_screen_er; 100} 101 102static int set_fd_flags(const int fd) 103{ 104/* Win32 doesn't have file descriptors and the fcntl function. This block sets the socket in non-blocking mode */ 105 106#ifdef _WIN32 107 u_long iMode = 1; /* non-zero puts it in non-blocking mode, 0 in blocking mode */ 108 int ret = 0; 109 110 ret = ioctlsocket(fd, FIONBIO, &iMode); 111 if(ret != 0) 112 return 0; 113 return 1; 114#else 115 int flags = fcntl(fd, F_GETFL, 0); 116 if(flags == -1) 117 return 0; 118 flags |= O_NONBLOCK; 119 if(fcntl(fd, F_SETFL, flags) == -1) 120 return 0; 121 if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 122 return 0; 123 return 1; 124#endif /* _WIN32 */ 125} 126 127static int write_setup(xcb_connection_t *c, xcb_auth_info_t *auth_info) 128{ 129 static const char pad[3]; 130 xcb_setup_request_t out; 131 struct iovec parts[6]; 132 int count = 0; 133 static const uint32_t endian = 0x01020304; 134 int ret; 135 136 memset(&out, 0, sizeof(out)); 137 138 /* B = 0x42 = MSB first, l = 0x6c = LSB first */ 139 if(htonl(endian) == endian) 140 out.byte_order = 0x42; 141 else 142 out.byte_order = 0x6c; 143 out.protocol_major_version = X_PROTOCOL; 144 out.protocol_minor_version = X_PROTOCOL_REVISION; 145 out.authorization_protocol_name_len = 0; 146 out.authorization_protocol_data_len = 0; 147 parts[count].iov_len = sizeof(xcb_setup_request_t); 148 parts[count++].iov_base = &out; 149 parts[count].iov_len = XCB_PAD(sizeof(xcb_setup_request_t)); 150 parts[count++].iov_base = (char *) pad; 151 152 if(auth_info) 153 { 154 parts[count].iov_len = out.authorization_protocol_name_len = auth_info->namelen; 155 parts[count++].iov_base = auth_info->name; 156 parts[count].iov_len = XCB_PAD(out.authorization_protocol_name_len); 157 parts[count++].iov_base = (char *) pad; 158 parts[count].iov_len = out.authorization_protocol_data_len = auth_info->datalen; 159 parts[count++].iov_base = auth_info->data; 160 parts[count].iov_len = XCB_PAD(out.authorization_protocol_data_len); 161 parts[count++].iov_base = (char *) pad; 162 } 163 assert(count <= (int) (sizeof(parts) / sizeof(*parts))); 164 165 pthread_mutex_lock(&c->iolock); 166 ret = _xcb_out_send(c, parts, count); 167 pthread_mutex_unlock(&c->iolock); 168 return ret; 169} 170 171static int read_setup(xcb_connection_t *c) 172{ 173 const char newline = '\n'; 174 175 /* Read the server response */ 176 c->setup = malloc(sizeof(xcb_setup_generic_t)); 177 if(!c->setup) 178 return 0; 179 180 if(_xcb_in_read_block(c, c->setup, sizeof(xcb_setup_generic_t)) != sizeof(xcb_setup_generic_t)) 181 return 0; 182 183 { 184 void *tmp = realloc(c->setup, c->setup->length * 4 + sizeof(xcb_setup_generic_t)); 185 if(!tmp) 186 return 0; 187 c->setup = tmp; 188 } 189 190 if(_xcb_in_read_block(c, (char *) c->setup + sizeof(xcb_setup_generic_t), c->setup->length * 4) <= 0) 191 return 0; 192 193 /* 0 = failed, 2 = authenticate, 1 = success */ 194 switch(c->setup->status) 195 { 196 case 0: /* failed */ 197 { 198 xcb_setup_failed_t *setup = (xcb_setup_failed_t *) c->setup; 199 write(STDERR_FILENO, xcb_setup_failed_reason(setup), xcb_setup_failed_reason_length(setup)); 200 write(STDERR_FILENO, &newline, 1); 201 return 0; 202 } 203 204 case 2: /* authenticate */ 205 { 206 xcb_setup_authenticate_t *setup = (xcb_setup_authenticate_t *) c->setup; 207 write(STDERR_FILENO, xcb_setup_authenticate_reason(setup), xcb_setup_authenticate_reason_length(setup)); 208 write(STDERR_FILENO, &newline, 1); 209 return 0; 210 } 211 } 212 213 return 1; 214} 215 216/* precondition: there must be something for us to write. */ 217static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count) 218{ 219#ifndef _WIN32 220 int n; 221#endif 222 223 assert(!c->out.queue_len); 224 225#ifdef _WIN32 226 /* Could use the WSASend win32 function for scatter/gather i/o but setting up the WSABUF struct from 227 an iovec would require more work and I'm not sure of the benefit....works for now */ 228 while (*count) 229 { 230 struct iovec *vec = *vector; 231 if (vec->iov_len) 232 { 233 int ret = send(c->fd, vec->iov_base, vec->iov_len, 0); 234 if (ret == SOCKET_ERROR) 235 { 236 int err = WSAGetLastError(); 237 if (err == WSAEWOULDBLOCK) 238 { 239 return 1; 240 } 241 } 242 if (ret <= 0) 243 { 244 _xcb_conn_shutdown(c, XCB_CONN_ERROR); 245 return 0; 246 } 247 c->out.total_written += ret; 248 vec->iov_len -= ret; 249 vec->iov_base = (char *)vec->iov_base + ret; 250 } 251 if (vec->iov_len == 0) { 252 (*vector)++; 253 (*count)--; 254 } 255 } 256 257 if (!*count) 258 *vector = 0; 259 260#else 261 n = *count; 262 if (n > IOV_MAX) 263 n = IOV_MAX; 264 265#if HAVE_SENDMSG 266 if (c->out.out_fd.nfd) { 267 union { 268 struct cmsghdr cmsghdr; 269 char buf[CMSG_SPACE(XCB_MAX_PASS_FD * sizeof(int))]; 270 } cmsgbuf; 271 struct msghdr msg = { 272 .msg_name = NULL, 273 .msg_namelen = 0, 274 .msg_iov = *vector, 275 .msg_iovlen = n, 276 .msg_control = cmsgbuf.buf, 277 .msg_controllen = CMSG_LEN(c->out.out_fd.nfd * sizeof (int)), 278 }; 279 int i; 280 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); 281 282 hdr->cmsg_len = msg.msg_controllen; 283 hdr->cmsg_level = SOL_SOCKET; 284 hdr->cmsg_type = SCM_RIGHTS; 285 memcpy(CMSG_DATA(hdr), c->out.out_fd.fd, c->out.out_fd.nfd * sizeof (int)); 286 287 n = sendmsg(c->fd, &msg, 0); 288 if(n < 0 && errno == EAGAIN) 289 return 1; 290 for (i = 0; i < c->out.out_fd.nfd; i++) 291 close(c->out.out_fd.fd[i]); 292 c->out.out_fd.nfd = 0; 293 } else 294#endif 295 { 296 n = writev(c->fd, *vector, n); 297 if(n < 0 && errno == EAGAIN) 298 return 1; 299 } 300 301 if(n <= 0) 302 { 303 _xcb_conn_shutdown(c, XCB_CONN_ERROR); 304 return 0; 305 } 306 307 c->out.total_written += n; 308 for(; *count; --*count, ++*vector) 309 { 310 int cur = (*vector)->iov_len; 311 if(cur > n) 312 cur = n; 313 if(cur) { 314 (*vector)->iov_len -= cur; 315 (*vector)->iov_base = (char *) (*vector)->iov_base + cur; 316 n -= cur; 317 } 318 if((*vector)->iov_len) 319 break; 320 } 321 if(!*count) 322 *vector = 0; 323 assert(n == 0); 324 325#endif /* _WIN32 */ 326 327 return 1; 328} 329 330/* Public interface */ 331 332const xcb_setup_t *xcb_get_setup(xcb_connection_t *c) 333{ 334 if(is_static_error_conn(c)) 335 return &xcb_error_setup; 336 /* doesn't need locking because it's never written to. */ 337 return c->setup; 338} 339 340int xcb_get_file_descriptor(xcb_connection_t *c) 341{ 342 if(is_static_error_conn(c)) 343 return -1; 344 /* doesn't need locking because it's never written to. */ 345 return c->fd; 346} 347 348int xcb_connection_has_error(xcb_connection_t *c) 349{ 350 /* doesn't need locking because it's read and written atomically. */ 351 return c->has_error; 352} 353 354xcb_connection_t *xcb_connect_to_fd(int fd, xcb_auth_info_t *auth_info) 355{ 356 xcb_connection_t* c; 357 358#ifndef _WIN32 359#ifndef USE_POLL 360 if(fd >= FD_SETSIZE) /* would overflow in FD_SET */ 361 { 362 close(fd); 363 return _xcb_conn_ret_error(XCB_CONN_ERROR); 364 } 365#endif 366#endif /* !_WIN32*/ 367 368 c = calloc(1, sizeof(xcb_connection_t)); 369 if(!c) { 370#ifdef _WIN32 371 closesocket(fd); 372#else 373 close(fd); 374#endif 375 return _xcb_conn_ret_error(XCB_CONN_CLOSED_MEM_INSUFFICIENT) ; 376 } 377 378 c->fd = fd; 379 380 if(!( 381 set_fd_flags(fd) && 382 pthread_mutex_init(&c->iolock, 0) == 0 && 383 _xcb_in_init(&c->in) && 384 _xcb_out_init(&c->out) && 385 write_setup(c, auth_info) && 386 read_setup(c) && 387 _xcb_ext_init(c) && 388 _xcb_xid_init(c) 389 )) 390 { 391 xcb_disconnect(c); 392 return _xcb_conn_ret_error(XCB_CONN_ERROR); 393 } 394 395 return c; 396} 397 398void xcb_disconnect(xcb_connection_t *c) 399{ 400 if(c == NULL || is_static_error_conn(c)) 401 return; 402 403 free(c->setup); 404 405 /* disallow further sends and receives */ 406 shutdown(c->fd, SHUT_RDWR); 407#ifdef _WIN32 408 closesocket(c->fd); 409#else 410 close(c->fd); 411#endif 412 413 pthread_mutex_destroy(&c->iolock); 414 _xcb_in_destroy(&c->in); 415 _xcb_out_destroy(&c->out); 416 417 _xcb_ext_destroy(c); 418 _xcb_xid_destroy(c); 419 420 free(c); 421 422#ifdef _WIN32 423 WSACleanup(); 424#endif 425} 426 427/* Private interface */ 428 429void _xcb_conn_shutdown(xcb_connection_t *c, int err) 430{ 431 c->has_error = err; 432} 433 434/* Return connection error state. 435 * To make thread-safe, I need a seperate static 436 * variable for every possible error. 437 * has_error is the first field in xcb_connection_t, so just 438 * return a casted int here; checking has_error (and only 439 * has_error) will be safe. 440 */ 441xcb_connection_t *_xcb_conn_ret_error(int err) 442{ 443 444 switch(err) 445 { 446 case XCB_CONN_CLOSED_MEM_INSUFFICIENT: 447 { 448 return (xcb_connection_t *) &xcb_con_closed_mem_er; 449 } 450 case XCB_CONN_CLOSED_PARSE_ERR: 451 { 452 return (xcb_connection_t *) &xcb_con_closed_parse_er; 453 } 454 case XCB_CONN_CLOSED_INVALID_SCREEN: 455 { 456 return (xcb_connection_t *) &xcb_con_closed_screen_er; 457 } 458 case XCB_CONN_ERROR: 459 default: 460 { 461 return (xcb_connection_t *) &xcb_con_error; 462 } 463 } 464} 465 466int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count) 467{ 468 int ret; 469#if USE_POLL 470 struct pollfd fd; 471#else 472 fd_set rfds, wfds; 473#endif 474 475 /* If the thing I should be doing is already being done, wait for it. */ 476 if(count ? c->out.writing : c->in.reading) 477 { 478 pthread_cond_wait(cond, &c->iolock); 479 return 1; 480 } 481 482#if USE_POLL 483 memset(&fd, 0, sizeof(fd)); 484 fd.fd = c->fd; 485 fd.events = POLLIN; 486#else 487 FD_ZERO(&rfds); 488 FD_SET(c->fd, &rfds); 489#endif 490 ++c->in.reading; 491 492#if USE_POLL 493 if(count) 494 { 495 fd.events |= POLLOUT; 496 ++c->out.writing; 497 } 498#else 499 FD_ZERO(&wfds); 500 if(count) 501 { 502 FD_SET(c->fd, &wfds); 503 ++c->out.writing; 504 } 505#endif 506 507 pthread_mutex_unlock(&c->iolock); 508 do { 509#if USE_POLL 510 ret = poll(&fd, 1, -1); 511 /* If poll() returns an event we didn't expect, such as POLLNVAL, treat 512 * it as if it failed. */ 513 if(ret >= 0 && (fd.revents & ~fd.events)) 514 { 515 ret = -1; 516 break; 517 } 518#else 519 ret = select(c->fd + 1, &rfds, &wfds, 0, 0); 520#endif 521 } while (ret == -1 && errno == EINTR); 522 if(ret < 0) 523 { 524 _xcb_conn_shutdown(c, XCB_CONN_ERROR); 525 ret = 0; 526 } 527 pthread_mutex_lock(&c->iolock); 528 529 if(ret) 530 { 531 /* The code allows two threads to call select()/poll() at the same time. 532 * First thread just wants to read, a second thread wants to write, too. 533 * We have to make sure that we don't steal the reading thread's reply 534 * and let it get stuck in select()/poll(). 535 * So a thread may read if either: 536 * - There is no other thread that wants to read (the above situation 537 * did not occur). 538 * - It is the reading thread (above situation occurred). 539 */ 540 int may_read = c->in.reading == 1 || !count; 541#if USE_POLL 542 if(may_read && (fd.revents & POLLIN) != 0) 543#else 544 if(may_read && FD_ISSET(c->fd, &rfds)) 545#endif 546 ret = ret && _xcb_in_read(c); 547 548#if USE_POLL 549 if((fd.revents & POLLOUT) != 0) 550#else 551 if(FD_ISSET(c->fd, &wfds)) 552#endif 553 ret = ret && write_vec(c, vector, count); 554 } 555 556 if(count) 557 --c->out.writing; 558 --c->in.reading; 559 560 return ret; 561} 562 563uint64_t xcb_total_read(xcb_connection_t *c) 564{ 565 uint64_t n; 566 567 if (xcb_connection_has_error(c)) 568 return 0; 569 570 pthread_mutex_lock(&c->iolock); 571 n = c->in.total_read; 572 pthread_mutex_unlock(&c->iolock); 573 return n; 574} 575 576uint64_t xcb_total_written(xcb_connection_t *c) 577{ 578 uint64_t n; 579 580 if (xcb_connection_has_error(c)) 581 return 0; 582 583 pthread_mutex_lock(&c->iolock); 584 n = c->out.total_written; 585 pthread_mutex_unlock(&c->iolock); 586 587 return n; 588} 589