AmendHub

Download

jcs

/

subtext

/

logger.c

 

(View History)

jcs   logger: syslog doesn't need the hostname on the wire after all Latest amendment: 586 on 2024-02-13

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include "focusable.h"
21 #include "logger.h"
22 #include "subtext.h"
23 #include "tcp.h"
24 #include "user.h"
25 #include "util.h"
26
27 struct logger *logger = NULL;
28
29 static UDPiopb syslog_pb;
30 static StreamPtr syslog_stream = 0;
31 static char *syslog_rcv_buf = NULL;
32 #define SYSLOG_RCV_BUF_SIZE 2048
33 static wdsEntry syslog_wds[2];
34 static char *syslog_send_buf = NULL;
35 #define SYSLOG_SEND_BUF_SIZE 1024
36
37 void logger_layout(Rect *init_bounds);
38 void logger_key_down(struct focusable *focusable, EventRecord *event);
39 void logger_mouse_down(struct focusable *focusable, EventRecord *event);
40 bool logger_menu(struct focusable *focusable, short menu, short item);
41 void logger_resize(struct focusable *focusable, EventRecord *event);
42 void logger_update(struct focusable *focusable, EventRecord *event);
43 void logger_resume(struct focusable *focusable, EventRecord *event);
44 bool logger_quit(struct focusable *focusable);
45 void logger_flush_buffer(bool force);
46
47 void syslog_vprintf(const char *format, va_list ap);
48
49 void
50 logger_init(void)
51 {
52 struct focusable *focusable;
53 Rect bounds = { 0 };
54 short padding = 5;
55
56 logger = xmalloczero(sizeof(struct logger));
57 if (!logger)
58 panic("Can't allocate logger");
59 logger->buffered_logs = xmalloc(LOGGER_BUFFERED_LOG_SIZE);
60 if (!logger->buffered_logs)
61 panic("Can't allocate logger buffer");
62 logger->buffered_logs[0] = '\0';
63 logger->autoflush = true;
64
65 bounds.left = padding;
66 bounds.top = ((screenBits.bounds.bottom -
67 screenBits.bounds.top) / 3);
68 bounds.right = screenBits.bounds.right - padding - 1;
69 bounds.bottom = screenBits.bounds.bottom - padding - 1;
70
71 logger->win = NewWindow(0L, &bounds, "\p", false,
72 documentProc, (WindowPtr)-1L, false, 0);
73 if (!logger->win)
74 panic("Can't create logger window");
75 logger_update_title();
76
77 SetPort(logger->win);
78
79 TextFont(LOGGER_FONT);
80 TextSize(LOGGER_FONT_SIZE);
81
82 bounds.right -= bounds.left;
83 bounds.bottom -= bounds.top;
84 bounds.top = bounds.left = 0;
85 logger_layout(&bounds);
86
87 focusable = xmalloczero(sizeof(struct focusable));
88 if (!focusable)
89 panic("Can't create focusable");
90 focusable->win = logger->win;
91 focusable->cookie = logger;
92 focusable->mouse_down = logger_mouse_down;
93 focusable->menu = logger_menu;
94 focusable->resize = logger_resize;
95 focusable->update = logger_update;
96 focusable->quit = logger_quit;
97 focusable->resume = logger_resume;
98 if (!add_focusable(focusable))
99 panic("Can't add focusable");
100 logger->focusable = focusable;
101
102 DrawControls(logger->win);
103 DrawGrowIconOnly(logger->win);
104 }
105
106 void
107 logger_layout(Rect *init_bounds)
108 {
109 Rect bounds, inset_bounds, win_bounds;
110 bool init = (init_bounds != NULL);
111
112 if (init)
113 win_bounds = *init_bounds;
114 else
115 win_bounds = logger->win->portRect;
116
117 /* messages scrollbar */
118 bounds.top = -1;
119 bounds.right = win_bounds.right - win_bounds.left + 1;
120 bounds.bottom = win_bounds.bottom - win_bounds.top - 14;
121 bounds.left = bounds.right - SCROLLBAR_WIDTH;
122 if (init) {
123 logger->messages_scroller = NewControl(logger->win, &bounds,
124 "\p", true, 1, 1, 1, scrollBarProc, 0L);
125 if (!logger->messages_scroller)
126 panic("Can't create messages scroller");
127 } else
128 (*(logger->messages_scroller))->contrlRect = bounds;
129
130 /* messages */
131 bounds.right = (*(logger->messages_scroller))->contrlRect.left;
132 bounds.left = 0;
133 bounds.top = 0;
134 bounds.bottom = win_bounds.bottom - win_bounds.top;
135 if (init) {
136 inset_bounds = bounds;
137 InsetRect(&inset_bounds, 4, 4);
138 logger->messages_te = TEStylNew(&inset_bounds, &bounds);
139 if (logger->messages_te == NULL)
140 panic("Can't create logger TE");
141 (*(logger->messages_te))->caretHook = NullCaretHook;
142 TEActivate(logger->messages_te);
143 } else {
144 (*(logger->messages_te))->viewRect = bounds;
145 InsetRect(&bounds, 4, 4);
146 (*(logger->messages_te))->destRect = bounds;
147 TECalText(logger->messages_te);
148 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
149 }
150
151 DrawControls(logger->win);
152 DrawGrowIconOnly(logger->win);
153
154 if (!init)
155 logger_flush_buffer(true);
156 }
157
158 void
159 logger_resume(struct focusable *focusable, EventRecord *event)
160 {
161 show_focusable(focusable);
162 InvalRect(logger->win->visRgn);
163 }
164
165 bool
166 logger_quit(struct focusable *focusable)
167 {
168 destroy_focusable(focusable);
169 xfree(&logger->buffered_logs);
170 xfree(&logger);
171
172 return true;
173 }
174
175 void
176 logger_update(struct focusable *focusable, EventRecord *event)
177 {
178 GrafPtr old_port;
179 Rect r;
180 short what = -1;
181
182 GetPort(&old_port);
183 SetPort(logger->win);
184
185 if (event != NULL)
186 what = event->what;
187
188 switch (what) {
189 case -1:
190 case updateEvt:
191 TextFont(applFont);
192 TextSize(10);
193
194 EraseRect(&logger->win->portRect);
195
196 r = (*(logger->messages_te))->viewRect;
197 InsetRect(&r, -1, -1);
198 FrameRect(&r);
199
200 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
201 DrawControls(logger->win);
202 DrawGrowIconOnly(logger->win);
203 break;
204 case activateEvt:
205 if (event->modifiers & activeFlag) {
206 TEActivate(logger->messages_te);
207 } else {
208 TEDeactivate(logger->messages_te);
209 }
210 break;
211 }
212
213 SetPort(old_port);
214 }
215
216 void
217 logger_update_title(void)
218 {
219 static char logger_title[64];
220 short n, uprinted = 0;
221
222 snprintf(logger_title, sizeof(logger_title),
223 "%s | %luKB Free | %lu Call%s | ", db->config.name,
224 (FreeMem() / 1024),
225 session_today_tally.calls, session_today_tally.calls == 1 ? "" : "s");
226
227 for (n = 0; n < MAX_SESSIONS; n++) {
228 if (sessions[n] == NULL || !sessions[n]->logged_in)
229 continue;
230
231 if (uprinted++)
232 strlcat(logger_title, ", ", sizeof(logger_title));
233 else
234 strlcat(logger_title, "Logged in: ", sizeof(logger_title));
235
236 strlcat(logger_title,
237 sessions[n]->user ? sessions[n]->user->username : GUEST_USERNAME,
238 sizeof(logger_title));
239 }
240
241 if (uprinted == 0)
242 strlcat(logger_title, "0 connected", sizeof(logger_title));
243
244 SetWTitle(logger->win, CtoPstr(logger_title));
245 }
246
247 void
248 logger_mouse_down(struct focusable *focusable, EventRecord *event)
249 {
250 Point p;
251 ControlHandle control;
252 Rect r;
253 int val, adj;
254
255 p = event->where;
256 GlobalToLocal(&p);
257
258 r = (*(logger->messages_te))->viewRect;
259 if (PtInRect(p, &r)) {
260 TEClick(p, ((event->modifiers & shiftKey) != 0),
261 logger->messages_te);
262 return;
263 }
264
265 switch (FindControl(p, logger->win, &control)) {
266 case inUpButton:
267 case inDownButton:
268 case inPageUp:
269 case inPageDown:
270 if (control != logger->messages_scroller)
271 break;
272 SetTrackControlTE(logger->messages_te);
273 TrackControl(control, p, TrackMouseDownInControl);
274 break;
275 case inThumb:
276 val = GetCtlValue(control);
277 if (TrackControl(control, p, 0L) == 0)
278 break;
279 adj = val - GetCtlValue(control);
280 if (adj != 0) {
281 val -= adj;
282 if (control == logger->messages_scroller)
283 TEScroll(0, adj * TEGetHeight(0, 0, logger->messages_te),
284 logger->messages_te);
285 SetCtlValue(control, val);
286 }
287 break;
288 }
289 }
290
291 bool
292 logger_menu(struct focusable *focusable, short menu, short item)
293 {
294 switch (menu) {
295 case EDIT_MENU_ID:
296 switch (item) {
297 case EDIT_MENU_COPY_ID:
298 HLock(logger->messages_te);
299 if ((*(logger->messages_te))->selStart ==
300 (*(logger->messages_te))->selEnd)
301 SysBeep(10);
302 else
303 TECopy(logger->messages_te);
304 HUnlock(logger->messages_te);
305 break;
306 default:
307 SysBeep(10);
308 break;
309 }
310 return true;
311 }
312
313 return false;
314 }
315
316 void
317 logger_resize(struct focusable *focusable, EventRecord *event)
318 {
319 Rect bounds;
320 long newsize, width, height;
321
322 bounds.left = 100;
323 bounds.top = 100;
324 bounds.right = screenBits.bounds.right;
325 bounds.bottom = screenBits.bounds.bottom;
326
327 newsize = GrowWindow(focusable->win, event->where, &bounds);
328
329 height = HiWord(newsize);
330 width = LoWord(newsize);
331 SizeWindow(focusable->win, width, height, true);
332 logger_layout(NULL);
333 }
334
335 size_t
336 logger_printf(const char *format, ...)
337 {
338 va_list va;
339 size_t len;
340
341 if (!logger)
342 return 0;
343
344 va_start(va, format);
345 len = logger_vprintf(format, va);
346 va_end(va);
347
348 if (db->config.syslog_ip) {
349 va_start(va, format);
350 syslog_vprintf(format, va);
351 va_end(va);
352 }
353
354 return len;
355 }
356
357 size_t
358 logger_vprintf(const char *format, va_list ap)
359 {
360 static char buf[600];
361 ssize_t len;
362 time_t now = Time;
363
364 if (!logger)
365 return 0;
366
367 blanker_unblank();
368
369 len = strftime(buf, sizeof(buf), "\r[%H:%M:%S] ", localtime(&now));
370 len += vsnprintf(buf + len, sizeof(buf) - len, format, ap);
371 if (len > sizeof(buf)) {
372 buf[sizeof(buf) - 1] = ']';
373 buf[sizeof(buf) - 2] = '…';
374 buf[sizeof(buf) - 3] = '[';
375 len = sizeof(buf);
376 }
377
378 while (buf[len - 1] == '\r') {
379 buf[len - 1] = '\0';
380 len--;
381 }
382
383 if (logger->buffered_logs_len + len > LOGGER_BUFFERED_LOG_SIZE)
384 logger_flush_buffer(false);
385
386 len = strlcat(logger->buffered_logs, buf, LOGGER_BUFFERED_LOG_SIZE);
387 logger->buffered_logs_len = MIN(len, LOGGER_BUFFERED_LOG_SIZE);
388
389 if (logger->autoflush)
390 logger_flush_buffer(false);
391
392 return len;
393 }
394
395 void
396 logger_flush_buffer(bool force)
397 {
398 RgnHandle savergn;
399 Rect zerorect = { 0, 0, 0, 0 };
400 GrafPtr old_port;
401 short line_height = 0, new_lines = 1, n;
402 char *buf;
403
404 if (!logger || (logger->buffered_logs_len == 0 && !force))
405 return;
406
407 for (n = 0; n < logger->buffered_logs_len; n++) {
408 if (logger->buffered_logs[n] == '\r')
409 new_lines++;
410 }
411
412 line_height = LOGGER_FONT_SIZE + 3;
413
414 GetPort(&old_port);
415 SetPort(logger->win);
416
417 /* check for TE overflow */
418
419 HLock(logger->messages_te);
420
421 /* too many lines */
422 if ((unsigned long)(*(logger->messages_te))->nLines >=
423 (nitems((*(logger->messages_te))->lineStarts) - new_lines))
424 goto te_overflow;
425
426 /* too many characters */
427 if ((unsigned long)(*(logger->messages_te))->teLength >=
428 (SHRT_MAX - logger->buffered_logs_len))
429 goto te_overflow;
430
431 /* destRect of all lines is too tall */
432 if ((unsigned long)(*(logger->messages_te))->nLines * line_height >=
433 (SHRT_MAX - new_lines))
434 goto te_overflow;
435
436 goto no_overflow;
437
438 te_overflow:
439 savergn = NewRgn();
440 GetClip(savergn);
441 /* create an empty clip region so all TE updates are hidden */
442 ClipRect(&zerorect);
443
444 /* select some lines at the start, delete them */
445 TESetSelect(0, (*(logger->messages_te))->lineStarts[new_lines * 2],
446 logger->messages_te);
447 TEDelete(logger->messages_te);
448
449 /* scroll up, causing a repaint */
450 TEPinScroll(0, SHRT_MAX, logger->messages_te);
451
452 /* then scroll back down to what it looked like before we did anything */
453 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
454
455 /* resume normal drawing */
456 SetClip(savergn);
457 DisposeRgn(savergn);
458
459 no_overflow:
460 TESetSelect(SHRT_MAX, SHRT_MAX, logger->messages_te);
461
462 buf = logger->buffered_logs;
463 if ((*(logger->messages_te))->teLength == 0) {
464 /* skip leading \r */
465 logger->buffered_logs_len--;
466 buf++;
467 }
468
469 TEInsert(buf, logger->buffered_logs_len, logger->messages_te);
470
471 if ((unsigned long)(*(logger->messages_te))->nLines >=
472 nitems((*(logger->messages_te))->lineStarts))
473 warn("te overflow!");
474
475 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
476 SetCtlValue(logger->messages_scroller,
477 GetCtlMax(logger->messages_scroller));
478 UpdateScrollbarForTE(logger->win, logger->messages_scroller,
479 logger->messages_te, false);
480 HUnlock(logger->messages_te);
481
482 SetPort(old_port);
483
484 logger->buffered_logs_len = 0;
485 logger->buffered_logs[0] = '\0';
486 }
487
488 void
489 syslog_init(void)
490 {
491 short error;
492
493 if (!db->config.syslog_ip)
494 return;
495
496 if (_TCPInit() != noErr)
497 panic("Failed initializing MacTCP");
498
499 syslog_rcv_buf = xmalloc(SYSLOG_RCV_BUF_SIZE);
500 if (syslog_rcv_buf == NULL)
501 panic("Failed allocating syslog buf");
502 error = _UDPCreate(&syslog_pb, &syslog_stream,
503 (Ptr)syslog_rcv_buf, SYSLOG_RCV_BUF_SIZE, NULL, NULL,
504 NULL, false);
505 if (error)
506 panic("UDPCreate failed: %d", error);
507
508 syslog_send_buf = xmalloc(SYSLOG_SEND_BUF_SIZE);
509 if (syslog_send_buf == NULL)
510 panic("Failed allocating syslog send buf");
511 }
512
513 void
514 syslog_deinit(void)
515 {
516 if (syslog_stream) {
517 _UDPRelease(&syslog_pb, syslog_stream, NULL, NULL, false);
518 syslog_stream = 0;
519 }
520
521 if (syslog_rcv_buf)
522 xfree(&syslog_rcv_buf);
523 if (syslog_send_buf)
524 xfree(&syslog_send_buf);
525 }
526
527 bool
528 syslog_reinit(void)
529 {
530 syslog_deinit();
531 syslog_init();
532 }
533
534 /* RFC3164 */
535 void
536 syslog_vprintf(const char *format, va_list ap)
537 {
538 size_t len;
539 time_t now = Time;
540
541 if (!db->config.syslog_ip || syslog_send_buf == NULL)
542 return;
543
544 /* system (3) priority, notice (5) severity */
545 len = strftime(syslog_send_buf, SYSLOG_SEND_BUF_SIZE,
546 "<29>%b %d %H:%M:%S subtext: ", localtime(&now));
547 len += vsnprintf(syslog_send_buf + len, SYSLOG_SEND_BUF_SIZE - len,
548 format, ap);
549
550 syslog_wds[0].ptr = (Ptr)syslog_send_buf;
551 syslog_wds[0].length = len;
552 syslog_wds[1].ptr = 0;
553 syslog_wds[1].length = 0;
554
555 _UDPSend(&syslog_pb, syslog_stream, syslog_wds,
556 db->config.syslog_ip, 514, NULL, NULL, false);
557 }