1/* 2 * Copyright (c) 2003-2012 Apple Inc. All rights reserved. 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 <signal.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <errno.h> 30#include <sys/un.h> 31#include <sys/ipc.h> 32#include <sys/mman.h> 33#include <sys/fcntl.h> 34#include <sys/syslimits.h> 35#include <sys/param.h> 36#include <sys/resource.h> 37#include <asl.h> 38#include <assert.h> 39#include <inttypes.h> 40#include <TargetConditionals.h> 41#include "pathwatch.h" 42#include "notifyd.h" 43#include "service.h" 44#include "pathwatch.h" 45#include "timer.h" 46 47#include "notify_ipc.h" 48#include "notify_private.h" 49 50#define forever for(;;) 51#define IndexNull -1 52 53/* Compile flags */ 54#define RUN_TIME_CHECKS 55 56#define CONFIG_FILE_PATH "/etc/notify.conf" 57#define DEBUG_LOG_PATH "/var/log/notifyd.log" 58 59#define STATUS_REQUEST_SHORT 0 60#define STATUS_REQUEST_LONG 1 61 62#define N_NOTIFY_TYPES 6 63 64static int notifyd_token; 65 66static char *status_file = NULL; 67 68typedef union 69{ 70 mach_msg_header_t head; 71 union __RequestUnion__notify_ipc_subsystem request; 72} notify_request_msg; 73 74typedef union 75{ 76 mach_msg_header_t head; 77 union __ReplyUnion__notify_ipc_subsystem reply; 78} notify_reply_msg; 79 80extern boolean_t notify_ipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); 81 82static const char * 83notify_type_name(uint32_t t) 84{ 85 switch (t) 86 { 87 case NOTIFY_TYPE_NONE: return "none "; 88 case NOTIFY_TYPE_MEMORY: return "memory"; 89 case NOTIFY_TYPE_PLAIN: return "plain "; 90 case NOTIFY_TYPE_PORT: return "port "; 91 case NOTIFY_TYPE_FILE: return "file "; 92 case NOTIFY_TYPE_SIGNAL: return "signal"; 93 default: return "unknown"; 94 } 95 96 return "unknown"; 97} 98 99static void 100fprint_client(FILE *f, client_t *c) 101{ 102 int token; 103 104 if (c == NULL) 105 { 106 fprintf(f, "NULL client\n"); 107 return; 108 } 109 110 token = c->client_id; 111 112 fprintf(f, "client_id: %llu\n", c->client_id); 113 fprintf(f, "pid: %d\n", c->pid); 114 fprintf(f, "token: %d\n", token); 115 fprintf(f, "lastval: %u\n", c->lastval); 116 fprintf(f, "suspend_count: %u\n", c->suspend_count); 117 fprintf(f, "type: %s\n", notify_type_name(c->notify_type)); 118 switch(c->notify_type) 119 { 120 case NOTIFY_TYPE_NONE: 121 break; 122 123 case NOTIFY_TYPE_PLAIN: 124 break; 125 126 case NOTIFY_TYPE_MEMORY: 127 break; 128 129 case NOTIFY_TYPE_PORT: 130 fprintf(f, "mach port: 0x%08x\n", c->port); 131 break; 132 133 case NOTIFY_TYPE_FILE: 134 fprintf(f, "fd: %d\n", c->fd); 135 break; 136 137 case NOTIFY_TYPE_SIGNAL: 138 fprintf(f, "signal: %d\n", c->sig); 139 break; 140 141 default: break; 142 } 143} 144 145static void 146fprint_quick_client(FILE *f, client_t *c, int pname) 147{ 148 int token; 149 if (c == NULL) return; 150 151 token = c->client_id; 152 153 if (pname == 1) fprintf(f, " [%s]", c->name_info->name); 154 fprintf(f, " %u %u", c->pid, token); 155 if (c->suspend_count > 0) fprintf(f, " suspend %d", c->suspend_count); 156 if (c->state & NOTIFY_CLIENT_STATE_PENDING) fprintf(f, " pending"); 157 if (c->state & NOTIFY_CLIENT_STATE_TIMEOUT) fprintf(f, " timeout"); 158 fprintf(f, " %s", notify_type_name(c->notify_type)); 159 if (c->notify_type == NOTIFY_TYPE_SIGNAL) fprintf(f, " %d", c->sig); 160 fprintf(f, "\n"); 161} 162 163static void 164fprint_quick_name_info(FILE *f, name_info_t *n) 165{ 166 list_t *sl; 167 client_t *c; 168 169 if (n == NULL) return; 170 171 fprintf(f, "\"%s\" uid=%u gid=%u %03x", n->name, n->uid, n->gid, n->access); 172 if (n->slot != -1) 173 { 174 fprintf(f, " slot %u", n->slot); 175 if (global.shared_memory_refcount[n->slot] != -1) fprintf(f, " = %u", global.shared_memory_base[n->slot]); 176 } 177 178 fprintf(f, "\n"); 179 180 for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) 181 { 182 c = _nc_list_data(sl); 183 if (c == NULL) break; 184 185 fprint_quick_client(f, c, 0); 186 } 187 188 fprintf(f, "\n"); 189} 190 191static void 192fprint_name_info(FILE *f, const char *name, name_info_t *n, table_t *pid_table, pid_t *max_pid) 193{ 194 list_t *sl; 195 client_t *c; 196 uint32_t i, reg[N_NOTIFY_TYPES]; 197 198 if (n == NULL) 199 { 200 fprintf(f, "%s unknown\n", name); 201 return; 202 } 203 204 fprintf(f, "name: %s\n", n->name); 205 fprintf(f, "id: %llu\n", n->name_id); 206 fprintf(f, "uid: %u\n", n->uid); 207 fprintf(f, "gid: %u\n", n->gid); 208 fprintf(f, "access: %03x\n", n->access); 209 fprintf(f, "refcount: %u\n", n->refcount); 210 if (n->slot == -1) fprintf(f, "slot: -unassigned-"); 211 else 212 { 213 fprintf(f, "slot: %u", n->slot); 214 if (global.shared_memory_refcount[n->slot] != -1) 215 fprintf(f, " = %u (%u)", global.shared_memory_base[n->slot], global.shared_memory_refcount[n->slot]); 216 } 217 fprintf(f, "\n"); 218 fprintf(f, "val: %u\n", n->val); 219 fprintf(f, "state: %llu\n", n->state); 220 221 for (i = 0; i < N_NOTIFY_TYPES; i++) reg[i] = 0; 222 223 for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) 224 { 225 list_t *l; 226 227 c = _nc_list_data(sl); 228 if (c == NULL) break; 229 230 if ((c->pid != (pid_t)-1) && (c->pid > *max_pid)) *max_pid = c->pid; 231 232 l = _nc_table_find_n(pid_table, c->pid); 233 if (l == NULL) 234 { 235 _nc_table_insert_n(pid_table, (uint32_t)c->pid, _nc_list_new(c)); 236 } 237 else 238 { 239 _nc_list_concat(l, _nc_list_new(c)); 240 } 241 242 switch (c->notify_type) 243 { 244 case NOTIFY_TYPE_MEMORY: reg[1]++; break; 245 case NOTIFY_TYPE_PLAIN: reg[2]++; break; 246 case NOTIFY_TYPE_PORT: reg[3]++; break; 247 case NOTIFY_TYPE_FILE: reg[4]++; break; 248 case NOTIFY_TYPE_SIGNAL: reg[5]++; break; 249 default: reg[0]++; 250 } 251 } 252 253 fprintf(f, "types: none %u memory %u plain %u port %u file %u signal %u\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5]); 254 255 for (sl = n->subscriptions; sl != NULL; sl = _nc_list_next(sl)) 256 { 257 c = _nc_list_data(sl); 258 if (c == NULL) break; 259 260 fprintf(f, "\n"); 261 fprint_client(f, c); 262 } 263} 264 265static void 266fprint_quick_status(FILE *f) 267{ 268 void *tt; 269 name_info_t *n; 270 271 tt = _nc_table_traverse_start(global.notify_state->name_table); 272 273 while (tt != NULL) 274 { 275 n = _nc_table_traverse(global.notify_state->name_table, tt); 276 if (n == NULL) break; 277 fprint_quick_name_info(f, n); 278 } 279 280 _nc_table_traverse_end(global.notify_state->name_table, tt); 281 fprintf(f, "\n"); 282} 283 284static void 285fprint_status(FILE *f) 286{ 287 void *tt; 288 name_info_t *n; 289 int32_t i; 290 client_t *c; 291 svc_info_t *info; 292 path_node_t *node; 293 timer_t *timer; 294 table_t *pid_table; 295 pid_t pid, max_pid; 296 uint32_t count; 297 portproc_data_t *pdata; 298 299 pid_table = _nc_table_new(0); 300 max_pid = 0; 301 302 fprintf(f, "--- GLOBALS ---\n"); 303 fprintf(f, "%u slots (current id %u)\n", global.nslots, global.slot_id); 304 fprintf(f, "%u log_cutoff (default %u)\n", global.log_cutoff, global.log_default); 305 fprintf(f, "\n"); 306 307 fprintf(f, "--- STATISTICS ---\n"); 308 fprintf(f, "post %llu\n", call_statistics.post); 309 fprintf(f, " id %llu\n", call_statistics.post_by_id); 310 fprintf(f, " name %llu\n", call_statistics.post_by_name); 311 fprintf(f, " fetch %llu\n", call_statistics.post_by_name_and_fetch_id); 312 fprintf(f, " no_op %llu\n", call_statistics.post_no_op); 313 fprintf(f, "\n"); 314 fprintf(f, "register %llu\n", call_statistics.reg); 315 fprintf(f, " plain %llu\n", call_statistics.reg_plain); 316 fprintf(f, " check %llu\n", call_statistics.reg_check); 317 fprintf(f, " signal %llu\n", call_statistics.reg_signal); 318 fprintf(f, " file %llu\n", call_statistics.reg_file); 319 fprintf(f, " port %llu\n", call_statistics.reg_port); 320 fprintf(f, "\n"); 321 fprintf(f, "check %llu\n", call_statistics.check); 322 fprintf(f, "cancel %llu\n", call_statistics.cancel); 323 fprintf(f, "cleanup %llu\n", call_statistics.cleanup); 324 fprintf(f, "regenerate %llu\n", call_statistics.regenerate); 325 fprintf(f, "\n"); 326 fprintf(f, "suspend %llu\n", call_statistics.suspend); 327 fprintf(f, "resume %llu\n", call_statistics.resume); 328 fprintf(f, "suspend_pid %llu\n", call_statistics.suspend_pid); 329 fprintf(f, "resume_pid %llu\n", call_statistics.resume_pid); 330 fprintf(f, "\n"); 331 fprintf(f, "get_state %llu\n", call_statistics.get_state); 332 fprintf(f, " id %llu\n", call_statistics.get_state_by_id); 333 fprintf(f, " client %llu\n", call_statistics.get_state_by_client); 334 fprintf(f, " fetch %llu\n", call_statistics.get_state_by_client_and_fetch_id); 335 fprintf(f, "\n"); 336 fprintf(f, "set_state %llu\n", call_statistics.set_state); 337 fprintf(f, " id %llu\n", call_statistics.set_state_by_id); 338 fprintf(f, " client %llu\n", call_statistics.set_state_by_client); 339 fprintf(f, " fetch %llu\n", call_statistics.set_state_by_client_and_fetch_id); 340 fprintf(f, "\n"); 341 fprintf(f, "get_owner %llu\n", call_statistics.get_owner); 342 fprintf(f, "set_owner %llu\n", call_statistics.set_owner); 343 fprintf(f, "\n"); 344 fprintf(f, "get_access %llu\n", call_statistics.get_access); 345 fprintf(f, "set_access %llu\n", call_statistics.set_access); 346 fprintf(f, "\n"); 347 fprintf(f, "monitor %llu\n", call_statistics.monitor_file); 348 fprintf(f, "svc_path %llu\n", call_statistics.service_path); 349 fprintf(f, "svc_timer %llu\n", call_statistics.service_timer); 350 351 fprintf(f, "\n"); 352 fprintf(f, "name alloc %9u free %9u extant %9u\n", global.notify_state->stat_name_alloc , global.notify_state->stat_name_free, global.notify_state->stat_name_alloc - global.notify_state->stat_name_free); 353 fprintf(f, "subscription alloc %9u free %9u extant %9u\n", global.notify_state->stat_client_alloc , global.notify_state->stat_client_free, global.notify_state->stat_client_alloc - global.notify_state->stat_client_free); 354 fprintf(f, "portproc alloc %9u free %9u extant %9u\n", global.notify_state->stat_portproc_alloc , global.notify_state->stat_portproc_free, global.notify_state->stat_portproc_alloc - global.notify_state->stat_portproc_free); 355 fprintf(f, "\n"); 356 357 count = 0; 358 tt = _nc_table_traverse_start(global.notify_state->port_table); 359 while (tt != NULL) 360 { 361 pdata = _nc_table_traverse(global.notify_state->port_table, tt); 362 if (pdata == NULL) break; 363 count++; 364 } 365 _nc_table_traverse_end(global.notify_state->port_table, tt); 366 fprintf(f, "port count %u\n", count); 367 368 count = 0; 369 tt = _nc_table_traverse_start(global.notify_state->proc_table); 370 while (tt != NULL) 371 { 372 pdata = _nc_table_traverse(global.notify_state->proc_table, tt); 373 if (pdata == NULL) break; 374 count++; 375 } 376 _nc_table_traverse_end(global.notify_state->proc_table, tt); 377 fprintf(f, "proc count %u\n", count); 378 fprintf(f, "\n"); 379 380 fprintf(f, "--- NAME TABLE ---\n"); 381 count = 0; 382 tt = _nc_table_traverse_start(global.notify_state->name_table); 383 384 while (tt != NULL) 385 { 386 n = _nc_table_traverse(global.notify_state->name_table, tt); 387 if (n == NULL) break; 388 fprint_name_info(f, n->name, n, pid_table, &max_pid); 389 fprintf(f, "\n"); 390 count++; 391 } 392 393 fprintf(f, "--- NAME COUNT %u ---\n", count); 394 _nc_table_traverse_end(global.notify_state->name_table, tt); 395 fprintf(f, "\n"); 396 397 fprintf(f, "--- SUBSCRIPTION TABLE ---\n"); 398 count = 0; 399 tt = _nc_table_traverse_start(global.notify_state->client_table); 400 401 while (tt != NULL) 402 { 403 c = _nc_table_traverse(global.notify_state->client_table, tt); 404 if (c == NULL) break; 405 fprint_quick_client(f, c, 1); 406 count++; 407 } 408 409 fprintf(f, "--- SUBSCRIPTION COUNT %u ---\n", count); 410 _nc_table_traverse_end(global.notify_state->client_table, tt); 411 fprintf(f, "\n"); 412 413 fprintf(f, "--- CONTROLLED NAME ---\n"); 414 for (i = 0; i < global.notify_state->controlled_name_count; i++) 415 { 416 fprintf(f, "%s %u %u %03x\n", global.notify_state->controlled_name[i]->name, global.notify_state->controlled_name[i]->uid, global.notify_state->controlled_name[i]->gid, global.notify_state->controlled_name[i]->access); 417 } 418 fprintf(f, "--- CONTROLLED NAME COUNT %u ---\n", global.notify_state->controlled_name_count); 419 fprintf(f, "\n"); 420 421 fprintf(f, "--- PUBLIC SERVICE ---\n"); 422 count = 0; 423 tt = _nc_table_traverse_start(global.notify_state->name_table); 424 while (tt != NULL) 425 { 426 n = _nc_table_traverse(global.notify_state->name_table, tt); 427 if (n == NULL) break; 428 if (n->private == NULL) continue; 429 430 count++; 431 info = (svc_info_t *)n->private; 432 433 if (info->type == 0) 434 { 435 fprintf(f, "Null service: %s\n", n->name); 436 } 437 if (info->type == SERVICE_TYPE_PATH_PUBLIC) 438 { 439 node = (path_node_t *)info->private; 440 fprintf(f, "Path Service: %s <- %s\n", n->name, node->path); 441 } 442 else if (info->type == SERVICE_TYPE_TIMER_PUBLIC) 443 { 444 timer = (timer_t *)info->private; 445 switch (timer->type) 446 { 447 case TIME_EVENT_ONESHOT: 448 { 449 fprintf(f, "Time Service: %s <- Oneshot %llu\n", n->name, timer->start); 450 break; 451 } 452 case TIME_EVENT_CLOCK: 453 { 454 fprintf(f, "Time Service: %s <- Clock start %lld freq %u end %lld\n", n->name, timer->start, timer->freq, timer->end); 455 break; 456 } 457 case TIME_EVENT_CAL: 458 { 459 fprintf(f, "Time Service: %s <- Calendar start %lld freq %u end %lld day %d\n", n->name, timer->start, timer->freq, timer->end, timer->day); 460 break; 461 } 462 } 463 } 464 else 465 { 466 fprintf(f, "Unknown service: %s (%u)\n", n->name, info->type); 467 } 468 } 469 470 fprintf(f, "--- PUBLIC SERVICE COUNT %u ---\n", count); 471 _nc_table_traverse_end(global.notify_state->name_table, tt); 472 fprintf(f, "\n"); 473 474 fprintf(f, "--- PRIVATE SERVICE ---\n"); 475 count = 0; 476 tt = _nc_table_traverse_start(global.notify_state->client_table); 477 while (tt != NULL) 478 { 479 c = _nc_table_traverse(global.notify_state->client_table, tt); 480 if (c == NULL) break; 481 if (c->private == NULL) continue; 482 483 count++; 484 info = (svc_info_t *)c->private; 485 n = c->name_info; 486 487 if (info->type == 0) 488 { 489 fprintf(f, "PID %u Null service: %s\n", c->pid, n->name); 490 } 491 if (info->type == SERVICE_TYPE_PATH_PRIVATE) 492 { 493 node = (path_node_t *)info->private; 494 fprintf(f, "PID %u Path Service: %s <- %s (UID %d GID %d)\n", c->pid, n->name, node->path, node->uid, node->gid); 495 } 496 else if (info->type == SERVICE_TYPE_TIMER_PRIVATE) 497 { 498 timer = (timer_t *)info->private; 499 switch (timer->type) 500 { 501 case TIME_EVENT_ONESHOT: 502 { 503 fprintf(f, "PID %u Time Service: %s <- Oneshot %"PRId64"\n", c->pid, n->name, timer->start); 504 break; 505 } 506 case TIME_EVENT_CLOCK: 507 { 508 fprintf(f, "PID %u Time Service: %s <- Clock start %"PRId64" freq %"PRIu32" end %"PRId64"\n", c->pid, n->name, timer->start, timer->freq, timer->end); 509 break; 510 } 511 case TIME_EVENT_CAL: 512 { 513 fprintf(f, "PID %u Time Service: %s <- Calendar start %"PRId64" freq %"PRIu32" end %"PRId64" day %"PRId32"\n", c->pid, n->name, timer->start, timer->freq, timer->end, timer->day); 514 break; 515 } 516 } 517 } 518 } 519 520 fprintf(f, "--- PRIVATE SERVICE COUNT %u ---\n", count); 521 _nc_table_traverse_end(global.notify_state->client_table, tt); 522 fprintf(f, "\n"); 523 524 fprintf(f, "--- PROCESSES ---\n"); 525 for (pid = 0; pid <= max_pid; pid++) 526 { 527 int mem_count, plain_count, file_count, port_count, sig_count, com_port_count; 528 mach_port_t common_port = MACH_PORT_NULL; 529 530 list_t *x; 531 list_t *l = _nc_table_find_n(pid_table, pid); 532 if (l == NULL) continue; 533 534 mem_count = 0; 535 plain_count = 0; 536 file_count = 0; 537 port_count = 0; 538 sig_count = 0; 539 com_port_count = 0; 540 541 for (x = l; x != NULL; x = _nc_list_next(x)) 542 { 543 c = _nc_list_data(x); 544 if (c != NULL) 545 { 546 if ((c->notify_type == NOTIFY_TYPE_PORT) && (!strcmp(c->name_info->name, COMMON_PORT_KEY))) common_port = c->port; 547 } 548 } 549 550 for (x = l; x != NULL; x = _nc_list_next(x)) 551 { 552 c = _nc_list_data(x); 553 if (c != NULL) 554 { 555 switch(c->notify_type) 556 { 557 case NOTIFY_TYPE_NONE: 558 break; 559 560 case NOTIFY_TYPE_PLAIN: 561 plain_count++; 562 break; 563 564 case NOTIFY_TYPE_MEMORY: 565 mem_count++; 566 break; 567 568 case NOTIFY_TYPE_PORT: 569 port_count++; 570 if (c->port == common_port) com_port_count++; 571 break; 572 573 case NOTIFY_TYPE_FILE: 574 file_count++; 575 break; 576 577 case NOTIFY_TYPE_SIGNAL: 578 sig_count++; 579 break; 580 581 default: break; 582 } 583 } 584 } 585 586 fprintf(f, "pid: %u ", pid); 587 if (file_count + sig_count == 0) 588 { 589 if (port_count == 0) fprintf(f, "regenerable / polling\n"); 590 else if (port_count == com_port_count) fprintf(f, "regenerable\n"); 591 else if (com_port_count > 0) fprintf(f, "partially regenerable\n"); 592 else fprintf(f, "non-regenerable\n"); 593 } 594 else 595 { 596 if (com_port_count == 0) fprintf(f, "non-regenerable\n"); 597 else fprintf(f, "partially regenerable\n"); 598 } 599 600 fprintf(f, "memory %u plain %u port %u file %u signal %u common port %u\n", mem_count, plain_count, port_count, file_count, sig_count, com_port_count); 601 for (x = l; x != NULL; x = _nc_list_next(x)) 602 { 603 c = _nc_list_data(x); 604 if (c != NULL) 605 { 606 fprintf(f, " %s: %s\n", notify_type_name(c->notify_type), c->name_info->name); 607 } 608 } 609 610 fprintf(f, "\n"); 611 _nc_list_release_list(l); 612 } 613 fprintf(f, "\n"); 614 _nc_table_free(pid_table); 615} 616 617void 618dump_status(uint32_t level) 619{ 620 FILE *f; 621 622 if (status_file == NULL) 623 { 624 asprintf(&status_file, "/var/run/notifyd_%u.status", getpid()); 625 if (status_file == NULL) return; 626 } 627 628 unlink(status_file); 629 f = fopen(status_file, "w"); 630 if (f == NULL) return; 631 632 if (level == STATUS_REQUEST_SHORT) fprint_quick_status(f); 633 else if (level == STATUS_REQUEST_LONG) fprint_status(f); 634 635 fclose(f); 636} 637 638void 639log_message(int priority, const char *str, ...) 640{ 641 time_t t; 642 char now[32]; 643 va_list ap; 644 645 if (priority > global.log_cutoff) return; 646 647 va_start(ap, str); 648 649 if (global.log_file != NULL) 650 { 651 t = time(NULL); 652 memset(now, 0, 32); 653 strftime(now, 32, "%b %e %T", localtime(&t)); 654 655 fprintf(global.log_file, "%s: ", now); 656 vfprintf(global.log_file, str, ap); 657 fflush(global.log_file); 658 } 659 else 660 { 661 vfprintf(stderr, str, ap); 662 } 663 664 va_end(ap); 665} 666 667uint32_t 668daemon_post(const char *name, uint32_t u, uint32_t g) 669{ 670 name_info_t *n; 671 uint32_t status; 672 673 if (name == NULL) return 0; 674 675 n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); 676 if (n == NULL) return 0; 677 678 if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++; 679 680 status = _notify_lib_post(global.notify_state, name, u, g); 681 return status; 682} 683 684uint32_t 685daemon_post_nid(uint64_t nid, uint32_t u, uint32_t g) 686{ 687 name_info_t *n; 688 uint32_t status; 689 690 n = (name_info_t *)_nc_table_find_64(global.notify_state->name_id_table, nid); 691 if (n == NULL) return 0; 692 693 if (n->slot != (uint32_t)-1) global.shared_memory_base[n->slot]++; 694 695 status = _notify_lib_post_nid(global.notify_state, nid, u, g); 696 return status; 697} 698 699void 700daemon_post_client(uint64_t cid) 701{ 702 client_t *c; 703 704 c = _nc_table_find_64(global.notify_state->client_table, cid); 705 if (c == NULL) return; 706 707 if ((c->notify_type == NOTIFY_TYPE_MEMORY) && (c->name_info != NULL) && (c->name_info->slot != (uint32_t)-1)) 708 { 709 global.shared_memory_base[c->name_info->slot]++; 710 } 711 712 _notify_lib_post_client(global.notify_state, c); 713} 714 715void 716daemon_set_state(const char *name, uint64_t val) 717{ 718 name_info_t *n; 719 720 if (name == NULL) return; 721 722 n = (name_info_t *)_nc_table_find(global.notify_state->name_table, name); 723 if (n == NULL) return; 724 725 n->state = val; 726} 727 728static void 729init_launch_config(const char *name) 730{ 731 launch_data_t tmp, pdict; 732 733 tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN); 734 global.launch_dict = launch_msg(tmp); 735 launch_data_free(tmp); 736 737 if (global.launch_dict == NULL) 738 { 739 fprintf(stderr, "%d launchd checkin failed\n", getpid()); 740 exit(1); 741 } 742 743 tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES); 744 if (tmp == NULL) 745 { 746 fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid()); 747 exit(1); 748 } 749 750 pdict = launch_data_dict_lookup(tmp, name); 751 if (pdict == NULL) 752 { 753 fprintf(stderr, "%d launchd lookup of name %s failed\n", getpid(), name); 754 exit(1); 755 } 756 757 global.server_port = launch_data_get_machport(pdict); 758 if (global.server_port == MACH_PORT_NULL) 759 { 760 fprintf(stderr, "%d launchd lookup of server port for name %s failed\n", getpid(), name); 761 exit(1); 762 } 763} 764 765static void 766string_list_free(char **l) 767{ 768 int i; 769 770 if (l == NULL) return; 771 for (i = 0; l[i] != NULL; i++) 772 { 773 if (l[i] != NULL) free(l[i]); 774 l[i] = NULL; 775 } 776 free(l); 777} 778 779static char ** 780string_insert(char *s, char **l, unsigned int x) 781{ 782 int i, len; 783 784 if (s == NULL) return l; 785 if (l == NULL) 786 { 787 l = (char **)malloc(2 * sizeof(char *)); 788 l[0] = strdup(s); 789 l[1] = NULL; 790 return l; 791 } 792 793 for (i = 0; l[i] != NULL; i++); 794 len = i + 1; /* count the NULL on the end of the list too! */ 795 796 l = (char **)realloc(l, (len + 1) * sizeof(char *)); 797 798 if ((x >= (len - 1)) || (x == IndexNull)) 799 { 800 l[len - 1] = strdup(s); 801 l[len] = NULL; 802 return l; 803 } 804 805 for (i = len; i > x; i--) l[i] = l[i - 1]; 806 l[x] = strdup(s); 807 return l; 808} 809 810static char ** 811string_append(char *s, char **l) 812{ 813 return string_insert(s, l, IndexNull); 814} 815 816static unsigned int 817string_list_length(char **l) 818{ 819 int i; 820 821 if (l == NULL) return 0; 822 for (i = 0; l[i] != NULL; i++); 823 return i; 824} 825 826static unsigned int 827string_index(char c, char *s) 828{ 829 int i; 830 char *p; 831 832 if (s == NULL) return IndexNull; 833 834 for (i = 0, p = s; p[0] != '\0'; p++, i++) 835 { 836 if (p[0] == c) return i; 837 } 838 839 return IndexNull; 840} 841 842static char ** 843explode(char *s, char *delim) 844{ 845 char **l = NULL; 846 char *p, *t; 847 int i, n; 848 849 if (s == NULL) return NULL; 850 851 p = s; 852 while (p[0] != '\0') 853 { 854 for (i = 0; ((p[i] != '\0') && (string_index(p[i], delim) == IndexNull)); i++); 855 n = i; 856 t = malloc(n + 1); 857 for (i = 0; i < n; i++) t[i] = p[i]; 858 t[n] = '\0'; 859 l = string_append(t, l); 860 free(t); 861 t = NULL; 862 if (p[i] == '\0') return l; 863 if (p[i + 1] == '\0') l = string_append("", l); 864 p = p + i + 1; 865 } 866 return l; 867} 868 869static uint32_t 870atoaccess(char *s) 871{ 872 uint32_t a; 873 874 if (s == NULL) return 0; 875 if (strlen(s) != 6) return 0; 876 877 a = 0; 878 if (s[0] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_USER_SHIFT); 879 if (s[1] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_USER_SHIFT); 880 881 if (s[2] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_GROUP_SHIFT); 882 if (s[3] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_GROUP_SHIFT); 883 884 if (s[4] == 'r') a |= (NOTIFY_ACCESS_READ << NOTIFY_ACCESS_OTHER_SHIFT); 885 if (s[5] == 'w') a |= (NOTIFY_ACCESS_WRITE << NOTIFY_ACCESS_OTHER_SHIFT); 886 887 return a; 888} 889 890static void 891init_config() 892{ 893 FILE *f; 894 struct stat sb; 895 char line[1024]; 896 char **args; 897 uint32_t status, argslen; 898 uint32_t uid, gid, access; 899 uint64_t nid, val64; 900 901 /* 902 * Set IPC Version Number & PID 903 */ 904 val64 = getpid(); 905 val64 <<= 32; 906 val64 |= NOTIFY_IPC_VERSION; 907 908 _notify_lib_register_plain(global.notify_state, NOTIFY_IPC_VERSION_NAME, -1, notifyd_token++, -1, 0, 0, &nid); 909 _notify_lib_set_state(global.notify_state, nid, val64, 0, 0); 910 911 /* Check config file */ 912 if (stat(CONFIG_FILE_PATH, &sb) != 0) return; 913 914 if (sb.st_uid != 0) 915 { 916 log_message(ASL_LEVEL_ERR, "config file %s not owned by root: ignored\n", CONFIG_FILE_PATH); 917 return; 918 } 919 920 if (sb.st_mode & 02) 921 { 922 log_message(ASL_LEVEL_ERR, "config file %s is world-writable: ignored\n", CONFIG_FILE_PATH); 923 return; 924 } 925 926 /* Read config file */ 927 f = fopen(CONFIG_FILE_PATH, "r"); 928 if (f == NULL) return; 929 930 forever 931 { 932 if (fgets(line, 1024, f) == NULL) break; 933 if (line[0] == '\0') continue; 934 if (line[0] == '#') continue; 935 936 line[strlen(line) - 1] = '\0'; 937 args = explode(line, "\t "); 938 argslen = string_list_length(args); 939 if (argslen == 0) continue; 940 941 if (!strcasecmp(args[0], "monitor")) 942 { 943 if (argslen < 3) 944 { 945 string_list_free(args); 946 continue; 947 } 948 _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); 949 service_open_path(args[1], args[2], 0, 0); 950 } 951 952 if (!strcasecmp(args[0], "timer")) 953 { 954 if (argslen < 3) 955 { 956 string_list_free(args); 957 continue; 958 } 959 _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); 960 status = service_open_timer(args[1], args[2]); 961 } 962 963 else if (!strcasecmp(args[0], "set")) 964 { 965 if (argslen < 3) 966 { 967 string_list_free(args); 968 continue; 969 } 970 971 val64 = atoll(args[2]); 972 973 _notify_lib_register_plain(global.notify_state, args[1], -1, notifyd_token++, -1, 0, 0, &nid); 974 _notify_lib_set_state(global.notify_state, nid, val64, 0, 0); 975 } 976 977 else if (!strcasecmp(args[0], "reserve")) 978 { 979 if (argslen == 1) 980 { 981 string_list_free(args); 982 continue; 983 } 984 985 uid = 0; 986 gid = 0; 987 access = NOTIFY_ACCESS_DEFAULT; 988 989 if (argslen > 2) uid = atoi(args[2]); 990 if (argslen > 3) gid = atoi(args[3]); 991 if (argslen > 4) access = atoaccess(args[4]); 992 993 if ((uid != 0) || (gid != 0)) _notify_lib_set_owner(global.notify_state, args[1], uid, gid); 994 if (access != NOTIFY_ACCESS_DEFAULT) _notify_lib_set_access(global.notify_state, args[1], access); 995 } 996 else if (!strcasecmp(args[0], "quit")) 997 { 998 string_list_free(args); 999 break; 1000 } 1001 1002 string_list_free(args); 1003 } 1004 1005 fclose(f); 1006} 1007 1008static void 1009service_mach_message(bool blocking) 1010{ 1011 __block kern_return_t status; 1012 uint32_t rbits, sbits; 1013 notify_request_msg *request; 1014 notify_reply_msg *reply; 1015 char rbuf[sizeof(notify_request_msg) + MAX_TRAILER_SIZE]; 1016 char sbuf[sizeof(notify_reply_msg) + MAX_TRAILER_SIZE]; 1017 1018 forever 1019 { 1020 memset(rbuf, 0, sizeof(rbuf)); 1021 memset(sbuf, 0, sizeof(sbuf)); 1022 1023 request = (notify_request_msg *)rbuf; 1024 reply = (notify_reply_msg *)sbuf; 1025 1026 request->head.msgh_local_port = global.server_port; 1027 request->head.msgh_size = global.request_size; 1028 1029 rbits = MACH_RCV_MSG | (blocking ? 0 : MACH_RCV_TIMEOUT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); 1030 sbits = MACH_SEND_MSG; 1031 1032 status = mach_msg(&(request->head), rbits, 0, global.request_size, global.server_port, 0, MACH_PORT_NULL); 1033 if (status != KERN_SUCCESS) return; 1034 1035#if TARGET_OS_EMBEDDED 1036 /* Synchronize with work_q since on embedded main() calls this 1037 * from the global concurrent queue. */ 1038 dispatch_sync(global.work_q, ^{ 1039 status = notify_ipc_server(&(request->head), &(reply->head)); 1040 }); 1041#else 1042 status = notify_ipc_server(&(request->head), &(reply->head)); 1043#endif 1044 if (!status && (request->head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) 1045 { 1046 /* destroy the request - but not the reply port */ 1047 request->head.msgh_remote_port = MACH_PORT_NULL; 1048 mach_msg_destroy(&(request->head)); 1049 } 1050 if (reply->head.msgh_remote_port) 1051 { 1052 status = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 1053 if (status == MACH_SEND_INVALID_DEST || status == MACH_SEND_TIMED_OUT) 1054 { 1055 /* deallocate reply port rights not consumed by failed mach_msg() send */ 1056 mach_msg_destroy(&(reply->head)); 1057 } 1058 } 1059 } 1060} 1061 1062static int32_t 1063open_shared_memory(const char *name) 1064{ 1065 int32_t shmfd, isnew; 1066 uint32_t size; 1067 1068 size = global.nslots * sizeof(uint32_t); 1069 1070 isnew = 1; 1071 shmfd = shm_open(name, O_RDWR, 0644); 1072 if (shmfd != -1) 1073 { 1074 isnew = 0; 1075 } 1076 else 1077 { 1078 shmfd = shm_open(name, O_RDWR | O_CREAT, 0644); 1079 } 1080 1081 if (shmfd == -1) 1082 { 1083 fprintf(stderr, "shm_open %s failed: %s\n", name, strerror(errno)); 1084 return -1; 1085 } 1086 1087 ftruncate(shmfd, size); 1088 global.shared_memory_base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0); 1089 close(shmfd); 1090 1091 if (isnew == 0) 1092 { 1093 global.last_shm_base = malloc(size); 1094 if (global.last_shm_base != NULL) memcpy(global.last_shm_base, global.shared_memory_base, size); 1095 } 1096 1097 memset(global.shared_memory_base, 0, size); 1098 global.shared_memory_refcount = (uint32_t *)malloc(size); 1099 if (global.shared_memory_refcount == NULL) return -1; 1100 1101 memset(global.shared_memory_refcount, 0, size); 1102 1103 /* slot 0 is notifyd's pid */ 1104 global.shared_memory_base[0] = getpid(); 1105 global.shared_memory_refcount[0] = 1; 1106 global.slot_id = 0; 1107 1108 return 0; 1109} 1110 1111int 1112main(int argc, const char *argv[]) 1113{ 1114 dispatch_queue_t main_q; 1115 const char *service_name; 1116 const char *shm_name; 1117 uint32_t i, status; 1118 struct rlimit rlim; 1119 1120 service_name = NOTIFY_SERVICE_NAME; 1121 shm_name = SHM_ID; 1122 1123#ifdef PORT_DEBUG 1124 debug_log_file = fopen("/var/log/notifyd.log", "a"); 1125#endif 1126 1127 notify_set_options(NOTIFY_OPT_DISABLE); 1128 1129 /* remove limit of number of file descriptors */ 1130 rlim.rlim_max = RLIM_INFINITY; 1131 rlim.rlim_cur = MIN(OPEN_MAX, rlim.rlim_max); 1132 setrlimit(RLIMIT_NOFILE, &rlim); 1133 1134 signal(SIGPIPE, SIG_IGN); 1135 signal(SIGHUP, SIG_IGN); 1136 signal(SIGUSR1, SIG_IGN); 1137 signal(SIGUSR2, SIG_IGN); 1138 signal(SIGWINCH, SIG_IGN); 1139 1140 memset(&call_statistics, 0, sizeof(struct call_statistics_s)); 1141 1142 global.request_size = sizeof(notify_request_msg) + MAX_TRAILER_SIZE; 1143 global.reply_size = sizeof(notify_reply_msg) + MAX_TRAILER_SIZE; 1144 global.nslots = getpagesize() / sizeof(uint32_t); 1145 global.notify_state = _notify_lib_notify_state_new(NOTIFY_STATE_ENABLE_RESEND, 1024); 1146 global.log_cutoff = ASL_LEVEL_NOTICE; 1147 global.log_file = NULL; 1148 global.slot_id = (uint32_t)-1; 1149 1150 for (i = 1; i < argc; i++) 1151 { 1152 if (!strcmp(argv[i], "-d")) 1153 { 1154 global.log_cutoff = ASL_LEVEL_DEBUG; 1155 } 1156 else if (!strcmp(argv[i], "-log_cutoff")) 1157 { 1158 global.log_cutoff = atoi(argv[++i]); 1159 } 1160 else if (!strcmp(argv[i], "-log_file")) 1161 { 1162 if (global.log_file != NULL) fclose(global.log_file); 1163 global.log_file = fopen(argv[++i], "a"); 1164 } 1165 else if (!strcmp(argv[i], "-service")) 1166 { 1167 service_name = argv[++i]; 1168 } 1169 else if (!strcmp(argv[i], "-shm")) 1170 { 1171 shm_name = argv[++i]; 1172 } 1173 else if (!strcmp(argv[i], "-shm_pages")) 1174 { 1175 global.nslots = atoi(argv[++i]) * (getpagesize() / sizeof(uint32_t)); 1176 } 1177 } 1178 1179 global.log_default = global.log_cutoff; 1180 1181 if (global.log_file == NULL) 1182 { 1183 global.log_file = fopen(DEBUG_LOG_PATH, "a"); 1184 } 1185 1186 log_message(ASL_LEVEL_DEBUG, "--------------------\n"); 1187 log_message(ASL_LEVEL_DEBUG, "notifyd start PID %u\n", getpid()); 1188 1189 init_launch_config(service_name); 1190 1191 if (global.nslots > 0) 1192 { 1193 status = open_shared_memory(shm_name); 1194 assert(status == 0); 1195 } 1196 1197 /* init from config file before starting the work queue */ 1198 init_config(); 1199 1200 main_q = dispatch_get_main_queue(); 1201 assert(main_q != NULL); 1202 1203 global.work_q = dispatch_queue_create("WorkQ", NULL); 1204 assert(global.work_q != NULL); 1205 1206#if TARGET_OS_EMBEDDED 1207 /* Block a thread in mach_msg() to avoid the syscall overhead of frequent 1208 * dispatch source wakeup, and synchronize with work_q after message 1209 * reception in service_mach_message(). <rdar://problem/8785140> */ 1210 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 1211 forever service_mach_message(true); 1212 }); 1213#else 1214 global.mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, global.server_port, 0, global.work_q); 1215 assert(global.mach_src != NULL); 1216 1217 dispatch_source_set_event_handler(global.mach_src, ^{ 1218 service_mach_message(false); 1219 }); 1220 dispatch_resume(global.mach_src); 1221#endif 1222 1223 /* Set up SIGUSR1 */ 1224 global.sig_usr1_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR1, 0, main_q); 1225 assert(global.sig_usr1_src != NULL); 1226 dispatch_source_set_event_handler(global.sig_usr1_src, ^{ 1227 dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_SHORT); }); 1228 }); 1229 dispatch_resume(global.sig_usr1_src); 1230 1231 /* Set up SIGUSR2 */ 1232 global.sig_usr2_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGUSR2, 0, main_q); 1233 assert(global.sig_usr2_src != NULL); 1234 dispatch_source_set_event_handler(global.sig_usr2_src, ^{ 1235 dispatch_async(global.work_q, ^{ dump_status(STATUS_REQUEST_LONG); }); 1236 }); 1237 dispatch_resume(global.sig_usr2_src); 1238 1239 /* Set up SIGWINCH */ 1240 global.sig_winch_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGWINCH, 0, main_q); 1241 assert(global.sig_winch_src != NULL); 1242 dispatch_source_set_event_handler(global.sig_winch_src, ^{ 1243 if (global.log_cutoff == ASL_LEVEL_DEBUG) global.log_cutoff = global.log_default; 1244 else global.log_cutoff = ASL_LEVEL_DEBUG; 1245 }); 1246 dispatch_resume(global.sig_winch_src); 1247 1248 dispatch_main(); 1249 1250 /* NOTREACHED */ 1251 return 0; 1252} 1253