| 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 |
} |