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