iPXE
downloader.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 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  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdlib.h>
00027 #include <errno.h>
00028 #include <syslog.h>
00029 #include <ipxe/iobuf.h>
00030 #include <ipxe/xfer.h>
00031 #include <ipxe/open.h>
00032 #include <ipxe/job.h>
00033 #include <ipxe/uaccess.h>
00034 #include <ipxe/umalloc.h>
00035 #include <ipxe/image.h>
00036 #include <ipxe/xferbuf.h>
00037 #include <ipxe/downloader.h>
00038 
00039 /** @file
00040  *
00041  * Image downloader
00042  *
00043  */
00044 
00045 /** A downloader */
00046 struct downloader {
00047         /** Reference count for this object */
00048         struct refcnt refcnt;
00049 
00050         /** Job control interface */
00051         struct interface job;
00052         /** Data transfer interface */
00053         struct interface xfer;
00054 
00055         /** Image to contain downloaded file */
00056         struct image *image;
00057         /** Data transfer buffer */
00058         struct xfer_buffer buffer;
00059 };
00060 
00061 /**
00062  * Free downloader object
00063  *
00064  * @v refcnt            Downloader reference counter
00065  */
00066 static void downloader_free ( struct refcnt *refcnt ) {
00067         struct downloader *downloader =
00068                 container_of ( refcnt, struct downloader, refcnt );
00069 
00070         image_put ( downloader->image );
00071         free ( downloader );
00072 }
00073 
00074 /**
00075  * Terminate download
00076  *
00077  * @v downloader        Downloader
00078  * @v rc                Reason for termination
00079  */
00080 static void downloader_finished ( struct downloader *downloader, int rc ) {
00081 
00082         /* Log download status */
00083         if ( rc == 0 ) {
00084                 syslog ( LOG_NOTICE, "Downloaded \"%s\"\n",
00085                          downloader->image->name );
00086         } else {
00087                 syslog ( LOG_ERR, "Download of \"%s\" failed: %s\n",
00088                          downloader->image->name, strerror ( rc ) );
00089         }
00090 
00091         /* Update image length */
00092         downloader->image->len = downloader->buffer.len;
00093 
00094         /* Shut down interfaces */
00095         intf_shutdown ( &downloader->xfer, rc );
00096         intf_shutdown ( &downloader->job, rc );
00097 }
00098 
00099 /****************************************************************************
00100  *
00101  * Job control interface
00102  *
00103  */
00104 
00105 /**
00106  * Report progress of download job
00107  *
00108  * @v downloader        Downloader
00109  * @v progress          Progress report to fill in
00110  * @ret ongoing_rc      Ongoing job status code (if known)
00111  */
00112 static int downloader_progress ( struct downloader *downloader,
00113                                  struct job_progress *progress ) {
00114         int rc;
00115 
00116         /* Allow data transfer to provide an accurate description */
00117         if ( ( rc = job_progress ( &downloader->xfer, progress ) ) != 0 )
00118                 return rc;
00119 
00120         /* This is not entirely accurate, since downloaded data may
00121          * arrive out of order (e.g. with multicast protocols), but
00122          * it's a reasonable first approximation.
00123          */
00124         if ( ! progress->total ) {
00125                 progress->completed = downloader->buffer.pos;
00126                 progress->total = downloader->buffer.len;
00127         }
00128 
00129         return 0;
00130 }
00131 
00132 /****************************************************************************
00133  *
00134  * Data transfer interface
00135  *
00136  */
00137 
00138 /**
00139  * Handle received data
00140  *
00141  * @v downloader        Downloader
00142  * @v iobuf             Datagram I/O buffer
00143  * @v meta              Data transfer metadata
00144  * @ret rc              Return status code
00145  */
00146 static int downloader_deliver ( struct downloader *downloader,
00147                                 struct io_buffer *iobuf,
00148                                 struct xfer_metadata *meta ) {
00149         int rc;
00150 
00151         /* Add data to buffer */
00152         if ( ( rc = xferbuf_deliver ( &downloader->buffer, iob_disown ( iobuf ),
00153                                       meta ) ) != 0 )
00154                 goto err_deliver;
00155 
00156         return 0;
00157 
00158  err_deliver:
00159         downloader_finished ( downloader, rc );
00160         return rc;
00161 }
00162 
00163 /**
00164  * Get underlying data transfer buffer
00165  *
00166  * @v downloader        Downloader
00167  * @ret xferbuf         Data transfer buffer, or NULL on error
00168  */
00169 static struct xfer_buffer *
00170 downloader_buffer ( struct downloader *downloader ) {
00171 
00172         /* Provide direct access to underlying data transfer buffer */
00173         return &downloader->buffer;
00174 }
00175 
00176 /**
00177  * Redirect data transfer interface
00178  *
00179  * @v downloader        Downloader
00180  * @v type              New location type
00181  * @v args              Remaining arguments depend upon location type
00182  * @ret rc              Return status code
00183  */
00184 static int downloader_vredirect ( struct downloader *downloader, int type,
00185                                   va_list args ) {
00186         va_list tmp;
00187         struct uri *uri;
00188         int rc;
00189 
00190         /* Intercept redirects to a LOCATION_URI and update the image URI */
00191         if ( type == LOCATION_URI ) {
00192 
00193                 /* Extract URI argument */
00194                 va_copy ( tmp, args );
00195                 uri = va_arg ( tmp, struct uri * );
00196                 va_end ( tmp );
00197 
00198                 /* Set image URI */
00199                 if ( ( rc = image_set_uri ( downloader->image, uri ) ) != 0 )
00200                         goto err;
00201         }
00202 
00203         /* Redirect to new location */
00204         if ( ( rc = xfer_vreopen ( &downloader->xfer, type, args ) ) != 0 )
00205                 goto err;
00206 
00207         return 0;
00208 
00209  err:
00210         downloader_finished ( downloader, rc );
00211         return rc;
00212 }
00213 
00214 /** Downloader data transfer interface operations */
00215 static struct interface_operation downloader_xfer_operations[] = {
00216         INTF_OP ( xfer_deliver, struct downloader *, downloader_deliver ),
00217         INTF_OP ( xfer_buffer, struct downloader *, downloader_buffer ),
00218         INTF_OP ( xfer_vredirect, struct downloader *, downloader_vredirect ),
00219         INTF_OP ( intf_close, struct downloader *, downloader_finished ),
00220 };
00221 
00222 /** Downloader data transfer interface descriptor */
00223 static struct interface_descriptor downloader_xfer_desc =
00224         INTF_DESC ( struct downloader, xfer, downloader_xfer_operations );
00225 
00226 /****************************************************************************
00227  *
00228  * Job control interface
00229  *
00230  */
00231 
00232 /** Downloader job control interface operations */
00233 static struct interface_operation downloader_job_op[] = {
00234         INTF_OP ( job_progress, struct downloader *, downloader_progress ),
00235         INTF_OP ( intf_close, struct downloader *, downloader_finished ),
00236 };
00237 
00238 /** Downloader job control interface descriptor */
00239 static struct interface_descriptor downloader_job_desc =
00240         INTF_DESC ( struct downloader, job, downloader_job_op );
00241 
00242 /****************************************************************************
00243  *
00244  * Instantiator
00245  *
00246  */
00247 
00248 /**
00249  * Instantiate a downloader
00250  *
00251  * @v job               Job control interface
00252  * @v image             Image to fill with downloaded file
00253  * @ret rc              Return status code
00254  *
00255  * Instantiates a downloader object to download the content of the
00256  * specified image from its URI.
00257  */
00258 int create_downloader ( struct interface *job, struct image *image ) {
00259         struct downloader *downloader;
00260         int rc;
00261 
00262         /* Allocate and initialise structure */
00263         downloader = zalloc ( sizeof ( *downloader ) );
00264         if ( ! downloader )
00265                 return -ENOMEM;
00266         ref_init ( &downloader->refcnt, downloader_free );
00267         intf_init ( &downloader->job, &downloader_job_desc,
00268                     &downloader->refcnt );
00269         intf_init ( &downloader->xfer, &downloader_xfer_desc,
00270                     &downloader->refcnt );
00271         downloader->image = image_get ( image );
00272         xferbuf_umalloc_init ( &downloader->buffer, &image->data );
00273 
00274         /* Instantiate child objects and attach to our interfaces */
00275         if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 )
00276                 goto err;
00277 
00278         /* Attach parent interface, mortalise self, and return */
00279         intf_plug_plug ( &downloader->job, job );
00280         ref_put ( &downloader->refcnt );
00281         return 0;
00282 
00283  err:
00284         downloader_finished ( downloader, rc );
00285         ref_put ( &downloader->refcnt );
00286         return rc;
00287 }