kern_rctl.c revision 227293
1220163Strasz/*- 2220163Strasz * Copyright (c) 2010 The FreeBSD Foundation 3220163Strasz * All rights reserved. 4220163Strasz * 5220163Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6220163Strasz * from the FreeBSD Foundation. 7220163Strasz * 8220163Strasz * Redistribution and use in source and binary forms, with or without 9220163Strasz * modification, are permitted provided that the following conditions 10220163Strasz * are met: 11220163Strasz * 1. Redistributions of source code must retain the above copyright 12220163Strasz * notice, this list of conditions and the following disclaimer. 13220163Strasz * 2. Redistributions in binary form must reproduce the above copyright 14220163Strasz * notice, this list of conditions and the following disclaimer in the 15220163Strasz * documentation and/or other materials provided with the distribution. 16220163Strasz * 17220163Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18220163Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19220163Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20220163Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21220163Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22220163Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23220163Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24220163Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25220163Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26220163Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27220163Strasz * SUCH DAMAGE. 28220163Strasz * 29220163Strasz * $FreeBSD: head/sys/kern/kern_rctl.c 227293 2011-11-07 06:44:47Z ed $ 30220163Strasz */ 31220163Strasz 32220163Strasz#include <sys/cdefs.h> 33220163Strasz__FBSDID("$FreeBSD: head/sys/kern/kern_rctl.c 227293 2011-11-07 06:44:47Z ed $"); 34220163Strasz 35220163Strasz#include <sys/param.h> 36220163Strasz#include <sys/bus.h> 37220163Strasz#include <sys/malloc.h> 38220163Strasz#include <sys/queue.h> 39220163Strasz#include <sys/refcount.h> 40220163Strasz#include <sys/jail.h> 41220163Strasz#include <sys/kernel.h> 42220163Strasz#include <sys/limits.h> 43220163Strasz#include <sys/loginclass.h> 44220163Strasz#include <sys/priv.h> 45220163Strasz#include <sys/proc.h> 46220163Strasz#include <sys/racct.h> 47220163Strasz#include <sys/rctl.h> 48220163Strasz#include <sys/resourcevar.h> 49220163Strasz#include <sys/sx.h> 50220163Strasz#include <sys/sysent.h> 51220163Strasz#include <sys/sysproto.h> 52220163Strasz#include <sys/systm.h> 53220163Strasz#include <sys/types.h> 54220163Strasz#include <sys/eventhandler.h> 55220163Strasz#include <sys/lock.h> 56220163Strasz#include <sys/mutex.h> 57220163Strasz#include <sys/rwlock.h> 58220163Strasz#include <sys/sbuf.h> 59220163Strasz#include <sys/taskqueue.h> 60220163Strasz#include <sys/tree.h> 61220163Strasz#include <vm/uma.h> 62220163Strasz 63220163Strasz#ifdef RCTL 64220163Strasz#ifndef RACCT 65220163Strasz#error "The RCTL option requires the RACCT option" 66220163Strasz#endif 67220163Strasz 68220163StraszFEATURE(rctl, "Resource Limits"); 69220163Strasz 70220163Strasz#define HRF_DEFAULT 0 71220163Strasz#define HRF_DONT_INHERIT 1 72220163Strasz#define HRF_DONT_ACCUMULATE 2 73220163Strasz 74220163Strasz/* Default buffer size for rctl_get_rules(2). */ 75220163Strasz#define RCTL_DEFAULT_BUFSIZE 4096 76220163Strasz#define RCTL_LOG_BUFSIZE 128 77220163Strasz 78220163Strasz/* 79220163Strasz * 'rctl_rule_link' connects a rule with every racct it's related to. 80220163Strasz * For example, rule 'user:X:openfiles:deny=N/process' is linked 81220163Strasz * with uidinfo for user X, and to each process of that user. 82220163Strasz */ 83220163Straszstruct rctl_rule_link { 84220163Strasz LIST_ENTRY(rctl_rule_link) rrl_next; 85220163Strasz struct rctl_rule *rrl_rule; 86220163Strasz int rrl_exceeded; 87220163Strasz}; 88220163Strasz 89220163Straszstruct dict { 90220163Strasz const char *d_name; 91220163Strasz int d_value; 92220163Strasz}; 93220163Strasz 94220163Straszstatic struct dict subjectnames[] = { 95220163Strasz { "process", RCTL_SUBJECT_TYPE_PROCESS }, 96220163Strasz { "user", RCTL_SUBJECT_TYPE_USER }, 97220163Strasz { "loginclass", RCTL_SUBJECT_TYPE_LOGINCLASS }, 98220163Strasz { "jail", RCTL_SUBJECT_TYPE_JAIL }, 99220163Strasz { NULL, -1 }}; 100220163Strasz 101220163Straszstatic struct dict resourcenames[] = { 102224036Strasz { "cputime", RACCT_CPU }, 103224036Strasz { "datasize", RACCT_DATA }, 104224036Strasz { "stacksize", RACCT_STACK }, 105224036Strasz { "coredumpsize", RACCT_CORE }, 106224036Strasz { "memoryuse", RACCT_RSS }, 107224036Strasz { "memorylocked", RACCT_MEMLOCK }, 108224036Strasz { "maxproc", RACCT_NPROC }, 109224036Strasz { "openfiles", RACCT_NOFILE }, 110224036Strasz { "vmemoryuse", RACCT_VMEM }, 111224036Strasz { "pseudoterminals", RACCT_NPTS }, 112224036Strasz { "swapuse", RACCT_SWAP }, 113220163Strasz { "nthr", RACCT_NTHR }, 114220163Strasz { "msgqqueued", RACCT_MSGQQUEUED }, 115220163Strasz { "msgqsize", RACCT_MSGQSIZE }, 116220163Strasz { "nmsgq", RACCT_NMSGQ }, 117220163Strasz { "nsem", RACCT_NSEM }, 118220163Strasz { "nsemop", RACCT_NSEMOP }, 119220163Strasz { "nshm", RACCT_NSHM }, 120220163Strasz { "shmsize", RACCT_SHMSIZE }, 121220163Strasz { "wallclock", RACCT_WALLCLOCK }, 122220163Strasz { NULL, -1 }}; 123220163Strasz 124220163Straszstatic struct dict actionnames[] = { 125220163Strasz { "sighup", RCTL_ACTION_SIGHUP }, 126220163Strasz { "sigint", RCTL_ACTION_SIGINT }, 127220163Strasz { "sigquit", RCTL_ACTION_SIGQUIT }, 128220163Strasz { "sigill", RCTL_ACTION_SIGILL }, 129220163Strasz { "sigtrap", RCTL_ACTION_SIGTRAP }, 130220163Strasz { "sigabrt", RCTL_ACTION_SIGABRT }, 131220163Strasz { "sigemt", RCTL_ACTION_SIGEMT }, 132220163Strasz { "sigfpe", RCTL_ACTION_SIGFPE }, 133220163Strasz { "sigkill", RCTL_ACTION_SIGKILL }, 134220163Strasz { "sigbus", RCTL_ACTION_SIGBUS }, 135220163Strasz { "sigsegv", RCTL_ACTION_SIGSEGV }, 136220163Strasz { "sigsys", RCTL_ACTION_SIGSYS }, 137220163Strasz { "sigpipe", RCTL_ACTION_SIGPIPE }, 138220163Strasz { "sigalrm", RCTL_ACTION_SIGALRM }, 139220163Strasz { "sigterm", RCTL_ACTION_SIGTERM }, 140220163Strasz { "sigurg", RCTL_ACTION_SIGURG }, 141220163Strasz { "sigstop", RCTL_ACTION_SIGSTOP }, 142220163Strasz { "sigtstp", RCTL_ACTION_SIGTSTP }, 143220163Strasz { "sigchld", RCTL_ACTION_SIGCHLD }, 144220163Strasz { "sigttin", RCTL_ACTION_SIGTTIN }, 145220163Strasz { "sigttou", RCTL_ACTION_SIGTTOU }, 146220163Strasz { "sigio", RCTL_ACTION_SIGIO }, 147220163Strasz { "sigxcpu", RCTL_ACTION_SIGXCPU }, 148220163Strasz { "sigxfsz", RCTL_ACTION_SIGXFSZ }, 149220163Strasz { "sigvtalrm", RCTL_ACTION_SIGVTALRM }, 150220163Strasz { "sigprof", RCTL_ACTION_SIGPROF }, 151220163Strasz { "sigwinch", RCTL_ACTION_SIGWINCH }, 152220163Strasz { "siginfo", RCTL_ACTION_SIGINFO }, 153220163Strasz { "sigusr1", RCTL_ACTION_SIGUSR1 }, 154220163Strasz { "sigusr2", RCTL_ACTION_SIGUSR2 }, 155220163Strasz { "sigthr", RCTL_ACTION_SIGTHR }, 156220163Strasz { "deny", RCTL_ACTION_DENY }, 157220163Strasz { "log", RCTL_ACTION_LOG }, 158220163Strasz { "devctl", RCTL_ACTION_DEVCTL }, 159220163Strasz { NULL, -1 }}; 160220163Strasz 161220163Straszstatic void rctl_init(void); 162220163StraszSYSINIT(rctl, SI_SUB_RACCT, SI_ORDER_FIRST, rctl_init, NULL); 163220163Strasz 164220163Straszstatic uma_zone_t rctl_rule_link_zone; 165220163Straszstatic uma_zone_t rctl_rule_zone; 166220163Straszstatic struct rwlock rctl_lock; 167220163StraszRW_SYSINIT(rctl_lock, &rctl_lock, "RCTL lock"); 168220163Strasz 169220163Straszstatic int rctl_rule_fully_specified(const struct rctl_rule *rule); 170220163Straszstatic void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule); 171220163Strasz 172227293Sedstatic MALLOC_DEFINE(M_RCTL, "rctl", "Resource Limits"); 173220163Strasz 174220163Straszstatic const char * 175220163Straszrctl_subject_type_name(int subject) 176220163Strasz{ 177220163Strasz int i; 178220163Strasz 179220163Strasz for (i = 0; subjectnames[i].d_name != NULL; i++) { 180220163Strasz if (subjectnames[i].d_value == subject) 181220163Strasz return (subjectnames[i].d_name); 182220163Strasz } 183220163Strasz 184220163Strasz panic("rctl_subject_type_name: unknown subject type %d", subject); 185220163Strasz} 186220163Strasz 187220163Straszstatic const char * 188220163Straszrctl_action_name(int action) 189220163Strasz{ 190220163Strasz int i; 191220163Strasz 192220163Strasz for (i = 0; actionnames[i].d_name != NULL; i++) { 193220163Strasz if (actionnames[i].d_value == action) 194220163Strasz return (actionnames[i].d_name); 195220163Strasz } 196220163Strasz 197220163Strasz panic("rctl_action_name: unknown action %d", action); 198220163Strasz} 199220163Strasz 200220163Straszconst char * 201220163Straszrctl_resource_name(int resource) 202220163Strasz{ 203220163Strasz int i; 204220163Strasz 205220163Strasz for (i = 0; resourcenames[i].d_name != NULL; i++) { 206220163Strasz if (resourcenames[i].d_value == resource) 207220163Strasz return (resourcenames[i].d_name); 208220163Strasz } 209220163Strasz 210220163Strasz panic("rctl_resource_name: unknown resource %d", resource); 211220163Strasz} 212220163Strasz 213220163Strasz/* 214220163Strasz * Return the amount of resource that can be allocated by 'p' before 215220163Strasz * hitting 'rule'. 216220163Strasz */ 217220163Straszstatic int64_t 218220163Straszrctl_available_resource(const struct proc *p, const struct rctl_rule *rule) 219220163Strasz{ 220220163Strasz int resource; 221220163Strasz int64_t available = INT64_MAX; 222220163Strasz struct ucred *cred = p->p_ucred; 223220163Strasz 224220163Strasz rw_assert(&rctl_lock, RA_LOCKED); 225220163Strasz 226220163Strasz resource = rule->rr_resource; 227220163Strasz switch (rule->rr_per) { 228220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 229220163Strasz available = rule->rr_amount - 230220163Strasz p->p_racct->r_resources[resource]; 231220163Strasz break; 232220163Strasz case RCTL_SUBJECT_TYPE_USER: 233220163Strasz available = rule->rr_amount - 234220163Strasz cred->cr_ruidinfo->ui_racct->r_resources[resource]; 235220163Strasz break; 236220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 237220163Strasz available = rule->rr_amount - 238220163Strasz cred->cr_loginclass->lc_racct->r_resources[resource]; 239220163Strasz break; 240220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 241220163Strasz available = rule->rr_amount - 242221362Strasz cred->cr_prison->pr_prison_racct->prr_racct-> 243221362Strasz r_resources[resource]; 244220163Strasz break; 245220163Strasz default: 246220163Strasz panic("rctl_compute_available: unknown per %d", 247220163Strasz rule->rr_per); 248220163Strasz } 249220163Strasz 250220163Strasz return (available); 251220163Strasz} 252220163Strasz 253220163Strasz/* 254220163Strasz * Return non-zero if allocating 'amount' by proc 'p' would exceed 255220163Strasz * resource limit specified by 'rule'. 256220163Strasz */ 257220163Straszstatic int 258220163Straszrctl_would_exceed(const struct proc *p, const struct rctl_rule *rule, 259220163Strasz int64_t amount) 260220163Strasz{ 261220163Strasz int64_t available; 262220163Strasz 263220163Strasz rw_assert(&rctl_lock, RA_LOCKED); 264220163Strasz 265220163Strasz available = rctl_available_resource(p, rule); 266220163Strasz if (available >= amount) 267220163Strasz return (0); 268220163Strasz 269220163Strasz return (1); 270220163Strasz} 271220163Strasz 272220163Strasz/* 273220163Strasz * Check whether the proc 'p' can allocate 'amount' of 'resource' in addition 274220163Strasz * to what it keeps allocated now. Returns non-zero if the allocation should 275220163Strasz * be denied, 0 otherwise. 276220163Strasz */ 277220163Straszint 278220163Straszrctl_enforce(struct proc *p, int resource, uint64_t amount) 279220163Strasz{ 280220163Strasz struct rctl_rule *rule; 281220163Strasz struct rctl_rule_link *link; 282220163Strasz struct sbuf sb; 283220163Strasz int should_deny = 0; 284220163Strasz char *buf; 285220163Strasz static int curtime = 0; 286220163Strasz static struct timeval lasttime; 287220163Strasz 288220163Strasz rw_rlock(&rctl_lock); 289220163Strasz 290220163Strasz /* 291220163Strasz * There may be more than one matching rule; go through all of them. 292220163Strasz * Denial should be done last, after logging and sending signals. 293220163Strasz */ 294220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 295220163Strasz rule = link->rrl_rule; 296220163Strasz if (rule->rr_resource != resource) 297220163Strasz continue; 298220163Strasz if (!rctl_would_exceed(p, rule, amount)) { 299220163Strasz link->rrl_exceeded = 0; 300220163Strasz continue; 301220163Strasz } 302220163Strasz 303220163Strasz switch (rule->rr_action) { 304220163Strasz case RCTL_ACTION_DENY: 305220163Strasz should_deny = 1; 306220163Strasz continue; 307220163Strasz case RCTL_ACTION_LOG: 308220163Strasz /* 309220163Strasz * If rrl_exceeded != 0, it means we've already 310220163Strasz * logged a warning for this process. 311220163Strasz */ 312220163Strasz if (link->rrl_exceeded != 0) 313220163Strasz continue; 314220163Strasz 315225940Strasz /* 316225940Strasz * If the process state is not fully initialized yet, 317225940Strasz * we can't access most of the required fields, e.g. 318225940Strasz * p->p_comm. This happens when called from fork1(). 319225940Strasz * Ignore this rule for now; it will be processed just 320225940Strasz * after fork, when called from racct_proc_fork_done(). 321225940Strasz */ 322225940Strasz if (p->p_state != PRS_NORMAL) 323225940Strasz continue; 324225940Strasz 325220163Strasz if (!ppsratecheck(&lasttime, &curtime, 10)) 326220163Strasz continue; 327220163Strasz 328220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 329220163Strasz if (buf == NULL) { 330220163Strasz printf("rctl_enforce: out of memory\n"); 331220163Strasz continue; 332220163Strasz } 333220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 334220163Strasz rctl_rule_to_sbuf(&sb, rule); 335220163Strasz sbuf_finish(&sb); 336220163Strasz printf("rctl: rule \"%s\" matched by pid %d " 337220163Strasz "(%s), uid %d, jail %s\n", sbuf_data(&sb), 338220163Strasz p->p_pid, p->p_comm, p->p_ucred->cr_uid, 339221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 340220163Strasz sbuf_delete(&sb); 341220163Strasz free(buf, M_RCTL); 342220163Strasz link->rrl_exceeded = 1; 343220163Strasz continue; 344220163Strasz case RCTL_ACTION_DEVCTL: 345220163Strasz if (link->rrl_exceeded != 0) 346220163Strasz continue; 347220163Strasz 348225940Strasz if (p->p_state != PRS_NORMAL) 349225940Strasz continue; 350225940Strasz 351220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 352220163Strasz if (buf == NULL) { 353220163Strasz printf("rctl_enforce: out of memory\n"); 354220163Strasz continue; 355220163Strasz } 356220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 357220163Strasz sbuf_printf(&sb, "rule="); 358220163Strasz rctl_rule_to_sbuf(&sb, rule); 359220163Strasz sbuf_printf(&sb, " pid=%d ruid=%d jail=%s", 360220163Strasz p->p_pid, p->p_ucred->cr_ruid, 361221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 362220163Strasz sbuf_finish(&sb); 363220163Strasz devctl_notify_f("RCTL", "rule", "matched", 364220163Strasz sbuf_data(&sb), M_NOWAIT); 365220163Strasz sbuf_delete(&sb); 366220163Strasz free(buf, M_RCTL); 367220163Strasz link->rrl_exceeded = 1; 368220163Strasz continue; 369220163Strasz default: 370220163Strasz if (link->rrl_exceeded != 0) 371220163Strasz continue; 372220163Strasz 373225940Strasz if (p->p_state != PRS_NORMAL) 374225940Strasz continue; 375225940Strasz 376220163Strasz KASSERT(rule->rr_action > 0 && 377220163Strasz rule->rr_action <= RCTL_ACTION_SIGNAL_MAX, 378220163Strasz ("rctl_enforce: unknown action %d", 379220163Strasz rule->rr_action)); 380220163Strasz 381220163Strasz /* 382220163Strasz * We're using the fact that RCTL_ACTION_SIG* values 383220163Strasz * are equal to their counterparts from sys/signal.h. 384220163Strasz */ 385225617Skmacy kern_psignal(p, rule->rr_action); 386220163Strasz link->rrl_exceeded = 1; 387220163Strasz continue; 388220163Strasz } 389220163Strasz } 390220163Strasz 391220163Strasz rw_runlock(&rctl_lock); 392220163Strasz 393220163Strasz if (should_deny) { 394220163Strasz /* 395220163Strasz * Return fake error code; the caller should change it 396220163Strasz * into one proper for the situation - EFSIZ, ENOMEM etc. 397220163Strasz */ 398220163Strasz return (EDOOFUS); 399220163Strasz } 400220163Strasz 401220163Strasz return (0); 402220163Strasz} 403220163Strasz 404220163Straszuint64_t 405220163Straszrctl_get_limit(struct proc *p, int resource) 406220163Strasz{ 407220163Strasz struct rctl_rule *rule; 408220163Strasz struct rctl_rule_link *link; 409220163Strasz uint64_t amount = UINT64_MAX; 410220163Strasz 411220163Strasz rw_rlock(&rctl_lock); 412220163Strasz 413220163Strasz /* 414220163Strasz * There may be more than one matching rule; go through all of them. 415220163Strasz * Denial should be done last, after logging and sending signals. 416220163Strasz */ 417220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 418220163Strasz rule = link->rrl_rule; 419220163Strasz if (rule->rr_resource != resource) 420220163Strasz continue; 421220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 422220163Strasz continue; 423220163Strasz if (rule->rr_amount < amount) 424220163Strasz amount = rule->rr_amount; 425220163Strasz } 426220163Strasz 427220163Strasz rw_runlock(&rctl_lock); 428220163Strasz 429220163Strasz return (amount); 430220163Strasz} 431220163Strasz 432220163Straszuint64_t 433220163Straszrctl_get_available(struct proc *p, int resource) 434220163Strasz{ 435220163Strasz struct rctl_rule *rule; 436220163Strasz struct rctl_rule_link *link; 437220163Strasz int64_t available, minavailable, allocated; 438220163Strasz 439220163Strasz minavailable = INT64_MAX; 440220163Strasz 441220163Strasz rw_rlock(&rctl_lock); 442220163Strasz 443220163Strasz /* 444220163Strasz * There may be more than one matching rule; go through all of them. 445220163Strasz * Denial should be done last, after logging and sending signals. 446220163Strasz */ 447220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 448220163Strasz rule = link->rrl_rule; 449220163Strasz if (rule->rr_resource != resource) 450220163Strasz continue; 451220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 452220163Strasz continue; 453220163Strasz available = rctl_available_resource(p, rule); 454220163Strasz if (available < minavailable) 455220163Strasz minavailable = available; 456220163Strasz } 457220163Strasz 458220163Strasz rw_runlock(&rctl_lock); 459220163Strasz 460220163Strasz /* 461220163Strasz * XXX: Think about this _hard_. 462220163Strasz */ 463220163Strasz allocated = p->p_racct->r_resources[resource]; 464220163Strasz if (minavailable < INT64_MAX - allocated) 465220163Strasz minavailable += allocated; 466220163Strasz if (minavailable < 0) 467220163Strasz minavailable = 0; 468220163Strasz return (minavailable); 469220163Strasz} 470220163Strasz 471220163Straszstatic int 472220163Straszrctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter) 473220163Strasz{ 474220163Strasz 475220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) { 476220163Strasz if (rule->rr_subject_type != filter->rr_subject_type) 477220163Strasz return (0); 478220163Strasz 479220163Strasz switch (filter->rr_subject_type) { 480220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 481220163Strasz if (filter->rr_subject.rs_proc != NULL && 482220163Strasz rule->rr_subject.rs_proc != 483220163Strasz filter->rr_subject.rs_proc) 484220163Strasz return (0); 485220163Strasz break; 486220163Strasz case RCTL_SUBJECT_TYPE_USER: 487220163Strasz if (filter->rr_subject.rs_uip != NULL && 488220163Strasz rule->rr_subject.rs_uip != 489220163Strasz filter->rr_subject.rs_uip) 490220163Strasz return (0); 491220163Strasz break; 492220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 493220527Strasz if (filter->rr_subject.rs_loginclass != NULL && 494220527Strasz rule->rr_subject.rs_loginclass != 495220527Strasz filter->rr_subject.rs_loginclass) 496220163Strasz return (0); 497220163Strasz break; 498220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 499221362Strasz if (filter->rr_subject.rs_prison_racct != NULL && 500221362Strasz rule->rr_subject.rs_prison_racct != 501221362Strasz filter->rr_subject.rs_prison_racct) 502220163Strasz return (0); 503220163Strasz break; 504220163Strasz default: 505220163Strasz panic("rctl_rule_matches: unknown subject type %d", 506220163Strasz filter->rr_subject_type); 507220163Strasz } 508220163Strasz } 509220163Strasz 510220163Strasz if (filter->rr_resource != RACCT_UNDEFINED) { 511220163Strasz if (rule->rr_resource != filter->rr_resource) 512220163Strasz return (0); 513220163Strasz } 514220163Strasz 515220163Strasz if (filter->rr_action != RCTL_ACTION_UNDEFINED) { 516220163Strasz if (rule->rr_action != filter->rr_action) 517220163Strasz return (0); 518220163Strasz } 519220163Strasz 520220163Strasz if (filter->rr_amount != RCTL_AMOUNT_UNDEFINED) { 521220163Strasz if (rule->rr_amount != filter->rr_amount) 522220163Strasz return (0); 523220163Strasz } 524220163Strasz 525220163Strasz if (filter->rr_per != RCTL_SUBJECT_TYPE_UNDEFINED) { 526220163Strasz if (rule->rr_per != filter->rr_per) 527220163Strasz return (0); 528220163Strasz } 529220163Strasz 530220163Strasz return (1); 531220163Strasz} 532220163Strasz 533220163Straszstatic int 534220163Straszstr2value(const char *str, int *value, struct dict *table) 535220163Strasz{ 536220163Strasz int i; 537220163Strasz 538220163Strasz if (value == NULL) 539220163Strasz return (EINVAL); 540220163Strasz 541220163Strasz for (i = 0; table[i].d_name != NULL; i++) { 542220163Strasz if (strcasecmp(table[i].d_name, str) == 0) { 543220163Strasz *value = table[i].d_value; 544220163Strasz return (0); 545220163Strasz } 546220163Strasz } 547220163Strasz 548220163Strasz return (EINVAL); 549220163Strasz} 550220163Strasz 551220163Straszstatic int 552220163Straszstr2id(const char *str, id_t *value) 553220163Strasz{ 554220163Strasz char *end; 555220163Strasz 556220163Strasz if (str == NULL) 557220163Strasz return (EINVAL); 558220163Strasz 559220163Strasz *value = strtoul(str, &end, 10); 560220163Strasz if ((size_t)(end - str) != strlen(str)) 561220163Strasz return (EINVAL); 562220163Strasz 563220163Strasz return (0); 564220163Strasz} 565220163Strasz 566220163Straszstatic int 567220163Straszstr2int64(const char *str, int64_t *value) 568220163Strasz{ 569220163Strasz char *end; 570220163Strasz 571220163Strasz if (str == NULL) 572220163Strasz return (EINVAL); 573220163Strasz 574220163Strasz *value = strtoul(str, &end, 10); 575220163Strasz if ((size_t)(end - str) != strlen(str)) 576220163Strasz return (EINVAL); 577220163Strasz 578220163Strasz return (0); 579220163Strasz} 580220163Strasz 581220163Strasz/* 582220163Strasz * Connect the rule to the racct, increasing refcount for the rule. 583220163Strasz */ 584220163Straszstatic void 585220163Straszrctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) 586220163Strasz{ 587220163Strasz struct rctl_rule_link *link; 588220163Strasz 589220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 590220163Strasz 591220163Strasz rctl_rule_acquire(rule); 592220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 593220163Strasz link->rrl_rule = rule; 594220163Strasz link->rrl_exceeded = 0; 595220163Strasz 596220163Strasz rw_wlock(&rctl_lock); 597220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 598220163Strasz rw_wunlock(&rctl_lock); 599220163Strasz} 600220163Strasz 601220163Straszstatic int 602220163Straszrctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) 603220163Strasz{ 604220163Strasz struct rctl_rule_link *link; 605220163Strasz 606220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 607220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 608220163Strasz 609220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); 610220163Strasz if (link == NULL) 611220163Strasz return (ENOMEM); 612220163Strasz rctl_rule_acquire(rule); 613220163Strasz link->rrl_rule = rule; 614220163Strasz link->rrl_exceeded = 0; 615220163Strasz 616220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 617220163Strasz return (0); 618220163Strasz} 619220163Strasz 620220163Strasz/* 621220163Strasz * Remove limits for a rules matching the filter and release 622220163Strasz * the refcounts for the rules, possibly freeing them. Returns 623220163Strasz * the number of limit structures removed. 624220163Strasz */ 625220163Straszstatic int 626220163Straszrctl_racct_remove_rules(struct racct *racct, 627220163Strasz const struct rctl_rule *filter) 628220163Strasz{ 629220163Strasz int removed = 0; 630220163Strasz struct rctl_rule_link *link, *linktmp; 631220163Strasz 632220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 633220163Strasz 634220163Strasz LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { 635220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 636220163Strasz continue; 637220163Strasz 638220163Strasz LIST_REMOVE(link, rrl_next); 639220163Strasz rctl_rule_release(link->rrl_rule); 640220163Strasz uma_zfree(rctl_rule_link_zone, link); 641220163Strasz removed++; 642220163Strasz } 643220163Strasz return (removed); 644220163Strasz} 645220163Strasz 646220163Straszstatic void 647220163Straszrctl_rule_acquire_subject(struct rctl_rule *rule) 648220163Strasz{ 649220163Strasz 650220163Strasz switch (rule->rr_subject_type) { 651220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 652220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 653221362Strasz break; 654220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 655221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 656221362Strasz prison_racct_hold(rule->rr_subject.rs_prison_racct); 657220163Strasz break; 658220163Strasz case RCTL_SUBJECT_TYPE_USER: 659220163Strasz if (rule->rr_subject.rs_uip != NULL) 660220163Strasz uihold(rule->rr_subject.rs_uip); 661220163Strasz break; 662220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 663220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 664220527Strasz loginclass_hold(rule->rr_subject.rs_loginclass); 665220163Strasz break; 666220163Strasz default: 667220163Strasz panic("rctl_rule_acquire_subject: unknown subject type %d", 668220163Strasz rule->rr_subject_type); 669220163Strasz } 670220163Strasz} 671220163Strasz 672220163Straszstatic void 673220163Straszrctl_rule_release_subject(struct rctl_rule *rule) 674220163Strasz{ 675220163Strasz 676220163Strasz switch (rule->rr_subject_type) { 677220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 678220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 679221362Strasz break; 680220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 681221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 682221362Strasz prison_racct_free(rule->rr_subject.rs_prison_racct); 683220163Strasz break; 684220163Strasz case RCTL_SUBJECT_TYPE_USER: 685220163Strasz if (rule->rr_subject.rs_uip != NULL) 686220163Strasz uifree(rule->rr_subject.rs_uip); 687220163Strasz break; 688220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 689220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 690220527Strasz loginclass_free(rule->rr_subject.rs_loginclass); 691220163Strasz break; 692220163Strasz default: 693220163Strasz panic("rctl_rule_release_subject: unknown subject type %d", 694220163Strasz rule->rr_subject_type); 695220163Strasz } 696220163Strasz} 697220163Strasz 698220163Straszstruct rctl_rule * 699220163Straszrctl_rule_alloc(int flags) 700220163Strasz{ 701220163Strasz struct rctl_rule *rule; 702220163Strasz 703220163Strasz rule = uma_zalloc(rctl_rule_zone, flags); 704220163Strasz if (rule == NULL) 705220163Strasz return (NULL); 706220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 707220163Strasz rule->rr_subject.rs_proc = NULL; 708220163Strasz rule->rr_subject.rs_uip = NULL; 709220527Strasz rule->rr_subject.rs_loginclass = NULL; 710221362Strasz rule->rr_subject.rs_prison_racct = NULL; 711220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 712220163Strasz rule->rr_resource = RACCT_UNDEFINED; 713220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 714220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 715220163Strasz refcount_init(&rule->rr_refcount, 1); 716220163Strasz 717220163Strasz return (rule); 718220163Strasz} 719220163Strasz 720220163Straszstruct rctl_rule * 721220163Straszrctl_rule_duplicate(const struct rctl_rule *rule, int flags) 722220163Strasz{ 723220163Strasz struct rctl_rule *copy; 724220163Strasz 725220163Strasz copy = uma_zalloc(rctl_rule_zone, flags); 726220163Strasz if (copy == NULL) 727220163Strasz return (NULL); 728220163Strasz copy->rr_subject_type = rule->rr_subject_type; 729220163Strasz copy->rr_subject.rs_proc = rule->rr_subject.rs_proc; 730220163Strasz copy->rr_subject.rs_uip = rule->rr_subject.rs_uip; 731220527Strasz copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass; 732221362Strasz copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct; 733220163Strasz copy->rr_per = rule->rr_per; 734220163Strasz copy->rr_resource = rule->rr_resource; 735220163Strasz copy->rr_action = rule->rr_action; 736220163Strasz copy->rr_amount = rule->rr_amount; 737220163Strasz refcount_init(©->rr_refcount, 1); 738220163Strasz rctl_rule_acquire_subject(copy); 739220163Strasz 740220163Strasz return (copy); 741220163Strasz} 742220163Strasz 743220163Straszvoid 744220163Straszrctl_rule_acquire(struct rctl_rule *rule) 745220163Strasz{ 746220163Strasz 747220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 748220163Strasz 749220163Strasz refcount_acquire(&rule->rr_refcount); 750220163Strasz} 751220163Strasz 752220163Straszstatic void 753220163Straszrctl_rule_free(void *context, int pending) 754220163Strasz{ 755220163Strasz struct rctl_rule *rule; 756220163Strasz 757220163Strasz rule = (struct rctl_rule *)context; 758220163Strasz 759220163Strasz KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0")); 760220163Strasz 761220163Strasz /* 762220163Strasz * We don't need locking here; rule is guaranteed to be inaccessible. 763220163Strasz */ 764220163Strasz 765220163Strasz rctl_rule_release_subject(rule); 766220163Strasz uma_zfree(rctl_rule_zone, rule); 767220163Strasz} 768220163Strasz 769220163Straszvoid 770220163Straszrctl_rule_release(struct rctl_rule *rule) 771220163Strasz{ 772220163Strasz 773220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 774220163Strasz 775220163Strasz if (refcount_release(&rule->rr_refcount)) { 776220163Strasz /* 777220163Strasz * rctl_rule_release() is often called when iterating 778220163Strasz * over all the uidinfo structures in the system, 779220163Strasz * holding uihashtbl_lock. Since rctl_rule_free() 780220163Strasz * might end up calling uifree(), this would lead 781220163Strasz * to lock recursion. Use taskqueue to avoid this. 782220163Strasz */ 783220163Strasz TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule); 784220163Strasz taskqueue_enqueue(taskqueue_thread, &rule->rr_task); 785220163Strasz } 786220163Strasz} 787220163Strasz 788220163Straszstatic int 789220163Straszrctl_rule_fully_specified(const struct rctl_rule *rule) 790220163Strasz{ 791220163Strasz 792220163Strasz switch (rule->rr_subject_type) { 793220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 794220163Strasz return (0); 795220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 796220163Strasz if (rule->rr_subject.rs_proc == NULL) 797220163Strasz return (0); 798220163Strasz break; 799220163Strasz case RCTL_SUBJECT_TYPE_USER: 800220163Strasz if (rule->rr_subject.rs_uip == NULL) 801220163Strasz return (0); 802220163Strasz break; 803220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 804220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 805220163Strasz return (0); 806220163Strasz break; 807220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 808221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 809220163Strasz return (0); 810220163Strasz break; 811220163Strasz default: 812220163Strasz panic("rctl_rule_fully_specified: unknown subject type %d", 813220163Strasz rule->rr_subject_type); 814220163Strasz } 815220163Strasz if (rule->rr_resource == RACCT_UNDEFINED) 816220163Strasz return (0); 817220163Strasz if (rule->rr_action == RCTL_ACTION_UNDEFINED) 818220163Strasz return (0); 819220163Strasz if (rule->rr_amount == RCTL_AMOUNT_UNDEFINED) 820220163Strasz return (0); 821220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED) 822220163Strasz return (0); 823220163Strasz 824220163Strasz return (1); 825220163Strasz} 826220163Strasz 827220163Straszstatic int 828220163Straszrctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) 829220163Strasz{ 830220163Strasz int error = 0; 831220163Strasz char *subjectstr, *subject_idstr, *resourcestr, *actionstr, 832220163Strasz *amountstr, *perstr; 833220163Strasz struct rctl_rule *rule; 834220163Strasz id_t id; 835220163Strasz 836220163Strasz rule = rctl_rule_alloc(M_WAITOK); 837220163Strasz 838220163Strasz subjectstr = strsep(&rulestr, ":"); 839220163Strasz subject_idstr = strsep(&rulestr, ":"); 840220163Strasz resourcestr = strsep(&rulestr, ":"); 841220163Strasz actionstr = strsep(&rulestr, "=/"); 842220163Strasz amountstr = strsep(&rulestr, "/"); 843220163Strasz perstr = rulestr; 844220163Strasz 845220163Strasz if (subjectstr == NULL || subjectstr[0] == '\0') 846220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 847220163Strasz else { 848220163Strasz error = str2value(subjectstr, &rule->rr_subject_type, subjectnames); 849220163Strasz if (error != 0) 850220163Strasz goto out; 851220163Strasz } 852220163Strasz 853220163Strasz if (subject_idstr == NULL || subject_idstr[0] == '\0') { 854220163Strasz rule->rr_subject.rs_proc = NULL; 855220163Strasz rule->rr_subject.rs_uip = NULL; 856220527Strasz rule->rr_subject.rs_loginclass = NULL; 857221362Strasz rule->rr_subject.rs_prison_racct = NULL; 858220163Strasz } else { 859220163Strasz switch (rule->rr_subject_type) { 860220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 861220163Strasz error = EINVAL; 862220163Strasz goto out; 863220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 864220163Strasz error = str2id(subject_idstr, &id); 865220163Strasz if (error != 0) 866220163Strasz goto out; 867220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 868220163Strasz rule->rr_subject.rs_proc = pfind(id); 869220163Strasz if (rule->rr_subject.rs_proc == NULL) { 870220163Strasz error = ESRCH; 871220163Strasz goto out; 872220163Strasz } 873220163Strasz PROC_UNLOCK(rule->rr_subject.rs_proc); 874220163Strasz break; 875220163Strasz case RCTL_SUBJECT_TYPE_USER: 876220163Strasz error = str2id(subject_idstr, &id); 877220163Strasz if (error != 0) 878220163Strasz goto out; 879220163Strasz rule->rr_subject.rs_uip = uifind(id); 880220163Strasz break; 881220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 882220527Strasz rule->rr_subject.rs_loginclass = 883220163Strasz loginclass_find(subject_idstr); 884220527Strasz if (rule->rr_subject.rs_loginclass == NULL) { 885220163Strasz error = ENAMETOOLONG; 886220163Strasz goto out; 887220163Strasz } 888220163Strasz break; 889220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 890221362Strasz rule->rr_subject.rs_prison_racct = 891221362Strasz prison_racct_find(subject_idstr); 892221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) { 893221362Strasz error = ENAMETOOLONG; 894221362Strasz goto out; 895220163Strasz } 896220163Strasz break; 897220163Strasz default: 898220163Strasz panic("rctl_string_to_rule: unknown subject type %d", 899220163Strasz rule->rr_subject_type); 900220163Strasz } 901220163Strasz } 902220163Strasz 903220163Strasz if (resourcestr == NULL || resourcestr[0] == '\0') 904220163Strasz rule->rr_resource = RACCT_UNDEFINED; 905220163Strasz else { 906220163Strasz error = str2value(resourcestr, &rule->rr_resource, 907220163Strasz resourcenames); 908220163Strasz if (error != 0) 909220163Strasz goto out; 910220163Strasz } 911220163Strasz 912220163Strasz if (actionstr == NULL || actionstr[0] == '\0') 913220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 914220163Strasz else { 915220163Strasz error = str2value(actionstr, &rule->rr_action, actionnames); 916220163Strasz if (error != 0) 917220163Strasz goto out; 918220163Strasz } 919220163Strasz 920220163Strasz if (amountstr == NULL || amountstr[0] == '\0') 921220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 922220163Strasz else { 923220163Strasz error = str2int64(amountstr, &rule->rr_amount); 924220163Strasz if (error != 0) 925220163Strasz goto out; 926224036Strasz if (RACCT_IS_IN_MILLIONS(rule->rr_resource)) 927225371Strasz rule->rr_amount *= 1000000; 928220163Strasz } 929220163Strasz 930220163Strasz if (perstr == NULL || perstr[0] == '\0') 931220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 932220163Strasz else { 933220163Strasz error = str2value(perstr, &rule->rr_per, subjectnames); 934220163Strasz if (error != 0) 935220163Strasz goto out; 936220163Strasz } 937220163Strasz 938220163Straszout: 939220163Strasz if (error == 0) 940220163Strasz *rulep = rule; 941220163Strasz else 942220163Strasz rctl_rule_release(rule); 943220163Strasz 944220163Strasz return (error); 945220163Strasz} 946220163Strasz 947220163Strasz/* 948220163Strasz * Link a rule with all the subjects it applies to. 949220163Strasz */ 950220163Straszint 951220163Straszrctl_rule_add(struct rctl_rule *rule) 952220163Strasz{ 953220163Strasz struct proc *p; 954220163Strasz struct ucred *cred; 955220163Strasz struct uidinfo *uip; 956220163Strasz struct prison *pr; 957221362Strasz struct prison_racct *prr; 958220163Strasz struct loginclass *lc; 959220163Strasz struct rctl_rule *rule2; 960220163Strasz int match; 961220163Strasz 962220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 963220163Strasz 964220163Strasz /* 965220163Strasz * Some rules just don't make sense. Note that the one below 966223844Strasz * cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU, 967220163Strasz * for example, is not deniable in the racct sense, but the 968220163Strasz * limit is enforced in a different way, so "deny" rules for %CPU 969220163Strasz * do make sense. 970220163Strasz */ 971220163Strasz if (rule->rr_action == RCTL_ACTION_DENY && 972220163Strasz (rule->rr_resource == RACCT_CPU || 973220163Strasz rule->rr_resource == RACCT_WALLCLOCK)) 974220163Strasz return (EOPNOTSUPP); 975220163Strasz 976220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_PROCESS && 977223844Strasz RACCT_IS_SLOPPY(rule->rr_resource)) 978220163Strasz return (EOPNOTSUPP); 979220163Strasz 980220163Strasz /* 981220163Strasz * Make sure there are no duplicated rules. Also, for the "deny" 982220163Strasz * rules, remove ones differing only by "amount". 983220163Strasz */ 984220163Strasz if (rule->rr_action == RCTL_ACTION_DENY) { 985220163Strasz rule2 = rctl_rule_duplicate(rule, M_WAITOK); 986220163Strasz rule2->rr_amount = RCTL_AMOUNT_UNDEFINED; 987220163Strasz rctl_rule_remove(rule2); 988220163Strasz rctl_rule_release(rule2); 989220163Strasz } else 990220163Strasz rctl_rule_remove(rule); 991220163Strasz 992220163Strasz switch (rule->rr_subject_type) { 993220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 994220163Strasz p = rule->rr_subject.rs_proc; 995220163Strasz KASSERT(p != NULL, ("rctl_rule_add: NULL proc")); 996220163Strasz /* 997220163Strasz * No resource limits for system processes. 998220163Strasz */ 999220163Strasz if (p->p_flag & P_SYSTEM) 1000220163Strasz return (EPERM); 1001220163Strasz 1002220163Strasz rctl_racct_add_rule(p->p_racct, rule); 1003220163Strasz /* 1004220163Strasz * In case of per-process rule, we don't have anything more 1005220163Strasz * to do. 1006220163Strasz */ 1007220163Strasz return (0); 1008220163Strasz 1009220163Strasz case RCTL_SUBJECT_TYPE_USER: 1010220163Strasz uip = rule->rr_subject.rs_uip; 1011220163Strasz KASSERT(uip != NULL, ("rctl_rule_add: NULL uip")); 1012220163Strasz rctl_racct_add_rule(uip->ui_racct, rule); 1013220163Strasz break; 1014220163Strasz 1015220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1016220527Strasz lc = rule->rr_subject.rs_loginclass; 1017220163Strasz KASSERT(lc != NULL, ("rctl_rule_add: NULL loginclass")); 1018220163Strasz rctl_racct_add_rule(lc->lc_racct, rule); 1019220163Strasz break; 1020220163Strasz 1021220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1022221362Strasz prr = rule->rr_subject.rs_prison_racct; 1023221362Strasz KASSERT(prr != NULL, ("rctl_rule_add: NULL pr")); 1024221362Strasz rctl_racct_add_rule(prr->prr_racct, rule); 1025220163Strasz break; 1026220163Strasz 1027220163Strasz default: 1028220163Strasz panic("rctl_rule_add: unknown subject type %d", 1029220163Strasz rule->rr_subject_type); 1030220163Strasz } 1031220163Strasz 1032220163Strasz /* 1033220163Strasz * Now go through all the processes and add the new rule to the ones 1034220163Strasz * it applies to. 1035220163Strasz */ 1036220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1037220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1038220163Strasz if (p->p_flag & P_SYSTEM) 1039220163Strasz continue; 1040220163Strasz cred = p->p_ucred; 1041220163Strasz switch (rule->rr_subject_type) { 1042220163Strasz case RCTL_SUBJECT_TYPE_USER: 1043220163Strasz if (cred->cr_uidinfo == rule->rr_subject.rs_uip || 1044220163Strasz cred->cr_ruidinfo == rule->rr_subject.rs_uip) 1045220163Strasz break; 1046220163Strasz continue; 1047220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1048220527Strasz if (cred->cr_loginclass == rule->rr_subject.rs_loginclass) 1049220163Strasz break; 1050220163Strasz continue; 1051220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1052220163Strasz match = 0; 1053220163Strasz for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) { 1054221362Strasz if (pr->pr_prison_racct == rule->rr_subject.rs_prison_racct) { 1055220163Strasz match = 1; 1056220163Strasz break; 1057220163Strasz } 1058220163Strasz } 1059220163Strasz if (match) 1060220163Strasz break; 1061220163Strasz continue; 1062220163Strasz default: 1063220163Strasz panic("rctl_rule_add: unknown subject type %d", 1064220163Strasz rule->rr_subject_type); 1065220163Strasz } 1066220163Strasz 1067220163Strasz rctl_racct_add_rule(p->p_racct, rule); 1068220163Strasz } 1069220163Strasz 1070220163Strasz return (0); 1071220163Strasz} 1072220163Strasz 1073220163Straszstatic void 1074220163Straszrctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) 1075220163Strasz{ 1076220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1077220163Strasz int found = 0; 1078220163Strasz 1079220163Strasz rw_wlock(&rctl_lock); 1080220163Strasz found += rctl_racct_remove_rules(racct, filter); 1081220163Strasz rw_wunlock(&rctl_lock); 1082220163Strasz 1083220163Strasz *((int *)arg3) += found; 1084220163Strasz} 1085220163Strasz 1086220163Strasz/* 1087220163Strasz * Remove all rules that match the filter. 1088220163Strasz */ 1089220163Straszint 1090220163Straszrctl_rule_remove(struct rctl_rule *filter) 1091220163Strasz{ 1092220163Strasz int found = 0; 1093220163Strasz struct proc *p; 1094220163Strasz 1095220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && 1096220163Strasz filter->rr_subject.rs_proc != NULL) { 1097220163Strasz p = filter->rr_subject.rs_proc; 1098220163Strasz rw_wlock(&rctl_lock); 1099220163Strasz found = rctl_racct_remove_rules(p->p_racct, filter); 1100220163Strasz rw_wunlock(&rctl_lock); 1101220163Strasz if (found) 1102220163Strasz return (0); 1103220163Strasz return (ESRCH); 1104220163Strasz } 1105220163Strasz 1106220163Strasz loginclass_racct_foreach(rctl_rule_remove_callback, filter, 1107220163Strasz (void *)&found); 1108220163Strasz ui_racct_foreach(rctl_rule_remove_callback, filter, 1109220163Strasz (void *)&found); 1110220163Strasz prison_racct_foreach(rctl_rule_remove_callback, filter, 1111220163Strasz (void *)&found); 1112220163Strasz 1113220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1114220163Strasz rw_wlock(&rctl_lock); 1115220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1116220163Strasz found += rctl_racct_remove_rules(p->p_racct, filter); 1117220163Strasz } 1118220163Strasz rw_wunlock(&rctl_lock); 1119220163Strasz 1120220163Strasz if (found) 1121220163Strasz return (0); 1122220163Strasz return (ESRCH); 1123220163Strasz} 1124220163Strasz 1125220163Strasz/* 1126220163Strasz * Appends a rule to the sbuf. 1127220163Strasz */ 1128220163Straszstatic void 1129220163Straszrctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule) 1130220163Strasz{ 1131220163Strasz int64_t amount; 1132220163Strasz 1133220163Strasz sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type)); 1134220163Strasz 1135220163Strasz switch (rule->rr_subject_type) { 1136220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1137220163Strasz if (rule->rr_subject.rs_proc == NULL) 1138220163Strasz sbuf_printf(sb, ":"); 1139220163Strasz else 1140220163Strasz sbuf_printf(sb, "%d:", 1141220163Strasz rule->rr_subject.rs_proc->p_pid); 1142220163Strasz break; 1143220163Strasz case RCTL_SUBJECT_TYPE_USER: 1144220163Strasz if (rule->rr_subject.rs_uip == NULL) 1145220163Strasz sbuf_printf(sb, ":"); 1146220163Strasz else 1147220163Strasz sbuf_printf(sb, "%d:", 1148220163Strasz rule->rr_subject.rs_uip->ui_uid); 1149220163Strasz break; 1150220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1151220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 1152220163Strasz sbuf_printf(sb, ":"); 1153220163Strasz else 1154220163Strasz sbuf_printf(sb, "%s:", 1155220527Strasz rule->rr_subject.rs_loginclass->lc_name); 1156220163Strasz break; 1157220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1158221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 1159220163Strasz sbuf_printf(sb, ":"); 1160220163Strasz else 1161220163Strasz sbuf_printf(sb, "%s:", 1162221362Strasz rule->rr_subject.rs_prison_racct->prr_name); 1163220163Strasz break; 1164220163Strasz default: 1165220163Strasz panic("rctl_rule_to_sbuf: unknown subject type %d", 1166220163Strasz rule->rr_subject_type); 1167220163Strasz } 1168220163Strasz 1169220163Strasz amount = rule->rr_amount; 1170220163Strasz if (amount != RCTL_AMOUNT_UNDEFINED && 1171224036Strasz RACCT_IS_IN_MILLIONS(rule->rr_resource)) 1172224036Strasz amount /= 1000000; 1173220163Strasz 1174220163Strasz sbuf_printf(sb, "%s:%s=%jd", 1175220163Strasz rctl_resource_name(rule->rr_resource), 1176220163Strasz rctl_action_name(rule->rr_action), 1177220163Strasz amount); 1178220163Strasz 1179220163Strasz if (rule->rr_per != rule->rr_subject_type) 1180220163Strasz sbuf_printf(sb, "/%s", rctl_subject_type_name(rule->rr_per)); 1181220163Strasz} 1182220163Strasz 1183220163Strasz/* 1184220163Strasz * Routine used by RCTL syscalls to read in input string. 1185220163Strasz */ 1186220163Straszstatic int 1187220163Straszrctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen) 1188220163Strasz{ 1189220163Strasz int error; 1190220163Strasz char *str; 1191220163Strasz 1192220163Strasz if (inbuflen <= 0) 1193220163Strasz return (EINVAL); 1194220163Strasz 1195220163Strasz str = malloc(inbuflen + 1, M_RCTL, M_WAITOK); 1196220163Strasz error = copyinstr(inbufp, str, inbuflen, NULL); 1197220163Strasz if (error != 0) { 1198220163Strasz free(str, M_RCTL); 1199220163Strasz return (error); 1200220163Strasz } 1201220163Strasz 1202220163Strasz *inputstr = str; 1203220163Strasz 1204220163Strasz return (0); 1205220163Strasz} 1206220163Strasz 1207220163Strasz/* 1208220163Strasz * Routine used by RCTL syscalls to write out output string. 1209220163Strasz */ 1210220163Straszstatic int 1211220163Straszrctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen) 1212220163Strasz{ 1213220163Strasz int error; 1214220163Strasz 1215220163Strasz if (outputsbuf == NULL) 1216220163Strasz return (0); 1217220163Strasz 1218220163Strasz sbuf_finish(outputsbuf); 1219220163Strasz if (outbuflen < sbuf_len(outputsbuf) + 1) { 1220220163Strasz sbuf_delete(outputsbuf); 1221220163Strasz return (ERANGE); 1222220163Strasz } 1223220163Strasz error = copyout(sbuf_data(outputsbuf), outbufp, 1224220163Strasz sbuf_len(outputsbuf) + 1); 1225220163Strasz sbuf_delete(outputsbuf); 1226220163Strasz return (error); 1227220163Strasz} 1228220163Strasz 1229220163Straszstatic struct sbuf * 1230220163Straszrctl_racct_to_sbuf(struct racct *racct, int sloppy) 1231220163Strasz{ 1232220163Strasz int i; 1233220163Strasz int64_t amount; 1234220163Strasz struct sbuf *sb; 1235220163Strasz 1236220163Strasz sb = sbuf_new_auto(); 1237220163Strasz for (i = 0; i <= RACCT_MAX; i++) { 1238223844Strasz if (sloppy == 0 && RACCT_IS_SLOPPY(i)) 1239220163Strasz continue; 1240220163Strasz amount = racct->r_resources[i]; 1241224036Strasz if (RACCT_IS_IN_MILLIONS(i)) 1242225371Strasz amount /= 1000000; 1243220163Strasz sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount); 1244220163Strasz } 1245220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1246220163Strasz return (sb); 1247220163Strasz} 1248220163Strasz 1249220163Straszint 1250225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1251220163Strasz{ 1252220163Strasz int error; 1253220163Strasz char *inputstr; 1254220163Strasz struct rctl_rule *filter; 1255220163Strasz struct sbuf *outputsbuf = NULL; 1256220163Strasz struct proc *p; 1257220163Strasz struct uidinfo *uip; 1258220163Strasz struct loginclass *lc; 1259221362Strasz struct prison_racct *prr; 1260220163Strasz 1261220527Strasz error = priv_check(td, PRIV_RCTL_GET_RACCT); 1262220163Strasz if (error != 0) 1263220163Strasz return (error); 1264220163Strasz 1265220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1266220163Strasz if (error != 0) 1267220163Strasz return (error); 1268220163Strasz 1269220163Strasz sx_slock(&allproc_lock); 1270220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1271220163Strasz free(inputstr, M_RCTL); 1272220163Strasz if (error != 0) { 1273220163Strasz sx_sunlock(&allproc_lock); 1274220163Strasz return (error); 1275220163Strasz } 1276220163Strasz 1277220163Strasz switch (filter->rr_subject_type) { 1278220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1279220163Strasz p = filter->rr_subject.rs_proc; 1280220163Strasz if (p == NULL) { 1281220163Strasz error = EINVAL; 1282220163Strasz goto out; 1283220163Strasz } 1284220163Strasz if (p->p_flag & P_SYSTEM) { 1285220163Strasz error = EINVAL; 1286220163Strasz goto out; 1287220163Strasz } 1288220163Strasz outputsbuf = rctl_racct_to_sbuf(p->p_racct, 0); 1289220163Strasz break; 1290220163Strasz case RCTL_SUBJECT_TYPE_USER: 1291220163Strasz uip = filter->rr_subject.rs_uip; 1292220163Strasz if (uip == NULL) { 1293220163Strasz error = EINVAL; 1294220163Strasz goto out; 1295220163Strasz } 1296220163Strasz outputsbuf = rctl_racct_to_sbuf(uip->ui_racct, 1); 1297220163Strasz break; 1298220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1299220527Strasz lc = filter->rr_subject.rs_loginclass; 1300220163Strasz if (lc == NULL) { 1301220163Strasz error = EINVAL; 1302220163Strasz goto out; 1303220163Strasz } 1304220163Strasz outputsbuf = rctl_racct_to_sbuf(lc->lc_racct, 1); 1305220163Strasz break; 1306220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1307221362Strasz prr = filter->rr_subject.rs_prison_racct; 1308221362Strasz if (prr == NULL) { 1309220163Strasz error = EINVAL; 1310220163Strasz goto out; 1311220163Strasz } 1312221362Strasz outputsbuf = rctl_racct_to_sbuf(prr->prr_racct, 1); 1313220163Strasz break; 1314220163Strasz default: 1315220163Strasz error = EINVAL; 1316220163Strasz } 1317220163Straszout: 1318220163Strasz rctl_rule_release(filter); 1319220163Strasz sx_sunlock(&allproc_lock); 1320220163Strasz if (error != 0) 1321220163Strasz return (error); 1322220163Strasz 1323220163Strasz error = rctl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); 1324220163Strasz 1325220163Strasz return (error); 1326220163Strasz} 1327220163Strasz 1328220163Straszstatic void 1329220163Straszrctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) 1330220163Strasz{ 1331220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1332220163Strasz struct rctl_rule_link *link; 1333220163Strasz struct sbuf *sb = (struct sbuf *)arg3; 1334220163Strasz 1335220163Strasz rw_rlock(&rctl_lock); 1336220163Strasz LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { 1337220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1338220163Strasz continue; 1339220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1340220163Strasz sbuf_printf(sb, ","); 1341220163Strasz } 1342220163Strasz rw_runlock(&rctl_lock); 1343220163Strasz} 1344220163Strasz 1345220163Straszint 1346225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1347220163Strasz{ 1348220163Strasz int error; 1349220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1350220163Strasz char *inputstr, *buf; 1351220163Strasz struct sbuf *sb; 1352220163Strasz struct rctl_rule *filter; 1353220163Strasz struct rctl_rule_link *link; 1354220163Strasz struct proc *p; 1355220163Strasz 1356220163Strasz error = priv_check(td, PRIV_RCTL_GET_RULES); 1357220163Strasz if (error != 0) 1358220163Strasz return (error); 1359220163Strasz 1360220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1361220163Strasz if (error != 0) 1362220163Strasz return (error); 1363220163Strasz 1364220163Strasz sx_slock(&allproc_lock); 1365220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1366220163Strasz free(inputstr, M_RCTL); 1367220163Strasz if (error != 0) { 1368220163Strasz sx_sunlock(&allproc_lock); 1369220163Strasz return (error); 1370220163Strasz } 1371220163Strasz 1372220163Straszagain: 1373220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1374220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1375220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1376220163Strasz 1377220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1378220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1379220163Strasz rw_rlock(&rctl_lock); 1380220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1381220163Strasz /* 1382220163Strasz * Non-process rules will be added to the buffer later. 1383220163Strasz * Adding them here would result in duplicated output. 1384220163Strasz */ 1385220163Strasz if (link->rrl_rule->rr_subject_type != 1386220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1387220163Strasz continue; 1388220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1389220163Strasz continue; 1390220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1391220163Strasz sbuf_printf(sb, ","); 1392220163Strasz } 1393220163Strasz rw_runlock(&rctl_lock); 1394220163Strasz } 1395220163Strasz 1396220163Strasz loginclass_racct_foreach(rctl_get_rules_callback, filter, sb); 1397220163Strasz ui_racct_foreach(rctl_get_rules_callback, filter, sb); 1398220163Strasz prison_racct_foreach(rctl_get_rules_callback, filter, sb); 1399220163Strasz if (sbuf_error(sb) == ENOMEM) { 1400220163Strasz sbuf_delete(sb); 1401220163Strasz free(buf, M_RCTL); 1402220163Strasz bufsize *= 4; 1403220163Strasz goto again; 1404220163Strasz } 1405220163Strasz 1406220163Strasz /* 1407220163Strasz * Remove trailing ",". 1408220163Strasz */ 1409220163Strasz if (sbuf_len(sb) > 0) 1410220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1411220163Strasz 1412220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1413220163Strasz 1414220163Strasz rctl_rule_release(filter); 1415220163Strasz sx_sunlock(&allproc_lock); 1416220163Strasz free(buf, M_RCTL); 1417220163Strasz return (error); 1418220163Strasz} 1419220163Strasz 1420220163Straszint 1421225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1422220163Strasz{ 1423220163Strasz int error; 1424220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1425220163Strasz char *inputstr, *buf; 1426220163Strasz struct sbuf *sb; 1427220163Strasz struct rctl_rule *filter; 1428220163Strasz struct rctl_rule_link *link; 1429220163Strasz 1430220163Strasz error = priv_check(td, PRIV_RCTL_GET_LIMITS); 1431220163Strasz if (error != 0) 1432220163Strasz return (error); 1433220163Strasz 1434220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1435220163Strasz if (error != 0) 1436220163Strasz return (error); 1437220163Strasz 1438220163Strasz sx_slock(&allproc_lock); 1439220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1440220163Strasz free(inputstr, M_RCTL); 1441220163Strasz if (error != 0) { 1442220163Strasz sx_sunlock(&allproc_lock); 1443220163Strasz return (error); 1444220163Strasz } 1445220163Strasz 1446220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_UNDEFINED) { 1447220163Strasz rctl_rule_release(filter); 1448220163Strasz sx_sunlock(&allproc_lock); 1449220163Strasz return (EINVAL); 1450220163Strasz } 1451220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_PROCESS) { 1452220163Strasz rctl_rule_release(filter); 1453220163Strasz sx_sunlock(&allproc_lock); 1454220163Strasz return (EOPNOTSUPP); 1455220163Strasz } 1456220163Strasz if (filter->rr_subject.rs_proc == NULL) { 1457220163Strasz rctl_rule_release(filter); 1458220163Strasz sx_sunlock(&allproc_lock); 1459220163Strasz return (EINVAL); 1460220163Strasz } 1461220163Strasz 1462220163Straszagain: 1463220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1464220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1465220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1466220163Strasz 1467220163Strasz rw_rlock(&rctl_lock); 1468220163Strasz LIST_FOREACH(link, &filter->rr_subject.rs_proc->p_racct->r_rule_links, 1469220163Strasz rrl_next) { 1470220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1471220163Strasz sbuf_printf(sb, ","); 1472220163Strasz } 1473220163Strasz rw_runlock(&rctl_lock); 1474220163Strasz if (sbuf_error(sb) == ENOMEM) { 1475220163Strasz sbuf_delete(sb); 1476220163Strasz free(buf, M_RCTL); 1477220163Strasz bufsize *= 4; 1478220163Strasz goto again; 1479220163Strasz } 1480220163Strasz 1481220163Strasz /* 1482220163Strasz * Remove trailing ",". 1483220163Strasz */ 1484220163Strasz if (sbuf_len(sb) > 0) 1485220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1486220163Strasz 1487220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1488220163Strasz rctl_rule_release(filter); 1489220163Strasz sx_sunlock(&allproc_lock); 1490220163Strasz free(buf, M_RCTL); 1491220163Strasz return (error); 1492220163Strasz} 1493220163Strasz 1494220163Straszint 1495225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1496220163Strasz{ 1497220163Strasz int error; 1498220163Strasz struct rctl_rule *rule; 1499220163Strasz char *inputstr; 1500220163Strasz 1501220163Strasz error = priv_check(td, PRIV_RCTL_ADD_RULE); 1502220163Strasz if (error != 0) 1503220163Strasz return (error); 1504220163Strasz 1505220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1506220163Strasz if (error != 0) 1507220163Strasz return (error); 1508220163Strasz 1509220163Strasz sx_slock(&allproc_lock); 1510220163Strasz error = rctl_string_to_rule(inputstr, &rule); 1511220163Strasz free(inputstr, M_RCTL); 1512220163Strasz if (error != 0) { 1513220163Strasz sx_sunlock(&allproc_lock); 1514220163Strasz return (error); 1515220163Strasz } 1516220163Strasz /* 1517220163Strasz * The 'per' part of a rule is optional. 1518220163Strasz */ 1519220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED && 1520220163Strasz rule->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) 1521220163Strasz rule->rr_per = rule->rr_subject_type; 1522220163Strasz 1523220163Strasz if (!rctl_rule_fully_specified(rule)) { 1524220163Strasz error = EINVAL; 1525220163Strasz goto out; 1526220163Strasz } 1527220163Strasz 1528220163Strasz error = rctl_rule_add(rule); 1529220163Strasz 1530220163Straszout: 1531220163Strasz rctl_rule_release(rule); 1532220163Strasz sx_sunlock(&allproc_lock); 1533220163Strasz return (error); 1534220163Strasz} 1535220163Strasz 1536220163Straszint 1537225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1538220163Strasz{ 1539220163Strasz int error; 1540220163Strasz struct rctl_rule *filter; 1541220163Strasz char *inputstr; 1542220163Strasz 1543220163Strasz error = priv_check(td, PRIV_RCTL_REMOVE_RULE); 1544220163Strasz if (error != 0) 1545220163Strasz return (error); 1546220163Strasz 1547220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1548220163Strasz if (error != 0) 1549220163Strasz return (error); 1550220163Strasz 1551220163Strasz sx_slock(&allproc_lock); 1552220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1553220163Strasz free(inputstr, M_RCTL); 1554220163Strasz if (error != 0) { 1555220163Strasz sx_sunlock(&allproc_lock); 1556220163Strasz return (error); 1557220163Strasz } 1558220163Strasz 1559220163Strasz error = rctl_rule_remove(filter); 1560220163Strasz rctl_rule_release(filter); 1561220163Strasz sx_sunlock(&allproc_lock); 1562220163Strasz 1563220163Strasz return (error); 1564220163Strasz} 1565220163Strasz 1566220163Strasz/* 1567220163Strasz * Update RCTL rule list after credential change. 1568220163Strasz */ 1569220163Straszvoid 1570220163Straszrctl_proc_ucred_changed(struct proc *p, struct ucred *newcred) 1571220163Strasz{ 1572220163Strasz int rulecnt, i; 1573220163Strasz struct rctl_rule_link *link, *newlink; 1574220163Strasz struct uidinfo *newuip; 1575220163Strasz struct loginclass *newlc; 1576221362Strasz struct prison_racct *newprr; 1577220163Strasz LIST_HEAD(, rctl_rule_link) newrules; 1578220163Strasz 1579220163Strasz newuip = newcred->cr_ruidinfo; 1580220163Strasz newlc = newcred->cr_loginclass; 1581221362Strasz newprr = newcred->cr_prison->pr_prison_racct; 1582220163Strasz 1583220163Strasz LIST_INIT(&newrules); 1584220163Strasz 1585220163Straszagain: 1586220163Strasz /* 1587220163Strasz * First, count the rules that apply to the process with new 1588220163Strasz * credentials. 1589220163Strasz */ 1590220163Strasz rulecnt = 0; 1591220163Strasz rw_rlock(&rctl_lock); 1592220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1593220163Strasz if (link->rrl_rule->rr_subject_type == 1594220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1595220163Strasz rulecnt++; 1596220163Strasz } 1597220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) 1598220163Strasz rulecnt++; 1599220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) 1600220163Strasz rulecnt++; 1601221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) 1602220163Strasz rulecnt++; 1603220163Strasz rw_runlock(&rctl_lock); 1604220163Strasz 1605220163Strasz /* 1606220163Strasz * Create temporary list. We've dropped the rctl_lock in order 1607220163Strasz * to use M_WAITOK. 1608220163Strasz */ 1609220163Strasz for (i = 0; i < rulecnt; i++) { 1610220163Strasz newlink = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 1611220163Strasz newlink->rrl_rule = NULL; 1612220163Strasz LIST_INSERT_HEAD(&newrules, newlink, rrl_next); 1613220163Strasz } 1614220163Strasz 1615220163Strasz newlink = LIST_FIRST(&newrules); 1616220163Strasz 1617220163Strasz /* 1618220163Strasz * Assign rules to the newly allocated list entries. 1619220163Strasz */ 1620220163Strasz rw_wlock(&rctl_lock); 1621220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1622220163Strasz if (link->rrl_rule->rr_subject_type == 1623220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1624220163Strasz if (newlink == NULL) 1625220163Strasz goto goaround; 1626220163Strasz rctl_rule_acquire(link->rrl_rule); 1627220163Strasz newlink->rrl_rule = link->rrl_rule; 1628220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1629220163Strasz rulecnt--; 1630220163Strasz } 1631220163Strasz } 1632220163Strasz 1633220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) { 1634220163Strasz if (newlink == NULL) 1635220163Strasz goto goaround; 1636220163Strasz rctl_rule_acquire(link->rrl_rule); 1637220163Strasz newlink->rrl_rule = link->rrl_rule; 1638220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1639220163Strasz rulecnt--; 1640220163Strasz } 1641220163Strasz 1642220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) { 1643220163Strasz if (newlink == NULL) 1644220163Strasz goto goaround; 1645220163Strasz rctl_rule_acquire(link->rrl_rule); 1646220163Strasz newlink->rrl_rule = link->rrl_rule; 1647220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1648220163Strasz rulecnt--; 1649220163Strasz } 1650220163Strasz 1651221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) { 1652220163Strasz if (newlink == NULL) 1653220163Strasz goto goaround; 1654220163Strasz rctl_rule_acquire(link->rrl_rule); 1655220163Strasz newlink->rrl_rule = link->rrl_rule; 1656220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1657220163Strasz rulecnt--; 1658220163Strasz } 1659220163Strasz 1660220163Strasz if (rulecnt == 0) { 1661220163Strasz /* 1662220163Strasz * Free the old rule list. 1663220163Strasz */ 1664220163Strasz while (!LIST_EMPTY(&p->p_racct->r_rule_links)) { 1665220163Strasz link = LIST_FIRST(&p->p_racct->r_rule_links); 1666220163Strasz LIST_REMOVE(link, rrl_next); 1667220163Strasz rctl_rule_release(link->rrl_rule); 1668220163Strasz uma_zfree(rctl_rule_link_zone, link); 1669220163Strasz } 1670220163Strasz 1671220163Strasz /* 1672220163Strasz * Replace lists and we're done. 1673220163Strasz * 1674220163Strasz * XXX: Is there any way to switch list heads instead 1675220163Strasz * of iterating here? 1676220163Strasz */ 1677220163Strasz while (!LIST_EMPTY(&newrules)) { 1678220163Strasz newlink = LIST_FIRST(&newrules); 1679220163Strasz LIST_REMOVE(newlink, rrl_next); 1680220163Strasz LIST_INSERT_HEAD(&p->p_racct->r_rule_links, 1681220163Strasz newlink, rrl_next); 1682220163Strasz } 1683220163Strasz 1684220163Strasz rw_wunlock(&rctl_lock); 1685220163Strasz 1686220163Strasz return; 1687220163Strasz } 1688220163Strasz 1689220163Straszgoaround: 1690220163Strasz rw_wunlock(&rctl_lock); 1691220163Strasz 1692220163Strasz /* 1693220163Strasz * Rule list changed while we were not holding the rctl_lock. 1694220163Strasz * Free the new list and try again. 1695220163Strasz */ 1696220163Strasz while (!LIST_EMPTY(&newrules)) { 1697220163Strasz newlink = LIST_FIRST(&newrules); 1698220163Strasz LIST_REMOVE(newlink, rrl_next); 1699220163Strasz if (newlink->rrl_rule != NULL) 1700220163Strasz rctl_rule_release(newlink->rrl_rule); 1701220163Strasz uma_zfree(rctl_rule_link_zone, newlink); 1702220163Strasz } 1703220163Strasz 1704220163Strasz goto again; 1705220163Strasz} 1706220163Strasz 1707220163Strasz/* 1708220163Strasz * Assign RCTL rules to the newly created process. 1709220163Strasz */ 1710220163Straszint 1711220163Straszrctl_proc_fork(struct proc *parent, struct proc *child) 1712220163Strasz{ 1713220163Strasz int error; 1714220163Strasz struct rctl_rule_link *link; 1715220163Strasz struct rctl_rule *rule; 1716220163Strasz 1717220163Strasz LIST_INIT(&child->p_racct->r_rule_links); 1718220163Strasz 1719220163Strasz /* 1720220163Strasz * No limits for kernel processes. 1721220163Strasz */ 1722220163Strasz if (child->p_flag & P_SYSTEM) 1723220163Strasz return (0); 1724220163Strasz 1725220163Strasz /* 1726220163Strasz * Nothing to inherit from P_SYSTEM parents. 1727220163Strasz */ 1728220163Strasz if (parent->p_racct == NULL) { 1729220163Strasz KASSERT(parent->p_flag & P_SYSTEM, 1730220163Strasz ("non-system process without racct; p = %p", parent)); 1731220163Strasz return (0); 1732220163Strasz } 1733220163Strasz 1734220163Strasz rw_wlock(&rctl_lock); 1735220163Strasz 1736220163Strasz /* 1737220163Strasz * Go through limits applicable to the parent and assign them 1738220163Strasz * to the child. Rules with 'process' subject have to be duplicated 1739220163Strasz * in order to make their rr_subject point to the new process. 1740220163Strasz */ 1741220163Strasz LIST_FOREACH(link, &parent->p_racct->r_rule_links, rrl_next) { 1742220163Strasz if (link->rrl_rule->rr_subject_type == 1743220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1744220163Strasz rule = rctl_rule_duplicate(link->rrl_rule, M_NOWAIT); 1745220163Strasz if (rule == NULL) 1746220163Strasz goto fail; 1747220163Strasz KASSERT(rule->rr_subject.rs_proc == parent, 1748220163Strasz ("rule->rr_subject.rs_proc != parent")); 1749220163Strasz rule->rr_subject.rs_proc = child; 1750220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1751220163Strasz rule); 1752220163Strasz rctl_rule_release(rule); 1753220163Strasz if (error != 0) 1754220163Strasz goto fail; 1755220163Strasz } else { 1756220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1757220163Strasz link->rrl_rule); 1758220163Strasz if (error != 0) 1759220163Strasz goto fail; 1760220163Strasz } 1761220163Strasz } 1762220163Strasz 1763220163Strasz rw_wunlock(&rctl_lock); 1764220163Strasz return (0); 1765220163Strasz 1766220163Straszfail: 1767220163Strasz while (!LIST_EMPTY(&child->p_racct->r_rule_links)) { 1768220163Strasz link = LIST_FIRST(&child->p_racct->r_rule_links); 1769220163Strasz LIST_REMOVE(link, rrl_next); 1770220163Strasz rctl_rule_release(link->rrl_rule); 1771220163Strasz uma_zfree(rctl_rule_link_zone, link); 1772220163Strasz } 1773220163Strasz rw_wunlock(&rctl_lock); 1774220163Strasz return (EAGAIN); 1775220163Strasz} 1776220163Strasz 1777220163Strasz/* 1778220163Strasz * Release rules attached to the racct. 1779220163Strasz */ 1780220163Straszvoid 1781220163Straszrctl_racct_release(struct racct *racct) 1782220163Strasz{ 1783220163Strasz struct rctl_rule_link *link; 1784220163Strasz 1785220163Strasz rw_wlock(&rctl_lock); 1786220163Strasz while (!LIST_EMPTY(&racct->r_rule_links)) { 1787220163Strasz link = LIST_FIRST(&racct->r_rule_links); 1788220163Strasz LIST_REMOVE(link, rrl_next); 1789220163Strasz rctl_rule_release(link->rrl_rule); 1790220163Strasz uma_zfree(rctl_rule_link_zone, link); 1791220163Strasz } 1792220163Strasz rw_wunlock(&rctl_lock); 1793220163Strasz} 1794220163Strasz 1795220163Straszstatic void 1796220163Straszrctl_init(void) 1797220163Strasz{ 1798220163Strasz 1799220163Strasz rctl_rule_link_zone = uma_zcreate("rctl_rule_link", 1800220163Strasz sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL, 1801220163Strasz UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1802220163Strasz rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule), 1803220163Strasz NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1804220163Strasz} 1805220163Strasz 1806220163Strasz#else /* !RCTL */ 1807220163Strasz 1808220163Straszint 1809225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1810220163Strasz{ 1811220163Strasz 1812220163Strasz return (ENOSYS); 1813220163Strasz} 1814220163Strasz 1815220163Straszint 1816225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1817220163Strasz{ 1818220163Strasz 1819220163Strasz return (ENOSYS); 1820220163Strasz} 1821220163Strasz 1822220163Straszint 1823225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1824220163Strasz{ 1825220163Strasz 1826220163Strasz return (ENOSYS); 1827220163Strasz} 1828220163Strasz 1829220163Straszint 1830225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1831220163Strasz{ 1832220163Strasz 1833220163Strasz return (ENOSYS); 1834220163Strasz} 1835220163Strasz 1836220163Straszint 1837225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1838220163Strasz{ 1839220163Strasz 1840220163Strasz return (ENOSYS); 1841220163Strasz} 1842220163Strasz 1843220163Strasz#endif /* !RCTL */ 1844