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