summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/download.c172
-rw-r--r--src/include/gpxe/download.h59
2 files changed, 231 insertions, 0 deletions
diff --git a/src/core/download.c b/src/core/download.c
new file mode 100644
index 000000000..f748d0233
--- /dev/null
+++ b/src/core/download.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Download protocols
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/ebuffer.h>
+#include <gpxe/download.h>
+
+/** Registered download protocols */
+static struct download_protocol download_protocols[0]
+ __table_start ( struct download_protocol, download_protocols );
+static struct download_protocol download_protocols_end[0]
+ __table_end ( struct download_protocol, download_protocols );
+
+/**
+ * Identify download protocol
+ *
+ * @v name Download protocol name
+ * @ret protocol Download protocol, or NULL
+ */
+static struct download_protocol * find_protocol ( const char *name ) {
+ struct download_protocol *protocol;
+
+ for ( protocol = download_protocols; protocol < download_protocols_end;
+ protocol++ ) {
+ if ( strcmp ( name, protocol->name ) == 0 )
+ return protocol;
+ }
+ return NULL;
+}
+
+/** Free download resources */
+static void download_reap ( struct async *async ) {
+ struct download *download =
+ container_of ( async, struct download, async );
+
+ free ( download );
+}
+
+/**
+ * Handle download termination
+ *
+ * @v async Download asynchronous operation
+ * @v signal SIGCHLD
+ */
+static void download_sigchld ( struct async *async,
+ enum signal signal __unused ) {
+ struct download *download =
+ container_of ( async, struct download, async );
+ int rc;
+
+ /* Reap child */
+ async_wait ( async, &rc, 1 );
+
+ /* Clean up */
+ if ( rc == 0 ) {
+ /* Transfer ownership of buffer to parent */
+ *(download->data) = download->buffer.addr;
+ *(download->len) = download->buffer.fill;
+ } else {
+ /* Discard the buffer */
+ ufree ( download->buffer.addr );
+ }
+ free_uri ( download->uri );
+ download->uri = NULL;
+
+ /* Terminate ourselves */
+ async_done ( async, rc );
+}
+
+/** Download asynchronous operations */
+static struct async_operations download_async_operations = {
+ .reap = download_reap,
+ .signal = {
+ [SIGCHLD] = download_sigchld,
+ },
+};
+
+/**
+ * Start download
+ *
+ * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz")
+ * @v parent Parent asynchronous operation
+ * @ret data Loaded file
+ * @ret len Length of loaded file
+ * @ret rc Return status code
+ *
+ * Starts download of a file to a user buffer. The user buffer is
+ * allocated with umalloc(). The parent asynchronous operation will
+ * be notified via SIGCHLD when the download completes. If the
+ * download completes successfully, the @c data and @c len fields will
+ * have been filled in, and the parent takes ownership of the buffer,
+ * which must eventually be freed with ufree().
+ *
+ * The uri_string does not need to remain persistent for the duration
+ * of the download; the parent may discard it as soon as
+ * start_download returns.
+ */
+int start_download ( const char *uri_string, struct async *parent,
+ userptr_t *data, size_t *len ) {
+ struct download *download;
+ int rc;
+
+ /* Allocate and populate download structure */
+ download = malloc ( sizeof ( *download ) );
+ if ( ! download )
+ return -ENOMEM;
+ memset ( download, 0, sizeof ( *download ) );
+ download->data = data;
+ download->len = len;
+ async_init ( &download->async, &download_async_operations, parent );
+
+ /* Parse the URI */
+ download->uri = parse_uri ( uri_string );
+ if ( ! download->uri ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Allocate an expandable buffer to hold the file */
+ if ( ( rc = ebuffer_alloc ( &download->buffer, 0 ) ) != 0 )
+ goto err;
+
+ /* Identify the download protocol */
+ download->protocol = find_protocol ( download->uri->scheme );
+ if ( ! download->protocol ) {
+ DBG ( "No such protocol \"%s\"\n", download->uri->scheme );
+ rc = -ENOTSUP;
+ goto err;
+ }
+
+ /* Start the actual download */
+ if ( ( rc = download->protocol->start_download ( download->uri,
+ &download->buffer, &download->async ) ) != 0 ) {
+ DBG ( "Could not start \"%s\" download: %s\n",
+ download->uri->scheme, strerror ( rc ) );
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ async_uninit ( &download->async );
+ ufree ( download->buffer.addr );
+ free_uri ( download->uri );
+ free ( download );
+ return rc;
+}
diff --git a/src/include/gpxe/download.h b/src/include/gpxe/download.h
new file mode 100644
index 000000000..cb58b7adc
--- /dev/null
+++ b/src/include/gpxe/download.h
@@ -0,0 +1,59 @@
+#ifndef _GPXE_DOWNLOAD_H
+#define _GPXE_DOWNLOAD_H
+
+/** @file
+ *
+ * Download protocols
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/async.h>
+#include <gpxe/buffer.h>
+#include <gpxe/uri.h>
+#include <gpxe/tables.h>
+
+/** A download protocol */
+struct download_protocol {
+ /** Protocol name (e.g. "http") */
+ const char *name;
+ /** Start a download via this protocol
+ *
+ * @v uri Uniform Resource Identifier
+ * @v buffer Buffer into which to download file
+ * @v parent Parent asynchronous operation
+ * @ret rc Return status code
+ *
+ * The @c uri and @c buffer will remain persistent for the
+ * duration of the asynchronous operation.
+ */
+ int ( * start_download ) ( struct uri *uri, struct buffer *buffer,
+ struct async *parent );
+};
+
+/** Register a download protocol */
+#define __download_protocol __table ( struct download_protocol, \
+ download_protocols, 01 )
+
+/** A download in progress */
+struct download {
+ /** User buffer allocated for the download */
+ userptr_t *data;
+ /** Size of the download */
+ size_t *len;
+
+ /** URI being downloaded */
+ struct uri *uri;
+ /** Expandable buffer for this download */
+ struct buffer buffer;
+ /** Download protocol */
+ struct download_protocol *protocol;
+ /** Asynchronous operation for this download */
+ struct async async;
+};
+
+extern int start_download ( const char *uri_string, struct async *parent,
+ userptr_t *data, size_t *len );
+
+#endif /* _GPXE_DOWNLOAD_H */