1/* 2 * Copyright (c) 2007 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 * firmwaresyncd.c 25 * bless 26 * 27 * Created by Shantonu Sen on 10/13/07. 28 * Copyright 2007 Apple Inc. All rights reserved. 29 * 30 */ 31 32#include <stdlib.h> 33#include <stdio.h> 34#include <string.h> 35#include <stdarg.h> 36#include <unistd.h> 37#include <getopt.h> 38#include <err.h> 39#include <spawn.h> 40#include <sys/stat.h> 41#include <sys/mount.h> 42#include <sys/fcntl.h> 43#include <sys/time.h> 44#include <syslog.h> 45#include <sysexits.h> 46#include <copyfile.h> 47#include <sys/sysctl.h> 48 49#include <DiskArbitration/DiskArbitration.h> 50#include <DiskArbitration/DiskArbitrationPrivate.h> 51 52#include <IOKit/kext/kextmanager_types.h> 53#include <IOKit/kext/kextmanager_mig.h> 54 55#include <mach/mach.h> 56#include <servers/bootstrap.h> 57#include <sys/resource.h> 58 59#include "bless.h" 60#include "bless_private.h" 61 62#define kFirmwareFileOSPath "/usr/standalone/i386/Firmware.scap" 63#define kFirmwareFileEFIDir "/EFI/APPLE/EXTENSIONS" 64#define kFirmwareFileEFIPath "/EFI/APPLE/EXTENSIONS/Firmware.scap" 65#define kTimeDelay (4*60) 66#define kTSCacheDir "/System/Library/Caches/com.apple.bootstamps" 67 68extern char **environ; 69 70void usage(void); 71void catch_sigterm(int sig); 72 73static bool gSIGTERM = false; 74 75/* Should we even run? If not, exit out. If so, use the volume UUID for / */ 76bool should_run(void); 77bool get_uuid(CFUUIDRef *uuid); 78bool allocate_mach_ports(mach_port_t *kextdport, mach_port_t *vollock); 79bool deallocate_mach_ports(mach_port_t kextdport, mach_port_t vollock); 80bool lock_volume(CFUUIDRef uuid, mach_port_t kextdport, mach_port_t vollock); 81bool unlock_volume(CFUUIDRef uuid, mach_port_t kextdport, mach_port_t vollock); 82bool generate_timestamp_path(CFUUIDRef uuid, char *path); 83bool check_if_uptodate(CFUUIDRef uuid); 84bool update_esp(void); 85bool update_timestamp(CFUUIDRef uuid); 86bool run_tool(char *argv[], CFDataRef *output); 87 88int main(int argc, char *argv[]) { 89 90 int ch; 91 int opt_d = 0; 92 CFUUIDRef uuid = NULL; 93 mach_port_t vollock = MACH_PORT_NULL, kextdport = MACH_PORT_NULL; 94 bool needunlock = false, immediately = false; 95 unsigned int sleepleft; 96 97 signal(SIGTERM, catch_sigterm); 98 99 while ((ch = getopt(argc, argv, "di")) != -1) { 100 switch (ch) { 101 case 'd': 102 opt_d = 1; 103 break; 104 case 'i': 105 immediately = true; 106 break; 107 case '?': 108 default: 109 usage(); 110 break; 111 } 112 } 113 114 argc -= optind; 115 argv += optind; 116 117 openlog(getprogname(), LOG_PID | (opt_d ? LOG_PERROR : 0), LOG_DAEMON); 118 setlogmask(opt_d ? LOG_UPTO(LOG_DEBUG) : LOG_UPTO(LOG_ERR)); 119 120// syslog(LOG_INFO, "This is informational"); 121// syslog(LOG_DEBUG, "This is debuggingational"); 122 123 // In general we try exit on failures without complaining 124 if (!should_run()) { 125 goto done; 126 } 127 128 syslog(LOG_DEBUG, "Preflight passed"); 129 130 if (!get_uuid(&uuid)) { 131 goto done; 132 } 133 134 if (!check_if_uptodate(uuid)) { 135 goto done; 136 } 137 138 sleepleft = (immediately ? 0 : kTimeDelay); 139 syslog(LOG_DEBUG, "Sleeping for %u seconds", sleepleft); 140 do { 141 if (gSIGTERM) { 142 syslog(LOG_DEBUG, "Caught SIGTERM and exiting"); 143 goto done; 144 } 145 sleepleft = sleep(sleepleft); 146 } while (sleepleft > 0); 147 syslog(LOG_DEBUG, "Done sleeping"); 148 149 if (!allocate_mach_ports(&kextdport, &vollock)) { 150 goto done; 151 } 152 153 // relock, in case the state changed while we were asleep 154 if (!lock_volume(uuid, kextdport, vollock)) { 155 goto done; 156 } 157 needunlock = true; 158 159 if (!check_if_uptodate(uuid)) { 160 goto done; 161 } 162 163 if (!update_esp()) { 164 goto done; 165 } 166 167 if (!update_timestamp(uuid)) { 168 goto done; 169 } 170 171 if (!unlock_volume(uuid, kextdport, vollock)) { 172 goto done; 173 } 174 needunlock = false; 175 176 177done: 178 if (needunlock) { 179 unlock_volume(uuid, kextdport, vollock); 180 } 181 if (kextdport != MACH_PORT_NULL || vollock != MACH_PORT_NULL) { 182 deallocate_mach_ports(kextdport, vollock); 183 } 184 if (uuid) { 185 CFRelease(uuid); 186 } 187 closelog(); 188 return 0; 189} 190 191void usage(void) 192{ 193 fprintf(stderr, "Usage: %s [-d]\n", getprogname()); 194 exit(EX_USAGE); 195} 196 197void catch_sigterm(int sig) 198{ 199 gSIGTERM = true; 200} 201 202bool should_run(void) 203{ 204 bool result = false; 205 BLPreBootEnvType preBootType; 206 struct stat sb; 207 uint32_t safeboot = 0; 208 size_t safebootsize = sizeof(safeboot); 209 int ret; 210 211 ret = sysctlbyname("kern.safeboot", &safeboot, &safebootsize, NULL, 0); 212 if (ret) { 213 syslog(LOG_DEBUG, "Could not determine safeboot status: %s", strerror(errno)); 214 return result; 215 } 216 syslog(LOG_DEBUG, "Safeboot status: %u", safeboot); 217 if (safeboot) { 218 return result; 219 } 220 221 if (0 != BLGetPreBootEnvironmentType(NULL, &preBootType)) { 222 syslog(LOG_DEBUG, "Could not determine preboot environment type"); 223 return result; 224 } 225 if (preBootType != kBLPreBootEnvType_EFI) { 226 syslog(LOG_DEBUG, "Preboot environment type is not EFI"); 227 return result; 228 } 229 230 if (0 != lstat(kFirmwareFileOSPath, &sb) || !S_ISREG(sb.st_mode)) { 231 syslog(LOG_DEBUG, "Font file %s is not accessible or not a regular file", kFirmwareFileOSPath); 232 return result; 233 } 234 235 result = true; 236 237 return result; 238} 239 240 241static void _DADiskAppearedCallback( DADiskRef disk, void * context ); 242 243static void _DADiskAppearedCallback( DADiskRef disk, void * context ) 244{ 245 CFUUIDRef *uuid = (CFUUIDRef *)context; 246 CFUUIDRef dauuid = NULL; 247 CFDictionaryRef dadescription; 248 249 dadescription = DADiskCopyDescription(disk); 250 if (dadescription) { 251 dauuid = CFDictionaryGetValue(dadescription, kDADiskDescriptionVolumeUUIDKey); 252 if (dauuid) { 253 if (*uuid) { 254 CFRelease(*uuid); 255 } 256 *uuid = CFRetain(dauuid); 257 } 258 CFRelease(dadescription); 259 } 260 261} 262 263bool get_uuid(CFUUIDRef *uuid) 264{ 265 DASessionRef dasession; 266 CFURLRef rootpath; 267 CFMutableDictionaryRef matchdict; 268 bool result = false; 269 270 *uuid = NULL; 271 272 rootpath = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)"/", 1, true); 273 if (rootpath) { 274 dasession = DASessionCreate(kCFAllocatorDefault); 275 if (dasession) { 276 DASessionScheduleWithRunLoop(dasession, CFRunLoopGetCurrent(), CFSTR("FIRMWARESYNCD")); 277 278 matchdict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 279 CFDictionaryAddValue(matchdict, kDADiskDescriptionVolumePathKey, rootpath); 280 DARegisterDiskAppearedCallback(dasession, matchdict, _DADiskAppearedCallback, uuid); 281 CFRelease(matchdict); 282 283 while (!gSIGTERM && !*uuid) { 284 CFRunLoopRunInMode(CFSTR("FIRMWARESYNCD"), 1.0, true); 285 } 286 DASessionUnscheduleFromRunLoop(dasession, CFRunLoopGetCurrent(), CFSTR("FIRMWARESYNCD")); 287 CFRelease(dasession); 288 } 289 CFRelease(rootpath); 290 } 291 if (*uuid) { 292 result = true; 293 syslog(LOG_DEBUG, "Determined volume UUID for /"); 294 } else { 295 syslog(LOG_DEBUG, "Could not determine volume UUID for /"); 296 } 297 298 return result; 299} 300 301bool lock_volume(CFUUIDRef uuid, mach_port_t kextdport, mach_port_t vollock) 302{ 303 bool result = false; 304 kern_return_t kret; 305 306 uuid_t s_vol_uuid; 307 CFUUIDBytes uuidbytes; 308 int lckres = 0; 309 310 uuidbytes = CFUUIDGetUUIDBytes(uuid); 311 memcpy(&s_vol_uuid, &uuidbytes, sizeof(uuidbytes)); 312 313 syslog(LOG_DEBUG, "Locking volume"); 314 315 kret = kextmanager_lock_volume(kextdport, vollock, s_vol_uuid, 316 1 /* block */, &lckres); 317 if (kret || lckres) { 318 syslog(LOG_DEBUG, "Failed to obtain lock: %s/%s", mach_error_string(kret), strerror(lckres)); 319 goto done; 320 } 321 322 result = true; 323 324done: 325 return result; 326} 327 328bool unlock_volume(CFUUIDRef uuid, mach_port_t kextdport, mach_port_t vollock) 329{ 330 bool result = false; 331 kern_return_t kret; 332 333 uuid_t s_vol_uuid; 334 CFUUIDBytes uuidbytes; 335 336 uuidbytes = CFUUIDGetUUIDBytes(uuid); 337 memcpy(&s_vol_uuid, &uuidbytes, sizeof(uuidbytes)); 338 339 syslog(LOG_DEBUG, "Unlocking volume"); 340 341 kret = kextmanager_unlock_volume(kextdport, vollock, s_vol_uuid, 342 0); 343 if (kret) { 344 syslog(LOG_DEBUG, "Failed to unlock: %s", mach_error_string(kret)); 345 goto done; 346 } 347 348 result = true; 349 350done: 351 return result; 352} 353 354bool allocate_mach_ports(mach_port_t *kextdport, mach_port_t *vollock) 355{ 356 bool result = false; 357 kern_return_t kret; 358 359 mach_port_t sLockPort = MACH_PORT_NULL; 360 mach_port_t sKextdPort = MACH_PORT_NULL; 361 362 syslog(LOG_DEBUG, "Obtaining mach ports"); 363 364 if (sKextdPort == MACH_PORT_NULL) { 365 kret = bootstrap_look_up(bootstrap_port, KEXTD_SERVER_NAME, &sKextdPort); 366 if (kret) { 367 syslog(LOG_DEBUG, "Failed to look up port for %s: %s", KEXTD_SERVER_NAME, mach_error_string(kret)); 368 goto done; 369 } 370 } 371 372 if (sLockPort == MACH_PORT_NULL) { 373 kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sLockPort); 374 if (kret) { 375 syslog(LOG_DEBUG, "Failed to look up port for %s: %s", KEXTD_SERVER_NAME, mach_error_string(kret)); 376 goto done; 377 } 378 } 379 380 381 result = true; 382 *kextdport = sKextdPort; 383 *vollock = sLockPort; 384 385done: 386 return result; 387} 388 389bool deallocate_mach_ports(mach_port_t kextdport, mach_port_t vollock) 390{ 391 bool result = false; 392 kern_return_t kret; 393 394 syslog(LOG_DEBUG, "Deallocating mach ports"); 395 396 if (kextdport != MACH_PORT_NULL) { 397 kret = mach_port_mod_refs(mach_task_self(), kextdport, MACH_PORT_RIGHT_SEND, -1); 398 if (kret) { 399 syslog(LOG_DEBUG, "Failed to drop reference for %s: %s", KEXTD_SERVER_NAME, mach_error_string(kret)); 400 goto done; 401 } 402 } 403 404 if (vollock != MACH_PORT_NULL) { 405 kret = mach_port_mod_refs(mach_task_self(), vollock, MACH_PORT_RIGHT_RECEIVE, -1); 406 if (kret) { 407 syslog(LOG_DEBUG, "Failed to drop reference for lock %s", mach_error_string(kret)); 408 goto done; 409 } 410 } 411 412 result = true; 413 414done: 415 return result; 416} 417 418bool generate_timestamp_path(CFUUIDRef uuid, char *path) 419{ 420 char timepath[MAXPATHLEN]; 421 char colonpath[MAXPATHLEN], *cptr; 422 char uuidcomponent[NAME_MAX]; 423 CFStringRef uuidstr; 424 bool result = false; 425 426 uuidstr = CFUUIDCreateString(kCFAllocatorDefault, uuid); 427 if (!CFStringGetCString(uuidstr, uuidcomponent, sizeof(uuidcomponent), kCFStringEncodingUTF8)) { 428 CFRelease(uuidstr); 429 goto done; 430 } 431 CFRelease(uuidstr); 432 433 strlcpy(colonpath, kFirmwareFileOSPath, sizeof(colonpath)); 434 cptr = strchr(colonpath, '/'); 435 while (cptr) { 436 *cptr++ = ':'; 437 cptr = strchr(cptr, '/'); 438 } 439 440 // we check for overflow on the last operation, since it should be cumulative 441 strlcpy(timepath, kTSCacheDir, sizeof(timepath)); 442 strlcat(timepath, "/", sizeof(timepath)); 443 strlcat(timepath, uuidcomponent, sizeof(timepath)); 444 strlcat(timepath, "/", sizeof(timepath)); 445 if (strlcat(timepath, colonpath, sizeof(timepath)) >= sizeof(timepath)) { 446 goto done; 447 } 448 449 strlcpy(path, timepath, MAXPATHLEN); 450 result = true; 451 452done: 453 return result; 454} 455 456 457/* Check in /S/L/C if we have a cookie file newer than the font file */ 458bool check_if_uptodate(CFUUIDRef uuid) 459{ 460 bool result = false, needupdate = false; 461 struct stat sb, sb2; 462 int ret; 463 char timepath[MAXPATHLEN]; 464 465 // ... 466 if (0 != lstat(kFirmwareFileOSPath, &sb)) { 467 syslog(LOG_DEBUG, "Could not access %s: %s", kFirmwareFileOSPath, strerror(errno)); 468 goto done; 469 } 470 471 if (!S_ISREG(sb.st_mode)) { 472 syslog(LOG_DEBUG, "%s is not a regular file", kFirmwareFileOSPath); 473 goto done; 474 } 475 476 if (!generate_timestamp_path(uuid, timepath)) { 477 syslog(LOG_DEBUG, "Could not generate path (%s)", timepath); 478 goto done; 479 } 480 481 syslog(LOG_DEBUG, "Timestamp file is %s", timepath); 482 ret = lstat(timepath, &sb2); 483 if (ret) { 484 if (errno == ENOENT) { 485 syslog(LOG_DEBUG, "Timestamp does not exist"); 486 needupdate = true; 487 } else { 488 ret = unlink(timepath); 489 if (ret) { 490 syslog(LOG_DEBUG, "Could not remove %s", timepath); 491 goto done; 492 } 493 needupdate = true; 494 } 495 } else { 496 struct timeval filetime, stamptime; 497 498 if (!S_ISREG(sb2.st_mode)) { 499 syslog(LOG_DEBUG, "%s is not a regular file", timepath); 500 goto done; 501 } 502 503 TIMESPEC_TO_TIMEVAL(&filetime, &sb.st_mtimespec); 504 TIMESPEC_TO_TIMEVAL(&stamptime, &sb2.st_mtimespec); 505 506 if (timercmp(&filetime, &stamptime, >)) { 507 syslog(LOG_DEBUG, "File newer than timestamp"); 508 needupdate = true; 509 } else { 510 syslog(LOG_DEBUG, "File matches timestamp"); 511 needupdate = false; 512 } 513 } 514 515 if (needupdate) { 516 result = true; 517 } 518 519done: 520 return result; 521} 522 523bool update_esp(void) 524{ 525 bool result = false, needunmount = false, needrmdir = false; 526 struct statfs sb; 527 int ret; 528 CFDictionaryRef dict = NULL; 529 CFArrayRef array = NULL; 530 CFStringRef esp = NULL; 531 char *newargv[10]; 532 int newargc; 533 char *slash; 534 char espname[MAXPATHLEN], espdev[MAXPATHLEN], mntpath[MAXPATHLEN], espfontpath[MAXPATHLEN]; 535 CFDataRef output = NULL; 536 537 ret = statfs("/", &sb); 538 if (ret) { 539 syslog(LOG_DEBUG, "Failed to statfs /: %s", strerror(errno)); 540 goto done; 541 } 542 543 if (0 != strncmp(sb.f_mntfromname, "/dev/", 5)) { 544 goto done; 545 } 546 547 ret = BLCreateBooterInformationDictionary(NULL, sb.f_mntfromname + 5, &dict); 548 if (ret) { 549 syslog(LOG_DEBUG, "Failed to obtain EFI System Partition information: %d", ret); 550 goto done; 551 } 552 553 array = CFDictionaryGetValue(dict, kBLSystemPartitionsKey); 554 if(array) { 555 if(CFArrayGetCount(array) > 0) { 556 esp = CFArrayGetValueAtIndex(array, 0); 557 558 if (!BLIsEFIRecoveryAccessibleDevice(NULL, esp)) { 559 syslog(LOG_DEBUG, "ESP %s is not accessible as a recovery device\n" , BLGetCStringDescription(esp)); 560 goto done; 561 } 562 } 563 } 564 565 if(!esp) { 566 // needed ESP, but could not find it 567 syslog(LOG_DEBUG, "No appropriate ESP for %s\n" , "/"); 568 goto done; 569 } 570 571 if (!CFStringGetCString(esp, espname, sizeof(espname), kCFStringEncodingUTF8)) { 572 goto done; 573 } 574 575 CFRelease(dict); 576 dict = NULL; 577 esp = NULL; 578 579 strlcpy(espdev, "/dev/", sizeof(espdev)); 580 strlcat(espdev, espname, sizeof(espdev)); 581 syslog(LOG_DEBUG, "ESP partition is %s", espdev); 582 583 584 syslog(LOG_DEBUG, "Verifying %s", espdev); 585 586 newargc = 0; 587 newargv[newargc++] = "/sbin/fsck_msdos"; 588 newargv[newargc++] = "-fn"; 589 newargv[newargc++] = espdev; 590 newargv[newargc++] = NULL; 591 592 if (!run_tool(newargv, &output)) { 593 if (output) { 594 syslog(LOG_ERR, "Command %s output: %.*s", newargv[0], (int)CFDataGetLength(output), CFDataGetBytePtr(output)); 595 CFRelease(output); 596 } 597 goto done; 598 } 599 if (output) { 600 CFRelease(output); 601 } 602 603 strlcpy(mntpath, "/Volumes/firmwaresyncd.XXXXXX", sizeof(mntpath)); 604 if(!mkdtemp(mntpath)) { 605 syslog(LOG_DEBUG, "Could not make temporary directory %s", mntpath); 606 goto done; 607 } 608 needrmdir = true; 609 syslog(LOG_DEBUG, "Temporary mount point is %s", mntpath); 610 611 newargc = 0; 612 newargv[newargc++] = "/sbin/mount"; 613 newargv[newargc++] = "-t"; 614 newargv[newargc++] = "msdos"; 615 newargv[newargc++] = "-o"; 616 newargv[newargc++] = "perm"; 617 newargv[newargc++] = "-o"; 618 newargv[newargc++] = "nobrowse"; 619 newargv[newargc++] = espdev; 620 newargv[newargc++] = mntpath; 621 newargv[newargc++] = NULL; 622 623 if (!run_tool(newargv, &output)) { 624 if (output) { 625 syslog(LOG_ERR, "Command %s output: %.*s", newargv[0], (int)CFDataGetLength(output), CFDataGetBytePtr(output)); 626 CFRelease(output); 627 } 628 goto done; 629 } 630 if (output) { 631 CFRelease(output); 632 } 633 634 needunmount = true; 635 636 strlcpy(espfontpath, mntpath, sizeof(espfontpath)); 637 strlcat(espfontpath, kFirmwareFileEFIDir, sizeof(espfontpath)); 638 639 // make parent directories if they are not present 640 slash = espfontpath + strlen(mntpath); 641 while (strsep(&slash, "/")) { 642 syslog(LOG_DEBUG, "Checking %s", espfontpath); 643 ret = mkdir(espfontpath, S_IRWXU); 644 if (ret && errno != EEXIST) { 645 syslog(LOG_DEBUG, "Failed to create %s: %s", espfontpath, strerror(errno)); 646 break; 647 } 648 if (slash == NULL) { 649 break; 650 } 651 *(slash - 1) = '/'; 652 } 653 654 strlcpy(espfontpath, mntpath, sizeof(espfontpath)); 655 strlcat(espfontpath, kFirmwareFileEFIPath, sizeof(espfontpath)); 656 657 ret = copyfile(kFirmwareFileOSPath, espfontpath, NULL, COPYFILE_DATA); 658 if (ret) { 659 syslog(LOG_DEBUG, "Could not copy %s to %s: %d", kFirmwareFileOSPath, espfontpath, ret); 660 goto done; 661 } 662 syslog(LOG_DEBUG, "Copied %s to %s", kFirmwareFileOSPath, espfontpath); 663 664 result = true; 665 666done: 667 if (needunmount) { 668 newargc = 0; 669 newargv[newargc++] = "/sbin/umount"; 670 newargv[newargc++] = mntpath; 671 newargv[newargc++] = NULL; 672 673 if (!run_tool(newargv, &output)) { 674 if (output) { 675 syslog(LOG_ERR, "Command %s output: %.*s", newargv[0], (int)CFDataGetLength(output), CFDataGetBytePtr(output)); 676 CFRelease(output); 677 } 678 goto done2; 679 } 680 if (output) { 681 CFRelease(output); 682 } 683 684 } 685 686done2: 687 if (needrmdir) { 688 syslog(LOG_DEBUG, "Removing %s\n", mntpath); 689 690 ret = rmdir(mntpath); 691 } 692 693 if (dict) { 694 CFRelease(dict); 695 } 696 return result; 697} 698 699bool update_timestamp(CFUUIDRef uuid) 700{ 701 bool result = false, closefd = false;; 702 char timepath[MAXPATHLEN]; 703 int ret, tfd; 704 struct stat sb; 705 struct timeval times[2]; 706 707 if (0 != lstat(kFirmwareFileOSPath, &sb)) { 708 syslog(LOG_DEBUG, "Could not access %s: %s", kFirmwareFileOSPath, strerror(errno)); 709 goto done; 710 } 711 712 if (!S_ISREG(sb.st_mode)) { 713 syslog(LOG_DEBUG, "%s is not a regular file", kFirmwareFileOSPath); 714 goto done; 715 } 716 717 if (!generate_timestamp_path(uuid, timepath)) { 718 syslog(LOG_DEBUG, "Could not generate path (%s)", timepath); 719 goto done; 720 } 721 722 syslog(LOG_DEBUG, "Updating timestamp file %s", timepath); 723 ret = unlink(timepath); 724 if (ret && errno != ENOENT) { 725 syslog(LOG_DEBUG, "Could not remove %s: %s", timepath, strerror(errno)); 726 goto done; 727 } 728 729 tfd = open(timepath, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 730 if (tfd < 0) { 731 syslog(LOG_DEBUG, "Could not create %s: %s", timepath, strerror(errno)); 732 goto done; 733 } 734 closefd = true; 735 736 TIMESPEC_TO_TIMEVAL(×[0], &sb.st_atimespec); 737 TIMESPEC_TO_TIMEVAL(×[1], &sb.st_mtimespec); 738 739 ret = futimes(tfd, times); 740 if (ret) { 741 syslog(LOG_DEBUG, "Could not set time for %s: %s", timepath, strerror(errno)); 742 goto done; 743 } 744 745 result = true; 746 747done: 748 if (closefd) { 749 ret = close(tfd); 750 if (ret) { 751 syslog(LOG_DEBUG, "Could not close %s: %s", timepath, strerror(errno)); 752 } 753 } 754 755 return result; 756 757} 758 759bool run_tool(char *argv[], CFDataRef *output) 760{ 761 bool result = false, destroyFileActions = false, closeFDs = false; 762 pid_t p, p2; 763 int ret; 764 CFMutableDataRef dataRef; 765 int fds[2]; 766 posix_spawn_file_actions_t file_actions; 767 char buffer[100]; 768 ssize_t readBytes; 769 770 dataRef = CFDataCreateMutable(kCFAllocatorDefault, 0); 771 772 ret = pipe(fds); 773 if (ret) { 774 syslog(LOG_DEBUG, "Could not call posix_spawn: %d", errno); 775 goto done; 776 } 777 closeFDs = true; 778 779 ret = posix_spawn_file_actions_init(&file_actions); 780 if (ret < 0) { 781 syslog(LOG_DEBUG, "Could not call posix_spawn_file_actions_init: %d", ret); 782 goto done; 783 } 784 destroyFileActions = true; 785 786 posix_spawn_file_actions_addclose(&file_actions, fds[0]); 787 if (fds[1] != STDOUT_FILENO) { 788 (void)posix_spawn_file_actions_adddup2(&file_actions, fds[1], STDOUT_FILENO); 789 if (fds[1] != STDERR_FILENO) { 790 (void)posix_spawn_file_actions_adddup2(&file_actions, fds[1], STDERR_FILENO); 791 } 792 (void)posix_spawn_file_actions_addclose(&file_actions, fds[1]); 793 } 794 795 syslog(LOG_DEBUG, "Calling %s\n", argv[0]); 796 ret = posix_spawn(&p, argv[0], &file_actions, NULL, argv, environ); 797 if (ret) { 798 syslog(LOG_DEBUG, "Could not call posix_spawn: %d", ret); 799 goto done; 800 } 801 802 // read 100 bytes at a time until we get EOF (child closed pipe); 803 close(fds[1]); 804 fds[1] = -1; 805 do { 806 readBytes = read(fds[0], buffer, sizeof(buffer)); 807 syslog(LOG_DEBUG, "Read %ld from pipe", readBytes); 808 if (readBytes > 0) { 809 CFDataAppendBytes(dataRef, (UInt8 *)buffer, readBytes); 810 } 811 } while (readBytes > 0); 812 813 if (readBytes < 0) { 814 syslog(LOG_DEBUG, "read returned error: %d\n", errno ); 815 goto done; 816 } 817 818 do { 819 p2 = waitpid(p, &ret, 0); 820 } while (p2 == -1 && errno == EINTR); 821 822 syslog(LOG_DEBUG, "Returned %d\n", ret); 823 if(p2 == -1) { 824 syslog(LOG_DEBUG, "%s failed to return: %d\n", argv[0], errno ); 825 goto done; 826 } 827 if(ret) { 828 if (WIFEXITED(ret)) { 829 syslog(LOG_ERR, "%s exited with %d\n", argv[0], WEXITSTATUS(ret) ); 830 } else { 831 syslog(LOG_ERR, "%s signaled with %d\n", argv[0], WTERMSIG(ret) ); 832 } 833 goto done; 834 } 835 836 result = true; 837 838done: 839 840 if (destroyFileActions) { 841 posix_spawn_file_actions_destroy(&file_actions); 842 } 843 844 if (closeFDs) { 845 close(fds[0]); 846 close(fds[1]); 847 } 848 849 *output = dataRef; 850 851 return result; 852} 853 854