1#include <stdlib.h> 2#include <stdio.h> 3#include <unistd.h> 4#include <strings.h> 5#include <errno.h> 6#include <err.h> 7#include <fcntl.h> 8#include <sysexits.h> 9#include <sys/types.h> 10#include <sys/param.h> 11#include <sys/wait.h> 12#include <pwd.h> 13#include <sys/stat.h> 14#include <sys/mman.h> 15#include <mach-o/fat.h> 16#include <mach-o/loader.h> 17#include <mach-o/arch.h> 18 19#include "dumpemacs.h" 20#include "bo.h" /* generated during build */ 21#include "src/version.h" 22 23void usage(void); 24int dumpemacs(int debugflag, char *output); 25int copythintemacs(int debugflag, const char *src, const char *dst); 26void *mmaparch(const char *file, size_t *psize); 27char *verfind(void *, size_t, char, const char *); 28 29int main(int argc, char *argv[]) { 30 31 int debugopt = 0, verboseopt = 0, testopt = 0, forceopt = 0; 32 char output[MAXPATHLEN]; 33 int ch; 34 int ret, fd; 35 36 umask(022); 37 while ((ch = getopt(argc, argv, "Vdfnv")) != -1) { 38 switch (ch) { 39 case 'd': 40 debugopt = 1; 41 verboseopt = 1; 42 break; 43 case 'v': 44 verboseopt = 1; 45 break; 46 case 'n': 47 testopt = 1; 48 break; 49 case 'f': 50 forceopt = 1; 51 break; 52 case 'V': 53 puts(kEmacsVersion); 54 exit(0); 55 break; 56 default: 57 usage(); 58 break; 59 } 60 } 61 62 if(!testopt) { 63 // must be run as root unless we're not planning on writing 64 if(geteuid() != 0) 65 errx(1, "Must be run as root unless -n is used"); 66 } 67 68 69 70 if(!verboseopt) { 71 fd = open("/dev/null", O_RDWR, 0600); 72 if(fd < 0) 73 err(1, "open(/dev/null)"); 74 75 ret = dup2(fd, STDIN_FILENO); 76 if(ret == -1) 77 err(1, "dup2(/dev/null, stdin)"); 78 79 ret = dup2(fd, STDOUT_FILENO); 80 if(ret == -1) 81 err(1, "dup2(/dev/null, stdout)"); 82 83 ret = dup2(fd, STDERR_FILENO); 84 if(ret == -1) 85 err(1, "dup2(/dev/null, stderr)"); 86 87 ret = close(fd); 88 if(ret == -1) 89 err(1, "close(/dev/null)"); 90 } 91 92 if (!forceopt) { 93 int dumpit = 1; 94 char *dumpedVersion = NULL; 95 char *undumpedVersion = NULL; 96 size_t dumpedSize, undumpedSize; 97 98 void *dumpedMem = mmaparch(kEmacsDumpedPath, &dumpedSize); 99 if (dumpedMem) { 100 /* Break up @(#) to avoid false tags in this binary */ 101 dumpedVersion = verfind(dumpedMem, dumpedSize, '@', "(#) emacs"); 102 munmap(dumpedMem, dumpedSize); 103 } 104 if (dumpedVersion) { 105 void *undumpedMem = mmaparch(kEmacsUndumpedPath, &undumpedSize); 106 if (undumpedMem) { 107 undumpedVersion = verfind(undumpedMem, undumpedSize, '@', "(#) emacs"); 108 munmap(undumpedMem, undumpedSize); 109 } 110 } 111 if (dumpedVersion != NULL && undumpedVersion != NULL && 112 (0 == strcmp(dumpedVersion, undumpedVersion))) { 113 dumpit = 0; 114 } 115 if (dumpedVersion) free(dumpedVersion); 116 if (undumpedVersion) free(undumpedVersion); 117 if (!dumpit) 118 return 0; 119 } 120 ret = dumpemacs(debugopt, output); 121 if(ret != 0) 122 errx(1, "Failed to dump native emacs"); 123 124 if(testopt) { 125 printf("emacs successfully dumped. Test mode successful.\n"); 126 return 0; 127 } 128 129 ret = chown(output, 0, 0); // reset to root:wheel 130 if(ret) 131 err(1, "chown(%s)", output); 132 133 const char * newargs[5]; 134 newargs[0] = "/bin/cp"; 135 newargs[1] = "-p"; 136 newargs[2] = output; 137 newargs[3] = kEmacsDumpedPath; 138 newargs[4] = NULL; 139 140 if(debugopt) printf("Installing dumped emacs\n"); 141 ret = runit(newargs, 0); 142 if(ret) 143 errx(1, "Failed to install dumped emacs"); 144 145 return 0; 146 147} 148 149void usage(void) 150{ 151 fprintf(stderr, "Usage: %s [-d] [-f] [-n] [-v] [-V]\n", getprogname()); 152 exit(EX_USAGE); 153} 154 155 156 157int dumpemacs(int debugflag, char *output) 158{ 159 char tempdir[MAXPATHLEN], newpath[MAXPATHLEN]; 160 char *tmp = NULL; 161 int ret, fd; 162 struct passwd *nobody = NULL; 163 uid_t nobodyUID = 0; 164 165 nobody = getpwnam("nobody"); 166 if(nobody == NULL) 167 err(1, "Don't know about nobody"); 168 169 nobodyUID = nobody->pw_uid; 170 171 tmp = "/tmp"; 172 173 snprintf(tempdir, sizeof(tempdir), "%s/emacs.XXXXXX", tmp); 174 175 if(debugflag) printf("Generating random directory with template %s\n", tempdir); 176 if(NULL == mkdtemp(tempdir)) 177 err(1, "mkdtemp(%s) failed", tempdir); 178 chown(tempdir, nobodyUID, 0); 179 if(debugflag) printf("Directory is %s\n", tempdir); 180 181 snprintf(newpath, sizeof(newpath), "%s/etc", tempdir); 182 if(debugflag) printf("Making directory %s\n", newpath); 183 ret = mkdir(newpath, S_IRWXU); 184 if(ret) 185 err(1, "mkdir(%s)", newpath); 186 chown(newpath, nobodyUID, 0); 187 188 snprintf(newpath, sizeof(newpath), "%s/lib-src", tempdir); 189 if(debugflag) printf("Making directory %s\n", newpath); 190 ret = mkdir(newpath, S_IRWXU); 191 if(ret) 192 err(1, "mkdir(%s)", newpath); 193 chown(newpath, nobodyUID, 0); 194 195 snprintf(newpath, sizeof(newpath), "%s/src", tempdir); 196 if(debugflag) printf("Making directory %s\n", newpath); 197 ret = mkdir(newpath, S_IRWXU); 198 if(ret) 199 err(1, "mkdir(%s)", newpath); 200 chown(newpath, nobodyUID, 0); 201 202 snprintf(newpath, sizeof(newpath), "%s/etc/GNU", tempdir); 203 if(debugflag) printf("Making symlink %s -> %s\n", 204 newpath, kEmacsShareDir "/" kEmacsVersion "/etc/GNU"); 205 ret = symlink(kEmacsShareDir "/" kEmacsVersion "/etc/GNU", newpath); 206 if(ret) 207 err(1, "symlink(%s)", newpath); 208 209 snprintf(newpath, sizeof(newpath), "%s/etc/DOC", tempdir); 210 if(debugflag) printf("Making symlink %s -> %s\n", 211 newpath, kEmacsShareDir "/" kEmacsVersion "/etc/DOC-" kEmacsVersion "." kEmacsVersionMinor); 212 ret = symlink(kEmacsShareDir "/" kEmacsVersion "/etc/DOC-" kEmacsVersion "." kEmacsVersionMinor, newpath); 213 if(ret) 214 err(1, "symlink(%s)", newpath); 215 216 snprintf(newpath, sizeof(newpath), "%s/src/temacs", tempdir); 217 ret = copythintemacs(debugflag, kEmacsUndumpedPath, newpath); 218 if(ret) 219 errx(1, "copythintemacs() failed"); 220 221 snprintf(newpath, sizeof(newpath), "%s/src", tempdir); 222 ret = chdir(newpath); 223 if(ret) 224 err(1, "chdir(%s)", newpath); 225 /* see emacs/src/doc.c */ 226 fd = open("buildobj.lst", O_CREAT|O_WRONLY, 0444); 227 if(fd < 0) 228 err(1, "open(buildobj.lst)"); 229 if (-1 == write(fd, bo, sizeof(bo))) 230 err(1, "write to buildobj.lst"); 231 close(fd); 232 233 ret = setenv("LC_ALL", "C", 1); 234 if(ret) 235 err(1, "setenv(LC_ALL, C)"); 236 237 const char *newargs[6]; 238 newargs[0] = "./temacs"; 239 newargs[1] = "-batch"; 240 newargs[2] = "-l"; 241 newargs[3] = "loadup"; 242 newargs[4] = "dump"; 243 newargs[5] = NULL; 244 245 if(debugflag) printf("Attempting to dump emacs\n"); 246 ret = runit(newargs, 1); 247 if(ret) 248 errx(1, "Failed to dump emacs"); 249 250 snprintf(output, MAXPATHLEN, "%s/src/emacs", tempdir); 251 if(debugflag) printf("emacs dumped as %s\n", output); 252 253 254 return 0; 255} 256 257int copythintemacs(int debugflag, const char *src, const char *dst) 258{ 259 int fd; 260 int ret; 261 char buffer[4096]; 262 struct fat_header *fh = (struct fat_header *)buffer; 263 struct fat_arch fakearch; 264 struct fat_arch *archs = NULL, *bestArch = NULL; 265 int archCount = 0; 266 ssize_t readBytes; 267 int isFat = 0; 268 const NXArchInfo *thisArch = NULL; 269 270 bzero(&fakearch, sizeof(fakearch)); 271 272 fd = open(src, O_RDONLY, 0400); 273 if(fd < 0) 274 err(1, "open(%s)", src); 275 276 readBytes = read(fd, buffer, sizeof(buffer)); 277 if(readBytes != sizeof(buffer)) 278 err(1, "read failed"); 279 280 ret = close(fd); 281 if(ret) 282 err(1, "close(%s)", src); 283 284 if(fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM) { 285 int i; 286 287 archs = (struct fat_arch *)(fh + 1); 288 289 fh->magic = OSSwapBigToHostInt32(fh->magic); 290 fh->nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch); 291 if(fh->nfat_arch >= 0x10000) 292 errx(1, "Illegal fat header"); 293 294 for(i=0; i < fh->nfat_arch; i++) { 295 archs[i].cputype = OSSwapBigToHostInt32(archs[i].cputype); 296 archs[i].cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); 297 archs[i].offset = OSSwapBigToHostInt32(archs[i].offset); 298 archs[i].size = OSSwapBigToHostInt32(archs[i].size); 299 archs[i].align = OSSwapBigToHostInt32(archs[i].align); 300 } 301 isFat = 1; 302 archCount = fh->nfat_arch; 303 } else if(fh->magic == MH_MAGIC) { 304 struct mach_header *mh = (struct mach_header *)buffer; 305 fakearch.cputype = mh->cputype; 306 fakearch.cpusubtype = mh->cpusubtype; 307 fakearch.offset = 0; 308 fakearch.size = 0; 309 fakearch.align = 0; 310 archs = &fakearch; 311 archCount = 1; 312 } else if(fh->magic == MH_CIGAM) { 313 struct mach_header *mh = (struct mach_header *)buffer; 314 fakearch.cputype = OSSwapInt32(mh->cputype); 315 fakearch.cpusubtype = OSSwapInt32(mh->cpusubtype); 316 fakearch.offset = 0; 317 fakearch.size = 0; 318 fakearch.align = 0; 319 archs = &fakearch; 320 archCount = 1; 321 } else if(fh->magic == MH_MAGIC_64) { 322 struct mach_header_64 *mh = (struct mach_header_64 *)buffer; 323 fakearch.cputype = mh->cputype; 324 fakearch.cpusubtype = mh->cpusubtype; 325 fakearch.offset = 0; 326 fakearch.size = 0; 327 fakearch.align = 0; 328 archs = &fakearch; 329 archCount = 1; 330 } else if(fh->magic == MH_CIGAM_64) { 331 struct mach_header_64 *mh = (struct mach_header_64 *)buffer; 332 fakearch.cputype = OSSwapInt32(mh->cputype); 333 fakearch.cpusubtype = OSSwapInt32(mh->cpusubtype); 334 fakearch.offset = 0; 335 fakearch.size = 0; 336 fakearch.align = 0; 337 archs = &fakearch; 338 archCount = 1; 339 } 340 341 thisArch = NXGetArchInfoFromName(kEmacsArch); 342 if(thisArch == NULL) 343 errx(1, "Unknown architecture: %s", kEmacsArch); 344 345 bestArch = NXFindBestFatArch(thisArch->cputype, 346 thisArch->cpusubtype, 347 archs, archCount); 348 if(bestArch == NULL) 349 errx(1, "No appropriate architecture in %s", src); 350 else 351 thisArch = NXGetArchInfoFromCpuType(bestArch->cputype, bestArch->cpusubtype); 352 353 // we need to copy it to dst, either as-is, or thinning 354 if(!isFat) { 355 const char * newargs[5]; 356 newargs[0] = "/bin/cp"; 357 newargs[1] = "-p"; 358 newargs[2] = src; 359 newargs[3] = dst; 360 newargs[4] = NULL; 361 362 if(debugflag) printf("Copying %s to %s\n", src, dst); 363 ret = runit(newargs, 0); 364 if(ret) 365 errx(1, "copying failed"); 366 367 } else { 368 const char * newargs[7]; 369 newargs[0] = "/usr/bin/lipo"; 370 newargs[1] = src; 371 newargs[2] = "-thin"; 372 newargs[3] = thisArch->name; 373 newargs[4] = "-output"; 374 newargs[5] = dst; 375 newargs[6] = NULL; 376 377 if(debugflag) printf("Thinning %s to %s\n", src, dst); 378 ret = runit(newargs, 0); 379 if(ret) 380 errx(1, "thinning failed"); 381 382 } 383 384 return 0; 385} 386 387void *mmaparch(const char *filename, size_t *psize) { 388 int fd; 389 int ret; 390 char buffer[4096]; 391 struct fat_header *fh = (struct fat_header *)buffer; 392 struct fat_arch fakearch; 393 struct fat_arch *archs = NULL, *bestArch = NULL; 394 int archCount = 0; 395 ssize_t readBytes; 396 int isFat = 0; 397 const NXArchInfo *thisArch = NULL; 398 off_t offset; 399 400 bzero(&fakearch, sizeof(fakearch)); 401 402 fd = open(filename, O_RDONLY, 0400); 403 if (fd < 0) 404 return NULL; 405 406 readBytes = read(fd, buffer, sizeof(buffer)); 407 if(readBytes != sizeof(buffer)) { 408 close(fd); 409 return NULL; 410 } 411 412 413 if(fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM) { 414 int i; 415 416 archs = (struct fat_arch *)(fh + 1); 417 418 fh->magic = OSSwapBigToHostInt32(fh->magic); 419 fh->nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch); 420 if(fh->nfat_arch >= 0x10000) 421 errx(1, "Illegal fat header"); 422 423 for(i=0; i < fh->nfat_arch; i++) { 424 archs[i].cputype = OSSwapBigToHostInt32(archs[i].cputype); 425 archs[i].cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); 426 archs[i].offset = OSSwapBigToHostInt32(archs[i].offset); 427 archs[i].size = OSSwapBigToHostInt32(archs[i].size); 428 archs[i].align = OSSwapBigToHostInt32(archs[i].align); 429 } 430 isFat = 1; 431 archCount = fh->nfat_arch; 432 } else if(fh->magic == MH_MAGIC) { 433 struct mach_header *mh = (struct mach_header *)buffer; 434 fakearch.cputype = mh->cputype; 435 fakearch.cpusubtype = mh->cpusubtype; 436 fakearch.offset = 0; 437 fakearch.size = 0; 438 fakearch.align = 0; 439 archs = &fakearch; 440 archCount = 1; 441 } else if(fh->magic == MH_CIGAM) { 442 struct mach_header *mh = (struct mach_header *)buffer; 443 fakearch.cputype = OSSwapInt32(mh->cputype); 444 fakearch.cpusubtype = OSSwapInt32(mh->cpusubtype); 445 fakearch.offset = 0; 446 fakearch.size = 0; 447 fakearch.align = 0; 448 archs = &fakearch; 449 archCount = 1; 450 } else if(fh->magic == MH_MAGIC_64) { 451 struct mach_header_64 *mh = (struct mach_header_64 *)buffer; 452 fakearch.cputype = mh->cputype; 453 fakearch.cpusubtype = mh->cpusubtype; 454 fakearch.offset = 0; 455 fakearch.size = 0; 456 fakearch.align = 0; 457 archs = &fakearch; 458 archCount = 1; 459 } else if(fh->magic == MH_CIGAM_64) { 460 struct mach_header_64 *mh = (struct mach_header_64 *)buffer; 461 fakearch.cputype = OSSwapInt32(mh->cputype); 462 fakearch.cpusubtype = OSSwapInt32(mh->cpusubtype); 463 fakearch.offset = 0; 464 fakearch.size = 0; 465 fakearch.align = 0; 466 archs = &fakearch; 467 archCount = 1; 468 } 469 470 thisArch = NXGetArchInfoFromName(kEmacsArch); 471 if(thisArch == NULL) 472 errx(1, "Unknown architecture: %s", kEmacsArch); 473 474 bestArch = NXFindBestFatArch(thisArch->cputype, 475 thisArch->cpusubtype, 476 archs, 477 archCount); 478 if(bestArch == NULL) 479 errx(1, "No appropriate architecture in %s", filename); 480 481 if(!isFat) { 482 /* mmap the whole file */ 483 struct stat statbuf; 484 fstat(fd, &statbuf); 485 *psize = statbuf.st_size; 486 offset = 0; 487 } else { 488 *psize = bestArch->size; 489 offset = bestArch->offset; 490 } 491 void *rc = mmap(NULL, *psize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, offset); 492 close(fd); 493 if (rc == (void *)-1) 494 return NULL; 495 return rc; 496} 497 498char *verfind(void *mem, size_t size, char marker, const char *search) { 499 char *first = (char *)mem; 500 char *last = first + size; 501 size_t search_size = strlen(search); 502 /* avoid searching past end of mmap region */ 503 void *found = memchr(mem, marker, size - (search_size+1)); 504 505 while (found != NULL) { 506 char *here = (char *)found; 507 if (0 == strncmp(here+1, search, search_size)) { 508 return strdup(here); 509 } else { 510 found = memchr(here+1, marker, last - (here+1) - (search_size+1)); 511 } 512 } 513 return NULL; 514} 515