#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>

#define LISTEN_PORT 4242
#define LENGTH 1024

int cnxfd = 0;

void fail(char *msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

void bindshell(void) {
  /* Binding the process to the connexion */
  dup2(cnxfd, STDIN_FILENO);
  dup2(cnxfd, STDOUT_FILENO);
  dup2(cnxfd, STDERR_FILENO);
  /* Running the shell */
  system("/bin/sh");
}

void server(int sockfd) {
  char input[LENGTH], output[LENGTH];

  while (true) {
    struct sockaddr_in caddr;
    socklen_t clen = sizeof(caddr);
    if ((cnxfd = accept(sockfd, (struct sockaddr *) &caddr, &clen)) < 0)
      fail("accept()");
    /* Blanking memory */
    memset(input, '\0', LENGTH);
    memset(output, '\0', LENGTH);
    /* Receiving and sending back the string */
    send(cnxfd, "Waiting for data: ", 18, 0);
    recv(cnxfd, input, LENGTH - 1, 0);
    snprintf(output, sizeof(output), input);
    output[sizeof(output) - 1] = '\0';
    send(cnxfd, "Sending data: ", 14, 0);
    send(cnxfd, output, strlen(output), 0);
    /* Closing the connection */
    close(cnxfd);
  }
}

int main(void) {
    int sockfd;
    const struct sockaddr_in saddr = {
      .sin_family = AF_INET,
      .sin_addr.s_addr = htonl(INADDR_ANY),
      .sin_port = htons(LISTEN_PORT)
    };

  while (true) {
    if (!fork()) { /* Child process */
      fprintf(stdout, "run (pid = %d)\n", getpid());
      /* Socket initialization and error checking */
      if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	fail("socket()");
      if (setsockopt(sockfd,
		     SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
	fail("setsockopt()");
      if (bind(sockfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
	fail("bind()");
      if (listen(sockfd, LENGTH) < 0)
	fail("listen()");
      /* Running the server */
      server(sockfd);
    } else { /* Parent process (wait for child) */
      wait(NULL);
      close(sockfd);
    }
  }
  return EXIT_SUCCESS;
}
