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$ 30220163Strasz */ 31220163Strasz 32220163Strasz#include <sys/cdefs.h> 33220163Strasz__FBSDID("$FreeBSD$"); 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 76234380Strasz#define RCTL_MAX_INBUFLEN 4096 77220163Strasz#define RCTL_LOG_BUFSIZE 128 78220163Strasz 79242139Strasz#define RCTL_PCPU_SHIFT (10 * 1000000) 80242139Strasz 81220163Strasz/* 82220163Strasz * 'rctl_rule_link' connects a rule with every racct it's related to. 83220163Strasz * For example, rule 'user:X:openfiles:deny=N/process' is linked 84220163Strasz * with uidinfo for user X, and to each process of that user. 85220163Strasz */ 86220163Straszstruct rctl_rule_link { 87220163Strasz LIST_ENTRY(rctl_rule_link) rrl_next; 88220163Strasz struct rctl_rule *rrl_rule; 89220163Strasz int rrl_exceeded; 90220163Strasz}; 91220163Strasz 92220163Straszstruct dict { 93220163Strasz const char *d_name; 94220163Strasz int d_value; 95220163Strasz}; 96220163Strasz 97220163Straszstatic struct dict subjectnames[] = { 98220163Strasz { "process", RCTL_SUBJECT_TYPE_PROCESS }, 99220163Strasz { "user", RCTL_SUBJECT_TYPE_USER }, 100220163Strasz { "loginclass", RCTL_SUBJECT_TYPE_LOGINCLASS }, 101220163Strasz { "jail", RCTL_SUBJECT_TYPE_JAIL }, 102220163Strasz { NULL, -1 }}; 103220163Strasz 104220163Straszstatic struct dict resourcenames[] = { 105224036Strasz { "cputime", RACCT_CPU }, 106224036Strasz { "datasize", RACCT_DATA }, 107224036Strasz { "stacksize", RACCT_STACK }, 108224036Strasz { "coredumpsize", RACCT_CORE }, 109224036Strasz { "memoryuse", RACCT_RSS }, 110224036Strasz { "memorylocked", RACCT_MEMLOCK }, 111224036Strasz { "maxproc", RACCT_NPROC }, 112224036Strasz { "openfiles", RACCT_NOFILE }, 113224036Strasz { "vmemoryuse", RACCT_VMEM }, 114224036Strasz { "pseudoterminals", RACCT_NPTS }, 115224036Strasz { "swapuse", RACCT_SWAP }, 116220163Strasz { "nthr", RACCT_NTHR }, 117220163Strasz { "msgqqueued", RACCT_MSGQQUEUED }, 118220163Strasz { "msgqsize", RACCT_MSGQSIZE }, 119220163Strasz { "nmsgq", RACCT_NMSGQ }, 120220163Strasz { "nsem", RACCT_NSEM }, 121220163Strasz { "nsemop", RACCT_NSEMOP }, 122220163Strasz { "nshm", RACCT_NSHM }, 123220163Strasz { "shmsize", RACCT_SHMSIZE }, 124220163Strasz { "wallclock", RACCT_WALLCLOCK }, 125242139Strasz { "pcpu", RACCT_PCTCPU }, 126220163Strasz { NULL, -1 }}; 127220163Strasz 128220163Straszstatic struct dict actionnames[] = { 129220163Strasz { "sighup", RCTL_ACTION_SIGHUP }, 130220163Strasz { "sigint", RCTL_ACTION_SIGINT }, 131220163Strasz { "sigquit", RCTL_ACTION_SIGQUIT }, 132220163Strasz { "sigill", RCTL_ACTION_SIGILL }, 133220163Strasz { "sigtrap", RCTL_ACTION_SIGTRAP }, 134220163Strasz { "sigabrt", RCTL_ACTION_SIGABRT }, 135220163Strasz { "sigemt", RCTL_ACTION_SIGEMT }, 136220163Strasz { "sigfpe", RCTL_ACTION_SIGFPE }, 137220163Strasz { "sigkill", RCTL_ACTION_SIGKILL }, 138220163Strasz { "sigbus", RCTL_ACTION_SIGBUS }, 139220163Strasz { "sigsegv", RCTL_ACTION_SIGSEGV }, 140220163Strasz { "sigsys", RCTL_ACTION_SIGSYS }, 141220163Strasz { "sigpipe", RCTL_ACTION_SIGPIPE }, 142220163Strasz { "sigalrm", RCTL_ACTION_SIGALRM }, 143220163Strasz { "sigterm", RCTL_ACTION_SIGTERM }, 144220163Strasz { "sigurg", RCTL_ACTION_SIGURG }, 145220163Strasz { "sigstop", RCTL_ACTION_SIGSTOP }, 146220163Strasz { "sigtstp", RCTL_ACTION_SIGTSTP }, 147220163Strasz { "sigchld", RCTL_ACTION_SIGCHLD }, 148220163Strasz { "sigttin", RCTL_ACTION_SIGTTIN }, 149220163Strasz { "sigttou", RCTL_ACTION_SIGTTOU }, 150220163Strasz { "sigio", RCTL_ACTION_SIGIO }, 151220163Strasz { "sigxcpu", RCTL_ACTION_SIGXCPU }, 152220163Strasz { "sigxfsz", RCTL_ACTION_SIGXFSZ }, 153220163Strasz { "sigvtalrm", RCTL_ACTION_SIGVTALRM }, 154220163Strasz { "sigprof", RCTL_ACTION_SIGPROF }, 155220163Strasz { "sigwinch", RCTL_ACTION_SIGWINCH }, 156220163Strasz { "siginfo", RCTL_ACTION_SIGINFO }, 157220163Strasz { "sigusr1", RCTL_ACTION_SIGUSR1 }, 158220163Strasz { "sigusr2", RCTL_ACTION_SIGUSR2 }, 159220163Strasz { "sigthr", RCTL_ACTION_SIGTHR }, 160220163Strasz { "deny", RCTL_ACTION_DENY }, 161220163Strasz { "log", RCTL_ACTION_LOG }, 162220163Strasz { "devctl", RCTL_ACTION_DEVCTL }, 163220163Strasz { NULL, -1 }}; 164220163Strasz 165220163Straszstatic void rctl_init(void); 166220163StraszSYSINIT(rctl, SI_SUB_RACCT, SI_ORDER_FIRST, rctl_init, NULL); 167220163Strasz 168220163Straszstatic uma_zone_t rctl_rule_link_zone; 169220163Straszstatic uma_zone_t rctl_rule_zone; 170220163Straszstatic struct rwlock rctl_lock; 171220163StraszRW_SYSINIT(rctl_lock, &rctl_lock, "RCTL lock"); 172220163Strasz 173220163Straszstatic int rctl_rule_fully_specified(const struct rctl_rule *rule); 174220163Straszstatic void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule); 175220163Strasz 176227293Sedstatic MALLOC_DEFINE(M_RCTL, "rctl", "Resource Limits"); 177220163Strasz 178220163Straszstatic const char * 179220163Straszrctl_subject_type_name(int subject) 180220163Strasz{ 181220163Strasz int i; 182220163Strasz 183220163Strasz for (i = 0; subjectnames[i].d_name != NULL; i++) { 184220163Strasz if (subjectnames[i].d_value == subject) 185220163Strasz return (subjectnames[i].d_name); 186220163Strasz } 187220163Strasz 188220163Strasz panic("rctl_subject_type_name: unknown subject type %d", subject); 189220163Strasz} 190220163Strasz 191220163Straszstatic const char * 192220163Straszrctl_action_name(int action) 193220163Strasz{ 194220163Strasz int i; 195220163Strasz 196220163Strasz for (i = 0; actionnames[i].d_name != NULL; i++) { 197220163Strasz if (actionnames[i].d_value == action) 198220163Strasz return (actionnames[i].d_name); 199220163Strasz } 200220163Strasz 201220163Strasz panic("rctl_action_name: unknown action %d", action); 202220163Strasz} 203220163Strasz 204220163Straszconst char * 205220163Straszrctl_resource_name(int resource) 206220163Strasz{ 207220163Strasz int i; 208220163Strasz 209220163Strasz for (i = 0; resourcenames[i].d_name != NULL; i++) { 210220163Strasz if (resourcenames[i].d_value == resource) 211220163Strasz return (resourcenames[i].d_name); 212220163Strasz } 213220163Strasz 214220163Strasz panic("rctl_resource_name: unknown resource %d", resource); 215220163Strasz} 216220163Strasz 217220163Strasz/* 218220163Strasz * Return the amount of resource that can be allocated by 'p' before 219220163Strasz * hitting 'rule'. 220220163Strasz */ 221220163Straszstatic int64_t 222220163Straszrctl_available_resource(const struct proc *p, const struct rctl_rule *rule) 223220163Strasz{ 224220163Strasz int resource; 225220163Strasz int64_t available = INT64_MAX; 226220163Strasz struct ucred *cred = p->p_ucred; 227220163Strasz 228220163Strasz rw_assert(&rctl_lock, RA_LOCKED); 229220163Strasz 230220163Strasz resource = rule->rr_resource; 231220163Strasz switch (rule->rr_per) { 232220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 233220163Strasz available = rule->rr_amount - 234220163Strasz p->p_racct->r_resources[resource]; 235220163Strasz break; 236220163Strasz case RCTL_SUBJECT_TYPE_USER: 237220163Strasz available = rule->rr_amount - 238220163Strasz cred->cr_ruidinfo->ui_racct->r_resources[resource]; 239220163Strasz break; 240220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 241220163Strasz available = rule->rr_amount - 242220163Strasz cred->cr_loginclass->lc_racct->r_resources[resource]; 243220163Strasz break; 244220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 245220163Strasz available = rule->rr_amount - 246221362Strasz cred->cr_prison->pr_prison_racct->prr_racct-> 247221362Strasz r_resources[resource]; 248220163Strasz break; 249220163Strasz default: 250220163Strasz panic("rctl_compute_available: unknown per %d", 251220163Strasz rule->rr_per); 252220163Strasz } 253220163Strasz 254220163Strasz return (available); 255220163Strasz} 256220163Strasz 257220163Strasz/* 258220163Strasz * Return non-zero if allocating 'amount' by proc 'p' would exceed 259220163Strasz * resource limit specified by 'rule'. 260220163Strasz */ 261220163Straszstatic int 262220163Straszrctl_would_exceed(const struct proc *p, const struct rctl_rule *rule, 263220163Strasz int64_t amount) 264220163Strasz{ 265220163Strasz int64_t available; 266220163Strasz 267220163Strasz rw_assert(&rctl_lock, RA_LOCKED); 268220163Strasz 269220163Strasz available = rctl_available_resource(p, rule); 270220163Strasz if (available >= amount) 271220163Strasz return (0); 272220163Strasz 273220163Strasz return (1); 274220163Strasz} 275220163Strasz 276220163Strasz/* 277242139Strasz * Special version of rctl_available() function for the %cpu resource. 278242139Strasz * We slightly cheat here and return less than we normally would. 279242139Strasz */ 280242139Straszint64_t 281242139Straszrctl_pcpu_available(const struct proc *p) { 282242139Strasz struct rctl_rule *rule; 283242139Strasz struct rctl_rule_link *link; 284242139Strasz int64_t available, minavailable, limit; 285242139Strasz 286242139Strasz minavailable = INT64_MAX; 287242139Strasz limit = 0; 288242139Strasz 289242139Strasz rw_rlock(&rctl_lock); 290242139Strasz 291242139Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 292242139Strasz rule = link->rrl_rule; 293242139Strasz if (rule->rr_resource != RACCT_PCTCPU) 294242139Strasz continue; 295242139Strasz if (rule->rr_action != RCTL_ACTION_DENY) 296242139Strasz continue; 297242139Strasz available = rctl_available_resource(p, rule); 298242139Strasz if (available < minavailable) { 299242139Strasz minavailable = available; 300242139Strasz limit = rule->rr_amount; 301242139Strasz } 302242139Strasz } 303242139Strasz 304242139Strasz rw_runlock(&rctl_lock); 305242139Strasz 306242139Strasz /* 307242139Strasz * Return slightly less than actual value of the available 308242139Strasz * %cpu resource. This makes %cpu throttling more agressive 309242139Strasz * and lets us act sooner than the limits are already exceeded. 310242139Strasz */ 311242139Strasz if (limit != 0) { 312242139Strasz if (limit > 2 * RCTL_PCPU_SHIFT) 313242139Strasz minavailable -= RCTL_PCPU_SHIFT; 314242139Strasz else 315242139Strasz minavailable -= (limit / 2); 316242139Strasz } 317242139Strasz 318242139Strasz return (minavailable); 319242139Strasz} 320242139Strasz 321242139Strasz/* 322220163Strasz * Check whether the proc 'p' can allocate 'amount' of 'resource' in addition 323220163Strasz * to what it keeps allocated now. Returns non-zero if the allocation should 324220163Strasz * be denied, 0 otherwise. 325220163Strasz */ 326220163Straszint 327220163Straszrctl_enforce(struct proc *p, int resource, uint64_t amount) 328220163Strasz{ 329220163Strasz struct rctl_rule *rule; 330220163Strasz struct rctl_rule_link *link; 331220163Strasz struct sbuf sb; 332220163Strasz int should_deny = 0; 333220163Strasz char *buf; 334220163Strasz static int curtime = 0; 335220163Strasz static struct timeval lasttime; 336220163Strasz 337220163Strasz rw_rlock(&rctl_lock); 338220163Strasz 339220163Strasz /* 340220163Strasz * There may be more than one matching rule; go through all of them. 341220163Strasz * Denial should be done last, after logging and sending signals. 342220163Strasz */ 343220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 344220163Strasz rule = link->rrl_rule; 345220163Strasz if (rule->rr_resource != resource) 346220163Strasz continue; 347220163Strasz if (!rctl_would_exceed(p, rule, amount)) { 348220163Strasz link->rrl_exceeded = 0; 349220163Strasz continue; 350220163Strasz } 351220163Strasz 352220163Strasz switch (rule->rr_action) { 353220163Strasz case RCTL_ACTION_DENY: 354220163Strasz should_deny = 1; 355220163Strasz continue; 356220163Strasz case RCTL_ACTION_LOG: 357220163Strasz /* 358220163Strasz * If rrl_exceeded != 0, it means we've already 359220163Strasz * logged a warning for this process. 360220163Strasz */ 361220163Strasz if (link->rrl_exceeded != 0) 362220163Strasz continue; 363220163Strasz 364225940Strasz /* 365225940Strasz * If the process state is not fully initialized yet, 366225940Strasz * we can't access most of the required fields, e.g. 367225940Strasz * p->p_comm. This happens when called from fork1(). 368225940Strasz * Ignore this rule for now; it will be processed just 369225940Strasz * after fork, when called from racct_proc_fork_done(). 370225940Strasz */ 371225940Strasz if (p->p_state != PRS_NORMAL) 372225940Strasz continue; 373225940Strasz 374220163Strasz if (!ppsratecheck(&lasttime, &curtime, 10)) 375220163Strasz continue; 376220163Strasz 377220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 378220163Strasz if (buf == NULL) { 379220163Strasz printf("rctl_enforce: out of memory\n"); 380220163Strasz continue; 381220163Strasz } 382220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 383220163Strasz rctl_rule_to_sbuf(&sb, rule); 384220163Strasz sbuf_finish(&sb); 385220163Strasz printf("rctl: rule \"%s\" matched by pid %d " 386220163Strasz "(%s), uid %d, jail %s\n", sbuf_data(&sb), 387220163Strasz p->p_pid, p->p_comm, p->p_ucred->cr_uid, 388221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 389220163Strasz sbuf_delete(&sb); 390220163Strasz free(buf, M_RCTL); 391220163Strasz link->rrl_exceeded = 1; 392220163Strasz continue; 393220163Strasz case RCTL_ACTION_DEVCTL: 394220163Strasz if (link->rrl_exceeded != 0) 395220163Strasz continue; 396220163Strasz 397225940Strasz if (p->p_state != PRS_NORMAL) 398225940Strasz continue; 399225940Strasz 400220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 401220163Strasz if (buf == NULL) { 402220163Strasz printf("rctl_enforce: out of memory\n"); 403220163Strasz continue; 404220163Strasz } 405220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 406220163Strasz sbuf_printf(&sb, "rule="); 407220163Strasz rctl_rule_to_sbuf(&sb, rule); 408220163Strasz sbuf_printf(&sb, " pid=%d ruid=%d jail=%s", 409220163Strasz p->p_pid, p->p_ucred->cr_ruid, 410221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 411220163Strasz sbuf_finish(&sb); 412220163Strasz devctl_notify_f("RCTL", "rule", "matched", 413220163Strasz sbuf_data(&sb), M_NOWAIT); 414220163Strasz sbuf_delete(&sb); 415220163Strasz free(buf, M_RCTL); 416220163Strasz link->rrl_exceeded = 1; 417220163Strasz continue; 418220163Strasz default: 419220163Strasz if (link->rrl_exceeded != 0) 420220163Strasz continue; 421220163Strasz 422225940Strasz if (p->p_state != PRS_NORMAL) 423225940Strasz continue; 424225940Strasz 425220163Strasz KASSERT(rule->rr_action > 0 && 426220163Strasz rule->rr_action <= RCTL_ACTION_SIGNAL_MAX, 427220163Strasz ("rctl_enforce: unknown action %d", 428220163Strasz rule->rr_action)); 429220163Strasz 430220163Strasz /* 431220163Strasz * We're using the fact that RCTL_ACTION_SIG* values 432220163Strasz * are equal to their counterparts from sys/signal.h. 433220163Strasz */ 434225617Skmacy kern_psignal(p, rule->rr_action); 435220163Strasz link->rrl_exceeded = 1; 436220163Strasz continue; 437220163Strasz } 438220163Strasz } 439220163Strasz 440220163Strasz rw_runlock(&rctl_lock); 441220163Strasz 442220163Strasz if (should_deny) { 443220163Strasz /* 444220163Strasz * Return fake error code; the caller should change it 445220163Strasz * into one proper for the situation - EFSIZ, ENOMEM etc. 446220163Strasz */ 447220163Strasz return (EDOOFUS); 448220163Strasz } 449220163Strasz 450220163Strasz return (0); 451220163Strasz} 452220163Strasz 453220163Straszuint64_t 454220163Straszrctl_get_limit(struct proc *p, int resource) 455220163Strasz{ 456220163Strasz struct rctl_rule *rule; 457220163Strasz struct rctl_rule_link *link; 458220163Strasz uint64_t amount = UINT64_MAX; 459220163Strasz 460220163Strasz rw_rlock(&rctl_lock); 461220163Strasz 462220163Strasz /* 463220163Strasz * There may be more than one matching rule; go through all of them. 464220163Strasz * Denial should be done last, after logging and sending signals. 465220163Strasz */ 466220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 467220163Strasz rule = link->rrl_rule; 468220163Strasz if (rule->rr_resource != resource) 469220163Strasz continue; 470220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 471220163Strasz continue; 472220163Strasz if (rule->rr_amount < amount) 473220163Strasz amount = rule->rr_amount; 474220163Strasz } 475220163Strasz 476220163Strasz rw_runlock(&rctl_lock); 477220163Strasz 478220163Strasz return (amount); 479220163Strasz} 480220163Strasz 481220163Straszuint64_t 482220163Straszrctl_get_available(struct proc *p, int resource) 483220163Strasz{ 484220163Strasz struct rctl_rule *rule; 485220163Strasz struct rctl_rule_link *link; 486220163Strasz int64_t available, minavailable, allocated; 487220163Strasz 488220163Strasz minavailable = INT64_MAX; 489220163Strasz 490220163Strasz rw_rlock(&rctl_lock); 491220163Strasz 492220163Strasz /* 493220163Strasz * There may be more than one matching rule; go through all of them. 494220163Strasz * Denial should be done last, after logging and sending signals. 495220163Strasz */ 496220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 497220163Strasz rule = link->rrl_rule; 498220163Strasz if (rule->rr_resource != resource) 499220163Strasz continue; 500220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 501220163Strasz continue; 502220163Strasz available = rctl_available_resource(p, rule); 503220163Strasz if (available < minavailable) 504220163Strasz minavailable = available; 505220163Strasz } 506220163Strasz 507220163Strasz rw_runlock(&rctl_lock); 508220163Strasz 509220163Strasz /* 510220163Strasz * XXX: Think about this _hard_. 511220163Strasz */ 512220163Strasz allocated = p->p_racct->r_resources[resource]; 513220163Strasz if (minavailable < INT64_MAX - allocated) 514220163Strasz minavailable += allocated; 515220163Strasz if (minavailable < 0) 516220163Strasz minavailable = 0; 517220163Strasz return (minavailable); 518220163Strasz} 519220163Strasz 520220163Straszstatic int 521220163Straszrctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter) 522220163Strasz{ 523220163Strasz 524220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) { 525220163Strasz if (rule->rr_subject_type != filter->rr_subject_type) 526220163Strasz return (0); 527220163Strasz 528220163Strasz switch (filter->rr_subject_type) { 529220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 530220163Strasz if (filter->rr_subject.rs_proc != NULL && 531220163Strasz rule->rr_subject.rs_proc != 532220163Strasz filter->rr_subject.rs_proc) 533220163Strasz return (0); 534220163Strasz break; 535220163Strasz case RCTL_SUBJECT_TYPE_USER: 536220163Strasz if (filter->rr_subject.rs_uip != NULL && 537220163Strasz rule->rr_subject.rs_uip != 538220163Strasz filter->rr_subject.rs_uip) 539220163Strasz return (0); 540220163Strasz break; 541220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 542220527Strasz if (filter->rr_subject.rs_loginclass != NULL && 543220527Strasz rule->rr_subject.rs_loginclass != 544220527Strasz filter->rr_subject.rs_loginclass) 545220163Strasz return (0); 546220163Strasz break; 547220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 548221362Strasz if (filter->rr_subject.rs_prison_racct != NULL && 549221362Strasz rule->rr_subject.rs_prison_racct != 550221362Strasz filter->rr_subject.rs_prison_racct) 551220163Strasz return (0); 552220163Strasz break; 553220163Strasz default: 554220163Strasz panic("rctl_rule_matches: unknown subject type %d", 555220163Strasz filter->rr_subject_type); 556220163Strasz } 557220163Strasz } 558220163Strasz 559220163Strasz if (filter->rr_resource != RACCT_UNDEFINED) { 560220163Strasz if (rule->rr_resource != filter->rr_resource) 561220163Strasz return (0); 562220163Strasz } 563220163Strasz 564220163Strasz if (filter->rr_action != RCTL_ACTION_UNDEFINED) { 565220163Strasz if (rule->rr_action != filter->rr_action) 566220163Strasz return (0); 567220163Strasz } 568220163Strasz 569220163Strasz if (filter->rr_amount != RCTL_AMOUNT_UNDEFINED) { 570220163Strasz if (rule->rr_amount != filter->rr_amount) 571220163Strasz return (0); 572220163Strasz } 573220163Strasz 574220163Strasz if (filter->rr_per != RCTL_SUBJECT_TYPE_UNDEFINED) { 575220163Strasz if (rule->rr_per != filter->rr_per) 576220163Strasz return (0); 577220163Strasz } 578220163Strasz 579220163Strasz return (1); 580220163Strasz} 581220163Strasz 582220163Straszstatic int 583220163Straszstr2value(const char *str, int *value, struct dict *table) 584220163Strasz{ 585220163Strasz int i; 586220163Strasz 587220163Strasz if (value == NULL) 588220163Strasz return (EINVAL); 589220163Strasz 590220163Strasz for (i = 0; table[i].d_name != NULL; i++) { 591220163Strasz if (strcasecmp(table[i].d_name, str) == 0) { 592220163Strasz *value = table[i].d_value; 593220163Strasz return (0); 594220163Strasz } 595220163Strasz } 596220163Strasz 597220163Strasz return (EINVAL); 598220163Strasz} 599220163Strasz 600220163Straszstatic int 601220163Straszstr2id(const char *str, id_t *value) 602220163Strasz{ 603220163Strasz char *end; 604220163Strasz 605220163Strasz if (str == NULL) 606220163Strasz return (EINVAL); 607220163Strasz 608220163Strasz *value = strtoul(str, &end, 10); 609220163Strasz if ((size_t)(end - str) != strlen(str)) 610220163Strasz return (EINVAL); 611220163Strasz 612220163Strasz return (0); 613220163Strasz} 614220163Strasz 615220163Straszstatic int 616220163Straszstr2int64(const char *str, int64_t *value) 617220163Strasz{ 618220163Strasz char *end; 619220163Strasz 620220163Strasz if (str == NULL) 621220163Strasz return (EINVAL); 622220163Strasz 623220163Strasz *value = strtoul(str, &end, 10); 624220163Strasz if ((size_t)(end - str) != strlen(str)) 625220163Strasz return (EINVAL); 626220163Strasz 627220163Strasz return (0); 628220163Strasz} 629220163Strasz 630220163Strasz/* 631220163Strasz * Connect the rule to the racct, increasing refcount for the rule. 632220163Strasz */ 633220163Straszstatic void 634220163Straszrctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) 635220163Strasz{ 636220163Strasz struct rctl_rule_link *link; 637220163Strasz 638220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 639220163Strasz 640220163Strasz rctl_rule_acquire(rule); 641220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 642220163Strasz link->rrl_rule = rule; 643220163Strasz link->rrl_exceeded = 0; 644220163Strasz 645220163Strasz rw_wlock(&rctl_lock); 646220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 647220163Strasz rw_wunlock(&rctl_lock); 648220163Strasz} 649220163Strasz 650220163Straszstatic int 651220163Straszrctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) 652220163Strasz{ 653220163Strasz struct rctl_rule_link *link; 654220163Strasz 655220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 656220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 657220163Strasz 658220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); 659220163Strasz if (link == NULL) 660220163Strasz return (ENOMEM); 661220163Strasz rctl_rule_acquire(rule); 662220163Strasz link->rrl_rule = rule; 663220163Strasz link->rrl_exceeded = 0; 664220163Strasz 665220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 666220163Strasz return (0); 667220163Strasz} 668220163Strasz 669220163Strasz/* 670220163Strasz * Remove limits for a rules matching the filter and release 671220163Strasz * the refcounts for the rules, possibly freeing them. Returns 672220163Strasz * the number of limit structures removed. 673220163Strasz */ 674220163Straszstatic int 675220163Straszrctl_racct_remove_rules(struct racct *racct, 676220163Strasz const struct rctl_rule *filter) 677220163Strasz{ 678220163Strasz int removed = 0; 679220163Strasz struct rctl_rule_link *link, *linktmp; 680220163Strasz 681220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 682220163Strasz 683220163Strasz LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { 684220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 685220163Strasz continue; 686220163Strasz 687220163Strasz LIST_REMOVE(link, rrl_next); 688220163Strasz rctl_rule_release(link->rrl_rule); 689220163Strasz uma_zfree(rctl_rule_link_zone, link); 690220163Strasz removed++; 691220163Strasz } 692220163Strasz return (removed); 693220163Strasz} 694220163Strasz 695220163Straszstatic void 696220163Straszrctl_rule_acquire_subject(struct rctl_rule *rule) 697220163Strasz{ 698220163Strasz 699220163Strasz switch (rule->rr_subject_type) { 700220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 701220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 702221362Strasz break; 703220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 704221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 705221362Strasz prison_racct_hold(rule->rr_subject.rs_prison_racct); 706220163Strasz break; 707220163Strasz case RCTL_SUBJECT_TYPE_USER: 708220163Strasz if (rule->rr_subject.rs_uip != NULL) 709220163Strasz uihold(rule->rr_subject.rs_uip); 710220163Strasz break; 711220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 712220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 713220527Strasz loginclass_hold(rule->rr_subject.rs_loginclass); 714220163Strasz break; 715220163Strasz default: 716220163Strasz panic("rctl_rule_acquire_subject: unknown subject type %d", 717220163Strasz rule->rr_subject_type); 718220163Strasz } 719220163Strasz} 720220163Strasz 721220163Straszstatic void 722220163Straszrctl_rule_release_subject(struct rctl_rule *rule) 723220163Strasz{ 724220163Strasz 725220163Strasz switch (rule->rr_subject_type) { 726220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 727220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 728221362Strasz break; 729220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 730221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 731221362Strasz prison_racct_free(rule->rr_subject.rs_prison_racct); 732220163Strasz break; 733220163Strasz case RCTL_SUBJECT_TYPE_USER: 734220163Strasz if (rule->rr_subject.rs_uip != NULL) 735220163Strasz uifree(rule->rr_subject.rs_uip); 736220163Strasz break; 737220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 738220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 739220527Strasz loginclass_free(rule->rr_subject.rs_loginclass); 740220163Strasz break; 741220163Strasz default: 742220163Strasz panic("rctl_rule_release_subject: unknown subject type %d", 743220163Strasz rule->rr_subject_type); 744220163Strasz } 745220163Strasz} 746220163Strasz 747220163Straszstruct rctl_rule * 748220163Straszrctl_rule_alloc(int flags) 749220163Strasz{ 750220163Strasz struct rctl_rule *rule; 751220163Strasz 752220163Strasz rule = uma_zalloc(rctl_rule_zone, flags); 753220163Strasz if (rule == NULL) 754220163Strasz return (NULL); 755220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 756220163Strasz rule->rr_subject.rs_proc = NULL; 757220163Strasz rule->rr_subject.rs_uip = NULL; 758220527Strasz rule->rr_subject.rs_loginclass = NULL; 759221362Strasz rule->rr_subject.rs_prison_racct = NULL; 760220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 761220163Strasz rule->rr_resource = RACCT_UNDEFINED; 762220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 763220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 764220163Strasz refcount_init(&rule->rr_refcount, 1); 765220163Strasz 766220163Strasz return (rule); 767220163Strasz} 768220163Strasz 769220163Straszstruct rctl_rule * 770220163Straszrctl_rule_duplicate(const struct rctl_rule *rule, int flags) 771220163Strasz{ 772220163Strasz struct rctl_rule *copy; 773220163Strasz 774220163Strasz copy = uma_zalloc(rctl_rule_zone, flags); 775220163Strasz if (copy == NULL) 776220163Strasz return (NULL); 777220163Strasz copy->rr_subject_type = rule->rr_subject_type; 778220163Strasz copy->rr_subject.rs_proc = rule->rr_subject.rs_proc; 779220163Strasz copy->rr_subject.rs_uip = rule->rr_subject.rs_uip; 780220527Strasz copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass; 781221362Strasz copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct; 782220163Strasz copy->rr_per = rule->rr_per; 783220163Strasz copy->rr_resource = rule->rr_resource; 784220163Strasz copy->rr_action = rule->rr_action; 785220163Strasz copy->rr_amount = rule->rr_amount; 786220163Strasz refcount_init(©->rr_refcount, 1); 787220163Strasz rctl_rule_acquire_subject(copy); 788220163Strasz 789220163Strasz return (copy); 790220163Strasz} 791220163Strasz 792220163Straszvoid 793220163Straszrctl_rule_acquire(struct rctl_rule *rule) 794220163Strasz{ 795220163Strasz 796220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 797220163Strasz 798220163Strasz refcount_acquire(&rule->rr_refcount); 799220163Strasz} 800220163Strasz 801220163Straszstatic void 802220163Straszrctl_rule_free(void *context, int pending) 803220163Strasz{ 804220163Strasz struct rctl_rule *rule; 805220163Strasz 806220163Strasz rule = (struct rctl_rule *)context; 807220163Strasz 808220163Strasz KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0")); 809220163Strasz 810220163Strasz /* 811220163Strasz * We don't need locking here; rule is guaranteed to be inaccessible. 812220163Strasz */ 813220163Strasz 814220163Strasz rctl_rule_release_subject(rule); 815220163Strasz uma_zfree(rctl_rule_zone, rule); 816220163Strasz} 817220163Strasz 818220163Straszvoid 819220163Straszrctl_rule_release(struct rctl_rule *rule) 820220163Strasz{ 821220163Strasz 822220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 823220163Strasz 824220163Strasz if (refcount_release(&rule->rr_refcount)) { 825220163Strasz /* 826220163Strasz * rctl_rule_release() is often called when iterating 827220163Strasz * over all the uidinfo structures in the system, 828220163Strasz * holding uihashtbl_lock. Since rctl_rule_free() 829220163Strasz * might end up calling uifree(), this would lead 830220163Strasz * to lock recursion. Use taskqueue to avoid this. 831220163Strasz */ 832220163Strasz TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule); 833220163Strasz taskqueue_enqueue(taskqueue_thread, &rule->rr_task); 834220163Strasz } 835220163Strasz} 836220163Strasz 837220163Straszstatic int 838220163Straszrctl_rule_fully_specified(const struct rctl_rule *rule) 839220163Strasz{ 840220163Strasz 841220163Strasz switch (rule->rr_subject_type) { 842220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 843220163Strasz return (0); 844220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 845220163Strasz if (rule->rr_subject.rs_proc == NULL) 846220163Strasz return (0); 847220163Strasz break; 848220163Strasz case RCTL_SUBJECT_TYPE_USER: 849220163Strasz if (rule->rr_subject.rs_uip == NULL) 850220163Strasz return (0); 851220163Strasz break; 852220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 853220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 854220163Strasz return (0); 855220163Strasz break; 856220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 857221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 858220163Strasz return (0); 859220163Strasz break; 860220163Strasz default: 861220163Strasz panic("rctl_rule_fully_specified: unknown subject type %d", 862220163Strasz rule->rr_subject_type); 863220163Strasz } 864220163Strasz if (rule->rr_resource == RACCT_UNDEFINED) 865220163Strasz return (0); 866220163Strasz if (rule->rr_action == RCTL_ACTION_UNDEFINED) 867220163Strasz return (0); 868220163Strasz if (rule->rr_amount == RCTL_AMOUNT_UNDEFINED) 869220163Strasz return (0); 870220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED) 871220163Strasz return (0); 872220163Strasz 873220163Strasz return (1); 874220163Strasz} 875220163Strasz 876220163Straszstatic int 877220163Straszrctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) 878220163Strasz{ 879220163Strasz int error = 0; 880220163Strasz char *subjectstr, *subject_idstr, *resourcestr, *actionstr, 881220163Strasz *amountstr, *perstr; 882220163Strasz struct rctl_rule *rule; 883220163Strasz id_t id; 884220163Strasz 885220163Strasz rule = rctl_rule_alloc(M_WAITOK); 886220163Strasz 887220163Strasz subjectstr = strsep(&rulestr, ":"); 888220163Strasz subject_idstr = strsep(&rulestr, ":"); 889220163Strasz resourcestr = strsep(&rulestr, ":"); 890220163Strasz actionstr = strsep(&rulestr, "=/"); 891220163Strasz amountstr = strsep(&rulestr, "/"); 892220163Strasz perstr = rulestr; 893220163Strasz 894220163Strasz if (subjectstr == NULL || subjectstr[0] == '\0') 895220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 896220163Strasz else { 897220163Strasz error = str2value(subjectstr, &rule->rr_subject_type, subjectnames); 898220163Strasz if (error != 0) 899220163Strasz goto out; 900220163Strasz } 901220163Strasz 902220163Strasz if (subject_idstr == NULL || subject_idstr[0] == '\0') { 903220163Strasz rule->rr_subject.rs_proc = NULL; 904220163Strasz rule->rr_subject.rs_uip = NULL; 905220527Strasz rule->rr_subject.rs_loginclass = NULL; 906221362Strasz rule->rr_subject.rs_prison_racct = NULL; 907220163Strasz } else { 908220163Strasz switch (rule->rr_subject_type) { 909220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 910220163Strasz error = EINVAL; 911220163Strasz goto out; 912220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 913220163Strasz error = str2id(subject_idstr, &id); 914220163Strasz if (error != 0) 915220163Strasz goto out; 916220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 917220163Strasz rule->rr_subject.rs_proc = pfind(id); 918220163Strasz if (rule->rr_subject.rs_proc == NULL) { 919220163Strasz error = ESRCH; 920220163Strasz goto out; 921220163Strasz } 922220163Strasz PROC_UNLOCK(rule->rr_subject.rs_proc); 923220163Strasz break; 924220163Strasz case RCTL_SUBJECT_TYPE_USER: 925220163Strasz error = str2id(subject_idstr, &id); 926220163Strasz if (error != 0) 927220163Strasz goto out; 928220163Strasz rule->rr_subject.rs_uip = uifind(id); 929220163Strasz break; 930220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 931220527Strasz rule->rr_subject.rs_loginclass = 932220163Strasz loginclass_find(subject_idstr); 933220527Strasz if (rule->rr_subject.rs_loginclass == NULL) { 934220163Strasz error = ENAMETOOLONG; 935220163Strasz goto out; 936220163Strasz } 937220163Strasz break; 938220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 939221362Strasz rule->rr_subject.rs_prison_racct = 940221362Strasz prison_racct_find(subject_idstr); 941221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) { 942221362Strasz error = ENAMETOOLONG; 943221362Strasz goto out; 944220163Strasz } 945220163Strasz break; 946220163Strasz default: 947220163Strasz panic("rctl_string_to_rule: unknown subject type %d", 948220163Strasz rule->rr_subject_type); 949220163Strasz } 950220163Strasz } 951220163Strasz 952220163Strasz if (resourcestr == NULL || resourcestr[0] == '\0') 953220163Strasz rule->rr_resource = RACCT_UNDEFINED; 954220163Strasz else { 955220163Strasz error = str2value(resourcestr, &rule->rr_resource, 956220163Strasz resourcenames); 957220163Strasz if (error != 0) 958220163Strasz goto out; 959220163Strasz } 960220163Strasz 961220163Strasz if (actionstr == NULL || actionstr[0] == '\0') 962220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 963220163Strasz else { 964220163Strasz error = str2value(actionstr, &rule->rr_action, actionnames); 965220163Strasz if (error != 0) 966220163Strasz goto out; 967220163Strasz } 968220163Strasz 969220163Strasz if (amountstr == NULL || amountstr[0] == '\0') 970220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 971220163Strasz else { 972220163Strasz error = str2int64(amountstr, &rule->rr_amount); 973220163Strasz if (error != 0) 974220163Strasz goto out; 975224036Strasz if (RACCT_IS_IN_MILLIONS(rule->rr_resource)) 976225371Strasz rule->rr_amount *= 1000000; 977220163Strasz } 978220163Strasz 979220163Strasz if (perstr == NULL || perstr[0] == '\0') 980220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 981220163Strasz else { 982220163Strasz error = str2value(perstr, &rule->rr_per, subjectnames); 983220163Strasz if (error != 0) 984220163Strasz goto out; 985220163Strasz } 986220163Strasz 987220163Straszout: 988220163Strasz if (error == 0) 989220163Strasz *rulep = rule; 990220163Strasz else 991220163Strasz rctl_rule_release(rule); 992220163Strasz 993220163Strasz return (error); 994220163Strasz} 995220163Strasz 996220163Strasz/* 997220163Strasz * Link a rule with all the subjects it applies to. 998220163Strasz */ 999220163Straszint 1000220163Straszrctl_rule_add(struct rctl_rule *rule) 1001220163Strasz{ 1002220163Strasz struct proc *p; 1003220163Strasz struct ucred *cred; 1004220163Strasz struct uidinfo *uip; 1005220163Strasz struct prison *pr; 1006221362Strasz struct prison_racct *prr; 1007220163Strasz struct loginclass *lc; 1008220163Strasz struct rctl_rule *rule2; 1009220163Strasz int match; 1010220163Strasz 1011220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 1012220163Strasz 1013220163Strasz /* 1014220163Strasz * Some rules just don't make sense. Note that the one below 1015223844Strasz * cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU, 1016220163Strasz * for example, is not deniable in the racct sense, but the 1017220163Strasz * limit is enforced in a different way, so "deny" rules for %CPU 1018220163Strasz * do make sense. 1019220163Strasz */ 1020220163Strasz if (rule->rr_action == RCTL_ACTION_DENY && 1021220163Strasz (rule->rr_resource == RACCT_CPU || 1022220163Strasz rule->rr_resource == RACCT_WALLCLOCK)) 1023220163Strasz return (EOPNOTSUPP); 1024220163Strasz 1025220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_PROCESS && 1026223844Strasz RACCT_IS_SLOPPY(rule->rr_resource)) 1027220163Strasz return (EOPNOTSUPP); 1028220163Strasz 1029220163Strasz /* 1030220163Strasz * Make sure there are no duplicated rules. Also, for the "deny" 1031220163Strasz * rules, remove ones differing only by "amount". 1032220163Strasz */ 1033220163Strasz if (rule->rr_action == RCTL_ACTION_DENY) { 1034220163Strasz rule2 = rctl_rule_duplicate(rule, M_WAITOK); 1035220163Strasz rule2->rr_amount = RCTL_AMOUNT_UNDEFINED; 1036220163Strasz rctl_rule_remove(rule2); 1037220163Strasz rctl_rule_release(rule2); 1038220163Strasz } else 1039220163Strasz rctl_rule_remove(rule); 1040220163Strasz 1041220163Strasz switch (rule->rr_subject_type) { 1042220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1043220163Strasz p = rule->rr_subject.rs_proc; 1044220163Strasz KASSERT(p != NULL, ("rctl_rule_add: NULL proc")); 1045220163Strasz 1046220163Strasz rctl_racct_add_rule(p->p_racct, rule); 1047220163Strasz /* 1048220163Strasz * In case of per-process rule, we don't have anything more 1049220163Strasz * to do. 1050220163Strasz */ 1051220163Strasz return (0); 1052220163Strasz 1053220163Strasz case RCTL_SUBJECT_TYPE_USER: 1054220163Strasz uip = rule->rr_subject.rs_uip; 1055220163Strasz KASSERT(uip != NULL, ("rctl_rule_add: NULL uip")); 1056220163Strasz rctl_racct_add_rule(uip->ui_racct, rule); 1057220163Strasz break; 1058220163Strasz 1059220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1060220527Strasz lc = rule->rr_subject.rs_loginclass; 1061220163Strasz KASSERT(lc != NULL, ("rctl_rule_add: NULL loginclass")); 1062220163Strasz rctl_racct_add_rule(lc->lc_racct, rule); 1063220163Strasz break; 1064220163Strasz 1065220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1066221362Strasz prr = rule->rr_subject.rs_prison_racct; 1067221362Strasz KASSERT(prr != NULL, ("rctl_rule_add: NULL pr")); 1068221362Strasz rctl_racct_add_rule(prr->prr_racct, rule); 1069220163Strasz break; 1070220163Strasz 1071220163Strasz default: 1072220163Strasz panic("rctl_rule_add: unknown subject type %d", 1073220163Strasz rule->rr_subject_type); 1074220163Strasz } 1075220163Strasz 1076220163Strasz /* 1077220163Strasz * Now go through all the processes and add the new rule to the ones 1078220163Strasz * it applies to. 1079220163Strasz */ 1080220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1081220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1082220163Strasz cred = p->p_ucred; 1083220163Strasz switch (rule->rr_subject_type) { 1084220163Strasz case RCTL_SUBJECT_TYPE_USER: 1085220163Strasz if (cred->cr_uidinfo == rule->rr_subject.rs_uip || 1086220163Strasz cred->cr_ruidinfo == rule->rr_subject.rs_uip) 1087220163Strasz break; 1088220163Strasz continue; 1089220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1090220527Strasz if (cred->cr_loginclass == rule->rr_subject.rs_loginclass) 1091220163Strasz break; 1092220163Strasz continue; 1093220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1094220163Strasz match = 0; 1095220163Strasz for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) { 1096221362Strasz if (pr->pr_prison_racct == rule->rr_subject.rs_prison_racct) { 1097220163Strasz match = 1; 1098220163Strasz break; 1099220163Strasz } 1100220163Strasz } 1101220163Strasz if (match) 1102220163Strasz break; 1103220163Strasz continue; 1104220163Strasz default: 1105220163Strasz panic("rctl_rule_add: unknown subject type %d", 1106220163Strasz rule->rr_subject_type); 1107220163Strasz } 1108220163Strasz 1109220163Strasz rctl_racct_add_rule(p->p_racct, rule); 1110220163Strasz } 1111220163Strasz 1112220163Strasz return (0); 1113220163Strasz} 1114220163Strasz 1115220163Straszstatic void 1116220163Straszrctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) 1117220163Strasz{ 1118220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1119220163Strasz int found = 0; 1120220163Strasz 1121220163Strasz rw_wlock(&rctl_lock); 1122220163Strasz found += rctl_racct_remove_rules(racct, filter); 1123220163Strasz rw_wunlock(&rctl_lock); 1124220163Strasz 1125220163Strasz *((int *)arg3) += found; 1126220163Strasz} 1127220163Strasz 1128220163Strasz/* 1129220163Strasz * Remove all rules that match the filter. 1130220163Strasz */ 1131220163Straszint 1132220163Straszrctl_rule_remove(struct rctl_rule *filter) 1133220163Strasz{ 1134220163Strasz int found = 0; 1135220163Strasz struct proc *p; 1136220163Strasz 1137220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && 1138220163Strasz filter->rr_subject.rs_proc != NULL) { 1139220163Strasz p = filter->rr_subject.rs_proc; 1140220163Strasz rw_wlock(&rctl_lock); 1141220163Strasz found = rctl_racct_remove_rules(p->p_racct, filter); 1142220163Strasz rw_wunlock(&rctl_lock); 1143220163Strasz if (found) 1144220163Strasz return (0); 1145220163Strasz return (ESRCH); 1146220163Strasz } 1147220163Strasz 1148220163Strasz loginclass_racct_foreach(rctl_rule_remove_callback, filter, 1149220163Strasz (void *)&found); 1150220163Strasz ui_racct_foreach(rctl_rule_remove_callback, filter, 1151220163Strasz (void *)&found); 1152220163Strasz prison_racct_foreach(rctl_rule_remove_callback, filter, 1153220163Strasz (void *)&found); 1154220163Strasz 1155220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1156220163Strasz rw_wlock(&rctl_lock); 1157220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1158220163Strasz found += rctl_racct_remove_rules(p->p_racct, filter); 1159220163Strasz } 1160220163Strasz rw_wunlock(&rctl_lock); 1161220163Strasz 1162220163Strasz if (found) 1163220163Strasz return (0); 1164220163Strasz return (ESRCH); 1165220163Strasz} 1166220163Strasz 1167220163Strasz/* 1168220163Strasz * Appends a rule to the sbuf. 1169220163Strasz */ 1170220163Straszstatic void 1171220163Straszrctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule) 1172220163Strasz{ 1173220163Strasz int64_t amount; 1174220163Strasz 1175220163Strasz sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type)); 1176220163Strasz 1177220163Strasz switch (rule->rr_subject_type) { 1178220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1179220163Strasz if (rule->rr_subject.rs_proc == NULL) 1180220163Strasz sbuf_printf(sb, ":"); 1181220163Strasz else 1182220163Strasz sbuf_printf(sb, "%d:", 1183220163Strasz rule->rr_subject.rs_proc->p_pid); 1184220163Strasz break; 1185220163Strasz case RCTL_SUBJECT_TYPE_USER: 1186220163Strasz if (rule->rr_subject.rs_uip == NULL) 1187220163Strasz sbuf_printf(sb, ":"); 1188220163Strasz else 1189220163Strasz sbuf_printf(sb, "%d:", 1190220163Strasz rule->rr_subject.rs_uip->ui_uid); 1191220163Strasz break; 1192220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1193220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 1194220163Strasz sbuf_printf(sb, ":"); 1195220163Strasz else 1196220163Strasz sbuf_printf(sb, "%s:", 1197220527Strasz rule->rr_subject.rs_loginclass->lc_name); 1198220163Strasz break; 1199220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1200221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 1201220163Strasz sbuf_printf(sb, ":"); 1202220163Strasz else 1203220163Strasz sbuf_printf(sb, "%s:", 1204221362Strasz rule->rr_subject.rs_prison_racct->prr_name); 1205220163Strasz break; 1206220163Strasz default: 1207220163Strasz panic("rctl_rule_to_sbuf: unknown subject type %d", 1208220163Strasz rule->rr_subject_type); 1209220163Strasz } 1210220163Strasz 1211220163Strasz amount = rule->rr_amount; 1212220163Strasz if (amount != RCTL_AMOUNT_UNDEFINED && 1213224036Strasz RACCT_IS_IN_MILLIONS(rule->rr_resource)) 1214224036Strasz amount /= 1000000; 1215220163Strasz 1216220163Strasz sbuf_printf(sb, "%s:%s=%jd", 1217220163Strasz rctl_resource_name(rule->rr_resource), 1218220163Strasz rctl_action_name(rule->rr_action), 1219220163Strasz amount); 1220220163Strasz 1221220163Strasz if (rule->rr_per != rule->rr_subject_type) 1222220163Strasz sbuf_printf(sb, "/%s", rctl_subject_type_name(rule->rr_per)); 1223220163Strasz} 1224220163Strasz 1225220163Strasz/* 1226220163Strasz * Routine used by RCTL syscalls to read in input string. 1227220163Strasz */ 1228220163Straszstatic int 1229220163Straszrctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen) 1230220163Strasz{ 1231220163Strasz int error; 1232220163Strasz char *str; 1233220163Strasz 1234220163Strasz if (inbuflen <= 0) 1235220163Strasz return (EINVAL); 1236234380Strasz if (inbuflen > RCTL_MAX_INBUFLEN) 1237234380Strasz return (E2BIG); 1238220163Strasz 1239220163Strasz str = malloc(inbuflen + 1, M_RCTL, M_WAITOK); 1240220163Strasz error = copyinstr(inbufp, str, inbuflen, NULL); 1241220163Strasz if (error != 0) { 1242220163Strasz free(str, M_RCTL); 1243220163Strasz return (error); 1244220163Strasz } 1245220163Strasz 1246220163Strasz *inputstr = str; 1247220163Strasz 1248220163Strasz return (0); 1249220163Strasz} 1250220163Strasz 1251220163Strasz/* 1252220163Strasz * Routine used by RCTL syscalls to write out output string. 1253220163Strasz */ 1254220163Straszstatic int 1255220163Straszrctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen) 1256220163Strasz{ 1257220163Strasz int error; 1258220163Strasz 1259220163Strasz if (outputsbuf == NULL) 1260220163Strasz return (0); 1261220163Strasz 1262220163Strasz sbuf_finish(outputsbuf); 1263220163Strasz if (outbuflen < sbuf_len(outputsbuf) + 1) { 1264220163Strasz sbuf_delete(outputsbuf); 1265220163Strasz return (ERANGE); 1266220163Strasz } 1267220163Strasz error = copyout(sbuf_data(outputsbuf), outbufp, 1268220163Strasz sbuf_len(outputsbuf) + 1); 1269220163Strasz sbuf_delete(outputsbuf); 1270220163Strasz return (error); 1271220163Strasz} 1272220163Strasz 1273220163Straszstatic struct sbuf * 1274220163Straszrctl_racct_to_sbuf(struct racct *racct, int sloppy) 1275220163Strasz{ 1276220163Strasz int i; 1277220163Strasz int64_t amount; 1278220163Strasz struct sbuf *sb; 1279220163Strasz 1280220163Strasz sb = sbuf_new_auto(); 1281220163Strasz for (i = 0; i <= RACCT_MAX; i++) { 1282223844Strasz if (sloppy == 0 && RACCT_IS_SLOPPY(i)) 1283220163Strasz continue; 1284220163Strasz amount = racct->r_resources[i]; 1285224036Strasz if (RACCT_IS_IN_MILLIONS(i)) 1286225371Strasz amount /= 1000000; 1287220163Strasz sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount); 1288220163Strasz } 1289220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1290220163Strasz return (sb); 1291220163Strasz} 1292220163Strasz 1293220163Straszint 1294225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1295220163Strasz{ 1296220163Strasz int error; 1297220163Strasz char *inputstr; 1298220163Strasz struct rctl_rule *filter; 1299220163Strasz struct sbuf *outputsbuf = NULL; 1300220163Strasz struct proc *p; 1301220163Strasz struct uidinfo *uip; 1302220163Strasz struct loginclass *lc; 1303221362Strasz struct prison_racct *prr; 1304220163Strasz 1305220527Strasz error = priv_check(td, PRIV_RCTL_GET_RACCT); 1306220163Strasz if (error != 0) 1307220163Strasz return (error); 1308220163Strasz 1309220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1310220163Strasz if (error != 0) 1311220163Strasz return (error); 1312220163Strasz 1313220163Strasz sx_slock(&allproc_lock); 1314220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1315220163Strasz free(inputstr, M_RCTL); 1316220163Strasz if (error != 0) { 1317220163Strasz sx_sunlock(&allproc_lock); 1318220163Strasz return (error); 1319220163Strasz } 1320220163Strasz 1321220163Strasz switch (filter->rr_subject_type) { 1322220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1323220163Strasz p = filter->rr_subject.rs_proc; 1324220163Strasz if (p == NULL) { 1325220163Strasz error = EINVAL; 1326220163Strasz goto out; 1327220163Strasz } 1328220163Strasz outputsbuf = rctl_racct_to_sbuf(p->p_racct, 0); 1329220163Strasz break; 1330220163Strasz case RCTL_SUBJECT_TYPE_USER: 1331220163Strasz uip = filter->rr_subject.rs_uip; 1332220163Strasz if (uip == NULL) { 1333220163Strasz error = EINVAL; 1334220163Strasz goto out; 1335220163Strasz } 1336220163Strasz outputsbuf = rctl_racct_to_sbuf(uip->ui_racct, 1); 1337220163Strasz break; 1338220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1339220527Strasz lc = filter->rr_subject.rs_loginclass; 1340220163Strasz if (lc == NULL) { 1341220163Strasz error = EINVAL; 1342220163Strasz goto out; 1343220163Strasz } 1344220163Strasz outputsbuf = rctl_racct_to_sbuf(lc->lc_racct, 1); 1345220163Strasz break; 1346220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1347221362Strasz prr = filter->rr_subject.rs_prison_racct; 1348221362Strasz if (prr == NULL) { 1349220163Strasz error = EINVAL; 1350220163Strasz goto out; 1351220163Strasz } 1352221362Strasz outputsbuf = rctl_racct_to_sbuf(prr->prr_racct, 1); 1353220163Strasz break; 1354220163Strasz default: 1355220163Strasz error = EINVAL; 1356220163Strasz } 1357220163Straszout: 1358220163Strasz rctl_rule_release(filter); 1359220163Strasz sx_sunlock(&allproc_lock); 1360220163Strasz if (error != 0) 1361220163Strasz return (error); 1362220163Strasz 1363220163Strasz error = rctl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); 1364220163Strasz 1365220163Strasz return (error); 1366220163Strasz} 1367220163Strasz 1368220163Straszstatic void 1369220163Straszrctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) 1370220163Strasz{ 1371220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1372220163Strasz struct rctl_rule_link *link; 1373220163Strasz struct sbuf *sb = (struct sbuf *)arg3; 1374220163Strasz 1375220163Strasz rw_rlock(&rctl_lock); 1376220163Strasz LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { 1377220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1378220163Strasz continue; 1379220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1380220163Strasz sbuf_printf(sb, ","); 1381220163Strasz } 1382220163Strasz rw_runlock(&rctl_lock); 1383220163Strasz} 1384220163Strasz 1385220163Straszint 1386225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1387220163Strasz{ 1388220163Strasz int error; 1389220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1390220163Strasz char *inputstr, *buf; 1391220163Strasz struct sbuf *sb; 1392220163Strasz struct rctl_rule *filter; 1393220163Strasz struct rctl_rule_link *link; 1394220163Strasz struct proc *p; 1395220163Strasz 1396220163Strasz error = priv_check(td, PRIV_RCTL_GET_RULES); 1397220163Strasz if (error != 0) 1398220163Strasz return (error); 1399220163Strasz 1400220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1401220163Strasz if (error != 0) 1402220163Strasz return (error); 1403220163Strasz 1404220163Strasz sx_slock(&allproc_lock); 1405220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1406220163Strasz free(inputstr, M_RCTL); 1407220163Strasz if (error != 0) { 1408220163Strasz sx_sunlock(&allproc_lock); 1409220163Strasz return (error); 1410220163Strasz } 1411220163Strasz 1412220163Straszagain: 1413220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1414220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1415220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1416220163Strasz 1417220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1418220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1419220163Strasz rw_rlock(&rctl_lock); 1420220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1421220163Strasz /* 1422220163Strasz * Non-process rules will be added to the buffer later. 1423220163Strasz * Adding them here would result in duplicated output. 1424220163Strasz */ 1425220163Strasz if (link->rrl_rule->rr_subject_type != 1426220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1427220163Strasz continue; 1428220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1429220163Strasz continue; 1430220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1431220163Strasz sbuf_printf(sb, ","); 1432220163Strasz } 1433220163Strasz rw_runlock(&rctl_lock); 1434220163Strasz } 1435220163Strasz 1436220163Strasz loginclass_racct_foreach(rctl_get_rules_callback, filter, sb); 1437220163Strasz ui_racct_foreach(rctl_get_rules_callback, filter, sb); 1438220163Strasz prison_racct_foreach(rctl_get_rules_callback, filter, sb); 1439220163Strasz if (sbuf_error(sb) == ENOMEM) { 1440220163Strasz sbuf_delete(sb); 1441220163Strasz free(buf, M_RCTL); 1442220163Strasz bufsize *= 4; 1443220163Strasz goto again; 1444220163Strasz } 1445220163Strasz 1446220163Strasz /* 1447220163Strasz * Remove trailing ",". 1448220163Strasz */ 1449220163Strasz if (sbuf_len(sb) > 0) 1450220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1451220163Strasz 1452220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1453220163Strasz 1454220163Strasz rctl_rule_release(filter); 1455220163Strasz sx_sunlock(&allproc_lock); 1456220163Strasz free(buf, M_RCTL); 1457220163Strasz return (error); 1458220163Strasz} 1459220163Strasz 1460220163Straszint 1461225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1462220163Strasz{ 1463220163Strasz int error; 1464220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1465220163Strasz char *inputstr, *buf; 1466220163Strasz struct sbuf *sb; 1467220163Strasz struct rctl_rule *filter; 1468220163Strasz struct rctl_rule_link *link; 1469220163Strasz 1470220163Strasz error = priv_check(td, PRIV_RCTL_GET_LIMITS); 1471220163Strasz if (error != 0) 1472220163Strasz return (error); 1473220163Strasz 1474220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1475220163Strasz if (error != 0) 1476220163Strasz return (error); 1477220163Strasz 1478220163Strasz sx_slock(&allproc_lock); 1479220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1480220163Strasz free(inputstr, M_RCTL); 1481220163Strasz if (error != 0) { 1482220163Strasz sx_sunlock(&allproc_lock); 1483220163Strasz return (error); 1484220163Strasz } 1485220163Strasz 1486220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_UNDEFINED) { 1487220163Strasz rctl_rule_release(filter); 1488220163Strasz sx_sunlock(&allproc_lock); 1489220163Strasz return (EINVAL); 1490220163Strasz } 1491220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_PROCESS) { 1492220163Strasz rctl_rule_release(filter); 1493220163Strasz sx_sunlock(&allproc_lock); 1494220163Strasz return (EOPNOTSUPP); 1495220163Strasz } 1496220163Strasz if (filter->rr_subject.rs_proc == NULL) { 1497220163Strasz rctl_rule_release(filter); 1498220163Strasz sx_sunlock(&allproc_lock); 1499220163Strasz return (EINVAL); 1500220163Strasz } 1501220163Strasz 1502220163Straszagain: 1503220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1504220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1505220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1506220163Strasz 1507220163Strasz rw_rlock(&rctl_lock); 1508220163Strasz LIST_FOREACH(link, &filter->rr_subject.rs_proc->p_racct->r_rule_links, 1509220163Strasz rrl_next) { 1510220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1511220163Strasz sbuf_printf(sb, ","); 1512220163Strasz } 1513220163Strasz rw_runlock(&rctl_lock); 1514220163Strasz if (sbuf_error(sb) == ENOMEM) { 1515220163Strasz sbuf_delete(sb); 1516220163Strasz free(buf, M_RCTL); 1517220163Strasz bufsize *= 4; 1518220163Strasz goto again; 1519220163Strasz } 1520220163Strasz 1521220163Strasz /* 1522220163Strasz * Remove trailing ",". 1523220163Strasz */ 1524220163Strasz if (sbuf_len(sb) > 0) 1525220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1526220163Strasz 1527220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1528220163Strasz rctl_rule_release(filter); 1529220163Strasz sx_sunlock(&allproc_lock); 1530220163Strasz free(buf, M_RCTL); 1531220163Strasz return (error); 1532220163Strasz} 1533220163Strasz 1534220163Straszint 1535225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1536220163Strasz{ 1537220163Strasz int error; 1538220163Strasz struct rctl_rule *rule; 1539220163Strasz char *inputstr; 1540220163Strasz 1541220163Strasz error = priv_check(td, PRIV_RCTL_ADD_RULE); 1542220163Strasz if (error != 0) 1543220163Strasz return (error); 1544220163Strasz 1545220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1546220163Strasz if (error != 0) 1547220163Strasz return (error); 1548220163Strasz 1549220163Strasz sx_slock(&allproc_lock); 1550220163Strasz error = rctl_string_to_rule(inputstr, &rule); 1551220163Strasz free(inputstr, M_RCTL); 1552220163Strasz if (error != 0) { 1553220163Strasz sx_sunlock(&allproc_lock); 1554220163Strasz return (error); 1555220163Strasz } 1556220163Strasz /* 1557220163Strasz * The 'per' part of a rule is optional. 1558220163Strasz */ 1559220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED && 1560220163Strasz rule->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) 1561220163Strasz rule->rr_per = rule->rr_subject_type; 1562220163Strasz 1563220163Strasz if (!rctl_rule_fully_specified(rule)) { 1564220163Strasz error = EINVAL; 1565220163Strasz goto out; 1566220163Strasz } 1567220163Strasz 1568220163Strasz error = rctl_rule_add(rule); 1569220163Strasz 1570220163Straszout: 1571220163Strasz rctl_rule_release(rule); 1572220163Strasz sx_sunlock(&allproc_lock); 1573220163Strasz return (error); 1574220163Strasz} 1575220163Strasz 1576220163Straszint 1577225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1578220163Strasz{ 1579220163Strasz int error; 1580220163Strasz struct rctl_rule *filter; 1581220163Strasz char *inputstr; 1582220163Strasz 1583220163Strasz error = priv_check(td, PRIV_RCTL_REMOVE_RULE); 1584220163Strasz if (error != 0) 1585220163Strasz return (error); 1586220163Strasz 1587220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1588220163Strasz if (error != 0) 1589220163Strasz return (error); 1590220163Strasz 1591220163Strasz sx_slock(&allproc_lock); 1592220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1593220163Strasz free(inputstr, M_RCTL); 1594220163Strasz if (error != 0) { 1595220163Strasz sx_sunlock(&allproc_lock); 1596220163Strasz return (error); 1597220163Strasz } 1598220163Strasz 1599220163Strasz error = rctl_rule_remove(filter); 1600220163Strasz rctl_rule_release(filter); 1601220163Strasz sx_sunlock(&allproc_lock); 1602220163Strasz 1603220163Strasz return (error); 1604220163Strasz} 1605220163Strasz 1606220163Strasz/* 1607220163Strasz * Update RCTL rule list after credential change. 1608220163Strasz */ 1609220163Straszvoid 1610220163Straszrctl_proc_ucred_changed(struct proc *p, struct ucred *newcred) 1611220163Strasz{ 1612220163Strasz int rulecnt, i; 1613220163Strasz struct rctl_rule_link *link, *newlink; 1614220163Strasz struct uidinfo *newuip; 1615220163Strasz struct loginclass *newlc; 1616221362Strasz struct prison_racct *newprr; 1617220163Strasz LIST_HEAD(, rctl_rule_link) newrules; 1618220163Strasz 1619220163Strasz newuip = newcred->cr_ruidinfo; 1620220163Strasz newlc = newcred->cr_loginclass; 1621221362Strasz newprr = newcred->cr_prison->pr_prison_racct; 1622220163Strasz 1623220163Strasz LIST_INIT(&newrules); 1624220163Strasz 1625220163Straszagain: 1626220163Strasz /* 1627220163Strasz * First, count the rules that apply to the process with new 1628220163Strasz * credentials. 1629220163Strasz */ 1630220163Strasz rulecnt = 0; 1631220163Strasz rw_rlock(&rctl_lock); 1632220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1633220163Strasz if (link->rrl_rule->rr_subject_type == 1634220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1635220163Strasz rulecnt++; 1636220163Strasz } 1637220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) 1638220163Strasz rulecnt++; 1639220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) 1640220163Strasz rulecnt++; 1641221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) 1642220163Strasz rulecnt++; 1643220163Strasz rw_runlock(&rctl_lock); 1644220163Strasz 1645220163Strasz /* 1646220163Strasz * Create temporary list. We've dropped the rctl_lock in order 1647220163Strasz * to use M_WAITOK. 1648220163Strasz */ 1649220163Strasz for (i = 0; i < rulecnt; i++) { 1650220163Strasz newlink = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 1651220163Strasz newlink->rrl_rule = NULL; 1652220163Strasz LIST_INSERT_HEAD(&newrules, newlink, rrl_next); 1653220163Strasz } 1654220163Strasz 1655220163Strasz newlink = LIST_FIRST(&newrules); 1656220163Strasz 1657220163Strasz /* 1658220163Strasz * Assign rules to the newly allocated list entries. 1659220163Strasz */ 1660220163Strasz rw_wlock(&rctl_lock); 1661220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1662220163Strasz if (link->rrl_rule->rr_subject_type == 1663220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1664220163Strasz if (newlink == NULL) 1665220163Strasz goto goaround; 1666220163Strasz rctl_rule_acquire(link->rrl_rule); 1667220163Strasz newlink->rrl_rule = link->rrl_rule; 1668220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1669220163Strasz rulecnt--; 1670220163Strasz } 1671220163Strasz } 1672220163Strasz 1673220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) { 1674220163Strasz if (newlink == NULL) 1675220163Strasz goto goaround; 1676220163Strasz rctl_rule_acquire(link->rrl_rule); 1677220163Strasz newlink->rrl_rule = link->rrl_rule; 1678220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1679220163Strasz rulecnt--; 1680220163Strasz } 1681220163Strasz 1682220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) { 1683220163Strasz if (newlink == NULL) 1684220163Strasz goto goaround; 1685220163Strasz rctl_rule_acquire(link->rrl_rule); 1686220163Strasz newlink->rrl_rule = link->rrl_rule; 1687220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1688220163Strasz rulecnt--; 1689220163Strasz } 1690220163Strasz 1691221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) { 1692220163Strasz if (newlink == NULL) 1693220163Strasz goto goaround; 1694220163Strasz rctl_rule_acquire(link->rrl_rule); 1695220163Strasz newlink->rrl_rule = link->rrl_rule; 1696220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1697220163Strasz rulecnt--; 1698220163Strasz } 1699220163Strasz 1700220163Strasz if (rulecnt == 0) { 1701220163Strasz /* 1702220163Strasz * Free the old rule list. 1703220163Strasz */ 1704220163Strasz while (!LIST_EMPTY(&p->p_racct->r_rule_links)) { 1705220163Strasz link = LIST_FIRST(&p->p_racct->r_rule_links); 1706220163Strasz LIST_REMOVE(link, rrl_next); 1707220163Strasz rctl_rule_release(link->rrl_rule); 1708220163Strasz uma_zfree(rctl_rule_link_zone, link); 1709220163Strasz } 1710220163Strasz 1711220163Strasz /* 1712220163Strasz * Replace lists and we're done. 1713220163Strasz * 1714220163Strasz * XXX: Is there any way to switch list heads instead 1715220163Strasz * of iterating here? 1716220163Strasz */ 1717220163Strasz while (!LIST_EMPTY(&newrules)) { 1718220163Strasz newlink = LIST_FIRST(&newrules); 1719220163Strasz LIST_REMOVE(newlink, rrl_next); 1720220163Strasz LIST_INSERT_HEAD(&p->p_racct->r_rule_links, 1721220163Strasz newlink, rrl_next); 1722220163Strasz } 1723220163Strasz 1724220163Strasz rw_wunlock(&rctl_lock); 1725220163Strasz 1726220163Strasz return; 1727220163Strasz } 1728220163Strasz 1729220163Straszgoaround: 1730220163Strasz rw_wunlock(&rctl_lock); 1731220163Strasz 1732220163Strasz /* 1733220163Strasz * Rule list changed while we were not holding the rctl_lock. 1734220163Strasz * Free the new list and try again. 1735220163Strasz */ 1736220163Strasz while (!LIST_EMPTY(&newrules)) { 1737220163Strasz newlink = LIST_FIRST(&newrules); 1738220163Strasz LIST_REMOVE(newlink, rrl_next); 1739220163Strasz if (newlink->rrl_rule != NULL) 1740220163Strasz rctl_rule_release(newlink->rrl_rule); 1741220163Strasz uma_zfree(rctl_rule_link_zone, newlink); 1742220163Strasz } 1743220163Strasz 1744220163Strasz goto again; 1745220163Strasz} 1746220163Strasz 1747220163Strasz/* 1748220163Strasz * Assign RCTL rules to the newly created process. 1749220163Strasz */ 1750220163Straszint 1751220163Straszrctl_proc_fork(struct proc *parent, struct proc *child) 1752220163Strasz{ 1753220163Strasz int error; 1754220163Strasz struct rctl_rule_link *link; 1755220163Strasz struct rctl_rule *rule; 1756220163Strasz 1757220163Strasz LIST_INIT(&child->p_racct->r_rule_links); 1758220163Strasz 1759234383Strasz KASSERT(parent->p_racct != NULL, ("process without racct; p = %p", parent)); 1760220163Strasz 1761220163Strasz rw_wlock(&rctl_lock); 1762220163Strasz 1763220163Strasz /* 1764220163Strasz * Go through limits applicable to the parent and assign them 1765220163Strasz * to the child. Rules with 'process' subject have to be duplicated 1766220163Strasz * in order to make their rr_subject point to the new process. 1767220163Strasz */ 1768220163Strasz LIST_FOREACH(link, &parent->p_racct->r_rule_links, rrl_next) { 1769220163Strasz if (link->rrl_rule->rr_subject_type == 1770220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1771220163Strasz rule = rctl_rule_duplicate(link->rrl_rule, M_NOWAIT); 1772220163Strasz if (rule == NULL) 1773220163Strasz goto fail; 1774220163Strasz KASSERT(rule->rr_subject.rs_proc == parent, 1775220163Strasz ("rule->rr_subject.rs_proc != parent")); 1776220163Strasz rule->rr_subject.rs_proc = child; 1777220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1778220163Strasz rule); 1779220163Strasz rctl_rule_release(rule); 1780220163Strasz if (error != 0) 1781220163Strasz goto fail; 1782220163Strasz } else { 1783220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1784220163Strasz link->rrl_rule); 1785220163Strasz if (error != 0) 1786220163Strasz goto fail; 1787220163Strasz } 1788220163Strasz } 1789220163Strasz 1790220163Strasz rw_wunlock(&rctl_lock); 1791220163Strasz return (0); 1792220163Strasz 1793220163Straszfail: 1794220163Strasz while (!LIST_EMPTY(&child->p_racct->r_rule_links)) { 1795220163Strasz link = LIST_FIRST(&child->p_racct->r_rule_links); 1796220163Strasz LIST_REMOVE(link, rrl_next); 1797220163Strasz rctl_rule_release(link->rrl_rule); 1798220163Strasz uma_zfree(rctl_rule_link_zone, link); 1799220163Strasz } 1800220163Strasz rw_wunlock(&rctl_lock); 1801220163Strasz return (EAGAIN); 1802220163Strasz} 1803220163Strasz 1804220163Strasz/* 1805220163Strasz * Release rules attached to the racct. 1806220163Strasz */ 1807220163Straszvoid 1808220163Straszrctl_racct_release(struct racct *racct) 1809220163Strasz{ 1810220163Strasz struct rctl_rule_link *link; 1811220163Strasz 1812220163Strasz rw_wlock(&rctl_lock); 1813220163Strasz while (!LIST_EMPTY(&racct->r_rule_links)) { 1814220163Strasz link = LIST_FIRST(&racct->r_rule_links); 1815220163Strasz LIST_REMOVE(link, rrl_next); 1816220163Strasz rctl_rule_release(link->rrl_rule); 1817220163Strasz uma_zfree(rctl_rule_link_zone, link); 1818220163Strasz } 1819220163Strasz rw_wunlock(&rctl_lock); 1820220163Strasz} 1821220163Strasz 1822220163Straszstatic void 1823220163Straszrctl_init(void) 1824220163Strasz{ 1825220163Strasz 1826220163Strasz rctl_rule_link_zone = uma_zcreate("rctl_rule_link", 1827220163Strasz sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL, 1828220163Strasz UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1829220163Strasz rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule), 1830220163Strasz NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1831220163Strasz} 1832220163Strasz 1833220163Strasz#else /* !RCTL */ 1834220163Strasz 1835220163Straszint 1836225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1837220163Strasz{ 1838220163Strasz 1839220163Strasz return (ENOSYS); 1840220163Strasz} 1841220163Strasz 1842220163Straszint 1843225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1844220163Strasz{ 1845220163Strasz 1846220163Strasz return (ENOSYS); 1847220163Strasz} 1848220163Strasz 1849220163Straszint 1850225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1851220163Strasz{ 1852220163Strasz 1853220163Strasz return (ENOSYS); 1854220163Strasz} 1855220163Strasz 1856220163Straszint 1857225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1858220163Strasz{ 1859220163Strasz 1860220163Strasz return (ENOSYS); 1861220163Strasz} 1862220163Strasz 1863220163Straszint 1864225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1865220163Strasz{ 1866220163Strasz 1867220163Strasz return (ENOSYS); 1868220163Strasz} 1869220163Strasz 1870220163Strasz#endif /* !RCTL */ 1871