Replacement for fetch() which operates asynchronously and identifies
authorMichael Brown <mcb30@etherboot.org>
Thu, 18 Jan 2007 03:37:05 +0000 (03:37 +0000)
committerMichael Brown <mcb30@etherboot.org>
Thu, 18 Jan 2007 03:37:05 +0000 (03:37 +0000)
protocols by URI scheme.

src/core/download.c [new file with mode: 0644]
src/include/gpxe/download.h [new file with mode: 0644]

diff --git a/src/core/download.c b/src/core/download.c
new file mode 100644 (file)
index 0000000..f748d02
--- /dev/null
@@ -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 (file)
index 0000000..cb58b7a
--- /dev/null
@@ -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 */