]>
Commit | Line | Data |
---|---|---|
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 |
27 | struct _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 | ||
68 | static int rfd4 = -1; | |
69 | static int rfd6 = -1; | |
70 | static int rfdl = -1; | |
71 | ||
db78b1bd A |
72 | static dispatch_source_t in_src_local; |
73 | static dispatch_source_t in_src_tcp; | |
74 | static dispatch_queue_t in_queue; | |
75 | ||
57b0aad2 A |
76 | #ifdef NSS32 |
77 | typedef uint32_t notify_state_t; | |
78 | extern int notify_set_state(int, notify_state_t); | |
79 | #else | |
80 | typedef uint64_t notify_state_t; | |
81 | #endif | |
82 | ||
57b0aad2 | 83 | extern size_t asl_memory_size(asl_memory_t *s); |
f3df4c03 | 84 | extern 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 | |
86 | extern void add_lockdown_session(int fd); | |
87 | extern 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 |
91 | typedef struct |
92 | { | |
93 | int sock; | |
94 | uint32_t flags; | |
95 | } session_args_t; | |
96 | ||
57b0aad2 A |
97 | uint32_t |
98 | remote_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 | ||
105 | uint32_t | |
106 | remote_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 | 113 | asl_msg_t * |
57b0aad2 A |
114 | remote_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 | ||
124 | void | |
125 | session(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 | ||
648 | exit_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 | 664 | asl_msg_t * |
57b0aad2 A |
665 | remote_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 | 724 | asl_msg_t * |
57b0aad2 A |
725 | remote_acceptmsg_local(int fd) |
726 | { | |
727 | return remote_acceptmsg(fd, 0); | |
728 | } | |
729 | ||
f3df4c03 | 730 | asl_msg_t * |
57b0aad2 A |
731 | remote_acceptmsg_tcp(int fd) |
732 | { | |
733 | return remote_acceptmsg(fd, 1); | |
734 | } | |
735 | ||
736 | int | |
737 | remote_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 | ||
800 | int | |
801 | remote_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 | ||
873 | int | |
874 | remote_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 | ||
899 | int | |
900 | remote_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 | ||
926 | int | |
927 | remote_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 */ |