mirror of
				https://github.com/xcat2/xNBA.git
				synced 2025-10-28 18:05:36 +00:00 
			
		
		
		
	Replacement for fetch() which operates asynchronously and identifies
protocols by URI scheme.
This commit is contained in:
		
							
								
								
									
										172
									
								
								src/core/download.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/core/download.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/include/gpxe/download.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/include/gpxe/download.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 */ | ||||
		Reference in New Issue
	
	Block a user