]> jfr.im git - irc/quakenet/newserv.git/blob - nterfacer/logging.c
BUILD: add require-all build mode
[irc/quakenet/newserv.git] / nterfacer / logging.c
1 /*
2 nterface logging functions
3 Copyright (C) 2003-2004 Chris Porter.
4
5 v1.04
6 - wrote own stat function as linux sucks
7 - modified permissions to allow paul to read the log files.
8 v1.03
9 - modified debug newserv level
10 v1.02
11 - merged logging and stdout stuff
12 - added log rotation
13 v1.01
14 - added new logging to file feature
15 - added sanitation
16 */
17
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <ctype.h>
23 #include <string.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <sys/stat.h>
27
28 #include "../core/error.h"
29 #include "../lib/sstring.h"
30 #include "../core/schedule.h"
31 #include "logging.h"
32 #include "library.h"
33 #include "esockets.h"
34
35 #define LOG_PERMISSIONS S_IRUSR | S_IWUSR | S_IRGRP
36 #define DIR_PERMISSIONS LOG_PERMISSIONS | S_IXUSR | S_IXGRP
37
38 void sanitise(char *buf) {
39 char *p;
40 for(p=buf;*p;p++)
41 if(!isprint(*p))
42 *p = '!';
43 }
44
45 /* 1 if exists, 0 if it doesn't */
46 /* linux sucks so we can't use stat */
47 int exists(char *filename) {
48 FILE *fp = fopen(filename, "r");
49 if(fp) {
50 fclose(fp);
51 return 1;
52 }
53 return 0;
54 }
55
56 int ifexistrename(char *from, char *to) {
57 if(!exists(from)) {
58 return 0;
59 } else {
60 return rename(from, to);
61 }
62 }
63
64 void nterface_rotate_log(void *arg) {
65 struct nterface_auto_log *us = (struct nterface_auto_log *)arg;
66 char *filename, *filename2, *swap;
67 int filenamelen = strlen(us->filename->content) + 15, i, aborted = 0;
68 FILE *newlog;
69
70 if(!us)
71 return;
72
73 filename = ntmalloc(filenamelen);
74 MemCheck(filename);
75 filename2 = ntmalloc(filenamelen);
76 if(!filename2) {
77 MemError();
78 ntfree(filename);
79 return;
80 }
81
82 /* ADD us->log checks, if rotation failed */
83
84 snprintf(filename, filenamelen, "%s.n", us->filename->content);
85
86 newlog = fopen(filename, "w");
87 if(!newlog) {
88 nterface_log(us, NL_WARNING, "Unable to rotate log %s: %s", us->filename->content, strerror(errno));
89 } else {
90 snprintf(filename, filenamelen, "%s.%d", us->filename->content, ROTATE_LOG_TIMES);
91 for(i=ROTATE_LOG_TIMES-1;i>=0;i--) {
92 snprintf(filename2, filenamelen, "%s.%d", us->filename->content, i);
93 if(ifexistrename(filename2, filename))
94 break;
95 swap = filename;
96 filename = filename2;
97 filename2 = swap;
98 }
99 if(i >= 0) {
100 nterface_log(us, NL_WARNING, "Error occured during log rotation (rename %s to %s), log files will have a gap: %s", filename2, filename, strerror(errno));
101 aborted = 1;
102 } else {
103 if(ifexistrename(us->filename->content, filename)) {
104 nterface_log(us, NL_WARNING, "Error occured during log rotation (rename %s to %s), log files will have a gap (.0 will be missing): %s", us->filename->content, filename, strerror(errno));
105 aborted = 1;
106 } else {
107 snprintf(filename, filenamelen, "%s.n", us->filename->content);
108 if(ifexistrename(filename, us->filename->content)) {
109 nterface_log(us, NL_WARNING, "Error occured during log rotation (rename %s to %s): %s", filename, us->filename->content, strerror(errno));
110 snprintf(filename, filenamelen, "%s.0", us->filename->content);
111 if(ifexistrename(filename, us->filename->content))
112 nterface_log(us, NL_WARNING, "Error occured during log rotation (rename %s to %s), now logging to: %s as rename back failed: %s", filename, us->filename->content, filename, strerror(errno));
113 aborted = 1;
114 } else {
115 nterface_log(us, NL_SYSTEM, "Log rotated out of use");
116 fclose(us->log);
117 us->log = newlog;
118 nterface_log(us, NL_SYSTEM, "Log rotated into use");
119 nterface_log(us, NL_INFO|NL_DONT_LOG, "Log rotated");
120 }
121 }
122 }
123 }
124
125 if(aborted) {
126 fclose(newlog);
127 snprintf(filename, filenamelen, "%s.n", us->filename->content);
128 unlink(filename);
129 }
130
131 ntfree(filename);
132 ntfree(filename2);
133 }
134
135 int direxists(char *filename) {
136 char *p = filename, *lastp = NULL;
137 for(;*p;p++)
138 if(*p == '/')
139 lastp = p;
140
141 if(lastp) {
142 int ret = 0;
143 char *newp = ntmalloc(lastp - filename + 1);
144 if(!newp) {
145 MemError();
146 return 0;
147 }
148
149 memcpy(newp, filename, lastp - filename);
150 newp[lastp - filename] = '\0';
151
152 if(exists(newp)) {
153 chmod(newp, DIR_PERMISSIONS);
154 } else {
155 ret = mkdir(newp, DIR_PERMISSIONS);
156 }
157
158 ntfree(newp);
159 if(ret)
160 return 0;
161 }
162 return 1;
163 }
164
165 struct nterface_auto_log *nterface_open_log(char *name, char *filename, int debug) {
166 struct nterface_auto_log *us = ntmalloc(sizeof(struct nterface_auto_log));
167 MemCheckR(us, NULL);
168
169 us->filename = getsstring(filename, strlen(filename));
170 if(!us->filename) {
171 MemError();
172 ntfree(us);
173 }
174
175 us->name = getsstring(name, strlen(name));
176 if(!us->name) {
177 MemError();
178 freesstring(us->filename);
179 ntfree(us);
180 }
181
182 if(direxists(filename)) {
183 us->log = fopen(filename, "a");
184 if(!us->log)
185 Error(name, ERR_INFO, "Unable to open log %s: %s", filename, strerror(errno));
186 } else {
187 Error(name, ERR_INFO, "Unable to open log %s, as directory does not exist and couldn't be created: %s", filename, strerror(errno));
188 }
189
190 us->debug = debug;
191
192 nterface_log(us, NL_SYSTEM, "Log opened");
193
194 chmod(filename, LOG_PERMISSIONS);
195
196 us->schedule = schedulerecurring((time(NULL)/ROTATE_LOG_EVERY + 1) * ROTATE_LOG_EVERY, 0, ROTATE_LOG_EVERY, nterface_rotate_log, us);
197 if(!us->schedule)
198 nterface_log(us, NL_WARNING, "Unable to schedule log rotation!");
199
200 return us;
201 }
202
203 struct nterface_auto_log *nterface_close_log(struct nterface_auto_log *ourlog) {
204 if(ourlog) {
205 if(ourlog->schedule)
206 deleteschedule(ourlog->schedule, nterface_rotate_log, ourlog);
207 freesstring(ourlog->name);
208 freesstring(ourlog->filename);
209 nterface_log(ourlog, NL_SYSTEM, "Log closed");
210 if(ourlog->log)
211 fclose(ourlog->log);
212 ntfree(ourlog);
213 }
214 return NULL;
215 }
216
217 void nterface_log(struct nterface_auto_log *ourlog, int loglevel, char *format, ...) {
218 char buf[MAX_BUFSIZE * 2], timebuf[100], loglevelname = '\0'; /* lazy */
219 int newserverrorcode = -1;
220 va_list va;
221 time_t now;
222 struct tm *tm;
223
224 if(loglevel & NL_INFO) {
225 loglevelname = 'I';
226 newserverrorcode = ERR_INFO;
227 } else if(loglevel & NL_DEBUG) {
228 if(!ourlog || ourlog->debug) {
229 loglevelname = 'D';
230 newserverrorcode = ERR_INFO; /* debug only shows up if debug newserv is enabled */
231 }
232 } else if(loglevel & NL_WARNING) {
233 loglevelname = 'W';
234 newserverrorcode = ERR_WARNING;
235 } else if(loglevel & NL_ERROR) {
236 loglevelname = 'E';
237 newserverrorcode = ERR_ERROR;
238 } else if(loglevel & NL_SYSTEM) {
239 loglevelname = 'S';
240 }
241
242 if(loglevel & NL_LOG_ONLY)
243 newserverrorcode = -1;
244
245 if(!loglevelname)
246 return;
247
248 va_start(va, format);
249 vsnprintf(buf, sizeof(buf), format, va);
250 va_end(va);
251
252 if(newserverrorcode != -1)
253 Error(ourlog?ourlog->name->content:"nterface-unknown", newserverrorcode, "%s", buf);
254
255 sanitise(buf);
256
257 if(!ourlog || !ourlog->log || (loglevel & NL_DONT_LOG))
258 return;
259
260 /* thanks splidge */
261 now = time(NULL);
262 tm = gmtime(&now);
263 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
264 fprintf(ourlog->log, "[%s] %c: %s\n", timebuf, loglevelname, buf);
265 fflush(ourlog->log);
266 }