zeroSquitto – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /*************************************************************************/ |
2 | /* Logging server - ZeroMQ and Mosquitto Publisher-Subscriber Project */ |
||
3 | /*************************************************************************/ |
||
4 | |||
5 | #include "constants.h" |
||
6 | #include <arpa/inet.h> |
||
7 | #include <fcntl.h> |
||
8 | #include <getopt.h> |
||
9 | #include <netdb.h> |
||
10 | #include <netinet/in.h> |
||
11 | #include <pthread.h> |
||
12 | #include <signal.h> |
||
13 | #include <stdio.h> |
||
14 | #include <stdlib.h> |
||
15 | #include <string.h> |
||
16 | #include <sys/socket.h> |
||
17 | #include <sys/types.h> |
||
18 | #include <syslog.h> |
||
19 | #include <termios.h> |
||
20 | #include <unistd.h> |
||
21 | |||
22 | // Used for program termination. |
||
23 | static volatile int run = 1; |
||
24 | |||
25 | // Stucture passed as argument to threads. |
||
26 | typedef struct { |
||
27 | char *name; |
||
28 | char *address; |
||
29 | int port; |
||
30 | } Targs; |
||
31 | |||
32 | void printUsage(char **argv) { |
||
33 | printf("Usage: %s [OPTIONS]\n", argv[0]); |
||
34 | printf("\t-n <name>\t\tname to log under\n"); |
||
35 | printf("\t-a <address>\t\taddress to listen on\n"); |
||
36 | printf("\t-p <port>\t\tport to listen on\n"); |
||
37 | |||
38 | printf("\n"); |
||
39 | } |
||
40 | |||
41 | // Handles SIGHUP and SIGINT (Ctrl + C) |
||
42 | void trap(int signal) { |
||
43 | printf("[✓] Received interrupt, terminating...\n"); |
||
44 | switch (signal) { |
||
45 | case SIGHUP: |
||
46 | case SIGINT: |
||
47 | run = 0; |
||
48 | break; |
||
49 | } |
||
50 | } |
||
51 | |||
52 | void *serveLog(void *argsThread) { |
||
53 | // Function arguments. |
||
54 | Targs *args = (Targs *)argsThread; |
||
55 | int serverSocket, clientSocket; |
||
56 | char *data = (char *)calloc(LOG_MAX_RECV_BYTES, sizeof(char)); |
||
57 | struct sockaddr_in serverSockAddr; |
||
58 | struct sockaddr_in clientSockAddr; |
||
59 | socklen_t clientAddrSize; |
||
60 | // To store connecting client address. |
||
61 | struct hostent *host; |
||
62 | char *hostaddrp; |
||
63 | int reuseSocket = 1; |
||
64 | int readBytes; |
||
65 | |||
66 | // Open syslog. |
||
67 | setlogmask(LOG_UPTO(LOG_INFO)); |
||
68 | openlog(args->name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); |
||
69 | |||
70 | // Create socket. |
||
71 | if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
||
72 | printf("[E] Error creating socket.\n"); |
||
73 | goto CLEANUP; |
||
74 | } |
||
75 | |||
76 | // Set non-blocking socket. |
||
77 | fcntl(serverSocket, F_SETFL, O_NONBLOCK); |
||
78 | |||
79 | // Reuse socket address. |
||
80 | setsockopt(clientSocket, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseSocket, |
||
81 | sizeof(int)); |
||
82 | bzero((char *)&serverSockAddr, sizeof(serverSockAddr)); |
||
83 | serverSockAddr.sin_family = AF_INET; |
||
84 | serverSockAddr.sin_addr.s_addr = inet_addr(args->address); |
||
85 | serverSockAddr.sin_port = htons(args->port); |
||
86 | |||
87 | // Bind to socket. |
||
88 | if (bind(serverSocket, (struct sockaddr *)&serverSockAddr, |
||
89 | sizeof(serverSockAddr)) < 0) { |
||
90 | printf("[E] Error binding to the socket socket.\n"); |
||
91 | goto CLEANUP; |
||
92 | } |
||
93 | |||
94 | // Listen to the socket with a specified number of queued requests. |
||
95 | if (listen(serverSocket, LOG_SERVER_TCP_LISTEN_QUEUE) < 0) { |
||
96 | printf("[E] Error listening on socket.\n"); |
||
97 | goto CLEANUP; |
||
98 | } |
||
99 | |||
100 | printf("[✓] Listening for log messages on %s:%d.\n", args->address, |
||
101 | args->port); |
||
102 | |||
103 | clientAddrSize = sizeof(clientSockAddr); |
||
104 | do { |
||
105 | |||
106 | // Listen for client connections in a non-blocking fashion. |
||
107 | if ((clientSocket = accept(serverSocket, (struct sockaddr *)&clientSockAddr, |
||
108 | &clientAddrSize)) < 0) { |
||
109 | sleep(1); |
||
110 | continue; |
||
111 | } |
||
112 | |||
113 | // Catch client address for reporting. |
||
114 | if ((host = gethostbyaddr((const char *)&clientSockAddr.sin_addr.s_addr, |
||
115 | sizeof(clientSockAddr.sin_addr.s_addr), |
||
116 | AF_INET)) == NULL || |
||
117 | (hostaddrp = inet_ntoa(clientSockAddr.sin_addr)) == NULL) { |
||
118 | printf("[E] Error retrieving client address.\n"); |
||
119 | close(clientSocket); |
||
120 | continue; |
||
121 | } |
||
122 | |||
123 | // Read data from the client. |
||
124 | if ((readBytes = read(clientSocket, data, LOG_MAX_RECV_BYTES)) < 0) { |
||
125 | printf("[E] Error reading client data.\n"); |
||
126 | close(clientSocket); |
||
127 | continue; |
||
128 | } |
||
129 | |||
130 | // Close the client socket. |
||
131 | close(clientSocket); |
||
132 | |||
133 | fprintf(stdout, "[✓] %s (%s) : %s\n", host->h_name, hostaddrp, data); |
||
134 | fflush(stdout); |
||
135 | |||
136 | // Send to syslog. |
||
137 | syslog(LOG_INFO, "%s (%s) : %s", host->h_name, hostaddrp, data); |
||
138 | |||
139 | // Clean the client receive buffer. |
||
140 | bzero(data, LOG_MAX_RECV_BYTES); |
||
141 | } while (run); |
||
142 | |||
143 | CLEANUP: |
||
144 | |||
145 | // Close the client socket. |
||
146 | close(clientSocket); |
||
147 | |||
148 | // Close syslog. |
||
149 | closelog(); |
||
150 | |||
151 | // Free buffers. |
||
152 | free(data); |
||
153 | |||
154 | // Exit context. |
||
155 | pthread_exit(NULL); |
||
156 | } |
||
157 | |||
158 | int main(int argc, char **argv) { |
||
159 | // For terminal input supression. |
||
160 | struct termios tattr; |
||
161 | struct termios tattr_store; |
||
162 | // The thread arguments. |
||
163 | Targs *args = (Targs *)calloc(1, sizeof(Targs)); |
||
164 | // The listener thread for ZeroMQ. |
||
165 | pthread_t logThread; |
||
166 | // Command-line processing. |
||
167 | const char *short_opt = "hn:a:p:"; |
||
168 | struct option long_opt[] = {{"help", no_argument, NULL, 'h'}, |
||
169 | {"name", required_argument, NULL, 'n'}, |
||
170 | {"address", required_argument, NULL, 'a'}, |
||
171 | {"port", required_argument, NULL, 'p'}, |
||
172 | {NULL, 0, NULL, 0}}; |
||
173 | int c; |
||
174 | int hasName, hasServer, hasPort; |
||
175 | |||
176 | while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) |
||
177 | switch (c) { |
||
178 | case -1: |
||
179 | case 0: |
||
180 | break; |
||
181 | |||
182 | case 'n': |
||
183 | args->name = (char *)calloc(strlen(optarg) + 1, sizeof(char)); |
||
184 | strncat(args->name, optarg, strlen(optarg)); |
||
185 | ++hasName; |
||
186 | break; |
||
187 | case 'a': |
||
188 | args->address = (char *)calloc(strlen(optarg) + 1, sizeof(char)); |
||
189 | strncat(args->address, optarg, strlen(optarg)); |
||
190 | ++hasServer; |
||
191 | break; |
||
192 | case 'p': |
||
193 | args->port = atoi(optarg); |
||
194 | ++hasPort; |
||
195 | break; |
||
196 | case 'h': |
||
197 | printUsage(argv); |
||
198 | return -1; |
||
199 | |||
200 | case ':': |
||
201 | case '?': |
||
202 | printf("Try `%s --help' for more information.\n", argv[0]); |
||
203 | return -1; |
||
204 | default: |
||
205 | printf("%s: invalid option -- %c\n", argv[0], c); |
||
206 | printf("Try `%s --help' for more information.\n", argv[0]); |
||
207 | return -1; |
||
208 | } |
||
209 | |||
210 | if (hasName == 0 || hasServer == 0 || hasPort == 0) { |
||
211 | printUsage(argv); |
||
212 | return -1; |
||
213 | } |
||
214 | |||
215 | // Suppress console input for clarity if this is a terminal. |
||
216 | if (isatty(STDIN_FILENO)) { |
||
217 | // Save the attributes and restore them on program termination. |
||
218 | tcgetattr(STDIN_FILENO, &tattr_store); |
||
219 | tcgetattr(STDIN_FILENO, &tattr); |
||
220 | tattr.c_lflag &= ~(ICANON | ECHO); |
||
221 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); |
||
222 | } |
||
223 | |||
224 | // Let there be bling! |
||
225 | printf("%s\n", LOGGING_BANNER); |
||
226 | printf("\tTip: Use Ctrl-c to end program.\n\n"); |
||
227 | |||
228 | // Bind to SIGHUP and SIGINT. |
||
229 | signal(SIGHUP, trap); |
||
230 | signal(SIGINT, trap); |
||
231 | |||
232 | if (pthread_create(&logThread, NULL, serveLog, (void *)args)) |
||
233 | printf("[E] Failed to create log thread!\n"); |
||
234 | |||
235 | // Await thread termination. |
||
236 | pthread_join(logThread, NULL); |
||
237 | |||
238 | // Free arguments. |
||
239 | free(args); |
||
240 | |||
241 | // If this is a terminal, restore the terminal attributes. |
||
242 | if (isatty(STDIN_FILENO)) |
||
243 | tcsetattr(STDIN_FILENO, TCSANOW, &tattr_store); |
||
244 | |||
245 | // Clean exit. |
||
246 | return 0; |
||
247 | } |