static void proc_readsignal(struct proc *p, int fd) { char _b[PIPE_BUF], *b = &_b[0]; ssize_t r; int signum; r = fdreadpipe(fd, b, sizeof(_b) / sizeof(_b[0])); switch(r) { case 0: die_readpipehang(fd); case 1: signum = (int)(char)(unsigned char)b[0]; switch(signum) { case SIGCHLD: proc_wait(p); break; default: die_readbaddata(fd, r); } break; default: die_overreadpipe(fd, r - 1); } }
/* * sscr_end -- * End the pipe to a shell. * * PUBLIC: int sscr_end(SCR *); */ int sscr_end(SCR *sp) { SCRIPT *sc; if ((sc = sp->script) == NULL) return (0); /* Turn off the script flags. */ F_CLR(sp, SC_SCRIPT); sscr_check(sp); /* Close down the parent's file descriptors. */ if (sc->sh_master != -1) (void)close(sc->sh_master); if (sc->sh_slave != -1) (void)close(sc->sh_slave); /* This should have killed the child. */ (void)proc_wait(sp, sc->sh_pid, "script-shell", 0, 0); /* Free memory. */ free(sc->sh_prompt); free(sc); sp->script = NULL; return (0); }
int main(int argc, char *argv[]) { if (argc != 3) { return 1; } int pid = atoi(argv[1]); unsigned long addr = strtol(argv[2], NULL, 16); struct proc *p = attach(pid); printf("attach %d\n", p->pid); proc_mprotect(p, (void *)(addr & -0x1000), PROT_READ); printf("begin...\n"); assert(p->stat<UNTRACED && p->stat>RUNNING); proc_cont(p, 0); while (1) { proc_wait(p); //printf("stat %d\n", p->stat); if (p->stat == SIGNAL_DELIVER_STOP) { if (WSTOPSIG(p->status) == SIGSEGV) { siginfo_t siginfo; ptrace(PTRACE_GETSIGINFO, p->pid, NULL, &siginfo); printf("memory address %p\n", siginfo.si_addr); if (siginfo.si_addr == (void *)addr) { proc_mprotect(p, (void *)(addr & -0x1000), PROT_READ|PROT_WRITE); ptrace(PTRACE_SINGLESTEP, p->pid, NULL, NULL); proc_wait(p); //fprintf(stderr, "%d %x\n", p->stat, p->status); proc_write(p, (void *)addr, (char *)&locked, 4); proc_mprotect(p, (void *)(addr & -0x1000), PROT_READ); } else { if (siginfo.si_addr >= (void *)(addr & ~0xfff) && siginfo.si_addr < (void *)((addr + 0xfff) & ~0xfff)) { proc_mprotect(p, (void *)(addr & -0x1000), PROT_READ|PROT_WRITE); ptrace(PTRACE_SINGLESTEP, p->pid, NULL, NULL); proc_wait(p); proc_mprotect(p, (void *)(addr & -0x1000), PROT_READ); } } } } proc_cont(p, 0); } //proc_exit(p, 42); proc_detach(p); }
/* * ex_exec_proc -- * Run a separate process. * * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, const char *, const char *, int)); */ int ex_exec_proc(SCR *sp, EXCMD *cmdp, const char *cmd, const char *msg, int need_newline) { GS *gp; const char *name; pid_t pid; gp = sp->gp; /* We'll need a shell. */ if (opts_empty(sp, O_SHELL, 0)) return (1); /* Enter ex mode. */ if (F_ISSET(sp, SC_VI)) { if (gp->scr_screen(sp, SC_EX)) { ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON); return (1); } (void)gp->scr_attr(sp, SA_ALTERNATE, 0); F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); } /* Put out additional newline, message. */ if (need_newline) (void)ex_puts(sp, "\n"); if (msg != NULL) { (void)ex_puts(sp, msg); (void)ex_puts(sp, "\n"); } (void)ex_fflush(sp); switch (pid = vfork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "vfork"); return (1); case 0: /* Utility. */ if (gp->scr_child) gp->scr_child(sp); if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) name = O_STR(sp, O_SHELL); else ++name; execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL); msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); _exit(127); /* NOTREACHED */ default: /* Parent. */ return (proc_wait(sp, (long)pid, cmd, 0, 0)); } /* NOTREACHED */ }
int proc_wait_debug_event_nohang(proc_obj_t* proc, int* sigret) { int rval; switch (rval = proc_wait(proc, sigret, TTRACE_NOWAIT)) { case EXEC_EVENT: return (DEBUG_EVENT); default: return rval; } return (NOTHING_EVENT); }
void *proc_mmap(struct proc *p, int prot, int flags) { void *addr = NULL; char *code = "\xcd\x80\x00\x00\x00\x00\x00\x00"; proc_save(p); p->regs = p->oregs; p->regs.rax = SYS_mmap; p->regs.rdi = 0; p->regs.rsi = 4; p->regs.rdx = prot; p->regs.r10 = flags; p->regs.r8 = -1; p->regs.r9 = 0; p->regs.rip = EBASE; char savebuf[8] = {0}; int n = proc_read(p, (void*)EBASE, savebuf, 8); if (n != 8) { perror("read"); exit(1); } n = proc_write(p, (void*)EBASE, code, 8); if (n != 8) { perror("write"); exit(1); } proc_regs(p, 1); printf("syscall mmap begin...\n"); ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); while (1) { proc_wait(p); if (p->stat != SYSCALL_STOP) { goto cont; } proc_regs(p, 0); if (p->regs.orig_rax == SYS_mmap) { if (p->insys == 0) { p->insys = 1; } else if (p->insys == 1) { addr = (void *)p->regs.rax; p->insys = 0; break; } } cont: ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); } proc_write(p, (void *)EBASE, savebuf, 4); proc_restore(p); return addr; }
int proc_mprotect(struct proc *p, void *addr, int prot) { int ret = 0; unsigned long err = 0; char *code = "\x0f\x05\x00\x00\x00\x00\x00\x00"; proc_save(p); p->regs = p->oregs; p->regs.rax = SYS_mprotect; p->regs.rdi = (unsigned long)addr; p->regs.rsi = 0x1000; p->regs.rdx = prot; p->regs.rip = EBASE; char savebuf[8] = {0}; int n = proc_read(p, (void*)EBASE, savebuf, 8); if (n != 8) { perror("read"); exit(1); } n = proc_write(p, (void*)EBASE, code, 8); if (n != 8) { perror("write"); exit(1); } proc_regs(p, 1); ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); while (1) { proc_wait(p); if (p->stat != SYSCALL_STOP) { printf("not syscall\n"); goto cont; } proc_regs(p, 0); if (p->regs.orig_rax == SYS_mprotect) { if (p->insys == 0) { p->insys = 1; } else if (p->insys == 1) { err = p->regs.rax; p->insys = 0; break; } } cont: ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); } proc_write(p, (void *)EBASE, savebuf, 8); proc_restore(p); printf("%d, %s\n", err, strerror(-err)); return ret; }
/* free a process */ extern int proc_free(process *pp) { int i; if(pp->std[1] == pp->std[2]) pp->std[2] = 0; /* avoid freeing it twice */ for (i = 0; i < 3; i++) if (pp->std[i]) stream_free(pp->std[i]); if (pp->pid >= 0) proc_wait(pp); free(pp->waitmsg); free((char *)pp); return 0; }
void proc_exit(struct proc *p, int no) { char *code = "\xcd\x80\x00\x00\x00\x00\x00\x00"; proc_save(p); p->regs = p->oregs; p->regs.rax = SYS_exit; p->regs.rdi = no; p->regs.rip = EBASE; char savebuf[8] = {0}; int n = proc_read(p, (void*)EBASE, savebuf, 8); if (n != 8) { perror("read"); exit(1); } proc_write(p, (void*)EBASE, code, 8); proc_regs(p, 1); printf("syscall exit begin...\n"); ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); while (1) { proc_wait(p); if (p->stat == DEAD) { printf("%p: ", p->status); if (WIFEXITED(p->status)) { printf("exit code %d\n", WEXITSTATUS(p->status)); } else if (WIFSIGNALED(p->status)) { printf("signal %d\n", WTERMSIG(p->status)); } break; } if (p->stat != SYSCALL_STOP) { goto cont; } proc_regs(p, 0); if (p->regs.orig_rax == SYS_exit) { if (p->insys == 0) { p->insys = 1; } else if (p->insys == 1) { p->insys = 0; break; } } cont: ptrace(PTRACE_SYSCALL, p->pid, NULL, NULL); } }
void verify(String *path) { char *p, *q; char *av[4]; if(rejectcheck()) return; if(shellchars(s_to_c(path))){ reply("503 5.1.3 Bad character in address %s.\r\n", s_to_c(path)); return; } av[0] = s_to_c(mailer); av[1] = "-x"; av[2] = s_to_c(path); av[3] = 0; pp = noshell_proc_start(av, (stream *)0, outstream(), (stream *)0, 1, 0); if (pp == 0) { reply("450 4.3.2 We're busy right now, try later\r\n"); return; } p = Brdline(pp->std[1]->fp, '\n'); if(p == 0){ reply("550 5.1.0 String does not match anything.\r\n"); } else { p[Blinelen(pp->std[1]->fp)-1] = 0; if(strchr(p, ':')) reply("550 5.1.0 String does not match anything.\r\n"); else{ q = strrchr(p, '!'); if(q) p = q+1; reply("250 2.0.0 %s <%s@%s>\r\n", s_to_c(path), p, dom); } } proc_wait(pp); proc_free(pp); pp = 0; }
/* pipe an address through a command to translate it */ extern dest * translate(dest *dp) { process *pp; String *line; dest *rv; char *cp; int n; pp = proc_start(s_to_c(dp->repl1), (stream *)0, outstream(), outstream(), 1, 0); if (pp == 0) { dp->status = d_resource; return 0; } line = s_new(); for(;;) { cp = Brdline(pp->std[1]->fp, '\n'); if(cp == 0) break; if(strncmp(cp, "_nosummary_", 11) == 0){ nosummary = 1; continue; } n = Blinelen(pp->std[1]->fp); cp[n-1] = ' '; s_nappend(line, cp, n); } rv = s_to_dest(s_restart(line), dp); s_restart(line); while(s_read_line(pp->std[2]->fp, line)) ; if ((dp->pstat = proc_wait(pp)) != 0) { dp->repl2 = line; rv = 0; } else s_free(line); proc_free(pp); return rv; }
/* * Run a command to authorize or refuse entry. Return status 0 means * authorize, -1 means refuse. */ void authorize(dest *dp) { process *pp; String *errstr; dp->authorized = 1; pp = proc_start(s_to_c(dp->repl1), (stream *)0, (stream *)0, outstream(), 1, 0); if (pp == 0){ dp->status = d_noforward; return; } errstr = s_new(); while(s_read_line(pp->std[2]->fp, errstr)) ; if ((dp->pstat = proc_wait(pp)) != 0) { dp->repl2 = errstr; dp->status = d_noforward; } else s_free(errstr); proc_free(pp); }
static void do_get(trapframe *tf, uint32_t cmd) { proc *p = proc_cur(); assert(p->state == PROC_RUN && p->runcpu == cpu_cur()); //cprintf("GET proc %x eip %x esp %x cmd %x\n", p, tf->eip, tf->esp, cmd); spinlock_acquire(&p->lock); // Find the named child process; DON'T create if it doesn't exist uint32_t cn = tf->regs.edx & 0xff; proc *cp = p->child[cn]; if (!cp) cp = &proc_null; // Synchronize with child if necessary. if (cp->state != PROC_STOP) proc_wait(p, cp, tf); // Since the child is now stopped, it's ours to control; // we no longer need our process lock - // and we don't want to be holding it if usercopy() below aborts. spinlock_release(&p->lock); // Get child's general register state if (cmd & SYS_REGS) { int len = offsetof(procstate, fx); // just integer regs if (cmd & SYS_FPU) len = sizeof(procstate); // whole shebang usercopy(tf, 1, &cp->sv, tf->regs.ebx, len); // Copy child process's trapframe into user space procstate *cs = (procstate*) tf->regs.ebx; memcpy(cs, &cp->sv, len); } uint32_t sva = tf->regs.esi; uint32_t dva = tf->regs.edi; uint32_t size = tf->regs.ecx; switch (cmd & SYS_MEMOP) { case 0: // no memory operation break; case SYS_COPY: case SYS_MERGE: // validate source region if (PTOFF(sva) || PTOFF(size) || sva < VM_USERLO || sva > VM_USERHI || size > VM_USERHI-sva) systrap(tf, T_GPFLT, 0); // fall thru... case SYS_ZERO: // validate destination region if (PTOFF(dva) || PTOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); switch (cmd & SYS_MEMOP) { case SYS_ZERO: // zero memory and clear permissions pmap_remove(p->pdir, dva, size); break; case SYS_COPY: // copy from local src to dest in child pmap_copy(cp->pdir, sva, p->pdir, dva, size); break; case SYS_MERGE: // merge from local src to dest in child pmap_merge(cp->rpdir, cp->pdir, sva, p->pdir, dva, size); break; } break; default: systrap(tf, T_GPFLT, 0); } if (cmd & SYS_PERM) { // validate destination region if (PGOFF(dva) || PGOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); if (!pmap_setperm(p->pdir, dva, size, cmd & SYS_RW)) panic("pmap_get: no memory to set permissions"); } if (cmd & SYS_SNAP) systrap(tf, T_GPFLT, 0); // only valid for PUT trap_return(tf); // syscall completed }
/* * Main loop for all victim process control threads. We initialize all the * appropriate /proc control mechanisms, and then enter a loop waiting for * the process to stop on an event or die. We process any events by calling * appropriate subroutines, and exit when the victim dies or we lose control. * * The control thread synchronizes the use of dpr_proc with other libdtrace * threads using dpr_lock. We hold the lock for all of our operations except * waiting while the process is running: this is accomplished by writing a * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. If the * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used. */ static void * dt_proc_control(void *arg) { dt_proc_control_data_t *datap = arg; dtrace_hdl_t *dtp = datap->dpcd_hdl; dt_proc_t *dpr = datap->dpcd_proc; dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs; struct ps_prochandle *P = dpr->dpr_proc; #if defined(sun) int pfd = Pctlfd(P); const long wstop = PCWSTOP; #endif int notify = B_FALSE; /* * We disable the POSIX thread cancellation mechanism so that the * client program using libdtrace can't accidentally cancel our thread. * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit. */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); dpr->dpr_pid = proc_getpid(P); int pid = dpr->dpr_pid; /* * Set up the corresponding process for tracing by libdtrace. We want * to be able to catch breakpoints and efficiently single-step over * them, and we need to enable librtld_db to watch libdl activity. */ do_ptrace(__func__, PTRACE_ATTACH, dpr->dpr_pid, 0, 0); (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Punsetflags(P, PR_ASYNC); /* require synchronous mode */ (void) Psetflags(P, PR_BPTADJ); /* always adjust eip on x86 */ (void) Punsetflags(P, PR_FORK); /* do not inherit on fork */ (void) Pfault(P, FLTBPT, B_TRUE); /* always trace breakpoints */ (void) Pfault(P, FLTTRACE, B_TRUE); /* always trace single-step */ /* * We must trace exit from exec() system calls so that if the exec is * successful, we can reset our breakpoints and re-initialize libproc. */ (void) Psysexit(P, SYS_exec, B_TRUE); (void) Psysexit(P, SYS_execve, B_TRUE); /* * We must trace entry and exit for fork() system calls in order to * disable our breakpoints temporarily during the fork. We do not set * the PR_FORK flag, so if fork succeeds the child begins executing and * does not inherit any other tracing behaviors or a control thread. */ (void) Psysentry(P, SYS_vfork, B_TRUE); (void) Psysexit(P, SYS_vfork, B_TRUE); (void) Psysentry(P, SYS_fork1, B_TRUE); (void) Psysexit(P, SYS_fork1, B_TRUE); (void) Psysentry(P, SYS_forkall, B_TRUE); (void) Psysexit(P, SYS_forkall, B_TRUE); (void) Psysentry(P, SYS_forksys, B_TRUE); (void) Psysexit(P, SYS_forksys, B_TRUE); Psync(P); /* enable all /proc changes */ dt_proc_attach(dpr, B_FALSE); /* enable rtld breakpoints */ /* * If PR_KLC is set, we created the process; otherwise we grabbed it. * Check for an appropriate stop request and wait for dt_proc_continue. */ dpr->dpr_stop |= DT_PROC_STOP_CREATE; if (Pstatus(P)->pr_flags & PR_KLC) dt_proc_stop(dpr, DT_PROC_STOP_CREATE); else dt_proc_stop(dpr, DT_PROC_STOP_GRAB); if (Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); /* * Wait for the process corresponding to this control thread to stop, * process the event, and then set it running again. We want to sleep * with dpr_lock *unheld* so that other parts of libdtrace can use the * ps_prochandle in the meantime (e.g. ustack()). To do this, we write * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. * Once the process stops, we wake up, grab dpr_lock, and then call * Pwait() (which will return immediately) and do our processing. */ //printf("%s: waiting to quit\n", __func__); while (!dpr->dpr_quit) { const lwpstatus_t *psp; #if defined(sun) if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ #else /* Wait for the process to report status. */ proc_wait(P); #endif (void) pthread_mutex_lock(&dpr->dpr_lock); pwait_locked: if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) { //printf("%s stopstatus (loop) pr_pid pid=%d\n", __func__, Pstatus(dpr->dpr_proc)->pr_pid); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; /* check dpr_quit and continue waiting */ } switch (Pstate(P)) { case PS_STOP: psp = &Pstatus(P)->pr_lwp; dt_dprintf("pid %d: proc stopped showing %d/%d\n", pid, psp->pr_why, psp->pr_what); #if defined(sun) /* * If the process stops showing PR_REQUESTED, then the * DTrace stop() action was applied to it or another * debugging utility (e.g. pstop(1)) asked it to stop. * In either case, the user's intention is for the * process to remain stopped until another external * mechanism (e.g. prun(1)) is applied. So instead of * setting the process running ourself, we wait for * someone else to do so. Once that happens, we return * to our normal loop waiting for an event of interest. */ if (psp->pr_why == PR_REQUESTED) { dt_proc_waitrun(dpr); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; } /* * If the process stops showing one of the events that * we are tracing, perform the appropriate response. * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and * PR_JOBCONTROL by design: if one of these conditions * occurs, we will fall through to Psetrun() but the * process will remain stopped in the kernel by the * corresponding mechanism (e.g. job control stop). */ if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT) dt_proc_bpmatch(dtp, dpr); else if (psp->pr_why == PR_SYSENTRY && IS_SYS_FORK(psp->pr_what)) dt_proc_bpdisable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_FORK(psp->pr_what)) dt_proc_bpenable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_EXEC(psp->pr_what)) dt_proc_attach(dpr, B_TRUE); #endif //printf("In PS_STOP dpr_stop=%x\n", dpr->dpr_stop); break; case PS_LOST: //printf("in PS_LOST\n"); if (Preopen(P) == 0) goto pwait_locked; dt_dprintf("pid %d: proc lost: %s\n", pid, strerror(errno)); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; case PS_UNDEAD: case PS_DEAD: dt_dprintf("pid %d: proc died\n", pid); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; } if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); } /* * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue * the dt_proc_t structure on the dt_proc_hash_t notification list. */ if (notify) dt_proc_notify(dtp, dph, dpr, NULL); /* * Destroy and remove any remaining breakpoints, set dpr_done and clear * dpr_tid to indicate the control thread has exited, and notify any * waiting thread in dt_proc_destroy() that we have succesfully exited. */ (void) pthread_mutex_lock(&dpr->dpr_lock); dt_proc_bpdestroy(dpr, B_TRUE); dpr->dpr_done = B_TRUE; dpr->dpr_tid = 0; (void) pthread_cond_broadcast(&dpr->dpr_cv); (void) pthread_mutex_unlock(&dpr->dpr_lock); return (NULL); }
void data(void) { int status, nbytes; char *cp, *ep; char errx[ERRMAX]; Link *l; String *cmd, *err; if(rejectcheck()) return; if(senders.last == 0){ reply("503 2.5.2 Data without MAIL FROM:\r\n"); rejectcount++; return; } if(rcvers.last == 0){ reply("503 2.5.2 Data without RCPT TO:\r\n"); rejectcount++; return; } if(!trusted && sendermxcheck()){ rerrstr(errx, sizeof errx); if(strncmp(errx, "rejected:", 9) == 0) reply("554 5.7.1 %s\r\n", errx); else reply("450 4.7.0 %s\r\n", errx); for(l=rcvers.first; l; l=l->next) syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(l->p), errx); rejectcount++; return; } cmd = startcmd(); if(cmd == 0) return; reply("354 Input message; end with <CRLF>.<CRLF>\r\n"); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# sent 354; accepting DATA %s\n", thedate()); } /* * allow 145 more minutes to move the data */ alarm(145*60*1000); status = pipemsg(&nbytes); /* * read any error messages */ err = s_new(); if (debug) { stamp(); fprint(2, "waiting for upas/send to close stderr\n"); } while(s_read_line(pp->std[2]->fp, err)) ; alarm(0); atnotify(catchalarm, 0); if (debug) { stamp(); fprint(2, "waiting for upas/send to exit\n"); } status |= proc_wait(pp); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# %d upas/send status %#ux at %s\n", getpid(), status, thedate()); if(*s_to_c(err)) fprint(2, "# %d error %s\n", getpid(), s_to_c(err)); } /* * if process terminated abnormally, send back error message */ if(status){ int code; char *ecode; if(strstr(s_to_c(err), "mail refused")){ syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), firstline(s_to_c(err))); code = 554; ecode = "5.0.0"; } else { syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), piperror? "error during pipemsg: ": "", piperror? piperror: "", piperror? "; ": "", pp->waitmsg->msg, firstline(s_to_c(err))); code = 450; ecode = "4.0.0"; } for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){ *ep++ = 0; reply("%d-%s %s\r\n", code, ecode, cp); } reply("%d %s mail process terminated abnormally\r\n", code, ecode); } else { /* * if a message appeared on stderr, despite good status, * log it. this can happen if rewrite.in contains a bad * r.e., for example. */ if(*s_to_c(err)) syslog(0, "smtpd", "%s returned good status, but said: %s", s_to_c(mailer), s_to_c(err)); if(filterstate == BLOCKED) reply("554 5.7.1 we believe this is spam. " "we don't accept it.\r\n"); else if(filterstate == DELAY) reply("450 4.3.0 There will be a delay in delivery " "of this message.\r\n"); else { reply("250 2.5.0 sent\r\n"); logcall(nbytes); if(debug){ seek(2, 0, 2); stamp(); fprint(2, "# %d sent 250 reply %s\n", getpid(), thedate()); } } } proc_free(pp); pp = 0; s_free(cmd); s_free(err); listfree(&senders); listfree(&rcvers); }
static void do_get(trapframe *tf, uint32_t cmd) { proc *curr = proc_cur(); spinlock_acquire(&curr->lock); // Find child index (includes node number and child number) int child_index = tf->regs.edx; uint8_t node_number = child_index >> 8; // First 8 bits are the node number uint8_t child_number = child_index & 0xff;// The last 8 bits for child number // cprintf("node %d get: dest node: %d, child: %d, home node: %d\n", // net_node, node_number, child_number, RRNODE(curr->home)); // When migrating, make sure to adjust eip! => entry == 0 // Trying to migrate home and this is not its home if(node_number == 0) { node_number = RRNODE(curr->home); } if (net_node != node_number) { // cprintf("sys_get: %p migrating to %d\n", curr, node_number); spinlock_release(&curr->lock); net_migrate(tf, node_number, 0); } proc *child = curr->child[child_number]; if(!child) child = &proc_null; // cprintf("No child process %d\n", child_index); if(child->state != PROC_STOP) proc_wait(curr, child, tf); spinlock_release(&curr->lock); // cprintf("do_get: current proc: %p, cpu_cur proc: %p\n", curr, cpu_cur()->proc); uint32_t dest = tf->regs.edi; //syscall.h uint32_t size = tf->regs.ecx; uint32_t src = tf->regs.esi; if(cmd & SYS_MEMOP) { int op = cmd & SYS_MEMOP; // Check if the destination range is okay if(dest < VM_USERLO || dest > VM_USERHI || dest + size > VM_USERHI) systrap(tf, T_GPFLT, 0); if(op == SYS_COPY) { // we have to check the source too if(src < VM_USERLO || src > VM_USERHI || src + size > VM_USERHI) systrap(tf, T_GPFLT, 0); pmap_copy(child->pdir, src, curr->pdir, dest, size); } else if(op == SYS_MERGE) { pmap_merge(child->rpdir, child->pdir, src, curr->pdir, dest, size); } else pmap_remove(curr->pdir, dest, size); } if(cmd & SYS_PERM) pmap_setperm(curr->pdir, dest, size, cmd & SYS_RW); if(cmd & SYS_REGS) usercopy(tf, 1, &child->sv, tf->regs.ebx, sizeof(procstate)); trap_return(tf); // syscall completed }
static void do_put(trapframe *tf, uint32_t cmd) { proc *curr = proc_cur(); spinlock_acquire(&curr->lock); uint32_t child_index = tf->regs.edx; uint8_t node_number = child_index >> 8 & 0xff; // First 8 bits are the node number uint8_t child_number = child_index & 0xff;// The last 8 bits for child number // cprintf("node %d put: dest node: %d, child: %d, home node: %d\n", // net_node, node_number, child_number, RRNODE(curr->home)); // When migrating, make sure to adjust eip! => entry == 0 // Trying to migrate home and this is not its home if(node_number == 0) { node_number = RRNODE(curr->home); } if (net_node != node_number) { // cprintf("sys_put: %p migrating to %d\n", curr, RRNODE(curr->home)); spinlock_release(&curr->lock); net_migrate(tf, node_number, 0); } proc *child = curr->child[child_number]; if(!child) child = proc_alloc(curr, child_number); if(child->state != PROC_STOP) proc_wait(curr, child, tf); spinlock_release(&curr->lock); // cprintf("do_put: current proc: %p, cpu_cur proc: %p\n", curr, cpu_cur()->proc); if(cmd & SYS_REGS) { usercopy(tf, 0, &child->sv, tf->regs.ebx, sizeof(procstate)); child->sv.tf.ds = CPU_GDT_UDATA | 3; child->sv.tf.es = CPU_GDT_UDATA | 3; child->sv.tf.cs = CPU_GDT_UCODE | 3; child->sv.tf.ss = CPU_GDT_UDATA | 3; child->sv.tf.eflags &= FL_USER; child->sv.tf.eflags |= FL_IF; } uint32_t dest = tf->regs.edi; //syscall.h uint32_t size = tf->regs.ecx; uint32_t src = tf->regs.esi; if(cmd & SYS_MEMOP) { int op = cmd & SYS_MEMOP; // Check if the destination range is okay if(dest < VM_USERLO || dest > VM_USERHI || dest + size > VM_USERHI) systrap(tf, T_GPFLT, 0); if(op == SYS_COPY) { // we have to check the source too if(src < VM_USERLO || src > VM_USERHI || src + size > VM_USERHI) systrap(tf, T_GPFLT, 0); pmap_copy(curr->pdir, src, child->pdir, dest, size); } else pmap_remove(child->pdir, dest, size); } if(cmd & SYS_PERM) pmap_setperm(child->pdir, dest, size, cmd & SYS_RW); if(cmd & SYS_SNAP) // copy pdir to rpdir pmap_copy(child->pdir, VM_USERLO, child->rpdir, VM_USERLO, VM_USERHI-VM_USERLO); if(cmd & SYS_START) proc_ready(child); trap_return(tf); // syscall completed }
/* * ex_filter -- * Run a range of lines through a filter utility and optionally * replace the original text with the stdout/stderr output of * the utility. * * PUBLIC: int ex_filter __P((SCR *, * PUBLIC: EXCMD *, MARK *, MARK *, MARK *, CHAR_T *, enum filtertype)); */ int ex_filter(SCR *sp, EXCMD *cmdp, MARK *fm, MARK *tm, MARK *rp, CHAR_T *cmd, enum filtertype ftype) { FILE *ifp, *ofp; pid_t parent_writer_pid, utility_pid; db_recno_t nread; int input[2], output[2], rval; char *name; char *np; size_t nlen; rval = 0; /* Set return cursor position, which is never less than line 1. */ *rp = *fm; if (rp->lno == 0) rp->lno = 1; /* We're going to need a shell. */ if (opts_empty(sp, O_SHELL, 0)) return (1); /* * There are three different processes running through this code. * They are the utility, the parent-writer and the parent-reader. * The parent-writer is the process that writes from the file to * the utility, the parent reader is the process that reads from * the utility. * * Input and output are named from the utility's point of view. * The utility reads from input[0] and the parent(s) write to * input[1]. The parent(s) read from output[0] and the utility * writes to output[1]. * * !!! * Historically, in the FILTER_READ case, the utility reads from * the terminal (e.g. :r! cat works). Otherwise open up utility * input pipe. */ ofp = NULL; input[0] = input[1] = output[0] = output[1] = -1; if (ftype != FILTER_READ && pipe(input) < 0) { msgq(sp, M_SYSERR, "pipe"); goto err; } /* Open up utility output pipe. */ if (pipe(output) < 0) { msgq(sp, M_SYSERR, "pipe"); goto err; } if ((ofp = fdopen(output[0], "r")) == NULL) { msgq(sp, M_SYSERR, "fdopen"); goto err; } /* Fork off the utility process. */ switch (utility_pid = vfork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "vfork"); err: if (input[0] != -1) (void)close(input[0]); if (input[1] != -1) (void)close(input[1]); if (ofp != NULL) (void)fclose(ofp); else if (output[0] != -1) (void)close(output[0]); if (output[1] != -1) (void)close(output[1]); return (1); case 0: /* Utility. */ /* * Redirect stdin from the read end of the input pipe, and * redirect stdout/stderr to the write end of the output pipe. * * !!! * Historically, ex only directed stdout into the input pipe, * letting stderr come out on the terminal as usual. Vi did * not, directing both stdout and stderr into the input pipe. * We match that practice in both ex and vi for consistency. */ if (input[0] != -1) (void)dup2(input[0], STDIN_FILENO); (void)dup2(output[1], STDOUT_FILENO); (void)dup2(output[1], STDERR_FILENO); /* Close the utility's file descriptors. */ if (input[0] != -1) (void)close(input[0]); if (input[1] != -1) (void)close(input[1]); (void)close(output[0]); (void)close(output[1]); if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) name = O_STR(sp, O_SHELL); else ++name; INT2SYS(sp, cmd, STRLEN(cmd)+1, np, nlen); execl(O_STR(sp, O_SHELL), name, "-c", np, (char *)NULL); msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); _exit (127); /* NOTREACHED */ default: /* Parent-reader, parent-writer. */ /* Close the pipe ends neither parent will use. */ if (input[0] != -1) (void)close(input[0]); (void)close(output[1]); break; } /* * FILTER_RBANG, FILTER_READ: * * Reading is the simple case -- we don't need a parent writer, * so the parent reads the output from the read end of the output * pipe until it finishes, then waits for the child. Ex_readfp * appends to the MARK, and closes ofp. * * For FILTER_RBANG, there is nothing to write to the utility. * Make sure it doesn't wait forever by closing its standard * input. * * !!! * Set the return cursor to the last line read in for FILTER_READ. * Historically, this behaves differently from ":r file" command, * which leaves the cursor at the first line read in. Check to * make sure that it's not past EOF because we were reading into an * empty file. */ if (ftype == FILTER_RBANG || ftype == FILTER_READ) { if (ftype == FILTER_RBANG) (void)close(input[1]); if (ex_readfp(sp, "filter", ofp, fm, &nread, 1)) rval = 1; sp->rptlines[L_ADDED] += nread; if (ftype == FILTER_READ) if (fm->lno == 0) rp->lno = nread; else rp->lno += nread; goto uwait; } /* * FILTER_BANG, FILTER_WRITE * * Here we need both a reader and a writer. Temporary files are * expensive and we'd like to avoid disk I/O. Using pipes has the * obvious starvation conditions. It's done as follows: * * fork * child * write lines out * exit * parent * FILTER_BANG: * read lines into the file * delete old lines * FILTER_WRITE * read and display lines * wait for child * * XXX * We get away without locking the underlying database because we know * that none of the records that we're reading will be modified until * after we've read them. This depends on the fact that the current * B+tree implementation doesn't balance pages or similar things when * it inserts new records. When the DB code has locking, we should * treat vi as if it were multiple applications sharing a database, and * do the required locking. If necessary a work-around would be to do * explicit locking in the line.c:db_get() code, based on the flag set * here. */ F_SET(sp->ep, F_MULTILOCK); switch (parent_writer_pid = fork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "fork"); (void)close(input[1]); (void)close(output[0]); rval = 1; break; case 0: /* Parent-writer. */ /* * Write the selected lines to the write end of the input * pipe. This instance of ifp is closed by ex_writefp. */ (void)close(output[0]); if ((ifp = fdopen(input[1], "w")) == NULL) _exit (1); _exit(ex_writefp(sp, "filter", ifp, fm, tm, NULL, NULL, 1)); /* NOTREACHED */ default: /* Parent-reader. */ (void)close(input[1]); if (ftype == FILTER_WRITE) { /* * Read the output from the read end of the output * pipe and display it. Filter_ldisplay closes ofp. */ if (filter_ldisplay(sp, ofp)) rval = 1; } else { /* * Read the output from the read end of the output * pipe. Ex_readfp appends to the MARK and closes * ofp. */ if (ex_readfp(sp, "filter", ofp, tm, &nread, 1)) rval = 1; sp->rptlines[L_ADDED] += nread; } /* Wait for the parent-writer. */ if (proc_wait(sp, (long)parent_writer_pid, "parent-writer", 0, 1)) rval = 1; /* Delete any lines written to the utility. */ if (rval == 0 && ftype == FILTER_BANG && (cut(sp, NULL, fm, tm, CUT_LINEMODE) || del(sp, fm, tm, 1))) { rval = 1; break; } /* * If the filter had no output, we may have just deleted * the cursor. Don't do any real error correction, we'll * try and recover later. */ if (rp->lno > 1 && !db_exist(sp, rp->lno)) --rp->lno; break; } F_CLR(sp->ep, F_MULTILOCK); /* * !!! * Ignore errors on vi file reads, to make reads prettier. It's * completely inconsistent, and historic practice. */ uwait: INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); return (proc_wait(sp, (long)utility_pid, np, ftype == FILTER_READ && F_ISSET(sp, SC_VI) ? 1 : 0, 0) || rval); }
static void do_put(trapframe *tf, uint32_t cmd) { proc *p = proc_cur(); assert(p->state == PROC_RUN && p->runcpu == cpu_cur()); cprintf("PUT proc %x eip %x esp %x cmd %x\n", p, tf->eip, tf->esp, cmd); spinlock_acquire(&p->lock); // Find the named child process; create if it doesn't exist uint32_t cn = tf->regs.edx & 0xff; proc *cp = p->child[cn]; if (!cp) { cp = proc_alloc(p, cn); if (!cp) // XX handle more gracefully panic("sys_put: no memory for child"); } // Synchronize with child if necessary. if (cp->state != PROC_STOP) proc_wait(p, cp, tf); // Since the child is now stopped, it's ours to control; // we no longer need our process lock - // and we don't want to be holding it if usercopy() below aborts. spinlock_release(&p->lock); // Put child's general register state if (cmd & SYS_REGS) { int len = offsetof(procstate, fx); // just integer regs if (cmd & SYS_FPU) len = sizeof(procstate); // whole shebang usercopy(tf,0,&cp->sv, tf->regs.ebx, len); // Copy user's trapframe into child process procstate *cs = (procstate*) tf->regs.ebx; memcpy(&cp->sv, cs, len); // Make sure process uses user-mode segments and eflag settings cp->sv.tf.ds = CPU_GDT_UDATA | 3; cp->sv.tf.es = CPU_GDT_UDATA | 3; cp->sv.tf.cs = CPU_GDT_UCODE | 3; cp->sv.tf.ss = CPU_GDT_UDATA | 3; cp->sv.tf.eflags &= FL_USER; cp->sv.tf.eflags |= FL_IF; // enable interrupts } uint32_t sva = tf->regs.esi; uint32_t dva = tf->regs.edi; uint32_t size = tf->regs.ecx; switch (cmd & SYS_MEMOP) { case 0: // no memory operation break; case SYS_COPY: // validate source region if (PTOFF(sva) || PTOFF(size) || sva < VM_USERLO || sva > VM_USERHI || size > VM_USERHI-sva) systrap(tf, T_GPFLT, 0); // fall thru... case SYS_ZERO: // validate destination region if (PTOFF(dva) || PTOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); switch (cmd & SYS_MEMOP) { case SYS_ZERO: // zero memory and clear permissions pmap_remove(cp->pdir, dva, size); break; case SYS_COPY: // copy from local src to dest in child pmap_copy(p->pdir, sva, cp->pdir, dva, size); break; } break; default: systrap(tf, T_GPFLT, 0); } if (cmd & SYS_PERM) { // validate destination region if (PGOFF(dva) || PGOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); if (!pmap_setperm(cp->pdir, dva, size, cmd & SYS_RW)) panic("pmap_put: no memory to set permissions"); } if (cmd & SYS_SNAP) // Snapshot child's state pmap_copy(cp->pdir, VM_USERLO, cp->rpdir, VM_USERLO, VM_USERHI-VM_USERLO); // Start the child if requested if (cmd & SYS_START) proc_ready(cp); trap_return(tf); // syscall completed }