For a project I'm building a "super-server" like inetd. It is supposed to read a set of ports and commands from a config file, and for each port spin up a listener socket. It should then use select() to determine when one or more of these sockets is ready to read from. When select finds a socket, it should use accept() to connect to this socket, and then fork() a child process in which the command will be executed. Unfortunately, select is always either timing out or failing when I try to call "nc -l localhost 12345" to test it (with '12345 echo "hello world"' in the config.txt file).
Can you spot anything I might be doing wrong? Thanks in advance! I've been going crazy trying to figure this out!
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
using namespace std;
map<int,string> parse_config_file() {
string line;
ifstream file;
stringstream ss;
int port;
string command;
map<int,string> port_to_command;
file.open("config.txt");
while (getline(file,line)) {
ss = stringstream(line);
ss >> port;
getline(ss,command);
port_to_command[port] = command;
}
file.close();
return port_to_command;
}
void handle_client(int socket, string command) {
dup2(socket, STDIN_FILENO);
dup2(socket, STDOUT_FILENO);
execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL);
}
int main(int argc, const char * argv[]) {
int rc;
int readyfd;
int peerfd;
int maxfd = 0;
int port;
pid_t child_pid;
fd_set readfds;
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
struct sockaddr* server_address;
socklen_t server_address_length = sizeof(server_address);
struct sockaddr client_address;
socklen_t client_address_length = sizeof(client_address);
map<int,string> port_to_command = parse_config_file();
map<int,string>::iterator pcitr;
map<int,int> socket_to_port;
map<int,int>::iterator spitr;
// Create, bind, and listen on the sockets:
for (pcitr = port_to_command.begin(); pcitr != port_to_command.end(); pcitr++) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
cerr << "ERROR opening socket";
exit(EXIT_FAILURE);
}
port = pcitr->first;
struct sockaddr_in server_address_internet;
bzero((char *) &server_address_internet, sizeof(server_address_internet));
server_address_internet.sin_family = AF_INET;
server_address_internet.sin_addr.s_addr = INADDR_ANY;
server_address_internet.sin_port = htons(port);
server_address = (struct sockaddr *)&server_address_internet;
bind(sockfd, server_address, server_address_length);
rc = listen(sockfd, 10);
if (rc < 0) {
cerr << "listen() failed";
exit(EXIT_FAILURE);
}
socket_to_port[sockfd] = pcitr->first;
if (sockfd > maxfd) {
maxfd = sockfd;
}
}
// Server Loop
while (true) {
// Rebuild the FD set:
FD_ZERO(&readfds);
for (spitr = socket_to_port.begin(); spitr != socket_to_port.end(); spitr++) {
FD_SET(spitr->first, &readfds);
}
// Select
rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
if (rc == 0) {
// Timeout
continue;
} else if (rc < 0) {
cerr << "select failed" << endl;
exit(EXIT_FAILURE);
}
// Find the socket that is ready to be read:
readyfd = -1;
for (spitr = socket_to_port.begin(); spitr != socket_to_port.end(); spitr++) {
if (FD_ISSET(spitr->first, &readfds)) {
readyfd = spitr->first;
break;
}
}
// Accept
peerfd = accept(readyfd, &client_address, &client_address_length);
if (peerfd < 0) {
cerr << "accept failed" << endl;
exit(EXIT_FAILURE);
}
// Fork to handle request:
child_pid = fork();
if (child_pid == 0) {
port = ((struct sockaddr_in*)&client_address)->sin_port;
handle_client(peerfd, port_to_command[port]);
close(peerfd);
exit(EXIT_SUCCESS);
} else {
close(peerfd);
}
}
return 0;
}