2005-03-20 22:15:48 +03:00
|
|
|
/* $NetBSD: kern_resource.c,v 1.88 2005/03/20 19:15:48 christos Exp $ */
|
1994-06-29 10:29:24 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*-
|
1994-05-19 12:13:09 +04:00
|
|
|
* Copyright (c) 1982, 1986, 1991, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1994-05-17 08:21:49 +04:00
|
|
|
* (c) UNIX System Laboratories, Inc.
|
|
|
|
* All or some portions of this file are derived from material licensed
|
|
|
|
* to the University of California by American Telephone and Telegraph
|
|
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
|
|
|
* the permission of UNIX System Laboratories, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-07 20:26:28 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1994-05-17 08:21:49 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
1998-03-01 05:20:01 +03:00
|
|
|
* @(#)kern_resource.c 8.8 (Berkeley) 2/14/95
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
|
|
|
|
2001-11-12 18:25:01 +03:00
|
|
|
#include <sys/cdefs.h>
|
2005-03-20 22:15:48 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_resource.c,v 1.88 2005/03/20 19:15:48 christos Exp $");
|
2001-11-12 18:25:01 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/param.h>
|
1994-10-20 07:22:35 +03:00
|
|
|
#include <sys/systm.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/kernel.h>
|
1994-05-19 12:13:09 +04:00
|
|
|
#include <sys/file.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/resourcevar.h>
|
|
|
|
#include <sys/malloc.h>
|
1998-09-01 03:53:19 +04:00
|
|
|
#include <sys/pool.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/proc.h>
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
#include <sys/sysctl.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
#include <sys/mount.h>
|
2003-01-18 13:06:22 +03:00
|
|
|
#include <sys/sa.h>
|
1994-10-20 07:22:35 +03:00
|
|
|
#include <sys/syscallargs.h>
|
|
|
|
|
1998-02-05 10:59:28 +03:00
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
2001-02-06 22:54:43 +03:00
|
|
|
/*
|
|
|
|
* Maximum process data and stack limits.
|
|
|
|
* They are variables so they are patchable.
|
|
|
|
*/
|
|
|
|
rlim_t maxdmap = MAXDSIZ;
|
|
|
|
rlim_t maxsmap = MAXSSIZ;
|
|
|
|
|
2004-05-01 10:17:26 +04:00
|
|
|
struct uihashhead *uihashtbl;
|
|
|
|
u_long uihash; /* size of hash table - 1 */
|
2005-03-20 22:15:48 +03:00
|
|
|
struct simplelock uihashtbl_slock = SIMPLELOCK_INITIALIZER;
|
2004-05-01 10:17:26 +04:00
|
|
|
|
2004-04-17 19:15:29 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Resource controls and accounting.
|
|
|
|
*/
|
|
|
|
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
2003-01-18 13:06:22 +03:00
|
|
|
sys_getpriority(l, v, retval)
|
|
|
|
struct lwp *l;
|
1995-09-20 01:40:36 +04:00
|
|
|
void *v;
|
|
|
|
register_t *retval;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct sys_getpriority_args /* {
|
1994-10-20 07:22:35 +03:00
|
|
|
syscallarg(int) which;
|
2004-04-26 02:18:08 +04:00
|
|
|
syscallarg(id_t) who;
|
1995-09-20 01:40:36 +04:00
|
|
|
} */ *uap = v;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct proc *curp = l->l_proc, *p;
|
2000-03-30 13:27:11 +04:00
|
|
|
int low = NZERO + PRIO_MAX + 1;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
switch (SCARG(uap, which)) {
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
case PRIO_PROCESS:
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
1994-05-17 08:21:49 +04:00
|
|
|
p = curp;
|
|
|
|
else
|
1994-10-20 07:22:35 +03:00
|
|
|
p = pfind(SCARG(uap, who));
|
1994-05-17 08:21:49 +04:00
|
|
|
if (p == 0)
|
|
|
|
break;
|
|
|
|
low = p->p_nice;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIO_PGRP: {
|
2000-03-30 13:27:11 +04:00
|
|
|
struct pgrp *pg;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
1994-05-17 08:21:49 +04:00
|
|
|
pg = curp->p_pgrp;
|
1994-10-20 07:22:35 +03:00
|
|
|
else if ((pg = pgfind(SCARG(uap, who))) == NULL)
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
2002-09-04 05:32:31 +04:00
|
|
|
LIST_FOREACH(p, &pg->pg_members, p_pglist) {
|
1994-05-17 08:21:49 +04:00
|
|
|
if (p->p_nice < low)
|
|
|
|
low = p->p_nice;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PRIO_USER:
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
|
|
|
SCARG(uap, who) = curp->p_ucred->cr_uid;
|
1999-07-25 10:30:33 +04:00
|
|
|
proclist_lock_read();
|
2004-10-01 20:30:52 +04:00
|
|
|
PROCLIST_FOREACH(p, &allproc) {
|
2002-08-26 01:44:13 +04:00
|
|
|
if (p->p_ucred->cr_uid == (uid_t) SCARG(uap, who) &&
|
1994-10-20 07:22:35 +03:00
|
|
|
p->p_nice < low)
|
1994-05-17 08:21:49 +04:00
|
|
|
low = p->p_nice;
|
2002-09-04 05:32:31 +04:00
|
|
|
}
|
1999-07-23 01:08:30 +04:00
|
|
|
proclist_unlock_read();
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
1996-10-02 22:04:56 +04:00
|
|
|
if (low == NZERO + PRIO_MAX + 1)
|
1994-05-17 08:21:49 +04:00
|
|
|
return (ESRCH);
|
1996-10-02 22:04:56 +04:00
|
|
|
*retval = low - NZERO;
|
1994-05-17 08:21:49 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
2003-01-18 13:06:22 +03:00
|
|
|
sys_setpriority(l, v, retval)
|
|
|
|
struct lwp *l;
|
1995-09-20 01:40:36 +04:00
|
|
|
void *v;
|
|
|
|
register_t *retval;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct sys_setpriority_args /* {
|
1994-10-20 07:22:35 +03:00
|
|
|
syscallarg(int) which;
|
2004-04-26 02:18:08 +04:00
|
|
|
syscallarg(id_t) who;
|
1994-10-20 07:22:35 +03:00
|
|
|
syscallarg(int) prio;
|
1995-09-20 01:40:36 +04:00
|
|
|
} */ *uap = v;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct proc *curp = l->l_proc, *p;
|
1994-05-17 08:21:49 +04:00
|
|
|
int found = 0, error = 0;
|
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
switch (SCARG(uap, which)) {
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
case PRIO_PROCESS:
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
1994-05-17 08:21:49 +04:00
|
|
|
p = curp;
|
|
|
|
else
|
1994-10-20 07:22:35 +03:00
|
|
|
p = pfind(SCARG(uap, who));
|
1994-05-17 08:21:49 +04:00
|
|
|
if (p == 0)
|
|
|
|
break;
|
1994-10-20 07:22:35 +03:00
|
|
|
error = donice(curp, p, SCARG(uap, prio));
|
1994-05-17 08:21:49 +04:00
|
|
|
found++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIO_PGRP: {
|
2000-03-30 13:27:11 +04:00
|
|
|
struct pgrp *pg;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
1994-05-17 08:21:49 +04:00
|
|
|
pg = curp->p_pgrp;
|
1994-10-20 07:22:35 +03:00
|
|
|
else if ((pg = pgfind(SCARG(uap, who))) == NULL)
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
2002-09-04 05:32:31 +04:00
|
|
|
LIST_FOREACH(p, &pg->pg_members, p_pglist) {
|
1994-10-20 07:22:35 +03:00
|
|
|
error = donice(curp, p, SCARG(uap, prio));
|
1994-05-17 08:21:49 +04:00
|
|
|
found++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PRIO_USER:
|
1994-10-20 07:22:35 +03:00
|
|
|
if (SCARG(uap, who) == 0)
|
|
|
|
SCARG(uap, who) = curp->p_ucred->cr_uid;
|
1999-07-25 10:30:33 +04:00
|
|
|
proclist_lock_read();
|
2004-10-01 20:30:52 +04:00
|
|
|
PROCLIST_FOREACH(p, &allproc) {
|
2002-08-26 01:44:13 +04:00
|
|
|
if (p->p_ucred->cr_uid == (uid_t) SCARG(uap, who)) {
|
1994-10-20 07:22:35 +03:00
|
|
|
error = donice(curp, p, SCARG(uap, prio));
|
1994-05-17 08:21:49 +04:00
|
|
|
found++;
|
|
|
|
}
|
2002-09-04 05:32:31 +04:00
|
|
|
}
|
1999-07-23 01:08:30 +04:00
|
|
|
proclist_unlock_read();
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (found == 0)
|
|
|
|
return (ESRCH);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
1994-05-17 08:21:49 +04:00
|
|
|
donice(curp, chgp, n)
|
2000-03-30 13:27:11 +04:00
|
|
|
struct proc *curp, *chgp;
|
|
|
|
int n;
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct pcred *pcred = curp->p_cred;
|
2000-08-21 01:50:06 +04:00
|
|
|
int s;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
|
|
|
|
pcred->pc_ucred->cr_uid != chgp->p_ucred->cr_uid &&
|
|
|
|
pcred->p_ruid != chgp->p_ucred->cr_uid)
|
|
|
|
return (EPERM);
|
|
|
|
if (n > PRIO_MAX)
|
|
|
|
n = PRIO_MAX;
|
|
|
|
if (n < PRIO_MIN)
|
|
|
|
n = PRIO_MIN;
|
1996-10-02 22:04:56 +04:00
|
|
|
n += NZERO;
|
1994-05-17 08:21:49 +04:00
|
|
|
if (n < chgp->p_nice && suser(pcred->pc_ucred, &curp->p_acflag))
|
|
|
|
return (EACCES);
|
|
|
|
chgp->p_nice = n;
|
2000-08-21 01:50:06 +04:00
|
|
|
SCHED_LOCK(s);
|
2003-01-18 13:06:22 +03:00
|
|
|
(void)resetprocpriority(chgp);
|
2000-08-21 01:50:06 +04:00
|
|
|
SCHED_UNLOCK(s);
|
1994-05-17 08:21:49 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
2003-01-18 13:06:22 +03:00
|
|
|
sys_setrlimit(l, v, retval)
|
|
|
|
struct lwp *l;
|
1995-09-20 01:40:36 +04:00
|
|
|
void *v;
|
|
|
|
register_t *retval;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct sys_setrlimit_args /* {
|
1997-10-15 21:03:52 +04:00
|
|
|
syscallarg(int) which;
|
1996-12-22 13:21:06 +03:00
|
|
|
syscallarg(const struct rlimit *) rlp;
|
1995-09-20 01:40:36 +04:00
|
|
|
} */ *uap = v;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct proc *p = l->l_proc;
|
1997-10-15 21:03:52 +04:00
|
|
|
int which = SCARG(uap, which);
|
1994-05-19 12:13:09 +04:00
|
|
|
struct rlimit alim;
|
1994-05-17 08:21:49 +04:00
|
|
|
int error;
|
|
|
|
|
1998-08-01 02:50:48 +04:00
|
|
|
error = copyin(SCARG(uap, rlp), &alim, sizeof(struct rlimit));
|
1996-02-04 05:15:01 +03:00
|
|
|
if (error)
|
1994-05-17 08:21:49 +04:00
|
|
|
return (error);
|
1999-09-28 18:47:00 +04:00
|
|
|
return (dosetrlimit(p, p->p_cred, which, &alim));
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
1999-09-28 18:47:00 +04:00
|
|
|
dosetrlimit(p, cred, which, limp)
|
1994-05-17 08:21:49 +04:00
|
|
|
struct proc *p;
|
1999-09-28 18:47:00 +04:00
|
|
|
struct pcred *cred;
|
1997-10-15 21:03:52 +04:00
|
|
|
int which;
|
1994-05-17 08:21:49 +04:00
|
|
|
struct rlimit *limp;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct rlimit *alimp;
|
2004-05-07 02:20:30 +04:00
|
|
|
struct plimit *oldplim;
|
1994-05-17 08:21:49 +04:00
|
|
|
int error;
|
|
|
|
|
2002-10-03 09:18:59 +04:00
|
|
|
if ((u_int)which >= RLIM_NLIMITS)
|
1994-05-17 08:21:49 +04:00
|
|
|
return (EINVAL);
|
1996-10-23 11:19:38 +04:00
|
|
|
|
|
|
|
if (limp->rlim_cur < 0 || limp->rlim_max < 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
alimp = &p->p_rlimit[which];
|
1999-09-28 18:47:00 +04:00
|
|
|
/* if we don't change the value, no need to limcopy() */
|
|
|
|
if (limp->rlim_cur == alimp->rlim_cur &&
|
|
|
|
limp->rlim_max == alimp->rlim_max)
|
|
|
|
return 0;
|
|
|
|
|
2001-11-23 21:56:33 +03:00
|
|
|
if (limp->rlim_cur > limp->rlim_max) {
|
|
|
|
/*
|
|
|
|
* This is programming error. According to SUSv2, we should
|
|
|
|
* return error in this case.
|
|
|
|
*/
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (limp->rlim_max > alimp->rlim_max
|
|
|
|
&& (error = suser(cred->pc_ucred, &p->p_acflag)) != 0)
|
1994-05-17 08:21:49 +04:00
|
|
|
return (error);
|
2001-11-23 21:56:33 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
if (p->p_limit->p_refcnt > 1 &&
|
|
|
|
(p->p_limit->p_lflags & PL_SHAREMOD) == 0) {
|
2004-05-07 02:20:30 +04:00
|
|
|
p->p_limit = limcopy(oldplim = p->p_limit);
|
|
|
|
limfree(oldplim);
|
1994-05-17 08:21:49 +04:00
|
|
|
alimp = &p->p_rlimit[which];
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (which) {
|
|
|
|
|
|
|
|
case RLIMIT_DATA:
|
1994-05-19 12:13:09 +04:00
|
|
|
if (limp->rlim_cur > maxdmap)
|
|
|
|
limp->rlim_cur = maxdmap;
|
|
|
|
if (limp->rlim_max > maxdmap)
|
|
|
|
limp->rlim_max = maxdmap;
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RLIMIT_STACK:
|
1994-05-19 12:13:09 +04:00
|
|
|
if (limp->rlim_cur > maxsmap)
|
|
|
|
limp->rlim_cur = maxsmap;
|
|
|
|
if (limp->rlim_max > maxsmap)
|
|
|
|
limp->rlim_max = maxsmap;
|
1997-10-09 05:04:13 +04:00
|
|
|
|
2001-11-23 21:56:33 +03:00
|
|
|
/*
|
|
|
|
* Return EINVAL if the new stack size limit is lower than
|
|
|
|
* current usage. Otherwise, the process would get SIGSEGV the
|
|
|
|
* moment it would try to access anything on it's current stack.
|
|
|
|
* This conforms to SUSv2.
|
|
|
|
*/
|
|
|
|
if (limp->rlim_cur < p->p_vmspace->vm_ssize * PAGE_SIZE
|
|
|
|
|| limp->rlim_max < p->p_vmspace->vm_ssize * PAGE_SIZE)
|
|
|
|
return (EINVAL);
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
1997-10-09 05:04:13 +04:00
|
|
|
* Stack is allocated to the max at exec time with
|
|
|
|
* only "rlim_cur" bytes accessible (In other words,
|
|
|
|
* allocates stack dividing two contiguous regions at
|
|
|
|
* "rlim_cur" bytes boundary).
|
|
|
|
*
|
|
|
|
* Since allocation is done in terms of page, roundup
|
|
|
|
* "rlim_cur" (otherwise, contiguous regions
|
|
|
|
* overlap). If stack limit is going up make more
|
|
|
|
* accessible, if going down make inaccessible.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1997-10-09 05:04:13 +04:00
|
|
|
limp->rlim_cur = round_page(limp->rlim_cur);
|
1994-05-17 08:21:49 +04:00
|
|
|
if (limp->rlim_cur != alimp->rlim_cur) {
|
1998-08-13 06:10:37 +04:00
|
|
|
vaddr_t addr;
|
|
|
|
vsize_t size;
|
1994-05-17 08:21:49 +04:00
|
|
|
vm_prot_t prot;
|
|
|
|
|
|
|
|
if (limp->rlim_cur > alimp->rlim_cur) {
|
2003-08-24 21:52:28 +04:00
|
|
|
prot = VM_PROT_READ | VM_PROT_WRITE;
|
1994-05-17 08:21:49 +04:00
|
|
|
size = limp->rlim_cur - alimp->rlim_cur;
|
|
|
|
addr = USRSTACK - limp->rlim_cur;
|
|
|
|
} else {
|
|
|
|
prot = VM_PROT_NONE;
|
|
|
|
size = alimp->rlim_cur - limp->rlim_cur;
|
|
|
|
addr = USRSTACK - alimp->rlim_cur;
|
|
|
|
}
|
1998-02-05 10:59:28 +03:00
|
|
|
(void) uvm_map_protect(&p->p_vmspace->vm_map,
|
|
|
|
addr, addr+size, prot, FALSE);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
break;
|
1994-05-19 12:13:09 +04:00
|
|
|
|
|
|
|
case RLIMIT_NOFILE:
|
|
|
|
if (limp->rlim_cur > maxfiles)
|
|
|
|
limp->rlim_cur = maxfiles;
|
|
|
|
if (limp->rlim_max > maxfiles)
|
|
|
|
limp->rlim_max = maxfiles;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RLIMIT_NPROC:
|
|
|
|
if (limp->rlim_cur > maxproc)
|
|
|
|
limp->rlim_cur = maxproc;
|
|
|
|
if (limp->rlim_max > maxproc)
|
|
|
|
limp->rlim_max = maxproc;
|
|
|
|
break;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
*alimp = *limp;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
2003-01-18 13:06:22 +03:00
|
|
|
sys_getrlimit(l, v, retval)
|
|
|
|
struct lwp *l;
|
1995-09-20 01:40:36 +04:00
|
|
|
void *v;
|
|
|
|
register_t *retval;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct sys_getrlimit_args /* {
|
1997-10-15 21:03:52 +04:00
|
|
|
syscallarg(int) which;
|
1994-10-20 07:22:35 +03:00
|
|
|
syscallarg(struct rlimit *) rlp;
|
1995-09-20 01:40:36 +04:00
|
|
|
} */ *uap = v;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct proc *p = l->l_proc;
|
1997-10-15 21:03:52 +04:00
|
|
|
int which = SCARG(uap, which);
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2002-10-03 09:18:59 +04:00
|
|
|
if ((u_int)which >= RLIM_NLIMITS)
|
1994-05-17 08:21:49 +04:00
|
|
|
return (EINVAL);
|
1997-10-15 21:03:52 +04:00
|
|
|
return (copyout(&p->p_rlimit[which], SCARG(uap, rlp),
|
1998-08-01 02:50:48 +04:00
|
|
|
sizeof(struct rlimit)));
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Transform the running time and tick information in proc p into user,
|
|
|
|
* system, and interrupt time usage.
|
|
|
|
*/
|
1994-12-24 18:07:22 +03:00
|
|
|
void
|
1994-05-17 08:21:49 +04:00
|
|
|
calcru(p, up, sp, ip)
|
2000-03-30 13:27:11 +04:00
|
|
|
struct proc *p;
|
|
|
|
struct timeval *up;
|
|
|
|
struct timeval *sp;
|
|
|
|
struct timeval *ip;
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
u_quad_t u, st, ut, it, tot;
|
2003-03-15 00:38:26 +03:00
|
|
|
unsigned long sec;
|
|
|
|
long usec;
|
2000-03-30 13:27:11 +04:00
|
|
|
int s;
|
1994-05-17 08:21:49 +04:00
|
|
|
struct timeval tv;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct lwp *l;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
s = splstatclock();
|
|
|
|
st = p->p_sticks;
|
|
|
|
ut = p->p_uticks;
|
|
|
|
it = p->p_iticks;
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
sec = p->p_rtime.tv_sec;
|
|
|
|
usec = p->p_rtime.tv_usec;
|
2003-03-15 00:38:26 +03:00
|
|
|
LIST_FOREACH(l, &p->p_lwps, l_sibling) {
|
2003-01-18 13:06:22 +03:00
|
|
|
if (l->l_stat == LSONPROC) {
|
|
|
|
struct schedstate_percpu *spc;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2003-01-18 13:06:22 +03:00
|
|
|
KDASSERT(l->l_cpu != NULL);
|
|
|
|
spc = &l->l_cpu->ci_schedstate;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2003-01-18 13:06:22 +03:00
|
|
|
/*
|
|
|
|
* Adjust for the current time slice. This is
|
|
|
|
* actually fairly important since the error
|
|
|
|
* here is on the order of a time quantum,
|
|
|
|
* which is much greater than the sampling
|
2005-02-27 00:34:55 +03:00
|
|
|
* error.
|
2003-01-18 13:06:22 +03:00
|
|
|
*/
|
|
|
|
microtime(&tv);
|
|
|
|
sec += tv.tv_sec - spc->spc_runtime.tv_sec;
|
|
|
|
usec += tv.tv_usec - spc->spc_runtime.tv_usec;
|
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2003-03-05 14:44:01 +03:00
|
|
|
|
|
|
|
tot = st + ut + it;
|
2003-03-15 00:38:26 +03:00
|
|
|
u = sec * 1000000ull + usec;
|
|
|
|
|
2003-03-05 14:44:01 +03:00
|
|
|
if (tot == 0) {
|
|
|
|
/* No ticks, so can't use to share time out, split 50-50 */
|
2003-03-15 00:38:26 +03:00
|
|
|
st = ut = u / 2;
|
|
|
|
} else {
|
|
|
|
st = (u * st) / tot;
|
|
|
|
ut = (u * ut) / tot;
|
2003-03-05 14:44:01 +03:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
sp->tv_sec = st / 1000000;
|
|
|
|
sp->tv_usec = st % 1000000;
|
|
|
|
up->tv_sec = ut / 1000000;
|
|
|
|
up->tv_usec = ut % 1000000;
|
|
|
|
if (ip != NULL) {
|
2003-03-15 00:38:26 +03:00
|
|
|
if (it != 0)
|
|
|
|
it = (u * it) / tot;
|
1994-05-17 08:21:49 +04:00
|
|
|
ip->tv_sec = it / 1000000;
|
|
|
|
ip->tv_usec = it % 1000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
1994-12-24 18:07:22 +03:00
|
|
|
int
|
2003-01-18 13:06:22 +03:00
|
|
|
sys_getrusage(l, v, retval)
|
|
|
|
struct lwp *l;
|
1995-09-20 01:40:36 +04:00
|
|
|
void *v;
|
|
|
|
register_t *retval;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct sys_getrusage_args /* {
|
1994-10-20 07:22:35 +03:00
|
|
|
syscallarg(int) who;
|
|
|
|
syscallarg(struct rusage *) rusage;
|
1995-09-20 01:40:36 +04:00
|
|
|
} */ *uap = v;
|
2000-03-30 13:27:11 +04:00
|
|
|
struct rusage *rup;
|
2003-01-18 13:06:22 +03:00
|
|
|
struct proc *p = l->l_proc;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-10-20 07:22:35 +03:00
|
|
|
switch (SCARG(uap, who)) {
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-05-19 12:13:09 +04:00
|
|
|
case RUSAGE_SELF:
|
1994-05-17 08:21:49 +04:00
|
|
|
rup = &p->p_stats->p_ru;
|
|
|
|
calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RUSAGE_CHILDREN:
|
|
|
|
rup = &p->p_stats->p_cru;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
1998-08-01 02:50:48 +04:00
|
|
|
return (copyout(rup, SCARG(uap, rusage), sizeof(struct rusage)));
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
1994-12-24 18:07:22 +03:00
|
|
|
void
|
1994-05-17 08:21:49 +04:00
|
|
|
ruadd(ru, ru2)
|
2000-03-30 13:27:11 +04:00
|
|
|
struct rusage *ru, *ru2;
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
long *ip, *ip2;
|
|
|
|
int i;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1995-03-21 16:33:34 +03:00
|
|
|
timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
|
|
|
|
timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
|
1994-05-17 08:21:49 +04:00
|
|
|
if (ru->ru_maxrss < ru2->ru_maxrss)
|
|
|
|
ru->ru_maxrss = ru2->ru_maxrss;
|
|
|
|
ip = &ru->ru_first; ip2 = &ru2->ru_first;
|
|
|
|
for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
|
|
|
|
*ip++ += *ip2++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a copy of the plimit structure.
|
|
|
|
* We share these structures copy-on-write after fork,
|
|
|
|
* and copy when a limit is changed.
|
|
|
|
*/
|
|
|
|
struct plimit *
|
|
|
|
limcopy(lim)
|
|
|
|
struct plimit *lim;
|
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
struct plimit *newlim;
|
2004-05-07 02:20:30 +04:00
|
|
|
size_t l = 0;
|
|
|
|
|
|
|
|
simple_lock(&lim->p_slock);
|
|
|
|
if (lim->pl_corename != defcorename)
|
|
|
|
l = strlen(lim->pl_corename) + 1;
|
|
|
|
simple_unlock(&lim->p_slock);
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1998-09-01 03:53:19 +04:00
|
|
|
newlim = pool_get(&plimit_pool, PR_WAITOK);
|
2004-05-07 02:20:30 +04:00
|
|
|
simple_lock_init(&newlim->p_slock);
|
|
|
|
newlim->p_lflags = 0;
|
|
|
|
newlim->p_refcnt = 1;
|
|
|
|
newlim->pl_corename = (l != 0)
|
|
|
|
? malloc(l, M_TEMP, M_WAITOK)
|
|
|
|
: defcorename;
|
|
|
|
|
|
|
|
simple_lock(&lim->p_slock);
|
Abolition of bcopy, ovbcopy, bcmp, and bzero, phase one.
bcopy(x, y, z) -> memcpy(y, x, z)
ovbcopy(x, y, z) -> memmove(y, x, z)
bcmp(x, y, z) -> memcmp(x, y, z)
bzero(x, y) -> memset(x, 0, y)
1998-08-04 08:03:10 +04:00
|
|
|
memcpy(newlim->pl_rlimit, lim->pl_rlimit,
|
1994-05-17 08:21:49 +04:00
|
|
|
sizeof(struct rlimit) * RLIM_NLIMITS);
|
2004-05-07 02:20:30 +04:00
|
|
|
|
|
|
|
if (l != 0)
|
2003-05-16 18:25:02 +04:00
|
|
|
strlcpy(newlim->pl_corename, lim->pl_corename, l);
|
2004-05-07 02:20:30 +04:00
|
|
|
simple_unlock(&lim->p_slock);
|
|
|
|
|
1995-12-09 07:09:32 +03:00
|
|
|
return (newlim);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
limfree(lim)
|
|
|
|
struct plimit *lim;
|
|
|
|
{
|
2004-05-13 21:43:11 +04:00
|
|
|
int n;
|
2004-05-13 21:56:14 +04:00
|
|
|
|
2004-05-07 02:20:30 +04:00
|
|
|
simple_lock(&lim->p_slock);
|
2004-05-13 21:43:11 +04:00
|
|
|
n = --lim->p_refcnt;
|
2004-05-07 02:20:30 +04:00
|
|
|
simple_unlock(&lim->p_slock);
|
|
|
|
if (n > 0)
|
1995-12-09 07:09:32 +03:00
|
|
|
return;
|
1999-09-28 18:47:00 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2004-05-07 02:20:30 +04:00
|
|
|
if (n < 0)
|
1999-09-28 18:47:00 +04:00
|
|
|
panic("limfree");
|
|
|
|
#endif
|
|
|
|
if (lim->pl_corename != defcorename)
|
|
|
|
free(lim->pl_corename, M_TEMP);
|
1998-09-01 03:53:19 +04:00
|
|
|
pool_put(&plimit_pool, lim);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2003-01-18 13:06:22 +03:00
|
|
|
|
|
|
|
struct pstats *
|
|
|
|
pstatscopy(ps)
|
|
|
|
struct pstats *ps;
|
|
|
|
{
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2003-01-18 13:06:22 +03:00
|
|
|
struct pstats *newps;
|
|
|
|
|
|
|
|
newps = pool_get(&pstats_pool, PR_WAITOK);
|
|
|
|
|
|
|
|
memset(&newps->pstat_startzero, 0,
|
|
|
|
(unsigned) ((caddr_t)&newps->pstat_endzero -
|
|
|
|
(caddr_t)&newps->pstat_startzero));
|
|
|
|
memcpy(&newps->pstat_startcopy, &ps->pstat_startcopy,
|
|
|
|
((caddr_t)&newps->pstat_endcopy -
|
|
|
|
(caddr_t)&newps->pstat_startcopy));
|
|
|
|
|
|
|
|
return (newps);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pstatsfree(ps)
|
|
|
|
struct pstats *ps;
|
|
|
|
{
|
|
|
|
|
|
|
|
pool_put(&pstats_pool, ps);
|
|
|
|
}
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* sysctl interface in five parts
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a routine for sysctl proc subtree helpers that need to pick a valid
|
|
|
|
* process by pid.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_proc_findproc(struct proc *p, struct proc **p2, pid_t pid)
|
|
|
|
{
|
|
|
|
struct proc *ptmp;
|
|
|
|
int i, error = 0;
|
|
|
|
|
|
|
|
if (pid == PROC_CURPROC)
|
|
|
|
ptmp = p;
|
|
|
|
else if ((ptmp = pfind(pid)) == NULL)
|
|
|
|
error = ESRCH;
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* suid proc of ours or proc not ours
|
|
|
|
*/
|
|
|
|
if (p->p_cred->p_ruid != ptmp->p_cred->p_ruid ||
|
|
|
|
p->p_cred->p_ruid != ptmp->p_cred->p_svuid)
|
|
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sgid proc has sgid back to us temporarily
|
|
|
|
*/
|
|
|
|
else if (ptmp->p_cred->p_rgid != ptmp->p_cred->p_svgid)
|
|
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* our rgid must be in target's group list (ie,
|
|
|
|
* sub-processes started by a sgid process)
|
|
|
|
*/
|
|
|
|
else {
|
|
|
|
for (i = 0; i < p->p_ucred->cr_ngroups; i++) {
|
|
|
|
if (p->p_ucred->cr_groups[i] ==
|
|
|
|
ptmp->p_cred->p_rgid)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == p->p_ucred->cr_ngroups)
|
|
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*p2 = ptmp;
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sysctl helper routine for setting a process's specific corefile
|
|
|
|
* name. picks the process based on the given pid and checks the
|
|
|
|
* correctness of the new value.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_proc_corename(SYSCTLFN_ARGS)
|
|
|
|
{
|
|
|
|
struct proc *ptmp, *p;
|
2004-05-07 02:20:30 +04:00
|
|
|
struct plimit *lim;
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
int error = 0, len;
|
|
|
|
char cname[MAXPATHLEN], *tmp;
|
|
|
|
struct sysctlnode node;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* is this all correct?
|
|
|
|
*/
|
|
|
|
if (namelen != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
if (name[-1] != PROC_PID_CORENAME)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* whom are we tweaking?
|
|
|
|
*/
|
|
|
|
p = l->l_proc;
|
|
|
|
error = sysctl_proc_findproc(p, &ptmp, (pid_t)name[-2]);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* let them modify a temporary copy of the core name
|
|
|
|
*/
|
|
|
|
node = *rnode;
|
|
|
|
strlcpy(cname, ptmp->p_limit->pl_corename, sizeof(cname));
|
|
|
|
node.sysctl_data = cname;
|
|
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if that failed, or they have nothing new to say, or we've
|
|
|
|
* heard it before...
|
|
|
|
*/
|
|
|
|
if (error || newp == NULL ||
|
|
|
|
strcmp(cname, ptmp->p_limit->pl_corename) == 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* no error yet and cname now has the new core name in it.
|
|
|
|
* let's see if it looks acceptable. it must be either "core"
|
|
|
|
* or end in ".core" or "/core".
|
|
|
|
*/
|
|
|
|
len = strlen(cname);
|
|
|
|
if (len < 4)
|
|
|
|
return (EINVAL);
|
|
|
|
if (strcmp(cname + len - 4, "core") != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
if (len > 4 && cname[len - 5] != '/' && cname[len - 5] != '.')
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hmm...looks good. now...where do we put it?
|
|
|
|
*/
|
|
|
|
tmp = malloc(len + 1, M_TEMP, M_WAITOK|M_CANFAIL);
|
|
|
|
if (tmp == NULL)
|
|
|
|
return (ENOMEM);
|
|
|
|
strlcpy(tmp, cname, len + 1);
|
|
|
|
|
2004-05-07 02:20:30 +04:00
|
|
|
lim = ptmp->p_limit;
|
|
|
|
if (lim->p_refcnt > 1 && (lim->p_lflags & PL_SHAREMOD) == 0) {
|
|
|
|
ptmp->p_limit = limcopy(lim);
|
|
|
|
limfree(lim);
|
|
|
|
lim = ptmp->p_limit;
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
}
|
2004-05-07 02:20:30 +04:00
|
|
|
if (lim->pl_corename != defcorename)
|
|
|
|
free(lim->pl_corename, M_TEMP);
|
|
|
|
lim->pl_corename = tmp;
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sysctl helper routine for checking/setting a process's stop flags,
|
|
|
|
* one for fork and one for exec.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_proc_stop(SYSCTLFN_ARGS)
|
|
|
|
{
|
|
|
|
struct proc *p, *ptmp;
|
|
|
|
int i, f, error = 0;
|
|
|
|
struct sysctlnode node;
|
|
|
|
|
|
|
|
if (namelen != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
p = l->l_proc;
|
|
|
|
error = sysctl_proc_findproc(p, &ptmp, (pid_t)name[-2]);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
switch (rnode->sysctl_num) {
|
|
|
|
case PROC_PID_STOPFORK:
|
|
|
|
f = P_STOPFORK;
|
|
|
|
break;
|
|
|
|
case PROC_PID_STOPEXEC:
|
|
|
|
f = P_STOPEXEC;
|
|
|
|
break;
|
|
|
|
case PROC_PID_STOPEXIT:
|
|
|
|
f = P_STOPEXIT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
i = (ptmp->p_flag & f) ? 1 : 0;
|
|
|
|
node = *rnode;
|
|
|
|
node.sysctl_data = &i;
|
|
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
|
|
if (error || newp == NULL)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if (i)
|
|
|
|
ptmp->p_flag |= f;
|
|
|
|
else
|
|
|
|
ptmp->p_flag &= ~f;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sysctl helper routine for a process's rlimits as exposed by sysctl.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
sysctl_proc_plimit(SYSCTLFN_ARGS)
|
|
|
|
{
|
|
|
|
struct proc *ptmp, *p;
|
|
|
|
u_int limitno;
|
|
|
|
int which, error = 0;
|
|
|
|
struct rlimit alim;
|
|
|
|
struct sysctlnode node;
|
|
|
|
|
|
|
|
if (namelen != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
which = name[-1];
|
|
|
|
if (which != PROC_PID_LIMIT_TYPE_SOFT &&
|
|
|
|
which != PROC_PID_LIMIT_TYPE_HARD)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
limitno = name[-2] - 1;
|
|
|
|
if (limitno >= RLIM_NLIMITS)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (name[-3] != PROC_PID_LIMIT)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
p = l->l_proc;
|
|
|
|
error = sysctl_proc_findproc(p, &ptmp, (pid_t)name[-4]);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
node = *rnode;
|
|
|
|
memcpy(&alim, &ptmp->p_rlimit[limitno], sizeof(alim));
|
|
|
|
if (which == PROC_PID_LIMIT_TYPE_HARD)
|
|
|
|
node.sysctl_data = &alim.rlim_max;
|
|
|
|
else
|
|
|
|
node.sysctl_data = &alim.rlim_cur;
|
|
|
|
|
|
|
|
error = sysctl_lookup(SYSCTLFN_CALL(&node));
|
|
|
|
if (error || newp == NULL)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
return (dosetrlimit(ptmp, p->p_cred, limitno, &alim));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* and finally, the actually glue that sticks it to the tree
|
|
|
|
*/
|
|
|
|
SYSCTL_SETUP(sysctl_proc_setup, "sysctl proc subtree setup")
|
|
|
|
{
|
|
|
|
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT,
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
CTLTYPE_NODE, "proc", NULL,
|
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_PROC, CTL_EOL);
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_ANYNUMBER,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_NODE, "curproc",
|
|
|
|
SYSCTL_DESCR("Per-process settings"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_PROC, PROC_CURPROC, CTL_EOL);
|
|
|
|
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READONLY2|CTLFLAG_ANYWRITE,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_STRING, "corename",
|
|
|
|
SYSCTL_DESCR("Core file name"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_corename, 0, NULL, MAXPATHLEN,
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_CORENAME, CTL_EOL);
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_NODE, "rlimit",
|
|
|
|
SYSCTL_DESCR("Process limits"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
NULL, 0, NULL, 0,
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_LIMIT, CTL_EOL);
|
|
|
|
|
|
|
|
#define create_proc_plimit(s, n) do { \
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL, \
|
|
|
|
CTLFLAG_PERMANENT, \
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_NODE, s, \
|
|
|
|
SYSCTL_DESCR("Process " s " limits"), \
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
NULL, 0, NULL, 0, \
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_LIMIT, n, \
|
|
|
|
CTL_EOL); \
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL, \
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE|CTLFLAG_ANYWRITE, \
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_QUAD, "soft", \
|
|
|
|
SYSCTL_DESCR("Process soft " s " limit"), \
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_plimit, 0, NULL, 0, \
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_LIMIT, n, \
|
|
|
|
PROC_PID_LIMIT_TYPE_SOFT, CTL_EOL); \
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL, \
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE|CTLFLAG_ANYWRITE, \
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_QUAD, "hard", \
|
|
|
|
SYSCTL_DESCR("Process hard " s " limit"), \
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_plimit, 0, NULL, 0, \
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_LIMIT, n, \
|
|
|
|
PROC_PID_LIMIT_TYPE_HARD, CTL_EOL); \
|
|
|
|
} while (0/*CONSTCOND*/)
|
|
|
|
|
|
|
|
create_proc_plimit("cputime", PROC_PID_LIMIT_CPU);
|
|
|
|
create_proc_plimit("filesize", PROC_PID_LIMIT_FSIZE);
|
|
|
|
create_proc_plimit("datasize", PROC_PID_LIMIT_DATA);
|
|
|
|
create_proc_plimit("stacksize", PROC_PID_LIMIT_STACK);
|
|
|
|
create_proc_plimit("coredumpsize", PROC_PID_LIMIT_CORE);
|
|
|
|
create_proc_plimit("memoryuse", PROC_PID_LIMIT_RSS);
|
|
|
|
create_proc_plimit("memorylocked", PROC_PID_LIMIT_MEMLOCK);
|
|
|
|
create_proc_plimit("maxproc", PROC_PID_LIMIT_NPROC);
|
|
|
|
create_proc_plimit("descriptors", PROC_PID_LIMIT_NOFILE);
|
2004-04-17 19:15:29 +04:00
|
|
|
create_proc_plimit("sbsize", PROC_PID_LIMIT_SBSIZE);
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
|
|
|
|
#undef create_proc_plimit
|
|
|
|
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE|CTLFLAG_ANYWRITE,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_INT, "stopfork",
|
|
|
|
SYSCTL_DESCR("Stop process at fork(2)"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_stop, 0, NULL, 0,
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_STOPFORK, CTL_EOL);
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE|CTLFLAG_ANYWRITE,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_INT, "stopexec",
|
|
|
|
SYSCTL_DESCR("Stop process at execve(2)"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_stop, 0, NULL, 0,
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_STOPEXEC, CTL_EOL);
|
2004-03-24 18:34:46 +03:00
|
|
|
sysctl_createv(clog, 0, NULL, NULL,
|
|
|
|
CTLFLAG_PERMANENT|CTLFLAG_READWRITE|CTLFLAG_ANYWRITE,
|
2004-04-08 10:20:29 +04:00
|
|
|
CTLTYPE_INT, "stopexit",
|
|
|
|
SYSCTL_DESCR("Stop process before completing exit"),
|
Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al. Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.
Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded. Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.
All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.
PS - I'm sorry, but there's a distinct lack of documentation at the
moment. I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 22:38:21 +03:00
|
|
|
sysctl_proc_stop, 0, NULL, 0,
|
|
|
|
CTL_PROC, PROC_CURPROC, PROC_PID_STOPEXIT, CTL_EOL);
|
|
|
|
}
|
2004-04-17 19:15:29 +04:00
|
|
|
|
2005-03-20 22:15:48 +03:00
|
|
|
struct uidinfo *
|
|
|
|
uid_find(uid_t uid)
|
2004-04-17 19:15:29 +04:00
|
|
|
{
|
|
|
|
struct uidinfo *uip;
|
|
|
|
struct uihashhead *uipp;
|
|
|
|
|
2005-03-20 22:15:48 +03:00
|
|
|
simple_lock(&uihashtbl_slock);
|
2004-04-17 19:15:29 +04:00
|
|
|
uipp = UIHASH(uid);
|
|
|
|
|
|
|
|
LIST_FOREACH(uip, uipp, ui_hash)
|
2005-03-20 22:15:48 +03:00
|
|
|
if (uip->ui_uid == uid) {
|
|
|
|
simple_unlock(&uihashtbl_slock);
|
2004-04-17 19:15:29 +04:00
|
|
|
return uip;
|
2005-03-20 22:15:48 +03:00
|
|
|
}
|
2004-04-17 19:15:29 +04:00
|
|
|
|
2005-03-20 22:15:48 +03:00
|
|
|
MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_WAITOK|M_ZERO);
|
2004-04-17 19:15:29 +04:00
|
|
|
LIST_INSERT_HEAD(uipp, uip, ui_hash);
|
|
|
|
uip->ui_uid = uid;
|
2005-03-20 22:15:48 +03:00
|
|
|
simple_unlock(&uihashtbl_slock);
|
2004-04-17 19:15:29 +04:00
|
|
|
return uip;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change the count associated with number of processes
|
|
|
|
* a given user is using.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
chgproccnt(uid_t uid, int diff)
|
|
|
|
{
|
|
|
|
struct uidinfo *uip;
|
|
|
|
|
|
|
|
if (diff == 0)
|
|
|
|
return 0;
|
|
|
|
|
2005-03-20 22:15:48 +03:00
|
|
|
uip = uid_find(uid);
|
|
|
|
uip->ui_proccnt += diff;
|
|
|
|
KASSERT(uip->ui_proccnt >= 0);
|
|
|
|
return uip->ui_proccnt;
|
2004-04-17 19:15:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
chgsbsize(uid_t uid, u_long *hiwat, u_long to, rlim_t max)
|
|
|
|
{
|
2004-05-13 21:43:11 +04:00
|
|
|
*hiwat = to;
|
|
|
|
return 1;
|
2004-04-17 19:15:29 +04:00
|
|
|
struct uidinfo *uip;
|
|
|
|
rlim_t nsb;
|
|
|
|
|
2005-03-20 22:15:48 +03:00
|
|
|
uip = uid_find(uid);
|
2004-04-23 06:13:29 +04:00
|
|
|
nsb = uip->ui_sbsize + to - *hiwat;
|
|
|
|
if (to > *hiwat && nsb > max)
|
2005-03-20 22:15:48 +03:00
|
|
|
return 0;
|
2004-04-17 19:15:29 +04:00
|
|
|
*hiwat = to;
|
|
|
|
uip->ui_sbsize = nsb;
|
|
|
|
KASSERT(uip->ui_sbsize >= 0);
|
2005-03-20 22:15:48 +03:00
|
|
|
return 1;
|
2004-04-17 19:15:29 +04:00
|
|
|
}
|