1224987Sjonathan/*-
2224987Sjonathan * Copyright (c) 2009 Robert N. M. Watson
3224987Sjonathan * All rights reserved.
4224987Sjonathan *
5224987Sjonathan * This software was developed at the University of Cambridge Computer
6224987Sjonathan * Laboratory with support from a grant from Google, Inc.
7224987Sjonathan *
8224987Sjonathan * Redistribution and use in source and binary forms, with or without
9224987Sjonathan * modification, are permitted provided that the following conditions
10224987Sjonathan * are met:
11224987Sjonathan * 1. Redistributions of source code must retain the above copyright
12224987Sjonathan *    notice, this list of conditions and the following disclaimer.
13224987Sjonathan * 2. Redistributions in binary form must reproduce the above copyright
14224987Sjonathan *    notice, this list of conditions and the following disclaimer in the
15224987Sjonathan *    documentation and/or other materials provided with the distribution.
16224987Sjonathan *
17224987Sjonathan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18224987Sjonathan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19224987Sjonathan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20224987Sjonathan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21224987Sjonathan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22224987Sjonathan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23224987Sjonathan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24224987Sjonathan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25224987Sjonathan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26224987Sjonathan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27224987Sjonathan * SUCH DAMAGE.
28224987Sjonathan */
29224987Sjonathan
30224987Sjonathan/*-
31224987Sjonathan * FreeBSD process descriptor facility.
32224987Sjonathan *
33224987Sjonathan * Some processes are represented by a file descriptor, which will be used in
34224987Sjonathan * preference to signaling and pids for the purposes of process management,
35224987Sjonathan * and is, in effect, a form of capability.  When a process descriptor is
36224987Sjonathan * used with a process, it ceases to be visible to certain traditional UNIX
37224987Sjonathan * process facilities, such as waitpid(2).
38224987Sjonathan *
39224987Sjonathan * Some semantics:
40224987Sjonathan *
41224987Sjonathan * - At most one process descriptor will exist for any process, although
42224987Sjonathan *   references to that descriptor may be held from many processes (or even
43224987Sjonathan *   be in flight between processes over a local domain socket).
44224987Sjonathan * - Last close on the process descriptor will terminate the process using
45224987Sjonathan *   SIGKILL and reparent it to init so that there's a process to reap it
46224987Sjonathan *   when it's done exiting.
47224987Sjonathan * - If the process exits before the descriptor is closed, it will not
48224987Sjonathan *   generate SIGCHLD on termination, or be picked up by waitpid().
49224987Sjonathan * - The pdkill(2) system call may be used to deliver a signal to the process
50224987Sjonathan *   using its process descriptor.
51224987Sjonathan * - The pdwait4(2) system call may be used to block (or not) on a process
52224987Sjonathan *   descriptor to collect termination information.
53224987Sjonathan *
54224987Sjonathan * Open questions:
55224987Sjonathan *
56224987Sjonathan * - How to handle ptrace(2)?
57224987Sjonathan * - Will we want to add a pidtoprocdesc(2) system call to allow process
58224987Sjonathan *   descriptors to be created for processes without pfork(2)?
59224987Sjonathan */
60224987Sjonathan
61224987Sjonathan#include <sys/cdefs.h>
62224987Sjonathan__FBSDID("$FreeBSD$");
63224987Sjonathan
64224987Sjonathan#include "opt_procdesc.h"
65224987Sjonathan
66224987Sjonathan#include <sys/param.h>
67280258Srwatson#include <sys/capsicum.h>
68224987Sjonathan#include <sys/fcntl.h>
69224987Sjonathan#include <sys/file.h>
70224987Sjonathan#include <sys/filedesc.h>
71224987Sjonathan#include <sys/kernel.h>
72224987Sjonathan#include <sys/lock.h>
73224987Sjonathan#include <sys/mutex.h>
74224987Sjonathan#include <sys/poll.h>
75224987Sjonathan#include <sys/proc.h>
76224987Sjonathan#include <sys/procdesc.h>
77224987Sjonathan#include <sys/resourcevar.h>
78224987Sjonathan#include <sys/stat.h>
79224987Sjonathan#include <sys/sysproto.h>
80224987Sjonathan#include <sys/sysctl.h>
81224987Sjonathan#include <sys/systm.h>
82224987Sjonathan#include <sys/ucred.h>
83224987Sjonathan
84224987Sjonathan#include <security/audit/audit.h>
85224987Sjonathan
86224987Sjonathan#include <vm/uma.h>
87224987Sjonathan
88224987Sjonathan#ifdef PROCDESC
89224987Sjonathan
90224987SjonathanFEATURE(process_descriptors, "Process Descriptors");
91224987Sjonathan
92224987Sjonathanstatic uma_zone_t procdesc_zone;
93224987Sjonathan
94224987Sjonathanstatic fo_rdwr_t	procdesc_read;
95224987Sjonathanstatic fo_rdwr_t	procdesc_write;
96224987Sjonathanstatic fo_truncate_t	procdesc_truncate;
97224987Sjonathanstatic fo_ioctl_t	procdesc_ioctl;
98224987Sjonathanstatic fo_poll_t	procdesc_poll;
99224987Sjonathanstatic fo_kqfilter_t	procdesc_kqfilter;
100224987Sjonathanstatic fo_stat_t	procdesc_stat;
101224987Sjonathanstatic fo_close_t	procdesc_close;
102224987Sjonathanstatic fo_chmod_t	procdesc_chmod;
103224987Sjonathanstatic fo_chown_t	procdesc_chown;
104224987Sjonathan
105224987Sjonathanstatic struct fileops procdesc_ops = {
106224987Sjonathan	.fo_read = procdesc_read,
107224987Sjonathan	.fo_write = procdesc_write,
108224987Sjonathan	.fo_truncate = procdesc_truncate,
109224987Sjonathan	.fo_ioctl = procdesc_ioctl,
110224987Sjonathan	.fo_poll = procdesc_poll,
111224987Sjonathan	.fo_kqfilter = procdesc_kqfilter,
112224987Sjonathan	.fo_stat = procdesc_stat,
113224987Sjonathan	.fo_close = procdesc_close,
114224987Sjonathan	.fo_chmod = procdesc_chmod,
115224987Sjonathan	.fo_chown = procdesc_chown,
116254415Skib	.fo_sendfile = invfo_sendfile,
117224987Sjonathan	.fo_flags = DFLAG_PASSABLE,
118224987Sjonathan};
119224987Sjonathan
120224987Sjonathan/*
121224987Sjonathan * Initialize with VFS so that process descriptors are available along with
122224987Sjonathan * other file descriptor types.  As long as it runs before init(8) starts,
123224987Sjonathan * there shouldn't be a problem.
124224987Sjonathan */
125224987Sjonathanstatic void
126224987Sjonathanprocdesc_init(void *dummy __unused)
127224987Sjonathan{
128224987Sjonathan
129224987Sjonathan	procdesc_zone = uma_zcreate("procdesc", sizeof(struct procdesc),
130224987Sjonathan	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
131224987Sjonathan	if (procdesc_zone == NULL)
132224987Sjonathan		panic("procdesc_init: procdesc_zone not initialized");
133224987Sjonathan}
134224987SjonathanSYSINIT(vfs, SI_SUB_VFS, SI_ORDER_ANY, procdesc_init, NULL);
135224987Sjonathan
136224987Sjonathan/*
137224987Sjonathan * Return a locked process given a process descriptor, or ESRCH if it has
138224987Sjonathan * died.
139224987Sjonathan */
140224987Sjonathanint
141255219Spjdprocdesc_find(struct thread *td, int fd, cap_rights_t *rightsp,
142224987Sjonathan    struct proc **p)
143224987Sjonathan{
144224987Sjonathan	struct procdesc *pd;
145224987Sjonathan	struct file *fp;
146224987Sjonathan	int error;
147224987Sjonathan
148255219Spjd	error = fget(td, fd, rightsp, &fp);
149224987Sjonathan	if (error)
150224987Sjonathan		return (error);
151224987Sjonathan	if (fp->f_type != DTYPE_PROCDESC) {
152224987Sjonathan		error = EBADF;
153224987Sjonathan		goto out;
154224987Sjonathan	}
155224987Sjonathan	pd = fp->f_data;
156224987Sjonathan	sx_slock(&proctree_lock);
157224987Sjonathan	if (pd->pd_proc != NULL) {
158224987Sjonathan		*p = pd->pd_proc;
159224987Sjonathan		PROC_LOCK(*p);
160224987Sjonathan	} else
161224987Sjonathan		error = ESRCH;
162224987Sjonathan	sx_sunlock(&proctree_lock);
163224987Sjonathanout:
164224987Sjonathan	fdrop(fp, td);
165224987Sjonathan	return (error);
166224987Sjonathan}
167224987Sjonathan
168224987Sjonathan/*
169224987Sjonathan * Function to be used by procstat(1) sysctls when returning procdesc
170224987Sjonathan * information.
171224987Sjonathan */
172224987Sjonathanpid_t
173224987Sjonathanprocdesc_pid(struct file *fp_procdesc)
174224987Sjonathan{
175224987Sjonathan	struct procdesc *pd;
176224987Sjonathan
177224987Sjonathan	KASSERT(fp_procdesc->f_type == DTYPE_PROCDESC,
178224987Sjonathan	   ("procdesc_pid: !procdesc"));
179224987Sjonathan
180224987Sjonathan	pd = fp_procdesc->f_data;
181224987Sjonathan	return (pd->pd_pid);
182224987Sjonathan}
183224987Sjonathan
184224987Sjonathan/*
185224987Sjonathan * Retrieve the PID associated with a process descriptor.
186224987Sjonathan */
187224987Sjonathanint
188255219Spjdkern_pdgetpid(struct thread *td, int fd, cap_rights_t *rightsp, pid_t *pidp)
189224987Sjonathan{
190224987Sjonathan	struct file *fp;
191224987Sjonathan	int error;
192224987Sjonathan
193255219Spjd	error = fget(td, fd, rightsp, &fp);
194224987Sjonathan	if (error)
195224987Sjonathan		return (error);
196224987Sjonathan	if (fp->f_type != DTYPE_PROCDESC) {
197224987Sjonathan		error = EBADF;
198224987Sjonathan		goto out;
199224987Sjonathan	}
200224987Sjonathan	*pidp = procdesc_pid(fp);
201224987Sjonathanout:
202224987Sjonathan	fdrop(fp, td);
203224987Sjonathan	return (error);
204224987Sjonathan}
205224987Sjonathan
206224987Sjonathan/*
207224987Sjonathan * System call to return the pid of a process given its process descriptor.
208224987Sjonathan */
209224987Sjonathanint
210225617Skmacysys_pdgetpid(struct thread *td, struct pdgetpid_args *uap)
211224987Sjonathan{
212255219Spjd	cap_rights_t rights;
213224987Sjonathan	pid_t pid;
214224987Sjonathan	int error;
215224987Sjonathan
216224987Sjonathan	AUDIT_ARG_FD(uap->fd);
217255219Spjd	error = kern_pdgetpid(td, uap->fd,
218255219Spjd	    cap_rights_init(&rights, CAP_PDGETPID), &pid);
219224987Sjonathan	if (error == 0)
220224987Sjonathan		error = copyout(&pid, uap->pidp, sizeof(pid));
221224987Sjonathan	return (error);
222224987Sjonathan}
223224987Sjonathan
224224987Sjonathan/*
225224987Sjonathan * When a new process is forked by pdfork(), a file descriptor is allocated
226224987Sjonathan * by the fork code first, then the process is forked, and then we get a
227224987Sjonathan * chance to set up the process descriptor.  Failure is not permitted at this
228224987Sjonathan * point, so procdesc_new() must succeed.
229224987Sjonathan */
230224987Sjonathanvoid
231224987Sjonathanprocdesc_new(struct proc *p, int flags)
232224987Sjonathan{
233224987Sjonathan	struct procdesc *pd;
234224987Sjonathan
235224987Sjonathan	pd = uma_zalloc(procdesc_zone, M_WAITOK | M_ZERO);
236224987Sjonathan	pd->pd_proc = p;
237224987Sjonathan	pd->pd_pid = p->p_pid;
238224987Sjonathan	p->p_procdesc = pd;
239224987Sjonathan	pd->pd_flags = 0;
240224987Sjonathan	if (flags & PD_DAEMON)
241224987Sjonathan		pd->pd_flags |= PDF_DAEMON;
242224987Sjonathan	PROCDESC_LOCK_INIT(pd);
243224987Sjonathan
244224987Sjonathan	/*
245224987Sjonathan	 * Process descriptors start out with two references: one from their
246224987Sjonathan	 * struct file, and the other from their struct proc.
247224987Sjonathan	 */
248224987Sjonathan	refcount_init(&pd->pd_refcount, 2);
249224987Sjonathan}
250224987Sjonathan
251224987Sjonathan/*
252224987Sjonathan * Initialize a file with a process descriptor.
253224987Sjonathan */
254224987Sjonathanvoid
255224987Sjonathanprocdesc_finit(struct procdesc *pdp, struct file *fp)
256224987Sjonathan{
257224987Sjonathan
258224987Sjonathan	finit(fp, FREAD | FWRITE, DTYPE_PROCDESC, pdp, &procdesc_ops);
259224987Sjonathan}
260224987Sjonathan
261224987Sjonathanstatic void
262224987Sjonathanprocdesc_free(struct procdesc *pd)
263224987Sjonathan{
264224987Sjonathan
265224987Sjonathan	/*
266224987Sjonathan	 * When the last reference is released, we assert that the descriptor
267224987Sjonathan	 * has been closed, but not that the process has exited, as we will
268224987Sjonathan	 * detach the descriptor before the process dies if the descript is
269224987Sjonathan	 * closed, as we can't wait synchronously.
270224987Sjonathan	 */
271224987Sjonathan	if (refcount_release(&pd->pd_refcount)) {
272224987Sjonathan		KASSERT(pd->pd_proc == NULL,
273224987Sjonathan		    ("procdesc_free: pd_proc != NULL"));
274224987Sjonathan		KASSERT((pd->pd_flags & PDF_CLOSED),
275224987Sjonathan		    ("procdesc_free: !PDF_CLOSED"));
276224987Sjonathan
277224987Sjonathan		PROCDESC_LOCK_DESTROY(pd);
278224987Sjonathan		uma_zfree(procdesc_zone, pd);
279224987Sjonathan	}
280224987Sjonathan}
281224987Sjonathan
282224987Sjonathan/*
283224987Sjonathan * procdesc_exit() - notify a process descriptor that its process is exiting.
284224987Sjonathan * We use the proctree_lock to ensure that process exit either happens
285224987Sjonathan * strictly before or strictly after a concurrent call to procdesc_close().
286224987Sjonathan */
287224987Sjonathanint
288224987Sjonathanprocdesc_exit(struct proc *p)
289224987Sjonathan{
290224987Sjonathan	struct procdesc *pd;
291224987Sjonathan
292224987Sjonathan	sx_assert(&proctree_lock, SA_XLOCKED);
293224987Sjonathan	PROC_LOCK_ASSERT(p, MA_OWNED);
294224987Sjonathan	KASSERT(p->p_procdesc != NULL, ("procdesc_exit: p_procdesc NULL"));
295224987Sjonathan
296224987Sjonathan	pd = p->p_procdesc;
297224987Sjonathan
298224987Sjonathan	PROCDESC_LOCK(pd);
299224987Sjonathan	KASSERT((pd->pd_flags & PDF_CLOSED) == 0 || p->p_pptr == initproc,
300224987Sjonathan	    ("procdesc_exit: closed && parent not init"));
301224987Sjonathan
302224987Sjonathan	pd->pd_flags |= PDF_EXITED;
303224987Sjonathan
304224987Sjonathan	/*
305224987Sjonathan	 * If the process descriptor has been closed, then we have nothing
306224987Sjonathan	 * to do; return 1 so that init will get SIGCHLD and do the reaping.
307224987Sjonathan	 * Clean up the procdesc now rather than letting it happen during
308224987Sjonathan	 * that reap.
309224987Sjonathan	 */
310224987Sjonathan	if (pd->pd_flags & PDF_CLOSED) {
311224987Sjonathan		PROCDESC_UNLOCK(pd);
312224987Sjonathan		pd->pd_proc = NULL;
313224987Sjonathan		p->p_procdesc = NULL;
314224987Sjonathan		procdesc_free(pd);
315224987Sjonathan		return (1);
316224987Sjonathan	}
317224987Sjonathan	if (pd->pd_flags & PDF_SELECTED) {
318224987Sjonathan		pd->pd_flags &= ~PDF_SELECTED;
319224987Sjonathan		selwakeup(&pd->pd_selinfo);
320224987Sjonathan	}
321224987Sjonathan	PROCDESC_UNLOCK(pd);
322224987Sjonathan	return (0);
323224987Sjonathan}
324224987Sjonathan
325224987Sjonathan/*
326224987Sjonathan * When a process descriptor is reaped, perhaps as a result of close() or
327224987Sjonathan * pdwait4(), release the process's reference on the process descriptor.
328224987Sjonathan */
329224987Sjonathanvoid
330224987Sjonathanprocdesc_reap(struct proc *p)
331224987Sjonathan{
332224987Sjonathan	struct procdesc *pd;
333224987Sjonathan
334224987Sjonathan	sx_assert(&proctree_lock, SA_XLOCKED);
335224987Sjonathan	KASSERT(p->p_procdesc != NULL, ("procdesc_reap: p_procdesc == NULL"));
336224987Sjonathan
337224987Sjonathan	pd = p->p_procdesc;
338224987Sjonathan	pd->pd_proc = NULL;
339239989Spjd	p->p_procdesc = NULL;
340224987Sjonathan	procdesc_free(pd);
341224987Sjonathan}
342224987Sjonathan
343224987Sjonathan/*
344224987Sjonathan * procdesc_close() - last close on a process descriptor.  If the process is
345237277Spjd * still running, terminate with SIGKILL (unless PDF_DAEMON is set) and let
346224987Sjonathan * init(8) clean up the mess; if not, we have to clean up the zombie ourselves.
347224987Sjonathan */
348224987Sjonathanstatic int
349224987Sjonathanprocdesc_close(struct file *fp, struct thread *td)
350224987Sjonathan{
351224987Sjonathan	struct procdesc *pd;
352224987Sjonathan	struct proc *p;
353224987Sjonathan
354224987Sjonathan	KASSERT(fp->f_type == DTYPE_PROCDESC, ("procdesc_close: !procdesc"));
355224987Sjonathan
356224987Sjonathan	pd = fp->f_data;
357224987Sjonathan	fp->f_ops = &badfileops;
358224987Sjonathan	fp->f_data = NULL;
359224987Sjonathan
360224987Sjonathan	sx_xlock(&proctree_lock);
361224987Sjonathan	PROCDESC_LOCK(pd);
362224987Sjonathan	pd->pd_flags |= PDF_CLOSED;
363224987Sjonathan	PROCDESC_UNLOCK(pd);
364224987Sjonathan	p = pd->pd_proc;
365239989Spjd	if (p == NULL) {
366224987Sjonathan		/*
367239989Spjd		 * This is the case where process' exit status was already
368239989Spjd		 * collected and procdesc_reap() was already called.
369239989Spjd		 */
370239989Spjd		sx_xunlock(&proctree_lock);
371239989Spjd	} else if (p->p_state == PRS_ZOMBIE) {
372239989Spjd		/*
373224987Sjonathan		 * If the process is already dead and just awaiting reaping,
374224987Sjonathan		 * do that now.  This will release the process's reference to
375224987Sjonathan		 * the process descriptor when it calls back into
376224987Sjonathan		 * procdesc_reap().
377224987Sjonathan		 */
378239989Spjd		PROC_LOCK(p);
379224987Sjonathan		PROC_SLOCK(p);
380242958Skib		proc_reap(curthread, p, NULL, 0);
381224987Sjonathan	} else {
382224987Sjonathan		/*
383224987Sjonathan		 * If the process is not yet dead, we need to kill it, but we
384224987Sjonathan		 * can't wait around synchronously for it to go away, as that
385224987Sjonathan		 * path leads to madness (and deadlocks).  First, detach the
386224987Sjonathan		 * process from its descriptor so that its exit status will
387224987Sjonathan		 * be reported normally.
388224987Sjonathan		 */
389239989Spjd		PROC_LOCK(p);
390224987Sjonathan		pd->pd_proc = NULL;
391224987Sjonathan		p->p_procdesc = NULL;
392224987Sjonathan		procdesc_free(pd);
393224987Sjonathan
394224987Sjonathan		/*
395224987Sjonathan		 * Next, reparent it to init(8) so that there's someone to
396224987Sjonathan		 * pick up the pieces; finally, terminate with prejudice.
397224987Sjonathan		 */
398224987Sjonathan		p->p_sigparent = SIGCHLD;
399224987Sjonathan		proc_reparent(p, initproc);
400237277Spjd		if ((pd->pd_flags & PDF_DAEMON) == 0)
401225617Skmacy			kern_psignal(p, SIGKILL);
402224987Sjonathan		PROC_UNLOCK(p);
403224987Sjonathan		sx_xunlock(&proctree_lock);
404224987Sjonathan	}
405224987Sjonathan
406224987Sjonathan	/*
407224987Sjonathan	 * Release the file descriptor's reference on the process descriptor.
408224987Sjonathan	 */
409224987Sjonathan	procdesc_free(pd);
410224987Sjonathan	return (0);
411224987Sjonathan}
412224987Sjonathan
413224987Sjonathanstatic int
414224987Sjonathanprocdesc_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
415224987Sjonathan    int flags, struct thread *td)
416224987Sjonathan{
417224987Sjonathan
418224987Sjonathan	return (EOPNOTSUPP);
419224987Sjonathan}
420224987Sjonathan
421224987Sjonathanstatic int
422224987Sjonathanprocdesc_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
423224987Sjonathan    int flags, struct thread *td)
424224987Sjonathan{
425224987Sjonathan
426224987Sjonathan	return (EOPNOTSUPP);
427224987Sjonathan}
428224987Sjonathan
429224987Sjonathanstatic int
430224987Sjonathanprocdesc_truncate(struct file *fp, off_t length, struct ucred *active_cred,
431224987Sjonathan    struct thread *td)
432224987Sjonathan{
433224987Sjonathan
434224987Sjonathan	return (EOPNOTSUPP);
435224987Sjonathan}
436224987Sjonathan
437224987Sjonathanstatic int
438224987Sjonathanprocdesc_ioctl(struct file *fp, u_long com, void *data,
439224987Sjonathan    struct ucred *active_cred, struct thread *td)
440224987Sjonathan{
441224987Sjonathan
442224987Sjonathan	return (EOPNOTSUPP);
443224987Sjonathan}
444224987Sjonathan
445224987Sjonathanstatic int
446224987Sjonathanprocdesc_poll(struct file *fp, int events, struct ucred *active_cred,
447224987Sjonathan    struct thread *td)
448224987Sjonathan{
449224987Sjonathan	struct procdesc *pd;
450224987Sjonathan	int revents;
451224987Sjonathan
452224987Sjonathan	revents = 0;
453224987Sjonathan	pd = fp->f_data;
454224987Sjonathan	PROCDESC_LOCK(pd);
455224987Sjonathan	if (pd->pd_flags & PDF_EXITED)
456224987Sjonathan		revents |= POLLHUP;
457224987Sjonathan	if (revents == 0) {
458224987Sjonathan		selrecord(td, &pd->pd_selinfo);
459224987Sjonathan		pd->pd_flags |= PDF_SELECTED;
460224987Sjonathan	}
461224987Sjonathan	PROCDESC_UNLOCK(pd);
462224987Sjonathan	return (revents);
463224987Sjonathan}
464224987Sjonathan
465224987Sjonathanstatic int
466224987Sjonathanprocdesc_kqfilter(struct file *fp, struct knote *kn)
467224987Sjonathan{
468224987Sjonathan
469224987Sjonathan	return (EOPNOTSUPP);
470224987Sjonathan}
471224987Sjonathan
472224987Sjonathanstatic int
473224987Sjonathanprocdesc_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
474224987Sjonathan    struct thread *td)
475224987Sjonathan{
476224987Sjonathan	struct procdesc *pd;
477224987Sjonathan	struct timeval pstart;
478224987Sjonathan
479224987Sjonathan	/*
480224987Sjonathan	 * XXXRW: Perhaps we should cache some more information from the
481224987Sjonathan	 * process so that we can return it reliably here even after it has
482224987Sjonathan	 * died.  For example, caching its credential data.
483224987Sjonathan	 */
484224987Sjonathan	bzero(sb, sizeof(*sb));
485224987Sjonathan	pd = fp->f_data;
486224987Sjonathan	sx_slock(&proctree_lock);
487224987Sjonathan	if (pd->pd_proc != NULL) {
488224987Sjonathan		PROC_LOCK(pd->pd_proc);
489224987Sjonathan
490224987Sjonathan		/* Set birth and [acm] times to process start time. */
491224987Sjonathan		pstart = pd->pd_proc->p_stats->p_start;
492224987Sjonathan		timevaladd(&pstart, &boottime);
493224987Sjonathan		TIMEVAL_TO_TIMESPEC(&pstart, &sb->st_birthtim);
494224987Sjonathan		sb->st_atim = sb->st_birthtim;
495224987Sjonathan		sb->st_ctim = sb->st_birthtim;
496224987Sjonathan		sb->st_mtim = sb->st_birthtim;
497224987Sjonathan		if (pd->pd_proc->p_state != PRS_ZOMBIE)
498224987Sjonathan			sb->st_mode = S_IFREG | S_IRWXU;
499224987Sjonathan		else
500224987Sjonathan			sb->st_mode = S_IFREG;
501224987Sjonathan		sb->st_uid = pd->pd_proc->p_ucred->cr_ruid;
502224987Sjonathan		sb->st_gid = pd->pd_proc->p_ucred->cr_rgid;
503224987Sjonathan		PROC_UNLOCK(pd->pd_proc);
504224987Sjonathan	} else
505224987Sjonathan		sb->st_mode = S_IFREG;
506224987Sjonathan	sx_sunlock(&proctree_lock);
507224987Sjonathan	return (0);
508224987Sjonathan}
509224987Sjonathan
510224987Sjonathanstatic int
511224987Sjonathanprocdesc_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
512224987Sjonathan    struct thread *td)
513224987Sjonathan{
514224987Sjonathan
515224987Sjonathan	return (EOPNOTSUPP);
516224987Sjonathan}
517224987Sjonathan
518224987Sjonathanstatic int
519224987Sjonathanprocdesc_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
520224987Sjonathan    struct thread *td)
521224987Sjonathan{
522224987Sjonathan
523224987Sjonathan	return (EOPNOTSUPP);
524224987Sjonathan}
525224987Sjonathan
526224987Sjonathan#else /* !PROCDESC */
527224987Sjonathan
528224987Sjonathanint
529225617Skmacysys_pdgetpid(struct thread *td, struct pdgetpid_args *uap)
530224987Sjonathan{
531224987Sjonathan
532224987Sjonathan	return (ENOSYS);
533224987Sjonathan}
534224987Sjonathan
535224987Sjonathan#endif /* PROCDESC */
536