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