1#include <assert.h> 2#include <ctype.h> 3#include <errno.h> 4#include <netdb.h> 5#include <setjmp.h> 6#include <signal.h> 7#include <stdlib.h> 8#include <stdio.h> 9#include <string.h> 10#include <unistd.h> 11#include <time.h> 12 13#include <arpa/inet.h> 14#include <IOKit/IOKitLib.h> 15#include <IOKit/IOCFSerialize.h> 16#include <IOKit/network/IONetworkLib.h> 17 18#include <mach/mach.h> 19#include <mach/mach_interface.h> 20#include <netinet/in.h> 21 22#include <sys/types.h> 23#include <sys/socket.h> 24#include <sys/time.h> 25#include <sys/file.h> 26 27#include "kdp_protocol.h" 28#include "IrDALog.h" 29 30#pragma mark -- Prototypes 31 32// Prototypes 33int DumpRemoteLog(int argc, char ** argv); 34int DumpLocalLog(); 35 36// common subs 37void DumpLog(void); // dump the log to disk once we have it 38void OutputBuffer(IrDALogHdr *hdr, IrDAEventDesc *events, char *msgs, FILE *out); 39Boolean CheckLog(IrDALogHdr *obj); 40FILE *CreateLogFile(void); // make the output file 41 42// network subs 43Boolean SetPeer(char **argv); 44Boolean DoRequest(kdp_req_t command, UInt32 length); 45Boolean DoConnect(UInt16 localport); 46Boolean DoDisconnect(void); 47Boolean ReadLog(void *addr); // addr is the kernal address of the info record 48Boolean DoRead(void *remote_addr, void *local_addr, int length); 49 50// iokit stuff 51#if 0 52// find this elsewhere ... 53extern "C" kern_return_t io_connect_method_structureI_structureO 54( 55 mach_port_t connection, 56 int selector, 57 io_struct_inband_t input, 58 mach_msg_type_number_t inputCnt, 59 io_struct_inband_t output, 60 mach_msg_type_number_t *outputCnt 61); 62#endif 63 64typedef struct IrDACommand 65{ 66 unsigned char commandID; // one of the commands above (tbd) 67 char data[1]; // this is not really one byte, it is as big as I like 68 // I set it to 1 just to make the compiler happy 69} IrDACommand; 70typedef IrDACommand *IrDACommandPtr; 71 72kern_return_t doCommand(io_connect_t con, unsigned char commandID, 73 void *inputData, unsigned long inputDataSize, 74 void *outputData, size_t *outputDataSize); 75 76kern_return_t openDevice(io_object_t obj, io_connect_t * con); 77kern_return_t closeDevice(io_connect_t con); 78io_object_t getInterfaceWithName(mach_port_t masterPort, char *className); 79 80#pragma mark -- Globals 81 82// Globals 83 84char bigbuffer[10*1024*1024]; // fix: do two passes and allocate the appropriate size 85IrDALogInfo info; // pointers and sizes returned directly 86UInt32 infoaddr; // address of info block on remote machine 87 88// globals for network 89 90int f; // our socket 91struct sockaddr_in peeraddr; 92char hostname[100]; // hostname of peer 93 94UInt8 packet[MAX_KDP_PKT_SIZE]; // just need one packet 95 96 // kdp header stuff 97UInt32 key; // session key (unique we hope) 98UInt8 seq; // sequence number within session 99 100// simple punt macro 101#define Punt(x) { fprintf(stderr, "%s: %s\n", argv[0], x); return -1; } 102// punt macro with errno text too 103#define PuntErr(x) { fprintf(stderr, "%s: %s [%s]\n", argv[0], x, strerror(errno)); return 1; } 104 105int 106main(int argc, char ** argv) 107{ 108 if (argc == 1) 109 return DumpLocalLog(); 110 else if (argc == 3) 111 return DumpRemoteLog(argc, argv); 112 113 printf("usage: %s # to dump irdalog on local machine\n", argv[0]); 114 printf(" %s hostname 0xLogInfoAddress # remote log\n", argv[0]); 115 return 1; 116} 117 118#pragma mark -- Dump local log 119 120int 121DumpLocalLog() 122{ 123 mach_port_t masterPort; 124 kern_return_t kr; 125 io_object_t netif; 126 io_connect_t conObj; 127 128 // Get master device port 129 // 130 kr = IOMasterPort(bootstrap_port, &masterPort); 131 if (kr != KERN_SUCCESS) { 132 printf("IOMasterPort() failed: %08lx\n", (unsigned long)kr); 133 return -1; 134 } 135 netif = getInterfaceWithName(masterPort, "AppleIrDA"); 136 if (netif) { 137 //printf("netif=0x%x\n", netif); 138 kr = openDevice(netif, &conObj); 139 if (kr == kIOReturnSuccess) { 140 UInt32 inbuf[2]; // big buffer address passed to userclient 141 size_t infosize; 142 143 inbuf[0] = (UInt32)&bigbuffer[0]; 144 inbuf[1] = sizeof(bigbuffer); 145 infosize = sizeof(info); 146 147 //printf("bigbuf at 0x%x\n", (int)&bigbuffer[0]); 148 kr = doCommand(conObj, 0x12, &inbuf, sizeof(inbuf), &info, &infosize); 149 if (kr == kIOReturnSuccess) { 150 //printf("command/request worked we think\n"); 151 DumpLog(); 152 } 153 else printf("command/request failed 0x%x\n", kr); 154 closeDevice(conObj); 155 } 156 else printf("open device failed 0x%x\n", kr); 157 IOObjectRelease(netif); 158 } 159 160 //printf("toodles\n"); 161 exit(0); 162} 163 164 165/* ========================================== 166 * open/close device. 167 * ========================================== */ 168 169kern_return_t 170openDevice(io_object_t obj, io_connect_t * con) 171{ 172 return IOServiceOpen(obj, mach_task_self(), 123, con); 173} 174 175kern_return_t 176closeDevice(io_connect_t con) 177{ 178 return IOServiceClose(con); 179} 180 181/* ========================================== 182 * Look through the registry and search for an 183 * IONetworkInterface objects with the given 184 * name. 185 * If a match is found, the object is returned. 186 * =========================================== */ 187 188io_object_t 189getInterfaceWithName(mach_port_t masterPort, char *className) 190{ 191 kern_return_t kr; 192 io_iterator_t ite; 193 io_object_t obj = 0; 194 195 kr = IORegistryCreateIterator(masterPort, 196 kIOServicePlane, 197 true, /* recursive */ 198 &ite); 199 200 if (kr != kIOReturnSuccess) { 201 printf("IORegistryCreateIterator() error %08lx\n", (unsigned long)kr); 202 return 0; 203 } 204 205 while ((obj = IOIteratorNext(ite))) { 206 if (IOObjectConformsTo(obj, (char *) className)) { 207 printf("Found IrDA UserClient !!\n"); 208 break; 209 } 210 else { 211 io_name_t name; 212 kern_return_t rc; 213 rc = IOObjectGetClass(obj, name); 214 if (rc == kIOReturnSuccess) { 215 //printf("Skipping class %s\n", name); 216 } 217 } 218 IOObjectRelease(obj); 219 obj = 0; 220 } 221 222 IOObjectRelease(ite); 223 224 return obj; 225} 226 227kern_return_t 228doCommand(io_connect_t con, 229 unsigned char commandID, 230 void *inputData, unsigned long inputDataSize, 231 void *outputData, size_t *outputDataSize) 232{ 233 kern_return_t err = KERN_SUCCESS; 234 //mach_msg_type_number_t outSize = outputDataSize; 235 IrDACommandPtr command = NULL; 236 237 // Creates a command block: 238 command = (IrDACommandPtr)malloc (inputDataSize + sizeof (unsigned char)); 239 if (!command) 240 return KERN_FAILURE; 241 command->commandID = commandID; 242 243 // Adds the data to the command block: 244 if ((inputData != NULL) && (inputDataSize != 0)) 245 memcpy(command->data, inputData, inputDataSize); 246 247 // Now we can (hopefully) transfer everything: 248 err = IOConnectCallStructMethod( 249 con, 250 0, /* method index */ 251 (char *) command, /* input[] */ 252 inputDataSize+sizeof(unsigned char),/* inputCount */ 253 (char *) outputData, /* output */ 254 outputDataSize); /* buffer size, then result */ 255 free (command); 256 return err; 257} 258 259 260 261 262 263#pragma mark -- Dump remote log 264 265 266 267int 268DumpRemoteLog(int argc, char ** argv) 269{ 270 struct sockaddr_in sin; // our local address 271 UInt16 localport; // our port number 272 Boolean ok; 273 int rc; 274 275 rc = sscanf(argv[2], "0x%lx", &infoaddr); 276 if (rc != 1) PuntErr("failed to parse info address"); 277 278 printf("info address 0x%lx\n", infoaddr); 279 280 (void)time((time_t *)&key); // session key had better be a long 281 282 f = socket(AF_INET, SOCK_DGRAM, 0); 283 if (f < 0) PuntErr("create socket"); 284 285 // make the socket non-blocking 286 rc = fcntl(f, F_SETFL, O_NONBLOCK); 287 if (rc) PuntErr("failed to set non-blocking io"); 288 289 bzero((char *)&sin, sizeof(sin)); 290 sin.sin_family = AF_INET; // system picks our port number 291 rc = bind(f, (struct sockaddr *)&sin, sizeof(sin)); 292 if (rc) PuntErr("failed to bind to local udp port"); 293 294 if (1) { // bind doesn't return our port number, sheesh. get it now. 295 sockaddr buffer; 296 socklen_t sz = sizeof(buffer); 297 rc = getsockname(f, &buffer, &sz); // udp returns length, family, port high, port low 298 if (rc) PuntErr("Failed to get local udp port number"); 299 // seems our udp port number is in the first two bytes, wonder where this 300 // is documented ... 301 localport = (UInt8)buffer.sa_data[0] << 8 | (UInt8)buffer.sa_data[1]; 302 printf("Using local port number %d\n", localport); 303 } 304 305 ok = SetPeer(argv); // lookup the hostname 306 if (!ok) return -1; 307 308 printf("Debugging with %s [%s]\n", hostname, inet_ntoa(peeraddr.sin_addr)); 309 310 ok = DoConnect(localport); 311 if (!ok) Punt("Connect failed"); 312 313 printf("Connected!\n"); 314 //sleep(5); 315 316 ok = ReadLog((void *)infoaddr); 317 if (!ok) printf("read failed\n"); 318 else DumpLog(); 319 320 ok = DoDisconnect(); 321 if (!ok) Punt("Disconnect failed"); 322 323 close(f); 324 325 printf("Toodles!\n"); 326 return 0; 327} 328 329// return true if hostname on command line is ok 330Boolean 331SetPeer(char **argv) 332{ 333 struct hostent *host; 334 335 host = gethostbyname(argv[1]); 336 if (host) { 337 peeraddr.sin_family = host->h_addrtype; 338 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 339 strcpy(hostname, host->h_name); 340 } else { 341 peeraddr.sin_family = AF_INET; 342 peeraddr.sin_addr.s_addr = inet_addr(argv[1]); 343 if (peeraddr.sin_addr.s_addr == (unsigned)-1) { 344 printf("%s: unknown host '%s'\n", argv[0], argv[1]); 345 return false; 346 } 347 strcpy(hostname, argv[1]); 348 } 349 peeraddr.sin_port = KDP_REMOTE_PORT; 350 return true; 351} 352 353Boolean 354DoConnect(UInt16 localport) 355{ 356 kdp_connect_req_t *request = (kdp_connect_req_t *)packet; 357 kdp_connect_reply_t *reply = (kdp_connect_reply_t *)packet; 358 char *msg = "Boo"; 359 int length; 360 Boolean ok; 361 362 bzero(packet, sizeof(packet)); 363 364 request->req_reply_port = localport; 365 request->exc_note_port = localport; // ****** REVIEW ****** 366 strcpy(request->greeting, msg); // not really referenced 367 length = sizeof(kdp_connect_req_t) + strlen(msg) + 1; 368 369 ok = DoRequest(KDP_CONNECT, length); // send packet, wait for response 370 if (ok && reply->error == 0) 371 return true; 372 373 return false; 374} 375 376// 377// Fill in the header and send off a request. 378// Wait for and read the response. 379// Do minimal checking on response packet 380// todo: if no response, resend a couple times 381// 382Boolean 383DoRequest(kdp_req_t command, UInt32 length) 384{ 385 socklen_t fromlen; 386 struct sockaddr_in from; 387 kdp_hdr_t *hdr = (kdp_hdr_t *)packet; 388 ssize_t rc; 389 fd_set fdset; 390 time_t timeout; 391 392 // fill in the header 393 hdr->request = command; 394 hdr->is_reply = 0; 395 hdr->seq = seq++; 396 hdr->len = length; 397 hdr->key = key; 398 399 // send the request in the packet buffer 400 rc = sendto(f, packet, length, 0, 401 (struct sockaddr *)&peeraddr, sizeof(peeraddr)); 402 if (rc != (ssize_t)length) return false; 403 404 // now wait for a response 405 FD_ZERO(&fdset); 406 FD_SET(f, &fdset); // prepare to wait until data read on f 407 timeout = time(NULL) + 5; // 5 second max timeout 408 while (1) { // keep trying until timeout 409 struct timeval t; 410 time_t now, delta; 411 412 now = time(NULL); // get current time 413 delta = timeout - now; // seconds left until timeout 414 if (delta <= 0) return false; // timeout, bail 415 416 t.tv_sec = delta + 1; // wait a bit more 417 t.tv_usec = 0; 418 rc = select(f+1, &fdset, NULL, NULL, &t); 419 if (rc <= 0) continue; // loop if no data ready 420 fromlen = sizeof(from); 421 rc = recvfrom(f, packet, sizeof(packet), 0, 422 (struct sockaddr *)&from, &fromlen); 423 if (rc > 0) { 424 if (hdr->request == command && // if same request type 425 hdr->is_reply && // but a reply 426 hdr->seq == (UInt8)(seq-1) && // right sequence number 427 hdr->key == key) // and right session key 428 return true; // then have an ack (data in packet for caller) 429 } 430 } 431 return false; // timeout 432} 433 434 435Boolean 436DoDisconnect() 437{ 438 Boolean ok; 439 440 bzero(packet, sizeof(packet)); 441 442 ok = DoRequest(KDP_DISCONNECT, sizeof(kdp_disconnect_req_t)); 443 444 return ok; // check response? 445} 446 447Boolean 448ReadLog(void *addr) 449{ 450 Boolean ok; 451 char *local = bigbuffer; 452 453 // First, read the info block 454 ok = DoRead(addr, &info, sizeof(info)); 455 if (!ok) return ok; 456 457 printf("hdr at 0x%lx, size %ld\n", (UInt32)info.hdr, info.hdrSize); 458 printf("log at 0x%lx, size %ld\n", (UInt32)info.eventLog, info.eventLogSize); 459 printf("msgs at 0x%lx, size %ld\n", (UInt32)info.msgBuffer, info.msgBufferSize); 460 461 ok = DoRead(info.hdr, local, info.hdrSize); 462 if (!ok) return ok; 463 local += info.hdrSize; 464 465 ok = DoRead(info.eventLog, local, info.eventLogSize); 466 if (!ok) return ok; 467 local += info.eventLogSize; 468 469 ok = DoRead(info.msgBuffer, local, info.msgBufferSize); 470 return ok; 471} 472 473Boolean 474DoRead(void *remote_addr, void *local_addr, int length) 475{ 476 kdp_readmem_req_t *req = (kdp_readmem_req_t *)packet; 477 kdp_readmem_reply_t *reply = (kdp_readmem_reply_t *)packet; 478 Boolean ok; 479 480 while (length > MAX_KDP_DATA_SIZE) { 481 ok = DoRead(remote_addr, local_addr, MAX_KDP_DATA_SIZE); 482 if (!ok) return ok; 483 remote_addr = (UInt8 *)remote_addr + MAX_KDP_DATA_SIZE; 484 local_addr = (UInt8 *)local_addr + MAX_KDP_DATA_SIZE; 485 length -= MAX_KDP_DATA_SIZE; 486 } 487 488 bzero(packet, sizeof(packet)); 489 req->address = remote_addr; 490 req->nbytes = length; 491 ok = DoRequest(KDP_READMEM, sizeof(kdp_readmem_req_t)); 492 if (!ok) return ok; 493 494 if (reply->error != 0) { 495 printf("read error %d\n", reply->error); 496 return false; 497 } 498 499 // use it 500 bcopy(&reply->data[0], local_addr, length); 501 return true; 502} 503 504#pragma mark -- Common log output routines 505 506void 507DumpLog() 508{ 509 IrDALogHdr *hdr = (IrDALogHdr *)&bigbuffer[0]; 510 IrDAEventDesc *events = (IrDAEventDescPtr)&bigbuffer[info.hdrSize]; 511 char *msgs = (char *)&bigbuffer[info.hdrSize + info.eventLogSize]; 512 FILE *out; 513 514 /*** 515 516 printf("Kernel data:\n"); 517 printf("Hdr at 0x%lx, size %ld\n", (UInt32)info.hdr, info.hdrSize); 518 printf("Events at 0x%lx, size %ld\n", (UInt32)info.eventLog, info.eventLogSize); 519 printf("Msgs at 0x%lx, size %ld\n", (UInt32)info.msgBuffer, info.msgBufferSize); 520 521 printf("My addresses:\n"); 522 printf("Hdr at 0x%lx\n", (UInt32)hdr); 523 printf("Events at 0x%lx\n", (UInt32)events); 524 printf("Msgs at 0x%lx\n", (UInt32)msgs); 525 526 printf("eventindex %ld, print %ld, count %ld, enabled %d, wrapped %d\n", 527 hdr->fEventIndex, hdr->fPrintIndex, hdr->fNumEntries, 528 hdr->fTracingOn, hdr->fWrapped); 529 530 ***/ 531 532 if (CheckLog(hdr)) { // if any new entries 533 out = CreateLogFile(); 534 if (out) { 535 OutputBuffer(hdr, events, msgs, out); 536 fclose(out); 537 } 538 printf("Done\n"); 539 } 540} 541 542 543FILE * 544CreateLogFile(void) 545{ 546 time_t now; 547 struct tm *lt; 548 char filename[100]; 549 550 time(&now); 551 lt = localtime(&now); 552 sprintf(filename, "log.%d.%d.%02d%02d.%02d", 553 lt->tm_mon+1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); 554 printf("Writing log to %s\n", filename); 555 556 return fopen(filename, "w"); 557} 558 559// return true if there are messages to dump 560Boolean CheckLog(IrDALogHdr *obj) 561{ 562 if (obj->fWrapped == false && // if we've not wrapped around and 563 obj->fEventIndex == obj->fPrintIndex) { // print index == new event index, then nothing new 564 printf("No new log entries\n" ); 565 printf("gIrDALog = 0x%lx\n", (UInt32)obj); 566 printf("buffer = 0x%lx\n", (UInt32)obj->fEventBuffer); 567 printf("index = %ld\n", obj->fEventIndex); 568 printf("prtindex = %ld\n", obj->fPrintIndex); 569 printf("#entries = %ld\n", obj->fNumEntries); 570 printf("traceon = %d\n", obj->fTracingOn); 571 printf("wrapped = %d\n", obj->fWrapped); 572 return false; 573 } 574 return true; 575} 576 577 578void OutputBuffer(IrDALogHdr *obj, IrDAEventDesc *events, char *msgs, FILE *out) 579{ 580 581 UInt16 len; 582 char buffer[256]; 583 IrDAEventDescPtr eventPtr; 584 Boolean oldTracingFlag; 585 586 /***/ 587 588 if (obj->fWrapped == false && // if we've not wrapped around and 589 obj->fEventIndex == obj->fPrintIndex) { // print index == new event index, then nothing new 590 printf("No new log entries\n" ); 591 printf("gIrDALog = 0x%lx\n", (UInt32)obj); 592 printf("buffer = 0x%lx\n", (UInt32)obj->fEventBuffer); 593 printf("index = %ld\n", obj->fEventIndex); 594 printf("prtindex = %ld\n", obj->fPrintIndex); 595 printf("#entries = %ld\n", obj->fNumEntries); 596 printf("traceon = %d\n", obj->fTracingOn); 597 printf("wrapped = %d\n", obj->fWrapped); 598 return; 599 } 600 /****/ 601 602 oldTracingFlag = obj->fTracingOn; // Save old value of tracing enabled bit 603 obj->fTracingOn = false; // temporarily turn off tracing during dcmd (let's not race) 604 605 if (obj->fWrapped) { // if adding new entries wrapped over print index 606 obj->fPrintIndex = obj->fEventIndex; // then start printing at next "avail" entry 607 } 608 609 if( obj->fPrintIndex >= obj->fNumEntries ) // sanity check only, this shouldn't happen 610 obj->fPrintIndex = 0; 611 612 do{ 613 UInt32 secs, usecs; // pulled out of timestamp 614 static UInt32 lasttime = 0; 615 SInt32 deltaTime; 616 char *msg; 617 618 619 eventPtr = &obj->fEventBuffer[obj->fPrintIndex]; 620 eventPtr = (eventPtr - info.eventLog) + events; // adjust event ptr for kernel/user address space change 621 msg = (eventPtr->msg - info.msgBuffer) + msgs; // adjust msg ptr too 622 623 //printf("msg before 0x%lx, after 0x%lx\n", (UInt32)eventPtr->msg, (UInt32)msg); 624 //{ UInt32 *p = (UInt32 *)msg; 625 // printf("first two are %lx %lx\n", p[0], p[1]); 626 //} 627 628 if (eventPtr->timeStamp) { // if log entry is timestamped 629 secs = eventPtr->timeStamp / 1000000; 630 usecs = eventPtr->timeStamp % 1000000; 631 632 deltaTime = eventPtr->timeStamp - lasttime; 633 lasttime = eventPtr->timeStamp; 634 if (deltaTime > 999999 || deltaTime < 0) deltaTime = 999999; 635 len = sprintf( buffer, "%4ld.%06ld [%6ld] D: %04hx,%04hx %s", 636 secs, usecs, 637 deltaTime, 638 eventPtr->data1, 639 eventPtr->data2, 640 msg ); 641 } 642 else // timestamp not provided, blank out to match above spacing 643 len = sprintf( buffer, " x.x [ ] D: %04hx,%04hx %s", 644 //secs, usecs, 645 //deltaTime, 646 eventPtr->data1, 647 eventPtr->data2, 648 msg ); 649 650 fprintf(out, buffer ); 651 fprintf(out, "\n"); 652 653 obj->fPrintIndex++; 654 655 if( obj->fPrintIndex >= obj->fNumEntries ) // wrap print index at end of circular buffer 656 obj->fPrintIndex = 0; 657 658 } while((obj->fPrintIndex != obj->fEventIndex) ); 659 660 obj->fPrintIndex = obj->fEventIndex; // FLUSH PENDING LOG ENTRIES if aborted 661 // we shouldn't do this once we get a -C flag :-) 662 obj->fWrapped = false; // reset wrapped flag 663 obj->fTracingOn = oldTracingFlag; // restore tracing state (enable again) 664} 665