bool handle_wait(pid_t pid, int status, int count, int *continued, int *terminated) { unsigned int alarm_remaining_sec = waitproc_flags_test(WAITPROC_FLAG_ALARMSET) ? alarm(0) : 0; if (pid != -1) { struct flagged_int *p = get_flagged_int(&waitproc_options.pids, pid); assert(p); if (WIFSTOPPED(status)) { long r = WSTOPSIG(status); assert(p->valid); switch (r) { case SIGSTOP: if (p->state == STATE_ATTACHED) { p->state = STATE_CONTINUED; r = 0; assert(*continued < count); if (++(*continued) == count) set_alarm(); } else { r = SIGCONT; } break; case SIGTSTP: r = SIGCONT; break; default: // forward the signal break; } if (r >= 0) { assert(p->state >= STATE_ATTACHED); r = ptrace(PTRACE_CONT, pid, 0, r); assert(r == 0); } } else if (WIFEXITED(status) || WIFSIGNALED(status)) { assert(p->valid && p->state < STATE_TERMINATED); p->state = STATE_TERMINATED; p->valid = false; print_terminated_process(pid); if (++(*terminated) == count) return false; } } else switch (errno) { case EINTR: if (waitproc_options.last_signal == SIGALRM) { waitproc_options.last_signal = SIGINVALID; if (DEBUG || !alarm_remaining_sec) return false; } else { fputs("Interrupted by unexpected signal\n", stderr); UNEXPECTED_STATE(); } break; case ECHILD: *terminated = count; return false; default: UNEXPECTED_STATE(); break; } assert(*terminated < count); if (alarm_remaining_sec) alarm(alarm_remaining_sec); return true; }
int main(int argc, char **argv) { int sig, status, exit_code; struct child son; /* Parse arguments */ if (argc < 2) { fprintf(stderr, "Usage: %s program [argument...]\n", argv[0]); return EXIT_FAILURE; } /* Fork */ if ((son.pid = fork()) < 0) { perror("fork"); return EXIT_FAILURE; } else if (!son.pid) { /* child */ /* Set up for tracing */ if (!pink_trace_me()) { perror("pink_trace_me"); _exit(EXIT_FAILURE); } /* Stop and let the parent continue the execution. */ kill(getpid(), SIGSTOP); ++argv; execvp(argv[0], argv); perror("execvp"); _exit(-1); } else { waitpid(son.pid, &status, 0); assert(WIFSTOPPED(status)); assert(WSTOPSIG(status) == SIGSTOP); /* Figure out the bitness of the traced child. */ son.bitness = pink_bitness_get(son.pid); if (son.bitness == PINK_BITNESS_UNKNOWN) err(1, "pink_bitness_get"); printf("Child %i runs in %s mode\n", son.pid, pink_bitness_name(son.bitness)); son.inexecve = son.insyscall = false; son.printret = true; sig = exit_code = 0; for (;;) { /* At this point the traced child is stopped and needs * to be resumed. */ if (!pink_trace_syscall(son.pid, sig)) { perror("pink_trace_syscall"); return (errno == ESRCH) ? 0 : 1; } sig = 0; /* Wait for the child */ if ((son.pid = waitpid(son.pid, &status, 0)) < 0) { perror("waitpid"); return (errno == ECHILD) ? 0 : 1; } /* Check the event */ if (WIFSTOPPED(status)) { if (WSTOPSIG(status) == SIGTRAP) handle_sigtrap(&son); else { /* Child received a genuine signal. * Send it to the child. */ sig = WSTOPSIG(status); } } else if (WIFEXITED(status)) { exit_code = WEXITSTATUS(status); printf("Child %i exited normally with return code %d\n", son.pid, exit_code); break; } else if (WIFSIGNALED(status)) { exit_code = 128 + WTERMSIG(status); printf("Child %i exited with signal %d\n", son.pid, WTERMSIG(status)); break; } } return exit_code; } }
/* Handle breakpoints - we expect to see breakpoints in the dynamic linker * (which we set ourselves) as well as profiler marks (embedded in the * profiled application's code). */ static int ProcessBreakpoint( pid_t pid, u_long ip ) { static int ld_state = RT_CONSISTENT; // This ought to be per-pid int ptrace_sig = 0; #if defined( MD_x86 ) user_regs_struct regs; // on x86, when breakpoint was hit the EIP points to the next // instruction, so we must be careful ptrace( PTRACE_GETREGS, pid, NULL, ®s ); if( ip == Rdebug.r_brk + sizeof( opcode_type ) ) { #elif defined( MD_ppc ) if( ip == Rdebug.r_brk ) { #endif opcode_type brk_opcode = BRKPOINT; int status; int ret; /* The dynamic linker breakpoint was hit, meaning that * libraries are being loaded or unloaded. This gets a bit * tricky because we must restore the original code that was * at the breakpoint and execute it, but we still want to * keep the breakpoint. */ if( WriteMem( pid, &saved_opcode, Rdebug.r_brk, sizeof( saved_opcode ) ) != sizeof( saved_opcode ) ) printf( "WriteMem() #1 failed\n" ); ReadMem( pid, &Rdebug, (addr_off)DbgRdebug, sizeof( Rdebug ) ); dbg_printf( "ld breakpoint hit, state is " ); switch( Rdebug.r_state ) { case RT_ADD: dbg_printf( "RT_ADD\n" ); AddLibrary( pid, Rdebug.r_map ); ld_state = RT_ADD; break; case RT_DELETE: dbg_printf( "RT_DELETE\n" ); ld_state = RT_DELETE; break; case RT_CONSISTENT: dbg_printf( "RT_CONSISTENT\n" ); if( ld_state == RT_DELETE ) RemoveLibrary( pid, Rdebug.r_map ); ld_state = RT_CONSISTENT; break; default: dbg_printf( "error!\n" ); break; } #if defined( MD_x86 ) regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif // unset breakpoint, single step, re-set breakpoint if( ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)ptrace_sig ) < 0 ) perror( "ptrace()" ); do { // have to loop since SIGALRM can interrupt us here ret = waitpid( pid, &status, 0 ); } while( (ret < 0) && (errno == EINTR) ); if( ret == -1) perror( "waitpid()" ); if( WriteMem( pid, &brk_opcode, Rdebug.r_brk, sizeof( brk_opcode ) ) != sizeof( brk_opcode ) ) dbg_printf( "WriteMem() #2 failed with errno %d for pid %d, %d bytes (at %p)!\n", errno, pid, sizeof( brk_opcode ), Rdebug.r_brk ); return( TRUE ); // indicate this was our breakpoint } else { dbg_printf( "Not an ld breakpoint, assuming mark\n" ); #if defined( MD_x86 ) if( ProcessMark( pid, ®s ) ) return( TRUE ); #endif } return( FALSE ); } /* * Real time signal (SIGALRM) handler. All we really need to do is wake up * periodically to interrupt waitpid(), the signal handler need not do much * at all. */ static void alarm_handler( int signo ) { TimerTicked = TRUE; } /* Install periodic real time alarm signal */ static void InstSigHandler( int msec_period ) { struct sigaction sigalrm; struct itimerval timer; sigalrm.sa_handler = (void *)alarm_handler; sigemptyset( &sigalrm.sa_mask ); sigalrm.sa_flags = 0; sigaction( SIGALRM, &sigalrm, NULL ); timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = msec_period * 1000; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = msec_period * 1000; if( setitimer( ITIMER_REAL, &timer, NULL ) ) { InternalError( MsgArray[MSG_SAMPLE_6 - ERR_FIRST_MESSAGE] ); } } /* * Main sampler loop. We run the profiled application under the control of * ptrace(). The sampler installs a SIGALRM handler which ticks at the desired * rate. Whenever the SIGALRM interrupts our own waitpid(), we send a SIGSTOP * to the profiled app and when the child app notifies us of the SIGSTOP, we * remember the current execution point and continue the profiled app. Note * that we may miss some ticks but this is not a problem - the ticks don't * even need to be regular to provide usable results. */ static void SampleLoop( pid_t pid ) { static int ptrace_sig = 0; static int do_cont = TRUE; int status; user_regs_struct regs; bool sample_continue = TRUE; int ret; opcode_type brk_opcode = BRKPOINT; TimerTicked = FALSE; InstSigHandler( SleepTime ); do { if( do_cont && ptrace( PTRACE_CONT, pid, NULL, (void *)ptrace_sig ) == -1) perror( "ptrace()" ); ret = waitpid( pid, &status, 0 ); if( (ret < 0) && (errno == EINTR) ) { /* did we get woken up by SIGALRM? */ if( TimerTicked ) { TimerTicked = FALSE; /* interrupt child process - next waitpid() will see this */ kill( pid, SIGSTOP ); } else { dbg_printf( "who the hell interrupted waitpid()?\n" ); } do_cont = FALSE; continue; } if( ret < 0 ) perror( "waitpid()" ); do_cont = TRUE; /* record current execution point */ #if defined( MD_x86 ) ptrace( PTRACE_GETREGS, pid, NULL, ®s ); #elif defined( MD_ppc ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_NIP, NULL ); #endif if( WIFSTOPPED( status ) ) { /* If debuggee has dynamic section, try getting the r_debug struct * every time the child process stops. The r_debug data may not be * available immediately after the child process first loads. */ if( !HaveRdebug && (DbgDyn != NULL) ) { if( Get_ld_info( pid, DbgDyn, &Rdebug, &DbgRdebug ) ) { AddLibrary( pid, Rdebug.r_map ); HaveRdebug = TRUE; /* Set a breakpoint in dynamic linker. That way we can be * informed on dynamic library load/unload events. */ ReadMem( pid, &saved_opcode, Rdebug.r_brk, sizeof( saved_opcode ) ); dbg_printf( "setting ld breakpoint at %p, old opcode was %X\n", Rdebug.r_brk, saved_opcode ); WriteMem( pid, &brk_opcode, Rdebug.r_brk, sizeof( brk_opcode ) ); } } sample_continue = FALSE; switch( (ptrace_sig = WSTOPSIG( status )) ) { case SIGSEGV: dbg_printf( "SIGSEGV at %p\n", regs.eip ); sample_continue = TRUE; break; case SIGILL: dbg_printf( "SIGILL at %p\n", regs.eip ); sample_continue = TRUE; break; case SIGABRT: dbg_printf( "SIGABRT at %p\n", regs.eip ); sample_continue = TRUE; break; case SIGINT: dbg_printf( "SIGINT at %p\n", regs.eip ); sample_continue = TRUE; break; case SIGTRAP: dbg_printf( "SIGTRAP at %p\n", regs.eip ); if( ProcessBreakpoint( pid, regs.eip ) ) { // don't pass on SIGTRAP if we expected this breakpoint ptrace_sig = 0; } sample_continue = TRUE; break; case SIGSTOP: /* presumably we were behind this SIGSTOP */ RecordSample( regs.eip, 1 ); ptrace_sig = 0; sample_continue = TRUE; break; default: /* all other signals get passed down to the child and we let * the child handle them (or not handle and die) */ sample_continue = TRUE; break; } } else if( WIFEXITED( status ) ) { dbg_printf( "WIFEXITED pid %d\n", pid ); report(); sample_continue = FALSE; } else if( WIFSIGNALED( status ) ) { dbg_printf( "WIFSIGNALED pid %d\n", pid ); report(); sample_continue = FALSE; } } while( sample_continue ); } static int GetExeNameFromPid( pid_t pid, char *buffer, int max_len ) { char procfile[24]; int len; sprintf( procfile, "/proc/%d/exe", pid ); len = readlink( procfile, buffer, max_len ); if( len < 0 ) len = 0; buffer[len] = '\0'; return( len ); }
void run(char **argv) { register char **p; pid_t pid; int noinvoke; int status; int pfd[2]; if (tflag) { (void)fprintf(stderr, "%s", *argv); for (p = argv + 1; *p; ++p) (void)fprintf(stderr, " %s", *p); (void)fprintf(stderr, "\n"); (void)fflush(stderr); } if (pipe(pfd) < 0) err("pipe: %s", strerror(errno)); switch(pid = fork()) { case -1: err("fork: %s", strerror(errno)); case 0: close(pfd[0]); fcntl(pfd[1], F_SETFD, fcntl(pfd[1], F_GETFD) | FD_CLOEXEC); execvp(argv[0], argv); noinvoke = (errno == ENOENT) ? 127 : 126; (void)fprintf(stderr, "xargs: %s: %s.\n", argv[0], strerror(errno)); /* Modern way of returning noinvoke instead of a dirty vfork() * trick: (kjb) */ write(pfd[1], &noinvoke, sizeof(noinvoke)); _exit(-1); } close(pfd[1]); if (read(pfd[0], &noinvoke, sizeof(noinvoke)) < sizeof(noinvoke)) noinvoke = 0; close(pfd[0]); pid = waitpid(pid, &status, 0); if (pid == -1) err("waitpid: %s", strerror(errno)); /* * If we couldn't invoke the utility or the utility didn't exit * properly, quit with 127 or 126 respectively. */ if (noinvoke) exit(noinvoke); /* * According to POSIX, we have to exit if the utility exits with * a 255 status, or is interrupted by a signal. xargs is allowed * to return any exit status between 1 and 125 in these cases, but * we'll use 124 and 125, the same values used by GNU xargs. */ if (WIFEXITED(status)) { if (WEXITSTATUS (status) == 255) { fprintf (stderr, "xargs: %s exited with status 255\n", argv[0]); exit(124); } else if (WEXITSTATUS (status) != 0) { exit_status = 123; } } else if (WIFSTOPPED (status)) { fprintf (stderr, "xargs: %s terminated by signal %d\n", argv[0], WSTOPSIG (status)); exit(125); } else if (WIFSIGNALED (status)) { fprintf (stderr, "xargs: %s terminated by signal %d\n", argv[0], WTERMSIG (status)); exit(125); } }
#include<sys/wait.h> #include "ourhdr.h" void pr_exit(int status) { if(WIFEXITED(status)) printf("normal termination, exit staus =%d\n",WEXITSTATUS(status)); else if(WIFSIGNALED(status)) printf("Abnormal termination, signal number =%d%s\n",WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status) ? "(core file generated)":""); #else ""); #endif else if(WIFSTOPPED(status)) printf("Child stopped, signal number = %d\n",WSTOPSIG(status)); }
/* * get_child_exit: This looks for dead child processes of the client. * There are two main sources of dead children: Either an /exec'd process * has exited, or the client has attempted to fork() off a helper process * (such as wserv or gzip) and that process has choked on itself. * * When SIGCHLD is recieved, the global variable 'dead_children_processes' * is incremented. When this function is called, we go through and call * waitpid() on all of the outstanding zombies, conditionally stopping when * we reach a specific wanted sub-process. * * If you want to stop reaping children when a specific subprocess is * reached, specify the process in 'wanted'. If all youre doing is cleaning * up after zombies and /exec's, then 'wanted' should be -1. */ int get_child_exit (pid_t wanted) { Process *proc; pid_t pid; int status, i; /* * Iterate until we've reaped all of the dead processes * or we've found the one asked for. */ if (dead_children_processes) { block_signal(SIGCHLD); while ((pid = waitpid(wanted, &status, WNOHANG)) > 0) { /* * First thing we do is look to see if the process we're * working on is the one that was asked for. If it is, * then we get its exit status information and return it. */ if (wanted != -1 && pid == wanted) { unblock_signal(SIGCHLD); if (WIFEXITED(status)) return WEXITSTATUS(status); if (WIFSTOPPED(status)) return -(WSTOPSIG(status)); if (WIFSIGNALED(status)) return -(WTERMSIG(status)); } /* * If it wasnt the process asked for, then we've probably * stumbled across a dead /exec'd process. Look for the * corresponding child process, and mark it as being dead. */ else { for (i = 0; i < process_list_size; i++) { proc = process_list[i]; if (proc && proc->pid == pid) { proc->exited = 1; proc->termsig = WTERMSIG(status); proc->retcode = WEXITSTATUS(status); break; } } } } dead_children_processes = 0; unblock_signal(SIGCHLD); } /* * Now we may have reaped some /exec'd processes that were previously * dumb and have now exited. So we call cleanup_dead_processes() to * find and delete any such processes. */ cleanup_dead_processes(); /* * If 'wanted' is not -1, then we didnt find that process, and * if 'wanted' is -1, then you should ignore the retval anyhow. ;-) */ return -1; }
static void * __sandbox_tracer(sandbox_t * psbox) { FUNC_BEGIN("sandbox_tracer(%p)", psbox); assert(psbox); #define UPDATE_RESULT(psbox,res) \ {{{ \ if (((psbox)->result) != (result_t)(res)) \ { \ ((psbox)->result) = (result_t)(res); \ } \ DBG("result: %s", s_result_name((result_t)(res))); \ }}} /* UPDATE_RESULT */ #define UPDATE_STATUS(psbox,sta) \ {{{ \ P(&((psbox)->mutex)); \ if (((psbox)->status) != (status_t)(sta)) \ { \ ((psbox)->status) = (status_t)(sta); \ pthread_cond_broadcast(&((psbox)->update)); \ } \ V(&((psbox)->mutex)); \ DBG("status: %s", s_status_name((status_t)(sta))); \ }}} /* UPDATE_STATUS */ #define SIGMLE SIGUSR1 #define TIMEVAL_INPLACE_SUBTRACT(x,y) \ {{{ \ if ((x).tv_usec < (y).tv_usec) \ { \ int nsec = ((y).tv_usec - (x).tv_usec) / 1000000 + 1; \ (x).tv_sec -= nsec; \ (x).tv_usec += 1000000 * nsec; \ } \ if ((x).tv_usec - (y).tv_usec >= 1000000) \ { \ int nsec = ((y).tv_usec - (x).tv_usec) / 1000000; \ (x).tv_usec -= 1000000 * nsec; \ (x).tv_sec += nsec; \ } \ (x).tv_sec -= (y).tv_sec; \ (x).tv_usec -= (y).tv_usec; \ }}} /* TIMEVAL_INPLACE_SUBTRACT */ #define CLEAR_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ ((pctrl)->idle) = true; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* CLEAR_EVENT */ #define POST_EVENT(pctrl,type,x...) \ {{{ \ P(&((pctrl)->mutex)); \ ((pctrl)->event) = (event_t){(S_EVENT ## type), {{x}}}; \ ((pctrl)->idle) = false; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* POST_EVENT */ #ifdef WITH_CUSTOM_MONITOR #define SINK_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ while (!IS_IDLE(pctrl)) \ { \ pthread_cond_wait(&((pctrl)->sched), &((pctrl)->mutex)); \ } \ V(&((pctrl)->mutex)); \ }}} /* SINK_EVENT */ #endif /* WITH_CUSTOM_MONITOR */ #ifdef WITH_NATIVE_MONITOR #define SINK_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ if (IS_IDLE(pctrl)) \ { \ DBG("no event detected"); \ V(&((pctrl)->mutex)); \ goto cont; \ } \ V(&((pctrl)->mutex)); \ \ DBG("detected event: %s {%lu %lu %lu %lu %lu %lu %lu}", \ s_event_type_name((pctrl)->event.type), \ (pctrl)->event.data.__bitmap__.A, \ (pctrl)->event.data.__bitmap__.B, \ (pctrl)->event.data.__bitmap__.C, \ (pctrl)->event.data.__bitmap__.D, \ (pctrl)->event.data.__bitmap__.E, \ (pctrl)->event.data.__bitmap__.F, \ (pctrl)->event.data.__bitmap__.G); \ \ ((policy_entry_t)(pctrl)->policy.entry)(&(pctrl)->policy, \ &(pctrl)->event, \ &(pctrl)->action); \ \ DBG("decided action: %s {%lu %lu}", \ s_action_type_name((pctrl)->action.type), \ (pctrl)->action.data.__bitmap__.A, \ (pctrl)->action.data.__bitmap__.B); \ \ P(&((pctrl)->mutex)); \ ((pctrl)->idle) = true; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* SINK_EVENT */ #endif /* WITH_NATIVE_MONITOR */ DBG("entering: the tracing thread"); /* The controller should contain pid of the prisoner process */ pid_t pid = psbox->ctrl.pid; /* Check if the prisoner process was correctly forked */ if (pid < 0) { WARNING("error forking the prisoner process"); UPDATE_RESULT(psbox, S_RESULT_IE); UPDATE_STATUS(psbox, S_STATUS_FIN); FUNC_RET((void *)&psbox->result, "sandbox_tracer()"); } /* Have signals kill the prisoner but not self (if possible). */ sighandler_t terminate_signal; sighandler_t interrupt_signal; sighandler_t quit_signal; terminate_signal = signal(SIGTERM, SIG_IGN); interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); /* Get wallclock start time */ gettimeofday(&psbox->stat.started, NULL); UPDATE_RESULT(psbox, S_RESULT_PD); UPDATE_STATUS(psbox, S_STATUS_EXE); /* Resume the control logic */ CLEAR_EVENT(&psbox->ctrl); /* Temporary variables. */ struct rusage initru; int waitstatus = 0; pid_t waitresult = 0; proc_t proc = {0}; /* System call stack. The prisoner process initially stops at *SYSRET* mode * after making the first call to SYS_execve. Therefore, we initialize the * stack as it is. */ int sc_stack[16] = {0, SYS_execve}; int sc_top = 1; /* Make an initial wait to verify the first system call as well as to * collect resource usage overhead. */ waitresult = wait4(pid, &waitstatus, 0, &psbox->stat.ru); UPDATE_STATUS(psbox, S_STATUS_BLK); /* Save the initial resource usage for further reference */ memcpy(&initru, &psbox->stat.ru, sizeof(struct rusage)); /* Clear cache in order to increase timing accuracy */ flush_cache(); flush_cache(); /* Entering the tracing loop */ do { /* Trace state refresh of the prisoner program */ UPDATE_STATUS(psbox, S_STATUS_BLK); /* In case nothing happened (possible when the 3rd argument of *wait4* * contains the WNOHANG flag), we just go on with next wait(). */ if (waitresult == 0) { DBG("wait: nothing happened"); goto cont; } /* Figure *net* resource usage (eliminate initru) */ TIMEVAL_INPLACE_SUBTRACT(psbox->stat.ru.ru_utime, initru.ru_utime); TIMEVAL_INPLACE_SUBTRACT(psbox->stat.ru.ru_stime, initru.ru_stime); psbox->stat.ru.ru_majflt -= initru.ru_majflt; psbox->stat.ru.ru_minflt -= initru.ru_minflt; psbox->stat.ru.ru_nswap -= initru.ru_nswap; DBG("ru.ru_utime.tv_sec % 10ld", psbox->stat.ru.ru_utime.tv_sec); DBG("ru.ru_utime.tv_usec % 10ld", psbox->stat.ru.ru_utime.tv_usec); DBG("ru.ru_stime.tv_sec % 10ld", psbox->stat.ru.ru_stime.tv_sec); DBG("ru.ru_stime.tv_usec % 10ld", psbox->stat.ru.ru_stime.tv_usec); DBG("ru.ru_majflt % 10ld", psbox->stat.ru.ru_majflt); DBG("ru.ru_minflt % 10ld", psbox->stat.ru.ru_minflt); DBG("ru.ru_nswap % 10ld", psbox->stat.ru.ru_nswap); /* Raise appropriate events judging each wait status */ if (WIFSTOPPED(waitstatus)) { DBG("wait: stopped (%d)", WSTOPSIG(waitstatus)); psbox->stat.signal = WSTOPSIG(waitstatus); /* Collect additional information of the prisoner process */ if (!proc_probe(pid, PROBE_STAT, &proc)) { WARNING("failed to probe the prisoner process"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); goto done; } /* Raise appropriate event judging stop signal */ switch (WSTOPSIG(waitstatus)) { case SIGALRM: /* real timer expired */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_WALLCLOCK); break; case SIGXCPU: /* CPU resource limit exceed */ case SIGPROF: /* profile timer expired */ case SIGVTALRM: /* virtual timer expired */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_CPU); break; case SIGMLE: /* SIGUSR1 used for reporting ML */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_MEMORY); break; case SIGXFSZ: /* Output file size exceeded */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_DISK); break; case SIGTRAP: /* Update the tsc instructions counter */ #ifdef WITH_TSC_COUNTER psbox->stat.tsc++; DBG("tsc %010llu", psbox->stat.tsc); #endif /* WITH_TSC_COUNTER */ /* Collect additional information of prisoner process */ if (!proc_probe(pid, PROBE_REGS, &proc)) { WARNING("failed to probe the prisoner process"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); goto done; } /* Update sandbox stat with the process runtime info */ { psbox->stat.vsize = proc.vsize; if (psbox->stat.vsize_peak < proc.vsize) { psbox->stat.vsize_peak = proc.vsize; } psbox->stat.rss = proc.rss * getpagesize(); } /* Detect memory usage against quota */ if (psbox->stat.vsize_peak > psbox->task.quota[S_QUOTA_MEMORY]) { kill(-pid, SIGMLE); } /* For `single step' tracing mode, we have to probe the current * op code (i.e. INT80 for i386 platforms) to tell whether the * prisoner process is invoking a system call. For `system call' * tracing mode, however, every *SIGTRAP* indicates a system * call currently being invoked or just returned. */ #ifdef WITH_TSC_COUNTER if (IS_SYSCALL(&proc) || IS_SYSRET(&proc)) #endif /* WITH_TSC_COUNTER */ { int scno = THE_SYSCALL(&proc); if (scno != sc_stack[sc_top]) { sc_stack[++sc_top] = scno; psbox->stat.syscall = scno; SET_IN_SYSCALL(&proc); POST_EVENT(&psbox->ctrl, _SYSCALL, scno, SYSCALL_ARG1(&proc), SYSCALL_ARG2(&proc), SYSCALL_ARG3(&proc), SYSCALL_ARG4(&proc), SYSCALL_ARG5(&proc)); } else { CLR_IN_SYSCALL(&proc); sc_stack[sc_top--] = 0; POST_EVENT(&psbox->ctrl, _SYSRET, scno, SYSRET_RETVAL(&proc)); } } #ifdef WITH_TSC_COUNTER else { goto next; } #endif /* WITH_TSC_COUNTER */ break; default: /* Other runtime error */ POST_EVENT(&psbox->ctrl, _SIGNAL, WSTOPSIG(waitstatus)); break; } } /* stopped */ else if (WIFSIGNALED(waitstatus)) { DBG("wait: signaled (%d)", WTERMSIG(waitstatus)); psbox->stat.signal = WTERMSIG(waitstatus); POST_EVENT(&psbox->ctrl, _SIGNAL, WTERMSIG(waitstatus)); } else if (WIFEXITED(waitstatus)) { DBG("wait: exited (%d)", WEXITSTATUS(waitstatus)); psbox->stat.exitcode = WEXITSTATUS(waitstatus); POST_EVENT(&psbox->ctrl, _EXIT, WEXITSTATUS(waitstatus)); } /* Wait for the policy to determine the next action */ SINK_EVENT(&psbox->ctrl); /* Perform the desired action */ switch (psbox->ctrl.action.type) { case S_ACTION_CONT: next: #ifdef WITH_TSC_COUNTER if (!trace_next(&proc, TRACE_SINGLE_STEP)) #else if (!trace_next(&proc, TRACE_SYSTEM_CALL)) #endif /* WITH_TSC_COUNTER */ { WARNING("trace_next"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); break; } /* There is no need to update state here! */ goto cont; /* Continue with next wait() */ case S_ACTION_FINI: UPDATE_RESULT(psbox, psbox->ctrl.action.data._FINI.result); break; case S_ACTION_KILL: /* Using trace_kill can effectively prevent overrun of undesired * behavior, i.e. illegal system call. */ trace_kill(&proc, SIGKILL); UPDATE_RESULT(psbox, psbox->ctrl.action.data._KILL.result); break; } break; /* Exiting the tracing loop! */ cont: /* Wait until the prisoner process is trapped */ UPDATE_STATUS(psbox, S_STATUS_EXE); DBG("----------------------------------------------------------------"); DBG("wait4(%d,%p,%d,%p)", pid, &waitstatus, 0, &psbox->stat.ru); waitresult = waitstatus = 0; } while ((waitresult = wait4(pid, &waitstatus, 0, &psbox->stat.ru)) >= 0); done: /* Get wallclock stop time (call a second time to compensate overhead) */ gettimeofday(&psbox->stat.stopped, NULL); UPDATE_STATUS(psbox, S_STATUS_FIN); /* Resume the control logic */ CLEAR_EVENT(&psbox->ctrl); DBG("leaving: the tracing thread"); /* Restore signal handlers */ signal(SIGTERM, interrupt_signal); signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); FUNC_RET((void *)&psbox->result, "sandbox_tracer()"); }
//------------------------------------------------------------------------------ // Name: handle_event // Desc: //------------------------------------------------------------------------------ IDebugEvent::const_pointer DebuggerCore::handle_event(edb::tid_t tid, int status) { // note that we have waited on this thread waited_threads_.insert(tid); // was it a thread exit event? if(WIFEXITED(status)) { threads_.remove(tid); waited_threads_.remove(tid); // if this was the last thread, return true // so we report it to the user. // if this wasn't, then we should silently // procceed. if(!threads_.empty()) { return nullptr; } } // was it a thread create event? if(is_clone_event(status)) { unsigned long new_tid; if(ptrace_get_event_message(tid, &new_tid) != -1) { auto newThread = std::make_shared<PlatformThread>(this, process_, new_tid); newThread->status_ = 0; newThread->state_ = PlatformThread::Stopped; threads_.insert(new_tid, newThread); int thread_status = 0; if(!waited_threads_.contains(new_tid)) { if(native::waitpid(new_tid, &thread_status, __WALL) > 0) { waited_threads_.insert(new_tid); } } if(!WIFSTOPPED(thread_status) || WSTOPSIG(thread_status) != SIGSTOP) { qDebug("[warning] new thread [%d] received an event besides SIGSTOP", static_cast<int>(new_tid)); } newThread->status_ = thread_status; // TODO: what the heck do we do if this isn't a SIGSTOP? newThread->resume(); } ptrace_continue(tid, 0); return nullptr; } // normal event auto e = std::make_shared<PlatformEvent>(); e->pid_ = pid(); e->tid_ = tid; e->status_ = status; if(ptrace_getsiginfo(tid, &e->siginfo_) == -1) { // TODO: handle no info? } active_thread_ = tid; auto it = threads_.find(tid); if(it != threads_.end()) { it.value()->status_ = status; } stop_threads(); return e; }
//------------------------------------------------------------------------------ // Name: open // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::open(const QString &path, const QString &cwd, const QList<QByteArray> &args, const QString &tty) { detach(); switch(pid_t pid = fork()) { case 0: // we are in the child now... // set ourselves (the child proc) up to be traced ptrace_traceme(); // redirect it's I/O if(!tty.isEmpty()) { FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout); FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin); FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr); Q_UNUSED(std_out); Q_UNUSED(std_in); Q_UNUSED(std_err); } // do the actual exec execute_process(path, cwd, args); // we should never get here! abort(); break; case -1: // error! for some reason we couldn't fork reset(); return false; default: // parent do { reset(); int status; if(native::waitpid(pid, &status, __WALL) == -1) { return false; } // the very first event should be a STOP of type SIGTRAP if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { detach(); return false; } waited_threads_.insert(pid); // enable following clones (threads) if(ptrace_set_options(pid, PTRACE_O_TRACECLONE) == -1) { qDebug("[DebuggerCore] failed to set PTRACE_SETOPTIONS: %s", strerror(errno)); detach(); return false; } #ifdef PTRACE_O_EXITKILL if(ptrace_set_options(pid, PTRACE_O_EXITKILL) == -1) { qDebug("[DebuggerCore] failed to set PTRACE_SETOPTIONS: %s", strerror(errno)); detach(); return false; } #endif // setup the first event data for the primary thread waited_threads_.insert(pid); // create the process process_ = new PlatformProcess(this, pid); // the PID == primary TID auto newThread = std::make_shared<PlatformThread>(this, process_, pid); newThread->status_ = status; newThread->state_ = PlatformThread::Stopped; threads_[pid] = newThread; pid_ = pid; active_thread_ = pid; binary_info_ = edb::v1::get_binary_info(edb::v1::primary_code_region()); detectDebuggeeBitness(); return true; } while(0); break; } }
int Sandbox::Run(bool is_rf, bool is_hvc) { if (access(m_path.c_str(), R_OK | X_OK)) { fprintf(stderr, "Cannot access %s\n", m_path.c_str()); return -1; } fprintf(stderr, "%s begin to run\n", m_path.c_str()); int fd1[2], fd2[2], info_fd[2]; if (pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(info_fd) < 0) { fprintf(stderr, "Error on pipe()\n"); return -1; } pid_t child_pid = fork(); if (child_pid < 0) { fprintf(stderr, "error on fork()\n"); return -1; } else if (child_pid == 0) { close(fd1[1]); close(fd2[0]); close(info_fd[0]); pid_t grandson_pid = fork(); if (grandson_pid < 0) { fprintf(stderr, "error on fork()\n"); return -1; } else if (grandson_pid == 0) { // grandson: the actual executable close(info_fd[1]); if (fd1[0] != STDIN_FILENO) { dup2(fd1[0], STDIN_FILENO); close(fd1[0]); } if (fd2[1] != STDOUT_FILENO) { dup2(fd2[1], STDOUT_FILENO); close(fd2[1]); } // setrlimit if (set_quota() < 0) { fprintf(stderr, "set_quota() error\n"); } ptrace(PTRACE_TRACEME, 0, NULL, NULL); if (is_hvc) { execl(m_path.c_str(), m_path.c_str(), "1", NULL); } else { execl(m_path.c_str(), m_path.c_str(), NULL); } fprintf(stderr, "execl failed, errno: %d\n", errno); return -1; } else { fprintf(stderr, "%d: i am the sandbox of %d\n", getpid(), grandson_pid); // son: the sandbox close(fd1[0]); close(fd2[1]); char pid_buf[16]; sprintf(pid_buf, "%d\n", grandson_pid); write(info_fd[1], pid_buf, strlen(pid_buf)); sleep(1); // sleep to ensure the grand recv the msg int st, in_call = 0; while (1) { struct rusage ru; wait4(grandson_pid, &st, 0, &ru); m_time_cost = ru.ru_utime.tv_sec * 1000 + ru.ru_utime.tv_usec / 1000; if (WIFEXITED(st)) { m_exit_flag = EXIT_NORMAL; fprintf(stderr, "exit_normal\n"); break; } if (WIFSIGNALED(st) || (WIFSTOPPED(st) && WSTOPSIG(st) != SIGTRAP)) { if (WIFSTOPPED(st)) { switch (WSTOPSIG(st)) { case SIGXCPU: m_exit_flag = EXIT_TLE; fprintf(stderr, "pid:%d m_exit_flag:%d\n", getpid(), EXIT_TLE); break; case SIGPIPE: // match is over, the match process is terminated m_exit_flag = EXIT_NORMAL; break; default: fprintf(stderr, "pid:%d exit: WSTOPSIG(st):%d\n", getpid(), WSTOPSIG(st)); m_exit_flag = EXIT_RE; } } else if (WIFSIGNALED(st)) { fprintf(stderr, "termed by sig: %d\n", WTERMSIG(st)); m_exit_flag = EXIT_RE; } else { fprintf(stderr, "termed by sig: %d\n", WTERMSIG(st)); } ptrace(PTRACE_KILL, grandson_pid, NULL, NULL); break; } if (in_call == 0) { #ifdef __x86_64__ long long orig_eax = ptrace(PTRACE_PEEKUSER, grandson_pid, 8 * ORIG_RAX, NULL); #else int orig_eax = ptrace(PTRACE_PEEKUSER, grandson_pid, 4 * ORIG_EAX, NULL); #endif assert(orig_eax >= 0 && orig_eax < 512); if (is_rf && --m_limit[orig_eax] < 0) { m_exit_flag = EXIT_RF; fprintf(stderr, "pid:%d Sys call %d reach limit\n", getpid(), static_cast<int>(orig_eax)); } ++m_stat[orig_eax]; in_call = 1; } else { in_call = 0; } if (m_exit_flag == EXIT_RF) { ptrace(PTRACE_KILL, grandson_pid, NULL, NULL); break; } ptrace(PTRACE_SYSCALL, grandson_pid, NULL, NULL); } char buf[16]; // sprintf(buf, "%d %d\n", GetID(), GetUsedTime()); // write(GetInfoFd(), buf, strlen(buf)); fprintf(stderr, "%d: the proc in sandbox exit type: %d, time_cost: %d\n", getpid(), m_exit_flag, m_time_cost); fprintf(stderr, "%d: i am exited\n", getpid()); sprintf(buf, "%d %d\n", m_exit_flag, m_time_cost); write(info_fd[1], buf, strlen(buf)); exit(0); // the sandbox process exit } } else { // parent close(fd1[0]); close(fd2[1]); close(info_fd[1]); recv_fd = fd2[0]; send_fd = fd1[1]; recv_info_fd = info_fd[0]; char buf_pid[16]; read(recv_info_fd, buf_pid, sizeof(buf_pid)-1); // read the client's pid sscanf(buf_pid, "%d", &m_client_pid); fprintf(stderr, "%d: i am grand, i recv the client pid: %d\n", getpid(), m_client_pid); } return 0; }
int main (int argc, char *argv[]) { char *filename; char out_filename[8192]; char buf[1024]; struct stat stat_buf; pid_t pid; int status; int ret_val; size_t file_size; unsigned int addr; int bad_usage; filename = NULL; addr = 0; /* process args: assigning values to bad_usage filename and possibly addr */ bad_usage = 1; if (argc == 2) { filename = argv[1]; bad_usage = 0; } else if (argc == 4) { filename = argv[3]; if (strcmp(argv[1], "-a") == 0) { if ((argv[2][0] == '0') && (tolower(argv[2][1]) == 'x')) addr = strtol(argv[2], NULL, 16); else addr = strtol(argv[2], NULL, 10); if (errno != ERANGE) bad_usage = 0; } } if (bad_usage) { fprintf(stderr, "Obtains an executable copy of a binary with execute " "but no read permission\n" "Usage: %s [-a addr] <file>\n" " where addr is the memory address of the elf header\n", argv[0]); exit(EXIT_FAILURE); } if (stat(filename, &stat_buf) != 0) { snprintf(buf, sizeof(buf), "couldn't stat file `%s'", filename); perror(buf); exit(EXIT_FAILURE); } /* remember file size of original file */ file_size = stat_buf.st_size; if ( (pid = fork()) == 0) { /* child */ if (ptrace(PTRACE_TRACEME, 0, 0, 0) == 0) { execl(filename, filename, NULL); snprintf(buf, sizeof(buf), "couldn't exec `%s'", filename); perror(buf); } else { perror("ptrace(PTRACE_TRACEME, ...)"); } _exit(EXIT_FAILURE); } ret_val = EXIT_FAILURE; if (waitpid(pid, &status, WUNTRACED) == pid) { if (!WIFEXITED(status)) { /* SIGTRAP is delivered to child after execve */ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { if (addr == 0) addr = find_elf_header(pid); if (addr != 0) { snprintf(out_filename, sizeof(out_filename), "%s.out", basename(filename)); if (save_to_file(out_filename, pid, addr, file_size)) { chmod(out_filename, 00755); fprintf(stdout, "created file `%s'\n", out_filename); ret_val = EXIT_SUCCESS; } } else { fprintf(stderr, "couldn't find elf header in memory\n"); } } else { fprintf(stderr, "didn't receive SIGTRAP after execve\n"); } /* kill child as we are finished */ ptrace(PTRACE_KILL, pid, 0, 0); } } else { perror("waitpid"); } return(ret_val); }
int main (int argc, char **argv) { int status, pid, pending_sig, optind = 1, state = 1; as = unw_create_addr_space (&_UPT_accessors, 0); if (!as) panic ("unw_create_addr_space() failed"); if (argc == 1) { static char *args[] = { "self", "/bin/ls", "/usr", NULL }; /* automated test case */ argv = args; } else if (argc > 1) while (argv[optind][0] == '-') { if (strcmp (argv[optind], "-v") == 0) ++optind, verbose = 1; else if (strcmp (argv[optind], "-i") == 0) ++optind, trace_mode = INSTRUCTION; /* backtrace at each insn */ else if (strcmp (argv[optind], "-s") == 0) ++optind, trace_mode = SYSCALL; /* backtrace at each syscall */ else if (strcmp (argv[optind], "-t") == 0) /* Execute until raise(SIGUSR1), then backtrace at each insn until raise(SIGUSR2). */ ++optind, trace_mode = TRIGGER; else if (strcmp (argv[optind], "-c") == 0) /* Enable caching of unwind-info. */ ++optind, unw_set_caching_policy (as, UNW_CACHE_GLOBAL); else if (strcmp (argv[optind], "-n") == 0) /* Don't look-up and print symbol names. */ ++optind, print_names = 0; else fprintf(stderr, "unrecognized option: %s\n", argv[optind++]); if (optind >= argc) break; } target_pid = fork (); if (!target_pid) { /* child */ if (!verbose) dup2 (open ("/dev/null", O_WRONLY), 1); #if HAVE_DECL_PTRACE_TRACEME ptrace (PTRACE_TRACEME, 0, 0, 0); #elif HAVE_DECL_PT_TRACE_ME ptrace (PT_TRACE_ME, 0, 0, 0); #else #error Trace me #endif if ((argc > 1) && (optind == argc)) { fprintf(stderr, "Need to specify a command line for the child\n"); exit (-1); } execve (argv[optind], argv + optind, environ); _exit (-1); } atexit (target_pid_kill); ui = _UPT_create (target_pid); while (nerrors <= nerrors_max) { pid = wait4 (-1, &status, 0, NULL); if (pid == -1) { if (errno == EINTR) continue; panic ("wait4() failed (errno=%d)\n", errno); } pending_sig = 0; if (WIFSIGNALED (status) || WIFEXITED (status) || (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP)) { if (WIFEXITED (status)) { if (WEXITSTATUS (status) != 0) panic ("child's exit status %d\n", WEXITSTATUS (status)); break; } else if (WIFSIGNALED (status)) { if (!killed) panic ("child terminated by signal %d\n", WTERMSIG (status)); break; } else { pending_sig = WSTOPSIG (status); /* Avoid deadlock: */ if (WSTOPSIG (status) == SIGKILL) break; if (trace_mode == TRIGGER) { if (WSTOPSIG (status) == SIGUSR1) state = 0; else if (WSTOPSIG (status) == SIGUSR2) state = 1; } if (WSTOPSIG (status) != SIGUSR1 && WSTOPSIG (status) != SIGUSR2) { static int count = 0; if (count++ > 100) { panic ("Too many child unexpected signals (now %d)\n", WSTOPSIG (status)); killed = 1; } } } } switch (trace_mode) { case TRIGGER: if (state) #if HAVE_DECL_PTRACE_CONT ptrace (PTRACE_CONT, target_pid, 0, 0); #elif HAVE_DECL_PT_CONTINUE ptrace (PT_CONTINUE, target_pid, (caddr_t)1, 0); #else #error Port me #endif else { do_backtrace (); #if HAVE_DECL_PTRACE_SINGLESTEP if (ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig) < 0) { panic ("ptrace(PTRACE_SINGLESTEP) failed (errno=%d)\n", errno); killed = 1; } #elif HAVE_DECL_PT_STEP if (ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig) < 0) { panic ("ptrace(PT_STEP) failed (errno=%d)\n", errno); killed = 1; } #else #error Singlestep me #endif } break; case SYSCALL: if (!state) do_backtrace (); state ^= 1; #if HAVE_DECL_PTRACE_SYSCALL ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig); #elif HAVE_DECL_PT_SYSCALL ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig); #else #error Syscall me #endif break; case INSTRUCTION: do_backtrace (); #if HAVE_DECL_PTRACE_SINGLESTEP ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig); #elif HAVE_DECL_PT_STEP ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig); #else #error Singlestep me #endif break; } if (killed) kill (target_pid, SIGKILL); }
/* Run the command with op as command line argument(s) and return the exit * status + the output */ static int external_run_cmd(struct pluginDevice *sd, const char *op, char **output) { const int BUFF_LEN=4096; char buff[BUFF_LEN]; int read_len = 0; int status, rc; char * data = NULL; FILE * file; char cmd[FILENAME_MAX+64]; struct stat buf; int slen; char *path, *new_path, *logtag, *savevar = NULL; int new_path_len, logtag_len; gboolean nodata; rc = snprintf(cmd, FILENAME_MAX, "%s/%s", STONITH_EXT_PLUGINDIR, sd->subplugin); if (rc <= 0 || rc >= FILENAME_MAX) { LOG(PIL_CRIT, "%s: external command too long.", __FUNCTION__); return -1; } if (stat(cmd, &buf) != 0) { LOG(PIL_CRIT, "%s: stat(2) of %s failed: %s", __FUNCTION__, cmd, strerror(errno)); return -1; } if (!S_ISREG(buf.st_mode) || (!(buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { LOG(PIL_CRIT, "%s: %s found NOT to be executable.", __FUNCTION__, cmd); return -1; } if (buf.st_mode & (S_IWGRP|S_IWOTH)) { LOG(PIL_CRIT, "%s: %s found to be writable by group/others, " "NOT executing for security purposes.", __FUNCTION__, cmd); return -1; } strcat(cmd, " "); strcat(cmd, op); /* We only have a global environment to use here. So we add our * options to it, and then later remove them again. */ if (sd->cmd_opts) { g_hash_table_foreach(sd->cmd_opts, ext_add_to_env, NULL); } /* external plugins need path to ha_log.sh */ path = getenv("PATH"); if (strncmp(GLUE_SHARED_DIR,path,strlen(GLUE_SHARED_DIR))) { new_path_len = strlen(path)+strlen(GLUE_SHARED_DIR)+2; new_path = (char *)g_malloc(new_path_len); snprintf(new_path, new_path_len, "%s:%s", GLUE_SHARED_DIR, path); setenv("PATH", new_path, 1); g_free(new_path); } /* set the logtag appropriately */ logtag_len = strlen(PIL_PLUGIN_S)+strlen(sd->subplugin)+2; logtag = (char *)g_malloc(logtag_len); snprintf(logtag, logtag_len, "%s/%s", PIL_PLUGIN_S, sd->subplugin); if (getenv(LOGTAG_VAR)) { savevar = g_strdup(getenv(LOGTAG_VAR)); } setenv(LOGTAG_VAR, logtag, 1); g_free(logtag); if (Debug) { LOG(PIL_DEBUG, "%s: Calling '%s'", __FUNCTION__, cmd ); } file = popen(cmd, "r"); if (NULL==file) { LOG(PIL_CRIT, "%s: Calling '%s' failed", __FUNCTION__, cmd); rc = -1; goto out; } if (output) { slen=0; data = MALLOC(1); data[slen] = EOS; } while (!feof(file)) { nodata = TRUE; if (output) { read_len = fread(buff, 1, BUFF_LEN, file); if (read_len > 0) { data = REALLOC(data, slen+read_len+1); if (data == NULL) { break; } memcpy(data + slen, buff, read_len); slen += read_len; data[slen] = EOS; nodata = FALSE; } } else { if (fgets(buff, BUFF_LEN, file)) { LOG(PIL_INFO, "%s: '%s' output: %s", __FUNCTION__, cmd, buff); nodata = FALSE; } } if (nodata) { sleep(1); } } if (output && !data) { LOG(PIL_CRIT, "%s: out of memory", __FUNCTION__); rc = -1; goto out; } status = pclose(file); if (WIFEXITED(status)) { rc = WEXITSTATUS(status); if (rc != 0 && Debug) { LOG(PIL_DEBUG, "%s: Calling '%s' returned %d", __FUNCTION__, cmd, rc); } } else { if (WIFSIGNALED(status)) { LOG(PIL_CRIT, "%s: '%s' got signal %d", __FUNCTION__, cmd, WTERMSIG(status)); } else if (WIFSTOPPED(status)) { LOG(PIL_INFO, "%s: '%s' stopped with signal %d", __FUNCTION__, cmd, WSTOPSIG(status)); } else { LOG(PIL_CRIT, "%s: '%s' exited abnormally (core dumped?)", __FUNCTION__, cmd); } rc = -1; } if (Debug && output && data) { LOG(PIL_DEBUG, "%s: '%s' output: %s", __FUNCTION__, cmd, data); } out: if (savevar) { setenv(LOGTAG_VAR, savevar, 1); g_free(savevar); } else { unsetenv(LOGTAG_VAR); } if (sd->cmd_opts) { g_hash_table_foreach(sd->cmd_opts, ext_del_from_env, NULL); } if (!rc) { if (output) { *output = data; } } else { if (data) { FREE(data); } if (output) { *output = NULL; } } return rc; }
void TraceProcess::traceMe() { struct user_regs_struct regs; DLOG(INFO) << pid << " trace_me\n"; //the waitpid function will be interuppted by the SIGCHLD signal int wret; #ifdef HAVE_WAIT4 while((wret = wait4(this->pid, &status, 0, &process_usage)) > 0) { getusage = true; #else while((wret = waitpid(this->pid, &status, 0)) > 0) { #endif //DLOG(INFO) << pid << " waitpid return " << wret << "\n"; if(WIFSIGNALED(status)) { DLOG(INFO) << "TraceProcess()::traceMe(): child was killed by signal\n"; _exit = true; if(WTERMSIG(status) == SIGKILL && this->_result == -1) { this->_result = RUNTIME_ERROR; } break; } if(!WIFSTOPPED(status)) { //if(WEXITSTATUS(status)) { // this->_result = RUNTIME_ERROR; //} _exit = true; break; } int sig = WSTOPSIG(status); if(sig != SIGTRAP) { DLOG(INFO) << "TraceProcess()::traceMe(): caught a signal " << sig << "\n"; getRunningTime(); getRunningMemory(); switch(sig) { case SIGFPE: DLOG(INFO) << "TraceProcess()::traceMe(): SIGFPE\n"; this->_result = FLOATING_POINT_ERROR; break; case SIGXCPU: DLOG(INFO) << "TraceProcess()::traceMe(): SIGXCPU\n"; this->_result = TIME_LIMIT_EXCEEDED; break; case SIGBUS: case SIGSEGV: DLOG(INFO) << "TraceProcess()::traceMe(): SIGSEGV\n"; this->_result = SEGMENTATION_FAULT; break; case SIGXFSZ: DLOG(INFO) << "TraceProcess()::traceMe(): SIGXFSZ\n"; this->_result = OUTPUT_LIMIT_EXCEEDED; break; case SIGILL: case SIGKILL: DLOG(INFO) << "TraceProcess()::traceMe(): SIGKILL\n"; if(this->_result == -1) { this->_result = RUNTIME_ERROR; } break; case SIGALRM: ; } ptrace(PTRACE_SYSCALL, this->pid, NULL, sig); continue; } ptrace(PTRACE_GETREGS, this->pid, 0, ®s); switch(regs.ORIG_EAX) { case SYS_exit: case SYS_exit_group: DLOG(INFO) << "TraceProcess()::traceMe(): child sys_exit\n"; getRunningTime(); getRunningMemory(); break; case SYS_execve: if(!first_exec) { first_exec = true; DLOG(INFO) << pid << " execve, ptrace it\n"; ptrace(PTRACE_SYSCALL, this->pid, NULL, NULL); continue; } break; case SYS_brk: if(!insyscall) { brk = (unsigned long)regs.EBX; insyscall = true; }else{ if(((unsigned long)regs.EAX) < brk) { DLOG(INFO) << "TraceProcess()::traceMe(): brk request " << brk << " return " << regs.EAX << "\n"; this->_result = MEMORY_LIMIT_EXCEEDED; ptrace(PTRACE_KILL, this->pid, 0, 0); continue; } insyscall = false; } break; case SYS_open: if(!insyscall) { char path[FILENAME_MAX + 1]; insyscall = true; getdata(regs.EBX, path, sizeof(path)); DLOG(INFO) << "open file:" << path << "\n"; if(!canOpen(path, regs.ECX)) { regs.ORIG_EAX = SYS_exit; regs.EBX = 1; ptrace(PTRACE_SETREGS, this->pid, 0, ®s); this->_result = RUNTIME_ERROR; break; } }else{ insyscall = false; } ptrace(PTRACE_SYSCALL, this->pid, 0, 0); continue; } if(regs.ORIG_EAX < sizeof(disabled_syscall) && disabled_syscall[regs.ORIG_EAX]) { DLOG(INFO) << "TraceProcess()::traceMe(): Restricted syscall " << regs.ORIG_EAX << "\n"; this->_result = RESTRICTED_FUNCTION; getRunningTime(); getRunningMemory(); regs.ORIG_EAX = SYS_exit; regs.EAX = SYS_exit; regs.EBX = 1; ptrace(PTRACE_SETREGS, this->pid, NULL, ®s); ptrace(PTRACE_KILL, this->pid, 0, 0); continue; }else{ ptrace(PTRACE_SYSCALL, this->pid, 0, 0); } ptrace(PTRACE_SYSCALL, this->pid, NULL, NULL); } }
static int linux_wait_for_event (struct thread_info *child) { CORE_ADDR stop_pc; struct process_info *event_child; int wstat; /* Check for a process with a pending status. */ /* It is possible that the user changed the pending task's registers since it stopped. We correctly handle the change of PC if we hit a breakpoint (in check_removed_breakpoints); signals should be reported anyway. */ if (child == NULL) { event_child = (struct process_info *) find_inferior (&all_processes, status_pending_p, NULL); if (debug_threads && event_child) fprintf (stderr, "Got a pending child %d\n", event_child->lwpid); } else { event_child = get_thread_process (child); if (event_child->status_pending_p && check_removed_breakpoint (event_child)) event_child = NULL; } if (event_child != NULL) { if (event_child->status_pending_p) { if (debug_threads) fprintf (stderr, "Got an event from pending child %d (%04x)\n", event_child->lwpid, event_child->status_pending); wstat = event_child->status_pending; event_child->status_pending_p = 0; event_child->status_pending = 0; current_inferior = get_process_thread (event_child); return wstat; } } /* We only enter this loop if no process has a pending wait status. Thus any action taken in response to a wait status inside this loop is responding as soon as we detect the status, not after any pending events. */ while (1) { if (child == NULL) event_child = NULL; else event_child = get_thread_process (child); linux_wait_for_process (&event_child, &wstat); if (event_child == NULL) error ("event from unknown child"); current_inferior = (struct thread_info *) find_inferior_id (&all_threads, event_child->tid); if (using_threads) { /* Check for thread exit. */ if (! WIFSTOPPED (wstat)) { if (debug_threads) fprintf (stderr, "Thread %d (LWP %d) exiting\n", event_child->tid, event_child->head.id); /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return wstat; dead_thread_notify (event_child->tid); remove_inferior (&all_processes, &event_child->head); free (event_child); remove_thread (current_inferior); current_inferior = (struct thread_info *) all_threads.head; /* If we were waiting for this particular child to do something... well, it did something. */ if (child != NULL) return wstat; /* Wait for a more interesting event. */ continue; } if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP && event_child->stop_expected) { if (debug_threads) fprintf (stderr, "Expected stop.\n"); event_child->stop_expected = 0; linux_resume_one_process (&event_child->head, event_child->stepping, 0); continue; } /* FIXME drow/2002-06-09: Get signal numbers from the inferior's thread library? */ if (WIFSTOPPED (wstat) && (WSTOPSIG (wstat) == SIGRTMIN || WSTOPSIG (wstat) == SIGRTMIN + 1)) { if (debug_threads) fprintf (stderr, "Ignored signal %d for %d (LWP %d).\n", WSTOPSIG (wstat), event_child->tid, event_child->head.id); linux_resume_one_process (&event_child->head, event_child->stepping, WSTOPSIG (wstat)); continue; } } /* If this event was not handled above, and is not a SIGTRAP, report it. */ if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP) return wstat; /* If this target does not support breakpoints, we simply report the SIGTRAP; it's of no concern to us. */ if (the_low_target.get_pc == NULL) return wstat; stop_pc = get_stop_pc (); /* bp_reinsert will only be set if we were single-stepping. Notice that we will resume the process after hitting a gdbserver breakpoint; single-stepping to/over one is not supported (yet). */ if (event_child->bp_reinsert != 0) { if (debug_threads) fprintf (stderr, "Reinserted breakpoint.\n"); reinsert_breakpoint (event_child->bp_reinsert); event_child->bp_reinsert = 0; /* Clear the single-stepping flag and SIGTRAP as we resume. */ linux_resume_one_process (&event_child->head, 0, 0); continue; } if (debug_threads) fprintf (stderr, "Hit a (non-reinsert) breakpoint.\n"); if (check_breakpoints (stop_pc) != 0) { /* We hit one of our own breakpoints. We mark it as a pending breakpoint, so that check_removed_breakpoints () will do the PC adjustment for us at the appropriate time. */ event_child->pending_is_breakpoint = 1; event_child->pending_stop_pc = stop_pc; /* Now we need to put the breakpoint back. We continue in the event loop instead of simply replacing the breakpoint right away, in order to not lose signals sent to the thread that hit the breakpoint. Unfortunately this increases the window where another thread could sneak past the removed breakpoint. For the current use of server-side breakpoints (thread creation) this is acceptable; but it needs to be considered before this breakpoint mechanism can be used in more general ways. For some breakpoints it may be necessary to stop all other threads, but that should be avoided where possible. If breakpoint_reinsert_addr is NULL, that means that we can use PTRACE_SINGLESTEP on this platform. Uninsert the breakpoint, mark it for reinsertion, and single-step. Otherwise, call the target function to figure out where we need our temporary breakpoint, create it, and continue executing this process. */ if (the_low_target.breakpoint_reinsert_addr == NULL) { event_child->bp_reinsert = stop_pc; uninsert_breakpoint (stop_pc); linux_resume_one_process (&event_child->head, 1, 0); } else { reinsert_breakpoint_by_bp (stop_pc, (*the_low_target.breakpoint_reinsert_addr) ()); linux_resume_one_process (&event_child->head, 0, 0); } continue; } /* If we were single-stepping, we definitely want to report the SIGTRAP. The single-step operation has completed, so also clear the stepping flag; in general this does not matter, because the SIGTRAP will be reported to the client, which will give us a new action for this thread, but clear it for consistency anyway. It's safe to clear the stepping flag because the only consumer of get_stop_pc () after this point is check_removed_breakpoints, and pending_is_breakpoint is not set. It might be wiser to use a step_completed flag instead. */ if (event_child->stepping) { event_child->stepping = 0; return wstat; } /* A SIGTRAP that we can't explain. It may have been a breakpoint. Check if it is a breakpoint, and if so mark the process information accordingly. This will handle both the necessary fiddling with the PC on decr_pc_after_break targets and suppressing extra threads hitting a breakpoint if two hit it at once and then GDB removes it after the first is reported. Arguably it would be better to report multiple threads hitting breakpoints simultaneously, but the current remote protocol does not allow this. */ if ((*the_low_target.breakpoint_at) (stop_pc)) { event_child->pending_is_breakpoint = 1; event_child->pending_stop_pc = stop_pc; } return wstat; } /* NOTREACHED */ return 0; }
static void __init check_sysemu(void) { unsigned long regs[MAX_REG_NR]; int pid, n, status, count=0; non_fatal("Checking syscall emulation patch for ptrace..."); sysemu_supported = 0; pid = start_ptraced_child(); if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) goto fail; CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if (n < 0) fatal_perror("check_sysemu : wait failed"); if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) fatal("check_sysemu : expected SIGTRAP, got status = %d\n", status); if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0) fatal_perror("check_sysemu : PTRACE_GETREGS failed"); if (PT_SYSCALL_NR(regs) != __NR_getpid) { non_fatal("check_sysemu got system call number %d, " "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid); goto fail; } n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid()); if (n < 0) { non_fatal("check_sysemu : failed to modify system call " "return"); goto fail; } if (stop_ptraced_child(pid, 0, 0) < 0) goto fail_stopped; sysemu_supported = 1; non_fatal("OK\n"); set_using_sysemu(!force_sysemu_disabled); non_fatal("Checking advanced syscall emulation patch for ptrace..."); pid = start_ptraced_child(); if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *) PTRACE_O_TRACESYSGOOD) < 0)) fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed"); while (1) { count++; if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) goto fail; CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if (n < 0) fatal_perror("check_sysemu: wait failed"); if (WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))) { if (!count) { non_fatal("check_sysemu: SYSEMU_SINGLESTEP " "doesn't singlestep"); goto fail; } n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid()); if (n < 0) fatal_perror("check_sysemu : failed to modify " "system call return"); break; } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) count++; else { non_fatal("check_sysemu: expected SIGTRAP or " "(SIGTRAP | 0x80), got status = %d\n", status); goto fail; } } if (stop_ptraced_child(pid, 0, 0) < 0) goto fail_stopped; sysemu_supported = 2; non_fatal("OK\n"); if (!force_sysemu_disabled) set_using_sysemu(sysemu_supported); return; fail: stop_ptraced_child(pid, 1, 0); fail_stopped: non_fatal("missing\n"); }
static unsigned char linux_wait (char *status) { int w; struct thread_info *child = NULL; retry: /* If we were only supposed to resume one thread, only wait for that thread - if it's still alive. If it died, however - which can happen if we're coming from the thread death case below - then we need to make sure we restart the other threads. We could pick a thread at random or restart all; restarting all is less arbitrary. */ if (cont_thread > 0) { child = (struct thread_info *) find_inferior_id (&all_threads, cont_thread); /* No stepping, no signal - unless one is pending already, of course. */ if (child == NULL) linux_resume (0, 0); } enable_async_io (); w = linux_wait_for_event (child); stop_all_processes (); disable_async_io (); /* If we are waiting for a particular child, and it exited, linux_wait_for_event will return its exit status. Similarly if the last child exited. If this is not the last child, however, do not report it as exited until there is a 'thread exited' response available in the remote protocol. Instead, just wait for another event. This should be safe, because if the thread crashed we will already have reported the termination signal to GDB; that should stop any in-progress stepping operations, etc. Report the exit status of the last thread to exit. This matches LinuxThreads' behavior. */ if (all_threads.head == all_threads.tail) { if (WIFEXITED (w)) { fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); *status = 'W'; clear_inferiors (); return ((unsigned char) WEXITSTATUS (w)); } else if (!WIFSTOPPED (w)) { fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); clear_inferiors (); *status = 'X'; return ((unsigned char) WTERMSIG (w)); } } else { if (!WIFSTOPPED (w)) goto retry; } *status = 'T'; return ((unsigned char) WSTOPSIG (w)); }
static void summarize (FILE *fp, const char *fmt, char **command, resource_t *resp) { unsigned long r; /* Elapsed real milliseconds. */ unsigned long v; /* Elapsed virtual (CPU) milliseconds. */ if (WIFSTOPPED (resp->waitstatus)) fprintf (fp, "Command stopped by signal %d\n", WSTOPSIG (resp->waitstatus)); else if (WIFSIGNALED (resp->waitstatus)) fprintf (fp, "Command terminated by signal %d\n", WTERMSIG (resp->waitstatus)); else if (WIFEXITED (resp->waitstatus) && WEXITSTATUS (resp->waitstatus)) fprintf (fp, "Command exited with non-zero status %d\n", WEXITSTATUS (resp->waitstatus)); /* Convert all times to milliseconds. Occasionally, one of these values comes out as zero. Dividing by zero causes problems, so we first check the time value. If it is zero, then we take `evasive action' instead of calculating a value. */ r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000; v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC + resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC; while (*fmt) { switch (*fmt) { case '%': switch (*++fmt) { case '%': /* Literal '%'. */ putc ('%', fp); break; case 'C': /* The command that got timed. */ fprintargv (fp, command, " "); break; case 'D': /* Average unshared data size. */ fprintf (fp, "%lu", MSEC_TO_TICKS (v) == 0 ? 0 : ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) + ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v)); break; case 'E': /* Elapsed real (wall clock) time. */ if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */ fprintf (fp, "%ldh %ldm %02lds", resp->elapsed.tv_sec / 3600, (resp->elapsed.tv_sec % 3600) / 60, resp->elapsed.tv_sec % 60); else fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ resp->elapsed.tv_sec / 60, resp->elapsed.tv_sec % 60, resp->elapsed.tv_usec / 10000); break; case 'F': /* Major page faults. */ fprintf (fp, "%ld", resp->ru.ru_majflt); break; case 'I': /* Inputs. */ fprintf (fp, "%ld", resp->ru.ru_inblock); break; case 'K': /* Average mem usage == data+stack+text. */ fprintf (fp, "%lu", MSEC_TO_TICKS (v) == 0 ? 0 : ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) + ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) + ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v)); break; case 'M': /* Maximum resident set size. */ fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss)); break; case 'O': /* Outputs. */ fprintf (fp, "%ld", resp->ru.ru_oublock); break; case 'P': /* Percent of CPU this job got. */ /* % cpu is (total cpu time)/(elapsed time). */ if (r > 0) fprintf (fp, "%lu%%", (v * 100 / r)); else fprintf (fp, "?%%"); break; case 'R': /* Minor page faults (reclaims). */ fprintf (fp, "%ld", resp->ru.ru_minflt); break; case 'S': /* System time. */ fprintf (fp, "%ld.%02ld", resp->ru.ru_stime.tv_sec, resp->ru.ru_stime.TV_MSEC / 10); break; case 'T': /* System time. */ if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */ fprintf (fp, "%ldh %ldm %02lds", resp->ru.ru_stime.tv_sec / 3600, (resp->ru.ru_stime.tv_sec % 3600) / 60, resp->ru.ru_stime.tv_sec % 60); else fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ resp->ru.ru_stime.tv_sec / 60, resp->ru.ru_stime.tv_sec % 60, resp->ru.ru_stime.tv_usec / 10000); break; case 'U': /* User time. */ fprintf (fp, "%ld.%02ld", resp->ru.ru_utime.tv_sec, resp->ru.ru_utime.TV_MSEC / 10); break; case 'u': /* User time. */ if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */ fprintf (fp, "%ldh %ldm %02lds", resp->ru.ru_utime.tv_sec / 3600, (resp->ru.ru_utime.tv_sec % 3600) / 60, resp->ru.ru_utime.tv_sec % 60); else fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */ resp->ru.ru_utime.tv_sec / 60, resp->ru.ru_utime.tv_sec % 60, resp->ru.ru_utime.tv_usec / 10000); break; case 'W': /* Times swapped out. */ fprintf (fp, "%ld", resp->ru.ru_nswap); break; case 'X': /* Average shared text size. */ fprintf (fp, "%lu", MSEC_TO_TICKS (v) == 0 ? 0 : ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v)); break; case 'Z': /* Page size. */ fprintf (fp, "%d", getpagesize ()); break; case 'c': /* Involuntary context switches. */ fprintf (fp, "%ld", resp->ru.ru_nivcsw); break; case 'e': /* Elapsed real time in seconds. */ fprintf (fp, "%ld.%02ld", resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000); break; case 'k': /* Signals delivered. */ fprintf (fp, "%ld", resp->ru.ru_nsignals); break; case 'p': /* Average stack segment. */ fprintf (fp, "%lu", MSEC_TO_TICKS (v) == 0 ? 0 : ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v)); break; case 'r': /* Incoming socket messages received. */ fprintf (fp, "%ld", resp->ru.ru_msgrcv); break; case 's': /* Outgoing socket messages sent. */ fprintf (fp, "%ld", resp->ru.ru_msgsnd); break; case 't': /* Average resident set size. */ fprintf (fp, "%lu", MSEC_TO_TICKS (v) == 0 ? 0 : ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v)); break; case 'w': /* Voluntary context switches. */ fprintf (fp, "%ld", resp->ru.ru_nvcsw); break; case 'x': /* Exit status. */ fprintf (fp, "%d", WEXITSTATUS (resp->waitstatus)); break; case '\0': putc ('?', fp); return; default: putc ('?', fp); putc (*fmt, fp); } ++fmt; break; case '\\': /* Format escape. */ switch (*++fmt) { case 't': putc ('\t', fp); break; case 'n': putc ('\n', fp); break; case '\\': putc ('\\', fp); break; default: putc ('?', fp); putc ('\\', fp); putc (*fmt, fp); } ++fmt; break; default: putc (*fmt++, fp); } if (ferror (fp)) bb_error_msg_and_die("write error"); } putc ('\n', fp); if (ferror (fp)) bb_error_msg_and_die("write error"); }
bool run_test(int cpu) { int status; pid_t pid = fork(); pid_t wpid; if (pid < 0) { ksft_print_msg("fork() failed: %s\n", strerror(errno)); return false; } if (pid == 0) child(cpu); wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGSTOP) { ksft_print_msg("child did not stop with SIGSTOP: %s\n", strerror(errno)); return false; } if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) { if (errno == EIO) { ksft_exit_skip( "ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s\n", strerror(errno)); } ksft_print_msg("ptrace(PTRACE_SINGLESTEP) failed: %s\n", strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { ksft_print_msg("waitpid() failed: $s\n", strerror(errno)); return false; } if (WIFEXITED(status)) { ksft_print_msg("child did not single-step: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGTRAP) { ksft_print_msg("child did not stop with SIGTRAP: %s\n", strerror(errno)); return false; } if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n", strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFEXITED(status)) { ksft_print_msg("child did not exit after PTRACE_CONT: %s\n", strerror(errno)); return false; } return true; }
/** * Signal handler for the server. */ void handle_sigs(void) { pid_t chld; int chld_status; int i; int do_exit; switch(sig_flag){ case 0: break; /* do nothing*/ case SIGPIPE: /* SIGPIPE might be rarely received on use of exec module; simply ignore it */ LM_WARN("SIGPIPE received and ignored\n"); break; case SIGINT: case SIGTERM: /* we end the program in all these cases */ if (sig_flag==SIGINT) LM_DBG("INT received, program terminates\n"); else LM_DBG("SIGTERM received, program terminates\n"); /* first of all, kill the children also */ kill_all_children(SIGTERM); if (signal(SIGALRM, sig_alarm_kill) == SIG_ERR ) { LM_ERR("could not install SIGALARM handler\n"); /* continue, the process will die anyway if no * alarm is installed which is exactly what we want */ } alarm(OPENSER_SHUTDOWN_TIME); /* 1 minute close timeout */ while(wait(0) > 0); /* Wait for all the children to terminate */ signal(SIGALRM, sig_alarm_abort); cleanup(1); /* cleanup & show status*/ alarm(0); signal(SIGALRM, SIG_IGN); dprint("Thank you for flying " NAME "\n"); exit(0); break; case SIGUSR1: #ifdef PKG_MALLOC LOG(memlog, "Memory status (pkg):\n"); pkg_status(); #endif #ifdef SHM_MEM LOG(memlog, "Memory status (shm):\n"); shm_status(); #endif break; case SIGCHLD: do_exit = 0; while ((chld=waitpid( -1, &chld_status, WNOHANG ))>0) { /* is it a process we know about? */ for( i=0 ; i<counted_processes ; i++ ) if (pt[i].pid==chld) break; if (i==counted_processes) { LM_DBG("unkown child process %d ended. Ignoring\n",chld); continue; } do_exit = 1; /* process the signal */ if (WIFEXITED(chld_status)) LM_INFO("child process %d exited normally," " status=%d\n", chld, WEXITSTATUS(chld_status)); else if (WIFSIGNALED(chld_status)) { LM_INFO("child process %d exited by a signal" " %d\n", chld, WTERMSIG(chld_status)); #ifdef WCOREDUMP LM_INFO("core was %sgenerated\n", WCOREDUMP(chld_status) ? "" : "not " ); #endif }else if (WIFSTOPPED(chld_status)) LM_INFO("child process %d stopped by a" " signal %d\n", chld, WSTOPSIG(chld_status)); } if (!do_exit) break; LM_INFO("terminating due to SIGCHLD\n"); /* exit */ kill_all_children(SIGTERM); if (signal(SIGALRM, sig_alarm_kill) == SIG_ERR ) { LM_ERR("could not install SIGALARM handler\n"); /* continue, the process will die anyway if no * alarm is installed which is exactly what we want */ } alarm(OPENSER_SHUTDOWN_TIME); /* 1 minute close timeout */ while(wait(0) > 0); /* wait for all the children to terminate*/ signal(SIGALRM, sig_alarm_abort); cleanup(1); /* cleanup & show status*/ alarm(0); signal(SIGALRM, SIG_IGN); LM_DBG("terminating due to SIGCHLD\n"); exit(0); break; case SIGHUP: /* ignoring it*/ LM_DBG("SIGHUP received, ignoring it\n"); break; default: LM_CRIT("unhandled signal %d\n", sig_flag); } sig_flag=0; }
int main(int argc, char **argv) { int i; long page_size=getpagesize(); double error; long long start_before,stop_after; void *addr[MAX_EVENTS],*addr2[MAX_EVENTS]; struct perf_event_attr pe; int fd[MAX_EVENTS],fd2[MAX_EVENTS],ret1,ret2; int status; unsigned long long values[MAX_EVENTS],enabled[MAX_EVENTS],running[MAX_EVENTS]; unsigned long long values2[MAX_EVENTS],enabled2[MAX_EVENTS],running2[MAX_EVENTS]; int count=2; pid_t pid,pid2; quiet=test_quiet(); if (!quiet) { printf("This test checks if rdpmc multi-attach works.\n\n"); } /* See if we support rdpmc access */ if (!detect_rdpmc(quiet)) { test_skip(test_string); } /*********************************/ /* start and pause two children */ /*********************************/ /* fork a child */ pid = fork(); if ( pid < 0 ) { fprintf(stderr,"Failed fork\n"); test_fail(test_string); } else if (pid==0) { /* in child */ exit(wait_for_attach_and_loop(1)); } if ( ptrace( PTRACE_ATTACH, pid, NULL, NULL ) == -1 ) { fprintf(stderr,"Error attaching to %d\n",pid); return -1; } if ( waitpid( pid, &status, 0 ) == -1 ) { fprintf(stderr,"Error waitpid %d\n",pid); return -1; } if ( WIFSTOPPED( status ) == 0 ) { fprintf(stderr,"WIFSTOPPED didn't happen %d\n",pid); test_fail(test_string); } /* fork a second child */ pid2 = fork(); if ( pid2 < 0 ) { fprintf(stderr,"Failed fork\n"); test_fail(test_string); } else if (pid2==0) { /* in child */ exit(wait_for_attach_and_loop(2)); } if ( ptrace( PTRACE_ATTACH, pid2, NULL, NULL ) == -1 ) { fprintf(stderr,"Error attaching to %d\n",pid2); return -1; } if ( waitpid( pid2, &status, 0 ) == -1 ) { fprintf(stderr,"Error waitpid %d\n",pid2); return -1; } if ( WIFSTOPPED( status ) == 0 ) { fprintf(stderr,"WIFSTOPPED didn't happen %d\n",pid2); test_fail(test_string); } /*****************************/ /* TEST START/WORK/READ/STOP */ /*****************************/ /* Open event, pid1 */ memset(&pe,0,sizeof(struct perf_event_attr)); pe.type=PERF_TYPE_HARDWARE; pe.size=sizeof(struct perf_event_attr); fd[0]=-1; for(i=0;i<count;i++) { if (i==0) { pe.config=PERF_COUNT_HW_INSTRUCTIONS; pe.disabled=1; pe.pinned=1; } else { pe.config=PERF_COUNT_HW_CPU_CYCLES; pe.disabled=0; pe.pinned=0; } fd[i]=perf_event_open(&pe,pid,-1,fd[0],0); if (fd[i]<0) { fprintf(stderr,"Error opening event %d\n",i); test_fail(test_string); } /* mmap() event */ addr[i]=mmap(NULL,page_size, PROT_READ, MAP_SHARED,fd[i],0); if (addr[i] == MAP_FAILED) { fprintf(stderr,"Error mmap()ing event %d!\n",i); test_fail(test_string); } } /* Open event, pid2 */ memset(&pe,0,sizeof(struct perf_event_attr)); pe.type=PERF_TYPE_HARDWARE; pe.size=sizeof(struct perf_event_attr); fd2[0]=-1; for(i=0;i<count;i++) { if (i==0) { pe.config=PERF_COUNT_HW_INSTRUCTIONS; pe.disabled=1; pe.pinned=1; } else { pe.config=PERF_COUNT_HW_CPU_CYCLES; pe.disabled=0; pe.pinned=0; } fd2[i]=perf_event_open(&pe,pid2,-1,fd2[0],0); if (fd2[i]<0) { fprintf(stderr,"Error opening event %d\n",i); test_fail(test_string); } /* mmap() event */ addr2[i]=mmap(NULL,page_size, PROT_READ, MAP_SHARED,fd2[i],0); if (addr2[i] == MAP_FAILED) { fprintf(stderr,"Error mmap()ing event %d!\n",i); test_fail(test_string); } } /* start */ start_before=rdtsc(); ret1=ioctl(fd[0], PERF_EVENT_IOC_ENABLE,0); /* start up pid */ if ( ptrace( PTRACE_CONT, pid, NULL, NULL ) == -1 ) { fprintf(stderr,"Couldn't continue %d\n",pid); return -1; } if ( waitpid( pid, &status, 0 ) == -1 ) { fprintf(stderr,"Couldn't waitpid() %d\n",pid ); return -1; } if ( WIFSTOPPED( status ) == 0 ) { test_fail(test_string); } if ( WSTOPSIG( status ) != SIGSTOP ) { test_fail(test_string); } ret1=ioctl(fd2[0], PERF_EVENT_IOC_ENABLE,0); /* start up pid2 */ if ( ptrace( PTRACE_CONT, pid2, NULL, NULL ) == -1 ) { fprintf(stderr,"Couldn't continue %d\n",pid2); return -1; } if ( waitpid( pid2, &status, 0 ) == -1 ) { fprintf(stderr,"Couldn't waitpid() %d\n",pid2 ); return -1; } if ( WIFSTOPPED( status ) == 0 ) { test_fail(test_string); } if ( WSTOPSIG( status ) != SIGSTOP ) { test_fail(test_string); } /* Wait for the SIGSTOP. */ if ( ptrace( PTRACE_CONT, pid, NULL, NULL ) == -1 ) { fprintf(stderr, "sigstop fail\n" ); return -1; } if ( waitpid( pid, &status, 0 ) == -1 ) { fprintf(stderr, "waitpid() pid\n" ); return -1; } if ( WIFSTOPPED( status ) == 0 ) { test_fail(test_string); } if ( WSTOPSIG( status ) != SIGSTOP ) { test_fail(test_string); } /* Wait for the SIGSTOP. */ if ( ptrace( PTRACE_CONT, pid2, NULL, NULL ) == -1 ) { fprintf(stderr, "sigstop fail\n" ); return -1; } if ( waitpid( pid2, &status, 0 ) == -1 ) { fprintf(stderr, "waitpid() pid\n" ); return -1; } if ( WIFSTOPPED( status ) == 0 ) { test_fail(test_string); } if ( WSTOPSIG( status ) != SIGSTOP ) { test_fail(test_string); } /* read */ for(i=0;i<count;i++) { values[i] = mmap_read_self(addr[i], &enabled[i], &running[i]); } /* stop */ ret2=ioctl(fd[0], PERF_EVENT_IOC_DISABLE,0); ret2=ioctl(fd2[0], PERF_EVENT_IOC_DISABLE,0); for(i=0;i<count;i++) { values2[i] = mmap_read_self(addr2[i], &enabled2[i], &running2[i]); } stop_after=rdtsc(); if (ret1<0) { fprintf(stderr,"Error starting!\n"); test_fail(test_string); } if (ret2<0) { fprintf(stderr,"Error stopping!\n"); test_fail(test_string); } if (values[0]<0) { if (!quiet) printf("rdpmc support not available.\n"); test_yellow_no(test_string); } for(i=0;i<count;i++) { close(fd[i]); close(fd2[i]); munmap(addr[i],page_size); munmap(addr2[i],page_size); } if (!quiet) { printf("Trying attach: %lld cycles\n", stop_after-start_before); for(i=0;i<count;i++) { if (values[i]==-1) { printf("\t* RDPMC 1 Event %x -- rdpmc not supported\n",i); } else { printf("\t* RDPMC 1 Event %x -- count: %lld enabled %lld running: %lld\n", i,values[i],enabled[i],running[i]); } } for(i=0;i<count;i++) { if (values2[i]==-1) { printf("\t* RDPMC 2 Event %x -- rdpmc not supported\n",i); } else { printf("\t* RDPMC 2 Event %x -- count: %lld enabled %lld running: %lld\n", i,values2[i],enabled2[i],running2[i]); } } } if (!quiet) printf("\n"); if ((values[0]!=-1) || (values2[0]!=-1)) { error=display_error(values[0], values[0], values[0], 1000000ULL,quiet); if ((error>10.0) || ( error<-10.0)) { if (!quiet) printf("Error out of range!\n"); test_fail(test_string); } error=display_error(values2[0], values2[0], values2[0], 2000000ULL,quiet); if ((error>10.0) || ( error<-10.0)) { if (!quiet) printf("Error out of range!\n"); test_fail(test_string); } if (!quiet) { printf("Should not have worked\n"); } test_fail(test_string); } for(i=0;i<count;i++) { close(fd[i]); munmap(addr[i],page_size); } test_pass(test_string); return 0; }
/* some of this code is based on /usr/bin/time source code */ void print_stats(struct timeval *start, struct timeval *end, struct rusage *ru, int status) { unsigned long r; /* Elapsed real milliseconds. */ unsigned long v; /* Elapsed virtual (CPU) milliseconds. */ end->tv_sec -= start->tv_sec; if (end->tv_usec < start->tv_usec) { /* Manually carry a one from the seconds field. */ end->tv_usec += 1000000; --end->tv_sec; } end->tv_usec -= start->tv_usec; if (WIFSTOPPED (status)) { fprintf(FP, "Command stopped by signal %d\n", WSTOPSIG (status)); } else if (WIFSIGNALED (status)) { fprintf(FP, "Command terminated by signal %d\n", WTERMSIG (status)); } else if (WIFEXITED (status) && WEXITSTATUS (status)) { fprintf(FP, "Command exited with non-zero status %d\n", WEXITSTATUS (status)); } /* Convert all times to milliseconds. Occasionally, one of these values comes out as zero. Dividing by zero causes problems, so we first check the time value. If it is zero, then we take `evasive action' instead of calculating a value. */ r = end->tv_sec * 1000 + end->tv_usec / 1000; v = ru->ru_utime.tv_sec * 1000 + ru->ru_utime.TV_MSEC + ru->ru_stime.tv_sec * 1000 + ru->ru_stime.TV_MSEC; /* Elapsed real (wall clock) time. */ if (end->tv_sec >= 3600) { /* One hour -> h:m:s. */ fprintf(FP, "%ld:%02ld:%02ldelapsed ", end->tv_sec / 3600, (end->tv_sec % 3600) / 60, end->tv_sec % 60); } else { fprintf(FP, "%ld:%02ld.%02ldelapsed ", /* -> m:s. */ end->tv_sec / 60, end->tv_sec % 60, end->tv_usec / 10000); } /* % cpu is (total cpu time)/(elapsed time). */ if (r > 0) fprintf(FP, "%lu%%CPU ", (v * 100 / r)); else fprintf(FP, "?%%CPU "); fprintf(FP, "%ld.%02lduser ", ru->ru_utime.tv_sec, ru->ru_utime.TV_MSEC / 10); fprintf(FP, "%ld.%02ldsystem ", ru->ru_stime.tv_sec, ru->ru_stime.TV_MSEC / 10); fprintf(FP, "(%ldmajor+%ldminor)pagefaults %ldswaps\n", ru->ru_majflt,/* Major page faults. */ ru->ru_minflt,/* Minor page faults. */ ru->ru_nswap); /* times swapped out */ fprintf(FP, "(%lu tot, %lu RSS, %lu data, %lu stk, %lu exe, %lu lib)k\n", vmstats.VmSize, vmstats.VmRSS, vmstats.VmData, vmstats.VmStk, vmstats.VmExe, vmstats.VmLib); #if VERBOSE fprintf(FP, "Memory usage:\n"); fprintf(FP, "\tVmSize: %d kB\n", vmstats.VmSize); fprintf(FP, "\tVmLck: %d kB\n", vmstats.VmLck); fprintf(FP, "\tVmRSS: %d kB\n", vmstats.VmRSS); fprintf(FP, "\tVmData: %d kB\n", vmstats.VmData); fprintf(FP, "\tVmStk: %d kB\n", vmstats.VmStk); fprintf(FP, "\tVmExe: %d kB\n", vmstats.VmExe); fprintf(FP, "\tVmLib: %d kB\n", vmstats.VmLib); #endif }
int64_t HHVM_FUNCTION(pcntl_wstopsig, int status) { return WSTOPSIG(status); }
static void ppp_watch_cb (GPid pid, gint status, gpointer user_data) { NMPPPManager *manager = NM_PPP_MANAGER (user_data); NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager); guint err; g_assert (pid == priv->pid); if (WIFEXITED (status)) { err = WEXITSTATUS (status); if (err != 0) ppp_exit_code (err, priv->pid); } else if (WIFSTOPPED (status)) { nm_log_info (LOGD_PPP, "pppd pid %d stopped unexpectedly with signal %d", priv->pid, WSTOPSIG (status)); } else if (WIFSIGNALED (status)) { nm_log_info (LOGD_PPP, "pppd pid %d died with signal %d", priv->pid, WTERMSIG (status)); } else nm_log_info (LOGD_PPP, "pppd pid %d died from an unknown cause", priv->pid); nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid); priv->pid = 0; g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD); }
int stop_crfs(const char *crfs_mnt, int crfs_pid) { extern int mt_id; int rv; char cmd[MAX_CMD_LEN]; if (strlen(crfs_mnt) != 0) { snprintf(cmd, MAX_CMD_LEN, "fusermount -u %s > /dev/null 2>&1", crfs_mnt); PRINT_DEBUG(DEBUG_Fork_verbose, "Stop CRFS: %s\n", cmd); rv = system(cmd); if (rv == -1) { PRINT_DEBUG(DEBUG_Fork_verbose, "system call to fusermount failed\n"); } else { PRINT_DEBUG(DEBUG_Fork_verbose, "system call to fusermount returned %d\n", rv); if (WIFEXITED(rv)) { PRINT_DEBUG(DEBUG_Fork_verbose, "fusermount exited with status %d\n", WEXITSTATUS(rv)); } else if (WIFSIGNALED(rv)) { PRINT_DEBUG(DEBUG_Fork_verbose, "fusermount terminated with signal %d\n", WTERMSIG(rv)); } else if (WIFSTOPPED(rv)) { PRINT_DEBUG(DEBUG_Fork_verbose, "fusermount stopped with signal %d\n", WSTOPSIG(rv)); } else if (WIFCONTINUED(rv)) { PRINT_DEBUG(DEBUG_Fork_verbose, "fusermount continued\n"); } } } if (crfs_pid > 0) { rv = kill(crfs_pid, SIGTERM); PRINT_DEBUG(DEBUG_Fork_verbose, "kill with SIGTERM ret=%d\n", rv); usleep(100000); // wait for CRFS to terminate rv = kill(crfs_pid, SIGINT); PRINT_DEBUG(DEBUG_Fork_verbose, "kill with SIGINT ret=%d\n", rv); } //snprintf(path, MAX_PATH_LEN, "/tmp/cr-%s/", crfs_sessionid); //rv = rmdir(path); return 0; }
unsigned ReqProg_load( void ) { char **args; char *parms; char *parm_start; int i; char exe_name[PATH_MAX]; char *name; pid_t save_pgrp; prog_load_req *acc; prog_load_ret *ret; unsigned len; int status; acc = GetInPtr( 0 ); ret = GetOutPtr( 0 ); last_sig = -1; have_rdebug = FALSE; dbg_dyn = NULL; at_end = FALSE; parms = (char *)GetInPtr( sizeof( *acc ) ); parm_start = parms; len = GetTotalSize() - sizeof( *acc ); if( acc->true_argv ) { i = 1; for( ;; ) { if( len == 0 ) break; if( *parms == '\0' ) { i++; } ++parms; --len; } args = alloca( i * sizeof( *args ) ); parms = parm_start; len = GetTotalSize() - sizeof( *acc ); i = 1; for( ;; ) { if( len == 0 ) break; if( *parms == '\0' ) { args[i++] = parms + 1; } ++parms; --len; } args[i - 1] = NULL; } else { while( *parms != '\0' ) { ++parms; --len; } ++parms; --len; i = SplitParms( parms, NULL, len ); args = alloca( (i + 2) * sizeof( *args ) ); args[SplitParms( parms, &args[1], len ) + 1] = NULL; } args[0] = parm_start; attached = TRUE; pid = RunningProc( args[0], &name ); if( pid == 0 || ptrace( PTRACE_ATTACH, pid, NULL, NULL ) == -1 ) { attached = FALSE; args[0] = name; if( FindFilePath( TRUE, args[0], exe_name ) == 0 ) { exe_name[0] = '\0'; } save_pgrp = getpgrp(); setpgid( 0, OrigPGrp ); pid = fork(); if( pid == -1 ) return( 0 ); if( pid == 0 ) { if( ptrace( PTRACE_TRACEME, 0, NULL, NULL ) < 0 ) { exit( 1 ); } execve( exe_name, (const char **)args, (const char **)dbg_environ ); exit( 1 ); /* failsafe */ } setpgid( 0, save_pgrp ); } else if( pid ) { GetExeNameFromPid( pid, exe_name, PATH_MAX ); } ret->flags = 0; ret->mod_handle = 0; if( (pid != -1) && (pid != 0) ) { int status; ret->task_id = pid; ret->flags |= LD_FLAG_IS_PROT | LD_FLAG_IS_32; /* wait until it hits _start (upon execve) or gives us a SIGSTOP (if attached) */ if( waitpid( pid, &status, 0 ) < 0 ) goto fail; if( !WIFSTOPPED( status ) ) goto fail; if( attached ) { ret->flags |= LD_FLAG_IS_STARTED; if( WSTOPSIG( status ) != SIGSTOP ) goto fail; } else { if( WSTOPSIG( status ) != SIGTRAP ) goto fail; } #if defined( MD_x86 ) if( !GetFlatSegs( &flatCS, &flatDS ) ) goto fail; #endif dbg_dyn = GetDebuggeeDynSection( exe_name ); AddProcess(); errno = 0; } ret->err = errno; if( ret->err != 0 ) { pid = 0; } CONV_LE_32( ret->err ); CONV_LE_32( ret->task_id ); CONV_LE_32( ret->mod_handle ); return( sizeof( *ret ) ); fail: if( pid != 0 && pid != -1 ) { if( attached ) { ptrace( PTRACE_DETACH, pid, NULL, NULL ); attached = FALSE; } else { ptrace( PTRACE_KILL, pid, NULL, NULL ); waitpid( pid, &status, 0 ); } } pid = 0; CONV_LE_32( ret->err ); CONV_LE_32( ret->task_id ); CONV_LE_32( ret->mod_handle ); return( 0 ); }
static bool client_exec_script(struct master_service_connection *conn) { const char *const *args; string_t *input; void *buf; size_t prev_size, scanpos; bool header_complete = FALSE; ssize_t ret; int status; pid_t pid; net_set_nonblock(conn->fd, FALSE); input = buffer_create_dynamic(pool_datastack_create(), IO_BLOCK_SIZE); /* Input contains: VERSION .. <lf> [alarm=<secs> <lf>] "noreply" | "-" (or anything really) <lf> arg 1 <lf> arg 2 <lf> ... <lf> DATA This is quite a horrible protocol. If alarm is specified, it MUST be before "noreply". If "noreply" isn't given, something other string (typically "-") must be given which is eaten away. */ alarm(SCRIPT_READ_TIMEOUT_SECS); scanpos = 1; while (!header_complete) { const unsigned char *pos, *end; prev_size = input->used; buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE); /* peek in socket input buffer */ ret = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK); if (ret <= 0) { buffer_set_used_size(input, prev_size); if (strchr(str_c(input), '\n') != NULL) script_verify_version(t_strcut(str_c(input), '\n')); if (ret < 0) i_fatal("recv(MSG_PEEK) failed: %m"); i_fatal("recv(MSG_PEEK) failed: disconnected"); } /* scan for final \n\n */ pos = CONST_PTR_OFFSET(input->data, scanpos); end = CONST_PTR_OFFSET(input->data, prev_size + ret); for (; pos < end; pos++) { if (pos[-1] == '\n' && pos[0] == '\n') { header_complete = TRUE; pos++; break; } } scanpos = pos - (const unsigned char *)input->data; /* read data for real (up to and including \n\n) */ ret = recv(conn->fd, buf, scanpos-prev_size, 0); if (prev_size+ret != scanpos) { if (ret < 0) i_fatal("recv() failed: %m"); if (ret == 0) i_fatal("recv() failed: disconnected"); i_fatal("recv() failed: size of definitive recv() differs from peek"); } buffer_set_used_size(input, scanpos); } alarm(0); /* drop the last two LFs */ buffer_set_used_size(input, scanpos-2); args = t_strsplit(str_c(input), "\n"); script_verify_version(*args); args++; if (*args != NULL) { if (strncmp(*args, "alarm=", 6) == 0) { unsigned int seconds; if (str_to_uint(*args + 6, &seconds) < 0) i_fatal("invalid alarm option"); alarm(seconds); args++; } if (strcmp(*args, "noreply") == 0) { /* no need to fork and check exit status */ exec_child(conn, args + 1); i_unreached(); } if (**args == '\0') i_fatal("empty options"); args++; } if ((pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); return FALSE; } if (pid == 0) { /* child */ exec_child(conn, args); i_unreached(); } /* parent */ /* check script exit status */ if (waitpid(pid, &status, 0) < 0) { i_error("waitpid() failed: %m"); return FALSE; } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret != 0) { i_error("Script terminated abnormally, exit status %d", (int)ret); return FALSE; } } else if (WIFSIGNALED(status)) { i_error("Script terminated abnormally, signal %d", WTERMSIG(status)); return FALSE; } else if (WIFSTOPPED(status)) { i_fatal("Script stopped, signal %d", WSTOPSIG(status)); return FALSE; } else { i_fatal("Script terminated abnormally, return status %d", status); return FALSE; } return TRUE; }
static unsigned ProgRun( int step ) { static int ptrace_sig = 0; static int ld_state = 0; user_regs_struct regs; int status; prog_go_ret *ret; void (*old)(int); int debug_continue; if( pid == 0 ) return( 0 ); ret = GetOutPtr( 0 ); if( at_end ) { ptrace_sig = 0; ret->conditions = COND_TERMINATE; goto end; } /* we only want child-generated SIGINTs now */ do { old = setsig( SIGINT, SIG_IGN ); if( step ) { Out( "PTRACE_SINGLESTEP\n" ); if( ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)ptrace_sig ) == -1 ) perror( "PTRACE_SINGLESTEP" ); } else { Out( "PTRACE_CONT\n" ); if( ptrace( PTRACE_CONT, pid, NULL, (void *)ptrace_sig ) == -1 ) perror( "PTRACE_CONT" ); } waitpid( pid, &status, 0 ); setsig( SIGINT, old ); #if defined( MD_x86 ) ptrace( PTRACE_GETREGS, pid, NULL, ®s ); #elif defined( MD_ppc ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_NIP, NULL ); regs.esp = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_R1, NULL ); #elif defined( MD_mips ) regs.eip = ptrace( PTRACE_PEEKUSER, pid, (void *)PC, NULL ); regs.esp = ptrace( PTRACE_PEEKUSER, pid, (void *)29, NULL ); #endif Out( " eip " ); OutNum( regs.eip ); Out( "\n" ); debug_continue = FALSE; if( WIFSTOPPED( status ) ) { switch( ( ptrace_sig = WSTOPSIG( status ) ) ) { case SIGSEGV: case SIGILL: case SIGFPE: case SIGABRT: case SIGBUS: case SIGQUIT: case SIGSYS: last_sig = ptrace_sig; ret->conditions = COND_EXCEPTION; ptrace_sig = 0; break; case SIGINT: ret->conditions = COND_USER; ptrace_sig = 0; break; case SIGTRAP: ret->conditions = step ? COND_TRACE : COND_BREAK; Out( "sigtrap\n" ); ptrace_sig = 0; break; default: /* For signals that we do not wish to handle, we need * to continue the debuggee until we get a signal * that we need to handle */ Out( "Unknown signal " ); OutNum( ptrace_sig ); Out( "\n" ); debug_continue = TRUE; break; } } else if( WIFEXITED( status ) ) { Out( "WIFEXITED\n" ); at_end = TRUE; ret->conditions = COND_TERMINATE; ptrace_sig = 0; goto end; } } while( debug_continue ); if( ret->conditions == COND_BREAK ) { #if defined( MD_x86 ) if( regs.eip == rdebug.r_brk + sizeof( old_ld_bp ) ) { #elif defined( MD_ppc ) || defined( MD_mips ) if( regs.eip == rdebug.r_brk ) { #endif int psig = 0; void (*oldsig)(int); bp_t opcode = BRK_POINT; /* The dynamic linker breakpoint was hit, meaning that * libraries are being loaded or unloaded. This gets a bit * tricky because we must restore the original code that was * at the breakpoint and execute it, but we still want to * keep the breakpoint. */ WriteMem( pid, &old_ld_bp, rdebug.r_brk, sizeof( old_ld_bp ) ); ReadMem( pid, &rdebug, (addr48_off)dbg_rdebug, sizeof( rdebug ) ); Out( "ld breakpoint hit, state is " ); switch( rdebug.r_state ) { case RT_ADD: Out( "RT_ADD\n" ); ld_state = RT_ADD; AddOneLib( rdebug.r_map ); break; case RT_DELETE: Out( "RT_DELETE\n" ); ld_state = RT_DELETE; break; case RT_CONSISTENT: Out( "RT_CONSISTENT\n" ); if( ld_state == RT_DELETE ) DelOneLib( rdebug.r_map ); ld_state = RT_CONSISTENT; break; default: Out( "error!\n" ); break; } regs.orig_eax = -1; #if defined( MD_x86 ) regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif oldsig = setsig( SIGINT, SIG_IGN ); ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)psig ); waitpid( pid, &status, 0 ); setsig( SIGINT, oldsig ); WriteMem( pid, &opcode, rdebug.r_brk, sizeof( old_ld_bp ) ); ret->conditions = COND_LIBRARIES; } else { #if defined( MD_x86 ) Out( "decrease eip(sigtrap)\n" ); regs.orig_eax = -1; regs.eip--; ptrace( PTRACE_SETREGS, pid, NULL, ®s ); #endif } } orig_eax = regs.orig_eax; last_eip = regs.eip; ret->program_counter.offset = regs.eip; ret->program_counter.segment = regs.cs; ret->stack_pointer.offset = regs.esp; ret->stack_pointer.segment = regs.ss; ret->conditions |= COND_CONFIG; /* If debuggee has dynamic section, try getting the r_debug struct * every time the debuggee stops. The r_debug data may not be available * immediately after the debuggee process loads. */ if( !have_rdebug && (dbg_dyn != NULL) ) { if( Get_ld_info( pid, dbg_dyn, &rdebug, &dbg_rdebug ) ) { bp_t opcode; AddInitialLibs( rdebug.r_map ); have_rdebug = TRUE; ret->conditions |= COND_LIBRARIES; /* Set a breakpoint in dynamic linker. That way we can be * informed on dynamic library load/unload events. */ ReadMem( pid, &old_ld_bp, rdebug.r_brk, sizeof( old_ld_bp ) ); Out( "Setting ld breakpoint at " ); OutNum( rdebug.r_brk ); Out( " old opcode was " ); OutNum( old_ld_bp ); Out( "\n" ); opcode = BRK_POINT; WriteMem( pid, &opcode, rdebug.r_brk, sizeof( opcode ) ); } } end: CONV_LE_32( ret->stack_pointer.offset ); CONV_LE_16( ret->stack_pointer.segment ); CONV_LE_32( ret->program_counter.offset ); CONV_LE_16( ret->program_counter.segment ); CONV_LE_16( ret->conditions ); return( sizeof( *ret ) ); } unsigned ReqProg_step( void ) { return( ProgRun( TRUE ) ); } unsigned ReqProg_go( void ) { return( ProgRun( FALSE ) ); } unsigned ReqRedirect_stdin( void ) { redirect_stdin_ret *ret; ret = GetOutPtr( 0 ); ret->err = 1; return( sizeof( *ret ) ); }
void StartProg( char *cmd, char *prog, char *full_args, char *dos_args ) { char exe_name[PATH_MAX]; pid_t save_pgrp; pid_t pid; int status; MaxThread = 0; GrowArrays( 1 ); HaveRdebug = FALSE; DbgDyn = NULL; OrigPGrp = getpgrp(); Attached = TRUE; pid = Pid = SamplePid; /* allow attaching to existing process by pid */ if( pid == 0 || ptrace( PTRACE_ATTACH, pid, NULL, NULL ) == -1 ) { int num_args; size_t len; char **argv; Attached = FALSE; /* massage 'full_args' into argv format */ len = strlen( full_args ); num_args = SplitParms( full_args, NULL, len ); argv = alloca( (num_args + 2) * sizeof( *argv ) ); argv[SplitParms( full_args, &argv[1], len ) + 1] = NULL; argv[0] = prog; Output( MsgArray[MSG_SAMPLE_1 - ERR_FIRST_MESSAGE] ); Output( prog ); Output( "\n" ); save_pgrp = getpgrp(); setpgid( 0, OrigPGrp ); pid = fork(); if( pid == -1 ) InternalError( MsgArray[MSG_SAMPLE_3 - ERR_FIRST_MESSAGE] ); if( pid == 0 ) { int rc; if( ptrace( PTRACE_TRACEME, 0, NULL, NULL ) < 0 ) { InternalError( MsgArray[MSG_SAMPLE_4 - ERR_FIRST_MESSAGE] ); } dbg_printf( "executing '%s'\n", prog ); for( rc = 0; argv[rc] != NULL; ++rc ) dbg_printf( "argv[%d] = '%s'\n", rc, argv[rc] ); rc = execve( prog, (char const * const *)argv, (char const * const *)environ ); dbg_printf( "execve() failed, returned %d\n", rc ); InternalError( MsgArray[MSG_SAMPLE_3 - ERR_FIRST_MESSAGE] ); // failsafe } setpgid( 0, save_pgrp ); strcpy( exe_name, prog ); } else if( pid ) { GetExeNameFromPid( pid, exe_name, PATH_MAX ); Output( MsgArray[MSG_SAMPLE_1 - ERR_FIRST_MESSAGE] ); Output( exe_name ); Output( "\n" ); } if( (pid != -1) && (pid != 0) ) { /* wait until it hits _start (upon execve) or gives us a SIGSTOP (if attached) */ if( waitpid( pid, &status, 0 ) < 0 ) goto fail; if( !WIFSTOPPED( status ) ) goto fail; if( Attached ) { if( WSTOPSIG( status ) != SIGSTOP ) goto fail; } else { if( WSTOPSIG( status ) != SIGTRAP ) goto fail; } DbgDyn = GetDebuggeeDynSection( exe_name ); errno = 0; } if( errno != 0 ) { pid = 0; } else { /* record information about main executable and initialize shared * library tracking */ InitLibMap(); CodeLoad( exe_name, 0, SAMP_MAIN_LOAD ); SampleLoop( pid ); FiniLibMap(); } return; fail: if( pid != 0 && pid != -1 ) { if( Attached ) { ptrace( PTRACE_DETACH, pid, NULL, NULL ); Attached = FALSE; } else { ptrace( PTRACE_KILL, pid, NULL, NULL ); waitpid( pid, &status, 0 ); } } InternalError( MsgArray[MSG_SAMPLE_5 - ERR_FIRST_MESSAGE] ); }
int main(int nargs, char *args[]) { char *line, *words[MAX_WORDS]; char string[1024],combined[1024],line_s[MAX_LINE]; char c, *dir=NULL,*cwd, pwd[PATH_MAX]; char username[128],password[128],viFile[256],viTemp[256]; char comname[128],authkey[80],authkeyword[64],key[64]; int gotauthkey=0,gotusername=0,gotvi=0,gotnew=0; int res, i, n, status, nwords=0; FILE *auth_fp = (FILE *)0; FILE *key_fp = (FILE *)0; pid_t wpid, pid; char mode[] = "0755"; struct stat stbuf; // get server login user and password while ((c = getopt(nargs, args, "dhu:l:")) != -1) switch (c) { case 'h': // help printf("Usage: %s {-d} {-h} {-u <username>}\n\n",args[0]); printf("-d : don't delete /tmp files\n"); printf("-h : help\n"); printf("-u : commands.com username\n"); exit(0); case 'u': // username strcpy(username,optarg); gotusername = 1; strcpy(password,getpass("Password: "******"Usage: %s {-d} {-h} {-u <username>}\n\n",args[0]); printf("-d : don't delete /tmp files\n"); printf("-h : help\n"); printf("-u : commands.com username\n"); exit(0); break; } // if we have both the username and password, login and // get the authkey // get the authorization key for the username if (gotusername) { if ((res = getAuthKey(username, password, authkey, key)) != 0) { printf("Unable to get authkey from server (%d)\n",res); exit(1); } } else { getKeyVal(key); } // create the authkey.json file only if we have -u sprintf (comname,"/tmp/.%s.commands.com",getenv("USER")); if (gotusername) { sscanf (authkey,"{\"%[^\"]\":\"%[^\"]",authkeyword,authKeyVal); if (!strcmp(authkeyword,"error")) { printf("Invalid Login.. Exiting.\n"); exit(1); // do not save error as authkey } else { auth_fp = fopen(comname,"w"); if (auth_fp == NULL) { printf("unable to create %s\n",comname); exit(1); } fputs(authkey,auth_fp); fputs("\n",auth_fp); fclose(auth_fp); } } else { auth_fp = fopen(comname, "r"); if (auth_fp != NULL) { if(fgets(authkey, 64, auth_fp) == NULL) { printf("Unable to read authkey from %s\n",comname); exit(1); } // remove trailing \n n = strlen(authkey); if (authkey[n-1] == '\n') authkey[n-1] = '\0'; gotauthkey = 1; } } // create the key.json file key_fp = fopen(fName(KEY_NAME),"w"); if (key_fp == NULL) { printf("unable to create %s\n",fName(KEY_NAME)); exit(1); } fputs(key,key_fp); fputs("\n",key_fp); fclose(key_fp); // save off the key url to display to the user when we are done sscanf (key,"{\"key\":\"%[^\"]",keyVal); sprintf (displayUrl,"%s/%s",gotoKeyUrl,keyVal); // save off the authkey value if (gotusername || gotauthkey) { sscanf (authkey,"{\"%[^\"]\":\"%[^\"]",authkeyword,authKeyVal); if (!strcmp(authkeyword,"error")) { printf("\nError: %s\n\n",authKeyVal); exit(1); } else { printf("\nSuccessfully logged in..."); if (gotusername) { printf("\nAuthKey saved to %s. Delete file to return to Anonymous posting.\n",comname); } else { printf("\nAuthKey retrieved from %s. Delete file to return to Anonymous posting.\n",comname); } } } else { strcpy(authKeyVal,""); } // set up termination function atexit((void *)terminate); // catch abort signals signal(SIGINT,(void *)terminate2); // trap ctl-c signal(SIGTERM,(void *)terminate2); // trap kill -15 // create the post.txt file post_fp = fopen(fName(POST_NAME),"w"); if (post_fp == NULL) { printf("unable to create post file %s\n",fName(POST_NAME)); exit(1); } // record all input from the user while(1) { if (!gotdebug) unlink(fName(SHELL_NAME)); line = rl_gets(); if (line == NULL) { // trap ctl-d exit(1); } else { strcpy(line_s, line); } line = rl_gets(); strcpy(line_s, line); if (line == NULL) { // trap ctl-d exit(1); } // break the line up into words tokenize(line, words, &nwords); // just a blank line? if (words[0] == NULL) { continue; } // are we done ? if (!strcasecmp(words[0], "exit")) { exit(1); } fputs("monitor$ ",post_fp); fflush(post_fp); fputs(line_s,post_fp); fflush(post_fp); fputs("\n",post_fp); fflush(post_fp); // toss out any commands that cannot be handled such // as those that use libcurses.so if (!strcasecmp(words[0], "top")) { printf("Unable to capture output from %s\n",words[0]); sprintf(string,"Unable to capture output from %s\n",words[0]); fputs(string,post_fp); fflush(post_fp); continue; } // builtin command if (!strcasecmp(words[0], "cd")) { if (nwords == 1) dir = getenv("HOME"); if (nwords == 2 && *words[1] == '~') dir = getenv("HOME"); if (nwords == 2 && *words[1] != '~') dir = words[1]; if (chdir(dir) == -1) { perror("chdir"); fputs(strerror(errno),post_fp); fflush(post_fp); fputs("\n",post_fp); fflush(post_fp); continue; } continue; } // builtin command if (!strcasecmp(words[0], "pwd")) { if(NULL == (cwd = getcwd(pwd, PATH_MAX))) { strcpy(pwd,"Unable to get current working directory\n"); } printf("%s\n",pwd); fputs(pwd,post_fp); fflush(post_fp); fputs("\n",post_fp); fflush(post_fp); continue; } // builtin command if (!strcasecmp(words[0], "export")) { if (nwords > 1) { putenv(words[1]); continue; } } // look for "ls" by itself and add -C to make it tabbed format // because when piped through tee, it thinks it is not connected // to a terminal. if (!strcasecmp(words[0], "ls")) { if (nwords == 1) { words[1] = "-C"; nwords = 2; } } // look for "man" and add | col -b if (!strcasecmp(words[0], "man")) { if (nwords == 2) { words[2] = "|"; words[3] = "col"; words[4] = "-b"; nwords = 5; } // for when there is a "man 3 foo" if (nwords == 3) { words[3] = "|"; words[4] = "col"; words[5] = "-b"; nwords = 6; } } // process special "vi" command if (!strcasecmp(words[0], "vi")) { if (nwords == 1) { printf("Please specify the new file you wish to create.\n"); printf("It is required to correctly log the new file.\n"); continue; } gotvi = 1; if (stat(words[1],&stbuf) != 0) { // new file gotnew=1; strcpy(viFile,words[1]); } else { // make copy of existing file strcpy(viFile,words[1]); strcpy(viTemp,fName(TEMP_NAME)); my_cp(viFile,viTemp); gotnew=0; } } else { gotvi = 0; } // close the post.txt before forking fclose(post_fp); // OK, lets process the external command using fork/execvp if ((pid = fork ()) < 0) { perror ("fork"); exit(0); } // this will split output between the terminal and post.txt // <command> 2>&1 | tee -ai <file> if (pid == 0) { // if child then exec the command if (!gotvi) { // create the bash script tmp_fp = fopen(fName(SHELL_NAME),"w"); if (tmp_fp == NULL) { printf("unable to create %s file\n",fName(SHELL_NAME)); exit(1); } // set file permission to 755 i = strtol(mode, 0, 8); chmod (fName(SHELL_NAME),i); memset(combined,0,sizeof(combined)); for(i=0;i<nwords;i++) { strcat(combined,words[i]); strcat(combined," "); } sprintf(string,"#!/bin/bash -l\n%s 2>&1 | tee -ai %s\n",combined,fName(POST_NAME)); fputs(string,tmp_fp); fclose(tmp_fp); execlp ("/bin/bash","bash","-c",fName(SHELL_NAME),(char *)0); perror ("execlp"); exit(0); } else { execlp ("vi","vi",viFile,(char *)0); perror ("execlp"); exit(0); } } if (pid > 0) // parent waits for child process to terminate { do { wpid = waitpid(pid, &status, WUNTRACED); if (wpid == -1) { perror("waitpid"); return(0); } if (WIFEXITED(status)) { //printf("child exited, status=%d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("process killed (signal %d)\n", WTERMSIG(status)); } else if (WIFSTOPPED(status)) { printf("process stopped (signal %d)\n", WSTOPSIG(status)); } else { // Non-standard case -- may never happen printf("Unexpected status (0x%x)\n", status); } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); // existing file if (gotvi && !gotnew) { my_diff(viTemp,viFile,fName(POST_NAME)); unlink(viTemp); } // new file if (gotvi && gotnew) { my_append(viFile,fName(POST_NAME)); } // re-open the post.txt file post_fp = fopen(fName(POST_NAME),"a"); if (post_fp == NULL) { printf("unable to re-open post file %s\n",fName(POST_NAME)); exit(2); } } } exit(0); }