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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <sys/openpromio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>			/* sprintf() */
34 #include <unistd.h>
35 
36 /*
37  * opp_zalloc(): allocates and initializes a struct openpromio
38  *
39  *   input: size_t: the size of the variable-length part of the openpromio
40  *          const char *: an initial value for oprom_array, if non-NULL
41  *  output: struct openpromio: the allocated, initialized openpromio
42  */
43 
44 static struct openpromio *
opp_zalloc(size_t size,const char * prop)45 opp_zalloc(size_t size, const char *prop)
46 {
47 	struct openpromio *opp = malloc(sizeof (struct openpromio) + size);
48 
49 	if (opp != NULL) {
50 		(void) memset(opp, 0, sizeof (struct openpromio) + size);
51 		opp->oprom_size = size;
52 		if (prop != NULL)
53 			(void) strcpy(opp->oprom_array, prop);
54 	}
55 	return (opp);
56 }
57 
58 /*
59  * goto_rootnode(): moves to the root of the devinfo tree
60  *
61  *   input: int: an open descriptor to /dev/openprom
62  *  output: int: nonzero on success
63  */
64 
65 static int
goto_rootnode(int prom_fd)66 goto_rootnode(int prom_fd)
67 {
68 	struct openpromio op = { sizeof (int), 0 };
69 
70 	/* zero it explicitly since a union is involved */
71 	op.oprom_node = 0;
72 	return (ioctl(prom_fd, OPROMNEXT, &op) == 0);
73 }
74 
75 /*
76  * return_property(): returns the value of a given property
77  *
78  *   input: int: an open descriptor to /dev/openprom
79  *          const char *: the property to look for in the current devinfo node
80  *  output: the value of that property (dynamically allocated)
81  */
82 
83 static char *
return_property(int prom_fd,const char * prop)84 return_property(int prom_fd, const char *prop)
85 {
86 	int			proplen;
87 	char			*result;
88 	struct openpromio	*opp = opp_zalloc(strlen(prop) + 1, prop);
89 
90 	if (opp == NULL)
91 		return (NULL);
92 
93 	if (ioctl(prom_fd, OPROMGETPROPLEN, opp) == -1) {
94 		free(opp);
95 		return (NULL);
96 	}
97 
98 	proplen = opp->oprom_len;
99 	if (proplen > (strlen(prop) + 1)) {
100 		free(opp);
101 		opp = opp_zalloc(proplen, prop);
102 		if (opp == NULL)
103 			return (NULL);
104 	}
105 
106 	if (ioctl(prom_fd, OPROMGETPROP, opp) == -1) {
107 		free(opp);
108 		return (NULL);
109 	}
110 
111 	result = strdup(opp->oprom_array);
112 	free(opp);
113 	return (result);
114 }
115 
116 /*
117  * sanitize_class_id(): translates the class id into a canonical format,
118  *			so that it can be used easily with dhcptab(5).
119  *
120  *   input: char *: the class id to canonicalize
121  *  output: void
122  */
123 
124 static void
sanitize_class_id(char * src_ptr)125 sanitize_class_id(char *src_ptr)
126 {
127 	char	*dst_ptr = src_ptr;
128 
129 	/* remove all spaces and change all commas to periods */
130 	while (*src_ptr != '\0') {
131 
132 		switch (*src_ptr) {
133 
134 		case ' ':
135 			break;
136 
137 		case ',':
138 			*dst_ptr++ = '.';
139 			break;
140 
141 		default:
142 			*dst_ptr++ = *src_ptr;
143 			break;
144 		}
145 		src_ptr++;
146 	}
147 	*dst_ptr = '\0';
148 }
149 
150 /*
151  * get_class_id(): retrieves the class id from the prom, then canonicalizes it
152  *
153  *   input: void
154  *  output: char *: the class id (dynamically allocated and sanitized)
155  */
156 
157 char *
get_class_id(void)158 get_class_id(void)
159 {
160 	int	prom_fd;
161 	char    *name, *class_id = NULL;
162 	size_t	len;
163 
164 	prom_fd = open("/dev/openprom", O_RDONLY);
165 	if (prom_fd == -1)
166 		return (NULL);
167 
168 	if (goto_rootnode(prom_fd) == 0) {
169 		(void) close(prom_fd);
170 		return (NULL);
171 	}
172 
173 	/*
174 	 * the `name' property is the same as the result of `uname -i', modulo
175 	 * some stylistic issues we fix up via sanitize_class_id() below.
176 	 */
177 
178 	name = return_property(prom_fd, "name");
179 	(void) close(prom_fd);
180 	if (name == NULL)
181 		return (NULL);
182 
183 	/*
184 	 * if the name is not prefixed with a vendor name, add "SUNW," to make
185 	 * it more likely to be globally unique; see PSARC/2004/674.
186 	 */
187 
188 	if (strchr(name, ',') == NULL) {
189 		len = strlen(name) + sizeof ("SUNW,");
190 		class_id = malloc(len);
191 		if (class_id == NULL) {
192 			free(name);
193 			return (NULL);
194 		}
195 		(void) snprintf(class_id, len, "SUNW,%s", name);
196 		free(name);
197 	} else {
198 		class_id = name;
199 	}
200 
201 	sanitize_class_id(class_id);
202 	return (class_id);
203 }
204