xcb_conn.c revision 8ffb90f1
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 (*vector)->iov_len -= cur; 314 (*vector)->iov_base = (char *) (*vector)->iov_base + cur; 315 n -= cur; 316 if((*vector)->iov_len) 317 break; 318 } 319 if(!*count) 320 *vector = 0; 321 assert(n == 0); 322 323#endif /* _WIN32 */ 324 325 return 1; 326} 327 328/* Public interface */ 329 330const xcb_setup_t *xcb_get_setup(xcb_connection_t *c) 331{ 332 if(is_static_error_conn(c)) 333 return &xcb_error_setup; 334 /* doesn't need locking because it's never written to. */ 335 return c->setup; 336} 337 338int xcb_get_file_descriptor(xcb_connection_t *c) 339{ 340 if(is_static_error_conn(c)) 341 return -1; 342 /* doesn't need locking because it's never written to. */ 343 return c->fd; 344} 345 346int xcb_connection_has_error(xcb_connection_t *c) 347{ 348 /* doesn't need locking because it's read and written atomically. */ 349 return c->has_error; 350} 351 352xcb_connection_t *xcb_connect_to_fd(int fd, xcb_auth_info_t *auth_info) 353{ 354 xcb_connection_t* c; 355 356#ifndef _WIN32 357#ifndef USE_POLL 358 if(fd >= FD_SETSIZE) /* would overflow in FD_SET */ 359 { 360 close(fd); 361 return _xcb_conn_ret_error(XCB_CONN_ERROR); 362 } 363#endif 364#endif /* !_WIN32*/ 365 366 c = calloc(1, sizeof(xcb_connection_t)); 367 if(!c) { 368#ifdef _WIN32 369 closesocket(fd); 370#else 371 close(fd); 372#endif 373 return _xcb_conn_ret_error(XCB_CONN_CLOSED_MEM_INSUFFICIENT) ; 374 } 375 376 c->fd = fd; 377 378 if(!( 379 set_fd_flags(fd) && 380 pthread_mutex_init(&c->iolock, 0) == 0 && 381 _xcb_in_init(&c->in) && 382 _xcb_out_init(&c->out) && 383 write_setup(c, auth_info) && 384 read_setup(c) && 385 _xcb_ext_init(c) && 386 _xcb_xid_init(c) 387 )) 388 { 389 xcb_disconnect(c); 390 return _xcb_conn_ret_error(XCB_CONN_ERROR); 391 } 392 393 return c; 394} 395 396void xcb_disconnect(xcb_connection_t *c) 397{ 398 if(c == NULL || is_static_error_conn(c)) 399 return; 400 401 free(c->setup); 402 403 /* disallow further sends and receives */ 404 shutdown(c->fd, SHUT_RDWR); 405#ifdef _WIN32 406 closesocket(c->fd); 407#else 408 close(c->fd); 409#endif 410 411 pthread_mutex_destroy(&c->iolock); 412 _xcb_in_destroy(&c->in); 413 _xcb_out_destroy(&c->out); 414 415 _xcb_ext_destroy(c); 416 _xcb_xid_destroy(c); 417 418 free(c); 419 420#ifdef _WIN32 421 WSACleanup(); 422#endif 423} 424 425/* Private interface */ 426 427void _xcb_conn_shutdown(xcb_connection_t *c, int err) 428{ 429 c->has_error = err; 430} 431 432/* Return connection error state. 433 * To make thread-safe, I need a seperate static 434 * variable for every possible error. 435 * has_error is the first field in xcb_connection_t, so just 436 * return a casted int here; checking has_error (and only 437 * has_error) will be safe. 438 */ 439xcb_connection_t *_xcb_conn_ret_error(int err) 440{ 441 442 switch(err) 443 { 444 case XCB_CONN_CLOSED_MEM_INSUFFICIENT: 445 { 446 return (xcb_connection_t *) &xcb_con_closed_mem_er; 447 } 448 case XCB_CONN_CLOSED_PARSE_ERR: 449 { 450 return (xcb_connection_t *) &xcb_con_closed_parse_er; 451 } 452 case XCB_CONN_CLOSED_INVALID_SCREEN: 453 { 454 return (xcb_connection_t *) &xcb_con_closed_screen_er; 455 } 456 case XCB_CONN_ERROR: 457 default: 458 { 459 return (xcb_connection_t *) &xcb_con_error; 460 } 461 } 462} 463 464int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count) 465{ 466 int ret; 467#if USE_POLL 468 struct pollfd fd; 469#else 470 fd_set rfds, wfds; 471#endif 472 473 /* If the thing I should be doing is already being done, wait for it. */ 474 if(count ? c->out.writing : c->in.reading) 475 { 476 pthread_cond_wait(cond, &c->iolock); 477 return 1; 478 } 479 480#if USE_POLL 481 memset(&fd, 0, sizeof(fd)); 482 fd.fd = c->fd; 483 fd.events = POLLIN; 484#else 485 FD_ZERO(&rfds); 486 FD_SET(c->fd, &rfds); 487#endif 488 ++c->in.reading; 489 490#if USE_POLL 491 if(count) 492 { 493 fd.events |= POLLOUT; 494 ++c->out.writing; 495 } 496#else 497 FD_ZERO(&wfds); 498 if(count) 499 { 500 FD_SET(c->fd, &wfds); 501 ++c->out.writing; 502 } 503#endif 504 505 pthread_mutex_unlock(&c->iolock); 506 do { 507#if USE_POLL 508 ret = poll(&fd, 1, -1); 509 /* If poll() returns an event we didn't expect, such as POLLNVAL, treat 510 * it as if it failed. */ 511 if(ret >= 0 && (fd.revents & ~fd.events)) 512 { 513 ret = -1; 514 break; 515 } 516#else 517 ret = select(c->fd + 1, &rfds, &wfds, 0, 0); 518#endif 519 } while (ret == -1 && errno == EINTR); 520 if(ret < 0) 521 { 522 _xcb_conn_shutdown(c, XCB_CONN_ERROR); 523 ret = 0; 524 } 525 pthread_mutex_lock(&c->iolock); 526 527 if(ret) 528 { 529 /* The code allows two threads to call select()/poll() at the same time. 530 * First thread just wants to read, a second thread wants to write, too. 531 * We have to make sure that we don't steal the reading thread's reply 532 * and let it get stuck in select()/poll(). 533 * So a thread may read if either: 534 * - There is no other thread that wants to read (the above situation 535 * did not occur). 536 * - It is the reading thread (above situation occurred). 537 */ 538 int may_read = c->in.reading == 1 || !count; 539#if USE_POLL 540 if(may_read && (fd.revents & POLLIN) != 0) 541#else 542 if(may_read && FD_ISSET(c->fd, &rfds)) 543#endif 544 ret = ret && _xcb_in_read(c); 545 546#if USE_POLL 547 if((fd.revents & POLLOUT) != 0) 548#else 549 if(FD_ISSET(c->fd, &wfds)) 550#endif 551 ret = ret && write_vec(c, vector, count); 552 } 553 554 if(count) 555 --c->out.writing; 556 --c->in.reading; 557 558 return ret; 559} 560 561uint64_t xcb_total_read(xcb_connection_t *c) 562{ 563 uint64_t n; 564 565 if (xcb_connection_has_error(c)) 566 return 0; 567 568 pthread_mutex_lock(&c->iolock); 569 n = c->in.total_read; 570 pthread_mutex_unlock(&c->iolock); 571 return n; 572} 573 574uint64_t xcb_total_written(xcb_connection_t *c) 575{ 576 uint64_t n; 577 578 if (xcb_connection_has_error(c)) 579 return 0; 580 581 pthread_mutex_lock(&c->iolock); 582 n = c->out.total_written; 583 pthread_mutex_unlock(&c->iolock); 584 585 return n; 586} 587