#include "inc.h" #include #include #include #include #include #include #include static int pm_exit_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "status", "%d", m_out->m_lc_pm_exit.status); return CT_NORETURN; } static const struct flags waitpid_options[] = { FLAG(WNOHANG), FLAG(WUNTRACED), FLAG(WALTSIG), FLAG(WALLSIG), FLAG(WNOWAIT), FLAG(WNOZOMBIE), FLAG(WOPTSCHECKED), }; static void put_waitpid_status(struct trace_proc * proc, const char * name, int status) { const char *signame; int sig; /* * There is no suitable set of macros to be used here, so we're going * to invent our own: W_EXITED, W_SIGNALED, and W_STOPPED. Hopefully * they are sufficiently clear even though they don't actually exist. * The code below is downright messy, but it also ensures that no bits * are set unexpectedly in the status. */ if (!valuesonly && WIFEXITED(status) && status == W_EXITCODE(WEXITSTATUS(status), 0)) { put_value(proc, name, "W_EXITED(%d)", WEXITSTATUS(status)); return; } /* WCOREDUMP() actually returns WCOREFLAG or 0, but better safe.. */ if (!valuesonly && WIFSIGNALED(status) && status == (W_EXITCODE(0, WTERMSIG(status)) | (WCOREDUMP(status) ? WCOREFLAG : 0))) { sig = WTERMSIG(status); if ((signame = get_signal_name(sig)) != NULL) put_value(proc, name, "W_SIGNALED(%s)", signame); else put_value(proc, name, "W_SIGNALED(%u)", sig); if (WCOREDUMP(status)) put_text(proc, "|WCOREDUMP"); return; } if (!valuesonly && WIFSTOPPED(status) && status == W_STOPCODE(WSTOPSIG(status))) { sig = WSTOPSIG(status); if ((signame = get_signal_name(sig)) != NULL) put_value(proc, name, "W_STOPPED(%s)", signame); else put_value(proc, name, "W_STOPPED(%u)", sig); return; } /* * If we get here, either valuesonly is enabled or the resulting status * is not one we recognize, for example because extra bits are set. */ put_value(proc, name, "0x%04x", status); } static int pm_waitpid_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "pid", "%d", m_out->m_lc_pm_waitpid.pid); return CT_NOTDONE; } static void pm_waitpid_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { /* * If the result is zero, there is no status to show. Also, since the * status is returned in the result message, we cannot print the user- * given pointer. Instead, upon failure we show "&.." to indicate an * unknown pointer. */ if (!failed && m_in->m_type > 0) put_waitpid_status(proc, "status", m_in->m_pm_lc_waitpid.status); else put_field(proc, "status", "&.."); put_flags(proc, "options", waitpid_options, COUNT(waitpid_options), "0x%x", m_out->m_lc_pm_waitpid.options); put_equals(proc); put_result(proc); } static void pm_getpid_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { put_result(proc); if (!failed) { put_open(proc, NULL, 0, "(", ", "); put_value(proc, "ppid", "%d", m_in->m_pm_lc_getpid.parent_pid); put_close(proc, ")"); } } /* This function is shared between setuid and seteuid. */ static int pm_setuid_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "uid", "%u", m_out->m_lc_pm_setuid.uid); return CT_DONE; } static void pm_getuid_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { put_result(proc); if (!failed) { put_open(proc, NULL, 0, "(", ", "); put_value(proc, "euid", "%u", m_in->m_pm_lc_getuid.euid); put_close(proc, ")"); } } static int pm_stime_out(struct trace_proc * proc, const message * m_out) { put_time(proc, "time", m_out->m_lc_pm_time.sec); return CT_DONE; } static void put_signal(struct trace_proc * proc, const char * name, int sig) { const char *signame; if (!valuesonly && (signame = get_signal_name(sig)) != NULL) put_field(proc, name, signame); else put_value(proc, name, "%d", sig); } static void put_ptrace_req(struct trace_proc * proc, const char * name, int req) { const char *text = NULL; if (!valuesonly) { switch (req) { TEXT(T_STOP); TEXT(T_OK); TEXT(T_ATTACH); TEXT(T_DETACH); TEXT(T_RESUME); TEXT(T_STEP); TEXT(T_SYSCALL); TEXT(T_EXIT); TEXT(T_GETINS); TEXT(T_GETDATA); TEXT(T_GETUSER); TEXT(T_SETINS); TEXT(T_SETDATA); TEXT(T_SETUSER); TEXT(T_SETOPT); TEXT(T_GETRANGE); TEXT(T_SETRANGE); TEXT(T_READB_INS); TEXT(T_WRITEB_INS); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", req); } static void put_struct_ptrace_range(struct trace_proc * proc, const char * name, int flags, vir_bytes addr) { struct ptrace_range pr; if (!put_open_struct(proc, name, flags, addr, &pr, sizeof(pr))) return; if (!valuesonly && pr.pr_space == TS_INS) put_field(proc, "pr_space", "TS_INS"); else if (!valuesonly && pr.pr_space == TS_DATA) put_field(proc, "pr_space", "TS_DATA"); else put_value(proc, "pr_space", "%d", pr.pr_space); put_value(proc, "pr_addr", "0x%lx", pr.pr_addr); put_ptr(proc, "pr_ptr", (vir_bytes)pr.pr_ptr); put_value(proc, "pr_size", "%zu", pr.pr_size); put_close_struct(proc, TRUE /*all*/); } static int pm_ptrace_out(struct trace_proc * proc, const message * m_out) { put_ptrace_req(proc, "req", m_out->m_lc_pm_ptrace.req); put_value(proc, "pid", "%d", m_out->m_lc_pm_ptrace.pid); switch (m_out->m_lc_pm_ptrace.req) { case T_GETINS: case T_GETDATA: case T_GETUSER: case T_READB_INS: put_value(proc, "addr", "0x%lx", m_out->m_lc_pm_ptrace.addr); put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); break; case T_SETINS: case T_SETDATA: case T_SETUSER: case T_WRITEB_INS: put_value(proc, "addr", "0x%lx", m_out->m_lc_pm_ptrace.addr); put_value(proc, "data", "0x%lx", m_out->m_lc_pm_ptrace.data); break; case T_RESUME: case T_STEP: case T_SYSCALL: put_value(proc, "addr", "%ld", m_out->m_lc_pm_ptrace.addr); put_signal(proc, "data", m_out->m_lc_pm_ptrace.data); break; case T_GETRANGE: case T_SETRANGE: put_struct_ptrace_range(proc, "addr", 0, m_out->m_lc_pm_ptrace.addr); put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); break; default: put_value(proc, "addr", "%ld", m_out->m_lc_pm_ptrace.addr); put_value(proc, "data", "%ld", m_out->m_lc_pm_ptrace.data); break; } return CT_DONE; } static void pm_ptrace_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { if (!failed) { switch (m_out->m_lc_pm_ptrace.req) { case T_GETINS: case T_GETDATA: case T_GETUSER: case T_READB_INS: put_value(proc, NULL, "0x%lx", m_in->m_pm_lc_ptrace.data); return; } } put_result(proc); } void put_groups(struct trace_proc * proc, const char * name, int flags, vir_bytes addr, int count) { gid_t groups[NGROUPS_MAX]; int i; if ((flags & PF_FAILED) || valuesonly || count < 0 || count > NGROUPS_MAX || (count > 0 && mem_get_data(proc->pid, addr, groups, count * sizeof(groups[0])) < 0)) { if (flags & PF_LOCADDR) put_field(proc, name, "&.."); else put_ptr(proc, name, addr); return; } put_open(proc, name, PF_NONAME, "[", ", "); for (i = 0; i < count; i++) put_value(proc, NULL, "%u", groups[i]); put_close(proc, "]"); } static int pm_setgroups_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "ngroups", "%d", m_out->m_lc_pm_groups.num); put_groups(proc, "grouplist", 0, m_out->m_lc_pm_groups.ptr, m_out->m_lc_pm_groups.num); return CT_DONE; } static int pm_getgroups_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "ngroups", "%d", m_out->m_lc_pm_groups.num); return CT_NOTDONE; } static void pm_getgroups_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { put_groups(proc, "grouplist", failed, m_out->m_lc_pm_groups.ptr, m_in->m_type); put_equals(proc); put_result(proc); } static int pm_kill_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "pid", "%d", m_out->m_lc_pm_sig.pid); put_signal(proc, "sig", m_out->m_lc_pm_sig.nr); return CT_DONE; } /* This function is shared between setgid and setegid. */ static int pm_setgid_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "gid", "%u", m_out->m_lc_pm_setgid.gid); return CT_DONE; } static void pm_getgid_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { put_result(proc); if (!failed) { put_open(proc, NULL, 0, "(", ", "); put_value(proc, "egid", "%u", m_in->m_pm_lc_getgid.egid); put_close(proc, ")"); } } static int put_frame_string(struct trace_proc * proc, vir_bytes frame, size_t len, vir_bytes addr) { vir_bytes stacktop, offset; /* * The addresses in the frame assume that the process has already been * changed, and the top of the frame is now located at the new process * stack top, which is a hardcoded system-global value. In order to * print the strings, we must convert back each address to its location * within the given frame. */ stacktop = kernel_get_stacktop(); if (addr >= stacktop) return FALSE; offset = stacktop - addr; if (offset >= len) return FALSE; addr = frame + len - offset; /* * TODO: while using put_buf() is highly convenient, it does require at * least one copy operation per printed string. The strings are very * likely to be consecutive in memory, so copying in larger chunks at * once would be preferable. Also, if copying from the frame fails, * put_buf() will print the string address as we corrected it above, * rather than the address as found in the frame. A copy failure would * always be a case of malice on the traced process's behalf, though. */ put_buf(proc, NULL, PF_STRING, addr, len - offset); return TRUE; } /* * Print the contents of the exec frame, which includes both pointers and * actual string data for the arguments and environment variables to be used. * Even though we know that the entire frame is not going to exceed ARG_MAX * bytes, this is too large a size for a static buffer, and we'd like to avoid * allocating large dynamic buffers as well. The situation is complicated by * the fact that any string in the frame may run up to the end of the frame. */ static void put_exec_frame(struct trace_proc * proc, vir_bytes addr, size_t len) { void *argv[64]; size_t off, chunk; unsigned int i, count, max, argv_max, envp_max; int first, ok, nulls; if (valuesonly) { put_ptr(proc, "frame", addr); put_value(proc, "framelen", "%zu", len); return; } if (verbose == 0) { argv_max = 16; envp_max = 0; } else if (verbose == 1) argv_max = envp_max = 64; else argv_max = envp_max = INT_MAX; off = sizeof(int); /* skip 'argc' at the start of the frame */ first = TRUE; ok = TRUE; nulls = 0; count = 0; max = argv_max; do { chunk = sizeof(argv); if (chunk > len - off) chunk = len - off; if (mem_get_data(proc->pid, addr + off, argv, chunk) != 0) break; if (first) { put_open(proc, "argv", PF_NONAME, "[", ", "); first = FALSE; } for (i = 0; i < chunk / sizeof(void *) && ok; i++) { if (argv[i] == NULL) { if (count > max) put_tail(proc, count, max); put_close(proc, "]"); if (nulls++ == 0) { put_open(proc, "envp", PF_NONAME, "[", ", "); count = 0; max = envp_max; } else break; /* two NULL pointers: done! */ } else if (count++ < max) ok = put_frame_string(proc, addr, len, (vir_bytes)argv[i]); } off += chunk; } while (nulls < 2 && ok); /* * Handle failure cases, implied by not reaching the second NULL * in the array. Successful completion is handled in the loop above. * Note that 'ok' is not always cleared on failure, as it is used only * to break out of the outer loop. */ if (first) { put_ptr(proc, "argv", addr + off); put_field(proc, "envp", "&.."); } else if (nulls < 2) { put_tail(proc, 0, 0); put_close(proc, "]"); if (nulls < 1) { put_open(proc, "envp", PF_NONAME, "[", ", "); put_tail(proc, 0, 0); put_close(proc, "]"); } } } static int pm_exec_out(struct trace_proc * proc, const message * m_out) { put_buf(proc, "path", PF_PATH, m_out->m_lc_pm_exec.name, m_out->m_lc_pm_exec.namelen); put_exec_frame(proc, m_out->m_lc_pm_exec.frame, m_out->m_lc_pm_exec.framelen); return CT_NORETURN; } /* The idea is that this function may one day print a human-readable time. */ void put_time(struct trace_proc * proc, const char * name, time_t time) { put_value(proc, name, "%"PRId64, time); } void put_struct_timeval(struct trace_proc * proc, const char * name, int flags, vir_bytes addr) { struct timeval tv; /* No field names; they just make things harder to read. */ if (!put_open_struct(proc, name, flags | PF_NONAME, addr, &tv, sizeof(tv))) return; if (flags & PF_ALT) put_time(proc, "tv_sec", tv.tv_sec); else put_value(proc, "tv_sec", "%"PRId64, tv.tv_sec); put_value(proc, "tv_usec", "%d", tv.tv_usec); put_close_struct(proc, TRUE /*all*/); } static void put_struct_itimerval(struct trace_proc * proc, const char * name, int flags, vir_bytes addr) { struct itimerval it; /* * This used to pass PF_NONAME, but the layout may not be clear enough * without names. It does turn simple alarm(1) calls into rather * lengthy output, though. */ if (!put_open_struct(proc, name, flags, addr, &it, sizeof(it))) return; put_struct_timeval(proc, "it_interval", PF_LOCADDR, (vir_bytes)&it.it_interval); put_struct_timeval(proc, "it_value", PF_LOCADDR, (vir_bytes)&it.it_value); put_close_struct(proc, TRUE /*all*/); } static void put_itimer_which(struct trace_proc * proc, const char * name, int which) { const char *text = NULL; if (!valuesonly) { switch (which) { TEXT(ITIMER_REAL); TEXT(ITIMER_VIRTUAL); TEXT(ITIMER_PROF); TEXT(ITIMER_MONOTONIC); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", which); } static const char * pm_itimer_name(const message * m_out) { return (m_out->m_lc_pm_itimer.value != 0) ? "setitimer" : "getitimer"; } static int pm_itimer_out(struct trace_proc * proc, const message * m_out) { put_itimer_which(proc, "which", m_out->m_lc_pm_itimer.which); if (m_out->m_lc_pm_itimer.value != 0) { put_struct_itimerval(proc, "value", 0, m_out->m_lc_pm_itimer.value); /* * If there will be no old values to print, finish the call * now. For setitimer only; getitimer may not pass NULL. */ if (m_out->m_lc_pm_itimer.ovalue == 0) { put_ptr(proc, "ovalue", 0); return CT_DONE; } } return CT_NOTDONE; } static void pm_itimer_in(struct trace_proc * proc, const message * m_out, const message * __unused m_in, int failed) { if (m_out->m_lc_pm_itimer.value == 0 || m_out->m_lc_pm_itimer.ovalue != 0) { put_struct_itimerval(proc, (m_out->m_lc_pm_itimer.value != 0) ? "ovalue" : "value", failed, m_out->m_lc_pm_itimer.ovalue); put_equals(proc); } put_result(proc); } static void put_struct_mcontext(struct trace_proc * proc, const char * name, int flags, vir_bytes addr) { mcontext_t ctx; if (!put_open_struct(proc, name, flags, addr, &ctx, sizeof(ctx))) return; /* * TODO: print actual fields. Then again, the ones that are saved and * restored (FPU state) are hardly interesting enough to print.. */ put_close_struct(proc, FALSE /*all*/); } static int pm_getmcontext_out(struct trace_proc * proc, const message * m_out) { return CT_NOTDONE; } static void pm_getmcontext_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { put_struct_mcontext(proc, "mcp", failed, m_out->m_lc_pm_mcontext.ctx); put_equals(proc); put_result(proc); } static int pm_setmcontext_out(struct trace_proc * proc, const message * m_out) { put_struct_mcontext(proc, "mcp", 0, m_out->m_lc_pm_mcontext.ctx); return CT_DONE; } static void put_sigset(struct trace_proc * proc, const char * name, sigset_t set) { const char *signame; unsigned int count, unknown; int sig, invert; /* * First decide whether we should print a normal or an inverted mask. * Unfortunately, depending on the place, a filled set may or may not * have bits outside the 1..NSIG range set. Therefore, we ignore the * bits outside this range entirely, and use simple heuristics to * decide whether to show an inverted set. If we know all the signal * names for either set and not the other, show that one; otherwise, * show an inverted mask if at least 3/4th of the bits are set. */ count = 0; unknown = 0; for (sig = 1; sig < NSIG; sig++) { if (sigismember(&set, sig)) count++; if (get_signal_name(sig) == NULL) unknown |= 1 << !!sigismember(&set, sig); } if (unknown == 1 /*for unset bit*/ || unknown == 2 /*for set bit*/) invert = unknown - 1; else invert = (count >= (NSIG - 1) * 3 / 4); put_open(proc, name, PF_NONAME, invert ? "~[" : "[", " "); for (sig = 1; sig < NSIG; sig++) { /* Note that sigismember() may not strictly return 0 or 1.. */ if (!sigismember(&set, sig) != invert) continue; if ((signame = get_signal_name(sig)) != NULL) { /* Skip the "SIG" prefix for brevity. */ if (!strncmp(signame, "SIG", 3)) put_field(proc, NULL, &signame[3]); else put_field(proc, NULL, signame); } else put_value(proc, NULL, "%d", sig); } put_close(proc, "]"); } static const struct flags sa_flags[] = { FLAG(SA_ONSTACK), FLAG(SA_RESTART), FLAG(SA_RESETHAND), FLAG(SA_NODEFER), FLAG(SA_NOCLDSTOP), FLAG(SA_NOCLDWAIT), #ifdef SA_SIGINFO FLAG(SA_SIGINFO), #endif FLAG(SA_NOKERNINFO) }; static void put_sa_handler(struct trace_proc * proc, const char * name, vir_bytes handler) { const char *text = NULL; if (!valuesonly) { switch ((int)handler) { case (int)SIG_DFL: text = "SIG_DFL"; break; case (int)SIG_IGN: text = "SIG_IGN"; break; case (int)SIG_HOLD: text = "SIG_HOLD"; break; } } if (text != NULL) put_field(proc, name, text); else put_ptr(proc, name, handler); } static void put_struct_sigaction(struct trace_proc * proc, const char * name, int flags, vir_bytes addr) { struct sigaction sa; if (!put_open_struct(proc, name, flags, addr, &sa, sizeof(sa))) return; put_sa_handler(proc, "sa_handler", (vir_bytes)sa.sa_handler); if (verbose > 1) put_sigset(proc, "sa_mask", sa.sa_mask); /* A somewhat lame attempt to reduce noise a bit. */ if ((sa.sa_flags & ~(SA_ONSTACK | SA_RESTART | SA_RESETHAND | SA_NODEFER)) != 0 || sa.sa_handler != SIG_DFL || verbose > 0) put_flags(proc, "sa_flags", sa_flags, COUNT(sa_flags), "0x%x", sa.sa_flags); put_close_struct(proc, verbose > 1); } static int pm_sigaction_out(struct trace_proc * proc, const message * m_out) { put_signal(proc, "signal", m_out->m_lc_pm_sig.nr); put_struct_sigaction(proc, "act", 0, m_out->m_lc_pm_sig.act); /* If there will be no old values to print, finish the call now. */ if (m_out->m_lc_pm_sig.oact == 0) { put_ptr(proc, "oact", 0); return CT_DONE; } else return CT_NOTDONE; } static void pm_sigaction_in(struct trace_proc * proc, const message * m_out, const message * __unused m_in, int failed) { if (m_out->m_lc_pm_sig.oact != 0) { put_struct_sigaction(proc, "oact", failed, m_out->m_lc_pm_sig.oact); put_equals(proc); } put_result(proc); } static int pm_sigsuspend_out(struct trace_proc * proc, const message * m_out) { put_sigset(proc, "set", m_out->m_lc_pm_sigset.set); return CT_DONE; } static int pm_sigpending_out(struct trace_proc * __unused proc, const message * __unused m_out) { return CT_NOTDONE; } static void pm_sigpending_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { if (!failed) put_sigset(proc, "set", m_in->m_pm_lc_sigset.set); else put_field(proc, "set", "&.."); put_equals(proc); put_result(proc); } static void put_sigprocmask_how(struct trace_proc * proc, const char * name, int how) { const char *text = NULL; if (!valuesonly) { switch (how) { case SIG_INQUIRE: /* pseudocode, print something else */ TEXT(SIG_BLOCK); TEXT(SIG_UNBLOCK); TEXT(SIG_SETMASK); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", how); } static int pm_sigprocmask_out(struct trace_proc * proc, const message * m_out) { put_sigprocmask_how(proc, "how", m_out->m_lc_pm_sigset.how); if (m_out->m_lc_pm_sigset.how == SIG_INQUIRE) put_ptr(proc, "set", 0); else put_sigset(proc, "set", m_out->m_lc_pm_sigset.set); return CT_NOTDONE; } static void pm_sigprocmask_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { if (!failed) put_sigset(proc, "oset", m_in->m_pm_lc_sigset.set); else put_field(proc, "oset", "&.."); put_equals(proc); put_result(proc); } static int pm_sigreturn_out(struct trace_proc * proc, const message * m_out) { struct sigcontext scp; if (put_open_struct(proc, "scp", 0, m_out->m_lc_pm_sigset.ctx, &scp, sizeof(scp))) { if (verbose == 1) { #if defined(__i386__) put_ptr(proc, "sc_eip", scp.sc_eip); put_ptr(proc, "sc_esp", scp.sc_esp); #elif defined(__arm__) put_ptr(proc, "sc_pc", scp.sc_pc); put_ptr(proc, "sc_usr_sp", scp.sc_usr_sp); #endif } /* * We deliberately print the signal set from the message rather * than from the structure, since in theory they may be * different and PM uses the one from the message only. */ put_sigset(proc, "sc_mask", m_out->m_lc_pm_sigset.set); /* * TODO: print some other fields, although it is probably not * useful to print all registers even with verbose > 1? */ put_close_struct(proc, FALSE /*all*/); } return CT_NORETURN; } static void pm_sigreturn_in(struct trace_proc * proc, const message * __unused m_out, const message * __unused m_in, int failed) { if (failed) { put_equals(proc); put_result(proc); } } static void put_sysuname_field(struct trace_proc * proc, const char * name, int field) { const char *text = NULL; if (!valuesonly) { switch (field) { TEXT(_UTS_ARCH); TEXT(_UTS_KERNEL); TEXT(_UTS_MACHINE); TEXT(_UTS_HOSTNAME); TEXT(_UTS_NODENAME); TEXT(_UTS_RELEASE); TEXT(_UTS_VERSION); TEXT(_UTS_SYSNAME); TEXT(_UTS_BUS); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", field); } static int pm_sysuname_out(struct trace_proc * proc, const message * m_out) { if (!valuesonly && m_out->m_lc_pm_sysuname.req == _UTS_GET) put_field(proc, "req", "_UTS_GET"); else if (!valuesonly && m_out->m_lc_pm_sysuname.req == _UTS_SET) put_field(proc, "req", "_UTS_SET"); else put_value(proc, "req", "%d", m_out->m_lc_pm_sysuname.req); put_sysuname_field(proc, "field", m_out->m_lc_pm_sysuname.field); if (m_out->m_lc_pm_sysuname.req == _UTS_GET) return CT_NOTDONE; put_buf(proc, "value", PF_STRING, m_out->m_lc_pm_sysuname.value, m_out->m_lc_pm_sysuname.len); put_value(proc, "len", "%zu", m_out->m_lc_pm_sysuname.len); return CT_DONE; } static void pm_sysuname_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { if (m_out->m_lc_pm_sysuname.req == _UTS_GET) { put_buf(proc, "value", failed | PF_STRING, m_out->m_lc_pm_sysuname.value, m_in->m_type); put_value(proc, "len", "%zu", m_out->m_lc_pm_sysuname.len); put_equals(proc); } put_result(proc); } static void put_priority_which(struct trace_proc * proc, const char * name, int which) { const char *text = NULL; if (!valuesonly) { switch (which) { TEXT(PRIO_PROCESS); TEXT(PRIO_PGRP); TEXT(PRIO_USER); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", which); } static int pm_getpriority_out(struct trace_proc * proc, const message * m_out) { put_priority_which(proc, "which", m_out->m_lc_pm_priority.which); put_value(proc, "who", "%d", m_out->m_lc_pm_priority.who); return CT_DONE; } static void pm_getpriority_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { if (!failed) put_value(proc, NULL, "%d", m_in->m_type + PRIO_MIN); else put_result(proc); } static int pm_setpriority_out(struct trace_proc * proc, const message * m_out) { put_priority_which(proc, "which", m_out->m_lc_pm_priority.which); put_value(proc, "who", "%d", m_out->m_lc_pm_priority.who); put_value(proc, "prio", "%d", m_out->m_lc_pm_priority.prio); return CT_DONE; } static int pm_gettimeofday_out(struct trace_proc * __unused proc, const message * __unused m_out) { return CT_NOTDONE; } static void put_timespec_as_timeval(struct trace_proc * proc, const char * name, time_t sec, long nsec) { /* No field names within the structure. */ put_open(proc, name, PF_NONAME, "{", ", "); put_time(proc, "tv_sec", sec); put_value(proc, "tv_usec", "%ld", nsec / 1000); put_close(proc, "}"); } static void pm_gettimeofday_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { if (!failed) { /* * The system call returns values which do not match the call * being made, so just like libc, we have to correct.. */ put_timespec_as_timeval(proc, "tp", m_in->m_pm_lc_time.sec, m_in->m_pm_lc_time.nsec); } else put_field(proc, "tp", "&.."); put_ptr(proc, "tzp", 0); /* not part of the system call (yet) */ put_equals(proc); put_result(proc); } static int pm_getsid_out(struct trace_proc * proc, const message * m_out) { put_value(proc, "pid", "%d", m_out->m_lc_pm_getsid.pid); return CT_DONE; } static void put_clockid(struct trace_proc * proc, const char * name, clockid_t clock_id) { const char *text = NULL; if (!valuesonly) { switch (clock_id) { TEXT(CLOCK_REALTIME); #ifdef CLOCK_VIRTUAL TEXT(CLOCK_VIRTUAL); #endif #ifdef CLOCK_PROF TEXT(CLOCK_PROF); #endif TEXT(CLOCK_MONOTONIC); } } if (text != NULL) put_field(proc, name, text); else put_value(proc, name, "%d", clock_id); } static void put_clock_timespec(struct trace_proc * proc, const char * name, int flags, time_t sec, long nsec) { if (flags & PF_FAILED) { put_field(proc, name, "&.."); return; } /* No field names within the structure. */ put_open(proc, name, PF_NONAME, "{", ", "); if (flags & PF_ALT) put_time(proc, "tv_sec", sec); else put_value(proc, "tv_sec", "%"PRId64, sec); put_value(proc, "tv_nsec", "%ld", nsec); put_close(proc, "}"); } /* This function is shared between clock_getres and clock_gettime. */ static int pm_clock_get_out(struct trace_proc * proc, const message * m_out) { put_clockid(proc, "clock_id", m_out->m_lc_pm_time.clk_id); return CT_NOTDONE; } static void pm_clock_getres_in(struct trace_proc * proc, const message * __unused m_out, const message * m_in, int failed) { put_clock_timespec(proc, "res", failed, m_in->m_pm_lc_time.sec, m_in->m_pm_lc_time.nsec); put_equals(proc); put_result(proc); } /* * Same as pm_clock_getres_in, but different field name and the option to print * at least some results as time strings (in the future). */ static void pm_clock_gettime_in(struct trace_proc * proc, const message * m_out, const message * m_in, int failed) { int flags; flags = failed; if (m_out->m_lc_pm_time.clk_id == CLOCK_REALTIME) flags |= PF_ALT; /* TODO: make this print a time string. */ put_clock_timespec(proc, "tp", flags, m_in->m_pm_lc_time.sec, m_in->m_pm_lc_time.nsec); put_equals(proc); put_result(proc); } static const char * pm_clock_settime_name(const message * m_out) { if (m_out->m_lc_pm_time.now == 0) return "adjtime"; else return "clock_settime"; } static int pm_clock_settime_out(struct trace_proc * proc, const message * m_out) { int flags; /* These two calls just look completely different.. */ if (m_out->m_lc_pm_time.now == 0) { put_timespec_as_timeval(proc, "delta", m_out->m_lc_pm_time.sec, m_out->m_lc_pm_time.nsec); put_ptr(proc, "odelta", 0); /* not supported on MINIX3 */ } else { flags = 0; if (m_out->m_lc_pm_time.clk_id == CLOCK_REALTIME) flags |= PF_ALT; put_clockid(proc, "clock_id", m_out->m_lc_pm_time.clk_id); put_clock_timespec(proc, "tp", flags, m_out->m_lc_pm_time.sec, m_out->m_lc_pm_time.nsec); } return CT_DONE; } static int pm_getrusage_out(struct trace_proc * proc, const message * m_out) { if (!valuesonly && m_out->m_lc_pm_rusage.who == RUSAGE_SELF) put_field(proc, "who", "RUSAGE_SELF"); else if (!valuesonly && m_out->m_lc_pm_rusage.who == RUSAGE_CHILDREN) put_field(proc, "who", "RUSAGE_CHILDREN"); else put_value(proc, "who", "%d", m_out->m_lc_pm_rusage.who); return CT_NOTDONE; } static void pm_getrusage_in(struct trace_proc * proc, const message * m_out, const message * __unused m_in, int failed) { struct rusage buf; /* Inline; we will certainly not be reusing this anywhere else. */ if (put_open_struct(proc, "rusage", failed, m_out->m_lc_pm_rusage.addr, &buf, sizeof(buf))) { put_struct_timeval(proc, "ru_utime", PF_LOCADDR, (vir_bytes)&buf.ru_utime); put_struct_timeval(proc, "ru_stime", PF_LOCADDR, (vir_bytes)&buf.ru_stime); if (verbose > 0) { put_value(proc, "ru_maxrss", "%ld", buf.ru_maxrss); put_value(proc, "ru_minflt", "%ld", buf.ru_minflt); put_value(proc, "ru_majflt", "%ld", buf.ru_majflt); } put_close_struct(proc, verbose > 0); } put_equals(proc); put_result(proc); } static const struct flags reboot_flags[] = { FLAG_ZERO(RB_AUTOBOOT), FLAG(RB_ASKNAME), FLAG(RB_DUMP), FLAG_MASK(RB_POWERDOWN, RB_HALT), FLAG(RB_POWERDOWN), FLAG(RB_INITNAME), FLAG(RB_KDB), FLAG(RB_NOSYNC), FLAG(RB_RDONLY), FLAG(RB_SINGLE), FLAG(RB_STRING), FLAG(RB_USERCONF), }; static int pm_reboot_out(struct trace_proc * proc, const message * m_out) { put_flags(proc, "how", reboot_flags, COUNT(reboot_flags), "0x%x", m_out->m_lc_pm_reboot.how); put_ptr(proc, "bootstr", 0); /* not supported on MINIX3 */ return CT_DONE; } static int pm_svrctl_out(struct trace_proc * proc, const message * m_out) { put_ioctl_req(proc, "request", m_out->m_lc_svrctl.request, TRUE /*is_svrctl*/); return put_ioctl_arg_out(proc, "arg", m_out->m_lc_svrctl.request, m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/); } static void pm_svrctl_in(struct trace_proc * proc, const message * m_out, const message * __unused m_in, int failed) { put_ioctl_arg_in(proc, "arg", failed, m_out->m_lc_svrctl.request, m_out->m_lc_svrctl.arg, TRUE /*is_svrctl*/); } static int pm_sprof_out(struct trace_proc * proc, const message * m_out) { int freq; if (!valuesonly && m_out->m_lc_pm_sprof.action == PROF_START) put_field(proc, "action", "PROF_START"); else if (!valuesonly && m_out->m_lc_pm_sprof.action == PROF_STOP) put_field(proc, "action", "PROF_STOP"); else put_value(proc, "action", "%d", m_out->m_lc_pm_sprof.action); put_value(proc, "size", "%zu", m_out->m_lc_pm_sprof.mem_size); freq = m_out->m_lc_pm_sprof.freq; if (!valuesonly && freq >= 3 && freq <= 15) /* no constants.. */ put_value(proc, "freq", "%u /*%uHz*/", freq, 1 << (16 - freq)); else put_value(proc, "freq", "%u", freq); if (!valuesonly && m_out->m_lc_pm_sprof.intr_type == PROF_RTC) put_field(proc, "type", "PROF_RTC"); else if (!valuesonly && m_out->m_lc_pm_sprof.intr_type == PROF_NMI) put_field(proc, "type", "PROF_NMI"); else put_value(proc, "type", "%d", m_out->m_lc_pm_sprof.intr_type); put_ptr(proc, "ctl_ptr", m_out->m_lc_pm_sprof.ctl_ptr); put_ptr(proc, "mem_ptr", m_out->m_lc_pm_sprof.mem_ptr); return CT_DONE; } #define PM_CALL(c) [((PM_ ## c) - PM_BASE)] static const struct call_handler pm_map[] = { PM_CALL(EXIT) = HANDLER("exit", pm_exit_out, default_in), PM_CALL(FORK) = HANDLER("fork", default_out, default_in), PM_CALL(WAITPID) = HANDLER("waitpid", pm_waitpid_out, pm_waitpid_in), PM_CALL(GETPID) = HANDLER("getpid", default_out, pm_getpid_in), PM_CALL(SETUID) = HANDLER("setuid", pm_setuid_out, default_in), PM_CALL(GETUID) = HANDLER("getuid", default_out, pm_getuid_in), PM_CALL(STIME) = HANDLER("stime", pm_stime_out, default_in), PM_CALL(PTRACE) = HANDLER("ptrace", pm_ptrace_out, pm_ptrace_in), PM_CALL(SETGROUPS) = HANDLER("setgroups", pm_setgroups_out, default_in), PM_CALL(GETGROUPS) = HANDLER("getgroups", pm_getgroups_out, pm_getgroups_in), PM_CALL(KILL) = HANDLER("kill", pm_kill_out, default_in), PM_CALL(SETGID) = HANDLER("setgid", pm_setgid_out, default_in), PM_CALL(GETGID) = HANDLER("getgid", default_out, pm_getgid_in), PM_CALL(EXEC) = HANDLER("execve", pm_exec_out, default_in), PM_CALL(SETSID) = HANDLER("setsid", default_out, default_in), PM_CALL(GETPGRP) = HANDLER("getpgrp", default_out, default_in), PM_CALL(ITIMER) = HANDLER_NAME(pm_itimer_name, pm_itimer_out, pm_itimer_in), PM_CALL(GETMCONTEXT) = HANDLER("getmcontext", pm_getmcontext_out, pm_getmcontext_in), PM_CALL(SETMCONTEXT) = HANDLER("setmcontext", pm_setmcontext_out, default_in), PM_CALL(SIGACTION) = HANDLER("sigaction", pm_sigaction_out, pm_sigaction_in), PM_CALL(SIGSUSPEND) = HANDLER("sigsuspend", pm_sigsuspend_out, default_in), PM_CALL(SIGPENDING) = HANDLER("sigpending", pm_sigpending_out, pm_sigpending_in), PM_CALL(SIGPROCMASK) = HANDLER("sigprocmask", pm_sigprocmask_out, pm_sigprocmask_in), PM_CALL(SIGRETURN) = HANDLER("sigreturn", pm_sigreturn_out, pm_sigreturn_in), PM_CALL(SYSUNAME) = HANDLER("sysuname", pm_sysuname_out, pm_sysuname_in), PM_CALL(GETPRIORITY) = HANDLER("getpriority", pm_getpriority_out, pm_getpriority_in), PM_CALL(SETPRIORITY) = HANDLER("setpriority", pm_setpriority_out, default_in), PM_CALL(GETTIMEOFDAY) = HANDLER("gettimeofday", pm_gettimeofday_out, pm_gettimeofday_in), PM_CALL(SETEUID) = HANDLER("seteuid", pm_setuid_out, default_in), PM_CALL(SETEGID) = HANDLER("setegid", pm_setgid_out, default_in), PM_CALL(ISSETUGID) = HANDLER("issetugid", default_out, default_in), PM_CALL(GETSID) = HANDLER("getsid", pm_getsid_out, default_in), PM_CALL(CLOCK_GETRES) = HANDLER("clock_getres", pm_clock_get_out, pm_clock_getres_in), PM_CALL(CLOCK_GETTIME) = HANDLER("clock_gettime", pm_clock_get_out, pm_clock_gettime_in), PM_CALL(CLOCK_SETTIME) = HANDLER_NAME(pm_clock_settime_name, pm_clock_settime_out, default_in), PM_CALL(GETRUSAGE) = HANDLER("pm_getrusage", pm_getrusage_out, pm_getrusage_in), PM_CALL(REBOOT) = HANDLER("reboot", pm_reboot_out, default_in), PM_CALL(SVRCTL) = HANDLER("pm_svrctl", pm_svrctl_out, pm_svrctl_in), PM_CALL(SPROF) = HANDLER("sprofile", pm_sprof_out, default_in), }; const struct calls pm_calls = { .endpt = PM_PROC_NR, .base = PM_BASE, .map = pm_map, .count = COUNT(pm_map) };