#define _XOPEN_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <err.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#define LISTEN_BACKLOG 10
#define FPAS_FORM "for port %s and service %s\n"
#define FPAS_P cfg->items[i].port
#define FPAS_S cfg->items[i].cmd
struct config_it{
char * port;
char wait;
char * cmd;
char ** args;
};
struct config{
int len;
struct config_it * items;
};
int sigpipe[2];
void usage(void){
printf("Usage: ./inetd <config>\n");
}
struct config * parse_config(char * filename){
FILE * f;
f = fopen(filename,"r");
if (f==NULL){
printf("Could not open config file %s\n",filename);
exit(1);
}
int ch;
int lines = 0;
ch = fgetc(f);
while (ch != EOF){
if (ch == '\n')
lines++;
ch = fgetc(f);
}
rewind(f);
struct config_it * cfg_its;
cfg_its = calloc(lines,sizeof(struct config_it));
for (int i=0;i<lines;i++){
cfg_its[i].port = malloc(10);
fscanf(f,"%s",cfg_its[i].port);
char waitstr[10];
fscanf(f,"%s",waitstr);
if (!strcmp(waitstr,"wait"))
cfg_its[i].wait = 1;
else
cfg_its[i].wait = 0;
cfg_its[i].cmd = malloc(100);
fscanf(f,"%s",cfg_its[i].cmd);
while(fgetc(f)!='\n'){}
}
struct config * cfg;
cfg = malloc(sizeof(struct config));
cfg->len = lines;
cfg->items = cfg_its;
return cfg;
}
void dump_config(struct config * cfg){
printf("port\twait\tcmd\n");
for (int i=0;i<cfg->len;i++){
printf("%s\t%d\t%s\n",cfg->items[i].port,cfg->items[i].wait,cfg->items[i].cmd);
}
}
void handle_sigchld(int sig){
write(sigpipe[1],"!",1);
}
void handle_error(int mem_errno,int n_srv, int * l_socks, char * msg){
for (int j=0;j<=n_srv;j++){
close(l_socks[j]);
}
errno = mem_errno;
err(1,msg);
}
void run(struct config * cfg){
printf("Running inetd...\n");
int n_srv;
n_srv = cfg->len;
int * l_socks;
struct addrinfo * addrinfo;
struct addrinfo hints;
l_socks = malloc(n_srv*sizeof(int));
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
for (int i=0;i<n_srv;i++){
int gai_res;
if ((gai_res=getaddrinfo(NULL,cfg->items[i].port,&hints,&addrinfo)) != 0){
fprintf(stderr,"Error: \"%s\""FPAS_FORM,gai_strerror(gai_res),FPAS_P,FPAS_S);
exit(1);
}
struct addrinfo * orig_ainfo;
for (orig_ainfo = addrinfo; addrinfo!=NULL; addrinfo = addrinfo->ai_next){
errno = 0;
l_socks[i]=socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
if (l_socks[i]==-1){
warn("Error in socket()"FPAS_FORM,FPAS_P,FPAS_S);
continue;
}
errno = 0;
if (bind(l_socks[i],addrinfo->ai_addr,addrinfo->ai_addrlen)!=0){
warn("Error in bind()"FPAS_FORM,FPAS_P,FPAS_S);
continue;
}
break;
}
if (addrinfo==NULL){
for (int j=0;j<=i;j++){
close(l_socks[j]);
}
err(1,"Exitting because of errors above");
}
freeaddrinfo(orig_ainfo);
int opt = 1;
errno = 0;
setsockopt(l_socks[i], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (errno){
handle_error(errno,n_srv,l_socks,"Error while setting REUSEADDR");
}
errno = 0;
if (listen(l_socks[i],LISTEN_BACKLOG)!=0){
int mem_errno = errno;
for (int j=0;j<=i;j++){
close(l_socks[j]);
}
errno = mem_errno;
err(1,"Error in listen()"FPAS_FORM,FPAS_P,FPAS_S);
}
}
errno = 0;
pipe(sigpipe);
fcntl(sigpipe[1],F_SETFL, fcntl(sigpipe[1], F_GETFL) | O_NONBLOCK);
if (errno)
handle_error(errno,n_srv,l_socks,"Error while setting signal pipe");
struct sigaction sa;
memset(&sa, 0, sizeof (sa));
sa.sa_handler = handle_sigchld;
// sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
handle_error(errno,n_srv,l_socks,"Error while setting signal");
}
struct pollfd * pollfds;
pid_t * children;
pollfds = malloc((n_srv+1)*sizeof(struct pollfd));
children = malloc(n_srv*sizeof(pid_t));
for (int i=0;i<n_srv;i++){
pollfds[i].fd = l_socks[i];
pollfds[i].events = POLLIN;
children[i]=-1;
}
pollfds[n_srv].fd=sigpipe[0];
pollfds[n_srv].events = POLLIN;
while (1){
errno = 0;
int pol_res;
pol_res = poll(pollfds,n_srv+1,1000);
if (pol_res == 0){
printf("Poll has timed out\n");
continue;
}else if (pol_res > 0){
printf("Got %d connections, serving them\n",pol_res);
}else if (errno & EINTR){
printf("Interrupted in poll\n");
continue;
}else {
handle_error(errno,n_srv,l_socks,"Poll failed");
}
for (int i=0;i<n_srv;i++){
if (!(pollfds[i].revents & POLLIN))
continue;
printf("Serving connection - port: %s wait: %d cmd: %s\n",FPAS_P,cfg->items[i].wait,FPAS_S);
if (cfg->items[i].wait){
if (children[i]!=-1){ // Child may be running
printf("Child is running and we are polling!!!\n");
}
printf("FORK!\n");
pid_t pid;
if ((pid = fork())!=0){ // We are parent
// printf("Parent\n");
children[i]=pid;
pollfds[i].fd = -pollfds[i].fd;
continue;
}
// We are child
for (int j=0;j<n_srv;j++){
if (j!=i)
close(l_socks[j]);
}
errno = 0;
close(0); dup(l_socks[i]);
//close(1); dup(l_socks[i]);
//close(2); dup(l_socks[i]);
close(l_socks[i]);
if (errno)
err(1,"Error while preparing file descriptors");
printf("EXEC!\n");
errno = 0;
execl(cfg->items[i].cmd,cfg->items[i].cmd,(char *)NULL);
err(1,"Error while running %s",cfg->items[i].cmd);
}else { // nowait
int rw_sock;
rw_sock = accept(l_socks[i],NULL,NULL);
if (rw_sock == -1){
err(1,"Error when accepting connection");
}
printf("FORK!\n");
if(fork()!=0){ // We are parent
close(rw_sock);
printf("Parent\n");
continue;
}
printf("Child\n");
// We are child
for (int j=0;j<n_srv;j++){
close(l_socks[j]);
}
errno = 0;
close(0); dup(rw_sock);
close(1); dup(rw_sock);
close(2); dup(rw_sock);
close(rw_sock);
if (errno)
err(1,"Error while preparing file descriptors");
printf("EXEC!\n");
errno = 0;
execl(cfg->items[i].cmd,cfg->items[i].cmd,(char *)NULL);
err(1,"Error while running %s",cfg->items[i].cmd);
}
}
if (pollfds[n_srv].revents & POLLIN){
char ch;
read(sigpipe[0],&ch,1);
pid_t dead;
dead = waitpid(-1,NULL,WNOHANG);
if (dead < 0){
handle_error(errno,n_srv,l_socks,"Waitpid failed");
}
if (dead == 0){
printf("All alive!\n");
}
printf("Child %d died%c\n",dead,ch);
for (int i=0;i<n_srv;i++){
if (children[i]==dead){
children[i]=-1;
pollfds[i].fd = -pollfds[i].fd;
}
}
}
}
for (int i=0;i<n_srv;i++){
close(l_socks[i]);
}
}
int main (int argc, char ** argv){
if (argc != 2){
usage();
printf("No config file supplied\n");
exit(1);
}
struct config * cfg;
cfg = parse_config(argv[1]);
dump_config(cfg);
run(cfg);
return 0;
}