2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
20 FILE_LICENCE ( GPL2_OR_LATER );
25 #include <ipxe/iobuf.h>
26 #include <ipxe/xfer.h>
27 #include <ipxe/open.h>
29 #include <ipxe/uaccess.h>
30 #include <ipxe/umalloc.h>
31 #include <ipxe/image.h>
32 #include <ipxe/profile.h>
33 #include <ipxe/downloader.h>
41 /** Receive profiler */
42 static struct profiler downloader_rx_profiler __profiler =
43 { .name = "downloader.rx" };
45 /** Data copy profiler */
46 static struct profiler downloader_copy_profiler __profiler =
47 { .name = "downloader.copy" };
51 /** Reference count for this object */
54 /** Job control interface */
56 /** Data transfer interface */
57 struct interface xfer;
59 /** Image to contain downloaded file */
61 /** Current position within image buffer */
66 * Free downloader object
68 * @v refcnt Downloader reference counter
70 static void downloader_free ( struct refcnt *refcnt ) {
71 struct downloader *downloader =
72 container_of ( refcnt, struct downloader, refcnt );
74 image_put ( downloader->image );
81 * @v downloader Downloader
82 * @v rc Reason for termination
84 static void downloader_finished ( struct downloader *downloader, int rc ) {
86 /* Log download status */
88 syslog ( LOG_NOTICE, "Downloaded \"%s\"\n",
89 downloader->image->name );
91 syslog ( LOG_ERR, "Download of \"%s\" failed: %s\n",
92 downloader->image->name, strerror ( rc ) );
95 /* Shut down interfaces */
96 intf_shutdown ( &downloader->xfer, rc );
97 intf_shutdown ( &downloader->job, rc );
101 * Ensure that download buffer is large enough for the specified size
103 * @v downloader Downloader
104 * @v len Required minimum size
105 * @ret rc Return status code
107 static int downloader_ensure_size ( struct downloader *downloader,
109 userptr_t new_buffer;
111 /* If buffer is already large enough, do nothing */
112 if ( len <= downloader->image->len )
115 DBGC ( downloader, "Downloader %p extending to %zd bytes\n",
119 new_buffer = urealloc ( downloader->image->data, len );
120 if ( ! new_buffer ) {
121 DBGC ( downloader, "Downloader %p could not extend buffer to "
122 "%zd bytes\n", downloader, len );
125 downloader->image->data = new_buffer;
126 downloader->image->len = len;
131 /****************************************************************************
133 * Job control interface
138 * Report progress of download job
140 * @v downloader Downloader
141 * @v progress Progress report to fill in
142 * @ret ongoing_rc Ongoing job status code (if known)
144 static int downloader_progress ( struct downloader *downloader,
145 struct job_progress *progress ) {
147 /* This is not entirely accurate, since downloaded data may
148 * arrive out of order (e.g. with multicast protocols), but
149 * it's a reasonable first approximation.
151 progress->completed = downloader->pos;
152 progress->total = downloader->image->len;
157 /****************************************************************************
159 * Data transfer interface
164 * Handle received data
166 * @v downloader Downloader
167 * @v iobuf Datagram I/O buffer
168 * @v meta Data transfer metadata
169 * @ret rc Return status code
171 static int downloader_xfer_deliver ( struct downloader *downloader,
172 struct io_buffer *iobuf,
173 struct xfer_metadata *meta ) {
178 /* Start profiling */
179 profile_start ( &downloader_rx_profiler );
181 /* Calculate new buffer position */
182 if ( meta->flags & XFER_FL_ABS_OFFSET )
184 downloader->pos += meta->offset;
186 /* Ensure that we have enough buffer space for this data */
187 len = iob_len ( iobuf );
188 max = ( downloader->pos + len );
189 if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
192 /* Copy data to buffer */
193 profile_start ( &downloader_copy_profiler );
194 copy_to_user ( downloader->image->data, downloader->pos,
196 profile_stop ( &downloader_copy_profiler );
198 /* Update current buffer position */
199 downloader->pos += len;
204 downloader_finished ( downloader, rc );
205 profile_stop ( &downloader_rx_profiler );
209 /** Downloader data transfer interface operations */
210 static struct interface_operation downloader_xfer_operations[] = {
211 INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ),
212 INTF_OP ( intf_close, struct downloader *, downloader_finished ),
215 /** Downloader data transfer interface descriptor */
216 static struct interface_descriptor downloader_xfer_desc =
217 INTF_DESC ( struct downloader, xfer, downloader_xfer_operations );
219 /****************************************************************************
221 * Job control interface
225 /** Downloader job control interface operations */
226 static struct interface_operation downloader_job_op[] = {
227 INTF_OP ( job_progress, struct downloader *, downloader_progress ),
228 INTF_OP ( intf_close, struct downloader *, downloader_finished ),
231 /** Downloader job control interface descriptor */
232 static struct interface_descriptor downloader_job_desc =
233 INTF_DESC ( struct downloader, job, downloader_job_op );
235 /****************************************************************************
242 * Instantiate a downloader
244 * @v job Job control interface
245 * @v image Image to fill with downloaded file
246 * @ret rc Return status code
248 * Instantiates a downloader object to download the content of the
249 * specified image from its URI.
251 int create_downloader ( struct interface *job, struct image *image ) {
252 struct downloader *downloader;
255 /* Allocate and initialise structure */
256 downloader = zalloc ( sizeof ( *downloader ) );
259 ref_init ( &downloader->refcnt, downloader_free );
260 intf_init ( &downloader->job, &downloader_job_desc,
261 &downloader->refcnt );
262 intf_init ( &downloader->xfer, &downloader_xfer_desc,
263 &downloader->refcnt );
264 downloader->image = image_get ( image );
266 /* Instantiate child objects and attach to our interfaces */
267 if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 )
270 /* Attach parent interface, mortalise self, and return */
271 intf_plug_plug ( &downloader->job, job );
272 ref_put ( &downloader->refcnt );
276 downloader_finished ( downloader, rc );
277 ref_put ( &downloader->refcnt );