1 /*
2  * LOG functions
3  * Copyright (C) 2006-2008 Unix Solutions Ltd.
4  *
5  * Released under MIT license.
6  * See LICENSE-MIT.txt for license terms.
7  */
8 /* Needed for POLLRDHUP */
9 #define _GNU_SOURCE 1
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <netdb.h>
17 #include <arpa/inet.h>
18 #include <netinet/in.h>
19 #include <netinet/tcp.h>
20 #include <sys/socket.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <poll.h>
25 #include <errno.h>
26 #include <time.h>
27 #include <pthread.h>
28 
29 #include "libfuncs.h"
30 #include "io.h"
31 #include "queue.h"
32 #include "asyncdns.h"
33 #include "log.h"
34 
35 static FILE *OUT_FD = NULL;
36 
37 struct logger {
38 	int			use_stderr;
39 	int			use_syslog;
40 
41 	char		*host_ident;
42 
43 	char		*log_host;
44 	int			log_port;
45 	int			log_sock;
46 
47 	int			dienow : 1,
48 				dying  : 1;
49 
50 	QUEUE		*queue;
51 	pthread_t	thread;
52 };
53 
log_connect(struct logger * l)54 static void log_connect(struct logger *l) {
55 	struct sockaddr_in sockname;
56 
57 	if (!l->use_syslog)
58 		return;
59 
60 	while (1) {
61 		if (l->dying || l->dienow)
62 			break;
63 		int active = 1;
64 		int dret = async_resolve_host(l->log_host, l->log_port, &sockname, 500, &active);
65 		if (dret != 0) {
66 			log_perror("Could not resolve log host.", errno);
67 			sleep(2);
68 			continue;
69 		}
70 		l->log_sock = socket(PF_INET, SOCK_STREAM, 0);
71 		if (l->log_sock < 0) {
72 			log_perror("Could not create syslog socket", errno);
73 			sleep(2);
74 			continue;
75 		}
76 		if (connect(l->log_sock, (struct sockaddr *) &sockname, sizeof (sockname)) < 0) {
77 			log_perror("Could not connect to log host.", errno);
78 			shutdown_fd(&l->log_sock);
79 			sleep(2);
80 			continue;
81 		}
82 		int on = 1;
83 		setsockopt(l->log_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof(int));
84 		break;
85 	}
86 }
87 
log_thread(void * param)88 static void * log_thread(void *param) {
89 	char date[64], logline[1024], *msg=NULL;
90 	struct logger *l = (struct logger *)param;
91 
92 	if (l->log_sock < 0)
93 		log_connect(l);
94 
95 	while (l && !l->dienow) {
96 		msg = queue_get(l->queue); // Waits...
97 		if (!msg)
98 			break;
99 
100 		time_t now = time(NULL);
101 		memset(date, 0, sizeof(date));
102 		struct tm ltime;
103 		localtime_r(&now, &ltime);
104 		strftime(date, sizeof(date)-1, "%b %d %H:%M:%S", &ltime);
105 
106 		memset(logline, 0, sizeof(logline));
107 		snprintf(logline, sizeof(logline)-1, "<30>%s host %s: %s", date, l->host_ident, msg);
108 		logline[sizeof(logline)-2] = '\n';
109 		logline[sizeof(logline)-1] = '\0';
110 
111 		if (l->use_stderr)
112 			fprintf(OUT_FD, "%s", logline+4);
113 
114 		while (l->use_syslog) {
115 			struct pollfd fdset[1];
116 			int fdready;
117 			do {
118 				fdset[0].fd = l->log_sock;
119 				fdset[0].events = POLLOUT | POLLERR | POLLHUP | POLLNVAL | POLLRDHUP;
120 				fdready = poll(fdset, 1, 5 * 1000);
121 			} while (fdready == -1 && errno == EINTR);
122 			if (fdready < 1 || (fdready == 1 && fdset[0].revents != POLLOUT)) { /* Timeout || error */
123 				do { /* Try to reconnect to log host */
124 					if (l->dienow)
125 						goto OUT;
126 					LOGf("ERROR: Lost connection to log server (%s), fd: %i\n", fdready == 1 ? "poll error" : "timeout", l->log_sock);
127 					shutdown_fd(&l->log_sock);
128 					log_connect(l);
129 					sleep(2);
130 				} while (l->log_sock < 0);
131 			} else {
132 				if (fdwrite(l->log_sock, logline, strlen(logline)) > 0)
133 					break;
134 				else
135 					if (l->dienow)
136 						goto OUT;
137 			}
138 		}
139 
140 		FREE(msg);
141 	}
142 OUT:
143 	FREE(msg);
144 	pthread_exit(0);
145 }
146 
147 static struct logger *logger = NULL;
148 
log_init(char * host_ident,int use_syslog,int use_stderr,char * log_host,int log_port)149 void log_init(char *host_ident, int use_syslog, int use_stderr, char *log_host, int log_port) {
150 	logger = calloc(1, sizeof(struct logger));
151 
152 	logger->queue = queue_new();
153 	logger->host_ident = strdup(host_ident);
154 
155 	if (log_host)
156 		logger->log_host = strdup(log_host);
157 	logger->log_port = log_port;
158 	logger->log_sock = -1;
159 
160 	logger->use_syslog = use_syslog;
161 	logger->use_stderr = use_stderr;
162 
163 	pthread_create(&logger->thread, NULL , &log_thread, logger);
164 }
165 
log_set_out_fd(FILE * new_out_fd)166 void log_set_out_fd(FILE *new_out_fd) {
167 	OUT_FD = new_out_fd;
168 }
169 
log_close(void)170 void log_close(void) {
171 	logger->dying = 1;
172 	int count = 0;
173 	while (logger->queue->items && count++ < 250)
174 		usleep(1000);
175 	logger->dienow = 1;
176 	queue_wakeup(logger->queue);
177 	pthread_join(logger->thread, NULL);
178 	shutdown_fd(&logger->log_sock);
179 	queue_free(&logger->queue);
180 	FREE(logger->host_ident);
181 	FREE(logger->log_host);
182 	FREE(logger);
183 }
184 
LOG(const char * msg)185 void LOG(const char *msg) {
186 	if (OUT_FD == NULL)
187 		OUT_FD = stderr;
188 	if (!logger || logger->dying) {
189 		fprintf(OUT_FD, "%s", msg);
190 	} else {
191 		queue_add(logger->queue, strdup(msg));
192 	}
193 }
194 
LOGf(const char * fmt,...)195 void LOGf(const char *fmt, ...) {
196 	char msg[1024];
197 	va_list args;
198 	va_start(args, fmt);
199 	vsnprintf(msg, sizeof(msg)-1, fmt, args);
200 	va_end(args);
201 	msg[sizeof(msg)-2] = '\n';
202 	msg[sizeof(msg)-1] = '\0';
203 	LOG(msg);
204 }
205 
log_perror(const char * message,int _errno)206 void log_perror(const char *message, int _errno) {
207 	char msg[1024];
208 	snprintf(msg, sizeof(msg)-1, "PERROR: %s | %s\n", message, strerror(_errno));
209 	msg[sizeof(msg)-2] = '\n';
210 	msg[sizeof(msg)-1] = '\0';
211 	LOG(msg);
212 }
213