Merge commit '7e3488dc6cdcb0c04e1ce167a1a3bfef83b5f2e0'
[unleashed.git] / kernel / syscall / corectl.c
blob540bcb6db66ef563a5d4ece9b7d0e615d21a25d9
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/proc.h>
27 #include <sys/systm.h>
28 #include <sys/param.h>
29 #include <sys/atomic.h>
30 #include <sys/kmem.h>
31 #include <sys/sysmacros.h>
32 #include <sys/procset.h>
33 #include <sys/corectl.h>
34 #include <sys/zone.h>
35 #include <sys/cmn_err.h>
36 #include <sys/policy.h>
39 * Core File Settings
40 * ------------------
42 * A process's core file path and content live in separate reference-counted
43 * structures. The corectl_content_t structure is fairly straightforward --
44 * the only subtlety is that we only really _need_ the mutex on architectures
45 * on which 64-bit memory operations are not atomic. The corectl_path_t
46 * structure is slightly trickier in that it contains a refstr_t rather than
47 * just a char * string. This is to allow consumers of the data in that
48 * structure (the core dumping sub-system for example) to safely use the
49 * string without holding any locks on it in light of updates.
51 * At system and zone boot, init_core() sets init(8)'s core file path and
52 * content to the same value as the fields core_default_path and
53 * core_default_content respectively (for the global zone). All subsequent
54 * children of init(8) reference those same settings. During boot coreadm(8)
55 * is invoked with the -u option to update the system settings from
56 * /etc/coreadm.conf. This has the effect of also changing the values in
57 * core_default_path and core_default_content which updates the core file
58 * settings for all processes in the zone. Each zone has different default
59 * settings; when processes enter a non-global zone, their core file path and
60 * content are set to the zone's default path and content.
62 * Processes that have their core file settings explicitly overridden using
63 * coreadm(8) no longer reference core_default_path or core_default_content
64 * so subsequent changes to the default will not affect them.
67 zone_key_t core_zone_key;
69 static int set_proc_info(pid_t pid, const char *path, core_content_t content);
71 static corectl_content_t *
72 corectl_content_alloc(core_content_t cc)
74 corectl_content_t *ccp;
76 ccp = kmem_zalloc(sizeof (corectl_content_t), KM_SLEEP);
77 ccp->ccc_content = cc;
78 ccp->ccc_refcnt = 1;
80 return (ccp);
83 core_content_t
84 corectl_content_value(corectl_content_t *ccp)
86 core_content_t content;
88 mutex_enter(&ccp->ccc_mtx);
89 content = ccp->ccc_content;
90 mutex_exit(&ccp->ccc_mtx);
92 return (content);
95 static void
96 corectl_content_set(corectl_content_t *ccp, core_content_t content)
98 mutex_enter(&ccp->ccc_mtx);
99 ccp->ccc_content = content;
100 mutex_exit(&ccp->ccc_mtx);
103 void
104 corectl_content_hold(corectl_content_t *ccp)
106 atomic_inc_32(&ccp->ccc_refcnt);
109 void
110 corectl_content_rele(corectl_content_t *ccp)
112 if (atomic_dec_32_nv(&ccp->ccc_refcnt) == 0)
113 kmem_free(ccp, sizeof (corectl_content_t));
117 static corectl_path_t *
118 corectl_path_alloc(const char *path)
120 corectl_path_t *ccp;
122 ccp = kmem_zalloc(sizeof (corectl_path_t), KM_SLEEP);
123 ccp->ccp_path = refstr_alloc(path);
124 ccp->ccp_refcnt = 1;
126 return (ccp);
129 refstr_t *
130 corectl_path_value(corectl_path_t *ccp)
132 refstr_t *path;
134 mutex_enter(&ccp->ccp_mtx);
135 refstr_hold(path = ccp->ccp_path);
136 mutex_exit(&ccp->ccp_mtx);
138 return (path);
141 static void
142 corectl_path_set(corectl_path_t *ccp, const char *path)
144 refstr_t *npath = refstr_alloc(path);
146 mutex_enter(&ccp->ccp_mtx);
147 refstr_rele(ccp->ccp_path);
148 ccp->ccp_path = npath;
149 mutex_exit(&ccp->ccp_mtx);
152 void
153 corectl_path_hold(corectl_path_t *ccp)
155 atomic_inc_32(&ccp->ccp_refcnt);
158 void
159 corectl_path_rele(corectl_path_t *ccp)
161 if (atomic_dec_32_nv(&ccp->ccp_refcnt) == 0) {
162 refstr_rele(ccp->ccp_path);
163 kmem_free(ccp, sizeof (corectl_path_t));
168 * Constructor routine to be called when a zone is created.
170 /*ARGSUSED*/
171 static void *
172 core_init_zone(zoneid_t zoneid)
174 struct core_globals *cg;
176 cg = kmem_alloc(sizeof (*cg), KM_SLEEP);
177 mutex_init(&cg->core_lock, NULL, MUTEX_DEFAULT, NULL);
178 cg->core_file = NULL;
179 cg->core_options = CC_PROCESS_PATH;
180 cg->core_content = CC_CONTENT_DEFAULT;
181 cg->core_rlimit = RLIM64_INFINITY;
182 cg->core_default_path = corectl_path_alloc("core");
183 cg->core_default_content = corectl_content_alloc(CC_CONTENT_DEFAULT);
185 return (cg);
189 * Destructor routine to be called when a zone is destroyed.
191 /*ARGSUSED*/
192 static void
193 core_free_zone(zoneid_t zoneid, void *arg)
195 struct core_globals *cg = arg;
197 if (cg == NULL)
198 return;
199 if (cg->core_file != NULL)
200 refstr_rele(cg->core_file);
201 corectl_path_rele(cg->core_default_path);
202 corectl_content_rele(cg->core_default_content);
203 kmem_free(cg, sizeof (*cg));
207 * Called from start_init_common(), to set init's core file path and content.
209 void
210 init_core(void)
212 struct core_globals *cg;
215 * The first time we hit this, in the global zone, we have to
216 * initialize the zsd key.
218 if (INGLOBALZONE(curproc)) {
219 zone_key_create(&core_zone_key, core_init_zone, NULL,
220 core_free_zone);
224 * zone_key_create will have called core_init_zone for the
225 * global zone, which sets up the default path and content
226 * variables.
228 VERIFY((cg = zone_getspecific(core_zone_key, curproc->p_zone)) != NULL);
230 corectl_path_hold(cg->core_default_path);
231 corectl_content_hold(cg->core_default_content);
233 curproc->p_corefile = cg->core_default_path;
234 curproc->p_content = cg->core_default_content;
238 corectl(int subcode, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
240 int error = 0;
241 proc_t *p;
242 refstr_t *rp;
243 size_t size;
244 char *path;
245 core_content_t content = CC_CONTENT_INVALID;
246 struct core_globals *cg;
247 zone_t *zone = curproc->p_zone;
249 cg = zone_getspecific(core_zone_key, zone);
250 ASSERT(cg != NULL);
252 switch (subcode) {
253 case CC_SET_OPTIONS:
254 if ((error = secpolicy_coreadm(CRED())) == 0) {
255 if (arg1 & ~CC_OPTIONS)
256 error = EINVAL;
257 else
258 cg->core_options = (uint32_t)arg1;
260 break;
262 case CC_GET_OPTIONS:
263 return (cg->core_options);
265 case CC_GET_GLOBAL_PATH:
266 case CC_GET_DEFAULT_PATH:
267 case CC_GET_PROCESS_PATH:
268 if (subcode == CC_GET_GLOBAL_PATH) {
269 mutex_enter(&cg->core_lock);
270 if ((rp = cg->core_file) != NULL)
271 refstr_hold(rp);
272 mutex_exit(&cg->core_lock);
273 } else if (subcode == CC_GET_DEFAULT_PATH) {
274 rp = corectl_path_value(cg->core_default_path);
275 } else {
276 rp = NULL;
277 mutex_enter(&pidlock);
278 if ((p = prfind((pid_t)arg3)) == NULL ||
279 p->p_stat == SIDL) {
280 mutex_exit(&pidlock);
281 error = ESRCH;
282 } else {
283 mutex_enter(&p->p_lock);
284 mutex_exit(&pidlock);
285 mutex_enter(&p->p_crlock);
286 if (!hasprocperm(p->p_cred, CRED()))
287 error = EPERM;
288 else if (p->p_corefile != NULL)
289 rp = corectl_path_value(p->p_corefile);
290 mutex_exit(&p->p_crlock);
291 mutex_exit(&p->p_lock);
294 if (rp == NULL) {
295 if (error == 0 && suword8((void *)arg1, 0))
296 error = EFAULT;
297 } else {
298 error = copyoutstr(refstr_value(rp), (char *)arg1,
299 (size_t)arg2, NULL);
300 refstr_rele(rp);
302 break;
304 case CC_SET_GLOBAL_PATH:
305 case CC_SET_DEFAULT_PATH:
306 if ((error = secpolicy_coreadm(CRED())) != 0)
307 break;
309 /* FALLTHROUGH */
310 case CC_SET_PROCESS_PATH:
311 if ((size = MIN((size_t)arg2, MAXPATHLEN)) == 0) {
312 error = EINVAL;
313 break;
315 path = kmem_alloc(size, KM_SLEEP);
316 error = copyinstr((char *)arg1, path, size, NULL);
317 if (error == 0) {
318 if (subcode == CC_SET_PROCESS_PATH) {
319 error = set_proc_info((pid_t)arg3, path, 0);
320 } else if (subcode == CC_SET_DEFAULT_PATH) {
321 corectl_path_set(cg->core_default_path, path);
322 } else if (*path != '\0' && *path != '/') {
323 error = EINVAL;
324 } else {
325 refstr_t *nrp = refstr_alloc(path);
327 mutex_enter(&cg->core_lock);
328 rp = cg->core_file;
329 if (*path == '\0')
330 cg->core_file = NULL;
331 else
332 refstr_hold(cg->core_file = nrp);
333 mutex_exit(&cg->core_lock);
335 if (rp != NULL)
336 refstr_rele(rp);
338 refstr_rele(nrp);
341 kmem_free(path, size);
342 break;
344 case CC_SET_GLOBAL_CONTENT:
345 case CC_SET_DEFAULT_CONTENT:
346 if ((error = secpolicy_coreadm(CRED())) != 0)
347 break;
349 /* FALLTHROUGH */
350 case CC_SET_PROCESS_CONTENT:
351 error = copyin((void *)arg1, &content, sizeof (content));
352 if (error != 0)
353 break;
356 * If any unknown bits are set, don't let this charade
357 * continue.
359 if (content & ~CC_CONTENT_ALL) {
360 error = EINVAL;
361 break;
364 if (subcode == CC_SET_PROCESS_CONTENT) {
365 error = set_proc_info((pid_t)arg2, NULL, content);
366 } else if (subcode == CC_SET_DEFAULT_CONTENT) {
367 corectl_content_set(cg->core_default_content, content);
368 } else {
369 mutex_enter(&cg->core_lock);
370 cg->core_content = content;
371 mutex_exit(&cg->core_lock);
374 break;
376 case CC_GET_GLOBAL_CONTENT:
377 content = cg->core_content;
378 error = copyout(&content, (void *)arg1, sizeof (content));
379 break;
381 case CC_GET_DEFAULT_CONTENT:
382 content = corectl_content_value(cg->core_default_content);
383 error = copyout(&content, (void *)arg1, sizeof (content));
384 break;
386 case CC_GET_PROCESS_CONTENT:
387 mutex_enter(&pidlock);
388 if ((p = prfind((pid_t)arg2)) == NULL || p->p_stat == SIDL) {
389 mutex_exit(&pidlock);
390 error = ESRCH;
391 break;
394 mutex_enter(&p->p_lock);
395 mutex_exit(&pidlock);
396 mutex_enter(&p->p_crlock);
397 if (!hasprocperm(p->p_cred, CRED()))
398 error = EPERM;
399 else if (p->p_content == NULL)
400 content = CC_CONTENT_NONE;
401 else
402 content = corectl_content_value(p->p_content);
403 mutex_exit(&p->p_crlock);
404 mutex_exit(&p->p_lock);
406 if (error == 0)
407 error = copyout(&content, (void *)arg1,
408 sizeof (content));
409 break;
411 default:
412 error = EINVAL;
413 break;
416 if (error)
417 return (set_errno(error));
418 return (0);
421 typedef struct {
422 int cc_count;
423 corectl_path_t *cc_path;
424 corectl_content_t *cc_content;
425 } counter_t;
427 static int
428 set_one_proc_info(proc_t *p, counter_t *counterp)
430 corectl_path_t *corefile;
431 corectl_content_t *content;
433 mutex_enter(&p->p_crlock);
435 if (!(p->p_flag & SSYS) && hasprocperm(p->p_cred, CRED())) {
436 mutex_exit(&p->p_crlock);
437 counterp->cc_count++;
438 if (counterp->cc_path != NULL) {
439 corectl_path_hold(counterp->cc_path);
440 mutex_enter(&p->p_lock);
441 corefile = p->p_corefile;
442 p->p_corefile = counterp->cc_path;
443 mutex_exit(&p->p_lock);
444 if (corefile != NULL)
445 corectl_path_rele(corefile);
446 } else {
447 corectl_content_hold(counterp->cc_content);
448 mutex_enter(&p->p_lock);
449 content = p->p_content;
450 p->p_content = counterp->cc_content;
451 mutex_exit(&p->p_lock);
452 if (content != NULL)
453 corectl_content_rele(content);
455 } else {
456 mutex_exit(&p->p_crlock);
459 return (0);
462 static int
463 set_proc_info(pid_t pid, const char *path, core_content_t content)
465 proc_t *p;
466 counter_t counter;
467 int error = 0;
469 counter.cc_count = 0;
471 * Only one of the core file path or content can be set at a time.
473 if (path != NULL) {
474 counter.cc_path = corectl_path_alloc(path);
475 counter.cc_content = NULL;
476 } else {
477 counter.cc_path = NULL;
478 counter.cc_content = corectl_content_alloc(content);
481 if (pid == -1) {
482 procset_t set;
484 setprocset(&set, POP_AND, P_ALL, P_MYID, P_ALL, P_MYID);
485 error = dotoprocs(&set, set_one_proc_info, (char *)&counter);
486 if (error == 0 && counter.cc_count == 0)
487 error = EPERM;
488 } else if (pid > 0) {
489 mutex_enter(&pidlock);
490 if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
491 error = ESRCH;
492 } else {
493 (void) set_one_proc_info(p, &counter);
494 if (counter.cc_count == 0)
495 error = EPERM;
497 mutex_exit(&pidlock);
498 } else {
499 int nfound = 0;
500 pid_t pgid;
502 if (pid == 0)
503 pgid = curproc->p_pgrp;
504 else
505 pgid = -pid;
507 mutex_enter(&pidlock);
508 for (p = pgfind(pgid); p != NULL; p = p->p_pglink) {
509 if (p->p_stat != SIDL) {
510 nfound++;
511 (void) set_one_proc_info(p, &counter);
514 mutex_exit(&pidlock);
515 if (nfound == 0)
516 error = ESRCH;
517 else if (counter.cc_count == 0)
518 error = EPERM;
521 if (path != NULL)
522 corectl_path_rele(counter.cc_path);
523 else
524 corectl_content_rele(counter.cc_content);
526 if (error)
527 return (set_errno(error));
528 return (0);
532 * Give current process the default core settings for its current zone;
533 * used for processes entering a zone via zone_enter.
535 void
536 set_core_defaults(void)
538 proc_t *p = curproc;
539 struct core_globals *cg;
540 corectl_path_t *oldpath, *newpath;
541 corectl_content_t *oldcontent, *newcontent;
543 cg = zone_getspecific(core_zone_key, p->p_zone);
545 /* make local copies of default values to protect against change */
546 newpath = cg->core_default_path;
547 newcontent = cg->core_default_content;
549 corectl_path_hold(newpath);
550 corectl_content_hold(newcontent);
551 mutex_enter(&p->p_lock);
552 oldpath = p->p_corefile;
553 p->p_corefile = newpath;
554 oldcontent = p->p_content;
555 p->p_content = newcontent;
556 mutex_exit(&p->p_lock);
557 if (oldpath != NULL)
558 corectl_path_rele(oldpath);
559 if (oldcontent != NULL)
560 corectl_content_rele(oldcontent);