1139749Simp/*- 265942Sgibbs * Copyright (c) 2008 John Birrell (jb@freebsd.org) 339220Sgibbs * All rights reserved. 471717Sgibbs * 595378Sgibbs * Redistribution and use in source and binary forms, with or without 639220Sgibbs * modification, are permitted provided that the following conditions 739220Sgibbs * are met: 839220Sgibbs * 1. Redistributions of source code must retain the above copyright 939220Sgibbs * notice, this list of conditions and the following disclaimer. 1039220Sgibbs * 2. Redistributions in binary form must reproduce the above copyright 1139220Sgibbs * notice, this list of conditions and the following disclaimer in the 1239220Sgibbs * documentation and/or other materials provided with the distribution. 1354211Sgibbs * 1495378Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1595378Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1695378Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1795378Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1895378Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1995378Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2095378Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2195378Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2239220Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2354211Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2495378Sgibbs * SUCH DAMAGE. 2595378Sgibbs * 2639220Sgibbs * $FreeBSD$ 2795378Sgibbs */ 2895378Sgibbs 2995378Sgibbs#include <sys/types.h> 3095378Sgibbs#include <sys/sysctl.h> 3195378Sgibbs#include <sys/wait.h> 3295378Sgibbs 3339220Sgibbs#include <err.h> 3439220Sgibbs#include <errno.h> 3595378Sgibbs#include <fcntl.h> 3695378Sgibbs#include <limits.h> 3795378Sgibbs#include <stdlib.h> 3895378Sgibbs#include <string.h> 3939220Sgibbs#include <unistd.h> 40123579Sgibbs 4165942Sgibbs#include "_libproc.h" 4250477Speter 4339220Sgibbsstatic int proc_init(pid_t, int, int, struct proc_handle *); 4439220Sgibbs 4539220Sgibbsstatic int 4639220Sgibbsproc_init(pid_t pid, int flags, int status, struct proc_handle *phdl) 4739220Sgibbs{ 4865942Sgibbs int mib[4], error; 4963457Sgibbs size_t len; 5039220Sgibbs 5165942Sgibbs memset(phdl, 0, sizeof(*phdl)); 5265942Sgibbs phdl->pid = pid; 5365942Sgibbs phdl->flags = flags; 5495378Sgibbs phdl->status = status; 5539220Sgibbs 5665942Sgibbs mib[0] = CTL_KERN; 5745965Sgibbs mib[1] = KERN_PROC; 5845965Sgibbs mib[2] = KERN_PROC_PATHNAME; 5945965Sgibbs mib[3] = pid; 6045965Sgibbs len = sizeof(phdl->execname); 6145965Sgibbs if (sysctl(mib, 4, phdl->execname, &len, NULL, 0) != 0) { 6245965Sgibbs error = errno; 6345965Sgibbs DPRINTF("ERROR: cannot get pathname for child process %d", pid); 6445965Sgibbs return (error); 6565942Sgibbs } 6665942Sgibbs if (len == 0) 6765942Sgibbs phdl->execname[0] = '\0'; 6865942Sgibbs 6965942Sgibbs return (0); 7065942Sgibbs} 7165942Sgibbs 7265942Sgibbsint 7365942Sgibbsproc_attach(pid_t pid, int flags, struct proc_handle **pphdl) 7465942Sgibbs{ 7565942Sgibbs struct proc_handle *phdl; 7665942Sgibbs int error = 0; 7765942Sgibbs int status; 7865942Sgibbs 7965942Sgibbs if (pid == 0 || pid == getpid()) 8065942Sgibbs return (EINVAL); 8165942Sgibbs 8265942Sgibbs /* 8365942Sgibbs * Allocate memory for the process handle, a structure containing 8465942Sgibbs * all things related to the process. 8565942Sgibbs */ 8665942Sgibbs if ((phdl = malloc(sizeof(struct proc_handle))) == NULL) 8765942Sgibbs return (ENOMEM); 8865942Sgibbs 8965942Sgibbs elf_version(EV_CURRENT); 9065942Sgibbs 9165942Sgibbs error = proc_init(pid, flags, PS_RUN, phdl); 9265942Sgibbs if (error != 0) 9365942Sgibbs goto out; 9465942Sgibbs 9565942Sgibbs if (ptrace(PT_ATTACH, phdl->pid, 0, 0) != 0) { 96115333Sgibbs error = errno; 9765942Sgibbs DPRINTF("ERROR: cannot ptrace child process %d", pid); 9865942Sgibbs goto out; 9965942Sgibbs } 10065942Sgibbs 101109590Sgibbs /* Wait for the child process to stop. */ 102109590Sgibbs if (waitpid(pid, &status, WUNTRACED) == -1) { 103109590Sgibbs error = errno; 104109590Sgibbs DPRINTF("ERROR: child process %d didn't stop as expected", pid); 105109590Sgibbs goto out; 106109590Sgibbs } 107109590Sgibbs 108109590Sgibbs /* Check for an unexpected status. */ 10965942Sgibbs if (WIFSTOPPED(status) == 0) 11065942Sgibbs DPRINTFX("ERROR: child process %d status 0x%x", pid, status); 11165942Sgibbs else 11265942Sgibbs phdl->status = PS_STOP; 11365942Sgibbs 11465942Sgibbsout: 11565942Sgibbs if (error) 11668579Sgibbs proc_free(phdl); 11768579Sgibbs else 11868579Sgibbs *pphdl = phdl; 11968579Sgibbs return (error); 12068579Sgibbs} 12165942Sgibbs 12245965Sgibbsint 12363457Sgibbsproc_create(const char *file, char * const *argv, proc_child_func *pcf, 12463457Sgibbs void *child_arg, struct proc_handle **pphdl) 12563457Sgibbs{ 12663457Sgibbs struct proc_handle *phdl; 12763457Sgibbs int error = 0; 12863457Sgibbs int status; 12970204Sgibbs pid_t pid; 13070204Sgibbs 13170204Sgibbs /* 13263457Sgibbs * Allocate memory for the process handle, a structure containing 13370204Sgibbs * all things related to the process. 13463457Sgibbs */ 13563457Sgibbs if ((phdl = malloc(sizeof(struct proc_handle))) == NULL) 13645965Sgibbs return (ENOMEM); 13745965Sgibbs 13839220Sgibbs elf_version(EV_CURRENT); 13939220Sgibbs 14045965Sgibbs /* Fork a new process. */ 14171390Sgibbs if ((pid = vfork()) == -1) 14271390Sgibbs error = errno; 14371390Sgibbs else if (pid == 0) { 14471390Sgibbs /* The child expects to be traced. */ 14571390Sgibbs if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0) 14671390Sgibbs _exit(1); 14771390Sgibbs 14865942Sgibbs if (pcf != NULL) 14965942Sgibbs (*pcf)(child_arg); 15065942Sgibbs 15165942Sgibbs /* Execute the specified file: */ 15271390Sgibbs execvp(file, argv); 15371390Sgibbs 15471390Sgibbs /* Couldn't execute the file. */ 15571390Sgibbs _exit(2); 15671390Sgibbs } else { 15771390Sgibbs /* The parent owns the process handle. */ 15871390Sgibbs error = proc_init(pid, 0, PS_IDLE, phdl); 15971390Sgibbs if (error != 0) 16071390Sgibbs goto bad; 16171390Sgibbs 16271390Sgibbs /* Wait for the child process to stop. */ 16372640Sasmodai if (waitpid(pid, &status, WUNTRACED) == -1) { 16445965Sgibbs error = errno; 16571390Sgibbs DPRINTF("ERROR: child process %d didn't stop as expected", pid); 16645965Sgibbs goto bad; 16765942Sgibbs } 16895378Sgibbs 16995378Sgibbs /* Check for an unexpected status. */ 17095378Sgibbs if (WIFSTOPPED(status) == 0) { 17195378Sgibbs error = errno; 17295378Sgibbs DPRINTFX("ERROR: child process %d status 0x%x", pid, status); 17395378Sgibbs goto bad; 17495378Sgibbs } else 17565942Sgibbs phdl->status = PS_STOP; 17665942Sgibbs } 17765942Sgibbsbad: 17865942Sgibbs if (error) 17965942Sgibbs proc_free(phdl); 18039220Sgibbs else 18165942Sgibbs *pphdl = phdl; 182102671Sgibbs return (error); 18339220Sgibbs} 184136711Sgibbs 185136711Sgibbsvoid 186136711Sgibbsproc_free(struct proc_handle *phdl) 187136711Sgibbs{ 18865942Sgibbs free(phdl); 18963457Sgibbs} 19065942Sgibbs