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