lx_prvnops.c revision 319378d99bca1eacf64f0a464b0175bf66b422ab
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* lxpr_vnops.c: Vnode operations for the lx /proc file system
*
* Assumptions and Gotchas:
*
* In order to preserve Solaris' security policy. This file system's
* functionality does not override Solaris' security policies even if
* that means breaking Linux compatability.
*
* Linux has no concept of lwps so we only implement procs here as in the
* old /proc interface.
*/
#include <lx_signum.h>
#include <sys/lx_brand.h>
#include <sys/x86_archext.h>
#include <sys/archsystm.h>
#include <sys/pool_pset.h>
#include <sys/vfs_opreg.h>
/* Dependent on the Solaris procfs */
#include "lx_proc.h"
extern pgcnt_t swapfs_minfree;
/*
* Pointer to the vnode ops vector for this fs.
* This is instantiated in lxprinit() in lxpr_vfsops.c
*/
static int lxpr_sync(void);
/*
* Simple conversion
*/
/*
* The lx /proc vnode operations vector
*/
const fs_operation_def_t lxpr_vnodeops_template[] = {
};
/*
* file contents of an lx /proc directory.
*/
static lxpr_dirent_t lx_procdir[] = {
{ LXPR_CMDLINE, "cmdline" },
{ LXPR_CPUINFO, "cpuinfo" },
{ LXPR_DEVICES, "devices" },
{ LXPR_DMA, "dma" },
{ LXPR_FILESYSTEMS, "filesystems" },
{ LXPR_INTERRUPTS, "interrupts" },
{ LXPR_IOPORTS, "ioports" },
{ LXPR_KCORE, "kcore" },
{ LXPR_KMSG, "kmsg" },
{ LXPR_LOADAVG, "loadavg" },
{ LXPR_MEMINFO, "meminfo" },
{ LXPR_MOUNTS, "mounts" },
{ LXPR_NETDIR, "net" },
{ LXPR_PARTITIONS, "partitions" },
{ LXPR_SELF, "self" },
{ LXPR_STAT, "stat" },
{ LXPR_UPTIME, "uptime" },
{ LXPR_VERSION, "version" }
};
/*
* Contents of an lx /proc/<pid> directory.
*/
static lxpr_dirent_t piddir[] = {
{ LXPR_PID_CMDLINE, "cmdline" },
{ LXPR_PID_CPU, "cpu" },
{ LXPR_PID_CURDIR, "cwd" },
{ LXPR_PID_ENV, "environ" },
{ LXPR_PID_EXE, "exe" },
{ LXPR_PID_MAPS, "maps" },
{ LXPR_PID_MEM, "mem" },
{ LXPR_PID_ROOTDIR, "root" },
{ LXPR_PID_STAT, "stat" },
{ LXPR_PID_STATM, "statm" },
{ LXPR_PID_STATUS, "status" },
{ LXPR_PID_FDDIR, "fd" }
};
/*
*/
static lxpr_dirent_t netdir[] = {
{ LXPR_NET_ARP, "arp" },
{ LXPR_NET_DEV, "dev" },
{ LXPR_NET_DEV_MCAST, "dev_mcast" },
{ LXPR_NET_IGMP, "igmp" },
{ LXPR_NET_IP_MR_CACHE, "ip_mr_cache" },
{ LXPR_NET_IP_MR_VIF, "ip_mr_vif" },
{ LXPR_NET_MCFILTER, "mcfilter" },
{ LXPR_NET_NETSTAT, "netstat" },
{ LXPR_NET_RAW, "raw" },
{ LXPR_NET_ROUTE, "route" },
{ LXPR_NET_RPC, "rpc" },
{ LXPR_NET_RT_CACHE, "rt_cache" },
{ LXPR_NET_SOCKSTAT, "sockstat" },
{ LXPR_NET_SNMP, "snmp" },
{ LXPR_NET_STAT, "stat" },
{ LXPR_NET_TCP, "tcp" },
{ LXPR_NET_UDP, "udp" },
{ LXPR_NET_UNIX, "unix" }
};
/*
* lxpr_open(): Vnode operation for VOP_OPEN()
*/
static int
{
int error = 0;
/*
* We only allow reading in this file systrem
*/
return (EROFS);
/*
* If we are opening an underlying file only allow regular files
* reject the open for anything but a regular file.
* Just do it if we are opening the current or root directory.
*/
else {
/*
* Need to hold rvp since VOP_OPEN() may release it.
*/
if (error) {
} else {
}
}
}
int rv;
/*
* Open the zone's console device using the layered driver
* interface.
*/
return (error);
/*
* Send an ioctl to the underlying console device, letting it
* know we're interested in getting console messages.
*/
return (error);
}
return (error);
}
/*
* lxpr_close(): Vnode operation for VOP_CLOSE()
*/
/* ARGSUSED */
static int
{
int err;
/*
* we should never get here because the close is done on the realvp
* for these nodes
*/
type != LXPR_PID_CURDIR &&
type != LXPR_PID_ROOTDIR &&
type != LXPR_PID_EXE);
return (err);
}
return (0);
}
static void (*lxpr_read_function[LXPR_NFILES])() = {
lxpr_read_isdir, /* /proc */
lxpr_read_isdir, /* /proc/<pid> */
lxpr_read_pid_cmdline, /* /proc/<pid>/cmdline */
lxpr_read_empty, /* /proc/<pid>/cpu */
lxpr_read_invalid, /* /proc/<pid>/cwd */
lxpr_read_empty, /* /proc/<pid>/environ */
lxpr_read_invalid, /* /proc/<pid>/exe */
lxpr_read_pid_maps, /* /proc/<pid>/maps */
lxpr_read_empty, /* /proc/<pid>/mem */
lxpr_read_invalid, /* /proc/<pid>/root */
lxpr_read_pid_stat, /* /proc/<pid>/stat */
lxpr_read_pid_statm, /* /proc/<pid>/statm */
lxpr_read_pid_status, /* /proc/<pid>/status */
lxpr_read_isdir, /* /proc/<pid>/fd */
lxpr_read_fd, /* /proc/<pid>/fd/nn */
lxpr_read_empty, /* /proc/cmdline */
lxpr_read_cpuinfo, /* /proc/cpuinfo */
lxpr_read_empty, /* /proc/devices */
lxpr_read_empty, /* /proc/dma */
lxpr_read_empty, /* /proc/filesystems */
lxpr_read_empty, /* /proc/interrupts */
lxpr_read_empty, /* /proc/ioports */
lxpr_read_empty, /* /proc/kcore */
lxpr_read_kmsg, /* /proc/kmsg */
lxpr_read_loadavg, /* /proc/loadavg */
lxpr_read_meminfo, /* /proc/meminfo */
lxpr_read_mounts, /* /proc/mounts */
lxpr_read_isdir, /* /proc/net */
lxpr_read_net_arp, /* /proc/net/arp */
lxpr_read_net_dev, /* /proc/net/dev */
lxpr_read_net_dev_mcast, /* /proc/net/dev_mcast */
lxpr_read_net_igmp, /* /proc/net/igmp */
lxpr_read_net_ip_mr_cache, /* /proc/net/ip_mr_cache */
lxpr_read_net_ip_mr_vif, /* /proc/net/ip_mr_vif */
lxpr_read_net_mcfilter, /* /proc/net/mcfilter */
lxpr_read_net_netstat, /* /proc/net/netstat */
lxpr_read_net_raw, /* /proc/net/raw */
lxpr_read_net_route, /* /proc/net/route */
lxpr_read_net_rpc, /* /proc/net/rpc */
lxpr_read_net_rt_cache, /* /proc/net/rt_cache */
lxpr_read_net_sockstat, /* /proc/net/sockstat */
lxpr_read_net_snmp, /* /proc/net/snmp */
lxpr_read_net_stat, /* /proc/net/stat */
lxpr_read_net_tcp, /* /proc/net/tcp */
lxpr_read_net_udp, /* /proc/net/udp */
lxpr_read_net_unix, /* /proc/net/unix */
lxpr_read_partitions, /* /proc/partitions */
lxpr_read_invalid, /* /proc/self */
lxpr_read_stat, /* /proc/stat */
lxpr_read_uptime, /* /proc/uptime */
lxpr_read_version, /* /proc/version */
};
/*
* Array of lookup functions, indexed by lx /proc file type.
*/
lxpr_lookup_procdir, /* /proc */
lxpr_lookup_piddir, /* /proc/<pid> */
lxpr_lookup_not_a_dir, /* /proc/<pid>/cmdline */
lxpr_lookup_not_a_dir, /* /proc/<pid>/cpu */
lxpr_lookup_not_a_dir, /* /proc/<pid>/cwd */
lxpr_lookup_not_a_dir, /* /proc/<pid>/environ */
lxpr_lookup_not_a_dir, /* /proc/<pid>/exe */
lxpr_lookup_not_a_dir, /* /proc/<pid>/maps */
lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
lxpr_lookup_not_a_dir, /* /proc/<pid>/status */
lxpr_lookup_fddir, /* /proc/<pid>/fd */
lxpr_lookup_not_a_dir, /* /proc/<pid>/fd/nn */
lxpr_lookup_not_a_dir, /* /proc/cmdline */
lxpr_lookup_not_a_dir, /* /proc/cpuinfo */
lxpr_lookup_not_a_dir, /* /proc/devices */
lxpr_lookup_not_a_dir, /* /proc/dma */
lxpr_lookup_not_a_dir, /* /proc/filesystems */
lxpr_lookup_not_a_dir, /* /proc/interrupts */
lxpr_lookup_not_a_dir, /* /proc/ioports */
lxpr_lookup_not_a_dir, /* /proc/kcore */
lxpr_lookup_not_a_dir, /* /proc/kmsg */
lxpr_lookup_not_a_dir, /* /proc/loadavg */
lxpr_lookup_not_a_dir, /* /proc/meminfo */
lxpr_lookup_not_a_dir, /* /proc/mounts */
lxpr_lookup_netdir, /* /proc/net */
lxpr_lookup_not_a_dir, /* /proc/net/arp */
lxpr_lookup_not_a_dir, /* /proc/net/dev */
lxpr_lookup_not_a_dir, /* /proc/net/dev_mcast */
lxpr_lookup_not_a_dir, /* /proc/net/igmp */
lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_cache */
lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_vif */
lxpr_lookup_not_a_dir, /* /proc/net/mcfilter */
lxpr_lookup_not_a_dir, /* /proc/net/netstat */
lxpr_lookup_not_a_dir, /* /proc/net/raw */
lxpr_lookup_not_a_dir, /* /proc/net/route */
lxpr_lookup_not_a_dir, /* /proc/net/rpc */
lxpr_lookup_not_a_dir, /* /proc/net/rt_cache */
lxpr_lookup_not_a_dir, /* /proc/net/sockstat */
lxpr_lookup_not_a_dir, /* /proc/net/snmp */
lxpr_lookup_not_a_dir, /* /proc/net/stat */
lxpr_lookup_not_a_dir, /* /proc/net/tcp */
lxpr_lookup_not_a_dir, /* /proc/net/udp */
lxpr_lookup_not_a_dir, /* /proc/net/unix */
lxpr_lookup_not_a_dir, /* /proc/partitions */
lxpr_lookup_not_a_dir, /* /proc/self */
lxpr_lookup_not_a_dir, /* /proc/stat */
lxpr_lookup_not_a_dir, /* /proc/uptime */
lxpr_lookup_not_a_dir, /* /proc/version */
};
/*
* Array of readdir functions, indexed by /proc file type.
*/
static int (*lxpr_readdir_function[LXPR_NFILES])() = {
lxpr_readdir_procdir, /* /proc */
lxpr_readdir_piddir, /* /proc/<pid> */
lxpr_readdir_not_a_dir, /* /proc/<pid>/cmdline */
lxpr_readdir_not_a_dir, /* /proc/<pid>/cpu */
lxpr_readdir_not_a_dir, /* /proc/<pid>/cwd */
lxpr_readdir_not_a_dir, /* /proc/<pid>/environ */
lxpr_readdir_not_a_dir, /* /proc/<pid>/exe */
lxpr_readdir_not_a_dir, /* /proc/<pid>/maps */
lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
lxpr_readdir_not_a_dir, /* /proc/<pid>/status */
lxpr_readdir_fddir, /* /proc/<pid>/fd */
lxpr_readdir_not_a_dir, /* /proc/<pid>/fd/nn */
lxpr_readdir_not_a_dir, /* /proc/cmdline */
lxpr_readdir_not_a_dir, /* /proc/cpuinfo */
lxpr_readdir_not_a_dir, /* /proc/devices */
lxpr_readdir_not_a_dir, /* /proc/dma */
lxpr_readdir_not_a_dir, /* /proc/filesystems */
lxpr_readdir_not_a_dir, /* /proc/interrupts */
lxpr_readdir_not_a_dir, /* /proc/ioports */
lxpr_readdir_not_a_dir, /* /proc/kcore */
lxpr_readdir_not_a_dir, /* /proc/kmsg */
lxpr_readdir_not_a_dir, /* /proc/loadavg */
lxpr_readdir_not_a_dir, /* /proc/meminfo */
lxpr_readdir_not_a_dir, /* /proc/mounts */
lxpr_readdir_netdir, /* /proc/net */
lxpr_readdir_not_a_dir, /* /proc/net/arp */
lxpr_readdir_not_a_dir, /* /proc/net/dev */
lxpr_readdir_not_a_dir, /* /proc/net/dev_mcast */
lxpr_readdir_not_a_dir, /* /proc/net/igmp */
lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_cache */
lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_vif */
lxpr_readdir_not_a_dir, /* /proc/net/mcfilter */
lxpr_readdir_not_a_dir, /* /proc/net/netstat */
lxpr_readdir_not_a_dir, /* /proc/net/raw */
lxpr_readdir_not_a_dir, /* /proc/net/route */
lxpr_readdir_not_a_dir, /* /proc/net/rpc */
lxpr_readdir_not_a_dir, /* /proc/net/rt_cache */
lxpr_readdir_not_a_dir, /* /proc/net/sockstat */
lxpr_readdir_not_a_dir, /* /proc/net/snmp */
lxpr_readdir_not_a_dir, /* /proc/net/stat */
lxpr_readdir_not_a_dir, /* /proc/net/tcp */
lxpr_readdir_not_a_dir, /* /proc/net/udp */
lxpr_readdir_not_a_dir, /* /proc/net/unix */
lxpr_readdir_not_a_dir, /* /proc/partitions */
lxpr_readdir_not_a_dir, /* /proc/self */
lxpr_readdir_not_a_dir, /* /proc/stat */
lxpr_readdir_not_a_dir, /* /proc/uptime */
lxpr_readdir_not_a_dir, /* /proc/version */
};
/*
* lxpr_read(): Vnode operation for VOP_READ()
*
* As the format of all the files that can be read in the lx procfs is human
* readable and not binary structures there do not have to be different
* read variants depending on whether the reading process model is 32 or 64 bits
* (at least in general, and certainly the difference is unlikely to be enough
* to justify have different routines for 32 and 64 bit reads
*/
/* ARGSUSED */
static int
{
int error;
return (error);
}
/*
* lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
*
* Various special case reads:
* - trying to read a directory
* - invalid file (used to mean a file that should be implemented,
* but isn't yet)
* - empty file
* - wait to be able to read a file that will never have anything to read
*/
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/*
* lxpr_read_pid_cmdline():
*
* This is not precisely compatible with linux:
*
* The linux cmdline returns argv with the correct separation
* using \0 between the arguments, we cannot do that without
* copying the real argv from the correct process context.
* This is too difficult to attempt so we pretend that the
* entire cmdline is just argv[0]. This is good enough for
* ps to display correctly, but might cause some other things
* not to work correctly.
*/
static void
{
proc_t *p;
if (p == NULL) {
return;
}
lxpr_unlock(p);
} else {
lxpr_unlock(p);
}
}
/*
* lxpr_read_pid_maps(): memory map file
*/
static void
{
proc_t *p;
char *buf;
int buflen = MAXPATHLEN;
struct print_data {
int type;
char prot[5];
struct print_data *next;
} *print_head = NULL;
struct print_data *pbuf;
if (p == NULL) {
return;
}
lxpr_unlock(p);
return;
}
mutex_exit(&p->p_lock);
/* Iterate over all segments in the address space */
/*
* Cheat and only use the protection bits of the first page
* in the segment
*/
} else {
}
*print_tail = pbuf;
}
mutex_enter(&p->p_lock);
lxpr_unlock(p);
/* print the data we've extracted */
pbuf = print_head;
struct print_data *pbuf_next;
int maj = 0;
int min = 0;
int inode = 0;
*buf = '\0';
}
}
if (*buf != '\0') {
"%08x-%08x %s %08x %02d:%03d %d %s\n",
} else {
"%08x-%08x %s %08x %02d:%03d %d\n",
}
}
}
/*
* lxpr_read_pid_statm(): memory status file
*/
static void
{
proc_t *p;
if (p == NULL) {
return;
}
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
lxpr_unlock(p);
"%lu %lu %lu %lu %lu %lu %lu\n",
}
/*
* lxpr_read_pid_status(): status file
*/
static void
{
proc_t *p;
kthread_t *t;
int ngroups;
char *status;
int i, lx_sig;
if (p == NULL) {
return;
}
/*
* Convert pid to the Linux default of 1 if we're the zone's init
* process
*/
pid = 1;
ppid = 0; /* parent pid for init is 0 */
} else {
/*
* Make sure not to reference parent PIDs that reside outside
* the zone
*/
/*
* Convert ppid to the Linux default of 1 if our parent is the
* zone's init process
*/
ppid = 1;
}
t = prchoose(p);
if (t != NULL) {
switch (t->t_state) {
case TS_SLEEP:
status = "S (sleeping)";
break;
case TS_RUN:
case TS_ONPROC:
status = "R (running)";
break;
case TS_ZOMB:
status = "Z (zombie)";
break;
case TS_STOPPED:
status = "T (stopped)";
break;
default:
status = "! (unknown)";
break;
}
thread_unlock(t);
} else {
/*
* there is a hole in the exit code, where a proc can have
* no threads but it is yet to be flagged SZOMB. We will
* assume we are about to become a zombie
*/
status = "Z (zombie)";
}
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
"Name:\t%s\n"
"State:\t%s\n"
"Tgid:\t%d\n"
"Pid:\t%d\n"
"PPid:\t%d\n"
"TracerPid:\t%d\n"
"Uid:\t%u\t%u\t%u\t%u\n"
"Gid:\t%u\t%u\t%u\t%u\n"
"FDSize:\t%d\n"
"Groups:\t",
pid, /* thread group id - same as pid until we map lwps to procs */
pid,
ppid,
0,
p->p_fno_ctl);
for (i = 0; i < ngroups; i++) {
"%u ",
groups[i]);
}
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
"\n"
"VmSize:\t%8lu kB\n"
"VmLck:\t%8lu kB\n"
"VmRSS:\t%8lu kB\n"
"VmData:\t%8lu kB\n"
"VmStk:\t%8lu kB\n"
"VmExe:\t%8lu kB\n"
"VmLib:\t%8lu kB",
0l,
0l,
0l);
}
for (i = 1; i < MAXSIG; i++) {
lx_sig = stol_signo[i];
if (sigismember(&p->p_sig, i))
}
}
"\n"
"SigPnd:\t%08x%08x\n"
"SigBlk:\t%08x%08x\n"
"SigIgn:\t%08x%08x\n"
"SigCgt:\t%08x%08x\n"
"CapInh:\t%016x\n"
"CapPrm:\t%016x\n"
"CapEff:\t%016x\n",
0, 0, /* signals blocked on per thread basis */
/* Can't do anything with linux capabilities */
0,
0,
0);
lxpr_unlock(p);
}
/*
* lxpr_read_pid_stat(): pid stat file
*/
static void
{
proc_t *p;
kthread_t *t;
char stat;
if (p == NULL) {
return;
}
/*
* Set Linux defaults if we're the zone's init process
*/
ppid = 0; /* parent PID for init is 0 */
pgpid = 0; /* process group for init is 0 */
spid = 0; /* session id for init is 0 */
psdev = 0; /* session device for init is 0 */
} else {
/*
* Make sure not to reference parent PIDs that reside outside
* the zone
*/
/*
* Convert ppid to the Linux default of 1 if our parent is the
* zone's init process
*/
ppid = 1;
mutex_enter(&p->p_splock);
/* XXBRAND psdev = DEV_TO_LXDEV(p->p_sessp->s_dev, VCHR); */
else
mutex_exit(&p->p_splock);
}
t = prchoose(p);
if (t != NULL) {
switch (t->t_state) {
case TS_SLEEP:
stat = 'S'; break;
case TS_RUN:
case TS_ONPROC:
stat = 'R'; break;
case TS_ZOMB:
stat = 'Z'; break;
case TS_STOPPED:
stat = 'T'; break;
default:
stat = '!'; break;
}
nice = 0;
thread_unlock(t);
} else {
/* Only zombies have no threads */
stat = 'Z';
nice = 0;
pri = 0;
wchan = 0;
cpu = 0;
}
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
"%d (%s) %c %d %d %d %d %d "
"%lu %lu %lu %lu %lu "
"%lu %lu %ld %ld "
"%d %d "
"0 "
"%ld %lu "
"%lu %ld %llu "
"%lu %lu %u "
"%lu %lu "
"%lu %lu %lu %lu "
"%lu "
"%lu %lu "
"%d "
"%d"
"\n",
pid,
stat,
0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
0l, 0l, USRSTACK, /* startcode, endcode, startstack */
0l, 0l, /* kstkesp, kstkeip */
0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch */
0l, 0l, /* nswap, cnswap */
0, /* exit_signal */
cpu);
lxpr_unlock(p);
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
" | Transmit\n");
" frame compressed multicast|bytes packets errs drop fifo"
" colls carrier compressed\n");
/*
* XXX: data about each interface should go here, but we'll wait to
* see if anybody wants to use it.
*/
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
}
/*
* lxpr_read_kmsg(): read the contents of the kernel message queue. We
* translate this into the reception of console messages for this lx zone; each
* read copies out a single zone console message, or blocks until the next one
* is produced.
*/
#define LX_KMSG_PRI "<0>"
static void
{
/*
* lx procfs doesn't like successive reads to the same file
* descriptor unless we do an explicit rewind each time.
*/
lxpr_uiobuf_seek(uiobuf, 0);
}
}
/*
* lxpr_read_loadavg(): read the contents of the "loadavg" file.
*
* Just enough for uptime to work
*/
extern int nthread;
static void
{
int loadavg[3];
int *loadbuf;
/*
* Need to add up values over all CPU partitions. If pools are active,
* only report the values of the zone's partition, which by definition
* includes the current CPU.
*/
if (pool_pset_enabled()) {
/*
* We'll report the total number of lwps in the zone for the
*/
} else {
cp = cp_list_head;
do {
/*
* This will report kernel threads as well as user lwps, but it
* should be good enough for lx consumers.
*/
}
"%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
}
/*
* lxpr_read_meminfo(): read the contents of the "meminfo" file.
*/
static void
{
" total: used: free: shared: buffers: cached:\n"
"Mem: %8lu %8lu %8lu %8u %8u %8u\n"
"Swap: %8lu %8lu %8lu\n"
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"MemShared: %8u kB\n"
"Buffers: %8u kB\n"
"Cached: %8u kB\n"
"SwapCached:%8u kB\n"
"Active: %8u kB\n"
"Inactive: %8u kB\n"
"HighTotal: %8u kB\n"
"HighFree: %8u kB\n"
"LowTotal: %8u kB\n"
"LowFree: %8u kB\n"
"SwapTotal: %8lu kB\n"
"SwapFree: %8lu kB\n",
0, /* MemShared */
0, /* Buffers */
0, /* Cached */
0, /* SwapCached */
0, /* Active */
0, /* Inactive */
0, /* HighTotal */
0, /* HighFree */
}
/*
* lxpr_read_mounts():
*/
/* ARGSUSED */
static void
{
struct print_data {
int vfs_fstype;
struct print_data *next;
} *print_head = NULL;
struct print_data *printp;
if (zone == global_zone) {
} else {
/*
* If the zone has a root entry, it will be the first in
* the list. If it doesn't, we conjure one up.
*/
zone->zone_rootpath) != 0) {
/*
* The root of the zone is not a mount point. The vfs
* we want to report is that of the zone's root vnode.
*/
"/ / %s %s 0 0\n",
}
return;
}
}
/*
* Later on we have to do a lookupname, which can end up causing
* another vfs_list_read_lock() to be called. Which can lead to a
* deadlock. To avoid this, we extract the data we need into a local
* list, then we can run this list without holding vfs_list_read_lock()
* We keep the list in the same order as the vfs_list
*/
do {
/* Skip mounts we shouldn't show */
goto nextfs;
}
*print_tail = printp;
/*
* now we can run through what we've extracted without holding
* vfs_list_read_lock()
*/
printp = print_head;
struct print_data *printp_next;
const char *resource;
char *mntpt;
int error;
else
mntpt = "-";
if (error != 0)
goto nextp;
goto nextp;
}
if (resource[0] == '/') {
}
} else {
resource = "-";
}
"%s %s %s %s 0 0\n",
}
}
/*
* lxpr_read_partitions():
*
* We don't support partitions in a local zone because it requires access to
* physical devices. But we need to fake up enough of the file to show that we
* have no partitions.
*/
/* ARGSUSED */
static void
{
"major minor #blocks name rio rmerge rsect ruse "
"wio wmerge wsect wuse running use aveq\n\n");
}
/*
* lxpr_read_version(): read the contents of the "version" file.
*/
/* ARGSUSED */
static void
{
char *vers;
else
"%s version %s (%s version %d.%d.%d) "
"#%s SMP %s\n",
#if defined(__GNUC__)
"gcc",
#else
"Sun C",
__SUNPRO_C / 0x100,
__SUNPRO_C & 0xf,
#endif
"00:00:00 %E%");
}
/*
* lxpr_read_stat(): read the contents of the "stat" file.
*
*/
/* ARGSUSED */
static void
{
int pools_enabled;
uint_t cpu_nrunnable_cum = 0;
ulong_t pgpgin_cum = 0;
ulong_t pgpgout_cum = 0;
ulong_t pgswapout_cum = 0;
ulong_t pgswapin_cum = 0;
ulong_t pswitch_cum = 0;
/* temporary variable since scalehrtime modifies data in place */
/* Calculate cumulative stats */
do {
int i;
/*
* Don't count CPUs that aren't even in the system
* or aren't up yet.
*/
continue;
}
if (lx_kern_version >= LX_KERN_2_6) {
for (i = 0; i < NCMSTATES; i++) {
}
}
for (i = 0; i < PIL_MAX; i++)
if (pools_enabled)
else
if (lx_kern_version >= LX_KERN_2_6) {
"cpu %ld %ld %ld %ld %ld %ld %ld\n",
} else {
"cpu %ld %ld %ld %ld\n",
}
/* Do per processor stats */
do {
int i;
/*
* Don't count CPUs that aren't even in the system
* or aren't up yet.
*/
continue;
}
if (lx_kern_version >= LX_KERN_2_6) {
for (i = 0; i < NCMSTATES; i++) {
}
"cpu%d %ld %ld %ld %ld %ld %ld %ld\n",
0, irq_ticks, 0);
} else {
"cpu%d %ld %ld %ld %ld\n",
}
if (pools_enabled)
else
if (lx_kern_version >= LX_KERN_2_6) {
"page %lu %lu\n"
"swap %lu %lu\n"
"intr %lu\n"
"ctxt %lu\n"
"btime %lu\n"
"processes %lu\n"
"procs_running %lu\n"
"procs_blocked %lu\n",
w_io_cum);
} else {
"page %lu %lu\n"
"swap %lu %lu\n"
"intr %lu\n"
"ctxt %lu\n"
"btime %lu\n"
"processes %lu\n",
}
}
/*
* lxpr_read_uptime(): read the contents of the "uptime" file.
*
* format is: "%.2lf, %.2lf",uptime_secs, idle_secs
* Use fixed point arithmetic to get 2 decimal places
*/
/* ARGSUSED */
static void
{
pid_t p;
int pools_enabled;
/* Calculate cumulative stats */
do {
/*
* Don't count CPUs that aren't even in the system
* or aren't up yet.
*/
continue;
}
cpu_count += 1;
if (pools_enabled)
else
/* Getting the Zone init process startup time */
up_cs %= 100;
idle_cs *= 100;
}
static const char *amd_x_edx[] = {
};
static const char *amd_x_ecx[] = {
"altmovcr8"
};
static const char *tm_x_edx[] = {
};
/*
* Intel calls no-execute "xd" in its docs, but Linux still reports it as "nx."
*/
static const char *intc_x_edx[] = {
};
static const char *intc_edx[] = {
"fpu", "vme", "de", "pse",
"tsc", "msr", "pae", "mce",
"mtrr", "pge", "mca", "cmov",
"pat", "pse36", "pn", "clflush",
"fxsr", "sse", "sse2", "ss",
"ht", "tm", "ia64", "pbe"
};
/*
* "sse3" on linux is called "pni" (Prescott New Instructions).
*/
static const char *intc_ecx[] = {
};
static void
{
int i;
int pools_enabled;
const char **fp;
char brandstr[CPU_IDSTRLEN];
struct cpuid_regs cpr;
int maxeax;
do {
/*
* This returns the maximum eax value for standard cpuid
* functions in eax.
*/
/*
* Get standard x86 feature flags.
*/
/*
* Now get extended feature flags.
*/
"processor\t: %d\n"
"vendor_id\t: %s\n"
"cpu family\t: %d\n"
"model\t\t: %d\n"
"model name\t: %s\n"
"stepping\t: %d\n"
"cpu MHz\t\t: %u.%03u\n",
if (x86_feature & X86_HTT) {
/*
* 'siblings' is used for HT-style threads
*/
"physical id\t: %lu\n"
"siblings\t: %u\n",
}
/*
* Since we're relatively picky about running on older hardware,
* we can be somewhat cavalier about the answers to these ones.
*
* In fact, given the hardware we support, we just say:
*
* fdiv_bug : no (if we're on a 64-bit kernel)
* hlt_bug : no
* f00f_bug : no
* coma_bug : no
* wp : yes (write protect in supervsr mode)
*/
"fdiv_bug\t: %s\n"
"hlt_bug \t: no\n"
"f00f_bug\t: no\n"
"coma_bug\t: no\n"
"fpu\t\t: %s\n"
"fpu_exception\t: %s\n"
"cpuid level\t: %d\n"
"flags\t\t:",
#if defined(__i386)
#else
"no",
#endif /* __i386 */
maxeax);
/*
* name additional features where appropriate
*/
switch (x86_vendor) {
case X86_VENDOR_Intel:
i < sizeof (intc_x_edx) / sizeof (intc_x_edx[0]);
fp++, i++)
break;
case X86_VENDOR_AMD:
fp++, i++)
fp++, i++)
break;
case X86_VENDOR_TM:
fp++, i++)
break;
default:
break;
}
if (pools_enabled)
else
}
/* ARGSUSED */
static void
{
}
/*
* lxpr_getattr(): Vnode operation for VOP_GETATTR()
*/
static int
{
int error;
/*
* Return attributes of underlying vnode if ATTR_REAL
*
* but keep fd files with the symlink permissions
*/
/*
* withold attribute information to owner or root
*/
return (error);
}
/*
* now its attributes
*/
return (error);
}
/*
* mode and keep it looking like a symlink
*/
if (type == LXPR_PID_FD_FD) {
}
return (0);
}
/* Default attributes, that may be overridden below */
switch (type) {
case LXPR_PROCDIR:
break;
case LXPR_PIDDIR:
break;
case LXPR_SELF:
break;
default:
break;
}
return (0);
}
/*
* lxpr_access(): Vnode operation for VOP_ACCESS()
*/
static int
{
int shift = 0;
/* lx /proc is a read only file system */
return (EROFS);
/*
* If this is a restricted file, check access permissions.
*/
case LXPR_PIDDIR:
return (0);
case LXPR_PID_CURDIR:
case LXPR_PID_ENV:
case LXPR_PID_EXE:
case LXPR_PID_MAPS:
case LXPR_PID_MEM:
case LXPR_PID_ROOTDIR:
case LXPR_PID_FDDIR:
case LXPR_PID_FD_FD:
return (ENOENT);
return (EACCES);
}
default:
break;
}
/*
* For these we use the underlying vnode's accessibility.
*/
}
/* If user is root allow access regardless of permission bits */
if (secpolicy_proc_access(cr) == 0)
return (0);
/*
* Access check is based on only
* one of owner, group, public.
* If not owner, then check group.
* If not a member of the group, then
* check public access.
*/
shift += 3;
shift += 3;
}
if (mode == 0)
return (0);
return (EACCES);
}
/* ARGSUSED */
static vnode_t *
{
return (NULL);
}
/*
* lxpr_lookup(): Vnode operation for VOP_LOOKUP()
*/
/* ARGSUSED */
static int
{
int error;
/*
* we should never get here because the lookup
* is done on the realvp for these nodes
*/
type != LXPR_PID_CURDIR &&
type != LXPR_PID_ROOTDIR);
/*
* restrict lookup permission to owner or root
*/
return (error);
}
/*
* Just return the parent vnode
* if thats where we are trying to go
*/
return (0);
}
/*
* Special handling for directory searches
* Note: null component name is synonym for
* current directory being searched.
*/
return (0);
}
}
/*
* Do a sequential search on the given directory table
*/
static vnode_t *
{
int count;
return (dp);
}
}
return (NULL);
}
static vnode_t *
{
proc_t *p;
if (p == NULL)
return (NULL);
lxpr_unlock(p);
return (dp);
}
/*
* Lookup one of the process's open files.
*/
static vnode_t *
{
proc_t *p;
int c;
/*
* convert the string rendition of the filename
* to a file descriptor
*/
fd = 0;
while ((c = *comp++) != '\0') {
int ofd;
if (c < '0' || c > '9')
return (NULL);
/* integer overflow */
return (NULL);
}
/*
* get the proc to work with and lock it
*/
if ((p == NULL))
return (NULL);
/*
* If the process is a zombie or system process
* it can't have any open files.
*/
lxpr_unlock(p);
return (NULL);
}
/*
*/
/*
* get open file info
*/
/*
* got the fd data so now done with this proc
*/
lxpr_unlock(p);
/*
* ensure the fd is still kosher.
* it may have gone between the readdir and
* the lookup
*/
return (NULL);
}
}
return (NULL);
} else {
/*
* Fill in the lxpr_node so future references will
* be able to find the underlying vnode.
* The vnode is held on the realvp.
*/
}
return (dp);
}
static vnode_t *
{
return (dp);
}
static vnode_t *
{
/*
* We know all the names of files & dirs in our
* file system structure except those that are pid names.
* So just look for a number as the first char to see if we
* are we doing pid lookups?
*
* Don't need to check for "self" as it is implemented as a symlink
*/
proc_t *p;
int c;
while ((c = *comp++) != '\0')
/*
* Can't continue if the process is still loading
* or it doesn't really exist yet (or maybe it just died!)
*/
if (p == NULL)
return (NULL);
lxpr_unlock(p);
return (NULL);
}
/*
* allocate and fill in a new lx /proc node
*/
lxpr_unlock(p);
return (dp);
}
/* Lookup fixed names */
}
/*
* lxpr_readdir(): Vnode operation for VOP_READDIR()
*/
/* ARGSUSED */
static int
{
int error;
/*
* we should never get here because the readdir
* is done on the realvp for these nodes
*/
type != LXPR_PID_CURDIR &&
type != LXPR_PID_ROOTDIR);
/*
* restrict readdir permission to owner or root
*/
return (error);
/* can't do negative reads */
return (EINVAL);
/* can't read directory entries that don't exist! */
if (uoffset % LXPR_SDSIZE)
return (ENOENT);
}
/* ARGSUSED */
static int
{
return (ENOTDIR);
}
/*
* This has the common logic for returning directory entries
*/
static int
{
/* bp holds one dirent64 structure */
/* clear out the dirent buffer */
/*
* Satisfy user request
*/
int dirindex;
int reclen;
int error;
if (uoffset == 0) {
} else if (uoffset == LXPR_SDSIZE) {
} else {
/* Run out of table entries */
if (eofp) {
*eofp = 1;
}
return (0);
}
/*
* if the size of the data to transfer is greater
* that that requested then we can't do it this transfer.
*/
/*
* Error if no entries have been returned yet.
*/
return (EINVAL);
}
break;
}
/*
* uiomove() updates both uiop->uio_resid and
* uiop->uio_offset by the same amount. But we want
* uiop->uio_offset to change in increments
* of LXPR_SDSIZE, which is different from the number of bytes
* being returned to the user.
* So we set uiop->uio_offset separately, ignoring what
* uiomove() does.
*/
return (error);
}
}
/* Have run out of space, but could have just done last table entry */
if (eofp) {
*eofp =
}
return (0);
}
static int
{
/* bp holds one dirent64 structure */
int error;
int ceof;
/*
* We return directory entries in the order:
* "." and ".." then the unique lx procfs files, then the
* directories corresponding to the running processes.
*
* This is a good order because it allows us to more easily
* keep track of where we are betwen calls to getdents().
* If the number of processes changes between calls then we
* can't lose track of where we are in the lx procfs files.
*/
/* Do the fixed entries */
/* Finished if we got an error or if we couldn't do all the table */
return (error);
/* clear out the dirent buffer */
/* Do the process entries */
proc_t *p;
int len;
int reclen;
int i;
/*
* Stop when entire proc table has been examined.
*/
if (i >= v.v_proc) {
/* Run out of table entries */
if (eofp) {
*eofp = 1;
}
return (0);
}
/*
* Skip indices for which there is no pid_entry, PIDs for
* which there is no corresponding process, a PID of 0,
* and anything the security policy doesn't allow
* us to look at.
*/
p->p_pid == 0 ||
goto next;
}
/*
* Convert pid to the Linux default of 1 if we're the zone's
* init process, otherwise use the value from the proc
* structure
*/
p->p_pid : 1);
/*
* If this /proc was mounted in the global zone, view
* all procs; otherwise, only view zone member procs.
*/
goto next;
}
/*
* if the size of the data to transfer is greater
* that that requested then we can't do it this transfer.
*/
/*
* Error if no entries have been returned yet.
*/
return (EINVAL);
break;
}
/*
* uiomove() updates both uiop->uio_resid and
* uiop->uio_offset by the same amount. But we want
* uiop->uio_offset to change in increments
* of LXPR_SDSIZE, which is different from the number of bytes
* being returned to the user.
* So we set uiop->uio_offset separately, in the
* increment of this for loop, ignoring what uiomove() does.
*/
return (error);
next:
}
if (eofp)
*eofp =
(uiop->uio_offset >=
return (0);
}
static int
{
proc_t *p;
/* can't read its contents if it died */
return (ENOENT);
}
}
static int
{
}
static int
{
/* bp holds one dirent64 structure */
int error;
int ceof;
proc_t *p;
int fddirsize;
/* can't read its contents if it died */
if (p == NULL)
return (ENOENT);
/* Get open file info */
fddirsize = 0;
else
lxpr_unlock(p);
/* Do the fixed entries (in this case just "." & "..") */
/* Finished if we got an error or if we couldn't do all the table */
return (error);
/* clear out the dirent buffer */
/*
* Loop until user's request is satisfied or until
* all file descriptors have been examined.
*/
int reclen;
int fd;
int len;
/*
* Stop at the end of the fd list
*/
if (eofp) {
*eofp = 1;
}
goto out;
}
continue;
/*
* Error if no entries have been returned yet.
*/
goto out;
}
goto out;
}
if (eofp)
*eofp =
out:
return (error);
}
/*
* lxpr_readlink(): Vnode operation for VOP_READLINK()
*/
/* ARGSUSED */
static int
{
int error = 0;
/* must be a symbolic link file */
return (EINVAL);
/* Try to produce a symlink name for anything that has a realvp */
return (error);
return (error);
} else {
case LXPR_SELF:
/*
* Don't need to check result as every possible int
* will fit within MAXPATHLEN bytes
*/
/*
* Convert pid to the Linux default of 1 if we're the
* zone's init process
*/
break;
case LXPR_PID_CURDIR:
case LXPR_PID_ROOTDIR:
case LXPR_PID_EXE:
return (EACCES);
default:
/*
* Need to return error so that nothing thinks
* that the symlink is empty and hence "."
*/
return (EINVAL);
}
}
/* copy the link data to user space */
}
/*
* lxpr_inactive(): Vnode operation for VOP_INACTIVE()
* Vnode is no longer referenced, deallocate the file
* and all its resources.
*/
/* ARGSUSED */
static void
{
}
/*
* lxpr_sync(): Vnode operation for VOP_SYNC()
*/
static int
{
/*
* nothing to sync but this
* function must never fail
*/
return (0);
}
/*
* lxpr_cmp(): Vnode operation for VOP_CMP()
*/
static int
{
}
/*
* lxpr_realvp(): Vnode operation for VOP_REALVP()
*/
static int
{
}
return (0);
}