zeroSquitto – Rev 1
?pathlinks?
/*************************************************************************/
/* 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.