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 62 typedef struct { 63 uint8_t status; 64 uint8_t pad0[5]; 65 uint16_t length; 66 } xcb_setup_generic_t; 67 68 static 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()! */ 89 static const int xcb_con_error = XCB_CONN_ERROR; 90 static const int xcb_con_closed_mem_er = XCB_CONN_CLOSED_MEM_INSUFFICIENT; 91 static const int xcb_con_closed_parse_er = XCB_CONN_CLOSED_PARSE_ERR; 92 static const int xcb_con_closed_screen_er = XCB_CONN_CLOSED_INVALID_SCREEN; 93 94 static 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 102 static 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 127 static 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 171 static 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. */ 217 static 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 332 const 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 340 int 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 348 int 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 354 xcb_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 398 void 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 429 void _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 */ 441 xcb_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 466 int _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 563 uint64_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 576 uint64_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