*/ DEVICE_CMD Write_Serial(REBREQ *req) /* ***********************************************************************/ { DWORD result = 0; DWORD len = req->length - req->actual; if (!req->requestee.handle) { req->error = -RFE_NO_HANDLE; return DR_ERROR; } if (len <= 0) return DR_DONE; if (!WriteFile( req->requestee.handle, req->common.data, len, &result, NULL )) { req->error = -RFE_BAD_WRITE; Signal_Device(req, EVT_ERROR); return DR_ERROR; } #ifdef DEBUG_SERIAL printf("write %d ret: %d\n", req->length, req->actual); #endif req->actual += result; req->common.data += result; if (req->actual >= req->length) { Signal_Device(req, EVT_WROTE); return DR_DONE; } else { SET_FLAG(req->flags, RRF_ACTIVE); /* notify OS_WAIT of activity */ return DR_PEND; } }
// // Read_Signal: C // DEVICE_CMD Read_Signal(REBREQ *req) { struct timespec timeout = {0, 0}; unsigned int i = 0; errno = 0; for (i = 0; i < req->length; i ++) { int result = sigtimedwait( &req->special.signal.mask, &(cast(siginfo_t*, req->common.data)[i]), &timeout ); if (result < 0) { if (errno != EAGAIN && i == 0) { Signal_Device(req, EVT_ERROR); return DR_ERROR; } else { break; } } } req->actual = i; if (i > 0) { //printf("read %d signals\n", req->actual); Signal_Device(req, EVT_READ); return DR_DONE; } else { return DR_PEND; } }
*/ DEVICE_CMD Read_Serial(REBREQ *req) /* ***********************************************************************/ { DWORD result = 0; if (!req->requestee.handle) { req->error = -RFE_NO_HANDLE; return DR_ERROR; } //RL_Print("reading %d bytes\n", req->length); if (!ReadFile(req->requestee.handle, req->common.data, req->length, &result, 0)) { req->error = -RFE_BAD_READ; Signal_Device(req, EVT_ERROR); return DR_ERROR; } else { if (result == 0) { return DR_PEND; } else if (result > 0){ //RL_Print("read %d bytes\n", req->actual); req->actual = result; Signal_Device(req, EVT_READ); } } #ifdef DEBUG_SERIAL printf("read %d ret: %d\n", req->length, req->actual); #endif return DR_DONE; }
// // Open_Signal: C // DEVICE_CMD Open_Signal(REBREQ *req) { //RL_Print("Open_Signal\n"); sigset_t mask; sigset_t overlap; #ifdef CHECK_MASK_OVERLAP //doesn't work yet if (sigprocmask(SIG_BLOCK, NULL, &mask) < 0) { goto error; } if (sigandset(&overlap, &mask, &req->special.signal.mask) < 0) { goto error; } if (!sigisemptyset(&overlap)) { req->error = EBUSY; return DR_ERROR; } #endif if (sigprocmask(SIG_BLOCK, &req->special.signal.mask, NULL) < 0) { goto error; } SET_OPEN(req); Signal_Device(req, EVT_OPEN); return DR_DONE; error: req->error = errno; return DR_ERROR; }
*/ DEVICE_CMD Accept_Socket(REBREQ *sock) /* ** Accept an inbound connection on a TCP listen socket. ** ** The function will return: ** =0: succeeded ** >0: in-progress, still trying ** <0: error occurred, no longer trying ** ** Before usage: ** Open_Socket(); ** Set local_port to desired port number. ** Listen_Socket(); ** ***********************************************************************/ { SOCKAI sa; REBREQ *news; int len = sizeof(sa); int result; extern void Attach_Request(REBREQ **prior, REBREQ *req); // Accept a new socket, if there is one: result = accept(sock->socket, (struct sockaddr *)&sa, &len); if (result == BAD_SOCKET) { result = GET_ERROR; if (result == NE_WOULDBLOCK) return DR_PEND; sock->error = result; //Signal_Device(sock, EVT_ERROR); return DR_ERROR; } // To report the new socket, the code here creates a temporary // request and copies the listen request to it. Then, it stores // the new values for IP and ports and links this request to the // original via the sock->data. news = MAKE_NEW(*news); // Be sure to deallocate it CLEARS(news); // *news = *sock; news->device = sock->device; SET_OPEN(news); SET_FLAG(news->state, RSM_OPEN); SET_FLAG(news->state, RSM_CONNECT); news->socket = result; news->net.remote_ip = sa.sin_addr.s_addr; //htonl(ip); NOTE: REBOL stays in network byte order news->net.remote_port = ntohs(sa.sin_port); Get_Local_IP(news); //Nonblocking_Mode(news->socket); ???Needed? Attach_Request((REBREQ**)&sock->data, news); Signal_Device(sock, EVT_ACCEPT); // Even though we signalled, we keep the listen pending to // accept additional connections. return DR_PEND; }
*/ int OS_Do_Device(REBREQ *req, REBCNT command) /* ** Tell a device to perform a command. Non-blocking in many ** cases and will attach the request for polling. ** ** Returns: ** =0: for command success ** >0: for command still pending ** <0: for command error ** ***********************************************************************/ { REBDEV *dev; REBINT result; req->error = 0; // A94 - be sure its cleared // Validate device: if (req->device >= RDI_MAX || !(dev = Devices[req->device])) { req->error = RDE_NO_DEVICE; return -1; } // Confirm device is initialized. If not, return an error or init // it if auto init option is set. if (!GET_FLAG(dev->flags, RDF_INIT)) { if (GET_FLAG(dev->flags, RDO_MUST_INIT)) { req->error = RDE_NO_INIT; return -1; } if (!dev->commands[RDC_INIT] || !dev->commands[RDC_INIT]((REBREQ*)dev)) SET_FLAG(dev->flags, RDF_INIT); } // Validate command: if (command > dev->max_command || dev->commands[command] == 0) { req->error = RDE_NO_COMMAND; return -1; } // Do the command: req->command = command; result = dev->commands[command](req); // If request is pending, attach it to device for polling: if (result > 0) Attach_Request(&dev->pending, req); else if (dev->pending) { Detach_Request(&dev->pending, req); // often a no-op if (result == DR_ERROR && GET_FLAG(req->flags, RRF_ALLOC)) { // not on stack Signal_Device(req, EVT_ERROR); } } return result; }
*/ DEVICE_CMD Poll_DNS(REBREQ *dr) /* ** Check for completed DNS requests. These are marked with ** RRF_DONE by the windows message event handler (dev-event.c). ** Completed requests are removed from the pending queue and ** event is signalled (for awake dispatch). ** ***********************************************************************/ { REBDEV *dev = (REBDEV*)dr; // to keep compiler happy REBREQ **prior = &dev->pending; REBREQ *req; BOOL change = FALSE; HOSTENT *host; // Scan the pending request list: for (req = *prior; req; req = *prior) { // If done or error, remove command from list: if (GET_FLAG(req->flags, RRF_DONE)) { // req->error may be set *prior = req->next; req->next = 0; CLR_FLAG(req->flags, RRF_PENDING); if (!req->error) { // success! host = (HOSTENT*)req->net.host_info; if (GET_FLAG(req->modes, RST_REVERSE)) req->data = host->h_name; else COPY_MEM((char*)&(req->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); Signal_Device(req, EVT_READ); } else Signal_Device(req, EVT_ERROR); change = TRUE; } else prior = &req->next; } return change; }
*/ DEVICE_CMD Transfer_Socket(REBREQ *sock) /* ** Write or read a socket (for connection-based protocols). ** ** This function is asynchronous. It will return immediately. ** You can call this function again to check the pending connection. ** ** The mode is RSM_RECEIVE or RSM_SEND. ** ** The function will return: ** =0: succeeded ** >0: in-progress, still trying ** <0: error occurred, no longer trying ** ** Before usage: ** Open_Socket() ** Connect_Socket() ** Verify that RSM_CONNECT is true ** Setup the sock->data and sock->length ** ** Note that the mode flag is cleared by the caller, not here. ** ***********************************************************************/ { int result; long len; int mode = (sock->command == RDC_READ ? RSM_RECEIVE : RSM_SEND); if (!GET_FLAG(sock->state, RSM_CONNECT)) { sock->error = -18; return DR_ERROR; } SET_FLAG(sock->state, mode); // Limit size of transfer: len = MIN(sock->length, MAX_TRANSFER); if (mode == RSM_SEND) { // If host is no longer connected: result = send(sock->socket, sock->data, len, 0); WATCH2("send() len: %d actual: %d\n", len, result); if (result >= 0) { sock->data += result; sock->actual += result; if (sock->actual >= sock->length) { Signal_Device(sock, EVT_WROTE); return DR_DONE; } return DR_PEND; } // if (result < 0) ... } else { result = recv(sock->socket, sock->data, len, 0); WATCH2("recv() len: %d result: %d\n", len, result); if (result > 0) { sock->actual = result; Signal_Device(sock, EVT_READ); return DR_DONE; } if (result == 0) { // The socket gracefully closed. sock->actual = 0; CLR_FLAG(sock->state, RSM_CONNECT); // But, keep RRF_OPEN true Signal_Device(sock, EVT_CLOSE); return DR_DONE; } // if (result < 0) ... } // Check error code: result = GET_ERROR; WATCH2("get error: %d %s\n", result, strerror(result)); if (result == NE_WOULDBLOCK) return DR_PEND; // still waiting WATCH4("ERROR: recv(%d %x) len: %d error: %d\n", sock->socket, sock->data, len, result); // A nasty error happened: sock->error = result; //Signal_Device(sock, EVT_ERROR); return DR_ERROR; }
*/ DEVICE_CMD Connect_Socket(REBREQ *sock) /* ** Connect a socket to a service. ** Only required for connection-based protocols (e.g. not UDP). ** The IP address must already be resolved before calling. ** ** This function is asynchronous. It will return immediately. ** You can call this function again to check the pending connection. ** ** The function will return: ** =0: connection succeeded (or already is connected) ** >0: in-progress, still trying ** <0: error occurred, no longer trying ** ** Before usage: ** Open_Socket() -- to allocate the socket ** ***********************************************************************/ { int result; SOCKAI sa; if (GET_FLAG(sock->modes, RST_LISTEN)) return Listen_Socket(sock); if (GET_FLAG(sock->state, RSM_CONNECT)) return DR_DONE; // already connected Set_Addr(&sa, sock->net.remote_ip, sock->net.remote_port); result = connect(sock->socket, (struct sockaddr *)&sa, sizeof(sa)); if (result != 0) result = GET_ERROR; WATCH2("connect() error: %d - %s\n", result, strerror(result)); switch (result) { case 0: // no error case NE_ISCONN: // Connected, set state: CLR_FLAG(sock->state, RSM_ATTEMPT); SET_FLAG(sock->state, RSM_CONNECT); Get_Local_IP(sock); Signal_Device(sock, EVT_CONNECT); return DR_DONE; // done #ifdef TO_WIN32 case NE_INVALID: // Corrects for Microsoft bug #endif case NE_WOULDBLOCK: case NE_INPROGRESS: case NE_ALREADY: // Still trying: SET_FLAG(sock->state, RSM_ATTEMPT); return DR_PEND; default: // An error happened: CLR_FLAG(sock->state, RSM_ATTEMPT); sock->error = result; //Signal_Device(sock, EVT_ERROR); return DR_ERROR; } }
*/ DEVICE_CMD Lookup_Socket(REBREQ *sock) /* ** Initiate the GetHost request and return immediately. ** This is very similar to the DNS device. ** The request will pend until the main event handler gets WM_DNS. ** Note the temporary results buffer (must be freed later). ** Note we use the sock->handle for the DNS handle. During use, ** we store the TCP socket in the length field. ** ***********************************************************************/ { #ifdef TO_WIN32 HANDLE handle; #endif HOSTENT *host; #ifdef HAS_ASYNC_DNS // Check if we are polling for completion: if (host = (HOSTENT*)(sock->net.host_info)) { // The windows main event handler will change this when it gets WM_DNS event: if (!GET_FLAG(sock->flags, RRF_DONE)) return DR_PEND; // still waiting CLR_FLAG(sock->flags, RRF_DONE); if (!sock->error) { // Success! host = (HOSTENT*)sock->net.host_info; COPY_MEM((char*)&(sock->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); Signal_Device(sock, EVT_LOOKUP); } else Signal_Device(sock, EVT_ERROR); OS_Free(host); // free what we allocated earlier sock->socket = sock->length; // Restore TCP socket saved below sock->net.host_info = 0; return DR_DONE; } // Else, make the lookup request: host = OS_Make(MAXGETHOSTSTRUCT); // be sure to free it handle = WSAAsyncGetHostByName(Event_Handle, WM_DNS, sock->data, (char*)host, MAXGETHOSTSTRUCT); if (handle != 0) { sock->net.host_info = host; sock->length = sock->socket; // save TCP socket temporarily sock->handle = handle; return DR_PEND; // keep it on pending list } OS_Free(host); #else // Use old-style blocking DNS (mainly for testing purposes): host = gethostbyname(sock->data); sock->net.host_info = 0; // no allocated data if (host) { COPY_MEM((char*)&(sock->net.remote_ip), (char *)(*host->h_addr_list), 4); //he->h_length); CLR_FLAG(sock->flags, RRF_DONE); Signal_Device(sock, EVT_LOOKUP); return DR_DONE; } #endif sock->error = GET_ERROR; //Signal_Device(sock, EVT_ERROR); return DR_ERROR; // Remove it from pending list }