xref: /illumos-gate/usr/src/uts/common/syscall/systeminfo.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All rights reserved.	*/
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/systm.h>
35 #include <sys/tuneable.h>
36 #include <sys/errno.h>
37 #include <sys/cred.h>
38 #include <sys/utsname.h>
39 #include <sys/systeminfo.h>
40 #include <sys/unistd.h>
41 #include <sys/debug.h>
42 #include <sys/bootconf.h>
43 #include <sys/socket.h>
44 #include <sys/policy.h>
45 #include <net/if.h>
46 #include <sys/sunddi.h>
47 #include <sys/promif.h>
48 #include <sys/zone.h>
49 #include <sys/model.h>
50 
51 static void get_netif_name(char *, char *);
52 
53 long
54 systeminfo(int command, char *buf, long count)
55 {
56 	int error = 0;
57 	long strcnt, getcnt;
58 	char *kstr;
59 
60 	if (count < 0 && command != SI_SET_HOSTNAME &&
61 	    command != SI_SET_SRPC_DOMAIN)
62 		return (set_errno(EINVAL));
63 
64 	/*
65 	 * Deal with the common "get a string" case first.
66 	 */
67 	switch (command) {
68 	case SI_SYSNAME:
69 		kstr = utsname.sysname;
70 		break;
71 	case SI_HOSTNAME:
72 		kstr = uts_nodename();
73 		break;
74 	case SI_RELEASE:
75 		kstr = utsname.release;
76 		break;
77 	case SI_VERSION:
78 		kstr = utsname.version;
79 		break;
80 	case SI_MACHINE:
81 		kstr = utsname.machine;
82 		break;
83 #ifdef _LP64
84 	case SI_ARCHITECTURE_64:
85 	case SI_ARCHITECTURE_K:
86 		kstr = architecture;
87 		break;
88 	case SI_ARCHITECTURE_32:
89 	case SI_ARCHITECTURE:
90 		kstr = architecture_32;
91 		break;
92 	case SI_ARCHITECTURE_NATIVE:
93 		kstr = get_udatamodel() == DATAMODEL_NATIVE ?
94 			architecture : architecture_32;
95 		break;
96 #else
97 	case SI_ARCHITECTURE_K:
98 	case SI_ARCHITECTURE_32:
99 	case SI_ARCHITECTURE:
100 	case SI_ARCHITECTURE_NATIVE:
101 		kstr = architecture;
102 		break;
103 #endif
104 	case SI_HW_SERIAL:
105 		kstr = hw_serial;
106 		break;
107 	case SI_HW_PROVIDER:
108 		kstr = hw_provider;
109 		break;
110 	case SI_SRPC_DOMAIN:
111 		kstr = curproc->p_zone->zone_domain;
112 		break;
113 	case SI_PLATFORM:
114 		kstr = platform;
115 		break;
116 	case SI_ISALIST:
117 		kstr = isa_list;
118 		break;
119 	default:
120 		kstr = NULL;
121 		break;
122 	}
123 
124 	if (kstr != NULL) {
125 		strcnt = strlen(kstr);
126 		if (count > 0) {
127 			if (count <= strcnt) {
128 				getcnt = count - 1;
129 				if (subyte(buf + getcnt, 0) < 0)
130 					return (set_errno(EFAULT));
131 			} else {
132 				getcnt = strcnt + 1;
133 			}
134 			if (copyout(kstr, buf, getcnt))
135 				return (set_errno(EFAULT));
136 		}
137 		return (strcnt + 1);
138 	}
139 
140 	switch (command) {
141 	case SI_DHCP_CACHE:
142 	{
143 		char	*tmp;
144 
145 		if (dhcack == NULL) {
146 			tmp = "";
147 			strcnt = 0;
148 		} else {
149 			/*
150 			 * If the interface name has not yet been resolved
151 			 * (first IFNAMSIZ bytes of dhcack[]) and a valid
152 			 * netdev_path[] was stashed by loadrootmodules in
153 			 * swapgeneric.c, resolve the interface name now.
154 			 */
155 			if (dhcack[0] == '\0' &&
156 			    netdev_path != NULL && netdev_path[0] != '\0') {
157 				get_netif_name(netdev_path, dhcack);
158 			}
159 
160 			tmp = dhcack;
161 			strcnt = IFNAMSIZ + strlen(&tmp[IFNAMSIZ]);
162 		}
163 
164 		if (count > 0) {
165 			if (count <= strcnt) {
166 				getcnt = count - 1;
167 				if (subyte((buf + getcnt), 0) < 0) {
168 					error = EFAULT;
169 					break;
170 				}
171 			} else {
172 				getcnt = strcnt + 1;
173 			}
174 			if (copyout(tmp, buf, getcnt)) {
175 				error = EFAULT;
176 				break;
177 			}
178 		}
179 
180 		return (strcnt + 1);
181 	}
182 
183 	case SI_SET_HOSTNAME:
184 	{
185 		size_t		len;
186 		char		name[SYS_NMLN];
187 		char		*name_to_use;
188 
189 		if ((error = secpolicy_systeminfo(CRED())) != 0)
190 			break;
191 
192 		name_to_use = uts_nodename();
193 		if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
194 			break;
195 
196 		/*
197 		 * Must be non-NULL string and string
198 		 * must be less than SYS_NMLN chars.
199 		 */
200 		if (len < 2 || (len == SYS_NMLN && name[SYS_NMLN-1] != '\0')) {
201 			error = EINVAL;
202 			break;
203 		}
204 
205 		/*
206 		 * Copy the name into the relevant zone's nodename.
207 		 */
208 		(void) strcpy(name_to_use, name);
209 
210 		/*
211 		 * Notify other interested parties that the nodename was set
212 		 */
213 		if (name_to_use == utsname.nodename) /* global zone nodename */
214 			nodename_set();
215 
216 		return (len);
217 	}
218 
219 	case SI_SET_SRPC_DOMAIN:
220 	{
221 		char name[SYS_NMLN];
222 		size_t len;
223 
224 		if ((error = secpolicy_systeminfo(CRED())) != 0)
225 			break;
226 		if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
227 			break;
228 		/*
229 		 * If string passed in is longer than length
230 		 * allowed for domain name, fail.
231 		 */
232 		if (len == SYS_NMLN && name[SYS_NMLN-1] != '\0') {
233 			error = EINVAL;
234 			break;
235 		}
236 
237 		(void) strcpy(curproc->p_zone->zone_domain, name);
238 		return (len);
239 	}
240 
241 	default:
242 		error = EINVAL;
243 		break;
244 	}
245 
246 	return (set_errno(error));
247 }
248 
249 /*
250  * i_path_find_node: Internal routine used by path_to_devinfo
251  * to locate a given nodeid in the device tree.
252  */
253 struct i_path_findnode {
254 	pnode_t nodeid;
255 	dev_info_t *dip;
256 };
257 
258 static int
259 i_path_find_node(dev_info_t *dev, void *arg)
260 {
261 	struct i_path_findnode *f = (struct i_path_findnode *)arg;
262 
263 
264 	if (ddi_get_nodeid(dev) == (int)f->nodeid) {
265 		f->dip = dev;
266 		return (DDI_WALK_TERMINATE);
267 	}
268 	return (DDI_WALK_CONTINUE);
269 }
270 
271 /*
272  * Return the devinfo node to a boot device
273  */
274 static dev_info_t *
275 path_to_devinfo(char *path)
276 {
277 	struct i_path_findnode fn;
278 	extern dev_info_t *top_devinfo;
279 
280 	/*
281 	 * Get the nodeid of the given pathname, if such a mapping exists.
282 	 */
283 	fn.dip = NULL;
284 	fn.nodeid = prom_finddevice(path);
285 	if (fn.nodeid != OBP_BADNODE) {
286 		/*
287 		 * Find the nodeid in our copy of the device tree and return
288 		 * whatever name we used to bind this node to a driver.
289 		 */
290 		ddi_walk_devs(top_devinfo, i_path_find_node, (void *)(&fn));
291 	}
292 
293 	return (fn.dip);
294 }
295 
296 /*
297  * Determine the network interface name from the device path argument.
298  */
299 static void
300 get_netif_name(char *devname, char *ifname)
301 {
302 	dev_info_t	*dip;
303 	major_t		ndev;
304 	char		*name;
305 	int		unit;
306 
307 	dip = path_to_devinfo(devname);
308 	if (dip == NULL) {
309 		cmn_err(CE_WARN, "get_netif_name: "
310 		    "can't bind driver for '%s'\n", devname);
311 		return;
312 	}
313 
314 	ndev = ddi_driver_major(dip);
315 	if (ndev == -1) {
316 		cmn_err(CE_WARN, "get_netif_name: "
317 		    "no driver bound to '%s'\n", devname);
318 		return;
319 	}
320 
321 	name = ddi_major_to_name(ndev);
322 	if (name == NULL) {
323 		cmn_err(CE_WARN, "get_netif_name: "
324 		    "no name for major number %d\n", ndev);
325 		return;
326 	}
327 
328 	unit = i_ddi_devi_get_ppa(dip);
329 	if (unit < 0) {
330 		cmn_err(CE_WARN, "get_netif_name: "
331 		    "illegal unit number %d\n", unit);
332 		return;
333 	}
334 
335 	(void) snprintf(ifname, IFNAMSIZ, "%s%d", name, unit);
336 }
337