#include #include #include #include #include #include #include #include #include #include #include #include "lx.h" #include "proto.h" #include "internal.h" static void call_err(LX_CONN *xc, const LX_LIB_ERR *err, int teardown_opening) { if (xc->flags & XCF_OPENING) { (*xc->os->err)(err,xc->os->cbarg); if (teardown_opening) { lx__teardown_xc(xc); return; } } else { (*xc->lib_err)(xc,err); } xc->flags |= XCF_FAIL; } static void syserr(LX_CONN *xc, const char *call, int eno, int teardown_opening) { LX_LIB_ERR err; err.type = LXLE_SYSERR; err.u.syserr.call = call; err.u.syserr.err = eno; call_err(xc,&err,teardown_opening); } static void osliberr(OPEN_STATE *os, const char *call, const char *msg) { LX_LIB_ERR err; err.type = LXLE_OSLIBERR; err.u.osliberr.call = call; err.u.osliberr.msg = msg; (*os->err)(&err,os->cbarg); } static void open_send_first(LX_CONN *xc) { unsigned char first_header[12]; if (xc->flags & XCF_DEBUG) printf("open_send_first()\n"); first_header[0] = XP_ENDIAN_BE; // proto.c knows we always use BE first_header[1] = 0; // unused w_card16(&first_header[2],XP_MAJOR_VERSION); w_card16(&first_header[4],XP_MINOR_VERSION); // XXX need real authorization info support here! // But, well, "first make it work..." w_card16(&first_header[6],0); w_card16(&first_header[8],0); w_card16(&first_header[10],0); // unused // auth protocol name and data go here (both zero length now) if (aio_oq_queue_copy(&xc->oq,&first_header[0],12) < 0) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); if (xc->flags & XCF_DEBUG) printf("out of memory queueing initial client data"); xc->flags |= XCF_FAIL; return; } } static void first_failure(LX_CONN *xc) { OPEN_STATE *os; int n; int xdl; LX_LIB_ERR e; char *m; if (xc->flags & XCF_DEBUG) printf("first_failure\n"); os = xc->os; if (os->ihfill < 8) { lx_abort(); return; } n = os->ihdata[1]; xdl = r_card16(os->ihdata+6); if (xdl != ((n+3)>>2)) { lx__protoerr(xc,"setup reply: length wrong (n=%d, len=%d)",n,xdl); return; } m = malloc(n+1); if (! m) { call_err(xc,&lx__nomem_err,0); return; } bcopy(os->ihdata+8,m,n); m[n] = '\0'; e.type = LXLE_REJECTED; e.u.rejected.msg = m; call_err(xc,&e,0); free(m); } static void done_opening(LX_CONN *xc) { xc->flags &= ~XCF_OPENING; xc->text = xc->os->text; xc->os->text = 0; xc->pid = xc->os->pid; xc->os->pid = AIO_NOID; (*xc->os->done)(xc,xc->os->cbarg); lx__teardown_os(xc->os); xc->os = 0; xc->gotdata = &lx__proto_pkt; lx__proto_newpkt(xc); } static void first_success(LX_CONN *xc) { OPEN_STATE *os; char *vendor; int o; int i; int j; int k; unsigned int v; if (xc->flags & XCF_DEBUG) printf("first_success\n"); os = xc->os; if (os->ihfill < 40) { lx__protoerr(xc,"setup reply: success too short (len %d)",os->ihfill); return; } // [0] success boolean (tested by our caller) // [1] unused xc->proto_major = r_card16(os->ihdata+2); xc->proto_minor = r_card16(os->ihdata+4); v = r_card16(os->ihdata+6); if (os->ihfill != 8+(4*v)) lx_abort(); xc->release = r_card32(os->ihdata+8); xc->resource_base = r_card32(os->ihdata+12); xc->resource_mask = r_card32(os->ihdata+16); /* * The protocol document says "The resource-id-mask contains a single * contiguous set of bits (at least 18)". It does not say explicitly * that resource-id-mask&resource-id-base==0, but I consider that to * be implicit in that, if they do have any bits in common, then * client-constructed resources can collide. */ if (xc->resource_base & xc->resource_mask) { lx__protoerr(xc,"setup reply: resource-id-base and resource-id-mask overlap"); return; } xc->resource_inc = xc->resource_mask & ~(xc->resource_mask - 1); v = xc->resource_mask + xc->resource_inc; if ((v & (v-1)) || (xc->resource_mask/xc->resource_inc < 0x3ffff)) { lx__protoerr(xc,"setup reply: invalid resource-id-mask"); return; } xc->resource_val = 0; xc->motion_size = r_card32(os->ihdata+20); xc->vendor_len = r_card16(os->ihdata+24); xc->max_request = r_card16(os->ihdata+26); xc->nscreens = os->ihdata[28]; if (xc->sno >= xc->nscreens) { LX_LIB_ERR e; e.type = LXLE_NO_SCREEN; call_err(xc,&e,0); return; } xc->nformats = os->ihdata[29]; switch (os->ihdata[30]) { case 0: xc->ibo = LX_IBO_LSBFirst; break; case 1: xc->ibo = LX_IBO_MSBFirst; break; default: lx__protoerr(xc,"setup reply: invalid image-byte-order (%d)",os->ihdata[30]); return; break; } switch (os->ihdata[31]) { case 0: xc->bbo = LX_BBO_LeastSignificant; break; case 1: xc->bbo = LX_BBO_MostSignificant; break; default: lx__protoerr(xc,"setup reply: invalid bitmap-format-bit-order (%d)",os->ihdata[31]); return; break; } xc->scanline_unit = os->ihdata[32]; xc->scanline_pad = os->ihdata[33]; xc->keycode_min = os->ihdata[34]; xc->keycode_max = os->ihdata[35]; // 36..39 unused o = 40; if (o+xc->vendor_len > os->ihfill) { lx__protoerr(xc,"setup reply: vendor string overflows response"); return; } vendor = malloc(xc->vendor_len); if (vendor == 0) { call_err(xc,&lx__nomem_err,0); return; } bcopy(os->ihdata+40,vendor,xc->vendor_len); o += (xc->vendor_len + 3) & ~3U; xc->pmformats = malloc(xc->nformats*sizeof(PMFORMAT)); if (! xc->pmformats) { call_err(xc,&lx__nomem_err,0); return; } for (i=0;informats;i++) { PMFORMAT *f; if (o+8 > os->ihfill) { lx__protoerr(xc,"setup reply: pixmap format [%d] overflows response",i); free(xc->pmformats); xc->pmformats = 0; return; } f = xc->pmformats + i; f->depth = os->ihdata[o]; f->bpp = os->ihdata[o+1]; f->scanline_pad = os->ihdata[o+2]; // o+3 through o+7 unused o += 8; } xc->screens = malloc(xc->nscreens*sizeof(SCREEN)); if (! xc->screens) { call_err(xc,&lx__nomem_err,0); return; } for (i=0;inscreens;i++) xc->screens[i].depths = 0; for (i=0;inscreens;i++) { SCREEN *s; if (o+40 > os->ihfill) { lx__protoerr(xc,"setup reply: screen [%d] overflows response",i); return; } s = xc->screens + i; s->root = r_card32(os->ihdata+o); s->cmap = r_card32(os->ihdata+o+4); s->whitepixel = r_card32(os->ihdata+o+8); s->blackpixel = r_card32(os->ihdata+o+12); s->inputmasks = r_card32(os->ihdata+o+16); s->w_pixels = r_card16(os->ihdata+o+20); s->h_pixels = r_card16(os->ihdata+o+22); s->w_mm = r_card16(os->ihdata+o+24); s->h_mm = r_card16(os->ihdata+o+26); s->min_maps = r_card16(os->ihdata+o+28); s->max_maps = r_card16(os->ihdata+o+30); s->rootvisual = r_card32(os->ihdata+o+32); switch (os->ihdata[o+36]) { case 0: s->backingstore = BS_NEVER; break; case 1: s->backingstore = BS_WHENMAPPED; break; case 2: s->backingstore = BS_ALWAYS; break; default: lx__protoerr(xc,"setup reply: invalid backing-store (%d) for screen [%d]",os->ihdata[o+36],i); return; break; } switch (os->ihdata[o+37]) { case 0: case 1: s->saveunders = os->ihdata[o+37]; break; default: lx__protoerr(xc,"setup reply: invalid save-unders (%d) for screen [%d]",os->ihdata[o+37],i); return; break; } s->rootdepth = os->ihdata[o+38]; s->ndepths = os->ihdata[o+39]; s->depths = malloc(s->ndepths*sizeof(SCREENDEPTH)); if (! s->depths) { call_err(xc,&lx__nomem_err,0); return; } o += 40; for (j=0;jndepths;j++) { SCREENDEPTH *d; if (o+8 > os->ihfill) { lx__protoerr(xc,"setup reply: screen [%d] depth [%d] overflows response",i,j); return; } d = s->depths + j; d->depth = os->ihdata[o]; // o+1 unused d->nvisuals = r_card16(os->ihdata+o+2); // o+4 through o+7 unused o += 8; d->visuals = malloc(d->nvisuals*sizeof(VISUALTYPE)); if (! d->visuals) { call_err(xc,&lx__nomem_err,0); return; } for (k=0;knvisuals;k++) { VISUALTYPE *v; if (o+24 > os->ihfill) { lx__protoerr(xc,"setup reply: screen [%d] depth [%d] visual [%d] overflows response",i,j,k); return; } v = d->visuals + k; v->id = r_card32(os->ihdata+o); switch (os->ihdata[o+4]) { case 0: v->class = VC_StaticGray; break; case 1: v->class = VC_GrayScale; break; case 2: v->class = VC_StaticColor; break; case 3: v->class = VC_PseudoColor; break; case 4: v->class = VC_TrueColor; break; case 5: v->class = VC_DirectColor; break; default: lx__protoerr(xc,"setup reply: invalid visual class (%d) for screen [%d] depth [%d] visual [%d]",os->ihdata[o+37],i,j,k); return; break; } v->bitsperrgb = os->ihdata[o+5]; v->cmapentries = r_card16(os->ihdata+o+6); v->rmask = r_card32(os->ihdata+o+8); v->gmask = r_card32(os->ihdata+o+12); v->bmask = r_card32(os->ihdata+o+16); // o+20 through o+24 unused o += 24; } } } if (o != os->ihfill) { lx__protoerr(xc,"setup reply: leftover data (o %d, ihfill %d)\n",o,os->ihfill); return; } xc->next_serial = 1; if (os->flags & OSF_NO_PREAMBLE) { done_opening(xc); } else { // XXX preamble requests here done_opening(xc); } } static int gotdata_first(LX_CONN *xc, const void *data, int len) { OPEN_STATE *os; int n; if (len < 1) { xc->flags |= XCF_FAIL; return(0); } os = xc->os; n = os->ihwant - os->ihfill; if (n < 1) { lx_abort(); return(-1); } if (n > len) n = len; bcopy(data,os->ihdata+os->ihfill,n); os->ihfill += n; if (os->ihfill < os->ihwant) return(n); if (os->ihwant == 8) { os->ihwant = 8 + (r_card16(os->ihdata+6) * 4); if (os->ihfill < os->ihwant) return(n); } switch (os->ihdata[0]) { case 0: // failure first_failure(xc); break; case 1: // success first_success(xc); break; default: // protocol violation lx__protoerr(xc,"invalid s->c first octet %02x",os->ihdata[0]); break; } return(n); } static int wtest_xc(void *xcv) { LX_CONN *xc; xc = xcv; if (xc->flags & XCF_FAIL) return(0); return(aio_oq_nonempty(&xc->oq)); } static void rd_xc(void *xcv) { LX_CONN *xc; unsigned char rbuf[8192]; int nr; int off; int consumed; int zerocount; LX_LIB_ERR e; xc = xcv; if (xc->flags & XCF_FAIL) return; nr = read(xc->fd,&rbuf[0],sizeof(rbuf)); if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } if (xc->flags & XCF_DEBUG) { int e; e = errno; printf("connection read error: %s\n",strerror(e)); errno = e; } syserr(xc,"read",errno,1); return; } if (nr == 0) { (*xc->gotdata)(xc,0,0); e.type = LXLE_UNX_EOF; call_err(xc,&e,1); return; } zerocount = 0; off = 0; while (off < nr) { consumed = (*xc->gotdata)(xc,&rbuf[off],nr-off); if (xc->flags & XCF_FAIL) { lx__teardown_xc(xc); return; } if (xc->flags & XCF_DEBUG) printf("gotdata %p consumed %d\n",(void *)xc->gotdata,consumed); if (consumed < 0) lx_abort(); if (consumed == 0) { if (zerocount++ > 10) lx_abort(); continue; } if (consumed > nr-off) lx_abort(); off += consumed; zerocount = 0; } } static void wr_xc(void *xcv) { LX_CONN *xc; int nw; xc = xcv; if (xc->flags & XCF_FAIL) return; nw = aio_oq_writev(&xc->oq,xc->fd,-1); if (nw == AIO_WRITEV_ERROR) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } if (xc->flags & XCF_DEBUG) { int e; e = errno; printf("connection write error: %s\n",strerror(e)); errno = e; } syserr(xc,"write",errno,1); return; } aio_oq_dropdata(&xc->oq,nw); } static void open_connected(LX_CONN *xc) { if (xc->flags & XCF_DEBUG) printf("connected\n"); do <"fail"> { xc->fd = xc->os->fd; xc->os->fd = -1; open_send_first(xc); if (xc->flags & XCF_FAIL) break <"fail">; if (xc->os->pid != AIO_NOID) lx_abort(); xc->os->pid = aio_add_poll_of(xc->io,xc->fd,&aio_rwtest_always,&wtest_xc,&rd_xc,&wr_xc,xc); if (xc->os->pid == AIO_ERR) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); break <"fail">; } xc->gotdata = &gotdata_first; xc->os->ihdata = malloc(8); xc->os->ihwant = 8; xc->os->ihfill = 0; return; } while (0); lx__teardown_xc(xc); } static void os_connect_error(OPEN_STATE *os, const char *call, int err) { char *s; if (os->failmsg) { os->multifail = 1; return; } if (asprintf(&s,"%s: %s",call,strerror(err)) < 0) { call_err(os->conn,&lx__nomem_err,0); return; } os->failmsg = s; } static void all_fail(LX_CONN *xc) { LX_LIB_ERR err; if (xc->flags & XCF_FAIL) { lx__teardown_xc(xc); return; } if (!(xc->flags & XCF_OPENING) || !xc->os) lx_abort(); err.type = LXLE_ALL_FAIL; if (! xc->os->failmsg) { err.u.all_fail.msg = "couldn't create any sockets"; } else if (xc->os->multifail) { err.u.all_fail.msg = "ran out of addresses to try"; } else { err.u.all_fail.msg = xc->os->failmsg; } call_err(xc,&err,1); } static void open_check_connect_local(void *xcv) { LX_CONN *xc; int err; socklen_t errlen; xc = xcv; errlen = sizeof(err); if (getsockopt(xc->os->fd,SOL_SOCKET,SO_ERROR,&err,&errlen) < 0) { syserr(xc,"getsockopt",errno,1); return; } if (err == EINPROGRESS) return; // Can this happen? aio_remove_poll_of(xc->io,xc->os->pid); xc->os->pid = AIO_NOID; if (err == 0) { open_connected(xc); } else { os_connect_error(xc->os,"connect",err); all_fail(xc); } } static void open_local(LX_CONN *xc) { struct sockaddr_un *sa; int sl; char *path; int pl; int s; int debug; path = 0; sa = 0; debug = (xc->flags & XCF_DEBUG) ? 1 : 0; xc->os->ai0 = 0; do <"ret"> { do <"mem"> { pl = asprintf(&path,"/tmp/.X11-unix/X%d",xc->os->dispno); if (pl < 0) break <"mem">; if (asprintf(&xc->os->text,":%d",xc->os->dispno) < 0) break <"mem">; if (debug) printf("path is %s\n",path); sl = sizeof(struct sockaddr_un) - sizeof(sa->sun_path) + pl; sa = malloc(sl); if (! sa) break <"mem">; sa->sun_len = sl; sa->sun_family = AF_LOCAL; bcopy(path,&sa->sun_path[0],pl); s = socket(AF_LOCAL,SOCK_STREAM,0); if (s < 0) { syserr(xc,"socket",errno,1); break <"ret">; } xc->os->fd = s; fcntl(s,F_SETFL,fcntl(s,F_GETFL,0)|O_NONBLOCK); if (connect(s,(void *)sa,sl) < 0) { if (errno != EINPROGRESS) { os_connect_error(xc->os,"connect",errno); all_fail(xc); break <"ret">; } // Can EINPROGRESS happen with AF_LOCAL? if (debug) printf("connect() EINPROGRESS\n"); if (xc->os->pid != AIO_NOID) lx_abort(); xc->os->pid = aio_add_poll_of(xc->io,s,&aio_rwtest_never,&aio_rwtest_always,0,&open_check_connect_local,xc); if (xc->os->pid == AIO_ERR) { if (debug) printf("error adding local connect check poll"); break <"mem">; } } else { open_connected(xc); } break <"ret">; } while (0); (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); lx__teardown_xc(xc); } while (0); if (debug) printf("open_local() returning\n"); free(path); free(sa); } static int open_next_addr(void *); // forward static void open_check_connect_network(void *xcv) { LX_CONN *xc; int err; socklen_t errlen; xc = xcv; errlen = sizeof(err); if (getsockopt(xc->os->fd,SOL_SOCKET,SO_ERROR,&err,&errlen) < 0) { aio_remove_poll_of(xc->io,xc->os->pid); xc->os->pid = AIO_NOID; if (xc->flags & XCF_DEBUG) printf("connect %s SO_ERROR: %s\n",xc->os->text,strerror(errno)); if (xc->os->bid != AIO_NOID) lx_abort(); xc->os->bid = aio_add_block_of(xc->io,&open_next_addr,xc); return; } if (err == EINPROGRESS) return; // Can this happen? aio_remove_poll_of(xc->io,xc->os->pid); xc->os->pid = AIO_NOID; if (err == 0) { open_connected(xc); } else { if (xc->flags & XCF_DEBUG) printf("connect %s (finally): %s\n",xc->os->text,strerror(err)); os_connect_error(xc->os,"connect",err); if (xc->os->bid != AIO_NOID) lx_abort(); xc->os->bid = aio_add_block_of(xc->io,&open_next_addr,xc); } } static int open_next_addr(void *xcv) { LX_CONN *xc; int s; struct sockaddr_storage ss; int e; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; struct addrinfo *ai; const char *aftext; xc = xcv; if (xc->flags & XCF_DEBUG) printf("open_next_addr() removing block fn\n"); aio_remove_block_of(xc->io,xc->os->bid); xc->os->bid = AIO_NOID; do <"nextaddr"> { ai = xc->os->ai ? xc->os->ai->ai_next : xc->os->ai0; xc->os->ai = ai; if (xc->flags & XCF_DEBUG) printf("open_next_addr() trying %p\n",(void *)ai); if (! ai) { all_fail(xc); return(AIO_BLOCK_LOOP); } /* * getaddrinfo is supposed to get us away from having to do things * like this. But mapping dno into something appropriate is an * inherently protocol-specific action, so it's pretty much * unavoidable here. */ switch (ai->ai_family) { case AF_INET: if (ai->ai_addrlen != sizeof(struct sockaddr_in)) break <"nextaddr">; *(struct sockaddr_in *)&ss = *(struct sockaddr_in *)ai->ai_addr; ((struct sockaddr_in *)&ss)->sin_port = htons(6000+xc->os->dispno); aftext = "IPv4"; break; case AF_INET6: if (ai->ai_addrlen != sizeof(struct sockaddr_in6)) break <"nextaddr">; *(struct sockaddr_in6 *)&ss = *(struct sockaddr_in6 *)ai->ai_addr; ((struct sockaddr_in6 *)&ss)->sin6_port = htons(6000+xc->os->dispno); aftext = "IPv6"; break; default: break <"nextaddr">; } free(xc->os->text); e = getnameinfo((void *)&ss,ai->ai_addrlen,&hn[0],sizeof(hn),&pn[0],sizeof(pn),NI_NUMERICHOST|NI_NUMERICSERV); if (e) { e = asprintf(&xc->os->text,"[AF %d, getnameinfo failed %d (%s)]",ai->ai_family,e,gai_strerror(e)); } else { e = asprintf(&xc->os->text,"%s/%s",&hn[0],&pn[0]); } if (e < 0) { xc->os->text = 0; call_err(xc,&lx__nomem_err,1); return(AIO_BLOCK_LOOP); } if (xc->flags & XCF_DEBUG) printf("open_next_addr() trying %s\n",xc->os->text); s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) break <"nextaddr">; xc->os->fd = s; fcntl(s,F_SETFL,fcntl(s,F_GETFL,0)|O_NONBLOCK); if (connect(s,(void *)&ss,ai->ai_addrlen) < 0) { if (errno != EINPROGRESS) { os_connect_error(xc->os,"connect",errno); if (xc->flags & XCF_DEBUG) printf("open_next_addr(): connect %s: %s\n",xc->os->text,strerror(errno)); close(s); xc->os->fd = -1; break <"nextaddr">; } if (xc->flags & XCF_DEBUG) printf("open_next_addr() connect: EINPROGRESS\n"); if (xc->os->pid != AIO_NOID) lx_abort(); xc->os->pid = aio_add_poll_of(xc->io,s,&aio_rwtest_never,&aio_rwtest_always,0,&open_check_connect_network,xc); } else { open_connected(xc); } return(AIO_BLOCK_LOOP); } while (0); if (xc->flags & XCF_DEBUG) printf("open_next_addr() reinstating block fn\n"); if (xc->os->bid != AIO_NOID) lx_abort(); xc->os->bid = aio_add_block_of(xc->io,&open_next_addr,xc); return(AIO_BLOCK_LOOP); } // Wish we had a nonblocking version of getaddrinfo()! static void open_network(LX_CONN *xc, const char *name, int namelen) { char *nt; struct addrinfo hints; struct addrinfo *ai0; int e; hints.ai_flags = (xc->os->flags & OSF_NUMERIC_ADDR) ? AI_NUMERICHOST : 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; // XXX API botch hints.ai_canonname = 0; // XXX API botch hints.ai_addr = 0; // XXX API botch hints.ai_next = 0; // XXX API botch nt = malloc(namelen+1); do <"fail"> { if (! nt) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); break <"fail">; } bcopy(name,nt,namelen); nt[namelen] = '\0'; if (xc->flags & XCF_DEBUG) printf("open_network() calling getaddrinfo\n"); e = getaddrinfo(nt,0,&hints,&ai0); free(nt); if (e) { osliberr(xc->os,"getaddrinfo",gai_strerror(e)); break <"fail">; } if (! ai0) { osliberr(xc->os,"getaddrinfo","address lookup worked but returned no addresses"); break <"fail">; } if (xc->flags & XCF_DEBUG) printf("open_network() getaddrinfo worked\n"); xc->os->ai0 = ai0; xc->os->ai = 0; if (xc->os->bid != AIO_NOID) lx_abort(); xc->os->bid = aio_add_block_of(xc->io,&open_next_addr,xc); if (xc->flags & XCF_DEBUG) printf("open_network() returning\n"); return; } while (0); lx__teardown_xc(xc); } /* * The error handler used by lx_open if the application passes in nil. */ static void lx_open_err_default(const LX_LIB_ERR *e, void *cbarg __attribute__((__unused__))) { const char *msg; char *msgf; msgf = 0; switch (e->type) { case LXLE_NOMEM: msg = "out of memory"; break; case LXLE_SYSERR: if (asprintf(&msgf,"%s: %s",e->u.syserr.call,strerror(e->u.syserr.err)) < 0) { msgf = 0; msg = "out of memory"; } else { msg = msgf; } break; case LXLE_OSLIBERR: if (asprintf(&msgf,"%s%s%s",e->u.osliberr.call,e->u.osliberr.msg?": ":"",e->u.osliberr.msg?e->u.osliberr.msg:"") < 0) { msgf = 0; msg = "out of memory"; } else { msg = msgf; } break; case LXLE_NO_DISPLAY: msg = "no display argument and no $DISPLAY"; break; case LXLE_BAD_DISPLAY: msg = "invalid display string"; break; case LXLE_NO_SCREEN: msg = "bad screen number"; break; case LXLE_UNX_EOF: msg = "unexpected connection EOF"; break; case LXLE_ALL_FAIL: if (asprintf(&msgf,"can't connect: %s",e->u.proto_err.msg) < 0) { msgf = 0; msg = "out of memory"; } else { msg = msgf; } break; case LXLE_PROTO_ERR: if (asprintf(&msgf,"protocol error: %s",e->u.proto_err.msg) < 0) { msgf = 0; msg = "out of memory"; } else { msg = msgf; } break; case LXLE_REJECTED: if (asprintf(&msgf,"connection rejected: %s",e->u.proto_err.msg) < 0) { msgf = 0; msg = "out of memory"; } else { msg = msgf; } break; default: lx_abort(); msg = "impossible lx_open error"; break; } fprintf(stderr,"lx_open: %s\n",msg); free(msgf); exit(1); } /* * Open the connection to the X server. */ void lx_open( const char *dispname, void (*err)(const LX_LIB_ERR *, void *), void (*done)(LX_CONN *, void *), AIO_LOOP *loop, void *cbarg, unsigned int flags ) { OPEN_STATE *os; const char *colon; int dno; int sno; int n1; int n2; LX_CONN *xc; LX_LIB_ERR liberr; if (! err) err = &lx_open_err_default; if (! dispname) { dispname = getenv("DISPLAY"); if (! dispname) { if (flags & LX_OPENF_DEBUG) printf("FAIL: no arg string and no $DISPLAY\n"); liberr.type = LXLE_NO_DISPLAY; (*err)(&liberr,cbarg); return; } } if (flags & LX_OPENF_DEBUG) printf("using name %s\n",dispname); colon = rindex(dispname,':'); if (! colon) { if (flags & LX_OPENF_DEBUG) printf("FAIL: no : in string: %s\n",dispname); liberr.type = LXLE_BAD_DISPLAY; (*err)(&liberr,cbarg); return; } n1 = -1; n2 = -1; sscanf(colon+1,"%d.%d %n%*c%n",&dno,&sno,&n1,&n2); if (n2 >= 0) { if (flags & LX_OPENF_DEBUG) printf("FAIL: junk after screen number: %s\n",dispname); liberr.type = LXLE_BAD_DISPLAY; (*err)(&liberr,cbarg); return; } if (n1 < 0) { n1 = -1; n2 = -1; sscanf(colon+1,"%d %n%*c%n",&dno,&n1,&n2); if (n2 >= 0) { if (flags & LX_OPENF_DEBUG) printf("FAIL: junk after display number: %s\n",dispname); liberr.type = LXLE_BAD_DISPLAY; (*err)(&liberr,cbarg); return; } if (n1 < 0) { if (flags & LX_OPENF_DEBUG) printf("FAIL: no display number: %s\n",dispname); liberr.type = LXLE_BAD_DISPLAY; (*err)(&liberr,cbarg); return; } sno = 0; } if (sno < 0) { if (flags & LX_OPENF_DEBUG) printf("FAIL: negative screen number %d\n",sno); liberr.type = LXLE_BAD_DISPLAY; (*err)(&liberr,cbarg); return; } xc = malloc(sizeof(LX_CONN)); if (! xc) { if (flags & LX_OPENF_DEBUG) printf("FAIL: no memory for LX_CONN\n"); (*err)(&lx__nomem_err,cbarg); return; } os = malloc(sizeof(OPEN_STATE)); if (! os) { if (flags & LX_OPENF_DEBUG) printf("FAIL: no memory for OPEN_STATE\n"); (*err)(&lx__nomem_err,cbarg); free(xc); return; } xc->flags = XCF_OPENING; if (flags & LX_OPENF_DEBUG) xc->flags |= XCF_DEBUG; xc->fd = -1; xc->sno = sno; xc->io = loop ? loop : aio_global_loop(); aio_oq_init(&xc->oq); xc->pid = AIO_NOID; xc->vendor = 0; xc->pmformats = 0; xc->screens = 0; xc->os = os; xc->pendingf = 0; xc->pendingb = 0; xc->text = 0; xc->rb = 0; xc->rba = 0; xc->rbl = 0; xc->udata = 0; // These depend on lx_err_set_* working on partially-set-up LX_CONNs lx_err_set_X(xc,0); lx_err_set_lib(xc,0); xc->event = 0; os->conn = xc; os->flags = 0; os->err = err; os->done = done; os->cbarg = cbarg; os->dispno = dno; os->ai0 = 0; os->fd = -1; os->pid = AIO_NOID; os->bid = AIO_NOID; os->text = 0; os->failmsg = 0; os->multifail = 0; if (flags & LX_OPENF_NO_PREAMBLE) os->flags |= OSF_NO_PREAMBLE; if ( (colon == dispname) || ((colon-dispname == 4) && !bcmp(dispname,"unix",4)) ) { if (flags & LX_OPENF_DEBUG) printf("local open\n"); open_local(xc); } else if ((dispname[0] == '[') && (colon[-1] == ']')) { if (flags & LX_OPENF_DEBUG) printf("network numeric address\n"); os->flags |= OSF_NUMERIC_ADDR; open_network(xc,dispname+1,(colon-1)-(dispname+1)); } else { if (flags & LX_OPENF_DEBUG) printf("network name\n"); open_network(xc,dispname,colon-dispname); } if (flags & LX_OPENF_DEBUG) printf("DONE: returning\n"); }