]> git.pond.sub.org Git - empserver/blob - src/lib/subs/journal.c
527a83f9f3a273e0e26d43159a90b2069a8cef1e
[empserver] / src / lib / subs / journal.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  journal.c: Log a journal of events to a file
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2004-2011
31  *     Ron Koenderink, 2008
32  */
33
34 /*
35  * Journal file format: each line logs an event, and looks like this:
36  *
37  *     TIME THREAD EVENT DATA
38  *
39  * Events and their data are:
40  *
41  *     startup
42  *     shutdown
43  *     prng NAME SEED
44  *     login CNUM HOSTADDR USER
45  *     logout CNUM
46  *     command NAME
47  *     input INPUT
48  *     output THREAD ID OUTPUT
49  *     update ETU
50  */
51
52 #include <config.h>
53
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <time.h>
59 #include "misc.h"
60 #include "empthread.h"
61 #include "journal.h"
62 #include "optlist.h"
63 #include "player.h"
64 #include "prototypes.h"
65
66 static char journal_fname[] = "journal.log";
67 static FILE *journal;
68
69 static void journal_entry_start(char *fmt, ...)
70     ATTRIBUTE((format (printf, 1, 2)));
71 static void journal_entry(char *fmt, ...)
72     ATTRIBUTE((format (printf, 1, 2)));
73 static void journal_output_start(struct player *, int);
74
75 static FILE *
76 journal_open(void)
77 {
78     return fopen(journal_fname, "a+");
79 }
80
81 static void
82 journal_entry_vstart(char *fmt, va_list ap)
83 {
84     time_t now;
85
86     if (!journal)
87         return;
88     time(&now);
89     fprintf(journal, "%.24s %10.10s ",
90             ctime(&now), empth_name(empth_self()));
91     vfprintf(journal, fmt, ap);
92 }
93
94 static void
95 journal_entry_start(char *fmt, ...)
96 {
97     va_list ap;
98
99     va_start(ap, fmt);
100     journal_entry_vstart(fmt, ap);
101     va_end(ap);
102 }
103
104 static void
105 journal_entry_pr(char *s, size_t n)
106 {
107     unsigned char *p;
108
109     if (!journal)
110         return;
111     for (p = (unsigned char *)s; *p && n; p++) {
112         if (*p == '\\')
113             fprintf(journal, "\\\\");
114         else if (isprint(*p) || *p == '\t')
115             putc(*p, journal);
116         else
117             fprintf(journal, "\\%03o", *p);
118         n--;
119     }
120 }
121
122 static void
123 journal_entry_end(int newline, int flush)
124 {
125     if (!journal)
126         return;
127     if (!newline)
128         fputc('\\', journal);
129     fputc('\n', journal);
130     fflush(journal);
131     if (ferror(journal)) {
132         logerror("Error writing journal (%s)", strerror(errno));
133         clearerr(journal);
134     }
135 }
136
137 static void
138 journal_entry(char *fmt, ...)
139 {
140     va_list ap;
141
142     va_start(ap, fmt);
143     journal_entry_vstart(fmt, ap);
144     va_end(ap);
145     journal_entry_end(1, 1);
146 }
147
148 int
149 journal_startup(void)
150 {
151     if (!keep_journal)
152         return 0;
153     journal = journal_open();
154     if (!journal) {
155         logerror("Can't open %s (%s)", journal_fname, strerror(errno));
156         return -1;
157     }
158     journal_entry("startup");
159     return 0;
160 }
161
162 void
163 journal_shutdown(void)
164 {
165     journal_entry("shutdown");
166     if (journal) {
167         fclose(journal);
168         journal = NULL;
169     }
170 }
171
172 int
173 journal_reopen(void)
174 {
175     FILE *j;
176
177     if (!keep_journal)
178         return 0;
179     j = journal_open();
180     if (!j) {
181         logerror("Can't open %s (%s)", journal_fname, strerror(errno));
182         return -1;
183     }
184     if (journal)
185         fclose(journal);
186     journal = j;
187     return 0;
188 }
189
190 void
191 journal_prng(unsigned seed)
192 {
193     journal_entry("prng BSD %d", seed);
194 }
195
196 void
197 journal_login(void)
198 {
199     journal_entry("login %d %s %s",
200                   player->cnum, player->hostaddr, player->userid);
201 }
202
203 void
204 journal_logout(void)
205 {
206     journal_entry("logout %d", player->cnum);
207 }
208
209 void
210 journal_output(struct player *pl, int id, char *output)
211 {
212     static char buf[1024];
213     static char *bp = buf;
214     static struct player *bpl;
215     static int bid;
216     char *s, *e;
217
218     if (keep_journal < 2)
219         return;
220
221     if (bp != buf && (pl != bpl || id != bid)) {
222         journal_output_start(bpl, bid);
223         journal_entry_pr(buf, bp - buf);
224         journal_entry_end(0, 0);
225         bp = buf;
226     }
227
228     for (s = output; (e = strchr(s, '\n')); s = e + 1) {
229         journal_output_start(pl, id);
230         journal_entry_pr(buf, bp - buf);
231         journal_entry_pr(s, e - s);
232         journal_entry_end(1, 0);
233         bp = buf;
234     }
235     e = strchr(s, 0);
236     if (bp + (e - s) <= buf + sizeof(buf)) {
237         memcpy(bp, s, e - s);
238         bp += e - s;
239         bpl = pl;
240         bid = id;
241     } else {
242         journal_output_start(pl, id);
243         journal_entry_pr(buf, bp - buf);
244         journal_entry_pr(s, e - s);
245         journal_entry_end(0, 0);
246         bp = buf;
247     }
248 }
249
250 static void
251 journal_output_start(struct player *pl, int id)
252 {
253     journal_entry_start("output %s %d ", empth_name(pl->proc), id);
254 }
255
256 void
257 journal_input(char *input)
258 {
259     journal_entry_start("input ");
260     journal_entry_pr(input, -1);
261     journal_entry_end(1, 1);
262 }
263
264 void
265 journal_command(char *cmd)
266 {
267     char *eptr = strchr(cmd, ' ');
268     journal_entry("command %.*s", eptr ? (int)(eptr - cmd) : -1, cmd);
269 }
270
271 void
272 journal_update(int etu)
273 {
274     journal_entry("update %d", etu);
275 }