1 2 #include "inc.h" 3 4 #include <signal.h> 5 #include <sys/time.h> 6 #include <sys/wait.h> 7 #include <sys/resource.h> 8 #include <sys/utsname.h> 9 #include <sys/reboot.h> 10 #include <minix/profile.h> 11 12 static int 13 pm_exit_out(struct trace_proc * proc, const message * m_out) 14 { 15 16 put_value(proc, "status", "%d", m_out->m_lc_pm_exit.status); 17 18 return CT_NORETURN; 19 } 20 21 static const struct flags waitpid_options[] = { 22 FLAG(WNOHANG), 23 FLAG(WUNTRACED), 24 FLAG(WALTSIG), 25 FLAG(WALLSIG), 26 FLAG(WNOWAIT), 27 FLAG(WNOZOMBIE), 28 FLAG(WOPTSCHECKED), 29 }; 30 31 static void 32 put_waitpid_status(struct trace_proc * proc, const char * name, int status) 33 { 34 const char *signame; 35 int sig; 36 37 /* 38 * There is no suitable set of macros to be used here, so we're going 39 * to invent our own: W_EXITED, W_SIGNALED, and W_STOPPED. Hopefully 40 * they are sufficiently clear even though they don't actually exist. 41 * The code below is downright messy, but it also ensures that no bits 42 * are set unexpectedly in the status. 43 */ 44 if (!valuesonly && WIFEXITED(status) && 45 status == W_EXITCODE(WEXITSTATUS(status), 0)) { 46 put_value(proc, name, "W_EXITED(%d)", 47 WEXITSTATUS(status)); 48 49 return; 50 } 51 52 /* WCOREDUMP() actually returns WCOREFLAG or 0, but better safe.. */ 53 if (!valuesonly && WIFSIGNALED(status) && status == (W_EXITCODE(0, 54 WTERMSIG(status)) | (WCOREDUMP(status) ? WCOREFLAG : 0))) { 55 sig = WTERMSIG(status); 56 57 if ((signame = get_signal_name(sig)) != NULL) 58 put_value(proc, name, "W_SIGNALED(%s)", signame); 59 else 60 put_value(proc, name, "W_SIGNALED(%u)", sig); 61 62 if (WCOREDUMP(status)) 63 put_text(proc, "|WCOREDUMP"); 64 65 return; 66 } 67 68 if (!valuesonly && WIFSTOPPED(status) && 69 status == W_STOPCODE(WSTOPSIG(status))) { 70 sig = WSTOPSIG(status); 71 72 if ((signame = get_signal_name(sig)) != NULL) 73 put_value(proc, name, "W_STOPPED(%s)", signame); 74 else 75 put_value(proc, name, "W_STOPPED(%u)", sig); 76 77 return; 78 } 79 80 /* 81 * If we get here, either valuesonly is enabled or the resulting status 82 * is not one we recognize, for example because extra bits are set. 83 */ 84 put_value(proc, name, "0x%04x", status); 85 } 86 87 static int 88 pm_waitpid_out(struct trace_proc * proc, const message * m_out) 89 { 90 91 put_value(proc, "pid", "%d", m_out->m_lc_pm_waitpid.pid); 92 93 return CT_NOTDONE; 94 } 95 96 static void 97 pm_waitpid_in(struct trace_proc * proc, const message * m_out, 98 const message * m_in, int failed) 99 { 100 101 /* 102 * If the result is zero, there is no status to show. Also, since the 103 * status is returned in the result message, we cannot print the user- 104 * given pointer. Instead, upon failure we show "&.." to indicate an 105 * unknown pointer. 106 */ 107 if (!failed && m_in->m_type > 0) 108 put_waitpid_status(proc, "status", 109 m_in->m_pm_lc_waitpid.status); 110 else 111 put_field(proc, "status", "&.."); 112 put_flags(proc, "options", waitpid_options, COUNT(waitpid_options), 113 "0x%x", m_out->m_lc_pm_waitpid.options); 114 put_equals(proc); 115 put_result(proc); 116 } 117 118 static void 119 pm_getpid_in(struct trace_proc * proc, const message * __unused m_out, 120 const message * m_in, int failed) 121 { 122 123 put_result(proc); 124 if (!failed) { 125 put_open(proc, NULL, 0, "(", ", "); 126 put_value(proc, "ppid", "%d", m_in->m_pm_lc_getpid.parent_pid); 127 put_close(proc, ")"); 128 } 129 } 130 131 /* This function is shared between setuid and seteuid. */ 132 static int 133 pm_setuid_out(struct trace_proc * proc, const message * m_out) 134 { 135 136 put_value(proc, "uid", "%u", m_out->m_lc_pm_setuid.uid); 137 138 return CT_DONE; 139 } 140 141 static void 142 pm_getuid_in(struct trace_proc * proc, const message * __unused m_out, 143 const message * m_in, int failed) 144 { 145 146 put_result(proc); 147 if (!failed) { 148 put_open(proc, NULL, 0, "(", ", "); 149 put_value(proc, "euid", "%u", m_in->m_pm_lc_getuid.euid); 150 put_close(proc, ")"); 151 } 152 } 153 154 static int 155 pm_stime_out(struct trace_proc * proc, const message * m_out) 156 { 157 158 put_time(proc, "time", m_out->m_lc_pm_time.sec); 159 160 return CT_DONE; 161 } 162 163 static void 164 put_signal(struct trace_proc * proc, const char * name, int sig) 165 { 166 const char *signame; 167 168 if (!valuesonly && (signame = get_signal_name(sig)) != NULL) 169 put_field(proc, name, signame); 170 else 171 put_value(proc, name, "%d", sig); 172 } 173 174 static void 175 put_ptrace_req(struct trace_proc * proc, const char * name, int req) 176 { 177 const char *text = NULL; 178 179 if (!valuesonly) { 180 switch (req) { 181 TEXT(T_STOP); 182 TEXT(T_OK); 183 TEXT(T_ATTACH); 184 TEXT(T_DETACH); 185 TEXT(T_RESUME); 186 TEXT(T_STEP); 187 TEXT(T_SYSCALL); 188 TEXT(T_EXIT); 189 TEXT(T_GETINS); 190 TEXT(T_GETDATA); 191 TEXT(T_GETUSER); 192 TEXT(T_SETINS); 193 TEXT(T_SETDATA); 194 TEXT(T_SETUSER); 195 TEXT(T_SETOPT); 196 TEXT(T_GETRANGE); 197 TEXT(T_SETRANGE); 198 TEXT(T_READB_INS); 199 TEXT(T_WRITEB_INS); 200 } 201 } 202 203 if (text != NULL) 204 put_field(proc, name, text); 205 else 206 put_value(proc, name, "%d", req); 207 } 208 209 static void 210 put_struct_ptrace_range(struct trace_proc * proc, const char * name, int flags, 211 vir_bytes addr) 212 { 213 struct ptrace_range pr; 214 215 if (!put_open_struct(proc, name, flags, addr, &pr, sizeof(pr))) 216 return; 217 218 if (!valuesonly && pr.pr_space == TS_INS) 219 put_field(proc, "pr_space", "TS_INS"); 220 else if (!valuesonly && pr.pr_space == TS_DATA) 221 put_field(proc, "pr_space", "TS_DATA"); 222 else 223 put_value(proc, "pr_space", "%d", pr.pr_space); 224 put_value(proc, "pr_addr", "0x%lx", pr.pr_addr); 225 put_ptr(proc, "pr_ptr", (vir_bytes)pr.pr_ptr); 226 put_value(proc, "pr_size", "%zu", pr.pr_size); 227 228 put_close_struct(proc, TRUE /*all*/); 229 } 230 231 static int 232 pm_ptrace_out(struct trace_proc * proc, const message * m_out) 233 { 234 235 put_ptrace_req(proc, "req", m_out->m_lc_pm_ptrace.req); 236 put_value(proc, "pid", "%d", m_out->m_lc_pm_ptrace.pid); 237 238 switch (m_out->m_lc_pm_ptrace.req) { 239 case T_GETINS: 240 case T_GETDATA: 241 case T_GETUSER: 242 case T_READB_INS: 243 put_value(proc, "addr", "0x%lx", m_out->m_lc_pm_ptrace.addr); 244 put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); 245 break; 246 case T_SETINS: 247 case T_SETDATA: 248 case T_SETUSER: 249 case T_WRITEB_INS: 250 put_value(proc, "addr", "0x%lx", m_out->m_lc_pm_ptrace.addr); 251 put_value(proc, "data", "0x%lx", m_out->m_lc_pm_ptrace.data); 252 break; 253 case T_RESUME: 254 case T_STEP: 255 case T_SYSCALL: 256 put_value(proc, "addr", "%ld", m_out->m_lc_pm_ptrace.addr); 257 put_signal(proc, "data", m_out->m_lc_pm_ptrace.data); 258 break; 259 case T_GETRANGE: 260 case T_SETRANGE: 261 put_struct_ptrace_range(proc, "addr", 0, 262 m_out->m_lc_pm_ptrace.addr); 263 put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); 264 break; 265 default: 266 put_value(proc, "addr", "%ld", m_out->m_lc_pm_ptrace.addr); 267 put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); 268 break; 269 } 270 271 return CT_DONE; 272 } 273 274 static void 275 pm_ptrace_in(struct trace_proc * proc, const message * m_out, 276 const message * m_in, int failed) 277 { 278 279 if (!failed) { 280 switch (m_out->m_lc_pm_ptrace.req) { 281 case T_GETINS: 282 case T_GETDATA: 283 case T_GETUSER: 284 case T_READB_INS: 285 put_value(proc, NULL, "0x%lx", 286 m_in->m_pm_lc_ptrace.data); 287 return; 288 } 289 } 290 291 put_result(proc); 292 } 293 294 void 295 put_groups(struct trace_proc * proc, const char * name, int flags, 296 vir_bytes addr, int count) 297 { 298 gid_t groups[NGROUPS_MAX]; 299 int i; 300 301 if ((flags & PF_FAILED) || valuesonly || count < 0 || 302 count > NGROUPS_MAX || (count > 0 && mem_get_data(proc->pid, addr, 303 groups, count * sizeof(groups[0])) < 0)) { 304 if (flags & PF_LOCADDR) 305 put_field(proc, name, "&.."); 306 else 307 put_ptr(proc, name, addr); 308 309 return; 310 } 311 312 put_open(proc, name, PF_NONAME, "[", ", "); 313 for (i = 0; i < count; i++) 314 put_value(proc, NULL, "%u", groups[i]); 315 put_close(proc, "]"); 316 } 317 318 static int 319 pm_setgroups_out(struct trace_proc * proc, const message * m_out) 320 { 321 322 put_value(proc, "ngroups", "%d", m_out->m_lc_pm_groups.num); 323 put_groups(proc, "grouplist", 0, m_out->m_lc_pm_groups.ptr, 324 m_out->m_lc_pm_groups.num); 325 326 return CT_DONE; 327 } 328 329 static int 330 pm_getgroups_out(struct trace_proc * proc, const message * m_out) 331 { 332 333 put_value(proc, "ngroups", "%d", m_out->m_lc_pm_groups.num); 334 335 return CT_NOTDONE; 336 } 337 338 static void 339 pm_getgroups_in(struct trace_proc * proc, const message * m_out, 340 const message * m_in, int failed) 341 { 342 343 put_groups(proc, "grouplist", failed, m_out->m_lc_pm_groups.ptr, 344 m_in->m_type); 345 put_equals(proc); 346 put_result(proc); 347 } 348 349 static int 350 pm_kill_out(struct trace_proc * proc, const message * m_out) 351 { 352 353 put_value(proc, "pid", "%d", m_out->m_lc_pm_sig.pid); 354 put_signal(proc, "sig", m_out->m_lc_pm_sig.nr); 355 356 return CT_DONE; 357 } 358 359 /* This function is shared between setgid and setegid. */ 360 static int 361 pm_setgid_out(struct trace_proc * proc, const message * m_out) 362 { 363 364 put_value(proc, "gid", "%u", m_out->m_lc_pm_setgid.gid); 365 366 return CT_DONE; 367 } 368 369 static void 370 pm_getgid_in(struct trace_proc * proc, const message * __unused m_out, 371 const message * m_in, int failed) 372 { 373 374 put_result(proc); 375 if (!failed) { 376 put_open(proc, NULL, 0, "(", ", "); 377 put_value(proc, "egid", "%u", m_in->m_pm_lc_getgid.egid); 378 put_close(proc, ")"); 379 } 380 } 381 382 static int 383 put_frame_string(struct trace_proc * proc, vir_bytes frame, size_t len, 384 vir_bytes addr) 385 { 386 vir_bytes stacktop, offset; 387 388 /* 389 * The addresses in the frame assume that the process has already been 390 * changed, and the top of the frame is now located at the new process 391 * stack top, which is a hardcoded system-global value. In order to 392 * print the strings, we must convert back each address to its location 393 * within the given frame. 394 */ 395 stacktop = kernel_get_stacktop(); 396 397 if (addr >= stacktop) 398 return FALSE; 399 offset = stacktop - addr; 400 if (offset >= len) 401 return FALSE; 402 addr = frame + len - offset; 403 404 /* 405 * TODO: while using put_buf() is highly convenient, it does require at 406 * least one copy operation per printed string. The strings are very 407 * likely to be consecutive in memory, so copying in larger chunks at 408 * once would be preferable. Also, if copying from the frame fails, 409 * put_buf() will print the string address as we corrected it above, 410 * rather than the address as found in the frame. A copy failure would 411 * always be a case of malice on the traced process's behalf, though. 412 */ 413 put_buf(proc, NULL, PF_STRING, addr, len - offset); 414 415 return TRUE; 416 } 417 418 /* 419 * Print the contents of the exec frame, which includes both pointers and 420 * actual string data for the arguments and environment variables to be used. 421 * Even though we know that the entire frame is not going to exceed ARG_MAX 422 * bytes, this is too large a size for a static buffer, and we'd like to avoid 423 * allocating large dynamic buffers as well. The situation is complicated by 424 * the fact that any string in the frame may run up to the end of the frame. 425 */ 426 static void 427 put_exec_frame(struct trace_proc * proc, vir_bytes addr, size_t len) 428 { 429 void *argv[64]; 430 size_t off, chunk; 431 unsigned int i, count, max, argv_max, envp_max; 432 int first, ok, nulls; 433 434 if (valuesonly) { 435 put_ptr(proc, "frame", addr); 436 put_value(proc, "framelen", "%zu", len); 437 438 return; 439 } 440 441 if (verbose == 0) { 442 argv_max = 16; 443 envp_max = 0; 444 } else if (verbose == 1) 445 argv_max = envp_max = 64; 446 else 447 argv_max = envp_max = INT_MAX; 448 449 off = sizeof(int); /* skip 'argc' at the start of the frame */ 450 first = TRUE; 451 ok = TRUE; 452 nulls = 0; 453 count = 0; 454 max = argv_max; 455 456 do { 457 chunk = sizeof(argv); 458 if (chunk > len - off) 459 chunk = len - off; 460 461 if (mem_get_data(proc->pid, addr + off, argv, chunk) != 0) 462 break; 463 464 if (first) { 465 put_open(proc, "argv", PF_NONAME, "[", ", "); 466 467 first = FALSE; 468 } 469 470 for (i = 0; i < chunk / sizeof(void *) && ok; i++) { 471 if (argv[i] == NULL) { 472 if (count > max) 473 put_tail(proc, count, max); 474 put_close(proc, "]"); 475 if (nulls++ == 0) { 476 put_open(proc, "envp", PF_NONAME, "[", 477 ", "); 478 count = 0; 479 max = envp_max; 480 } else 481 break; /* two NULL pointers: done! */ 482 } else if (count++ < max) 483 ok = put_frame_string(proc, addr, len, 484 (vir_bytes)argv[i]); 485 } 486 487 off += chunk; 488 } while (nulls < 2 && ok); 489 490 /* 491 * Handle failure cases, implied by not reaching the second NULL 492 * in the array. Successful completion is handled in the loop above. 493 * Note that 'ok' is not always cleared on failure, as it is used only 494 * to break out of the outer loop. 495 */ 496 if (first) { 497 put_ptr(proc, "argv", addr + off); 498 put_field(proc, "envp", "&.."); 499 } else if (nulls < 2) { 500 put_tail(proc, 0, 0); 501 put_close(proc, "]"); 502 if (nulls < 1) { 503 put_open(proc, "envp", PF_NONAME, "[", ", "); 504 put_tail(proc, 0, 0); 505 put_close(proc, "]"); 506 } 507 } 508 } 509 510 static int 511 pm_exec_out(struct trace_proc * proc, const message * m_out) 512 { 513 514 put_buf(proc, "path", PF_PATH, m_out->m_lc_pm_exec.name, 515 m_out->m_lc_pm_exec.namelen); 516 put_exec_frame(proc, m_out->m_lc_pm_exec.frame, 517 m_out->m_lc_pm_exec.framelen); 518 519 return CT_NORETURN; 520 } 521 522 /* The idea is that this function may one day print a human-readable time. */ 523 void 524 put_time(struct trace_proc * proc, const char * name, time_t time) 525 { 526 527 put_value(proc, name, "%"PRId64, time); 528 } 529 530 void 531 put_struct_timeval(struct trace_proc * proc, const char * name, int flags, 532 vir_bytes addr) 533 { 534 struct timeval tv; 535 536 /* No field names; they just make things harder to read. */ 537 if (!put_open_struct(proc, name, flags | PF_NONAME, addr, &tv, 538 sizeof(tv))) 539 return; 540 541 if (flags & PF_ALT) 542 put_time(proc, "tv_sec", tv.tv_sec); 543 else 544 put_value(proc, "tv_sec", "%"PRId64, tv.tv_sec); 545 put_value(proc, "tv_usec", "%d", tv.tv_usec); 546 547 put_close_struct(proc, TRUE /*all*/); 548 } 549 550 static void 551 put_struct_itimerval(struct trace_proc * proc, const char * name, int flags, 552 vir_bytes addr) 553 { 554 struct itimerval it; 555 556 /* 557 * This used to pass PF_NONAME, but the layout may not be clear enough 558 * without names. It does turn simple alarm(1) calls into rather 559 * lengthy output, though. 560 */ 561 if (!put_open_struct(proc, name, flags, addr, &it, sizeof(it))) 562 return; 563 564 put_struct_timeval(proc, "it_interval", PF_LOCADDR, 565 (vir_bytes)&it.it_interval); 566 put_struct_timeval(proc, "it_value", PF_LOCADDR, 567 (vir_bytes)&it.it_value); 568 569 put_close_struct(proc, TRUE /*all*/); 570 } 571 572 static void 573 put_itimer_which(struct trace_proc * proc, const char * name, int which) 574 { 575 const char *text = NULL; 576 577 if (!valuesonly) { 578 switch (which) { 579 TEXT(ITIMER_REAL); 580 TEXT(ITIMER_VIRTUAL); 581 TEXT(ITIMER_PROF); 582 TEXT(ITIMER_MONOTONIC); 583 } 584 } 585 586 if (text != NULL) 587 put_field(proc, name, text); 588 else 589 put_value(proc, name, "%d", which); 590 } 591 592 static const char * 593 pm_itimer_name(const message * m_out) 594 { 595 596 return (m_out->m_lc_pm_itimer.value != 0) ? "setitimer" : "getitimer"; 597 } 598 599 static int 600 pm_itimer_out(struct trace_proc * proc, const message * m_out) 601 { 602 603 put_itimer_which(proc, "which", m_out->m_lc_pm_itimer.which); 604 if (m_out->m_lc_pm_itimer.value != 0) { 605 put_struct_itimerval(proc, "value", 0, 606 m_out->m_lc_pm_itimer.value); 607 608 /* 609 * If there will be no old values to print, finish the call 610 * now. For setitimer only; getitimer may not pass NULL. 611 */ 612 if (m_out->m_lc_pm_itimer.ovalue == 0) { 613 put_ptr(proc, "ovalue", 0); 614 615 return CT_DONE; 616 } 617 } 618 619 return CT_NOTDONE; 620 } 621 622 static void 623 pm_itimer_in(struct trace_proc * proc, const message * m_out, 624 const message * __unused m_in, int failed) 625 { 626 627 if (m_out->m_lc_pm_itimer.value == 0 || 628 m_out->m_lc_pm_itimer.ovalue != 0) { 629 put_struct_itimerval(proc, 630 (m_out->m_lc_pm_itimer.value != 0) ? "ovalue" : "value", 631 failed, m_out->m_lc_pm_itimer.ovalue); 632 put_equals(proc); 633 } 634 put_result(proc); 635 } 636 637 static void 638 put_struct_mcontext(struct trace_proc * proc, const char * name, int flags, 639 vir_bytes addr) 640 { 641 mcontext_t ctx; 642 643 if (!put_open_struct(proc, name, flags, addr, &ctx, sizeof(ctx))) 644 return; 645 646 /* 647 * TODO: print actual fields. Then again, the ones that are saved and 648 * restored (FPU state) are hardly interesting enough to print.. 649 */ 650 651 put_close_struct(proc, FALSE /*all*/); 652 } 653 654 static int 655 pm_getmcontext_out(struct trace_proc * proc, const message * m_out) 656 { 657 658 return CT_NOTDONE; 659 } 660 661 static void 662 pm_getmcontext_in(struct trace_proc * proc, const message * m_out, 663 const message * m_in, int failed) 664 { 665 666 put_struct_mcontext(proc, "mcp", failed, m_out->m_lc_pm_mcontext.ctx); 667 put_equals(proc); 668 put_result(proc); 669 } 670 671 static int 672 pm_setmcontext_out(struct trace_proc * proc, const message * m_out) 673 { 674 675 put_struct_mcontext(proc, "mcp", 0, m_out->m_lc_pm_mcontext.ctx); 676 677 return CT_DONE; 678 } 679 680 static void 681 put_sigset(struct trace_proc * proc, const char * name, sigset_t set) 682 { 683 const char *signame; 684 unsigned int count, unknown; 685 int sig, invert; 686 687 /* 688 * First decide whether we should print a normal or an inverted mask. 689 * Unfortunately, depending on the place, a filled set may or may not 690 * have bits outside the 1..NSIG range set. Therefore, we ignore the 691 * bits outside this range entirely, and use simple heuristics to 692 * decide whether to show an inverted set. If we know all the signal 693 * names for either set and not the other, show that one; otherwise, 694 * show an inverted mask if at least 3/4th of the bits are set. 695 */ 696 count = 0; 697 unknown = 0; 698 for (sig = 1; sig < NSIG; sig++) { 699 if (sigismember(&set, sig)) 700 count++; 701 if (get_signal_name(sig) == NULL) 702 unknown |= 1 << !!sigismember(&set, sig); 703 } 704 if (unknown == 1 /*for unset bit*/ || unknown == 2 /*for set bit*/) 705 invert = unknown - 1; 706 else 707 invert = (count >= (NSIG - 1) * 3 / 4); 708 709 put_open(proc, name, PF_NONAME, invert ? "~[" : "[", " "); 710 711 for (sig = 1; sig < NSIG; sig++) { 712 /* Note that sigismember() may not strictly return 0 or 1.. */ 713 if (!sigismember(&set, sig) != invert) 714 continue; 715 716 if ((signame = get_signal_name(sig)) != NULL) { 717 /* Skip the "SIG" prefix for brevity. */ 718 if (!strncmp(signame, "SIG", 3)) 719 put_field(proc, NULL, &signame[3]); 720 else 721 put_field(proc, NULL, signame); 722 } else 723 put_value(proc, NULL, "%d", sig); 724 } 725 726 put_close(proc, "]"); 727 } 728 729 static const struct flags sa_flags[] = { 730 FLAG(SA_ONSTACK), 731 FLAG(SA_RESTART), 732 FLAG(SA_RESETHAND), 733 FLAG(SA_NODEFER), 734 FLAG(SA_NOCLDSTOP), 735 FLAG(SA_NOCLDWAIT), 736 #ifdef SA_SIGINFO 737 FLAG(SA_SIGINFO), 738 #endif 739 FLAG(SA_NOKERNINFO) 740 }; 741 742 static void 743 put_sa_handler(struct trace_proc * proc, const char * name, vir_bytes handler) 744 { 745 const char *text = NULL; 746 747 if (!valuesonly) { 748 switch ((int)handler) { 749 case (int)SIG_DFL: text = "SIG_DFL"; break; 750 case (int)SIG_IGN: text = "SIG_IGN"; break; 751 case (int)SIG_HOLD: text = "SIG_HOLD"; break; 752 } 753 } 754 755 if (text != NULL) 756 put_field(proc, name, text); 757 else 758 put_ptr(proc, name, handler); 759 } 760 761 static void 762 put_struct_sigaction(struct trace_proc * proc, const char * name, int flags, 763 vir_bytes addr) 764 { 765 struct sigaction sa; 766 767 if (!put_open_struct(proc, name, flags, addr, &sa, sizeof(sa))) 768 return; 769 770 put_sa_handler(proc, "sa_handler", (vir_bytes)sa.sa_handler); 771 772 if (verbose > 1) 773 put_sigset(proc, "sa_mask", sa.sa_mask); 774 775 /* A somewhat lame attempt to reduce noise a bit. */ 776 if ((sa.sa_flags & ~(SA_ONSTACK | SA_RESTART | SA_RESETHAND | 777 SA_NODEFER)) != 0 || sa.sa_handler != SIG_DFL || verbose > 0) 778 put_flags(proc, "sa_flags", sa_flags, COUNT(sa_flags), "0x%x", 779 sa.sa_flags); 780 781 put_close_struct(proc, verbose > 1); 782 } 783 784 static int 785 pm_sigaction_out(struct trace_proc * proc, const message * m_out) 786 { 787 788 put_signal(proc, "signal", m_out->m_lc_pm_sig.nr); 789 put_struct_sigaction(proc, "act", 0, m_out->m_lc_pm_sig.act); 790 791 /* If there will be no old values to print, finish the call now. */ 792 if (m_out->m_lc_pm_sig.oact == 0) { 793 put_ptr(proc, "oact", 0); 794 return CT_DONE; 795 } else 796 return CT_NOTDONE; 797 } 798 799 static void 800 pm_sigaction_in(struct trace_proc * proc, const message * m_out, 801 const message * __unused m_in, int failed) 802 { 803 804 if (m_out->m_lc_pm_sig.oact != 0) { 805 put_struct_sigaction(proc, "oact", failed, 806 m_out->m_lc_pm_sig.oact); 807 put_equals(proc); 808 } 809 put_result(proc); 810 } 811 812 static int 813 pm_sigsuspend_out(struct trace_proc * proc, const message * m_out) 814 { 815 816 put_sigset(proc, "set", m_out->m_lc_pm_sigset.set); 817 818 return CT_DONE; 819 } 820 821 static int 822 pm_sigpending_out(struct trace_proc * __unused proc, 823 const message * __unused m_out) 824 { 825 826 return CT_NOTDONE; 827 } 828 829 static void 830 pm_sigpending_in(struct trace_proc * proc, const message * __unused m_out, 831 const message * m_in, int failed) 832 { 833 834 if (!failed) 835 put_sigset(proc, "set", m_in->m_pm_lc_sigset.set); 836 else 837 put_field(proc, "set", "&.."); 838 put_equals(proc); 839 put_result(proc); 840 } 841 842 static void 843 put_sigprocmask_how(struct trace_proc * proc, const char * name, int how) 844 { 845 const char *text = NULL; 846 847 if (!valuesonly) { 848 switch (how) { 849 case SIG_INQUIRE: /* pseudocode, print something else */ 850 TEXT(SIG_BLOCK); 851 TEXT(SIG_UNBLOCK); 852 TEXT(SIG_SETMASK); 853 } 854 } 855 856 if (text != NULL) 857 put_field(proc, name, text); 858 else 859 put_value(proc, name, "%d", how); 860 } 861 862 static int 863 pm_sigprocmask_out(struct trace_proc * proc, const message * m_out) 864 { 865 866 put_sigprocmask_how(proc, "how", m_out->m_lc_pm_sigset.how); 867 if (m_out->m_lc_pm_sigset.how == SIG_INQUIRE) 868 put_ptr(proc, "set", 0); 869 else 870 put_sigset(proc, "set", m_out->m_lc_pm_sigset.set); 871 872 return CT_NOTDONE; 873 } 874 875 static void 876 pm_sigprocmask_in(struct trace_proc * proc, const message * __unused m_out, 877 const message * m_in, int failed) 878 { 879 880 if (!failed) 881 put_sigset(proc, "oset", m_in->m_pm_lc_sigset.set); 882 else 883 put_field(proc, "oset", "&.."); 884 put_equals(proc); 885 put_result(proc); 886 } 887 888 static int 889 pm_sigreturn_out(struct trace_proc * proc, const message * m_out) 890 { 891 struct sigcontext scp; 892 893 if (put_open_struct(proc, "scp", 0, m_out->m_lc_pm_sigset.ctx, &scp, 894 sizeof(scp))) { 895 if (verbose == 1) { 896 #if defined(__i386__) 897 put_ptr(proc, "sc_eip", scp.sc_eip); 898 put_ptr(proc, "sc_esp", scp.sc_esp); 899 #elif defined(__arm__) 900 put_ptr(proc, "sc_pc", scp.sc_pc); 901 put_ptr(proc, "sc_usr_sp", scp.sc_usr_sp); 902 #endif 903 } 904 905 /* 906 * We deliberately print the signal set from the message rather 907 * than from the structure, since in theory they may be 908 * different and PM uses the one from the message only. 909 */ 910 put_sigset(proc, "sc_mask", m_out->m_lc_pm_sigset.set); 911 912 /* 913 * TODO: print some other fields, although it is probably not 914 * useful to print all registers even with verbose > 1? 915 */ 916 put_close_struct(proc, FALSE /*all*/); 917 } 918 919 return CT_NORETURN; 920 } 921 922 static void 923 pm_sigreturn_in(struct trace_proc * proc, const message * __unused m_out, 924 const message * __unused m_in, int failed) 925 { 926 927 if (failed) { 928 put_equals(proc); 929 put_result(proc); 930 } 931 } 932 933 static void 934 put_sysuname_field(struct trace_proc * proc, const char * name, int field) 935 { 936 const char *text = NULL; 937 938 if (!valuesonly) { 939 switch (field) { 940 TEXT(_UTS_ARCH); 941 TEXT(_UTS_KERNEL); 942 TEXT(_UTS_MACHINE); 943 TEXT(_UTS_HOSTNAME); 944 TEXT(_UTS_NODENAME); 945 TEXT(_UTS_RELEASE); 946 TEXT(_UTS_VERSION); 947 TEXT(_UTS_SYSNAME); 948 TEXT(_UTS_BUS); 949 } 950 } 951 952 if (text != NULL) 953 put_field(proc, name, text); 954 else 955 put_value(proc, name, "%d", field); 956 } 957 958 static int 959 pm_sysuname_out(struct trace_proc * proc, const message * m_out) 960 { 961 962 if (!valuesonly && m_out->m_lc_pm_sysuname.req == _UTS_GET) 963 put_field(proc, "req", "_UTS_GET"); 964 else if (!valuesonly && m_out->m_lc_pm_sysuname.req == _UTS_SET) 965 put_field(proc, "req", "_UTS_SET"); 966 else 967 put_value(proc, "req", "%d", m_out->m_lc_pm_sysuname.req); 968 put_sysuname_field(proc, "field", m_out->m_lc_pm_sysuname.field); 969 970 if (m_out->m_lc_pm_sysuname.req == _UTS_GET) 971 return CT_NOTDONE; 972 973 put_buf(proc, "value", PF_STRING, m_out->m_lc_pm_sysuname.value, 974 m_out->m_lc_pm_sysuname.len); 975 put_value(proc, "len", "%zu", m_out->m_lc_pm_sysuname.len); 976 return CT_DONE; 977 } 978 979 static void 980 pm_sysuname_in(struct trace_proc * proc, const message * m_out, 981 const message * m_in, int failed) 982 { 983 984 if (m_out->m_lc_pm_sysuname.req == _UTS_GET) { 985 put_buf(proc, "value", failed | PF_STRING, 986 m_out->m_lc_pm_sysuname.value, m_in->m_type); 987 put_value(proc, "len", "%zu", m_out->m_lc_pm_sysuname.len); 988 put_equals(proc); 989 } 990 put_result(proc); 991 } 992 993 static void 994 put_priority_which(struct trace_proc * proc, const char * name, int which) 995 { 996 const char *text = NULL; 997 998 if (!valuesonly) { 999 switch (which) { 1000 TEXT(PRIO_PROCESS); 1001 TEXT(PRIO_PGRP); 1002 TEXT(PRIO_USER); 1003 } 1004 } 1005 1006 if (text != NULL) 1007 put_field(proc, name, text); 1008 else 1009 put_value(proc, name, "%d", which); 1010 } 1011 1012 static int 1013 pm_getpriority_out(struct trace_proc * proc, const message * m_out) 1014 { 1015 1016 put_priority_which(proc, "which", m_out->m_lc_pm_priority.which); 1017 put_value(proc, "who", "%d", m_out->m_lc_pm_priority.who); 1018 1019 return CT_DONE; 1020 } 1021 1022 static void 1023 pm_getpriority_in(struct trace_proc * proc, const message * __unused m_out, 1024 const message * m_in, int failed) 1025 { 1026 1027 if (!failed) 1028 put_value(proc, NULL, "%d", m_in->m_type + PRIO_MIN); 1029 else 1030 put_result(proc); 1031 } 1032 1033 static int 1034 pm_setpriority_out(struct trace_proc * proc, const message * m_out) 1035 { 1036 1037 put_priority_which(proc, "which", m_out->m_lc_pm_priority.which); 1038 put_value(proc, "who", "%d", m_out->m_lc_pm_priority.who); 1039 put_value(proc, "prio", "%d", m_out->m_lc_pm_priority.prio); 1040 1041 return CT_DONE; 1042 } 1043 1044 static int 1045 pm_gettimeofday_out(struct trace_proc * __unused proc, 1046 const message * __unused m_out) 1047 { 1048 1049 return CT_NOTDONE; 1050 } 1051 1052 static void 1053 put_timespec_as_timeval(struct trace_proc * proc, const char * name, 1054 time_t sec, long nsec) 1055 { 1056 1057 /* No field names within the structure. */ 1058 put_open(proc, name, PF_NONAME, "{", ", "); 1059 1060 put_time(proc, "tv_sec", sec); 1061 put_value(proc, "tv_usec", "%ld", nsec / 1000); 1062 1063 put_close(proc, "}"); 1064 } 1065 1066 static void 1067 pm_gettimeofday_in(struct trace_proc * proc, const message * __unused m_out, 1068 const message * m_in, int failed) 1069 { 1070 1071 if (!failed) { 1072 /* 1073 * The system call returns values which do not match the call 1074 * being made, so just like libc, we have to correct.. 1075 */ 1076 put_timespec_as_timeval(proc, "tp", m_in->m_pm_lc_time.sec, 1077 m_in->m_pm_lc_time.nsec); 1078 } else 1079 put_field(proc, "tp", "&.."); 1080 put_ptr(proc, "tzp", 0); /* not part of the system call (yet) */ 1081 1082 put_equals(proc); 1083 put_result(proc); 1084 } 1085 1086 static int 1087 pm_getsid_out(struct trace_proc * proc, const message * m_out) 1088 { 1089 1090 put_value(proc, "pid", "%d", m_out->m_lc_pm_getsid.pid); 1091 1092 return CT_DONE; 1093 } 1094 1095 static void 1096 put_clockid(struct trace_proc * proc, const char * name, clockid_t clock_id) 1097 { 1098 const char *text = NULL; 1099 1100 if (!valuesonly) { 1101 switch (clock_id) { 1102 TEXT(CLOCK_REALTIME); 1103 #ifdef CLOCK_VIRTUAL 1104 TEXT(CLOCK_VIRTUAL); 1105 #endif 1106 #ifdef CLOCK_PROF 1107 TEXT(CLOCK_PROF); 1108 #endif 1109 TEXT(CLOCK_MONOTONIC); 1110 } 1111 } 1112 1113 if (text != NULL) 1114 put_field(proc, name, text); 1115 else 1116 put_value(proc, name, "%d", clock_id); 1117 } 1118 1119 static void 1120 put_clock_timespec(struct trace_proc * proc, const char * name, int flags, 1121 time_t sec, long nsec) 1122 { 1123 1124 if (flags & PF_FAILED) { 1125 put_field(proc, name, "&.."); 1126 1127 return; 1128 } 1129 1130 /* No field names within the structure. */ 1131 put_open(proc, name, PF_NONAME, "{", ", "); 1132 1133 if (flags & PF_ALT) 1134 put_time(proc, "tv_sec", sec); 1135 else 1136 put_value(proc, "tv_sec", "%"PRId64, sec); 1137 put_value(proc, "tv_nsec", "%ld", nsec); 1138 1139 put_close(proc, "}"); 1140 } 1141 1142 /* This function is shared between clock_getres and clock_gettime. */ 1143 static int 1144 pm_clock_get_out(struct trace_proc * proc, const message * m_out) 1145 { 1146 1147 put_clockid(proc, "clock_id", m_out->m_lc_pm_time.clk_id); 1148 1149 return CT_NOTDONE; 1150 } 1151 1152 static void 1153 pm_clock_getres_in(struct trace_proc * proc, const message * __unused m_out, 1154 const message * m_in, int failed) 1155 { 1156 1157 put_clock_timespec(proc, "res", failed, m_in->m_pm_lc_time.sec, 1158 m_in->m_pm_lc_time.nsec); 1159 put_equals(proc); 1160 put_result(proc); 1161 } 1162 1163 /* 1164 * Same as pm_clock_getres_in, but different field name and the option to print 1165 * at least some results as time strings (in the future). 1166 */ 1167 static void 1168 pm_clock_gettime_in(struct trace_proc * proc, const message * m_out, 1169 const message * m_in, int failed) 1170 { 1171 int flags; 1172 1173 flags = failed; 1174 if (m_out->m_lc_pm_time.clk_id == CLOCK_REALTIME) 1175 flags |= PF_ALT; /* TODO: make this print a time string. */ 1176 1177 put_clock_timespec(proc, "tp", flags, m_in->m_pm_lc_time.sec, 1178 m_in->m_pm_lc_time.nsec); 1179 put_equals(proc); 1180 put_result(proc); 1181 } 1182 1183 static const char * 1184 pm_clock_settime_name(const message * m_out) 1185 { 1186 1187 if (m_out->m_lc_pm_time.now == 0) 1188 return "adjtime"; 1189 else 1190 return "clock_settime"; 1191 } 1192 1193 static int 1194 pm_clock_settime_out(struct trace_proc * proc, const message * m_out) 1195 { 1196 int flags; 1197 1198 /* These two calls just look completely different.. */ 1199 if (m_out->m_lc_pm_time.now == 0) { 1200 put_timespec_as_timeval(proc, "delta", m_out->m_lc_pm_time.sec, 1201 m_out->m_lc_pm_time.nsec); 1202 put_ptr(proc, "odelta", 0); /* not supported on MINIX3 */ 1203 } else { 1204 flags = 0; 1205 if (m_out->m_lc_pm_time.clk_id == CLOCK_REALTIME) 1206 flags |= PF_ALT; 1207 put_clockid(proc, "clock_id", m_out->m_lc_pm_time.clk_id); 1208 put_clock_timespec(proc, "tp", flags, m_out->m_lc_pm_time.sec, 1209 m_out->m_lc_pm_time.nsec); 1210 } 1211 1212 return CT_DONE; 1213 } 1214 1215 static int 1216 pm_getrusage_out(struct trace_proc * proc, const message * m_out) 1217 { 1218 1219 if (!valuesonly && m_out->m_lc_pm_rusage.who == RUSAGE_SELF) 1220 put_field(proc, "who", "RUSAGE_SELF"); 1221 else if (!valuesonly && m_out->m_lc_pm_rusage.who == RUSAGE_CHILDREN) 1222 put_field(proc, "who", "RUSAGE_CHILDREN"); 1223 else 1224 put_value(proc, "who", "%d", m_out->m_lc_pm_rusage.who); 1225 1226 return CT_NOTDONE; 1227 } 1228 1229 static void 1230 pm_getrusage_in(struct trace_proc * proc, const message * m_out, 1231 const message * __unused m_in, int failed) 1232 { 1233 struct rusage buf; 1234 1235 /* Inline; we will certainly not be reusing this anywhere else. */ 1236 if (put_open_struct(proc, "rusage", failed, m_out->m_lc_pm_rusage.addr, 1237 &buf, sizeof(buf))) { 1238 put_struct_timeval(proc, "ru_utime", PF_LOCADDR, 1239 (vir_bytes)&buf.ru_utime); 1240 put_struct_timeval(proc, "ru_stime", PF_LOCADDR, 1241 (vir_bytes)&buf.ru_stime); 1242 1243 if (verbose > 0) 1244 put_value(proc, "ru_nsignals", "%ld", buf.ru_nsignals); 1245 put_close_struct(proc, verbose > 0); 1246 } 1247 put_equals(proc); 1248 put_result(proc); 1249 } 1250 1251 static const struct flags reboot_flags[] = { 1252 FLAG_ZERO(RB_AUTOBOOT), 1253 FLAG(RB_ASKNAME), 1254 FLAG(RB_DUMP), 1255 FLAG_MASK(RB_POWERDOWN, RB_HALT), 1256 FLAG(RB_POWERDOWN), 1257 FLAG(RB_INITNAME), 1258 FLAG(RB_KDB), 1259 FLAG(RB_NOSYNC), 1260 FLAG(RB_RDONLY), 1261 FLAG(RB_SINGLE), 1262 FLAG(RB_STRING), 1263 FLAG(RB_USERCONF), 1264 }; 1265 1266 static int 1267 pm_reboot_out(struct trace_proc * proc, const message * m_out) 1268 { 1269 1270 put_flags(proc, "how", reboot_flags, COUNT(reboot_flags), "0x%x", 1271 m_out->m_lc_pm_reboot.how); 1272 put_ptr(proc, "bootstr", 0); /* not supported on MINIX3 */ 1273 1274 return CT_DONE; 1275 } 1276 1277 static int 1278 pm_svrctl_out(struct trace_proc * proc, const message * m_out) 1279 { 1280 1281 put_ioctl_req(proc, "request", m_out->m_lc_svrctl.request, 1282 TRUE /*is_svrctl*/); 1283 return put_ioctl_arg_out(proc, "arg", m_out->m_lc_svrctl.request, 1284 m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/); 1285 } 1286 1287 static void 1288 pm_svrctl_in(struct trace_proc * proc, const message * m_out, 1289 const message * __unused m_in, int failed) 1290 { 1291 1292 put_ioctl_arg_in(proc, "arg", failed, m_out->m_lc_svrctl.request, 1293 m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/); 1294 } 1295 1296 static int 1297 pm_sprof_out(struct trace_proc * proc, const message * m_out) 1298 { 1299 int freq; 1300 1301 if (!valuesonly && m_out->m_lc_pm_sprof.action == PROF_START) 1302 put_field(proc, "action", "PROF_START"); 1303 else if (!valuesonly && m_out->m_lc_pm_sprof.action == PROF_STOP) 1304 put_field(proc, "action", "PROF_STOP"); 1305 else 1306 put_value(proc, "action", "%d", m_out->m_lc_pm_sprof.action); 1307 1308 put_value(proc, "size", "%zu", m_out->m_lc_pm_sprof.mem_size); 1309 1310 freq = m_out->m_lc_pm_sprof.freq; 1311 if (!valuesonly && freq >= 3 && freq <= 15) /* no constants.. */ 1312 put_value(proc, "freq", "%u /*%uHz*/", freq, 1 << (16 - freq)); 1313 else 1314 put_value(proc, "freq", "%u", freq); 1315 1316 if (!valuesonly && m_out->m_lc_pm_sprof.intr_type == PROF_RTC) 1317 put_field(proc, "type", "PROF_RTC"); 1318 else if (!valuesonly && m_out->m_lc_pm_sprof.intr_type == PROF_NMI) 1319 put_field(proc, "type", "PROF_NMI"); 1320 else 1321 put_value(proc, "type", "%d", m_out->m_lc_pm_sprof.intr_type); 1322 1323 put_ptr(proc, "ctl_ptr", m_out->m_lc_pm_sprof.ctl_ptr); 1324 put_ptr(proc, "mem_ptr", m_out->m_lc_pm_sprof.mem_ptr); 1325 1326 return CT_DONE; 1327 } 1328 1329 #define PM_CALL(c) [((PM_ ## c) - PM_BASE)] 1330 1331 static const struct call_handler pm_map[] = { 1332 PM_CALL(EXIT) = HANDLER("exit", pm_exit_out, default_in), 1333 PM_CALL(FORK) = HANDLER("fork", default_out, default_in), 1334 PM_CALL(WAITPID) = HANDLER("waitpid", pm_waitpid_out, pm_waitpid_in), 1335 PM_CALL(GETPID) = HANDLER("getpid", default_out, pm_getpid_in), 1336 PM_CALL(SETUID) = HANDLER("setuid", pm_setuid_out, default_in), 1337 PM_CALL(GETUID) = HANDLER("getuid", default_out, pm_getuid_in), 1338 PM_CALL(STIME) = HANDLER("stime", pm_stime_out, default_in), 1339 PM_CALL(PTRACE) = HANDLER("ptrace", pm_ptrace_out, pm_ptrace_in), 1340 PM_CALL(SETGROUPS) = HANDLER("setgroups", pm_setgroups_out, 1341 default_in), 1342 PM_CALL(GETGROUPS) = HANDLER("getgroups", pm_getgroups_out, 1343 pm_getgroups_in), 1344 PM_CALL(KILL) = HANDLER("kill", pm_kill_out, default_in), 1345 PM_CALL(SETGID) = HANDLER("setgid", pm_setgid_out, default_in), 1346 PM_CALL(GETGID) = HANDLER("getgid", default_out, pm_getgid_in), 1347 PM_CALL(EXEC) = HANDLER("execve", pm_exec_out, default_in), 1348 PM_CALL(SETSID) = HANDLER("setsid", default_out, default_in), 1349 PM_CALL(GETPGRP) = HANDLER("getpgrp", default_out, default_in), 1350 PM_CALL(ITIMER) = HANDLER_NAME(pm_itimer_name, pm_itimer_out, 1351 pm_itimer_in), 1352 PM_CALL(GETMCONTEXT) = HANDLER("getmcontext", pm_getmcontext_out, 1353 pm_getmcontext_in), 1354 PM_CALL(SETMCONTEXT) = HANDLER("setmcontext", pm_setmcontext_out, 1355 default_in), 1356 PM_CALL(SIGACTION) = HANDLER("sigaction", pm_sigaction_out, 1357 pm_sigaction_in), 1358 PM_CALL(SIGSUSPEND) = HANDLER("sigsuspend", pm_sigsuspend_out, 1359 default_in), 1360 PM_CALL(SIGPENDING) = HANDLER("sigpending", pm_sigpending_out, 1361 pm_sigpending_in), 1362 PM_CALL(SIGPROCMASK) = HANDLER("sigprocmask", pm_sigprocmask_out, 1363 pm_sigprocmask_in), 1364 PM_CALL(SIGRETURN) = HANDLER("sigreturn", pm_sigreturn_out, 1365 pm_sigreturn_in), 1366 PM_CALL(SYSUNAME) = HANDLER("sysuname", pm_sysuname_out, 1367 pm_sysuname_in), 1368 PM_CALL(GETPRIORITY) = HANDLER("getpriority", pm_getpriority_out, 1369 pm_getpriority_in), 1370 PM_CALL(SETPRIORITY) = HANDLER("setpriority", pm_setpriority_out, 1371 default_in), 1372 PM_CALL(GETTIMEOFDAY) = HANDLER("gettimeofday", pm_gettimeofday_out, 1373 pm_gettimeofday_in), 1374 PM_CALL(SETEUID) = HANDLER("seteuid", pm_setuid_out, default_in), 1375 PM_CALL(SETEGID) = HANDLER("setegid", pm_setgid_out, default_in), 1376 PM_CALL(ISSETUGID) = HANDLER("issetugid", default_out, default_in), 1377 PM_CALL(GETSID) = HANDLER("getsid", pm_getsid_out, default_in), 1378 PM_CALL(CLOCK_GETRES) = HANDLER("clock_getres", pm_clock_get_out, 1379 pm_clock_getres_in), 1380 PM_CALL(CLOCK_GETTIME) = HANDLER("clock_gettime", pm_clock_get_out, 1381 pm_clock_gettime_in), 1382 PM_CALL(CLOCK_SETTIME) = HANDLER_NAME(pm_clock_settime_name, 1383 pm_clock_settime_out, default_in), 1384 PM_CALL(GETRUSAGE) = HANDLER("pm_getrusage", pm_getrusage_out, 1385 pm_getrusage_in), 1386 PM_CALL(REBOOT) = HANDLER("reboot", pm_reboot_out, default_in), 1387 PM_CALL(SVRCTL) = HANDLER("pm_svrctl", pm_svrctl_out, pm_svrctl_in), 1388 PM_CALL(SPROF) = HANDLER("sprofile", pm_sprof_out, default_in), 1389 }; 1390 1391 const struct calls pm_calls = { 1392 .endpt = PM_PROC_NR, 1393 .base = PM_BASE, 1394 .map = pm_map, 1395 .count = COUNT(pm_map) 1396 }; 1397