zeroSquitto – Rev 1

Subversion Repositories:
Rev:
/*************************************************************************/
/* Logging server - ZeroMQ and Mosquitto Publisher-Subscriber Project              */
/*************************************************************************/

#include "constants.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>

// Used for program termination.
static volatile int run = 1;

// Stucture passed as argument to threads.
typedef struct {
  char *name;
  char *address;
  int port;
} Targs;

void printUsage(char **argv) {
  printf("Usage: %s [OPTIONS]\n", argv[0]);
  printf("\t-n <name>\t\tname to log under\n");
  printf("\t-a <address>\t\taddress to listen on\n");
  printf("\t-p <port>\t\tport to listen on\n");

  printf("\n");
}

// Handles SIGHUP and SIGINT (Ctrl + C)
void trap(int signal) {
  printf("[✓] Received interrupt, terminating...\n");
  switch (signal) {
  case SIGHUP:
  case SIGINT:
    run = 0;
    break;
  }
}

void *serveLog(void *argsThread) {
  // Function arguments.
  Targs *args = (Targs *)argsThread;
  int serverSocket, clientSocket;
  char *data = (char *)calloc(LOG_MAX_RECV_BYTES, sizeof(char));
  struct sockaddr_in serverSockAddr;
  struct sockaddr_in clientSockAddr;
  socklen_t clientAddrSize;
  // To store connecting client address.
  struct hostent *host;
  char *hostaddrp;
  int reuseSocket = 1;
  int readBytes;

  // Open syslog.
  setlogmask(LOG_UPTO(LOG_INFO));
  openlog(args->name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);

  // Create socket.
  if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    printf("[E] Error creating socket.\n");
    goto CLEANUP;
  }

  // Set non-blocking socket.
  fcntl(serverSocket, F_SETFL, O_NONBLOCK);

  // Reuse socket address.
  setsockopt(clientSocket, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseSocket,
             sizeof(int));
  bzero((char *)&serverSockAddr, sizeof(serverSockAddr));
  serverSockAddr.sin_family = AF_INET;
  serverSockAddr.sin_addr.s_addr = inet_addr(args->address);
  serverSockAddr.sin_port = htons(args->port);

  // Bind to socket.
  if (bind(serverSocket, (struct sockaddr *)&serverSockAddr,
           sizeof(serverSockAddr)) < 0) {
    printf("[E] Error binding to the socket socket.\n");
    goto CLEANUP;
  }

  // Listen to the socket with a specified number of queued requests.
  if (listen(serverSocket, LOG_SERVER_TCP_LISTEN_QUEUE) < 0) {
    printf("[E] Error listening on socket.\n");
    goto CLEANUP;
  }

  printf("[✓] Listening for log messages on %s:%d.\n", args->address,
         args->port);

  clientAddrSize = sizeof(clientSockAddr);
  do {

    // Listen for client connections in a non-blocking fashion.
    if ((clientSocket = accept(serverSocket, (struct sockaddr *)&clientSockAddr,
                               &clientAddrSize)) < 0) {
      sleep(1);
      continue;
    }

    // Catch client address for reporting.
    if ((host = gethostbyaddr((const char *)&clientSockAddr.sin_addr.s_addr,
                              sizeof(clientSockAddr.sin_addr.s_addr),
                              AF_INET)) == NULL ||
        (hostaddrp = inet_ntoa(clientSockAddr.sin_addr)) == NULL) {
      printf("[E] Error retrieving client address.\n");
      close(clientSocket);
      continue;
    }

    // Read data from the client.
    if ((readBytes = read(clientSocket, data, LOG_MAX_RECV_BYTES)) < 0) {
      printf("[E] Error reading client data.\n");
      close(clientSocket);
      continue;
    }

    // Close the client socket.
    close(clientSocket);

    fprintf(stdout, "[✓] %s (%s) : %s\n", host->h_name, hostaddrp, data);
    fflush(stdout);

    // Send to syslog.
    syslog(LOG_INFO, "%s (%s) : %s", host->h_name, hostaddrp, data);

    // Clean the client receive buffer.
    bzero(data, LOG_MAX_RECV_BYTES);
  } while (run);

CLEANUP:

  // Close the client socket.
  close(clientSocket);

  // Close syslog.
  closelog();

  // Free buffers.
  free(data);

  // Exit context.
  pthread_exit(NULL);
}

int main(int argc, char **argv) {
  // For terminal input supression.
  struct termios tattr;
  struct termios tattr_store;
  // The thread arguments.
  Targs *args = (Targs *)calloc(1, sizeof(Targs));
  // The listener thread for ZeroMQ.
  pthread_t logThread;
  // Command-line processing.
  const char *short_opt = "hn:a:p:";
  struct option long_opt[] = {{"help", no_argument, NULL, 'h'},
                              {"name", required_argument, NULL, 'n'},
                              {"address", required_argument, NULL, 'a'},
                              {"port", required_argument, NULL, 'p'},
                              {NULL, 0, NULL, 0}};
  int c;
  int hasName, hasServer, hasPort;

  while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1)
    switch (c) {
    case -1:
    case 0:
      break;

    case 'n':
      args->name = (char *)calloc(strlen(optarg) + 1, sizeof(char));
      strncat(args->name, optarg, strlen(optarg));
      ++hasName;
      break;
    case 'a':
      args->address = (char *)calloc(strlen(optarg) + 1, sizeof(char));
      strncat(args->address, optarg, strlen(optarg));
      ++hasServer;
      break;
    case 'p':
      args->port = atoi(optarg);
      ++hasPort;
      break;
    case 'h':
      printUsage(argv);
      return -1;

    case ':':
    case '?':
      printf("Try `%s --help' for more information.\n", argv[0]);
      return -1;
    default:
      printf("%s: invalid option -- %c\n", argv[0], c);
      printf("Try `%s --help' for more information.\n", argv[0]);
      return -1;
    }

  if (hasName == 0 || hasServer == 0 || hasPort == 0) {
    printUsage(argv);
    return -1;
  }

  // Suppress console input for clarity if this is a terminal.
  if (isatty(STDIN_FILENO)) {
    // Save the attributes and restore them on program termination.
    tcgetattr(STDIN_FILENO, &tattr_store);
    tcgetattr(STDIN_FILENO, &tattr);
    tattr.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
  }

  // Let there be bling!
  printf("%s\n", LOGGING_BANNER);
  printf("\tTip: Use Ctrl-c to end program.\n\n");

  // Bind to SIGHUP and SIGINT.
  signal(SIGHUP, trap);
  signal(SIGINT, trap);

  if (pthread_create(&logThread, NULL, serveLog, (void *)args))
    printf("[E] Failed to create log thread!\n");

  // Await thread termination.
  pthread_join(logThread, NULL);

  // Free arguments.
  free(args);

  // If this is a terminal, restore the terminal attributes.
  if (isatty(STDIN_FILENO))
    tcsetattr(STDIN_FILENO, TCSANOW, &tattr_store);

  // Clean exit.
  return 0;
}

Generated by GNU Enscript 1.6.5.90.