#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/tuneable.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <sys/unistd.h>
#include <sys/debug.h>
#include <sys/bootconf.h>
#include <sys/socket.h>
#include <sys/policy.h>
#include <net/if.h>
#include <sys/sunddi.h>
#include <sys/promif.h>
#include <sys/zone.h>
#include <sys/model.h>
#include <netinet/inetutil.h>
static void get_netif_name(char *, char *);
long
systeminfo(int command, char *buf, long count)
{
int error = 0;
long strcnt, getcnt;
char *kstr;
char hostidp[HW_HOSTID_LEN];
if (count < 0 && command != SI_SET_HOSTNAME &&
command != SI_SET_SRPC_DOMAIN)
return (set_errno(EINVAL));
switch (command) {
case SI_SYSNAME:
kstr = utsname.sysname;
break;
case SI_HOSTNAME:
kstr = uts_nodename();
break;
case SI_RELEASE:
kstr = utsname.release;
break;
case SI_VERSION:
kstr = utsname.version;
break;
case SI_MACHINE:
kstr = utsname.machine;
break;
#ifdef _LP64
case SI_ADDRESS_WIDTH:
kstr = "64";
break;
case SI_ARCHITECTURE_64:
case SI_ARCHITECTURE_K:
kstr = architecture;
break;
case SI_ARCHITECTURE_32:
case SI_ARCHITECTURE:
kstr = architecture_32;
break;
case SI_ARCHITECTURE_NATIVE:
kstr = get_udatamodel() == DATAMODEL_NATIVE ?
architecture : architecture_32;
break;
#else
case SI_ADDRESS_WIDTH:
kstr = "32";
break;
case SI_ARCHITECTURE_K:
case SI_ARCHITECTURE_32:
case SI_ARCHITECTURE:
case SI_ARCHITECTURE_NATIVE:
kstr = architecture;
break;
#endif
case SI_HW_SERIAL:
(void) snprintf(hostidp, sizeof (hostidp), "%u",
zone_get_hostid(curzone));
kstr = hostidp;
break;
case SI_HW_PROVIDER:
kstr = hw_provider;
break;
case SI_SRPC_DOMAIN:
kstr = curproc->p_zone->zone_domain;
break;
case SI_PLATFORM:
kstr = platform;
break;
case SI_ISALIST:
kstr = isa_list;
break;
default:
kstr = NULL;
break;
}
if (kstr != NULL) {
strcnt = strlen(kstr);
if (count > 0) {
if (count <= strcnt) {
getcnt = count - 1;
if (subyte(buf + getcnt, 0) < 0)
return (set_errno(EFAULT));
} else {
getcnt = strcnt + 1;
}
if (copyout(kstr, buf, getcnt))
return (set_errno(EFAULT));
}
return (strcnt + 1);
}
switch (command) {
case SI_DHCP_CACHE:
{
char *tmp;
unsigned int tlen, octlen;
if (dhcack == NULL) {
tmp = "";
strcnt = 0;
} else {
if (netdev_path == NULL || netdev_path[0] == '\0') {
netdev_path = strplumb_get_netdev_path();
}
if (dhcifname[0] == '\0' &&
netdev_path != NULL && netdev_path[0] != '\0') {
get_netif_name(netdev_path, dhcifname);
}
octlen = dhcacklen * 2 + 1;
tlen = octlen + IFNAMSIZ;
tmp = kmem_alloc(tlen, KM_SLEEP);
(void) strncpy(tmp, dhcifname, IFNAMSIZ);
if (octet_to_hexascii(dhcack, dhcacklen,
&tmp[IFNAMSIZ], &octlen) != 0) {
kmem_free(tmp, tlen);
error = EINVAL;
break;
} else {
strcnt = IFNAMSIZ + octlen;
}
}
if (count > 0) {
if (count <= strcnt) {
getcnt = count - 1;
if (subyte((buf + getcnt), 0) < 0)
goto fail;
} else {
getcnt = strcnt + 1;
}
if (copyout(tmp, buf, getcnt))
goto fail;
}
if (strcnt != 0)
kmem_free(tmp, tlen);
return (strcnt + 1);
fail:
if (strcnt != 0)
kmem_free(tmp, tlen);
error = EFAULT;
break;
}
case SI_SET_HOSTNAME:
{
size_t len;
char name[SYS_NMLN];
char *name_to_use;
if ((error = secpolicy_systeminfo(CRED())) != 0)
break;
name_to_use = uts_nodename();
if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
break;
if (len < 2 || (len == SYS_NMLN && name[SYS_NMLN-1] != '\0')) {
error = EINVAL;
break;
}
(void) strcpy(name_to_use, name);
if (name_to_use == utsname.nodename)
nodename_set();
return (len);
}
case SI_SET_SRPC_DOMAIN:
{
char name[SYS_NMLN];
size_t len;
if ((error = secpolicy_systeminfo(CRED())) != 0)
break;
if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
break;
if (len == SYS_NMLN && name[SYS_NMLN-1] != '\0') {
error = EINVAL;
break;
}
(void) strcpy(curproc->p_zone->zone_domain, name);
return (len);
}
default:
error = EINVAL;
break;
}
return (set_errno(error));
}
struct i_path_findnode {
pnode_t nodeid;
dev_info_t *dip;
};
static int
i_path_find_node(dev_info_t *dev, void *arg)
{
struct i_path_findnode *f = (struct i_path_findnode *)arg;
if (ddi_get_nodeid(dev) == (int)f->nodeid) {
f->dip = dev;
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
static dev_info_t *
path_to_devinfo(char *path)
{
struct i_path_findnode fn;
extern dev_info_t *top_devinfo;
fn.dip = NULL;
fn.nodeid = prom_finddevice(path);
if (fn.nodeid != OBP_BADNODE) {
ddi_walk_devs(top_devinfo, i_path_find_node, (void *)(&fn));
}
return (fn.dip);
}
static void
get_netif_name(char *devname, char *ifname)
{
dev_info_t *dip;
major_t ndev;
char *name;
int unit;
dip = path_to_devinfo(devname);
if (dip == NULL) {
cmn_err(CE_WARN, "get_netif_name: "
"can't bind driver for '%s'\n", devname);
return;
}
ndev = ddi_driver_major(dip);
if (ndev == -1) {
cmn_err(CE_WARN, "get_netif_name: "
"no driver bound to '%s'\n", devname);
return;
}
name = ddi_major_to_name(ndev);
if (name == NULL) {
cmn_err(CE_WARN, "get_netif_name: "
"no name for major number %d\n", ndev);
return;
}
unit = i_ddi_devi_get_ppa(dip);
if (unit < 0) {
cmn_err(CE_WARN, "get_netif_name: "
"illegal unit number %d\n", unit);
return;
}
(void) snprintf(ifname, IFNAMSIZ, "%s%d", name, unit);
}