]>
Commit | Line | Data |
---|---|---|
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 | ||
62 | static int rfd4 = -1; | |
63 | static int rfd6 = -1; | |
64 | static int rfdl = -1; | |
65 | ||
db78b1bd A |
66 | static dispatch_source_t in_src_local; |
67 | static dispatch_source_t in_src_tcp; | |
68 | static dispatch_queue_t in_queue; | |
69 | ||
57b0aad2 A |
70 | #ifdef NSS32 |
71 | typedef uint32_t notify_state_t; | |
72 | extern int notify_set_state(int, notify_state_t); | |
73 | #else | |
74 | typedef uint64_t notify_state_t; | |
75 | #endif | |
76 | ||
77 | extern char *asl_list_to_string(asl_search_result_t *list, uint32_t *outlen); | |
78 | extern size_t asl_memory_size(asl_memory_t *s); | |
79 | 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); | |
80 | ||
81 | #define SESSION_WRITE(f,x) if (write(f, x, strlen(x)) < 0) goto exit_session | |
82 | ||
a83ff38a A |
83 | typedef struct |
84 | { | |
85 | int sock; | |
86 | uint32_t flags; | |
87 | } session_args_t; | |
88 | ||
57b0aad2 A |
89 | uint32_t |
90 | remote_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 | ||
98 | uint32_t | |
99 | remote_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 | 107 | aslmsg |
57b0aad2 A |
108 | remote_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 | ||
119 | void | |
120 | session(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 | ||
666 | exit_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 | 683 | aslmsg |
57b0aad2 A |
684 | remote_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 | 746 | aslmsg |
57b0aad2 A |
747 | remote_acceptmsg_local(int fd) |
748 | { | |
749 | return remote_acceptmsg(fd, 0); | |
750 | } | |
751 | ||
a83ff38a | 752 | aslmsg |
57b0aad2 A |
753 | remote_acceptmsg_tcp(int fd) |
754 | { | |
755 | return remote_acceptmsg(fd, 1); | |
756 | } | |
757 | ||
758 | int | |
759 | remote_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 | ||
822 | int | |
823 | remote_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 | ||
895 | int | |
896 | remote_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 | ||
921 | int | |
922 | remote_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 | ||
948 | int | |
949 | remote_reset(void) | |
950 | { | |
db78b1bd A |
951 | return 0; |
952 | ||
57b0aad2 A |
953 | remote_close(); |
954 | return remote_init(); | |
955 | } |