]> git.saurik.com Git - apple/syslog.git/blame - syslogd.tproj/remote.c
syslog-148.7.tar.gz
[apple/syslog.git] / syslogd.tproj / remote.c
CommitLineData
57b0aad2 1/*
db78b1bd 2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
57b0aad2
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <netinet/tcp.h>
29#include <arpa/inet.h>
30#include <sys/un.h>
31#include <sys/uio.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <unistd.h>
38#include <netdb.h>
39#include <pthread.h>
40#include <notify.h>
57b0aad2
A
41#include "daemon.h"
42
43#define forever for(;;)
44
45#define MY_ID "remote"
46#define MAXLINE 4096
47#define LOCKDOWN_PATH "/var/run/lockdown"
48#define SYSLOG_SOCK_PATH "/var/run/lockdown/syslog.sock"
49#define ASL_REMOTE_PORT 203
50
51#define PRINT_STD 0
52#define PRINT_RAW 1
53
a83ff38a
A
54#define WATCH_OFF 0
55#define WATCH_LOCKDOWN_START 1
56#define WATCH_RUN 2
57
58#define SESSION_FLAGS_LOCKDOWN 0x00000001
59
57b0aad2
A
60#define MAXSOCK 1
61
62static int rfd4 = -1;
63static int rfd6 = -1;
64static int rfdl = -1;
65
db78b1bd
A
66static dispatch_source_t in_src_local;
67static dispatch_source_t in_src_tcp;
68static dispatch_queue_t in_queue;
69
57b0aad2
A
70#ifdef NSS32
71typedef uint32_t notify_state_t;
72extern int notify_set_state(int, notify_state_t);
73#else
74typedef uint64_t notify_state_t;
75#endif
76
77extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen);
78extern size_t asl_memory_size(asl_memory_t *s);
79extern uint32_t db_query(aslresponse query, aslresponse *res, uint64_t startid, int count, int flags, uint64_t *lastid, int32_t ruid, int32_t rgid);
80
81#define SESSION_WRITE(f,x) if (write(f, x, strlen(x)) < 0) goto exit_session
82
a83ff38a
A
83typedef struct
84{
85 int sock;
86 uint32_t flags;
87} session_args_t;
88
57b0aad2
A
89uint32_t
90remote_db_size(uint32_t sel)
91{
92 if (sel == DB_TYPE_FILE) return global.db_file_max;
93 if (sel == DB_TYPE_MEMORY) return global.db_memory_max;
94 if (sel == DB_TYPE_MINI) return global.db_mini_max;
95 return 0;
96}
97
98uint32_t
99remote_db_set_size(uint32_t sel, uint32_t size)
100{
101 if (sel == DB_TYPE_FILE) global.db_file_max = size;
102 if (sel == DB_TYPE_MEMORY) global.db_memory_max = size;
103 if (sel == DB_TYPE_MINI) global.db_mini_max = size;
104 return 0;
105}
106
a83ff38a 107aslmsg
57b0aad2
A
108remote_db_stats(uint32_t sel)
109{
a83ff38a 110 aslmsg m;
57b0aad2
A
111 m = NULL;
112
113 if (sel == DB_TYPE_FILE) asl_store_statistics(global.file_db, &m);
114 if (sel == DB_TYPE_MEMORY) asl_memory_statistics(global.memory_db, &m);
115 if (sel == DB_TYPE_MINI) asl_mini_memory_statistics(global.mini_db, &m);
116 return m;
117}
118
119void
120session(void *x)
121{
db78b1bd 122 int i, s, wfd, status, pfmt, watch, wtoken, nfd, do_prompt;
57b0aad2
A
123 aslresponse res;
124 asl_search_result_t ql;
125 uint32_t outlen;
a83ff38a 126 aslmsg stats;
57b0aad2
A
127 asl_msg_t *query;
128 asl_msg_t *qlq[1];
129 char str[1024], *p, *qs, *out;
130 ssize_t len;
db78b1bd 131 fd_set readfds, errfds;
57b0aad2 132 uint64_t low_id, high_id;
a83ff38a
A
133 uint32_t dbselect, flags;
134 session_args_t *sp;
57b0aad2
A
135
136 if (x == NULL) pthread_exit(NULL);
137
a83ff38a
A
138 sp = (session_args_t *)x;
139 s = sp->sock;
140 flags = sp->flags;
57b0aad2
A
141 free(x);
142
db78b1bd
A
143 asldebug("%s %d: starting interactive session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
144
145 do_prompt = 1;
a83ff38a 146 watch = WATCH_OFF;
57b0aad2
A
147 wfd = -1;
148 wtoken = -1;
149
150 dbselect = 0;
151 if (global.dbtype & DB_TYPE_MEMORY) dbselect = DB_TYPE_MEMORY;
152 else if (global.dbtype & DB_TYPE_MINI) dbselect = DB_TYPE_MINI;
153 else if (global.dbtype & DB_TYPE_FILE) dbselect = DB_TYPE_FILE;
154
155 low_id = 0;
156 high_id = 0;
157
158 pfmt = PRINT_STD;
159 query = NULL;
160 memset(&ql, 0, sizeof(asl_search_result_t));
161
db78b1bd 162 snprintf(str, sizeof(str), "\n========================\nASL is here to serve you\n");
57b0aad2
A
163 if (write(s, str, strlen(str)) < 0)
164 {
165 close(s);
166 pthread_exit(NULL);
167 return;
168 }
169
57b0aad2
A
170 forever
171 {
db78b1bd 172 if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (do_prompt > 0))
57b0aad2 173 {
db78b1bd 174 snprintf(str, sizeof(str), "> ");
57b0aad2
A
175 SESSION_WRITE(s, str);
176 }
177
178 do_prompt = 1;
db78b1bd 179
57b0aad2
A
180 memset(str, 0, sizeof(str));
181
182 FD_ZERO(&readfds);
183 FD_SET(s, &readfds);
db78b1bd
A
184 FD_ZERO(&errfds);
185 FD_SET(s, &errfds);
57b0aad2
A
186 nfd = s;
187
188 if (wfd != -1)
189 {
190 FD_SET(wfd, &readfds);
191 if (wfd > nfd) nfd = wfd;
192 }
193
db78b1bd
A
194 status = select(nfd + 1, &readfds, NULL, &errfds, NULL);
195 if (status == 0) continue;
196 if (status < 0)
197 {
198 asldebug("%s %d: select %d %s\n", MY_ID, s, errno, strerror(errno));
199 goto exit_session;
200 }
201
202 if (FD_ISSET(s, &errfds))
203 {
204 asldebug("%s %d: error on socket %d\n", MY_ID, s, s);
205 goto exit_session;
206 }
57b0aad2
A
207
208 if ((wfd != -1) && (FD_ISSET(wfd, &readfds)))
209 {
db78b1bd 210 (void)read(wfd, &i, sizeof(int));
57b0aad2
A
211 }
212
213 if (FD_ISSET(s, &readfds))
214 {
215 len = read(s, str, sizeof(str) - 1);
db78b1bd
A
216 if (len <= 0)
217 {
218 asldebug("%s %d: read error on socket %d: %d %s\n", MY_ID, s, s, errno, strerror(errno));
219 goto exit_session;
220 }
57b0aad2
A
221
222 while ((len > 1) && ((str[len - 1] == '\n') || (str[len - 1] == '\r')))
223 {
224 str[len - 1] = '\0';
225 len--;
226 }
227
228 if ((!strcmp(str, "q")) || (!strcmp(str, "quit")) || (!strcmp(str, "exit")))
229 {
db78b1bd 230 snprintf(str, sizeof(str), "Goodbye\n");
57b0aad2
A
231 write(s, str, strlen(str));
232 close(s);
233 s = -1;
234 break;
235 }
236
237 if ((!strcmp(str, "?")) || (!strcmp(str, "help")))
238 {
db78b1bd 239 snprintf(str, sizeof(str), "Commands\n");
57b0aad2 240 SESSION_WRITE(s, str);
db78b1bd 241 snprintf(str, sizeof(str), " quit exit session\n");
57b0aad2 242 SESSION_WRITE(s, str);
db78b1bd 243 snprintf(str, sizeof(str), " select [val] get [set] current database\n");
57b0aad2 244 SESSION_WRITE(s, str);
db78b1bd 245 snprintf(str, sizeof(str), " val must be \"file\", \"mem\", or \"mini\"\n");
57b0aad2 246 SESSION_WRITE(s, str);
db78b1bd 247 snprintf(str, sizeof(str), " file [on/off] enable / disable file store\n");
57b0aad2 248 SESSION_WRITE(s, str);
db78b1bd 249 snprintf(str, sizeof(str), " memory [on/off] enable / disable memory store\n");
57b0aad2 250 SESSION_WRITE(s, str);
db78b1bd 251 snprintf(str, sizeof(str), " mini [on/off] enable / disable mini memory store\n");
57b0aad2 252 SESSION_WRITE(s, str);
db78b1bd 253 snprintf(str, sizeof(str), " stats database statistics\n");
57b0aad2 254 SESSION_WRITE(s, str);
db78b1bd 255 snprintf(str, sizeof(str), " flush flush database\n");
57b0aad2 256 SESSION_WRITE(s, str);
db78b1bd 257 snprintf(str, sizeof(str), " dbsize [val] get [set] database size (# of records)\n");
57b0aad2 258 SESSION_WRITE(s, str);
db78b1bd 259 snprintf(str, sizeof(str), " watch print new messages as they arrive\n");
57b0aad2 260 SESSION_WRITE(s, str);
db78b1bd 261 snprintf(str, sizeof(str), " stop stop watching for new messages\n");
57b0aad2 262 SESSION_WRITE(s, str);
db78b1bd 263 snprintf(str, sizeof(str), " raw use raw format for printing messages\n");
57b0aad2 264 SESSION_WRITE(s, str);
db78b1bd 265 snprintf(str, sizeof(str), " std use standard format for printing messages\n");
57b0aad2 266 SESSION_WRITE(s, str);
db78b1bd 267 snprintf(str, sizeof(str), " * show all log messages\n");
57b0aad2 268 SESSION_WRITE(s, str);
db78b1bd 269 snprintf(str, sizeof(str), " * key val equality search for messages (single key/value pair)\n");
57b0aad2 270 SESSION_WRITE(s, str);
db78b1bd 271 snprintf(str, sizeof(str), " * op key val search for matching messages (single key/value pair)\n");
57b0aad2 272 SESSION_WRITE(s, str);
db78b1bd 273 snprintf(str, sizeof(str), " * [op key val] ... search for matching messages (multiple key/value pairs)\n");
57b0aad2 274 SESSION_WRITE(s, str);
db78b1bd 275 snprintf(str, sizeof(str), " operators: = < > ! (not equal) T (key exists) R (regex)\n");
57b0aad2 276 SESSION_WRITE(s, str);
db78b1bd 277 snprintf(str, sizeof(str), " modifiers (must follow operator):\n");
57b0aad2 278 SESSION_WRITE(s, str);
db78b1bd 279 snprintf(str, sizeof(str), " C=casefold N=numeric S=substring A=prefix Z=suffix\n");
57b0aad2 280 SESSION_WRITE(s, str);
db78b1bd 281 snprintf(str, sizeof(str), "\n");
57b0aad2
A
282 SESSION_WRITE(s, str);
283 continue;
284 }
285 else if (!strcmp(str, "stats"))
286 {
287 stats = remote_db_stats(dbselect);
a83ff38a 288 out = asl_format_message((asl_msg_t *)stats, ASL_MSG_FMT_RAW, ASL_TIME_FMT_SEC, ASL_ENCODE_NONE, &outlen);
57b0aad2
A
289 write(s, out, outlen);
290 free(out);
291 asl_free(stats);
292 continue;
293 }
294 else if (!strcmp(str, "flush"))
295 {}
296 else if (!strncmp(str, "select", 6))
297 {
298 p = str + 6;
299 while ((*p == ' ') || (*p == '\t')) p++;
300 if (*p == '\0')
301 {
db78b1bd
A
302 if (dbselect == 0) snprintf(str, sizeof(str), "no store\n");
303 else if (dbselect == DB_TYPE_FILE) snprintf(str, sizeof(str), "file store\n");
304 else if (dbselect == DB_TYPE_MEMORY) snprintf(str, sizeof(str), "memory store\n");
305 else if (dbselect == DB_TYPE_MINI) snprintf(str, sizeof(str), "mini memory store\n");
57b0aad2
A
306 SESSION_WRITE(s, str);
307 continue;
308 }
309
310 if (!strncmp(p, "file", 4))
311 {
312 if ((global.dbtype & DB_TYPE_FILE) == 0)
313 {
db78b1bd 314 snprintf(str, sizeof(str), "file database is not enabled\n");
57b0aad2
A
315 SESSION_WRITE(s, str);
316 continue;
317 }
318
319 dbselect = DB_TYPE_FILE;
320 }
321 else if (!strncmp(p, "mem", 3))
322 {
323 if ((global.dbtype & DB_TYPE_MEMORY) == 0)
324 {
db78b1bd 325 snprintf(str, sizeof(str), "memory database is not enabled\n");
57b0aad2
A
326 SESSION_WRITE(s, str);
327 continue;
328 }
329
330 dbselect = DB_TYPE_MEMORY;
331 }
332 else if (!strncmp(p, "mini", 4))
333 {
334 if ((global.dbtype & DB_TYPE_MINI) == 0)
335 {
336 if (global.mini_db != NULL)
337 {
db78b1bd 338 snprintf(str, sizeof(str), "mini memory database is enabled for disaster messages\n");
57b0aad2
A
339 SESSION_WRITE(s, str);
340 }
341 else
342 {
db78b1bd 343 snprintf(str, sizeof(str), "mini memory database is not enabled\n");
57b0aad2
A
344 SESSION_WRITE(s, str);
345 continue;
346 }
347 }
348
349 dbselect = DB_TYPE_MINI;
350 }
351 else
352 {
db78b1bd 353 snprintf(str, sizeof(str), "unknown database type\n");
57b0aad2
A
354 SESSION_WRITE(s, str);
355 continue;
356 }
357
db78b1bd 358 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
359 SESSION_WRITE(s, str);
360 continue;
361 }
362 else if (!strncmp(str, "file", 4))
363 {
364 p = str + 4;
365 while ((*p == ' ') || (*p == '\t')) p++;
366 if (*p == '\0')
367 {
db78b1bd 368 snprintf(str, sizeof(str), "file database is %senabled\n", (global.dbtype & DB_TYPE_FILE) ? "" : "not ");
57b0aad2
A
369 SESSION_WRITE(s, str);
370 if ((global.dbtype & DB_TYPE_FILE) != 0) dbselect = DB_TYPE_FILE;
371 continue;
372 }
373
374 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_FILE;
375 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_FILE;
376
db78b1bd 377 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
378 SESSION_WRITE(s, str);
379 continue;
380 }
381 else if (!strncmp(str, "memory", 6))
382 {
383 p = str + 6;
384 while ((*p == ' ') || (*p == '\t')) p++;
385 if (*p == '\0')
386 {
db78b1bd 387 snprintf(str, sizeof(str), "memory database is %senabled\n", (global.dbtype & DB_TYPE_MEMORY) ? "" : "not ");
57b0aad2
A
388 SESSION_WRITE(s, str);
389 if ((global.dbtype & DB_TYPE_MEMORY) != 0) dbselect = DB_TYPE_MEMORY;
390 continue;
391 }
392
393 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MEMORY;
394 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MEMORY;
395
db78b1bd 396 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
397 SESSION_WRITE(s, str);
398 continue;
399 }
400 else if (!strncmp(str, "mini", 4))
401 {
402 p = str + 4;
403 while ((*p == ' ') || (*p == '\t')) p++;
404 if (*p == '\0')
405 {
db78b1bd 406 snprintf(str, sizeof(str), "mini database is %senabled\n", (global.dbtype & DB_TYPE_MINI) ? "" : "not ");
57b0aad2
A
407 SESSION_WRITE(s, str);
408 if ((global.dbtype & DB_TYPE_MINI) != 0) dbselect = DB_TYPE_MINI;
409 continue;
410 }
411
412 if (!strcmp(p, "on")) global.dbtype |= DB_TYPE_MINI;
413 else if (!strcmp(p, "off")) global.dbtype &= ~ DB_TYPE_MINI;
414
db78b1bd 415 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
416 SESSION_WRITE(s, str);
417 continue;
418 }
419 else if (!strncmp(str, "dbsize", 6))
420 {
421 if (dbselect == 0)
422 {
db78b1bd 423 snprintf(str, sizeof(str), "no store\n");
57b0aad2
A
424 SESSION_WRITE(s, str);
425 continue;
426 }
427
428 p = str + 6;
429 while ((*p == ' ') || (*p == '\t')) p++;
430 if (*p == '\0')
431 {
db78b1bd 432 snprintf(str, sizeof(str), "DB size %u\n", remote_db_size(dbselect));
57b0aad2
A
433 SESSION_WRITE(s, str);
434 continue;
435 }
436
437 i = atoi(p);
438 remote_db_set_size(dbselect, i);
439
db78b1bd 440 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
441 SESSION_WRITE(s, str);
442 continue;
443 }
57b0aad2
A
444 else if (!strcmp(str, "stop"))
445 {
a83ff38a 446 if (watch != WATCH_OFF)
57b0aad2 447 {
a83ff38a 448 watch = WATCH_OFF;
57b0aad2
A
449 notify_cancel(wtoken);
450 wfd = -1;
451 wtoken = -1;
452
453 low_id = 0;
454 high_id = 0;
455
456 if (query != NULL) free(query);
457 query = NULL;
458
db78b1bd 459 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
460 SESSION_WRITE(s, str);
461 continue;
462 }
463
db78b1bd 464 snprintf(str, sizeof(str), "not watching!\n");
57b0aad2
A
465 SESSION_WRITE(s, str);
466 continue;
467 }
468 else if (!strcmp(str, "raw"))
469 {
470 pfmt = PRINT_RAW;
471 continue;
472 }
473 else if (!strcmp(str, "std"))
474 {
475 pfmt = PRINT_STD;
476 continue;
477 }
478 else if (!strcmp(str, "watch"))
479 {
db78b1bd 480 if (((flags & SESSION_FLAGS_LOCKDOWN) == 0) && (watch != WATCH_OFF))
57b0aad2 481 {
db78b1bd 482 snprintf(str, sizeof(str), "already watching!\n");
57b0aad2
A
483 SESSION_WRITE(s, str);
484 continue;
485 }
486
a83ff38a 487 if (flags & SESSION_FLAGS_LOCKDOWN)
57b0aad2 488 {
a83ff38a 489 watch = WATCH_LOCKDOWN_START;
57b0aad2 490 }
a83ff38a
A
491 else
492 {
db78b1bd 493 status = notify_register_file_descriptor(kNotifyASLDBUpdate, &wfd, 0, &wtoken);
a83ff38a
A
494 if (status != 0)
495 {
db78b1bd 496 snprintf(str, sizeof(str), "notify_register_file_descriptor failed: %d\n", status);
a83ff38a
A
497 SESSION_WRITE(s, str);
498 continue;
499 }
57b0aad2 500
a83ff38a
A
501 watch = WATCH_RUN;
502 }
57b0aad2 503
db78b1bd 504 snprintf(str, sizeof(str), "OK\n");
57b0aad2
A
505 SESSION_WRITE(s, str);
506 do_prompt = 2;
507 }
508 else if ((str[0] == '*') || (str[0] == 'T') || (str[0] == '=') || (str[0] == '!') || (str[0] == '<') || (str[0] == '>'))
509 {
510 memset(&ql, 0, sizeof(asl_search_result_t));
511 if (query != NULL) free(query);
512 query = NULL;
513
514 p = str;
515 if (*p == '*') p++;
516 while ((*p == ' ') || (*p == '\t')) p++;
517
518 if (*p == '\0')
519 {
520 /* NULL query */
521 }
522 else if (*p == '[')
523 {
524 qs = NULL;
525 asprintf(&qs, "Q %s", p);
526 query = asl_msg_from_string(qs);
527 free(qs);
528 }
529 else if ((*p == 'T') || (*p == '=') || (*p == '!') || (*p == '<') || (*p == '>') || (*p == 'R'))
530 {
531 qs = NULL;
532 asprintf(&qs, "Q [%s]", p);
533 query = asl_msg_from_string(qs);
534 free(qs);
535 }
536 else
537 {
538 qs = NULL;
539 asprintf(&qs, "Q [= %s]", p);
540 query = asl_msg_from_string(qs);
541 free(qs);
542 }
543 }
544 else
545 {
db78b1bd 546 snprintf(str, sizeof(str), "unrecognized command\n");
57b0aad2 547 SESSION_WRITE(s, str);
db78b1bd 548 snprintf(str, sizeof(str), "enter \"help\" for help\n");
57b0aad2
A
549 SESSION_WRITE(s, str);
550 continue;
551 }
552 }
553
a83ff38a
A
554 /*
555 * If this session is PurpleConsole watching for log messages,
556 * we pass through this part of the loop once initially to pick up
557 * existing messages already in memory. After that, dbserver will
558 * send new messages in send_to_direct_watchers(). We wait until
559 * the initial messages are sent to PurpleConsole before setting
560 * global.lockdown_session_fd to allow this query to complete before
561 * dbserver starts sending. To prevent a race between this query and
db78b1bd
A
562 * when messages are sent by send_to_direct_watchers, we suspend the
563 * work queue here and resume it when lockdown_session_fd is set.
a83ff38a
A
564 */
565 if ((flags & SESSION_FLAGS_LOCKDOWN) && (watch == WATCH_RUN)) continue;
566
db78b1bd 567 if (watch == WATCH_LOCKDOWN_START) dispatch_suspend(global.work_queue);
a83ff38a 568
57b0aad2
A
569 if (query != NULL)
570 {
571 ql.count = 1;
572 qlq[0] = query;
573 ql.msg = qlq;
574 }
575
a83ff38a 576 if (watch == WATCH_OFF) low_id = 0;
57b0aad2
A
577
578 memset(&res, 0, sizeof(aslresponse));
579 high_id = 0;
db78b1bd 580 (void)db_query(&ql, (aslresponse *)&res, low_id, 0, 0, &high_id, 0, 0);
57b0aad2 581
a83ff38a 582 if ((watch == WATCH_RUN) && (high_id >= low_id)) low_id = high_id + 1;
57b0aad2
A
583
584 if (res == NULL)
585 {
a83ff38a 586 if (watch == WATCH_OFF)
57b0aad2 587 {
db78b1bd 588 snprintf(str, sizeof(str), "-nil-\n");
57b0aad2
A
589 SESSION_WRITE(s, str);
590 }
591 else
592 {
593 if (do_prompt != 2) do_prompt = 0;
594 }
595 }
596 else if (pfmt == PRINT_RAW)
597 {
a83ff38a 598 if (watch == WATCH_RUN)
57b0aad2 599 {
db78b1bd 600 snprintf(str, sizeof(str), "\n");
57b0aad2
A
601 SESSION_WRITE(s, str);
602 }
603
604 outlen = 0;
605 out = asl_list_to_string((asl_search_result_t *)res, &outlen);
606 write(s, out, outlen);
607 free(out);
608
db78b1bd 609 snprintf(str, sizeof(str), "\n");
57b0aad2
A
610 SESSION_WRITE(s, str);
611 }
612 else
613 {
a83ff38a 614 if (watch == WATCH_RUN)
57b0aad2 615 {
db78b1bd 616 snprintf(str, sizeof(str), "\n");
57b0aad2
A
617 SESSION_WRITE(s, str);
618 }
619
db78b1bd 620 snprintf(str, sizeof(str), "\n");
57b0aad2
A
621 for (i = 0; i < res->count; i++)
622 {
db78b1bd
A
623 int wstatus;
624
57b0aad2 625 out = asl_format_message(res->msg[i], ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &outlen);
db78b1bd
A
626
627 do
628 {
629 int n = 0;
630
631 errno = 0;
632 wstatus = write(s, out, outlen);
633 if (wstatus < 0)
634 {
635 asldebug("%s %d: %d/%d write data failed: %d %s\n", MY_ID, s, i, res->count, errno, strerror(errno));
636 if (errno == EAGAIN)
637 {
638 n++;
639 if (n > 10) break;
640 usleep(10000);
641 }
642 else
643 {
644 goto exit_session;
645 }
646 }
647 } while (errno == EAGAIN);
648
57b0aad2 649 free(out);
db78b1bd 650 if (global.remote_delay_time > 0) usleep(global.remote_delay_time);
57b0aad2
A
651 }
652 }
653
654 aslresponse_free(res);
a83ff38a
A
655
656 if (watch == WATCH_LOCKDOWN_START)
657 {
658 global.lockdown_session_fd = s;
659 global.watchers_active++;
660 watch = WATCH_RUN;
661
db78b1bd 662 dispatch_resume(global.work_queue);
a83ff38a 663 }
57b0aad2
A
664 }
665
666exit_session:
667
db78b1bd
A
668 asldebug("%s %d: terminating session for %ssocket %d\n", MY_ID, s, (flags & SESSION_FLAGS_LOCKDOWN) ? "lockdown " : "", s);
669
57b0aad2
A
670 if (s >= 0)
671 {
a83ff38a
A
672 if (s == global.lockdown_session_fd) global.lockdown_session_fd = -1;
673 if (global.watchers_active > 0) global.watchers_active--;
57b0aad2 674 close(s);
57b0aad2
A
675 }
676
db78b1bd
A
677 if (watch == WATCH_LOCKDOWN_START) dispatch_resume(global.work_queue);
678 if (wtoken >= 0) notify_cancel(wtoken);
a83ff38a 679 if (query != NULL) asl_msg_release(query);
57b0aad2
A
680 pthread_exit(NULL);
681}
682
a83ff38a 683aslmsg
57b0aad2
A
684remote_acceptmsg(int fd, int tcp)
685{
686 socklen_t fromlen;
db78b1bd 687 int s, flags, status, v;
57b0aad2
A
688 pthread_attr_t attr;
689 pthread_t t;
690 struct sockaddr_storage from;
a83ff38a 691 session_args_t *sp;
57b0aad2
A
692
693 fromlen = sizeof(struct sockaddr_un);
694 if (tcp == 1) fromlen = sizeof(struct sockaddr_storage);
695
696 memset(&from, 0, sizeof(from));
697
698 s = accept(fd, (struct sockaddr *)&from, &fromlen);
699 if (s == -1)
700 {
701 asldebug("%s: accept: %s\n", MY_ID, strerror(errno));
702 return NULL;
703 }
704
705 flags = fcntl(s, F_GETFL, 0);
706 flags &= ~ O_NONBLOCK;
707 status = fcntl(s, F_SETFL, flags);
708 if (status < 0)
709 {
710 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
711 close(s);
712 return NULL;
713 }
714
a83ff38a
A
715 v = 1;
716 setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &v, sizeof(v));
717
57b0aad2
A
718 if (tcp == 1)
719 {
720 flags = 1;
721 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(int));
722 }
723
a83ff38a 724 sp = (session_args_t *)calloc(1, sizeof(session_args_t));
57b0aad2
A
725 if (sp == NULL)
726 {
727 asldebug("%s: malloc: %s\n", MY_ID, strerror(errno));
728 close(s);
729 return NULL;
730 }
731
a83ff38a
A
732 sp->sock = s;
733 if ((tcp == 0) && (global.lockdown_session_fd < 0))
734 {
735 sp->flags |= SESSION_FLAGS_LOCKDOWN;
736 }
57b0aad2
A
737
738 pthread_attr_init(&attr);
739 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
740 pthread_create(&t, &attr, (void *(*)(void *))session, (void *)sp);
741 pthread_attr_destroy(&attr);
742
743 return NULL;
744}
745
a83ff38a 746aslmsg
57b0aad2
A
747remote_acceptmsg_local(int fd)
748{
749 return remote_acceptmsg(fd, 0);
750}
751
a83ff38a 752aslmsg
57b0aad2
A
753remote_acceptmsg_tcp(int fd)
754{
755 return remote_acceptmsg(fd, 1);
756}
757
758int
759remote_init_lockdown(void)
760{
761 int status, reuse, fd;
762 struct sockaddr_un local;
763
764 fd = socket(AF_UNIX, SOCK_STREAM, 0);
765 if (fd < 0)
766 {
767 asldebug("%s: socket: %s\n", MY_ID, strerror(errno));
768 return -1;
769 }
770
771 reuse = 1;
772 status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
773 if (status < 0)
774 {
775 asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno));
776 close(fd);
777 return -1;
778 }
779
780 /* make sure the lockdown directory exists */
781 mkdir(LOCKDOWN_PATH, 0777);
782
783 memset(&local, 0, sizeof(local));
784 local.sun_family = AF_UNIX;
785 strlcpy(local.sun_path, SYSLOG_SOCK_PATH, sizeof(local.sun_path));
786 unlink(local.sun_path);
787
788 status = bind(fd, (struct sockaddr *)&local, sizeof(local.sun_family) + sizeof(local.sun_path));
789
790 if (status < 0)
791 {
792 asldebug("%s: bind: %s\n", MY_ID, strerror(errno));
793 close(fd);
794 return -1;
795 }
796
797 status = fcntl(fd, F_SETFL, O_NONBLOCK);
798 if (status < 0)
799 {
800 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
801 close(fd);
802 return -1;
803 }
804
805 status = listen(fd, 5);
806 if (status < 0)
807 {
808 asldebug("%s: listen: %s\n", MY_ID, strerror(errno));
809 close(fd);
810 return -1;
811 }
812
813 chmod(SYSLOG_SOCK_PATH, 0666);
814
db78b1bd
A
815 in_src_local = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
816 dispatch_source_set_event_handler(in_src_local, ^{ remote_acceptmsg_local(fd); });
817 dispatch_resume(in_src_local);
818
57b0aad2
A
819 return fd;
820}
821
822int
823remote_init_tcp(int family)
824{
825 int status, reuse, fd;
826 struct sockaddr_in a4;
827 struct sockaddr_in6 a6;
828 struct sockaddr *s;
829 socklen_t len;
830
831 fd = socket(family, SOCK_STREAM, 0);
832 if (fd < 0)
833 {
834 asldebug("%s: socket: %s\n", MY_ID, strerror(errno));
835 return -1;
836 }
837
838 reuse = 1;
839 status = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(int));
840 if (status < 0)
841 {
842 asldebug("%s: setsockopt: %s\n", MY_ID, strerror(errno));
843 close(fd);
844 return -1;
845 }
846
847 memset(&(a4.sin_addr), 0, sizeof(struct in_addr));
848 a4.sin_family = AF_INET;
849 a4.sin_port = htons(ASL_REMOTE_PORT);
850
851 memset(&(a6.sin6_addr), 0, sizeof(struct in6_addr));
852 a6.sin6_family = AF_INET6;
853 a6.sin6_port = htons(ASL_REMOTE_PORT);
854
855 s = (struct sockaddr *)&a4;
856 len = sizeof(struct sockaddr_in);
857
858 if (family == AF_INET6)
859 {
860 s = (struct sockaddr *)&a6;
861 len = sizeof(struct sockaddr_in6);
862 }
863
864 status = bind(fd, s, len);
865 if (status < 0)
866 {
867 asldebug("%s: bind: %s\n", MY_ID, strerror(errno));
868 close(fd);
869 return -1;
870 }
871
872 status = fcntl(fd, F_SETFL, O_NONBLOCK);
873 if (status < 0)
874 {
875 asldebug("%s: fcntl: %s\n", MY_ID, strerror(errno));
876 close(fd);
877 return -1;
878 }
879
880 status = listen(fd, 5);
881 if (status < 0)
882 {
883 asldebug("%s: listen: %s\n", MY_ID, strerror(errno));
884 close(fd);
885 return -1;
886 }
887
db78b1bd
A
888 in_src_tcp = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)fd, 0, in_queue);
889 dispatch_source_set_event_handler(in_src_tcp, ^{ remote_acceptmsg_tcp(fd); });
890 dispatch_resume(in_src_tcp);
891
57b0aad2
A
892 return fd;
893}
894
895int
896remote_init(void)
897{
db78b1bd
A
898 static dispatch_once_t once;
899
900 dispatch_once(&once, ^{
901 in_queue = dispatch_queue_create(MY_ID, NULL);
902 });
903
57b0aad2
A
904 asldebug("%s: init\n", MY_ID);
905
906#ifdef LOCKDOWN
907 rfdl = remote_init_lockdown();
908#endif
909
910#ifdef REMOTE_IPV4
911 rfd4 = remote_init_tcp(AF_INET);
912#endif
913
914#ifdef REMOTE_IPV6
915 rfd6 = remote_init_tcp(AF_INET6);
916#endif
917
918 return 0;
919}
920
921int
922remote_close(void)
923{
a83ff38a
A
924 if (rfdl >= 0)
925 {
a83ff38a
A
926 close(rfdl);
927 }
928
57b0aad2
A
929 rfdl = -1;
930
a83ff38a
A
931 if (rfd4 >= 0)
932 {
a83ff38a
A
933 close(rfd4);
934 }
935
57b0aad2
A
936 rfd4 = -1;
937
a83ff38a
A
938 if (rfd6 >= 0)
939 {
a83ff38a
A
940 close(rfd6);
941 }
942
57b0aad2
A
943 rfd6 = -1;
944
945 return 0;
946}
947
948int
949remote_reset(void)
950{
db78b1bd
A
951 return 0;
952
57b0aad2
A
953 remote_close();
954 return remote_init();
955}