#include #include #include #include #include #include #include #include #include int sherver_bind_builtin(WORD_LIST *args) { uint quiet = 0; char *host = "127.0.0.1"; uint port = 0; char *var_name = "SHERVER_FD"; uint arg = 0; reset_internal_getopt(); while((arg = internal_getopt(args, "qh:p:v:")) != -1) { if(arg == 'q') { quiet = 1; } else if(arg == 'h') { host = list_optarg; } else if(arg == 'p') { char *end = NULL; unsigned long res = strtoul(list_optarg, &end, 10); if(*end != '\0' || res > 65535) { builtin_usage(); return EX_USAGE; } port = res; } else if(arg == 'v') { var_name = list_optarg; } else { builtin_usage(); return EX_USAGE; } } unbind_variable(var_name); int server_sock = 0; int on = 1; if((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { int cached = errno; if(!quiet) { builtin_error("cannot open server socket: %s", strerror(errno)); } return errno; } else if(setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { int cached = errno; if(!quiet) { builtin_error("cannot configure server socket: %s", strerror(errno)); } return errno; } int flags = fcntl(server_sock, F_GETFL, 0); if(flags < 0) { int cached = errno; if(!quiet) { builtin_error("cannot obtain information on socket: %s", strerror(errno)); } return errno; } else if(fcntl(server_sock, F_SETFL, flags | O_NONBLOCK) < 0) { int cached = errno; if(!quiet) { builtin_error("cannot make server socket non-blocking: %s", strerror(errno)); } return errno; } struct sockaddr_in server; memset(&server, 0, sizeof(server)); inet_pton(AF_INET, host, &server.sin_addr); server.sin_family = AF_INET; server.sin_port = htons(port); if(bind(server_sock, (struct sockaddr *) &server, sizeof(server)) < 0) { int cached = errno; if(!quiet) { builtin_error("cannot bind to host: %s", strerror(errno)); } return errno; } if(listen(server_sock, 0) < 0) { int cached = errno; if(!quiet) { builtin_error("cannot start listening on host: %s", strerror(errno)); } return errno; } char ibuf[INT_STRLEN_BOUND(int) + 1] = { '\0' }; char *sock_str = fmtulong(server_sock, 10, ibuf, sizeof(ibuf), 0); bind_global_variable(var_name, sock_str, 0); return EXECUTION_SUCCESS; } char *sherver_bind_doc[] = { "Open a socket on a network interface for accepting connections.", "", "Binds a socket to a specified interface and port for accepting incoming", "connections. If NAME is supplied (via -v), then the server fd is stored in", "a variable with that name. Otherwise, the name SHERVER_FD is used.", "", "Options:", " -q enable quiet mode. Do not report error messages.", " -h HOST the HOST to bind to for incoming connections.", " -p PORT the PORT to listen for incoming connections.", " -v NAME the NAME to bind the server fd to.", "", "Exit Status:", "On a successful binding, sherver-bind exits with a status of 0.", "Otherwise, if an intermediate network operation fails, it returns with", "the error code from that failure. SHERVER_FD (or the variable name", "supplied with -v) is unset in the event of a failure.", (char *) NULL }; struct builtin sherver_bind_struct = { "sherver-bind", sherver_bind_builtin, BUILTIN_ENABLED, sherver_bind_doc, "sherver-bind [-q] [-h HOST] [-p PORT] [-v NAME]", 0 }; int sherver_select_builtin(WORD_LIST *args) { uint quiet = 0; uint ignore_timeout = 0; time_t timeout_secs = 5; char *var_name = "READY_FDS"; uint arg = 0; reset_internal_getopt(); while((arg = internal_getopt(args, "qit:v:")) != -1) { if(arg == 'q') { quiet = 1; } else if(arg == 'i') { ignore_timeout = 1; } else if(arg == 'v') { var_name = list_optarg; } else if(arg == 't') { char *end = NULL; unsigned long res = strtoul(list_optarg, &end, 10); if(*end != '\0') { builtin_usage(); return EX_USAGE; } timeout_secs = res; } else { builtin_usage(); return EX_USAGE; } } int max_fd = 0; fd_set read_set, reads; FD_ZERO(&read_set); for(args = loptend; args; args = args->next) { char *end = NULL; uint fd = strtoul(args->word->word, &end, 10); if(*end != '\0' || fd >= FD_SETSIZE) { if(!quiet) { builtin_error("can only monitor file descriptors less than %lu", fd); } continue; } else if(fd > max_fd) { max_fd = fd; } FD_SET(fd, &read_set); } SHELL_VAR *var = find_or_make_array_variable(var_name, 1); if(var == 0 || readonly_p(var) || noassign_p(var)) { if(!quiet) { builtin_error("unable to create array variable %s", var_name); } return EXECUTION_FAILURE; } else if(array_p(var) == 0) { if(!quiet) { builtin_error("%s is not an indexed array", var_name); } return EXECUTION_FAILURE; } if(invisible_p(var)) { VUNSETATTR(var, att_invisible); } array_flush(array_cell(var)); while(1) { struct timeval timeout = { .tv_sec = timeout_secs, .tv_usec = 0 }; memcpy(&reads, &read_set, sizeof(read_set)); int result = select(max_fd + 1, &reads, NULL, NULL, &timeout); if(!ignore_timeout && result == 0) { return EAGAIN; } else if(result < 0) { int cached = errno; if(!quiet) { builtin_error("unable to poll file descriptors: %s", strerror(errno)); } return cached; } else if(result > 0) { break; } } int fd = 0; int idx = 0; for(; fd < max_fd + 1; fd += 1) { if(FD_ISSET(fd, &reads)) { char fbuf[FD_SETSIZE] = { '\0' }; char *fd_str = fmtulong(fd, 10, fbuf, sizeof(fbuf), 0); bind_array_element(var, idx, fd_str, 0); idx += 1; } } return EXECUTION_SUCCESS; } char *sherver_select_doc[] = { "Monitor a list of file descriptors for reading activity.", "", "Takes a list of 0 or more file descriptors to monitor for reading activity.", "`sherver-select` will monitor for up to SECONDS seconds (a default of 5)", "unless -i is provided, in which case the timeout is ignored. Any file", "descriptors that are ready for reading are copied into the array named NAME", "(which defaults to READY_FDS).", "", "Options:", " -q enable quiet mode. Do not report error messages.", " -i ignore the timeout and try again (effectively disables -t).", " -t SECONDS the number of SECONDS to monitor for.", " -v NAME the NAME to bind the server fd to.", "", "Exit Status:", "If successful, sherver-select returns 0. Otherwise, it returns the error", "code of select(2).", (char *) NULL }; struct builtin sherver_select_struct = { "sherver-select", sherver_select_builtin, BUILTIN_ENABLED, sherver_select_doc, "sherver-select [-q] [-i] [-t SECONDS] [-v NAME] [FD ...]", 0 }; int sherver_accept_builtin(WORD_LIST *args) { uint quiet = 0; char *var_name = "CLIENT_INFO"; uint arg = 0; reset_internal_getopt(); while((arg = internal_getopt(args, "qv:")) != -1) { if(arg == 'q') { quiet = 1; } else if(arg == 'v') { var_name = list_optarg; } else { builtin_usage(); return EX_USAGE; } } args = loptend; if(!args) { if(!quiet) { builtin_error("no server file descriptor given."); } return EXECUTION_FAILURE; } char *end = NULL; unsigned long server_fd = strtoul(args->word->word, &end, 10); if(*end != '\0') { if(!quiet) { builtin_error("'%s' is not a valid file descriptor.", args->word->word); } return EXECUTION_FAILURE; } struct sockaddr_in peer; socklen_t peer_size = sizeof(peer); int client_sock = accept(server_fd, (struct sockaddr *) &peer, &peer_size); if(client_sock < 0) { int cached = errno; if(!quiet) { builtin_error("could not accept connection: %s", strerror(errno)); } return cached; } char ip[INET_ADDRSTRLEN] = { '\0' }; unsigned short port = ntohs(peer.sin_port); inet_ntop(AF_INET, &peer.sin_addr, ip, INET_ADDRSTRLEN); SHELL_VAR *var = find_or_make_array_variable(var_name, 1); char sbuf[FD_SETSIZE] = { '\0' }; char pbuf[6] = { '\0' }; char *sock_str = fmtulong(client_sock, 10, sbuf, sizeof(sbuf), 0); char *port_str = fmtulong(port, 10, pbuf, sizeof(pbuf), 0); if(var == 0 || readonly_p(var) || noassign_p(var)) { if(!quiet) { builtin_error("unable to create array variable %s", var_name); } return EXECUTION_FAILURE; } else if(array_p(var) == 0) { if(!quiet) { builtin_error("%s is not an indexed array", var_name); } return EXECUTION_FAILURE; } if(invisible_p(var)) { VUNSETATTR(var, att_invisible); } array_flush(array_cell(var)); bind_array_element(var, 0, sock_str, 0); bind_array_element(var, 1, ip, 0); bind_array_element(var, 2, port_str, 0); return EXECUTION_SUCCESS; } char *sherver_accept_doc[] = { "Accept a connection from a server socket file descriptor.", "", "Accepts a connection from a server file descriptor. The client file", "descriptor, client ip address, and client port are stored in the array", "named NAME (default CLIENT_INFO) at indexes 0, 1, and 2 respectively.", "", "Options:", " -q enable quiet mode. Do not report error messages.", " -v NAME the NAME to bind the server fd to.", "", "Exit Status:", "If successful, sherver-accept returns 0. Otherwise, it returns the error", "code of accept(2).", (char *) NULL }; struct builtin sherver_accept_struct = { "sherver-accept", sherver_accept_builtin, BUILTIN_ENABLED, sherver_accept_doc, "sherver-accept [-q] [-v NAME] [SERVER FD]", 0 };