iPXE
guestinfo.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 /** @file
00023  *
00024  * VMware GuestInfo settings
00025  *
00026  */
00027 
00028 #include <stdint.h>
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <errno.h>
00033 #include <ipxe/init.h>
00034 #include <ipxe/settings.h>
00035 #include <ipxe/netdevice.h>
00036 #include <ipxe/guestrpc.h>
00037 
00038 /** GuestInfo GuestRPC channel */
00039 static int guestinfo_channel;
00040 
00041 /**
00042  * Fetch value of typed GuestInfo setting
00043  *
00044  * @v settings          Settings block
00045  * @v setting           Setting to fetch
00046  * @v type              Setting type to attempt (or NULL for default)
00047  * @v data              Buffer to fill with setting data
00048  * @v len               Length of buffer
00049  * @ret found           Setting found in GuestInfo
00050  * @ret len             Length of setting data, or negative error
00051  */
00052 static int guestinfo_fetch_type ( struct settings *settings,
00053                                   struct setting *setting,
00054                                   const struct setting_type *type,
00055                                   void *data, size_t len, int *found ) {
00056         const char *parent_name = settings->parent->name;
00057         char command[ 24 /* "info-get guestinfo.ipxe." */ +
00058                       strlen ( parent_name ) + 1 /* "." */ +
00059                       strlen ( setting->name ) + 1 /* "." */ +
00060                       ( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ];
00061         struct setting *predefined;
00062         char *info;
00063         int info_len;
00064         int check_len;
00065         int ret;
00066 
00067         /* Construct info-get command */
00068         snprintf ( command, sizeof ( command ),
00069                    "info-get guestinfo.ipxe.%s%s%s%s%s",
00070                    parent_name, ( parent_name[0] ? "." : "" ), setting->name,
00071                    ( type ? "." : "" ), ( type ? type->name : "" ) );
00072 
00073         /* Check for existence and obtain length of GuestInfo value */
00074         info_len = guestrpc_command ( guestinfo_channel, command, NULL, 0 );
00075         if ( info_len < 0 ) {
00076                 ret = info_len;
00077                 goto err_get_info_len;
00078         }
00079 
00080         /* Mark as found */
00081         *found = 1;
00082 
00083         /* Determine default type if necessary */
00084         if ( ! type ) {
00085                 predefined = find_setting ( setting->name );
00086                 type = ( predefined ? predefined->type : &setting_type_string );
00087         }
00088         assert ( type != NULL );
00089 
00090         /* Allocate temporary block to hold GuestInfo value */
00091         info = zalloc ( info_len + 1 /* NUL */ );
00092         if ( ! info ) {
00093                 DBGC ( settings, "GuestInfo %p could not allocate %d bytes\n",
00094                        settings, info_len );
00095                 ret = -ENOMEM;
00096                 goto err_alloc;
00097         }
00098         info[info_len] = '\0';
00099 
00100         /* Fetch GuestInfo value */
00101         check_len = guestrpc_command ( guestinfo_channel, command,
00102                                        info, info_len );
00103         if ( check_len < 0 ) {
00104                 ret = check_len;
00105                 goto err_get_info;
00106         }
00107         if ( check_len != info_len ) {
00108                 DBGC ( settings, "GuestInfo %p length mismatch (expected %d, "
00109                        "got %d)\n", settings, info_len, check_len );
00110                 ret = -EIO;
00111                 goto err_get_info;
00112         }
00113         DBGC2 ( settings, "GuestInfo %p found %s = \"%s\"\n",
00114                 settings, &command[9] /* Skip "info-get " */, info );
00115 
00116         /* Parse GuestInfo value according to type */
00117         ret = setting_parse ( type, info, data, len );
00118         if ( ret < 0 ) {
00119                 DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: "
00120                        "%s\n", settings, info, type->name, strerror ( ret ) );
00121                 goto err_parse;
00122         }
00123 
00124  err_parse:
00125  err_get_info:
00126         free ( info );
00127  err_alloc:
00128  err_get_info_len:
00129         return ret;
00130 }
00131 
00132 /**
00133  * Fetch value of GuestInfo setting
00134  *
00135  * @v settings          Settings block
00136  * @v setting           Setting to fetch
00137  * @v data              Buffer to fill with setting data
00138  * @v len               Length of buffer
00139  * @ret len             Length of setting data, or negative error
00140  */
00141 static int guestinfo_fetch ( struct settings *settings,
00142                              struct setting *setting,
00143                              void *data, size_t len ) {
00144         struct setting_type *type;
00145         int found = 0;
00146         int ret;
00147 
00148         /* Try default type first */
00149         ret = guestinfo_fetch_type ( settings, setting, NULL,
00150                                      data, len, &found );
00151         if ( found )
00152                 return ret;
00153 
00154         /* Otherwise, try all possible types */
00155         for_each_table_entry ( type, SETTING_TYPES ) {
00156                 ret = guestinfo_fetch_type ( settings, setting, type,
00157                                              data, len, &found );
00158                 if ( found )
00159                         return ret;
00160         }
00161 
00162         /* Not found */
00163         return -ENOENT;
00164 }
00165 
00166 /** GuestInfo settings operations */
00167 static struct settings_operations guestinfo_settings_operations = {
00168         .fetch = guestinfo_fetch,
00169 };
00170 
00171 /** GuestInfo settings */
00172 static struct settings guestinfo_settings = {
00173         .refcnt = NULL,
00174         .siblings = LIST_HEAD_INIT ( guestinfo_settings.siblings ),
00175         .children = LIST_HEAD_INIT ( guestinfo_settings.children ),
00176         .op = &guestinfo_settings_operations,
00177 };
00178 
00179 /** Initialise GuestInfo settings */
00180 static void guestinfo_init ( void ) {
00181         int rc;
00182 
00183         /* Open GuestRPC channel */
00184         guestinfo_channel = guestrpc_open();
00185         if ( guestinfo_channel < 0 ) {
00186                 rc = guestinfo_channel;
00187                 DBG ( "GuestInfo could not open channel: %s\n",
00188                       strerror ( rc ) );
00189                 return;
00190         }
00191 
00192         /* Register root GuestInfo settings */
00193         if ( ( rc = register_settings ( &guestinfo_settings, NULL,
00194                                         "vmware" ) ) != 0 ) {
00195                 DBG ( "GuestInfo could not register settings: %s\n",
00196                       strerror ( rc ) );
00197                 return;
00198         }
00199 }
00200 
00201 /** GuestInfo settings initialiser */
00202 struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
00203         .initialise = guestinfo_init,
00204 };
00205 
00206 /**
00207  * Create per-netdevice GuestInfo settings
00208  *
00209  * @v netdev            Network device
00210  * @ret rc              Return status code
00211  */
00212 static int guestinfo_net_probe ( struct net_device *netdev ) {
00213         struct settings *settings;
00214         int rc;
00215 
00216         /* Do nothing unless we have a GuestInfo channel available */
00217         if ( guestinfo_channel < 0 )
00218                 return 0;
00219 
00220         /* Allocate and initialise settings block */
00221         settings = zalloc ( sizeof ( *settings ) );
00222         if ( ! settings ) {
00223                 rc = -ENOMEM;
00224                 goto err_alloc;
00225         }
00226         settings_init ( settings, &guestinfo_settings_operations, NULL, NULL );
00227 
00228         /* Register settings */
00229         if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
00230                                         "vmware" ) ) != 0 ) {
00231                 DBGC ( settings, "GuestInfo %p could not register for %s: %s\n",
00232                        settings, netdev->name, strerror ( rc ) );
00233                 goto err_register;
00234         }
00235         DBGC ( settings, "GuestInfo %p registered for %s\n",
00236                settings, netdev->name );
00237 
00238         return 0;
00239 
00240  err_register:
00241         free ( settings );
00242  err_alloc:
00243         return rc;
00244 }
00245 
00246 /**
00247  * Remove per-netdevice GuestInfo settings
00248  *
00249  * @v netdev            Network device
00250  */
00251 static void guestinfo_net_remove ( struct net_device *netdev ) {
00252         struct settings *parent = netdev_settings ( netdev );
00253         struct settings *settings;
00254 
00255         list_for_each_entry ( settings, &parent->children, siblings ) {
00256                 if ( settings->op == &guestinfo_settings_operations ) {
00257                         DBGC ( settings, "GuestInfo %p unregistered for %s\n",
00258                                settings, netdev->name );
00259                         unregister_settings ( settings );
00260                         free ( settings );
00261                         return;
00262                 }
00263         }
00264 }
00265 
00266 /** GuestInfo per-netdevice driver */
00267 struct net_driver guestinfo_net_driver __net_driver = {
00268         .name = "GuestInfo",
00269         .probe = guestinfo_net_probe,
00270         .remove = guestinfo_net_remove,
00271 };