]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libproc/proc_create.c
Add an envp argument to proc_create().
[FreeBSD/FreeBSD.git] / lib / libproc / proc_create.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 John Birrell (jb@freebsd.org)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
34 #include <sys/user.h>
35 #include <sys/wait.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include <libelf.h>
46 #include <libprocstat.h>
47
48 #include "_libproc.h"
49
50 static int      getelfclass(int);
51 static int      proc_init(pid_t, int, int, struct proc_handle **);
52
53 static int
54 getelfclass(int fd)
55 {
56         GElf_Ehdr ehdr;
57         Elf *e;
58         int class;
59
60         class = ELFCLASSNONE;
61
62         if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
63                 goto out;
64         if (gelf_getehdr(e, &ehdr) == NULL)
65                 goto out;
66         class = ehdr.e_ident[EI_CLASS];
67 out:
68         (void)elf_end(e);
69         return (class);
70 }
71
72 static int
73 proc_init(pid_t pid, int flags, int status, struct proc_handle **pphdl)
74 {
75         struct kinfo_proc *kp;
76         struct proc_handle *phdl;
77         int error, class, count, fd;
78
79         error = ENOMEM;
80         if ((phdl = malloc(sizeof(*phdl))) == NULL)
81                 goto out;
82
83         memset(phdl, 0, sizeof(*phdl));
84         phdl->public.pid = pid;
85         phdl->flags = flags;
86         phdl->status = status;
87         phdl->procstat = procstat_open_sysctl();
88         if (phdl->procstat == NULL)
89                 goto out;
90
91         /* Obtain a path to the executable. */
92         if ((kp = procstat_getprocs(phdl->procstat, KERN_PROC_PID, pid,
93             &count)) == NULL)
94                 goto out;
95         error = procstat_getpathname(phdl->procstat, kp, phdl->execpath,
96             sizeof(phdl->execpath));
97         procstat_freeprocs(phdl->procstat, kp);
98         if (error != 0)
99                 goto out;
100
101         /* Use it to determine the data model for the process. */
102         if ((fd = open(phdl->execpath, O_RDONLY)) < 0) {
103                 error = errno;
104                 goto out;
105         }
106         class = getelfclass(fd);
107         switch (class) {
108         case ELFCLASS64:
109                 phdl->model = PR_MODEL_LP64;
110                 break;
111         case ELFCLASS32:
112                 phdl->model = PR_MODEL_ILP32;
113                 break;
114         case ELFCLASSNONE:
115         default:
116                 error = EINVAL;
117                 break;
118         }
119         (void)close(fd);
120
121 out:
122         *pphdl = phdl;
123         return (error);
124 }
125
126 int
127 proc_attach(pid_t pid, int flags, struct proc_handle **pphdl)
128 {
129         struct proc_handle *phdl;
130         int error, status;
131
132         if (pid == 0 || (pid == getpid() && (flags & PATTACH_RDONLY) == 0))
133                 return (EINVAL);
134         if (elf_version(EV_CURRENT) == EV_NONE)
135                 return (ENOENT);
136
137         /*
138          * Allocate memory for the process handle, a structure containing
139          * all things related to the process.
140          */
141         error = proc_init(pid, flags, PS_RUN, &phdl);
142         if (error != 0)
143                 goto out;
144
145         if ((flags & PATTACH_RDONLY) == 0) {
146                 if (ptrace(PT_ATTACH, proc_getpid(phdl), 0, 0) != 0) {
147                         error = errno;
148                         DPRINTF("ERROR: cannot ptrace child process %d", pid);
149                         goto out;
150                 }
151
152                 /* Wait for the child process to stop. */
153                 if (waitpid(pid, &status, WUNTRACED) == -1) {
154                         error = errno;
155                         DPRINTF("ERROR: child process %d didn't stop as expected", pid);
156                         goto out;
157                 }
158
159                 /* Check for an unexpected status. */
160                 if (!WIFSTOPPED(status))
161                         DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
162                 else
163                         phdl->status = PS_STOP;
164
165                 if ((flags & PATTACH_NOSTOP) != 0)
166                         proc_continue(phdl);
167         }
168
169 out:
170         if (error != 0 && phdl != NULL) {
171                 proc_free(phdl);
172                 phdl = NULL;
173         }
174         *pphdl = phdl;
175         return (error);
176 }
177
178 int
179 proc_create(const char *file, char * const *argv, char * const *envp,
180     proc_child_func *pcf, void *child_arg, struct proc_handle **pphdl)
181 {
182         extern char * const *environ;
183         struct proc_handle *phdl;
184         int error, status;
185         pid_t pid;
186
187         if (elf_version(EV_CURRENT) == EV_NONE)
188                 return (ENOENT);
189
190         error = 0;
191         phdl = NULL;
192
193         if ((pid = fork()) == -1)
194                 error = errno;
195         else if (pid == 0) {
196                 /* The child expects to be traced. */
197                 if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0)
198                         _exit(1);
199
200                 if (pcf != NULL)
201                         (*pcf)(child_arg);
202
203                 if (envp != NULL)
204                         environ = envp;
205
206                 execvp(file, argv);
207
208                 _exit(2);
209                 /* NOTREACHED */
210         } else {
211                 /* Wait for the child process to stop. */
212                 if (waitpid(pid, &status, WUNTRACED) == -1) {
213                         error = errno;
214                         DPRINTF("ERROR: child process %d didn't stop as expected", pid);
215                         goto bad;
216                 }
217
218                 /* Check for an unexpected status. */
219                 if (!WIFSTOPPED(status)) {
220                         error = ENOENT;
221                         DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
222                         goto bad;
223                 }
224
225                 /* The parent owns the process handle. */
226                 error = proc_init(pid, 0, PS_IDLE, &phdl);
227                 if (error == 0)
228                         phdl->status = PS_STOP;
229
230 bad:
231                 if (error != 0 && phdl != NULL) {
232                         proc_free(phdl);
233                         phdl = NULL;
234                 }
235         }
236         *pphdl = phdl;
237         return (error);
238 }
239
240 void
241 proc_free(struct proc_handle *phdl)
242 {
243         struct file_info *file;
244         size_t i;
245
246         for (i = 0; i < phdl->nmappings; i++) {
247                 file = phdl->mappings[i].file;
248                 if (file != NULL && --file->refs == 0) {
249                         if (file->elf != NULL) {
250                                 (void)elf_end(file->elf);
251                                 (void)close(file->fd);
252                                 if (file->symtab.nsyms > 0)
253                                         free(file->symtab.index);
254                                 if (file->dynsymtab.nsyms > 0)
255                                         free(file->dynsymtab.index);
256                         }
257                         free(file);
258                 }
259         }
260         if (phdl->maparrsz > 0)
261                 free(phdl->mappings);
262         if (phdl->procstat != NULL)
263                 procstat_close(phdl->procstat);
264         if (phdl->rdap != NULL)
265                 rd_delete(phdl->rdap);
266         free(phdl);
267 }