[process] Include process name in debug messages
[ipxe.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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <ipxe/iobuf.h>
30 #include <ipxe/xfer.h>
31 #include <ipxe/open.h>
32 #include <ipxe/job.h>
33 #include <ipxe/uaccess.h>
34 #include <ipxe/umalloc.h>
35 #include <ipxe/image.h>
36 #include <ipxe/xferbuf.h>
37 #include <ipxe/downloader.h>
38
39 /** @file
40 *
41 * Image downloader
42 *
43 */
44
45 /** A downloader */
46 struct downloader {
47 /** Reference count for this object */
48 struct refcnt refcnt;
49
50 /** Job control interface */
51 struct interface job;
52 /** Data transfer interface */
53 struct interface xfer;
54
55 /** Image to contain downloaded file */
56 struct image *image;
57 /** Data transfer buffer */
58 struct xfer_buffer buffer;
59 };
60
61 /**
62 * Free downloader object
63 *
64 * @v refcnt Downloader reference counter
65 */
66 static void downloader_free ( struct refcnt *refcnt ) {
67 struct downloader *downloader =
68 container_of ( refcnt, struct downloader, refcnt );
69
70 image_put ( downloader->image );
71 free ( downloader );
72 }
73
74 /**
75 * Terminate download
76 *
77 * @v downloader Downloader
78 * @v rc Reason for termination
79 */
80 static void downloader_finished ( struct downloader *downloader, int rc ) {
81
82 /* Log download status */
83 if ( rc == 0 ) {
84 syslog ( LOG_NOTICE, "Downloaded \"%s\"\n",
85 downloader->image->name );
86 } else {
87 syslog ( LOG_ERR, "Download of \"%s\" failed: %s\n",
88 downloader->image->name, strerror ( rc ) );
89 }
90
91 /* Update image length */
92 downloader->image->len = downloader->buffer.len;
93
94 /* Shut down interfaces */
95 intf_shutdown ( &downloader->xfer, rc );
96 intf_shutdown ( &downloader->job, rc );
97 }
98
99 /****************************************************************************
100 *
101 * Job control interface
102 *
103 */
104
105 /**
106 * Report progress of download job
107 *
108 * @v downloader Downloader
109 * @v progress Progress report to fill in
110 * @ret ongoing_rc Ongoing job status code (if known)
111 */
112 static int downloader_progress ( struct downloader *downloader,
113 struct job_progress *progress ) {
114 int rc;
115
116 /* Allow data transfer to provide an accurate description */
117 if ( ( rc = job_progress ( &downloader->xfer, progress ) ) != 0 )
118 return rc;
119
120 /* This is not entirely accurate, since downloaded data may
121 * arrive out of order (e.g. with multicast protocols), but
122 * it's a reasonable first approximation.
123 */
124 if ( ! progress->total ) {
125 progress->completed = downloader->buffer.pos;
126 progress->total = downloader->buffer.len;
127 }
128
129 return 0;
130 }
131
132 /****************************************************************************
133 *
134 * Data transfer interface
135 *
136 */
137
138 /**
139 * Handle received data
140 *
141 * @v downloader Downloader
142 * @v iobuf Datagram I/O buffer
143 * @v meta Data transfer metadata
144 * @ret rc Return status code
145 */
146 static int downloader_deliver ( struct downloader *downloader,
147 struct io_buffer *iobuf,
148 struct xfer_metadata *meta ) {
149 int rc;
150
151 /* Add data to buffer */
152 if ( ( rc = xferbuf_deliver ( &downloader->buffer, iob_disown ( iobuf ),
153 meta ) ) != 0 )
154 goto err_deliver;
155
156 return 0;
157
158 err_deliver:
159 downloader_finished ( downloader, rc );
160 return rc;
161 }
162
163 /**
164 * Get underlying data transfer buffer
165 *
166 * @v downloader Downloader
167 * @ret xferbuf Data transfer buffer, or NULL on error
168 */
169 static struct xfer_buffer *
170 downloader_buffer ( struct downloader *downloader ) {
171
172 /* Provide direct access to underlying data transfer buffer */
173 return &downloader->buffer;
174 }
175
176 /**
177 * Redirect data transfer interface
178 *
179 * @v downloader Downloader
180 * @v type New location type
181 * @v args Remaining arguments depend upon location type
182 * @ret rc Return status code
183 */
184 static int downloader_vredirect ( struct downloader *downloader, int type,
185 va_list args ) {
186 va_list tmp;
187 struct uri *uri;
188 int rc;
189
190 /* Intercept redirects to a LOCATION_URI and update the image URI */
191 if ( type == LOCATION_URI ) {
192
193 /* Extract URI argument */
194 va_copy ( tmp, args );
195 uri = va_arg ( tmp, struct uri * );
196 va_end ( tmp );
197
198 /* Set image URI */
199 if ( ( rc = image_set_uri ( downloader->image, uri ) ) != 0 )
200 goto err;
201 }
202
203 /* Redirect to new location */
204 if ( ( rc = xfer_vreopen ( &downloader->xfer, type, args ) ) != 0 )
205 goto err;
206
207 return 0;
208
209 err:
210 downloader_finished ( downloader, rc );
211 return rc;
212 }
213
214 /** Downloader data transfer interface operations */
215 static struct interface_operation downloader_xfer_operations[] = {
216 INTF_OP ( xfer_deliver, struct downloader *, downloader_deliver ),
217 INTF_OP ( xfer_buffer, struct downloader *, downloader_buffer ),
218 INTF_OP ( xfer_vredirect, struct downloader *, downloader_vredirect ),
219 INTF_OP ( intf_close, struct downloader *, downloader_finished ),
220 };
221
222 /** Downloader data transfer interface descriptor */
223 static struct interface_descriptor downloader_xfer_desc =
224 INTF_DESC ( struct downloader, xfer, downloader_xfer_operations );
225
226 /****************************************************************************
227 *
228 * Job control interface
229 *
230 */
231
232 /** Downloader job control interface operations */
233 static struct interface_operation downloader_job_op[] = {
234 INTF_OP ( job_progress, struct downloader *, downloader_progress ),
235 INTF_OP ( intf_close, struct downloader *, downloader_finished ),
236 };
237
238 /** Downloader job control interface descriptor */
239 static struct interface_descriptor downloader_job_desc =
240 INTF_DESC ( struct downloader, job, downloader_job_op );
241
242 /****************************************************************************
243 *
244 * Instantiator
245 *
246 */
247
248 /**
249 * Instantiate a downloader
250 *
251 * @v job Job control interface
252 * @v image Image to fill with downloaded file
253 * @ret rc Return status code
254 *
255 * Instantiates a downloader object to download the content of the
256 * specified image from its URI.
257 */
258 int create_downloader ( struct interface *job, struct image *image ) {
259 struct downloader *downloader;
260 int rc;
261
262 /* Allocate and initialise structure */
263 downloader = zalloc ( sizeof ( *downloader ) );
264 if ( ! downloader )
265 return -ENOMEM;
266 ref_init ( &downloader->refcnt, downloader_free );
267 intf_init ( &downloader->job, &downloader_job_desc,
268 &downloader->refcnt );
269 intf_init ( &downloader->xfer, &downloader_xfer_desc,
270 &downloader->refcnt );
271 downloader->image = image_get ( image );
272 xferbuf_umalloc_init ( &downloader->buffer, &image->data );
273
274 /* Instantiate child objects and attach to our interfaces */
275 if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 )
276 goto err;
277
278 /* Attach parent interface, mortalise self, and return */
279 intf_plug_plug ( &downloader->job, job );
280 ref_put ( &downloader->refcnt );
281 return 0;
282
283 err:
284 downloader_finished ( downloader, rc );
285 ref_put ( &downloader->refcnt );
286 return rc;
287 }