1 /*
2  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 FILE_LICENCE ( GPL2_OR_LATER );
21 
22 /** @file
23  *
24  * VMware GuestInfo settings
25  *
26  */
27 
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <ipxe/init.h>
34 #include <ipxe/settings.h>
35 #include <ipxe/netdevice.h>
36 #include <ipxe/guestrpc.h>
37 
38 /** GuestInfo GuestRPC channel */
39 static int guestinfo_channel;
40 
41 /**
42  * Fetch value of typed GuestInfo setting
43  *
44  * @v settings		Settings block
45  * @v setting		Setting to fetch
46  * @v type		Setting type to attempt (or NULL for default)
47  * @v data		Buffer to fill with setting data
48  * @v len		Length of buffer
49  * @ret found		Setting found in GuestInfo
50  * @ret len		Length of setting data, or negative error
51  */
guestinfo_fetch_type(struct settings * settings,struct setting * setting,const struct setting_type * type,void * data,size_t len,int * found)52 static int guestinfo_fetch_type ( struct settings *settings,
53 				  struct setting *setting,
54 				  const struct setting_type *type,
55 				  void *data, size_t len, int *found ) {
56 	const char *parent_name = settings->parent->name;
57 	char command[ 24 /* "info-get guestinfo.ipxe." */ +
58 		      strlen ( parent_name ) + 1 /* "." */ +
59 		      strlen ( setting->name ) + 1 /* "." */ +
60 		      ( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ];
61 	struct setting *predefined;
62 	char *info;
63 	int info_len;
64 	int check_len;
65 	int ret;
66 
67 	/* Construct info-get command */
68 	snprintf ( command, sizeof ( command ),
69 		   "info-get guestinfo.ipxe.%s%s%s%s%s",
70 		   parent_name, ( parent_name[0] ? "." : "" ), setting->name,
71 		   ( type ? "." : "" ), ( type ? type->name : "" ) );
72 
73 	/* Check for existence and obtain length of GuestInfo value */
74 	info_len = guestrpc_command ( guestinfo_channel, command, NULL, 0 );
75 	if ( info_len < 0 ) {
76 		ret = info_len;
77 		goto err_get_info_len;
78 	}
79 
80 	/* Mark as found */
81 	*found = 1;
82 
83 	/* Determine default type if necessary */
84 	if ( ! type ) {
85 		predefined = find_setting ( setting->name );
86 		type = ( predefined ? predefined->type : &setting_type_string );
87 	}
88 	assert ( type != NULL );
89 
90 	/* Allocate temporary block to hold GuestInfo value */
91 	info = zalloc ( info_len + 1 /* NUL */ );
92 	if ( ! info ) {
93 		DBGC ( settings, "GuestInfo %p could not allocate %d bytes\n",
94 		       settings, info_len );
95 		ret = -ENOMEM;
96 		goto err_alloc;
97 	}
98 	info[info_len] = '\0';
99 
100 	/* Fetch GuestInfo value */
101 	check_len = guestrpc_command ( guestinfo_channel, command,
102 				       info, info_len );
103 	if ( check_len < 0 ) {
104 		ret = check_len;
105 		goto err_get_info;
106 	}
107 	if ( check_len != info_len ) {
108 		DBGC ( settings, "GuestInfo %p length mismatch (expected %d, "
109 		       "got %d)\n", settings, info_len, check_len );
110 		ret = -EIO;
111 		goto err_get_info;
112 	}
113 	DBGC2 ( settings, "GuestInfo %p found %s = \"%s\"\n",
114 		settings, &command[9] /* Skip "info-get " */, info );
115 
116 	/* Parse GuestInfo value according to type */
117 	ret = setting_parse ( type, info, data, len );
118 	if ( ret < 0 ) {
119 		DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: "
120 		       "%s\n", settings, info, type->name, strerror ( ret ) );
121 		goto err_parse;
122 	}
123 
124  err_parse:
125  err_get_info:
126 	free ( info );
127  err_alloc:
128  err_get_info_len:
129 	return ret;
130 }
131 
132 /**
133  * Fetch value of GuestInfo setting
134  *
135  * @v settings		Settings block
136  * @v setting		Setting to fetch
137  * @v data		Buffer to fill with setting data
138  * @v len		Length of buffer
139  * @ret len		Length of setting data, or negative error
140  */
guestinfo_fetch(struct settings * settings,struct setting * setting,void * data,size_t len)141 static int guestinfo_fetch ( struct settings *settings,
142 			     struct setting *setting,
143 			     void *data, size_t len ) {
144 	struct setting_type *type;
145 	int found = 0;
146 	int ret;
147 
148 	/* Try default type first */
149 	ret = guestinfo_fetch_type ( settings, setting, NULL,
150 				     data, len, &found );
151 	if ( found )
152 		return ret;
153 
154 	/* Otherwise, try all possible types */
155 	for_each_table_entry ( type, SETTING_TYPES ) {
156 		ret = guestinfo_fetch_type ( settings, setting, type,
157 					     data, len, &found );
158 		if ( found )
159 			return ret;
160 	}
161 
162 	/* Not found */
163 	return -ENOENT;
164 }
165 
166 /** GuestInfo settings operations */
167 static struct settings_operations guestinfo_settings_operations = {
168 	.fetch = guestinfo_fetch,
169 };
170 
171 /** GuestInfo settings */
172 static struct settings guestinfo_settings = {
173 	.refcnt = NULL,
174 	.siblings = LIST_HEAD_INIT ( guestinfo_settings.siblings ),
175 	.children = LIST_HEAD_INIT ( guestinfo_settings.children ),
176 	.op = &guestinfo_settings_operations,
177 };
178 
179 /** Initialise GuestInfo settings */
guestinfo_init(void)180 static void guestinfo_init ( void ) {
181 	int rc;
182 
183 	/* Open GuestRPC channel */
184 	guestinfo_channel = guestrpc_open();
185 	if ( guestinfo_channel < 0 ) {
186 		rc = guestinfo_channel;
187 		DBG ( "GuestInfo could not open channel: %s\n",
188 		      strerror ( rc ) );
189 		return;
190 	}
191 
192 	/* Register root GuestInfo settings */
193 	if ( ( rc = register_settings ( &guestinfo_settings, NULL,
194 					"vmware" ) ) != 0 ) {
195 		DBG ( "GuestInfo could not register settings: %s\n",
196 		      strerror ( rc ) );
197 		return;
198 	}
199 }
200 
201 /** GuestInfo settings initialiser */
202 struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
203 	.initialise = guestinfo_init,
204 };
205 
206 /**
207  * Create per-netdevice GuestInfo settings
208  *
209  * @v netdev		Network device
210  * @ret rc		Return status code
211  */
guestinfo_net_probe(struct net_device * netdev)212 static int guestinfo_net_probe ( struct net_device *netdev ) {
213 	struct settings *settings;
214 	int rc;
215 
216 	/* Do nothing unless we have a GuestInfo channel available */
217 	if ( guestinfo_channel < 0 )
218 		return 0;
219 
220 	/* Allocate and initialise settings block */
221 	settings = zalloc ( sizeof ( *settings ) );
222 	if ( ! settings ) {
223 		rc = -ENOMEM;
224 		goto err_alloc;
225 	}
226 	settings_init ( settings, &guestinfo_settings_operations, NULL, NULL );
227 
228 	/* Register settings */
229 	if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
230 					"vmware" ) ) != 0 ) {
231 		DBGC ( settings, "GuestInfo %p could not register for %s: %s\n",
232 		       settings, netdev->name, strerror ( rc ) );
233 		goto err_register;
234 	}
235 	DBGC ( settings, "GuestInfo %p registered for %s\n",
236 	       settings, netdev->name );
237 
238 	return 0;
239 
240  err_register:
241 	free ( settings );
242  err_alloc:
243 	return rc;
244 }
245 
246 /**
247  * Remove per-netdevice GuestInfo settings
248  *
249  * @v netdev		Network device
250  */
guestinfo_net_remove(struct net_device * netdev)251 static void guestinfo_net_remove ( struct net_device *netdev ) {
252 	struct settings *parent = netdev_settings ( netdev );
253 	struct settings *settings;
254 
255 	list_for_each_entry ( settings, &parent->children, siblings ) {
256 		if ( settings->op == &guestinfo_settings_operations ) {
257 			DBGC ( settings, "GuestInfo %p unregistered for %s\n",
258 			       settings, netdev->name );
259 			unregister_settings ( settings );
260 			free ( settings );
261 			return;
262 		}
263 	}
264 }
265 
266 /** GuestInfo per-netdevice driver */
267 struct net_driver guestinfo_net_driver __net_driver = {
268 	.name = "GuestInfo",
269 	.probe = guestinfo_net_probe,
270 	.remove = guestinfo_net_remove,
271 };
272