1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * lxprsubr.c: Various functions for the /lxproc vnodeops. 30 */ 31 32 #include <sys/varargs.h> 33 34 #include <sys/cpuvar.h> 35 #include <sys/mman.h> 36 #include <sys/vmsystm.h> 37 #include <sys/prsystm.h> 38 39 #include "lx_proc.h" 40 41 #define LXPRCACHE_NAME "lxpr_cache" 42 43 static int lxpr_node_constructor(void*, void*, int); 44 static void lxpr_node_destructor(void*, void*); 45 46 static kmem_cache_t *lxpr_node_cache; 47 48 struct lxpr_uiobuf { 49 uio_t *uiop; 50 char *buffer; 51 uint32_t buffsize; 52 char *pos; 53 size_t beg; 54 int error; 55 }; 56 57 #define BUFSIZE 4000 58 59 struct lxpr_uiobuf * 60 lxpr_uiobuf_new(uio_t *uiop) 61 { 62 /* Allocate memory for both lxpr_uiobuf and output buffer */ 63 struct lxpr_uiobuf *uiobuf = 64 kmem_alloc(sizeof (struct lxpr_uiobuf) + BUFSIZE, KM_SLEEP); 65 66 uiobuf->uiop = uiop; 67 uiobuf->buffer = (char *)&uiobuf[1]; 68 uiobuf->buffsize = BUFSIZE; 69 uiobuf->pos = uiobuf->buffer; 70 uiobuf->beg = 0; 71 uiobuf->error = 0; 72 73 return (uiobuf); 74 } 75 76 void 77 lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf) 78 { 79 ASSERT(uiobuf != NULL); 80 ASSERT(uiobuf->pos == uiobuf->buffer); 81 82 kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize); 83 } 84 85 void 86 lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset) 87 { 88 uiobuf->uiop->uio_offset = offset; 89 } 90 91 void 92 lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err) 93 { 94 ASSERT(uiobuf->error == 0); 95 96 uiobuf->error = err; 97 } 98 99 int 100 lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf) 101 { 102 off_t off = uiobuf->uiop->uio_offset; 103 caddr_t uaddr = uiobuf->buffer; 104 size_t beg = uiobuf->beg; 105 106 size_t size = uiobuf->pos - uaddr; 107 108 if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) { 109 ASSERT(off >= beg); 110 111 if (beg+size > off && off >= 0) 112 uiobuf->error = 113 uiomove(uaddr+(off-beg), size-(off-beg), 114 UIO_READ, uiobuf->uiop); 115 116 uiobuf->beg += size; 117 } 118 119 uiobuf->pos = uaddr; 120 121 return (uiobuf->error); 122 } 123 124 void 125 lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size) 126 { 127 /* While we can still carry on */ 128 while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) { 129 uint_t remain 130 = uiobuf->buffsize-(uiobuf->pos-uiobuf->buffer); 131 132 /* Enough space in buffer? */ 133 if (remain >= size) { 134 bcopy(buf, uiobuf->pos, size); 135 uiobuf->pos += size; 136 return; 137 } 138 139 /* Not enough space, so copy all we can and try again */ 140 bcopy(buf, uiobuf->pos, remain); 141 uiobuf->pos += remain; 142 (void) lxpr_uiobuf_flush(uiobuf); 143 buf += remain; 144 size -= remain; 145 } 146 } 147 148 #define TYPBUFFSIZE 256 149 void 150 lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...) 151 { 152 va_list args; 153 char buff[TYPBUFFSIZE]; 154 int len; 155 char *buffer; 156 157 /* Can we still do any output */ 158 if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0) 159 return; 160 161 va_start(args, fmt); 162 163 /* Try using stack allocated buffer */ 164 len = vsnprintf(buff, TYPBUFFSIZE, fmt, args); 165 if (len < TYPBUFFSIZE) { 166 va_end(args); 167 lxpr_uiobuf_write(uiobuf, buff, len); 168 return; 169 } 170 171 /* Not enough space in pre-allocated buffer */ 172 buffer = kmem_alloc(len+1, KM_SLEEP); 173 174 /* 175 * We know we allocated the correct amount of space 176 * so no check on the return value 177 */ 178 (void) vsnprintf(buffer, len+1, fmt, args); 179 lxpr_uiobuf_write(uiobuf, buffer, len); 180 va_end(args); 181 kmem_free(buffer, len+1); 182 } 183 184 /* 185 * lxpr_lock(): 186 * 187 * Lookup process from pid and return with p_plock and P_PR_LOCK held. 188 */ 189 proc_t * 190 lxpr_lock(pid_t pid) 191 { 192 proc_t *p; 193 kmutex_t *mp; 194 195 ASSERT(!MUTEX_HELD(&pidlock)); 196 197 for (;;) { 198 mutex_enter(&pidlock); 199 200 /* 201 * If the pid is 1, we really want the zone's init process 202 */ 203 p = prfind((pid == 1) ? 204 curproc->p_zone->zone_proc_initpid : pid); 205 206 if (p == NULL || p->p_stat == SIDL) { 207 mutex_exit(&pidlock); 208 return (NULL); 209 } 210 /* 211 * p_lock is persistent, but p itself is not -- it could 212 * vanish during cv_wait(). Load p->p_lock now so we can 213 * drop it after cv_wait() without referencing p. 214 */ 215 mp = &p->p_lock; 216 mutex_enter(mp); 217 218 mutex_exit(&pidlock); 219 220 if (!(p->p_proc_flag & P_PR_LOCK)) 221 break; 222 223 cv_wait(&pr_pid_cv[p->p_slot], mp); 224 mutex_exit(mp); 225 } 226 p->p_proc_flag |= P_PR_LOCK; 227 THREAD_KPRI_REQUEST(); 228 return (p); 229 } 230 231 /* 232 * lxpr_unlock() 233 * 234 * Unlock locked process 235 */ 236 void 237 lxpr_unlock(proc_t *p) 238 { 239 ASSERT(p->p_proc_flag & P_PR_LOCK); 240 ASSERT(MUTEX_HELD(&p->p_lock)); 241 ASSERT(!MUTEX_HELD(&pidlock)); 242 243 cv_signal(&pr_pid_cv[p->p_slot]); 244 p->p_proc_flag &= ~P_PR_LOCK; 245 mutex_exit(&p->p_lock); 246 THREAD_KPRI_RELEASE(); 247 } 248 249 void 250 lxpr_initnodecache() 251 { 252 lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME, 253 sizeof (lxpr_node_t), 0, 254 lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0); 255 } 256 257 void 258 lxpr_fininodecache() 259 { 260 kmem_cache_destroy(lxpr_node_cache); 261 } 262 263 /* ARGSUSED */ 264 static int 265 lxpr_node_constructor(void *buf, void *un, int kmflags) 266 { 267 lxpr_node_t *lxpnp = buf; 268 vnode_t *vp; 269 270 vp = lxpnp->lxpr_vnode = vn_alloc(kmflags); 271 if (vp == NULL) 272 return (-1); 273 274 (void) vn_setops(vp, lxpr_vnodeops); 275 vp->v_data = lxpnp; 276 277 return (0); 278 } 279 280 /* ARGSUSED */ 281 static void 282 lxpr_node_destructor(void *buf, void *un) 283 { 284 lxpr_node_t *lxpnp = buf; 285 286 vn_free(LXPTOV(lxpnp)); 287 } 288 289 /* 290 * Calculate an inode number 291 * 292 * This takes various bits of info and munges them 293 * to give the inode number for an lxproc node 294 */ 295 ino_t 296 lxpr_inode(lxpr_nodetype_t type, pid_t pid, int fd) 297 { 298 if (pid == 1) 299 pid = curproc->p_zone->zone_proc_initpid; 300 301 switch (type) { 302 case LXPR_PIDDIR: 303 return (pid + 1); 304 case LXPR_PROCDIR: 305 return (maxpid + 2); 306 case LXPR_PID_FD_FD: 307 return (maxpid + 2 + 308 (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) + 309 LXPR_NFILES + fd); 310 default: 311 return (maxpid + 2 + 312 (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) + 313 type); 314 } 315 } 316 317 /* 318 * Return inode number of parent (directory) 319 */ 320 ino_t 321 lxpr_parentinode(lxpr_node_t *lxpnp) 322 { 323 /* 324 * If the input node is the root then the parent inode 325 * is the mounted on inode so just return our inode number 326 */ 327 if (lxpnp->lxpr_type != LXPR_PROCDIR) 328 return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino); 329 else 330 return (lxpnp->lxpr_ino); 331 } 332 333 /* 334 * Allocate a new lxproc node 335 * 336 * This also allocates the vnode associated with it 337 */ 338 lxpr_node_t * 339 lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int fd) 340 { 341 lxpr_node_t *lxpnp; 342 vnode_t *vp; 343 user_t *up; 344 timestruc_t now; 345 346 /* 347 * Allocate a new node. It is deallocated in vop_innactive 348 */ 349 lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP); 350 351 /* 352 * Set defaults (may be overridden below) 353 */ 354 gethrestime(&now); 355 lxpnp->lxpr_type = type; 356 lxpnp->lxpr_realvp = NULL; 357 lxpnp->lxpr_parent = dp; 358 VN_HOLD(dp); 359 if (p != NULL) { 360 lxpnp->lxpr_pid = ((p->p_pid == 361 curproc->p_zone->zone_proc_initpid) ? 1 : p->p_pid); 362 363 lxpnp->lxpr_time = PTOU(p)->u_start; 364 lxpnp->lxpr_uid = crgetruid(p->p_cred); 365 lxpnp->lxpr_gid = crgetrgid(p->p_cred); 366 lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, fd); 367 } else { 368 /* Pretend files without a proc belong to sched */ 369 lxpnp->lxpr_pid = 0; 370 lxpnp->lxpr_time = now; 371 lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0; 372 lxpnp->lxpr_ino = lxpr_inode(type, 0, 0); 373 } 374 375 /* initialize the vnode data */ 376 vp = lxpnp->lxpr_vnode; 377 vn_reinit(vp); 378 vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT; 379 vp->v_vfsp = dp->v_vfsp; 380 381 /* 382 * Do node specific stuff 383 */ 384 switch (type) { 385 case LXPR_PROCDIR: 386 vp->v_flag |= VROOT; 387 vp->v_type = VDIR; 388 lxpnp->lxpr_mode = 0555; /* read-search by everyone */ 389 break; 390 391 case LXPR_PID_CURDIR: 392 ASSERT(p != NULL); 393 394 /* 395 * Zombie check. p_stat is officially protected by pidlock, 396 * but we can't grab pidlock here because we already hold 397 * p_lock. Luckily if we look at the process exit code 398 * we see that p_stat only transisions from SRUN to SZOMB 399 * while p_lock is held. Aside from this, the only other 400 * p_stat transition that we need to be aware about is 401 * SIDL to SRUN, but that's not a problem since lxpr_lock() 402 * ignores nodes in the SIDL state so we'll never get a node 403 * that isn't already in the SRUN state. 404 */ 405 if (p->p_stat == SZOMB) { 406 lxpnp->lxpr_realvp = NULL; 407 } else { 408 up = PTOU(p); 409 lxpnp->lxpr_realvp = up->u_cdir; 410 ASSERT(lxpnp->lxpr_realvp != NULL); 411 VN_HOLD(lxpnp->lxpr_realvp); 412 } 413 vp->v_type = VLNK; 414 lxpnp->lxpr_mode = 0777; /* anyone does anything ! */ 415 break; 416 417 case LXPR_PID_ROOTDIR: 418 ASSERT(p != NULL); 419 /* Zombie check. see locking comment above */ 420 if (p->p_stat == SZOMB) { 421 lxpnp->lxpr_realvp = NULL; 422 } else { 423 up = PTOU(p); 424 lxpnp->lxpr_realvp = 425 up->u_rdir != NULL ? up->u_rdir : rootdir; 426 ASSERT(lxpnp->lxpr_realvp != NULL); 427 VN_HOLD(lxpnp->lxpr_realvp); 428 } 429 vp->v_type = VLNK; 430 lxpnp->lxpr_mode = 0777; /* anyone does anything ! */ 431 break; 432 433 case LXPR_PID_EXE: 434 ASSERT(p != NULL); 435 lxpnp->lxpr_realvp = p->p_exec; 436 if (lxpnp->lxpr_realvp != NULL) { 437 VN_HOLD(lxpnp->lxpr_realvp); 438 } 439 vp->v_type = VLNK; 440 lxpnp->lxpr_mode = 0777; 441 break; 442 443 case LXPR_SELF: 444 vp->v_type = VLNK; 445 lxpnp->lxpr_mode = 0777; /* anyone does anything ! */ 446 break; 447 448 case LXPR_PID_FD_FD: 449 ASSERT(p != NULL); 450 /* lxpr_realvp is set after we return */ 451 vp->v_type = VLNK; 452 lxpnp->lxpr_mode = 0700; /* read-write-exe owner only */ 453 break; 454 455 case LXPR_PID_FDDIR: 456 ASSERT(p != NULL); 457 vp->v_type = VDIR; 458 lxpnp->lxpr_mode = 0500; /* read-search by owner only */ 459 break; 460 461 case LXPR_PIDDIR: 462 ASSERT(p != NULL); 463 vp->v_type = VDIR; 464 lxpnp->lxpr_mode = 0511; 465 break; 466 467 case LXPR_NETDIR: 468 vp->v_type = VDIR; 469 lxpnp->lxpr_mode = 0555; /* read-search by all */ 470 break; 471 472 case LXPR_PID_ENV: 473 case LXPR_PID_MEM: 474 ASSERT(p != NULL); 475 /*FALLTHRU*/ 476 case LXPR_KCORE: 477 vp->v_type = VREG; 478 lxpnp->lxpr_mode = 0400; /* read-only by owner only */ 479 break; 480 481 default: 482 vp->v_type = VREG; 483 lxpnp->lxpr_mode = 0444; /* read-only by all */ 484 break; 485 } 486 487 return (lxpnp); 488 } 489 490 491 /* 492 * Free the storage obtained from lxpr_getnode(). 493 */ 494 void 495 lxpr_freenode(lxpr_node_t *lxpnp) 496 { 497 ASSERT(lxpnp != NULL); 498 ASSERT(LXPTOV(lxpnp) != NULL); 499 500 /* 501 * delete any association with realvp 502 */ 503 if (lxpnp->lxpr_realvp != NULL) 504 VN_RELE(lxpnp->lxpr_realvp); 505 506 /* 507 * delete any association with parent vp 508 */ 509 if (lxpnp->lxpr_parent != NULL) 510 VN_RELE(lxpnp->lxpr_parent); 511 512 /* 513 * Release the lxprnode. 514 */ 515 kmem_cache_free(lxpr_node_cache, lxpnp); 516 }