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 }