Initial sketch for a downloader object
[people/pcmattman/gpxe.git] / src / core / downloader.c
1 /*
2  * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <gpxe/xfer.h>
22 #include <gpxe/open.h>
23 #include <gpxe/job.h>
24 #include <gpxe/uaccess.h>
25 #include <gpxe/umalloc.h>
26 #include <gpxe/image.h>
27 #include <gpxe/downloader.h>
28
29 /** @file
30  *
31  * Image downloader
32  *
33  */
34
35 /** A downloader */
36 struct downloader {
37         /** Reference count for this object */
38         struct refcnt refcnt;
39
40         /** Job control interface */
41         struct job_interface job;
42         /** Data transfer interface */
43         struct xfer_interface xfer;
44
45         /** Image to contain downloaded file */
46         struct image *image;
47         /** Current position within image buffer */
48         size_t pos;
49         /** Image registration routine */
50         int ( * register_image ) ( struct image *image );
51 };
52
53 /**
54  * Terminate download
55  *
56  * @v downloader        Downloader
57  * @v rc                Reason for termination
58  */
59 static void downloader_finished ( struct downloader *downloader, int rc ) {
60
61         /* Block further incoming messages */
62         job_nullify ( &downloader->job );
63         xfer_nullify ( &downloader->xfer );
64
65         /* Free resources and close interfaces */
66         image_put ( downloader->image );
67         xfer_close ( &downloader->xfer, rc );
68         job_done ( &downloader->job, rc );
69
70         /* Drop reference to self */
71         ref_put ( &downloader->refcnt );
72 }
73
74 /**
75  * Ensure that download buffer is large enough for the specified size
76  *
77  * @v downloader        Downloader
78  * @v len               Required minimum size
79  * @ret rc              Return status code
80  */
81 static int downloader_ensure_size ( struct downloader *downloader,
82                                     size_t len ) {
83         userptr_t new_buffer;
84
85         /* If buffer is already large enough, do nothing */
86         if ( len <= downloader->image->len )
87                 return 0;
88
89         /* Extend buffer */
90         new_buffer = urealloc ( downloader->image->data, len );
91         if ( ! new_buffer ) {
92                 DBGC ( downloader, "Downloader %p could not extend buffer to "
93                        "%zd bytes\n", downloader, len );
94                 return -ENOBUFS;
95         }
96         downloader->image->data = new_buffer;
97         downloader->image->len = len;
98
99         return 0;
100 }
101
102 /****************************************************************************
103  *
104  * Job control interface
105  *
106  */
107
108 /**
109  * Handle start() event received via job control interface
110  *
111  * @v job               Downloader job control interface
112  */
113 static void downloader_job_start ( struct job_interface *job ) {
114         struct downloader *downloader = 
115                 container_of ( job, struct downloader, job );
116
117         /* Start data transfer */
118         xfer_start ( &downloader->xfer );
119 }
120
121 /**
122  * Handle kill() event received via job control interface
123  *
124  * @v job               Downloader job control interface
125  */
126 static void downloader_job_kill ( struct job_interface *job ) {
127         struct downloader *downloader = 
128                 container_of ( job, struct downloader, job );
129
130         /* Terminate download */
131         downloader_finished ( downloader, -ECANCELED );
132 }
133
134 /** Downloader job control interface operations */
135 static struct job_interface_operations downloader_job_operations = {
136         .start          = downloader_job_start,
137         .done           = ignore_job_done,
138         .kill           = downloader_job_kill,
139         .progress       = ignore_job_progress,
140 };
141
142 /****************************************************************************
143  *
144  * Data transfer interface
145  *
146  */
147
148 /**
149  * Handle seek() event received via data transfer interface
150  *
151  * @v xfer              Downloader data transfer interface
152  * @v pos               New position
153  * @ret rc              Return status code
154  */
155 static int downloader_xfer_seek ( struct xfer_interface *xfer, size_t pos ) {
156         struct downloader *downloader =
157                 container_of ( xfer, struct downloader, xfer );
158         int rc;
159
160         /* Ensure that we have enough buffer space for this buffer position */
161         if ( ( rc = downloader_ensure_size ( downloader, pos ) ) != 0 )
162                 return rc;
163
164         /* Update current buffer position */
165         downloader->pos = pos;
166
167         return 0;
168 }
169
170 /**
171  * Handle deliver_raw() event received via data transfer interface
172  *
173  * @v xfer              Downloader data transfer interface
174  * @v data              Data buffer
175  * @v len               Length of data buffer
176  * @ret rc              Return status code
177  */
178 static int downloader_xfer_deliver_raw ( struct xfer_interface *xfer,
179                                          const void *data, size_t len ) {
180         struct downloader *downloader =
181                 container_of ( xfer, struct downloader, xfer );
182         size_t max;
183         int rc;
184
185         /* Ensure that we have enough buffer space for this data */
186         max = ( downloader->pos + len );
187         if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 )
188                 return rc;
189
190         /* Copy data to buffer */
191         copy_to_user ( downloader->image->data, downloader->pos, data, len );
192
193         /* Update current buffer position */
194         downloader->pos += len;
195
196         return 0;
197 }
198
199 /**
200  * Handle close() event received via data transfer interface
201  *
202  * @v xfer              Downloader data transfer interface
203  * @v rc                Reason for close
204  */
205 static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) {
206         struct downloader *downloader =
207                 container_of ( xfer, struct downloader, xfer );
208
209         /* Register image if download was successful */
210         if ( rc == 0 )
211                 rc = downloader->register_image ( downloader->image );
212
213         /* Terminate download */
214         downloader_finished ( downloader, rc );
215 }
216
217 /** Downloader data transfer interface operations */
218 static struct xfer_interface_operations downloader_xfer_operations = {
219         .start          = ignore_xfer_start,
220         .close          = downloader_xfer_close,
221         .vredirect      = default_xfer_vredirect,
222         .seek           = downloader_xfer_seek,
223         .deliver        = xfer_deliver_as_raw,
224         .deliver_raw    = downloader_xfer_deliver_raw,
225 };
226
227 /****************************************************************************
228  *
229  * Instantiator
230  *
231  */
232
233 /**
234  * Instantiate a downloader
235  *
236  * @v job               Job control interface
237  * @v uri_string        URI string
238  * @v image             Image to fill with downloaded file
239  * @v register_image    Image registration routine
240  * @ret rc              Return status code
241  *
242  * Instantiates a downloader object to download the specified URI into
243  * the specified image object.  If the download is successful, the
244  * image registration routine @c register_image() will be called.
245  */
246 int create_downloader ( struct job_interface *job, const char *uri_string,
247                         struct image *image,
248                         int ( * register_image ) ( struct image *image ) ) {
249         struct downloader *downloader;
250         int rc;
251
252         /* Allocate and initialise structure */
253         downloader = malloc ( sizeof ( *downloader ) );
254         if ( ! downloader )
255                 return -ENOMEM;
256         memset ( downloader, 0, sizeof ( *downloader ) );
257         job_init ( &downloader->job, &downloader_job_operations,
258                    &downloader->refcnt );
259         xfer_init ( &downloader->xfer, &downloader_xfer_operations,
260                     &downloader->refcnt );
261         downloader->image = image_get ( image );
262         downloader->register_image = register_image;
263
264         /* Instantiate child objects and attach to our interfaces */
265         if ( ( rc = open ( &downloader->xfer, LOCATION_URI,
266                            uri_string ) ) != 0 )
267                 goto err;
268
269         /* Attach parent interface and return */
270         job_plug_plug ( &downloader->job, job );
271         return 0;
272
273  err:
274         downloader_finished ( downloader, rc );
275         return rc;
276 }