xref: /illumos-gate/usr/src/uts/common/syscall/systeminfo.c (revision 24472db64c485d6744c0321b7581cf066556cf2d)
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 2007 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 didn't have a name (bindable
151 			 * driver) to begin with, it might have one now.
152 			 * So, re-run strplumb_get_netdev_path() to see
153 			 * if one can be established at this time.
154 			 */
155 			if (netdev_path == NULL || netdev_path[0] == '\0') {
156 				netdev_path = strplumb_get_netdev_path();
157 			}
158 			/*
159 			 * If the interface name has not yet been resolved
160 			 * (first IFNAMSIZ bytes of dhcack[]) and a valid
161 			 * netdev_path[] was stashed by loadrootmodules in
162 			 * swapgeneric.c, or established above, resolve
163 			 * the interface name now.
164 			 */
165 			if (dhcack[0] == '\0' &&
166 			    netdev_path != NULL && netdev_path[0] != '\0') {
167 				get_netif_name(netdev_path, dhcack);
168 			}
169 
170 			tmp = dhcack;
171 			strcnt = IFNAMSIZ + strlen(&tmp[IFNAMSIZ]);
172 		}
173 
174 		if (count > 0) {
175 			if (count <= strcnt) {
176 				getcnt = count - 1;
177 				if (subyte((buf + getcnt), 0) < 0) {
178 					error = EFAULT;
179 					break;
180 				}
181 			} else {
182 				getcnt = strcnt + 1;
183 			}
184 			if (copyout(tmp, buf, getcnt)) {
185 				error = EFAULT;
186 				break;
187 			}
188 		}
189 
190 		return (strcnt + 1);
191 	}
192 
193 	case SI_SET_HOSTNAME:
194 	{
195 		size_t		len;
196 		char		name[SYS_NMLN];
197 		char		*name_to_use;
198 
199 		if ((error = secpolicy_systeminfo(CRED())) != 0)
200 			break;
201 
202 		name_to_use = uts_nodename();
203 		if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
204 			break;
205 
206 		/*
207 		 * Must be non-NULL string and string
208 		 * must be less than SYS_NMLN chars.
209 		 */
210 		if (len < 2 || (len == SYS_NMLN && name[SYS_NMLN-1] != '\0')) {
211 			error = EINVAL;
212 			break;
213 		}
214 
215 		/*
216 		 * Copy the name into the relevant zone's nodename.
217 		 */
218 		(void) strcpy(name_to_use, name);
219 
220 		/*
221 		 * Notify other interested parties that the nodename was set
222 		 */
223 		if (name_to_use == utsname.nodename) /* global zone nodename */
224 			nodename_set();
225 
226 		return (len);
227 	}
228 
229 	case SI_SET_SRPC_DOMAIN:
230 	{
231 		char name[SYS_NMLN];
232 		size_t len;
233 
234 		if ((error = secpolicy_systeminfo(CRED())) != 0)
235 			break;
236 		if ((error = copyinstr(buf, name, SYS_NMLN, &len)) != 0)
237 			break;
238 		/*
239 		 * If string passed in is longer than length
240 		 * allowed for domain name, fail.
241 		 */
242 		if (len == SYS_NMLN && name[SYS_NMLN-1] != '\0') {
243 			error = EINVAL;
244 			break;
245 		}
246 
247 		(void) strcpy(curproc->p_zone->zone_domain, name);
248 		return (len);
249 	}
250 
251 	default:
252 		error = EINVAL;
253 		break;
254 	}
255 
256 	return (set_errno(error));
257 }
258 
259 /*
260  * i_path_find_node: Internal routine used by path_to_devinfo
261  * to locate a given nodeid in the device tree.
262  */
263 struct i_path_findnode {
264 	pnode_t nodeid;
265 	dev_info_t *dip;
266 };
267 
268 static int
269 i_path_find_node(dev_info_t *dev, void *arg)
270 {
271 	struct i_path_findnode *f = (struct i_path_findnode *)arg;
272 
273 
274 	if (ddi_get_nodeid(dev) == (int)f->nodeid) {
275 		f->dip = dev;
276 		return (DDI_WALK_TERMINATE);
277 	}
278 	return (DDI_WALK_CONTINUE);
279 }
280 
281 /*
282  * Return the devinfo node to a boot device
283  */
284 static dev_info_t *
285 path_to_devinfo(char *path)
286 {
287 	struct i_path_findnode fn;
288 	extern dev_info_t *top_devinfo;
289 
290 	/*
291 	 * Get the nodeid of the given pathname, if such a mapping exists.
292 	 */
293 	fn.dip = NULL;
294 	fn.nodeid = prom_finddevice(path);
295 	if (fn.nodeid != OBP_BADNODE) {
296 		/*
297 		 * Find the nodeid in our copy of the device tree and return
298 		 * whatever name we used to bind this node to a driver.
299 		 */
300 		ddi_walk_devs(top_devinfo, i_path_find_node, (void *)(&fn));
301 	}
302 
303 	return (fn.dip);
304 }
305 
306 /*
307  * Determine the network interface name from the device path argument.
308  */
309 static void
310 get_netif_name(char *devname, char *ifname)
311 {
312 	dev_info_t	*dip;
313 	major_t		ndev;
314 	char		*name;
315 	int		unit;
316 
317 	dip = path_to_devinfo(devname);
318 	if (dip == NULL) {
319 		cmn_err(CE_WARN, "get_netif_name: "
320 		    "can't bind driver for '%s'\n", devname);
321 		return;
322 	}
323 
324 	ndev = ddi_driver_major(dip);
325 	if (ndev == -1) {
326 		cmn_err(CE_WARN, "get_netif_name: "
327 		    "no driver bound to '%s'\n", devname);
328 		return;
329 	}
330 
331 	name = ddi_major_to_name(ndev);
332 	if (name == NULL) {
333 		cmn_err(CE_WARN, "get_netif_name: "
334 		    "no name for major number %d\n", ndev);
335 		return;
336 	}
337 
338 	unit = i_ddi_devi_get_ppa(dip);
339 	if (unit < 0) {
340 		cmn_err(CE_WARN, "get_netif_name: "
341 		    "illegal unit number %d\n", unit);
342 		return;
343 	}
344 
345 	(void) snprintf(ifname, IFNAMSIZ, "%s%d", name, unit);
346 }
347