kern_rctl.c revision 223844
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 223844 2011-07-07 17:44:42Z trasz $ 30220163Strasz */ 31220163Strasz 32220163Strasz#include <sys/cdefs.h> 33220163Strasz__FBSDID("$FreeBSD: head/sys/kern/kern_rctl.c 223844 2011-07-07 17:44:42Z trasz $"); 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[] = { 102220163Strasz { "cpu", RACCT_CPU }, 103220163Strasz { "data", RACCT_DATA }, 104220163Strasz { "stack", RACCT_STACK }, 105220163Strasz { "core", RACCT_CORE }, 106220163Strasz { "rss", RACCT_RSS }, 107220163Strasz { "memlock", RACCT_MEMLOCK }, 108220163Strasz { "nproc", RACCT_NPROC }, 109220163Strasz { "nofile", RACCT_NOFILE }, 110220163Strasz { "vmem", RACCT_VMEM }, 111220163Strasz { "npts", RACCT_NPTS }, 112220163Strasz { "swap", 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 172220163StraszMALLOC_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 315220163Strasz if (!ppsratecheck(&lasttime, &curtime, 10)) 316220163Strasz continue; 317220163Strasz 318220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 319220163Strasz if (buf == NULL) { 320220163Strasz printf("rctl_enforce: out of memory\n"); 321220163Strasz continue; 322220163Strasz } 323220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 324220163Strasz rctl_rule_to_sbuf(&sb, rule); 325220163Strasz sbuf_finish(&sb); 326220163Strasz printf("rctl: rule \"%s\" matched by pid %d " 327220163Strasz "(%s), uid %d, jail %s\n", sbuf_data(&sb), 328220163Strasz p->p_pid, p->p_comm, p->p_ucred->cr_uid, 329221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 330220163Strasz sbuf_delete(&sb); 331220163Strasz free(buf, M_RCTL); 332220163Strasz link->rrl_exceeded = 1; 333220163Strasz continue; 334220163Strasz case RCTL_ACTION_DEVCTL: 335220163Strasz if (link->rrl_exceeded != 0) 336220163Strasz continue; 337220163Strasz 338220163Strasz buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); 339220163Strasz if (buf == NULL) { 340220163Strasz printf("rctl_enforce: out of memory\n"); 341220163Strasz continue; 342220163Strasz } 343220163Strasz sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); 344220163Strasz sbuf_printf(&sb, "rule="); 345220163Strasz rctl_rule_to_sbuf(&sb, rule); 346220163Strasz sbuf_printf(&sb, " pid=%d ruid=%d jail=%s", 347220163Strasz p->p_pid, p->p_ucred->cr_ruid, 348221362Strasz p->p_ucred->cr_prison->pr_prison_racct->prr_name); 349220163Strasz sbuf_finish(&sb); 350220163Strasz devctl_notify_f("RCTL", "rule", "matched", 351220163Strasz sbuf_data(&sb), M_NOWAIT); 352220163Strasz sbuf_delete(&sb); 353220163Strasz free(buf, M_RCTL); 354220163Strasz link->rrl_exceeded = 1; 355220163Strasz continue; 356220163Strasz default: 357220163Strasz if (link->rrl_exceeded != 0) 358220163Strasz continue; 359220163Strasz 360220163Strasz KASSERT(rule->rr_action > 0 && 361220163Strasz rule->rr_action <= RCTL_ACTION_SIGNAL_MAX, 362220163Strasz ("rctl_enforce: unknown action %d", 363220163Strasz rule->rr_action)); 364220163Strasz 365220163Strasz /* 366220163Strasz * We're using the fact that RCTL_ACTION_SIG* values 367220163Strasz * are equal to their counterparts from sys/signal.h. 368220163Strasz */ 369220163Strasz psignal(p, rule->rr_action); 370220163Strasz link->rrl_exceeded = 1; 371220163Strasz continue; 372220163Strasz } 373220163Strasz } 374220163Strasz 375220163Strasz rw_runlock(&rctl_lock); 376220163Strasz 377220163Strasz if (should_deny) { 378220163Strasz /* 379220163Strasz * Return fake error code; the caller should change it 380220163Strasz * into one proper for the situation - EFSIZ, ENOMEM etc. 381220163Strasz */ 382220163Strasz return (EDOOFUS); 383220163Strasz } 384220163Strasz 385220163Strasz return (0); 386220163Strasz} 387220163Strasz 388220163Straszuint64_t 389220163Straszrctl_get_limit(struct proc *p, int resource) 390220163Strasz{ 391220163Strasz struct rctl_rule *rule; 392220163Strasz struct rctl_rule_link *link; 393220163Strasz uint64_t amount = UINT64_MAX; 394220163Strasz 395220163Strasz rw_rlock(&rctl_lock); 396220163Strasz 397220163Strasz /* 398220163Strasz * There may be more than one matching rule; go through all of them. 399220163Strasz * Denial should be done last, after logging and sending signals. 400220163Strasz */ 401220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 402220163Strasz rule = link->rrl_rule; 403220163Strasz if (rule->rr_resource != resource) 404220163Strasz continue; 405220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 406220163Strasz continue; 407220163Strasz if (rule->rr_amount < amount) 408220163Strasz amount = rule->rr_amount; 409220163Strasz } 410220163Strasz 411220163Strasz rw_runlock(&rctl_lock); 412220163Strasz 413220163Strasz return (amount); 414220163Strasz} 415220163Strasz 416220163Straszuint64_t 417220163Straszrctl_get_available(struct proc *p, int resource) 418220163Strasz{ 419220163Strasz struct rctl_rule *rule; 420220163Strasz struct rctl_rule_link *link; 421220163Strasz int64_t available, minavailable, allocated; 422220163Strasz 423220163Strasz minavailable = INT64_MAX; 424220163Strasz 425220163Strasz rw_rlock(&rctl_lock); 426220163Strasz 427220163Strasz /* 428220163Strasz * There may be more than one matching rule; go through all of them. 429220163Strasz * Denial should be done last, after logging and sending signals. 430220163Strasz */ 431220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 432220163Strasz rule = link->rrl_rule; 433220163Strasz if (rule->rr_resource != resource) 434220163Strasz continue; 435220163Strasz if (rule->rr_action != RCTL_ACTION_DENY) 436220163Strasz continue; 437220163Strasz available = rctl_available_resource(p, rule); 438220163Strasz if (available < minavailable) 439220163Strasz minavailable = available; 440220163Strasz } 441220163Strasz 442220163Strasz rw_runlock(&rctl_lock); 443220163Strasz 444220163Strasz /* 445220163Strasz * XXX: Think about this _hard_. 446220163Strasz */ 447220163Strasz allocated = p->p_racct->r_resources[resource]; 448220163Strasz if (minavailable < INT64_MAX - allocated) 449220163Strasz minavailable += allocated; 450220163Strasz if (minavailable < 0) 451220163Strasz minavailable = 0; 452220163Strasz return (minavailable); 453220163Strasz} 454220163Strasz 455220163Straszstatic int 456220163Straszrctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter) 457220163Strasz{ 458220163Strasz 459220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) { 460220163Strasz if (rule->rr_subject_type != filter->rr_subject_type) 461220163Strasz return (0); 462220163Strasz 463220163Strasz switch (filter->rr_subject_type) { 464220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 465220163Strasz if (filter->rr_subject.rs_proc != NULL && 466220163Strasz rule->rr_subject.rs_proc != 467220163Strasz filter->rr_subject.rs_proc) 468220163Strasz return (0); 469220163Strasz break; 470220163Strasz case RCTL_SUBJECT_TYPE_USER: 471220163Strasz if (filter->rr_subject.rs_uip != NULL && 472220163Strasz rule->rr_subject.rs_uip != 473220163Strasz filter->rr_subject.rs_uip) 474220163Strasz return (0); 475220163Strasz break; 476220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 477220527Strasz if (filter->rr_subject.rs_loginclass != NULL && 478220527Strasz rule->rr_subject.rs_loginclass != 479220527Strasz filter->rr_subject.rs_loginclass) 480220163Strasz return (0); 481220163Strasz break; 482220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 483221362Strasz if (filter->rr_subject.rs_prison_racct != NULL && 484221362Strasz rule->rr_subject.rs_prison_racct != 485221362Strasz filter->rr_subject.rs_prison_racct) 486220163Strasz return (0); 487220163Strasz break; 488220163Strasz default: 489220163Strasz panic("rctl_rule_matches: unknown subject type %d", 490220163Strasz filter->rr_subject_type); 491220163Strasz } 492220163Strasz } 493220163Strasz 494220163Strasz if (filter->rr_resource != RACCT_UNDEFINED) { 495220163Strasz if (rule->rr_resource != filter->rr_resource) 496220163Strasz return (0); 497220163Strasz } 498220163Strasz 499220163Strasz if (filter->rr_action != RCTL_ACTION_UNDEFINED) { 500220163Strasz if (rule->rr_action != filter->rr_action) 501220163Strasz return (0); 502220163Strasz } 503220163Strasz 504220163Strasz if (filter->rr_amount != RCTL_AMOUNT_UNDEFINED) { 505220163Strasz if (rule->rr_amount != filter->rr_amount) 506220163Strasz return (0); 507220163Strasz } 508220163Strasz 509220163Strasz if (filter->rr_per != RCTL_SUBJECT_TYPE_UNDEFINED) { 510220163Strasz if (rule->rr_per != filter->rr_per) 511220163Strasz return (0); 512220163Strasz } 513220163Strasz 514220163Strasz return (1); 515220163Strasz} 516220163Strasz 517220163Straszstatic int 518220163Straszstr2value(const char *str, int *value, struct dict *table) 519220163Strasz{ 520220163Strasz int i; 521220163Strasz 522220163Strasz if (value == NULL) 523220163Strasz return (EINVAL); 524220163Strasz 525220163Strasz for (i = 0; table[i].d_name != NULL; i++) { 526220163Strasz if (strcasecmp(table[i].d_name, str) == 0) { 527220163Strasz *value = table[i].d_value; 528220163Strasz return (0); 529220163Strasz } 530220163Strasz } 531220163Strasz 532220163Strasz return (EINVAL); 533220163Strasz} 534220163Strasz 535220163Straszstatic int 536220163Straszstr2id(const char *str, id_t *value) 537220163Strasz{ 538220163Strasz char *end; 539220163Strasz 540220163Strasz if (str == NULL) 541220163Strasz return (EINVAL); 542220163Strasz 543220163Strasz *value = strtoul(str, &end, 10); 544220163Strasz if ((size_t)(end - str) != strlen(str)) 545220163Strasz return (EINVAL); 546220163Strasz 547220163Strasz return (0); 548220163Strasz} 549220163Strasz 550220163Straszstatic int 551220163Straszstr2int64(const char *str, int64_t *value) 552220163Strasz{ 553220163Strasz char *end; 554220163Strasz 555220163Strasz if (str == NULL) 556220163Strasz return (EINVAL); 557220163Strasz 558220163Strasz *value = strtoul(str, &end, 10); 559220163Strasz if ((size_t)(end - str) != strlen(str)) 560220163Strasz return (EINVAL); 561220163Strasz 562220163Strasz return (0); 563220163Strasz} 564220163Strasz 565220163Strasz/* 566220163Strasz * Connect the rule to the racct, increasing refcount for the rule. 567220163Strasz */ 568220163Straszstatic void 569220163Straszrctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) 570220163Strasz{ 571220163Strasz struct rctl_rule_link *link; 572220163Strasz 573220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 574220163Strasz 575220163Strasz rctl_rule_acquire(rule); 576220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 577220163Strasz link->rrl_rule = rule; 578220163Strasz link->rrl_exceeded = 0; 579220163Strasz 580220163Strasz rw_wlock(&rctl_lock); 581220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 582220163Strasz rw_wunlock(&rctl_lock); 583220163Strasz} 584220163Strasz 585220163Straszstatic int 586220163Straszrctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) 587220163Strasz{ 588220163Strasz struct rctl_rule_link *link; 589220163Strasz 590220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 591220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 592220163Strasz 593220163Strasz link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); 594220163Strasz if (link == NULL) 595220163Strasz return (ENOMEM); 596220163Strasz rctl_rule_acquire(rule); 597220163Strasz link->rrl_rule = rule; 598220163Strasz link->rrl_exceeded = 0; 599220163Strasz 600220163Strasz LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); 601220163Strasz return (0); 602220163Strasz} 603220163Strasz 604220163Strasz/* 605220163Strasz * Remove limits for a rules matching the filter and release 606220163Strasz * the refcounts for the rules, possibly freeing them. Returns 607220163Strasz * the number of limit structures removed. 608220163Strasz */ 609220163Straszstatic int 610220163Straszrctl_racct_remove_rules(struct racct *racct, 611220163Strasz const struct rctl_rule *filter) 612220163Strasz{ 613220163Strasz int removed = 0; 614220163Strasz struct rctl_rule_link *link, *linktmp; 615220163Strasz 616220163Strasz rw_assert(&rctl_lock, RA_WLOCKED); 617220163Strasz 618220163Strasz LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { 619220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 620220163Strasz continue; 621220163Strasz 622220163Strasz LIST_REMOVE(link, rrl_next); 623220163Strasz rctl_rule_release(link->rrl_rule); 624220163Strasz uma_zfree(rctl_rule_link_zone, link); 625220163Strasz removed++; 626220163Strasz } 627220163Strasz return (removed); 628220163Strasz} 629220163Strasz 630220163Straszstatic void 631220163Straszrctl_rule_acquire_subject(struct rctl_rule *rule) 632220163Strasz{ 633220163Strasz 634220163Strasz switch (rule->rr_subject_type) { 635220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 636220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 637221362Strasz break; 638220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 639221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 640221362Strasz prison_racct_hold(rule->rr_subject.rs_prison_racct); 641220163Strasz break; 642220163Strasz case RCTL_SUBJECT_TYPE_USER: 643220163Strasz if (rule->rr_subject.rs_uip != NULL) 644220163Strasz uihold(rule->rr_subject.rs_uip); 645220163Strasz break; 646220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 647220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 648220527Strasz loginclass_hold(rule->rr_subject.rs_loginclass); 649220163Strasz break; 650220163Strasz default: 651220163Strasz panic("rctl_rule_acquire_subject: unknown subject type %d", 652220163Strasz rule->rr_subject_type); 653220163Strasz } 654220163Strasz} 655220163Strasz 656220163Straszstatic void 657220163Straszrctl_rule_release_subject(struct rctl_rule *rule) 658220163Strasz{ 659220163Strasz 660220163Strasz switch (rule->rr_subject_type) { 661220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 662220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 663221362Strasz break; 664220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 665221362Strasz if (rule->rr_subject.rs_prison_racct != NULL) 666221362Strasz prison_racct_free(rule->rr_subject.rs_prison_racct); 667220163Strasz break; 668220163Strasz case RCTL_SUBJECT_TYPE_USER: 669220163Strasz if (rule->rr_subject.rs_uip != NULL) 670220163Strasz uifree(rule->rr_subject.rs_uip); 671220163Strasz break; 672220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 673220527Strasz if (rule->rr_subject.rs_loginclass != NULL) 674220527Strasz loginclass_free(rule->rr_subject.rs_loginclass); 675220163Strasz break; 676220163Strasz default: 677220163Strasz panic("rctl_rule_release_subject: unknown subject type %d", 678220163Strasz rule->rr_subject_type); 679220163Strasz } 680220163Strasz} 681220163Strasz 682220163Straszstruct rctl_rule * 683220163Straszrctl_rule_alloc(int flags) 684220163Strasz{ 685220163Strasz struct rctl_rule *rule; 686220163Strasz 687220163Strasz rule = uma_zalloc(rctl_rule_zone, flags); 688220163Strasz if (rule == NULL) 689220163Strasz return (NULL); 690220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 691220163Strasz rule->rr_subject.rs_proc = NULL; 692220163Strasz rule->rr_subject.rs_uip = NULL; 693220527Strasz rule->rr_subject.rs_loginclass = NULL; 694221362Strasz rule->rr_subject.rs_prison_racct = NULL; 695220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 696220163Strasz rule->rr_resource = RACCT_UNDEFINED; 697220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 698220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 699220163Strasz refcount_init(&rule->rr_refcount, 1); 700220163Strasz 701220163Strasz return (rule); 702220163Strasz} 703220163Strasz 704220163Straszstruct rctl_rule * 705220163Straszrctl_rule_duplicate(const struct rctl_rule *rule, int flags) 706220163Strasz{ 707220163Strasz struct rctl_rule *copy; 708220163Strasz 709220163Strasz copy = uma_zalloc(rctl_rule_zone, flags); 710220163Strasz if (copy == NULL) 711220163Strasz return (NULL); 712220163Strasz copy->rr_subject_type = rule->rr_subject_type; 713220163Strasz copy->rr_subject.rs_proc = rule->rr_subject.rs_proc; 714220163Strasz copy->rr_subject.rs_uip = rule->rr_subject.rs_uip; 715220527Strasz copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass; 716221362Strasz copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct; 717220163Strasz copy->rr_per = rule->rr_per; 718220163Strasz copy->rr_resource = rule->rr_resource; 719220163Strasz copy->rr_action = rule->rr_action; 720220163Strasz copy->rr_amount = rule->rr_amount; 721220163Strasz refcount_init(©->rr_refcount, 1); 722220163Strasz rctl_rule_acquire_subject(copy); 723220163Strasz 724220163Strasz return (copy); 725220163Strasz} 726220163Strasz 727220163Straszvoid 728220163Straszrctl_rule_acquire(struct rctl_rule *rule) 729220163Strasz{ 730220163Strasz 731220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 732220163Strasz 733220163Strasz refcount_acquire(&rule->rr_refcount); 734220163Strasz} 735220163Strasz 736220163Straszstatic void 737220163Straszrctl_rule_free(void *context, int pending) 738220163Strasz{ 739220163Strasz struct rctl_rule *rule; 740220163Strasz 741220163Strasz rule = (struct rctl_rule *)context; 742220163Strasz 743220163Strasz KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0")); 744220163Strasz 745220163Strasz /* 746220163Strasz * We don't need locking here; rule is guaranteed to be inaccessible. 747220163Strasz */ 748220163Strasz 749220163Strasz rctl_rule_release_subject(rule); 750220163Strasz uma_zfree(rctl_rule_zone, rule); 751220163Strasz} 752220163Strasz 753220163Straszvoid 754220163Straszrctl_rule_release(struct rctl_rule *rule) 755220163Strasz{ 756220163Strasz 757220163Strasz KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); 758220163Strasz 759220163Strasz if (refcount_release(&rule->rr_refcount)) { 760220163Strasz /* 761220163Strasz * rctl_rule_release() is often called when iterating 762220163Strasz * over all the uidinfo structures in the system, 763220163Strasz * holding uihashtbl_lock. Since rctl_rule_free() 764220163Strasz * might end up calling uifree(), this would lead 765220163Strasz * to lock recursion. Use taskqueue to avoid this. 766220163Strasz */ 767220163Strasz TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule); 768220163Strasz taskqueue_enqueue(taskqueue_thread, &rule->rr_task); 769220163Strasz } 770220163Strasz} 771220163Strasz 772220163Straszstatic int 773220163Straszrctl_rule_fully_specified(const struct rctl_rule *rule) 774220163Strasz{ 775220163Strasz 776220163Strasz switch (rule->rr_subject_type) { 777220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 778220163Strasz return (0); 779220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 780220163Strasz if (rule->rr_subject.rs_proc == NULL) 781220163Strasz return (0); 782220163Strasz break; 783220163Strasz case RCTL_SUBJECT_TYPE_USER: 784220163Strasz if (rule->rr_subject.rs_uip == NULL) 785220163Strasz return (0); 786220163Strasz break; 787220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 788220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 789220163Strasz return (0); 790220163Strasz break; 791220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 792221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 793220163Strasz return (0); 794220163Strasz break; 795220163Strasz default: 796220163Strasz panic("rctl_rule_fully_specified: unknown subject type %d", 797220163Strasz rule->rr_subject_type); 798220163Strasz } 799220163Strasz if (rule->rr_resource == RACCT_UNDEFINED) 800220163Strasz return (0); 801220163Strasz if (rule->rr_action == RCTL_ACTION_UNDEFINED) 802220163Strasz return (0); 803220163Strasz if (rule->rr_amount == RCTL_AMOUNT_UNDEFINED) 804220163Strasz return (0); 805220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED) 806220163Strasz return (0); 807220163Strasz 808220163Strasz return (1); 809220163Strasz} 810220163Strasz 811220163Straszstatic int 812220163Straszrctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) 813220163Strasz{ 814220163Strasz int error = 0; 815220163Strasz char *subjectstr, *subject_idstr, *resourcestr, *actionstr, 816220163Strasz *amountstr, *perstr; 817220163Strasz struct rctl_rule *rule; 818220163Strasz id_t id; 819220163Strasz 820220163Strasz rule = rctl_rule_alloc(M_WAITOK); 821220163Strasz 822220163Strasz subjectstr = strsep(&rulestr, ":"); 823220163Strasz subject_idstr = strsep(&rulestr, ":"); 824220163Strasz resourcestr = strsep(&rulestr, ":"); 825220163Strasz actionstr = strsep(&rulestr, "=/"); 826220163Strasz amountstr = strsep(&rulestr, "/"); 827220163Strasz perstr = rulestr; 828220163Strasz 829220163Strasz if (subjectstr == NULL || subjectstr[0] == '\0') 830220163Strasz rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; 831220163Strasz else { 832220163Strasz error = str2value(subjectstr, &rule->rr_subject_type, subjectnames); 833220163Strasz if (error != 0) 834220163Strasz goto out; 835220163Strasz } 836220163Strasz 837220163Strasz if (subject_idstr == NULL || subject_idstr[0] == '\0') { 838220163Strasz rule->rr_subject.rs_proc = NULL; 839220163Strasz rule->rr_subject.rs_uip = NULL; 840220527Strasz rule->rr_subject.rs_loginclass = NULL; 841221362Strasz rule->rr_subject.rs_prison_racct = NULL; 842220163Strasz } else { 843220163Strasz switch (rule->rr_subject_type) { 844220163Strasz case RCTL_SUBJECT_TYPE_UNDEFINED: 845220163Strasz error = EINVAL; 846220163Strasz goto out; 847220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 848220163Strasz error = str2id(subject_idstr, &id); 849220163Strasz if (error != 0) 850220163Strasz goto out; 851220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 852220163Strasz rule->rr_subject.rs_proc = pfind(id); 853220163Strasz if (rule->rr_subject.rs_proc == NULL) { 854220163Strasz error = ESRCH; 855220163Strasz goto out; 856220163Strasz } 857220163Strasz PROC_UNLOCK(rule->rr_subject.rs_proc); 858220163Strasz break; 859220163Strasz case RCTL_SUBJECT_TYPE_USER: 860220163Strasz error = str2id(subject_idstr, &id); 861220163Strasz if (error != 0) 862220163Strasz goto out; 863220163Strasz rule->rr_subject.rs_uip = uifind(id); 864220163Strasz break; 865220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 866220527Strasz rule->rr_subject.rs_loginclass = 867220163Strasz loginclass_find(subject_idstr); 868220527Strasz if (rule->rr_subject.rs_loginclass == NULL) { 869220163Strasz error = ENAMETOOLONG; 870220163Strasz goto out; 871220163Strasz } 872220163Strasz break; 873220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 874221362Strasz rule->rr_subject.rs_prison_racct = 875221362Strasz prison_racct_find(subject_idstr); 876221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) { 877221362Strasz error = ENAMETOOLONG; 878221362Strasz goto out; 879220163Strasz } 880220163Strasz break; 881220163Strasz default: 882220163Strasz panic("rctl_string_to_rule: unknown subject type %d", 883220163Strasz rule->rr_subject_type); 884220163Strasz } 885220163Strasz } 886220163Strasz 887220163Strasz if (resourcestr == NULL || resourcestr[0] == '\0') 888220163Strasz rule->rr_resource = RACCT_UNDEFINED; 889220163Strasz else { 890220163Strasz error = str2value(resourcestr, &rule->rr_resource, 891220163Strasz resourcenames); 892220163Strasz if (error != 0) 893220163Strasz goto out; 894220163Strasz } 895220163Strasz 896220163Strasz if (actionstr == NULL || actionstr[0] == '\0') 897220163Strasz rule->rr_action = RCTL_ACTION_UNDEFINED; 898220163Strasz else { 899220163Strasz error = str2value(actionstr, &rule->rr_action, actionnames); 900220163Strasz if (error != 0) 901220163Strasz goto out; 902220163Strasz } 903220163Strasz 904220163Strasz if (amountstr == NULL || amountstr[0] == '\0') 905220163Strasz rule->rr_amount = RCTL_AMOUNT_UNDEFINED; 906220163Strasz else { 907220163Strasz error = str2int64(amountstr, &rule->rr_amount); 908220163Strasz if (error != 0) 909220163Strasz goto out; 910223844Strasz if (RACCT_IS_IN_THOUSANDS(rule->rr_resource)) 911220163Strasz rule->rr_amount *= 1000; 912220163Strasz } 913220163Strasz 914220163Strasz if (perstr == NULL || perstr[0] == '\0') 915220163Strasz rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; 916220163Strasz else { 917220163Strasz error = str2value(perstr, &rule->rr_per, subjectnames); 918220163Strasz if (error != 0) 919220163Strasz goto out; 920220163Strasz } 921220163Strasz 922220163Straszout: 923220163Strasz if (error == 0) 924220163Strasz *rulep = rule; 925220163Strasz else 926220163Strasz rctl_rule_release(rule); 927220163Strasz 928220163Strasz return (error); 929220163Strasz} 930220163Strasz 931220163Strasz/* 932220163Strasz * Link a rule with all the subjects it applies to. 933220163Strasz */ 934220163Straszint 935220163Straszrctl_rule_add(struct rctl_rule *rule) 936220163Strasz{ 937220163Strasz struct proc *p; 938220163Strasz struct ucred *cred; 939220163Strasz struct uidinfo *uip; 940220163Strasz struct prison *pr; 941221362Strasz struct prison_racct *prr; 942220163Strasz struct loginclass *lc; 943220163Strasz struct rctl_rule *rule2; 944220163Strasz int match; 945220163Strasz 946220163Strasz KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); 947220163Strasz 948220163Strasz /* 949220163Strasz * Some rules just don't make sense. Note that the one below 950223844Strasz * cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU, 951220163Strasz * for example, is not deniable in the racct sense, but the 952220163Strasz * limit is enforced in a different way, so "deny" rules for %CPU 953220163Strasz * do make sense. 954220163Strasz */ 955220163Strasz if (rule->rr_action == RCTL_ACTION_DENY && 956220163Strasz (rule->rr_resource == RACCT_CPU || 957220163Strasz rule->rr_resource == RACCT_WALLCLOCK)) 958220163Strasz return (EOPNOTSUPP); 959220163Strasz 960220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_PROCESS && 961223844Strasz RACCT_IS_SLOPPY(rule->rr_resource)) 962220163Strasz return (EOPNOTSUPP); 963220163Strasz 964220163Strasz /* 965220163Strasz * Make sure there are no duplicated rules. Also, for the "deny" 966220163Strasz * rules, remove ones differing only by "amount". 967220163Strasz */ 968220163Strasz if (rule->rr_action == RCTL_ACTION_DENY) { 969220163Strasz rule2 = rctl_rule_duplicate(rule, M_WAITOK); 970220163Strasz rule2->rr_amount = RCTL_AMOUNT_UNDEFINED; 971220163Strasz rctl_rule_remove(rule2); 972220163Strasz rctl_rule_release(rule2); 973220163Strasz } else 974220163Strasz rctl_rule_remove(rule); 975220163Strasz 976220163Strasz switch (rule->rr_subject_type) { 977220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 978220163Strasz p = rule->rr_subject.rs_proc; 979220163Strasz KASSERT(p != NULL, ("rctl_rule_add: NULL proc")); 980220163Strasz /* 981220163Strasz * No resource limits for system processes. 982220163Strasz */ 983220163Strasz if (p->p_flag & P_SYSTEM) 984220163Strasz return (EPERM); 985220163Strasz 986220163Strasz rctl_racct_add_rule(p->p_racct, rule); 987220163Strasz /* 988220163Strasz * In case of per-process rule, we don't have anything more 989220163Strasz * to do. 990220163Strasz */ 991220163Strasz return (0); 992220163Strasz 993220163Strasz case RCTL_SUBJECT_TYPE_USER: 994220163Strasz uip = rule->rr_subject.rs_uip; 995220163Strasz KASSERT(uip != NULL, ("rctl_rule_add: NULL uip")); 996220163Strasz rctl_racct_add_rule(uip->ui_racct, rule); 997220163Strasz break; 998220163Strasz 999220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1000220527Strasz lc = rule->rr_subject.rs_loginclass; 1001220163Strasz KASSERT(lc != NULL, ("rctl_rule_add: NULL loginclass")); 1002220163Strasz rctl_racct_add_rule(lc->lc_racct, rule); 1003220163Strasz break; 1004220163Strasz 1005220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1006221362Strasz prr = rule->rr_subject.rs_prison_racct; 1007221362Strasz KASSERT(prr != NULL, ("rctl_rule_add: NULL pr")); 1008221362Strasz rctl_racct_add_rule(prr->prr_racct, rule); 1009220163Strasz break; 1010220163Strasz 1011220163Strasz default: 1012220163Strasz panic("rctl_rule_add: unknown subject type %d", 1013220163Strasz rule->rr_subject_type); 1014220163Strasz } 1015220163Strasz 1016220163Strasz /* 1017220163Strasz * Now go through all the processes and add the new rule to the ones 1018220163Strasz * it applies to. 1019220163Strasz */ 1020220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1021220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1022220163Strasz if (p->p_flag & P_SYSTEM) 1023220163Strasz continue; 1024220163Strasz cred = p->p_ucred; 1025220163Strasz switch (rule->rr_subject_type) { 1026220163Strasz case RCTL_SUBJECT_TYPE_USER: 1027220163Strasz if (cred->cr_uidinfo == rule->rr_subject.rs_uip || 1028220163Strasz cred->cr_ruidinfo == rule->rr_subject.rs_uip) 1029220163Strasz break; 1030220163Strasz continue; 1031220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1032220527Strasz if (cred->cr_loginclass == rule->rr_subject.rs_loginclass) 1033220163Strasz break; 1034220163Strasz continue; 1035220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1036220163Strasz match = 0; 1037220163Strasz for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) { 1038221362Strasz if (pr->pr_prison_racct == rule->rr_subject.rs_prison_racct) { 1039220163Strasz match = 1; 1040220163Strasz break; 1041220163Strasz } 1042220163Strasz } 1043220163Strasz if (match) 1044220163Strasz break; 1045220163Strasz continue; 1046220163Strasz default: 1047220163Strasz panic("rctl_rule_add: unknown subject type %d", 1048220163Strasz rule->rr_subject_type); 1049220163Strasz } 1050220163Strasz 1051220163Strasz rctl_racct_add_rule(p->p_racct, rule); 1052220163Strasz } 1053220163Strasz 1054220163Strasz return (0); 1055220163Strasz} 1056220163Strasz 1057220163Straszstatic void 1058220163Straszrctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) 1059220163Strasz{ 1060220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1061220163Strasz int found = 0; 1062220163Strasz 1063220163Strasz rw_wlock(&rctl_lock); 1064220163Strasz found += rctl_racct_remove_rules(racct, filter); 1065220163Strasz rw_wunlock(&rctl_lock); 1066220163Strasz 1067220163Strasz *((int *)arg3) += found; 1068220163Strasz} 1069220163Strasz 1070220163Strasz/* 1071220163Strasz * Remove all rules that match the filter. 1072220163Strasz */ 1073220163Straszint 1074220163Straszrctl_rule_remove(struct rctl_rule *filter) 1075220163Strasz{ 1076220163Strasz int found = 0; 1077220163Strasz struct proc *p; 1078220163Strasz 1079220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && 1080220163Strasz filter->rr_subject.rs_proc != NULL) { 1081220163Strasz p = filter->rr_subject.rs_proc; 1082220163Strasz rw_wlock(&rctl_lock); 1083220163Strasz found = rctl_racct_remove_rules(p->p_racct, filter); 1084220163Strasz rw_wunlock(&rctl_lock); 1085220163Strasz if (found) 1086220163Strasz return (0); 1087220163Strasz return (ESRCH); 1088220163Strasz } 1089220163Strasz 1090220163Strasz loginclass_racct_foreach(rctl_rule_remove_callback, filter, 1091220163Strasz (void *)&found); 1092220163Strasz ui_racct_foreach(rctl_rule_remove_callback, filter, 1093220163Strasz (void *)&found); 1094220163Strasz prison_racct_foreach(rctl_rule_remove_callback, filter, 1095220163Strasz (void *)&found); 1096220163Strasz 1097220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1098220163Strasz rw_wlock(&rctl_lock); 1099220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1100220163Strasz found += rctl_racct_remove_rules(p->p_racct, filter); 1101220163Strasz } 1102220163Strasz rw_wunlock(&rctl_lock); 1103220163Strasz 1104220163Strasz if (found) 1105220163Strasz return (0); 1106220163Strasz return (ESRCH); 1107220163Strasz} 1108220163Strasz 1109220163Strasz/* 1110220163Strasz * Appends a rule to the sbuf. 1111220163Strasz */ 1112220163Straszstatic void 1113220163Straszrctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule) 1114220163Strasz{ 1115220163Strasz int64_t amount; 1116220163Strasz 1117220163Strasz sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type)); 1118220163Strasz 1119220163Strasz switch (rule->rr_subject_type) { 1120220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1121220163Strasz if (rule->rr_subject.rs_proc == NULL) 1122220163Strasz sbuf_printf(sb, ":"); 1123220163Strasz else 1124220163Strasz sbuf_printf(sb, "%d:", 1125220163Strasz rule->rr_subject.rs_proc->p_pid); 1126220163Strasz break; 1127220163Strasz case RCTL_SUBJECT_TYPE_USER: 1128220163Strasz if (rule->rr_subject.rs_uip == NULL) 1129220163Strasz sbuf_printf(sb, ":"); 1130220163Strasz else 1131220163Strasz sbuf_printf(sb, "%d:", 1132220163Strasz rule->rr_subject.rs_uip->ui_uid); 1133220163Strasz break; 1134220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1135220527Strasz if (rule->rr_subject.rs_loginclass == NULL) 1136220163Strasz sbuf_printf(sb, ":"); 1137220163Strasz else 1138220163Strasz sbuf_printf(sb, "%s:", 1139220527Strasz rule->rr_subject.rs_loginclass->lc_name); 1140220163Strasz break; 1141220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1142221362Strasz if (rule->rr_subject.rs_prison_racct == NULL) 1143220163Strasz sbuf_printf(sb, ":"); 1144220163Strasz else 1145220163Strasz sbuf_printf(sb, "%s:", 1146221362Strasz rule->rr_subject.rs_prison_racct->prr_name); 1147220163Strasz break; 1148220163Strasz default: 1149220163Strasz panic("rctl_rule_to_sbuf: unknown subject type %d", 1150220163Strasz rule->rr_subject_type); 1151220163Strasz } 1152220163Strasz 1153220163Strasz amount = rule->rr_amount; 1154220163Strasz if (amount != RCTL_AMOUNT_UNDEFINED && 1155223844Strasz RACCT_IS_IN_THOUSANDS(rule->rr_resource)) 1156220163Strasz amount /= 1000; 1157220163Strasz 1158220163Strasz sbuf_printf(sb, "%s:%s=%jd", 1159220163Strasz rctl_resource_name(rule->rr_resource), 1160220163Strasz rctl_action_name(rule->rr_action), 1161220163Strasz amount); 1162220163Strasz 1163220163Strasz if (rule->rr_per != rule->rr_subject_type) 1164220163Strasz sbuf_printf(sb, "/%s", rctl_subject_type_name(rule->rr_per)); 1165220163Strasz} 1166220163Strasz 1167220163Strasz/* 1168220163Strasz * Routine used by RCTL syscalls to read in input string. 1169220163Strasz */ 1170220163Straszstatic int 1171220163Straszrctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen) 1172220163Strasz{ 1173220163Strasz int error; 1174220163Strasz char *str; 1175220163Strasz 1176220163Strasz if (inbuflen <= 0) 1177220163Strasz return (EINVAL); 1178220163Strasz 1179220163Strasz str = malloc(inbuflen + 1, M_RCTL, M_WAITOK); 1180220163Strasz error = copyinstr(inbufp, str, inbuflen, NULL); 1181220163Strasz if (error != 0) { 1182220163Strasz free(str, M_RCTL); 1183220163Strasz return (error); 1184220163Strasz } 1185220163Strasz 1186220163Strasz *inputstr = str; 1187220163Strasz 1188220163Strasz return (0); 1189220163Strasz} 1190220163Strasz 1191220163Strasz/* 1192220163Strasz * Routine used by RCTL syscalls to write out output string. 1193220163Strasz */ 1194220163Straszstatic int 1195220163Straszrctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen) 1196220163Strasz{ 1197220163Strasz int error; 1198220163Strasz 1199220163Strasz if (outputsbuf == NULL) 1200220163Strasz return (0); 1201220163Strasz 1202220163Strasz sbuf_finish(outputsbuf); 1203220163Strasz if (outbuflen < sbuf_len(outputsbuf) + 1) { 1204220163Strasz sbuf_delete(outputsbuf); 1205220163Strasz return (ERANGE); 1206220163Strasz } 1207220163Strasz error = copyout(sbuf_data(outputsbuf), outbufp, 1208220163Strasz sbuf_len(outputsbuf) + 1); 1209220163Strasz sbuf_delete(outputsbuf); 1210220163Strasz return (error); 1211220163Strasz} 1212220163Strasz 1213220163Straszstatic struct sbuf * 1214220163Straszrctl_racct_to_sbuf(struct racct *racct, int sloppy) 1215220163Strasz{ 1216220163Strasz int i; 1217220163Strasz int64_t amount; 1218220163Strasz struct sbuf *sb; 1219220163Strasz 1220220163Strasz sb = sbuf_new_auto(); 1221220163Strasz for (i = 0; i <= RACCT_MAX; i++) { 1222223844Strasz if (sloppy == 0 && RACCT_IS_SLOPPY(i)) 1223220163Strasz continue; 1224220163Strasz amount = racct->r_resources[i]; 1225223844Strasz if (RACCT_IS_IN_THOUSANDS(i)) 1226220163Strasz amount /= 1000; 1227220163Strasz sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount); 1228220163Strasz } 1229220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1230220163Strasz return (sb); 1231220163Strasz} 1232220163Strasz 1233220163Straszint 1234220163Straszrctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1235220163Strasz{ 1236220163Strasz int error; 1237220163Strasz char *inputstr; 1238220163Strasz struct rctl_rule *filter; 1239220163Strasz struct sbuf *outputsbuf = NULL; 1240220163Strasz struct proc *p; 1241220163Strasz struct uidinfo *uip; 1242220163Strasz struct loginclass *lc; 1243221362Strasz struct prison_racct *prr; 1244220163Strasz 1245220527Strasz error = priv_check(td, PRIV_RCTL_GET_RACCT); 1246220163Strasz if (error != 0) 1247220163Strasz return (error); 1248220163Strasz 1249220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1250220163Strasz if (error != 0) 1251220163Strasz return (error); 1252220163Strasz 1253220163Strasz sx_slock(&allproc_lock); 1254220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1255220163Strasz free(inputstr, M_RCTL); 1256220163Strasz if (error != 0) { 1257220163Strasz sx_sunlock(&allproc_lock); 1258220163Strasz return (error); 1259220163Strasz } 1260220163Strasz 1261220163Strasz switch (filter->rr_subject_type) { 1262220163Strasz case RCTL_SUBJECT_TYPE_PROCESS: 1263220163Strasz p = filter->rr_subject.rs_proc; 1264220163Strasz if (p == NULL) { 1265220163Strasz error = EINVAL; 1266220163Strasz goto out; 1267220163Strasz } 1268220163Strasz if (p->p_flag & P_SYSTEM) { 1269220163Strasz error = EINVAL; 1270220163Strasz goto out; 1271220163Strasz } 1272220163Strasz outputsbuf = rctl_racct_to_sbuf(p->p_racct, 0); 1273220163Strasz break; 1274220163Strasz case RCTL_SUBJECT_TYPE_USER: 1275220163Strasz uip = filter->rr_subject.rs_uip; 1276220163Strasz if (uip == NULL) { 1277220163Strasz error = EINVAL; 1278220163Strasz goto out; 1279220163Strasz } 1280220163Strasz outputsbuf = rctl_racct_to_sbuf(uip->ui_racct, 1); 1281220163Strasz break; 1282220163Strasz case RCTL_SUBJECT_TYPE_LOGINCLASS: 1283220527Strasz lc = filter->rr_subject.rs_loginclass; 1284220163Strasz if (lc == NULL) { 1285220163Strasz error = EINVAL; 1286220163Strasz goto out; 1287220163Strasz } 1288220163Strasz outputsbuf = rctl_racct_to_sbuf(lc->lc_racct, 1); 1289220163Strasz break; 1290220163Strasz case RCTL_SUBJECT_TYPE_JAIL: 1291221362Strasz prr = filter->rr_subject.rs_prison_racct; 1292221362Strasz if (prr == NULL) { 1293220163Strasz error = EINVAL; 1294220163Strasz goto out; 1295220163Strasz } 1296221362Strasz outputsbuf = rctl_racct_to_sbuf(prr->prr_racct, 1); 1297220163Strasz break; 1298220163Strasz default: 1299220163Strasz error = EINVAL; 1300220163Strasz } 1301220163Straszout: 1302220163Strasz rctl_rule_release(filter); 1303220163Strasz sx_sunlock(&allproc_lock); 1304220163Strasz if (error != 0) 1305220163Strasz return (error); 1306220163Strasz 1307220163Strasz error = rctl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); 1308220163Strasz 1309220163Strasz return (error); 1310220163Strasz} 1311220163Strasz 1312220163Straszstatic void 1313220163Straszrctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) 1314220163Strasz{ 1315220163Strasz struct rctl_rule *filter = (struct rctl_rule *)arg2; 1316220163Strasz struct rctl_rule_link *link; 1317220163Strasz struct sbuf *sb = (struct sbuf *)arg3; 1318220163Strasz 1319220163Strasz rw_rlock(&rctl_lock); 1320220163Strasz LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { 1321220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1322220163Strasz continue; 1323220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1324220163Strasz sbuf_printf(sb, ","); 1325220163Strasz } 1326220163Strasz rw_runlock(&rctl_lock); 1327220163Strasz} 1328220163Strasz 1329220163Straszint 1330220163Straszrctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1331220163Strasz{ 1332220163Strasz int error; 1333220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1334220163Strasz char *inputstr, *buf; 1335220163Strasz struct sbuf *sb; 1336220163Strasz struct rctl_rule *filter; 1337220163Strasz struct rctl_rule_link *link; 1338220163Strasz struct proc *p; 1339220163Strasz 1340220163Strasz error = priv_check(td, PRIV_RCTL_GET_RULES); 1341220163Strasz if (error != 0) 1342220163Strasz return (error); 1343220163Strasz 1344220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1345220163Strasz if (error != 0) 1346220163Strasz return (error); 1347220163Strasz 1348220163Strasz sx_slock(&allproc_lock); 1349220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1350220163Strasz free(inputstr, M_RCTL); 1351220163Strasz if (error != 0) { 1352220163Strasz sx_sunlock(&allproc_lock); 1353220163Strasz return (error); 1354220163Strasz } 1355220163Strasz 1356220163Straszagain: 1357220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1358220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1359220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1360220163Strasz 1361220163Strasz sx_assert(&allproc_lock, SA_LOCKED); 1362220163Strasz FOREACH_PROC_IN_SYSTEM(p) { 1363220163Strasz rw_rlock(&rctl_lock); 1364220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1365220163Strasz /* 1366220163Strasz * Non-process rules will be added to the buffer later. 1367220163Strasz * Adding them here would result in duplicated output. 1368220163Strasz */ 1369220163Strasz if (link->rrl_rule->rr_subject_type != 1370220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1371220163Strasz continue; 1372220163Strasz if (!rctl_rule_matches(link->rrl_rule, filter)) 1373220163Strasz continue; 1374220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1375220163Strasz sbuf_printf(sb, ","); 1376220163Strasz } 1377220163Strasz rw_runlock(&rctl_lock); 1378220163Strasz } 1379220163Strasz 1380220163Strasz loginclass_racct_foreach(rctl_get_rules_callback, filter, sb); 1381220163Strasz ui_racct_foreach(rctl_get_rules_callback, filter, sb); 1382220163Strasz prison_racct_foreach(rctl_get_rules_callback, filter, sb); 1383220163Strasz if (sbuf_error(sb) == ENOMEM) { 1384220163Strasz sbuf_delete(sb); 1385220163Strasz free(buf, M_RCTL); 1386220163Strasz bufsize *= 4; 1387220163Strasz goto again; 1388220163Strasz } 1389220163Strasz 1390220163Strasz /* 1391220163Strasz * Remove trailing ",". 1392220163Strasz */ 1393220163Strasz if (sbuf_len(sb) > 0) 1394220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1395220163Strasz 1396220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1397220163Strasz 1398220163Strasz rctl_rule_release(filter); 1399220163Strasz sx_sunlock(&allproc_lock); 1400220163Strasz free(buf, M_RCTL); 1401220163Strasz return (error); 1402220163Strasz} 1403220163Strasz 1404220163Straszint 1405220163Straszrctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1406220163Strasz{ 1407220163Strasz int error; 1408220163Strasz size_t bufsize = RCTL_DEFAULT_BUFSIZE; 1409220163Strasz char *inputstr, *buf; 1410220163Strasz struct sbuf *sb; 1411220163Strasz struct rctl_rule *filter; 1412220163Strasz struct rctl_rule_link *link; 1413220163Strasz 1414220163Strasz error = priv_check(td, PRIV_RCTL_GET_LIMITS); 1415220163Strasz if (error != 0) 1416220163Strasz return (error); 1417220163Strasz 1418220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1419220163Strasz if (error != 0) 1420220163Strasz return (error); 1421220163Strasz 1422220163Strasz sx_slock(&allproc_lock); 1423220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1424220163Strasz free(inputstr, M_RCTL); 1425220163Strasz if (error != 0) { 1426220163Strasz sx_sunlock(&allproc_lock); 1427220163Strasz return (error); 1428220163Strasz } 1429220163Strasz 1430220163Strasz if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_UNDEFINED) { 1431220163Strasz rctl_rule_release(filter); 1432220163Strasz sx_sunlock(&allproc_lock); 1433220163Strasz return (EINVAL); 1434220163Strasz } 1435220163Strasz if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_PROCESS) { 1436220163Strasz rctl_rule_release(filter); 1437220163Strasz sx_sunlock(&allproc_lock); 1438220163Strasz return (EOPNOTSUPP); 1439220163Strasz } 1440220163Strasz if (filter->rr_subject.rs_proc == NULL) { 1441220163Strasz rctl_rule_release(filter); 1442220163Strasz sx_sunlock(&allproc_lock); 1443220163Strasz return (EINVAL); 1444220163Strasz } 1445220163Strasz 1446220163Straszagain: 1447220163Strasz buf = malloc(bufsize, M_RCTL, M_WAITOK); 1448220163Strasz sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN); 1449220163Strasz KASSERT(sb != NULL, ("sbuf_new failed")); 1450220163Strasz 1451220163Strasz rw_rlock(&rctl_lock); 1452220163Strasz LIST_FOREACH(link, &filter->rr_subject.rs_proc->p_racct->r_rule_links, 1453220163Strasz rrl_next) { 1454220163Strasz rctl_rule_to_sbuf(sb, link->rrl_rule); 1455220163Strasz sbuf_printf(sb, ","); 1456220163Strasz } 1457220163Strasz rw_runlock(&rctl_lock); 1458220163Strasz if (sbuf_error(sb) == ENOMEM) { 1459220163Strasz sbuf_delete(sb); 1460220163Strasz free(buf, M_RCTL); 1461220163Strasz bufsize *= 4; 1462220163Strasz goto again; 1463220163Strasz } 1464220163Strasz 1465220163Strasz /* 1466220163Strasz * Remove trailing ",". 1467220163Strasz */ 1468220163Strasz if (sbuf_len(sb) > 0) 1469220163Strasz sbuf_setpos(sb, sbuf_len(sb) - 1); 1470220163Strasz 1471220163Strasz error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen); 1472220163Strasz rctl_rule_release(filter); 1473220163Strasz sx_sunlock(&allproc_lock); 1474220163Strasz free(buf, M_RCTL); 1475220163Strasz return (error); 1476220163Strasz} 1477220163Strasz 1478220163Straszint 1479220163Straszrctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1480220163Strasz{ 1481220163Strasz int error; 1482220163Strasz struct rctl_rule *rule; 1483220163Strasz char *inputstr; 1484220163Strasz 1485220163Strasz error = priv_check(td, PRIV_RCTL_ADD_RULE); 1486220163Strasz if (error != 0) 1487220163Strasz return (error); 1488220163Strasz 1489220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1490220163Strasz if (error != 0) 1491220163Strasz return (error); 1492220163Strasz 1493220163Strasz sx_slock(&allproc_lock); 1494220163Strasz error = rctl_string_to_rule(inputstr, &rule); 1495220163Strasz free(inputstr, M_RCTL); 1496220163Strasz if (error != 0) { 1497220163Strasz sx_sunlock(&allproc_lock); 1498220163Strasz return (error); 1499220163Strasz } 1500220163Strasz /* 1501220163Strasz * The 'per' part of a rule is optional. 1502220163Strasz */ 1503220163Strasz if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED && 1504220163Strasz rule->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) 1505220163Strasz rule->rr_per = rule->rr_subject_type; 1506220163Strasz 1507220163Strasz if (!rctl_rule_fully_specified(rule)) { 1508220163Strasz error = EINVAL; 1509220163Strasz goto out; 1510220163Strasz } 1511220163Strasz 1512220163Strasz error = rctl_rule_add(rule); 1513220163Strasz 1514220163Straszout: 1515220163Strasz rctl_rule_release(rule); 1516220163Strasz sx_sunlock(&allproc_lock); 1517220163Strasz return (error); 1518220163Strasz} 1519220163Strasz 1520220163Straszint 1521220163Straszrctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1522220163Strasz{ 1523220163Strasz int error; 1524220163Strasz struct rctl_rule *filter; 1525220163Strasz char *inputstr; 1526220163Strasz 1527220163Strasz error = priv_check(td, PRIV_RCTL_REMOVE_RULE); 1528220163Strasz if (error != 0) 1529220163Strasz return (error); 1530220163Strasz 1531220163Strasz error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); 1532220163Strasz if (error != 0) 1533220163Strasz return (error); 1534220163Strasz 1535220163Strasz sx_slock(&allproc_lock); 1536220163Strasz error = rctl_string_to_rule(inputstr, &filter); 1537220163Strasz free(inputstr, M_RCTL); 1538220163Strasz if (error != 0) { 1539220163Strasz sx_sunlock(&allproc_lock); 1540220163Strasz return (error); 1541220163Strasz } 1542220163Strasz 1543220163Strasz error = rctl_rule_remove(filter); 1544220163Strasz rctl_rule_release(filter); 1545220163Strasz sx_sunlock(&allproc_lock); 1546220163Strasz 1547220163Strasz return (error); 1548220163Strasz} 1549220163Strasz 1550220163Strasz/* 1551220163Strasz * Update RCTL rule list after credential change. 1552220163Strasz */ 1553220163Straszvoid 1554220163Straszrctl_proc_ucred_changed(struct proc *p, struct ucred *newcred) 1555220163Strasz{ 1556220163Strasz int rulecnt, i; 1557220163Strasz struct rctl_rule_link *link, *newlink; 1558220163Strasz struct uidinfo *newuip; 1559220163Strasz struct loginclass *newlc; 1560221362Strasz struct prison_racct *newprr; 1561220163Strasz LIST_HEAD(, rctl_rule_link) newrules; 1562220163Strasz 1563220163Strasz newuip = newcred->cr_ruidinfo; 1564220163Strasz newlc = newcred->cr_loginclass; 1565221362Strasz newprr = newcred->cr_prison->pr_prison_racct; 1566220163Strasz 1567220163Strasz LIST_INIT(&newrules); 1568220163Strasz 1569220163Straszagain: 1570220163Strasz /* 1571220163Strasz * First, count the rules that apply to the process with new 1572220163Strasz * credentials. 1573220163Strasz */ 1574220163Strasz rulecnt = 0; 1575220163Strasz rw_rlock(&rctl_lock); 1576220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1577220163Strasz if (link->rrl_rule->rr_subject_type == 1578220163Strasz RCTL_SUBJECT_TYPE_PROCESS) 1579220163Strasz rulecnt++; 1580220163Strasz } 1581220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) 1582220163Strasz rulecnt++; 1583220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) 1584220163Strasz rulecnt++; 1585221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) 1586220163Strasz rulecnt++; 1587220163Strasz rw_runlock(&rctl_lock); 1588220163Strasz 1589220163Strasz /* 1590220163Strasz * Create temporary list. We've dropped the rctl_lock in order 1591220163Strasz * to use M_WAITOK. 1592220163Strasz */ 1593220163Strasz for (i = 0; i < rulecnt; i++) { 1594220163Strasz newlink = uma_zalloc(rctl_rule_link_zone, M_WAITOK); 1595220163Strasz newlink->rrl_rule = NULL; 1596220163Strasz LIST_INSERT_HEAD(&newrules, newlink, rrl_next); 1597220163Strasz } 1598220163Strasz 1599220163Strasz newlink = LIST_FIRST(&newrules); 1600220163Strasz 1601220163Strasz /* 1602220163Strasz * Assign rules to the newly allocated list entries. 1603220163Strasz */ 1604220163Strasz rw_wlock(&rctl_lock); 1605220163Strasz LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { 1606220163Strasz if (link->rrl_rule->rr_subject_type == 1607220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1608220163Strasz if (newlink == NULL) 1609220163Strasz goto goaround; 1610220163Strasz rctl_rule_acquire(link->rrl_rule); 1611220163Strasz newlink->rrl_rule = link->rrl_rule; 1612220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1613220163Strasz rulecnt--; 1614220163Strasz } 1615220163Strasz } 1616220163Strasz 1617220163Strasz LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) { 1618220163Strasz if (newlink == NULL) 1619220163Strasz goto goaround; 1620220163Strasz rctl_rule_acquire(link->rrl_rule); 1621220163Strasz newlink->rrl_rule = link->rrl_rule; 1622220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1623220163Strasz rulecnt--; 1624220163Strasz } 1625220163Strasz 1626220163Strasz LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) { 1627220163Strasz if (newlink == NULL) 1628220163Strasz goto goaround; 1629220163Strasz rctl_rule_acquire(link->rrl_rule); 1630220163Strasz newlink->rrl_rule = link->rrl_rule; 1631220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1632220163Strasz rulecnt--; 1633220163Strasz } 1634220163Strasz 1635221362Strasz LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) { 1636220163Strasz if (newlink == NULL) 1637220163Strasz goto goaround; 1638220163Strasz rctl_rule_acquire(link->rrl_rule); 1639220163Strasz newlink->rrl_rule = link->rrl_rule; 1640220163Strasz newlink = LIST_NEXT(newlink, rrl_next); 1641220163Strasz rulecnt--; 1642220163Strasz } 1643220163Strasz 1644220163Strasz if (rulecnt == 0) { 1645220163Strasz /* 1646220163Strasz * Free the old rule list. 1647220163Strasz */ 1648220163Strasz while (!LIST_EMPTY(&p->p_racct->r_rule_links)) { 1649220163Strasz link = LIST_FIRST(&p->p_racct->r_rule_links); 1650220163Strasz LIST_REMOVE(link, rrl_next); 1651220163Strasz rctl_rule_release(link->rrl_rule); 1652220163Strasz uma_zfree(rctl_rule_link_zone, link); 1653220163Strasz } 1654220163Strasz 1655220163Strasz /* 1656220163Strasz * Replace lists and we're done. 1657220163Strasz * 1658220163Strasz * XXX: Is there any way to switch list heads instead 1659220163Strasz * of iterating here? 1660220163Strasz */ 1661220163Strasz while (!LIST_EMPTY(&newrules)) { 1662220163Strasz newlink = LIST_FIRST(&newrules); 1663220163Strasz LIST_REMOVE(newlink, rrl_next); 1664220163Strasz LIST_INSERT_HEAD(&p->p_racct->r_rule_links, 1665220163Strasz newlink, rrl_next); 1666220163Strasz } 1667220163Strasz 1668220163Strasz rw_wunlock(&rctl_lock); 1669220163Strasz 1670220163Strasz return; 1671220163Strasz } 1672220163Strasz 1673220163Straszgoaround: 1674220163Strasz rw_wunlock(&rctl_lock); 1675220163Strasz 1676220163Strasz /* 1677220163Strasz * Rule list changed while we were not holding the rctl_lock. 1678220163Strasz * Free the new list and try again. 1679220163Strasz */ 1680220163Strasz while (!LIST_EMPTY(&newrules)) { 1681220163Strasz newlink = LIST_FIRST(&newrules); 1682220163Strasz LIST_REMOVE(newlink, rrl_next); 1683220163Strasz if (newlink->rrl_rule != NULL) 1684220163Strasz rctl_rule_release(newlink->rrl_rule); 1685220163Strasz uma_zfree(rctl_rule_link_zone, newlink); 1686220163Strasz } 1687220163Strasz 1688220163Strasz goto again; 1689220163Strasz} 1690220163Strasz 1691220163Strasz/* 1692220163Strasz * Assign RCTL rules to the newly created process. 1693220163Strasz */ 1694220163Straszint 1695220163Straszrctl_proc_fork(struct proc *parent, struct proc *child) 1696220163Strasz{ 1697220163Strasz int error; 1698220163Strasz struct rctl_rule_link *link; 1699220163Strasz struct rctl_rule *rule; 1700220163Strasz 1701220163Strasz LIST_INIT(&child->p_racct->r_rule_links); 1702220163Strasz 1703220163Strasz /* 1704220163Strasz * No limits for kernel processes. 1705220163Strasz */ 1706220163Strasz if (child->p_flag & P_SYSTEM) 1707220163Strasz return (0); 1708220163Strasz 1709220163Strasz /* 1710220163Strasz * Nothing to inherit from P_SYSTEM parents. 1711220163Strasz */ 1712220163Strasz if (parent->p_racct == NULL) { 1713220163Strasz KASSERT(parent->p_flag & P_SYSTEM, 1714220163Strasz ("non-system process without racct; p = %p", parent)); 1715220163Strasz return (0); 1716220163Strasz } 1717220163Strasz 1718220163Strasz rw_wlock(&rctl_lock); 1719220163Strasz 1720220163Strasz /* 1721220163Strasz * Go through limits applicable to the parent and assign them 1722220163Strasz * to the child. Rules with 'process' subject have to be duplicated 1723220163Strasz * in order to make their rr_subject point to the new process. 1724220163Strasz */ 1725220163Strasz LIST_FOREACH(link, &parent->p_racct->r_rule_links, rrl_next) { 1726220163Strasz if (link->rrl_rule->rr_subject_type == 1727220163Strasz RCTL_SUBJECT_TYPE_PROCESS) { 1728220163Strasz rule = rctl_rule_duplicate(link->rrl_rule, M_NOWAIT); 1729220163Strasz if (rule == NULL) 1730220163Strasz goto fail; 1731220163Strasz KASSERT(rule->rr_subject.rs_proc == parent, 1732220163Strasz ("rule->rr_subject.rs_proc != parent")); 1733220163Strasz rule->rr_subject.rs_proc = child; 1734220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1735220163Strasz rule); 1736220163Strasz rctl_rule_release(rule); 1737220163Strasz if (error != 0) 1738220163Strasz goto fail; 1739220163Strasz } else { 1740220163Strasz error = rctl_racct_add_rule_locked(child->p_racct, 1741220163Strasz link->rrl_rule); 1742220163Strasz if (error != 0) 1743220163Strasz goto fail; 1744220163Strasz } 1745220163Strasz } 1746220163Strasz 1747220163Strasz rw_wunlock(&rctl_lock); 1748220163Strasz return (0); 1749220163Strasz 1750220163Straszfail: 1751220163Strasz while (!LIST_EMPTY(&child->p_racct->r_rule_links)) { 1752220163Strasz link = LIST_FIRST(&child->p_racct->r_rule_links); 1753220163Strasz LIST_REMOVE(link, rrl_next); 1754220163Strasz rctl_rule_release(link->rrl_rule); 1755220163Strasz uma_zfree(rctl_rule_link_zone, link); 1756220163Strasz } 1757220163Strasz rw_wunlock(&rctl_lock); 1758220163Strasz return (EAGAIN); 1759220163Strasz} 1760220163Strasz 1761220163Strasz/* 1762220163Strasz * Release rules attached to the racct. 1763220163Strasz */ 1764220163Straszvoid 1765220163Straszrctl_racct_release(struct racct *racct) 1766220163Strasz{ 1767220163Strasz struct rctl_rule_link *link; 1768220163Strasz 1769220163Strasz rw_wlock(&rctl_lock); 1770220163Strasz while (!LIST_EMPTY(&racct->r_rule_links)) { 1771220163Strasz link = LIST_FIRST(&racct->r_rule_links); 1772220163Strasz LIST_REMOVE(link, rrl_next); 1773220163Strasz rctl_rule_release(link->rrl_rule); 1774220163Strasz uma_zfree(rctl_rule_link_zone, link); 1775220163Strasz } 1776220163Strasz rw_wunlock(&rctl_lock); 1777220163Strasz} 1778220163Strasz 1779220163Straszstatic void 1780220163Straszrctl_init(void) 1781220163Strasz{ 1782220163Strasz 1783220163Strasz rctl_rule_link_zone = uma_zcreate("rctl_rule_link", 1784220163Strasz sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL, 1785220163Strasz UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1786220163Strasz rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule), 1787220163Strasz NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 1788220163Strasz} 1789220163Strasz 1790220163Strasz#else /* !RCTL */ 1791220163Strasz 1792220163Straszint 1793220163Straszrctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) 1794220163Strasz{ 1795220163Strasz 1796220163Strasz return (ENOSYS); 1797220163Strasz} 1798220163Strasz 1799220163Straszint 1800220163Straszrctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) 1801220163Strasz{ 1802220163Strasz 1803220163Strasz return (ENOSYS); 1804220163Strasz} 1805220163Strasz 1806220163Straszint 1807220163Straszrctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap) 1808220163Strasz{ 1809220163Strasz 1810220163Strasz return (ENOSYS); 1811220163Strasz} 1812220163Strasz 1813220163Straszint 1814220163Straszrctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) 1815220163Strasz{ 1816220163Strasz 1817220163Strasz return (ENOSYS); 1818220163Strasz} 1819220163Strasz 1820220163Straszint 1821220163Straszrctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) 1822220163Strasz{ 1823220163Strasz 1824220163Strasz return (ENOSYS); 1825220163Strasz} 1826220163Strasz 1827220163Strasz#endif /* !RCTL */ 1828