summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/async.c326
-rw-r--r--src/drivers/ata/aoedev.c9
-rw-r--r--src/drivers/scsi/iscsidev.c8
-rw-r--r--src/include/errno.h5
-rw-r--r--src/include/gpxe/aoe.h7
-rw-r--r--src/include/gpxe/async.h184
-rw-r--r--src/include/gpxe/dhcp.h4
-rw-r--r--src/include/gpxe/ftp.h2
-rw-r--r--src/include/gpxe/hello.h2
-rw-r--r--src/include/gpxe/http.h21
-rw-r--r--src/include/gpxe/iscsi.h7
-rw-r--r--src/include/gpxe/tftp.h2
-rw-r--r--src/include/usr/fetch.h2
-rw-r--r--src/net/aoe.c12
-rw-r--r--src/net/tcp/ftp.c4
-rw-r--r--src/net/tcp/hello.c6
-rw-r--r--src/net/tcp/http.c84
-rw-r--r--src/net/tcp/iscsi.c16
-rw-r--r--src/net/udp/dhcp.c17
-rw-r--r--src/net/udp/tftp.c6
-rw-r--r--src/tests/aoeboot.c1
-rw-r--r--src/tests/dhcptest.c5
-rw-r--r--src/tests/ftptest.c52
-rw-r--r--src/tests/hellotest.c43
-rw-r--r--src/usr/dhcpmgmt.c12
-rw-r--r--src/usr/fetch.c63
26 files changed, 654 insertions, 246 deletions
diff --git a/src/core/async.c b/src/core/async.c
index 8bd5d7c29..95bbc057e 100644
--- a/src/core/async.c
+++ b/src/core/async.c
@@ -16,7 +16,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <string.h>
#include <errno.h>
+#include <assert.h>
#include <gpxe/process.h>
#include <gpxe/async.h>
@@ -27,26 +29,316 @@
*/
/**
- * Wait for asynchronous operation to complete
+ * Name signal
*
- * @v aop Asynchronous operation
- * @ret rc Return status code
+ * @v signal Signal number
+ * @ret name Name of signal
+ */
+static inline __attribute__ (( always_inline )) const char *
+signal_name ( enum signal signal ) {
+ switch ( signal ) {
+ case SIGCHLD: return "SIGCHLD";
+ case SIGKILL: return "SIGKILL";
+ case SIGUPDATE: return "SIGUPDATE";
+ default: return "SIG<UNKNOWN>";
+ }
+}
+
+/**
+ * Initialise an asynchronous operation
+ *
+ * @v async Asynchronous operation
+ * @v aop Asynchronous operation operations to use
+ * @v parent Parent asynchronous operation, or NULL
+ * @ret aid Asynchronous operation ID
*
- * Blocks until the specified asynchronous operation has completed.
- * The return status is the return status of the asynchronous
- * operation itself.
+ * It is valid to create an asynchronous operation with no parent
+ * operation; see async_init_orphan().
*/
-int async_wait ( struct async_operation *aop ) {
- int rc;
+aid_t async_init ( struct async *async, struct async_operations *aop,
+ struct async *parent ) {
+ static aid_t aid = 1;
- /* Wait for operation to complete */
- do {
- step();
- rc = async_status ( aop );
- } while ( rc == -EINPROGRESS );
-
- /* Prepare for next call to async_wait() */
- async_set_status ( aop, -EINPROGRESS );
+ /* Assign identifier. Negative IDs are used to indicate
+ * errors, so avoid assigning them.
+ */
+ ++aid;
+ aid &= ( ( ~( ( aid_t ) 0 ) ) >> 1 );
+
+ DBGC ( async, "ASYNC %p (type %p) initialising as", async, aop );
+ if ( parent ) {
+ DBGC ( async, " child of ASYNC %p", parent );
+ } else {
+ DBGC ( async, " orphan" );
+ }
+ DBGC ( async, " with ID %ld\n", aid );
+
+ assert ( async != NULL );
+ assert ( aop != NULL );
+
+ /* Add to hierarchy */
+ if ( parent ) {
+ async->parent = parent;
+ list_add ( &async->siblings, &parent->children );
+ }
+ INIT_LIST_HEAD ( &async->children );
+
+ /* Initialise fields */
+ async->rc = -EINPROGRESS;
+ async->completed = 0;
+ async->total = 0;
+ async->aop = aop;
+ async->aid = aid;
+
+ return async->aid;
+}
- return rc;
+/**
+ * SIGCHLD 'ignore' handler
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal received
+ */
+static void async_ignore_sigchld ( struct async *async, enum signal signal ) {
+ aid_t waited_aid;
+
+ assert ( async != NULL );
+ assert ( signal == SIGCHLD );
+
+ /* Reap the child */
+ waited_aid = async_wait ( async, NULL, 0 );
+ assert ( waited_aid >= 0 );
}
+
+/**
+ * 'Ignore' signal handler
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal received
+ */
+void async_ignore_signal ( struct async *async, enum signal signal ) {
+
+ DBGC ( async, "ASYNC %p using ignore handler for %s\n",
+ async, signal_name ( signal ) );
+
+ assert ( async != NULL );
+
+ switch ( signal ) {
+ case SIGCHLD:
+ async_ignore_sigchld ( async, signal );
+ break;
+ case SIGKILL:
+ case SIGUPDATE:
+ default:
+ /* Nothing to do */
+ break;
+ }
+}
+
+/**
+ * Default signal handler
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal received
+ */
+static void async_default_signal ( struct async *async, enum signal signal ) {
+
+ DBGC ( async, "ASYNC %p using default handler for %s\n",
+ async, signal_name ( signal ) );
+
+ assert ( async != NULL );
+
+ switch ( signal ) {
+ case SIGCHLD:
+ case SIGKILL:
+ case SIGUPDATE:
+ default:
+ /* Nothing to do */
+ break;
+ }
+}
+
+/**
+ * Send signal to asynchronous operation
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal to send
+ */
+void async_signal ( struct async *async, enum signal signal ) {
+ signal_handler_t handler;
+
+ DBGC ( async, "ASYNC %p receiving %s\n",
+ async, signal_name ( signal ) );
+
+ assert ( async != NULL );
+ assert ( async->aop != NULL );
+ assert ( signal < SIGMAX );
+
+ handler = async->aop->signal[signal];
+ if ( handler ) {
+ /* Use the asynchronous operation's signal handler */
+ handler ( async, signal );
+ } else {
+ /* Use the default handler */
+ async_default_signal ( async, signal );
+ }
+}
+
+/**
+ * Send signal to all child asynchronous operations
+ *
+ * @v async Asynchronous operation
+ * @v signal Signal to send
+ */
+void async_signal_children ( struct async *async, enum signal signal ) {
+ struct async *child;
+ struct async *tmp;
+
+ assert ( async != NULL );
+
+ list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
+ async_signal ( child, signal );
+ }
+}
+
+/**
+ * Mark asynchronous operation as complete
+ *
+ * @v async Asynchronous operation
+ * @v rc Return status code
+ *
+ * An asynchronous operation should call this once it has completed.
+ * After calling async_done(), it must be prepared to be reaped by
+ * having its reap() method called.
+ */
+void async_done ( struct async *async, int rc ) {
+
+ DBGC ( async, "ASYNC %p completing with status %d (%s)\n",
+ async, rc, strerror ( rc ) );
+
+ assert ( async != NULL );
+ assert ( async->parent != NULL );
+ assert ( rc != -EINPROGRESS );
+
+ /* Store return status code */
+ async->rc = rc;
+
+ /* Send SIGCHLD to parent. Guard against NULL pointer dereferences */
+ if ( async->parent )
+ async_signal ( async->parent, SIGCHLD );
+}
+
+/**
+ * Reap default handler
+ *
+ * @v async Asynchronous operation
+ */
+static void async_reap_default ( struct async *async ) {
+
+ DBGC ( async, "ASYNC %p ignoring REAP\n", async );
+
+ assert ( async != NULL );
+
+ /* Nothing to do */
+}
+
+/**
+ * Wait for any child asynchronous operation to complete
+ *
+ * @v child Child asynchronous operation
+ * @v rc Child exit status to fill in, or NULL
+ * @v block Block waiting for child operation to complete
+ * @ret aid Asynchronous operation ID, or -1 on error
+ */
+aid_t async_wait ( struct async *async, int *rc, int block ) {
+ struct async *child;
+ aid_t child_aid;
+ int dummy_rc;
+
+ DBGC ( async, "ASYNC %p performing %sblocking wait%s\n", async,
+ ( block ? "" : "non-" ), ( rc ? "" : " (ignoring status)" ) );
+
+ assert ( async != NULL );
+
+ /* Avoid multiple tests for "if ( rc )" */
+ if ( ! rc )
+ rc = &dummy_rc;
+
+ while ( 1 ) {
+
+ /* Return immediately if we have no children */
+ if ( list_empty ( &async->children ) ) {
+ DBGC ( async, "ASYNC %p has no more children\n",
+ async );
+ *rc = -ECHILD;
+ return -1;
+ }
+
+ /* Look for a completed child */
+ list_for_each_entry ( child, &async->children, siblings ) {
+ if ( child->rc == -EINPROGRESS )
+ continue;
+
+ /* Found a completed child */
+ *rc = child->rc;
+ child_aid = child->aid;
+
+ DBGC ( async, "ASYNC %p reaping child ASYNC %p (ID "
+ "%ld), exit status %d (%s)\n", async, child,
+ child_aid, child->rc, strerror ( child->rc ) );
+
+ /* Reap the child */
+ assert ( child->aop != NULL );
+ assert ( list_empty ( &child->children ) );
+
+ /* Unlink from operations hierarchy */
+ list_del ( &child->siblings );
+ child->parent = NULL;
+
+ /* Release all resources */
+ if ( child->aop->reap ) {
+ child->aop->reap ( child );
+ } else {
+ async_reap_default ( child );
+ }
+
+ return child_aid;
+ }
+
+ /* Return immediately if non-blocking */
+ if ( ! block ) {
+ *rc = -EINPROGRESS;
+ return -1;
+ }
+
+ /* Allow processes to run */
+ step();
+ }
+}
+
+/**
+ * Default asynchronous operations
+ *
+ * The default is to ignore SIGCHLD (i.e. to automatically reap
+ * children) and to use the default handler (i.e. do nothing) for all
+ * other signals.
+ */
+struct async_operations default_async_operations = {
+ .signal = {
+ [SIGCHLD] = SIG_IGN,
+ },
+};
+
+/**
+ * Default asynchronous operations for orphan asynchronous operations
+ *
+ * The default for orphan asynchronous operations is to do nothing for
+ * SIGCHLD (i.e. to not automatically reap children), on the
+ * assumption that you're probably creating the orphan solely in order
+ * to async_wait() on it.
+ */
+struct async_operations orphan_async_operations = {
+ .signal = {
+ [SIGCHLD] = SIG_DFL,
+ },
+};
diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c
index 9679fc1bd..6658827cf 100644
--- a/src/drivers/ata/aoedev.c
+++ b/src/drivers/ata/aoedev.c
@@ -36,9 +36,14 @@ static int aoe_command ( struct ata_device *ata,
struct ata_command *command ) {
struct aoe_device *aoedev
= container_of ( ata, struct aoe_device, ata );
+ struct async async;
+ int rc;
- aoe_issue ( &aoedev->aoe, command );
- return async_wait ( &aoedev->aoe.aop );
+ async_init_orphan ( &async );
+ if ( ( rc = aoe_issue ( &aoedev->aoe, command, &async ) ) != 0 )
+ return rc;
+ async_wait ( &async, &rc, 1 );
+ return rc;
}
/**
diff --git a/src/drivers/scsi/iscsidev.c b/src/drivers/scsi/iscsidev.c
index b92d4bc9b..75b857e71 100644
--- a/src/drivers/scsi/iscsidev.c
+++ b/src/drivers/scsi/iscsidev.c
@@ -36,8 +36,14 @@ static int iscsi_command ( struct scsi_device *scsi,
struct scsi_command *command ) {
struct iscsi_device *iscsidev
= container_of ( scsi, struct iscsi_device, scsi );
+ struct async async;
+ int rc;
- return async_wait ( iscsi_issue ( &iscsidev->iscsi, command ) );
+ async_init_orphan ( &async );
+ if ( ( rc = iscsi_issue ( &iscsidev->iscsi, command, &async ) ) != 0 )
+ return rc;
+ async_wait ( &async, &rc, 1 );
+ return rc;
}
/**
diff --git a/src/include/errno.h b/src/include/errno.h
index dc4e4c195..3025fa965 100644
--- a/src/include/errno.h
+++ b/src/include/errno.h
@@ -125,6 +125,7 @@
#define EBUSY 0xd4 /**< Device or resource busy */
/** Operation cancelled */
#define ECANCELED PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE
+#define ECHILD ENOENT /**< No child processes */
#define ECONNABORTED 0xd5 /**< Software caused connection abort */
#define ECONNREFUSED 0xd6 /**< Connection refused */
#define ECONNRESET 0xd7 /**< Connection reset by peer */
@@ -157,10 +158,10 @@
#define ENOSYS 0xee /**< Function not implemented */
#define ENOTCONN 0xef /**< Transport endpoint is not connected */
#define ENOTSOCK 0xf0 /**< Socket operation on non-socket */
-#define EOPNOTSUPP 0xf1 /**< Operation not supported */
-#define ENOTSUP EOPNOTSUPP /**< Not supported */
+#define ENOTSUP 0xf1 /**< Not supported */
#define ENOTTY 0xf2 /**< Inappropriate ioctl for device */
#define ENXIO ENODEV /**< No such device or address */
+#define EOPNOTSUPP ENOTSUP /**< Operation not supported */
#define EOVERFLOW 0xf3 /**< Result too large */
#define EPERM EACCES /**< Operation not permitted */
#define EPROTO 0xf4 /**< Protocol error */
diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h
index 8b3e0ca48..3c54b6bb1 100644
--- a/src/include/gpxe/aoe.h
+++ b/src/include/gpxe/aoe.h
@@ -107,7 +107,7 @@ struct aoe_session {
/** Byte offset within command's data buffer */
unsigned int command_offset;
/** Asynchronous operation for this command */
- struct async_operation aop;
+ struct async async;
/** Retransmission timer */
struct retry_timer timer;
@@ -121,8 +121,9 @@ struct aoe_session {
extern void aoe_open ( struct aoe_session *aoe );
extern void aoe_close ( struct aoe_session *aoe );
-extern struct async_operation * aoe_issue ( struct aoe_session *aoe,
- struct ata_command *command );
+extern int aoe_issue ( struct aoe_session *aoe,
+ struct ata_command *command,
+ struct async *parent );
/** An AoE device */
struct aoe_device {
diff --git a/src/include/gpxe/async.h b/src/include/gpxe/async.h
index 8a6819786..d3b075b92 100644
--- a/src/include/gpxe/async.h
+++ b/src/include/gpxe/async.h
@@ -7,56 +7,164 @@
*
*/
-#include <errno.h>
-#include <assert.h>
+#include <gpxe/list.h>
-/** An asynchronous operation */
-struct async_operation {
- /** Operation status
- *
- * This is an error code as defined in errno.h, plus an offset
- * of EINPROGRESS. This means that a status value of 0
- * corresponds to a return status code of -EINPROGRESS,
- * i.e. that the default state of an asynchronous operation is
- * "not yet completed".
- */
- int status;
-};
+struct async;
-/**
- * Set asynchronous operation status
+/** An asynchronous operation ID
*
- * @v aop Asynchronous operation
- * @v rc Return status code
+ * Only positive identifiers are valid; negative values are used to
+ * indicate errors.
*/
-static inline __attribute__ (( always_inline )) void
-async_set_status ( struct async_operation *aop, int rc ) {
- aop->status = ( rc + EINPROGRESS );
-}
+typedef long aid_t;
+
+/** Signals that can be delivered to asynchronous operations */
+enum signal {
+ /** A child asynchronous operation has completed
+ *
+ * The parent should call async_wait() to reap the completed
+ * child. async_wait() will return the exit status and
+ * operation identifier of the child.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then the children will accumulate as zombies until
+ * async_wait() is called.
+ *
+ * The handler for this signal can also be set to @c SIG_IGN;
+ * if it is, then the children will automatically be reaped.
+ * Note that if you use @c SIG_IGN then you will not be able
+ * to retrieve the return status of the children; the call to
+ * async_wait() will simply return -ECHILD.
+ */
+ SIGCHLD = 0,
+ /** Cancel asynchronous operation
+ *
+ * This signal should trigger the asynchronous operation to
+ * cancel itself (including killing all its own children, if
+ * any), and then call async_done(). The asynchronous
+ * operation is allowed to not complete immediately.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then attempts to cancel the asynchronous operation will
+ * fail and the operation will complete normally. Anything
+ * waiting for the operation to cancel will block.
+ */
+ SIGKILL,
+ /** Update progress of asynchronous operation
+ *
+ * This signal should cause the asynchronous operation to
+ * immediately update the @c completed and @c total fields.
+ *
+ * The handler for this signal can be set to @c NULL; if it
+ * is, then the asynchronous operation is expected to keep its
+ * @c completed and @c total fields up to date at all times.
+ */
+ SIGUPDATE,
+ SIGMAX
+};
/**
- * Get asynchronous operation status
+ * A signal handler
*
- * @v aop Asynchronous operation
- * @ret rc Return status code
+ * @v async Asynchronous operation
+ * @v signal Signal received
*/
-static inline __attribute__ (( always_inline )) int
-async_status ( struct async_operation *aop ) {
- return ( aop->status - EINPROGRESS );
-}
+typedef void ( * signal_handler_t ) ( struct async *async,
+ enum signal signal );
+
+/** Asynchronous operation operations */
+struct async_operations {
+ /** Reap asynchronous operation
+ *
+ * @v async Asynchronous operation
+ *
+ * Release all resources associated with the asynchronous
+ * operation. This will be called only after the asynchronous
+ * operation itself calls async_done(), so the only remaining
+ * resources will probably be the memory used by the struct
+ * async itself.
+ *
+ * This method can be set to @c NULL; if it is, then no
+ * resources will be freed. This may be suitable for
+ * asynchronous operations that consume no dynamically
+ * allocated memory.
+ */
+ void ( * reap ) ( struct async *async );
+ /** Handle signals */
+ signal_handler_t signal[SIGMAX];
+};
+
+/** An asynchronous operation */
+struct async {
+ /** Other asynchronous operations with the same parent */
+ struct list_head siblings;
+ /** Child asynchronous operations */
+ struct list_head children;
+ /** Parent asynchronous operation
+ *
+ * This field is optional; if left to NULL then the owner must
+ * never call async_done().
+ */
+ struct async *parent;
+ /** Asynchronous operation ID */
+ aid_t aid;
+ /** Final return status code */
+ int rc;
+
+ /** Amount of operation completed so far
+ *
+ * The units for this quantity are arbitrary. @c completed
+ * divded by @total should give something which approximately
+ * represents the progress through the operation. For a
+ * download operation, using byte counts would make sense.
+ *
+ * This progress indicator should also incorporate the status
+ * of any child asynchronous operations.
+ */
+ unsigned long completed;
+ /** Total operation size
+ *
+ * See @c completed. A zero value means "total size unknown"
+ * and is explcitly permitted; users should take this into
+ * account before calculating @c completed/total.
+ */
+ unsigned long total;
+
+ struct async_operations *aop;
+};
+
+extern struct async_operations default_async_operations;
+extern struct async_operations orphan_async_operations;
+
+extern aid_t async_init ( struct async *async, struct async_operations *aop,
+ struct async *parent );
+extern void async_ignore_signal ( struct async *async, enum signal signal );
+extern void async_signal ( struct async *async, enum signal signal );
+extern void async_signal_children ( struct async *async, enum signal signal );
+extern void async_done ( struct async *async, int rc );
+extern aid_t async_wait ( struct async *async, int *rc, int block );
+
+/** Default signal handler */
+#define SIG_DFL NULL
+
+/** Ignore signal */
+#define SIG_IGN async_ignore_signal
/**
- * Flag asynchronous operation as complete
+ * Initialise orphan asynchronous operation
*
- * @v aop Asynchronous operation
- * @v rc Return status code
+ * @v async Asynchronous operation
+ * @ret aid Asynchronous operation ID
+ *
+ * An orphan asynchronous operation can act as a context for child
+ * operations. However, you must not call async_done() on such an
+ * operation, since this would attempt to send a signal to its
+ * (non-existent) parent. Instead, simply free the structure (after
+ * calling async_wait() to ensure that any child operations have
+ * completed).
*/
-static inline __attribute__ (( always_inline )) void
-async_done ( struct async_operation *aop, int rc ) {
- assert ( rc != -EINPROGRESS );
- async_set_status ( aop, rc );
+static inline aid_t async_init_orphan ( struct async *async ) {
+ return async_init ( async, &orphan_async_operations, NULL );
}
-extern int async_wait ( struct async_operation *aop );
-
#endif /* _GPXE_ASYNC_H */
diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h
index 60991e7e8..a3311d1a1 100644
--- a/src/include/gpxe/dhcp.h
+++ b/src/include/gpxe/dhcp.h
@@ -466,7 +466,7 @@ struct dhcp_session {
*/
int state;
/** Asynchronous operation for this DHCP session */
- struct async_operation aop;
+ struct async async;
/** Retransmission timer */
struct retry_timer timer;
};
@@ -504,6 +504,6 @@ extern int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
struct dhcp_packet *dhcppkt );
extern int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
struct dhcp_option_block *options );
-extern struct async_operation * start_dhcp ( struct dhcp_session *dhcp );
+extern int start_dhcp ( struct dhcp_session *dhcp, struct async *parent );
#endif /* _GPXE_DHCP_H */
diff --git a/src/include/gpxe/ftp.h b/src/include/gpxe/ftp.h
index 2c51036ae..06799d248 100644
--- a/src/include/gpxe/ftp.h
+++ b/src/include/gpxe/ftp.h
@@ -64,7 +64,7 @@ struct ftp_request {
struct tcp_application tcp_data;
/** Asynchronous operation for this FTP operation */
- struct async_operation aop;
+ struct async async;
};
struct async_operation * ftp_get ( struct ftp_request *ftp );
diff --git a/src/include/gpxe/hello.h b/src/include/gpxe/hello.h
index 18d0b3781..31e035cd7 100644
--- a/src/include/gpxe/hello.h
+++ b/src/include/gpxe/hello.h
@@ -44,7 +44,7 @@ struct hello_request {
struct tcp_application tcp;
/** Asynchronous operation */
- struct async_operation aop;
+ struct async async;
};
extern struct async_operation * say_hello ( struct hello_request *hello );
diff --git a/src/include/gpxe/http.h b/src/include/gpxe/http.h
index ca0afb942..49922e502 100644
--- a/src/include/gpxe/http.h
+++ b/src/include/gpxe/http.h
@@ -11,6 +11,7 @@
#include <gpxe/tcp.h>
#include <gpxe/async.h>
#include <gpxe/linebuf.h>
+#include <gpxe/uri.h>
/** HTTP default port */
#define HTTP_PORT 80
@@ -28,33 +29,29 @@ enum http_rx_state {
*
*/
struct http_request {
- /** Server address */
- struct sockaddr_tcpip server;
- /** Server host name */
- const char *hostname;
- /** Filename */
- const char *filename;
+ /** URI being fetched */
+ struct uri *uri;
/** Data buffer to fill */
struct buffer *buffer;
+ /** Asynchronous operation */
+ struct async async;
/** HTTP response code */
unsigned int response;
/** HTTP Content-Length */
size_t content_length;
+ /** TCP application for this request */
+ struct tcp_application tcp;
/** Number of bytes already sent */
size_t tx_offset;
/** RX state */
enum http_rx_state rx_state;
/** Line buffer for received header lines */
struct line_buffer linebuf;
-
- /** TCP application for this request */
- struct tcp_application tcp;
- /** Asynchronous operation */
- struct async_operation aop;
};
-extern struct async_operation * http_get ( struct http_request *http );
+extern int http_get ( struct uri *uri, struct buffer *buffer,
+ struct async *parent );
#endif /* _GPXE_HTTP_H */
diff --git a/src/include/gpxe/iscsi.h b/src/include/gpxe/iscsi.h
index bcb27376f..745606496 100644
--- a/src/include/gpxe/iscsi.h
+++ b/src/include/gpxe/iscsi.h
@@ -591,7 +591,7 @@ struct iscsi_session {
*/
struct scsi_command *command;
/** Asynchronous operation for the current iSCSI operation */
- struct async_operation aop;
+ struct async async;
/** Instant return code
*
* Set to a non-zero value if all requests should return
@@ -637,8 +637,9 @@ struct iscsi_session {
/** Maximum number of retries at connecting */
#define ISCSI_MAX_RETRIES 2
-extern struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
- struct scsi_command *command );
+extern int iscsi_issue ( struct iscsi_session *iscsi,
+ struct scsi_command *command,
+ struct async *parent );
extern void iscsi_shutdown ( struct iscsi_session *iscsi );
/** An iSCSI device */
diff --git a/src/include/gpxe/tftp.h b/src/include/gpxe/tftp.h
index 2359dcc45..551a6fc9d 100644
--- a/src/include/gpxe/tftp.h
+++ b/src/include/gpxe/tftp.h
@@ -135,7 +135,7 @@ struct tftp_session {
int state;
/** Asynchronous operation for this session */
- struct async_operation aop;
+ struct async async;
/** Retransmission timer */
struct retry_timer timer;
};
diff --git a/src/include/usr/fetch.h b/src/include/usr/fetch.h
index 372f6f8cd..85b856346 100644
--- a/src/include/usr/fetch.h
+++ b/src/include/usr/fetch.h
@@ -11,6 +11,6 @@
#include <stdint.h>
#include <gpxe/uaccess.h>
-extern int fetch ( const char *filename, userptr_t *data, size_t *len );
+extern int fetch ( const char *uri_string, userptr_t *data, size_t *len );
#endif /* _USR_FETCH_H */
diff --git a/src/net/aoe.c b/src/net/aoe.c
index 2dc5ed406..1c3cd1d2b 100644
--- a/src/net/aoe.c
+++ b/src/net/aoe.c
@@ -56,7 +56,7 @@ static void aoe_done ( struct aoe_session *aoe, int rc ) {
aoe->command = NULL;
/* Mark async operation as complete */
- async_done ( &aoe->aop, rc );
+ async_done ( &aoe->async, rc );
}
/**
@@ -309,17 +309,19 @@ void aoe_close ( struct aoe_session *aoe ) {
*
* @v aoe AoE session
* @v command ATA command
- * @ret aop Asynchronous operation
+ * @v parent Parent asynchronous operation
+ * @ret rc Return status code
*
* Only one command may be issued concurrently per session. This call
* is non-blocking; use async_wait() to wait for the command to
* complete.
*/
-struct async_operation * aoe_issue ( struct aoe_session *aoe,
- struct ata_command *command ) {
+int aoe_issue ( struct aoe_session *aoe, struct ata_command *command,
+ struct async *parent ) {
aoe->command = command;
aoe->status = 0;
aoe->command_offset = 0;
aoe_send_command ( aoe );
- return &aoe->aop;
+ async_init ( &aoe->async, &default_async_operations, parent );
+ return 0;
}
diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c
index a857f7b7b..8a27f8cb6 100644
--- a/src/net/tcp/ftp.c
+++ b/src/net/tcp/ftp.c
@@ -83,7 +83,7 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) {
tcp_close ( &ftp->tcp_data );
/* Mark asynchronous operation as complete */
- async_done ( &ftp->aop, rc );
+ async_done ( &ftp->async, rc );
}
/**
@@ -379,5 +379,5 @@ struct async_operation * ftp_get ( struct ftp_request *ftp ) {
if ( ( rc = tcp_connect ( &ftp->tcp, &ftp->server, 0 ) ) != 0 )
ftp_done ( ftp, rc );
- return &ftp->aop;
+ return &ftp->async;
}
diff --git a/src/net/tcp/hello.c b/src/net/tcp/hello.c
index 4de7e8729..25c079369 100644
--- a/src/net/tcp/hello.c
+++ b/src/net/tcp/hello.c
@@ -51,7 +51,7 @@ tcp_to_hello ( struct tcp_application *app ) {
static void hello_closed ( struct tcp_application *app, int status ) {
struct hello_request *hello = tcp_to_hello ( app );
- async_done ( &hello->aop, status );
+ async_done ( &hello->async, status );
}
static void hello_connected ( struct tcp_application *app ) {
@@ -116,7 +116,7 @@ struct async_operation * say_hello ( struct hello_request *hello ) {
hello->tcp.tcp_op = &hello_tcp_operations;
if ( ( rc = tcp_connect ( &hello->tcp, &hello->server, 0 ) ) != 0 )
- async_done ( &hello->aop, rc );
+ async_done ( &hello->async, rc );
- return &hello->aop;
+ return &hello->async;
}
diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c
index 22e24082e..dfa39f537 100644
--- a/src/net/tcp/http.c
+++ b/src/net/tcp/http.c
@@ -27,9 +27,11 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include <vsprintf.h>
+#include <errno.h>
#include <assert.h>
+#include <vsprintf.h>
#include <gpxe/async.h>
+#include <gpxe/uri.h>
#include <gpxe/buffer.h>
#include <gpxe/http.h>
@@ -67,7 +69,7 @@ static void http_done ( struct http_request *http, int rc ) {
}
/* Mark async operation as complete */
- async_done ( &http->aop, rc );
+ async_done ( &http->async, rc );
}
/**
@@ -303,12 +305,21 @@ static void http_newdata ( struct tcp_application *app,
static void http_senddata ( struct tcp_application *app,
void *buf, size_t len ) {
struct http_request *http = tcp_to_http ( app );
+ const char *path = http->uri->path;
+ const char *host = http->uri->host;
+
+ if ( ! path )
+ path = "/";
+
+ if ( ! host )
+ host = "";
len = snprintf ( buf, len,
- "GET /%s HTTP/1.1\r\n"
+ "GET %s HTTP/1.1\r\n"
"User-Agent: gPXE/" VERSION "\r\n"
"Host: %s\r\n"
- "\r\n", http->filename, http->hostname );
+ "\r\n", path, host );
+
tcp_send ( app, ( buf + http->tx_offset ), ( len - http->tx_offset ) );
}
@@ -347,16 +358,71 @@ static struct tcp_operations http_tcp_operations = {
};
/**
+ * Reap asynchronous operation
+ *
+ * @v async Asynchronous operation
+ */
+static void http_reap ( struct async *async ) {
+ struct http_request *http =
+ container_of ( async, struct http_request, async );
+
+ free_uri ( http->uri );
+ free ( http );
+}
+
+/** HTTP asynchronous operations */
+static struct async_operations http_async_operations = {
+ .reap = http_reap,
+};
+
+#warning "Quick name resolution hack"
+#include <byteswap.h>
+
+/**
* Initiate a HTTP connection
*
- * @v http a HTTP request
+ * @v uri Uniform Resource Identifier
+ * @v buffer Buffer into which to download file
+ * @v parent Parent asynchronous operation
+ * @ret rc Return status code
+ *
+ * If it returns success, this function takes ownership of the URI.
*/
-struct async_operation * http_get ( struct http_request *http ) {
+int http_get ( struct uri *uri, struct buffer *buffer, struct async *parent ) {
+ struct http_request *http;
int rc;
+ /* Allocate and populate HTTP structure */
+ http = malloc ( sizeof ( *http ) );
+ if ( ! http ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset ( http, 0, sizeof ( *http ) );
+ http->uri = uri;
+ http->buffer = buffer;
http->tcp.tcp_op = &http_tcp_operations;
- if ( ( rc = tcp_connect ( &http->tcp, &http->server, 0 ) ) != 0 )
- async_done ( &http->aop, rc );
- return &http->aop;
+#warning "Quick name resolution hack"
+ union {
+ struct sockaddr_tcpip st;
+ struct sockaddr_in sin;
+ } server;
+ server.sin.sin_port = htons ( HTTP_PORT );
+ server.sin.sin_family = AF_INET;
+ if ( inet_aton ( uri->host, &server.sin.sin_addr ) == 0 ) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if ( ( rc = tcp_connect ( &http->tcp, &server.st, 0 ) ) != 0 )
+ goto err;
+
+ async_init ( &http->async, &http_async_operations, parent );
+ return 0;
+
+ err:
+ DBGC ( http, "HTTP %p could not create request: %s\n",
+ http, strerror ( rc ) );
+ return rc;
}
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c
index ae23cfe8e..09edf697d 100644
--- a/src/net/tcp/iscsi.c
+++ b/src/net/tcp/iscsi.c
@@ -121,7 +121,7 @@ static void iscsi_done ( struct iscsi_session *iscsi, int rc ) {
iscsi->command = NULL;
/* Mark asynchronous operation as complete */
- async_done ( &iscsi->aop, rc );
+ async_done ( &iscsi->async, rc );
}
/****************************************************************************
@@ -1208,10 +1208,11 @@ static struct tcp_operations iscsi_tcp_operations = {
*
* @v iscsi iSCSI session
* @v command SCSI command
- * @ret aop Asynchronous operation for this SCSI command
+ * @v parent Parent asynchronous operation
+ * @ret rc Return status code
*/
-struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
- struct scsi_command *command ) {
+int iscsi_issue ( struct iscsi_session *iscsi, struct scsi_command *command,
+ struct async *parent ) {
int rc;
assert ( iscsi->command == NULL );
@@ -1219,7 +1220,7 @@ struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
if ( iscsi->instant_rc ) {
/* Abort immediately rather than retrying */
- iscsi_done ( iscsi, iscsi->instant_rc );
+ return iscsi->instant_rc;
} else if ( iscsi->status ) {
/* Session already open: issue command */
iscsi_start_command ( iscsi );
@@ -1231,11 +1232,12 @@ struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
0 ) ) != 0 ) {
DBGC ( iscsi, "iSCSI %p could not open TCP "
"connection: %s\n", iscsi, strerror ( rc ) );
- iscsi_done ( iscsi, rc );
+ return rc;
}
}
- return &iscsi->aop;
+ async_init ( &iscsi->async, &default_async_operations, parent );
+ return 0;
}
/**
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 8f8297018..a2d3db6bb 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -517,7 +517,7 @@ static void dhcp_done ( struct dhcp_session *dhcp, int rc ) {
ref_del ( &dhcp->netdev_ref );
/* Mark async operation as complete */
- async_done ( &dhcp->aop, rc );
+ async_done ( &dhcp->async, rc );
}
/** Address for transmitting DHCP requests */
@@ -713,14 +713,15 @@ static void dhcp_forget_netdev ( struct reference *ref ) {
* Initiate DHCP on a network interface
*
* @v dhcp DHCP session
- * @ret aop Asynchronous operation
+ * @v parent Parent asynchronous operation
+ * @ret rc Return status code
*
* If the DHCP operation completes successfully, the
* dhcp_session::options field will be filled in with the resulting
* options block. The caller takes responsibility for eventually
* calling free_dhcp_options().
*/
-struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
+int start_dhcp ( struct dhcp_session *dhcp, struct async *parent ) {
int rc;
/* Initialise DHCP session */
@@ -729,10 +730,8 @@ struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
dhcp->state = DHCPDISCOVER;
/* Bind to local port */
- if ( ( rc = udp_open ( &dhcp->udp, htons ( BOOTPC_PORT ) ) ) != 0 ) {
- async_done ( &dhcp->aop, rc );
- goto out;
- }
+ if ( ( rc = udp_open ( &dhcp->udp, htons ( BOOTPC_PORT ) ) ) != 0 )
+ return rc;
/* Add persistent reference to net device */
dhcp->netdev_ref.forget = dhcp_forget_netdev;
@@ -741,6 +740,6 @@ struct async_operation * start_dhcp ( struct dhcp_session *dhcp ) {
/* Proof of concept: just send a single DHCPDISCOVER */
dhcp_send_request ( dhcp );
- out:
- return &dhcp->aop;
+ async_init ( &dhcp->async, &default_async_operations, parent );
+ return 0;
}
diff --git a/src/net/udp/tftp.c b/src/net/udp/tftp.c
index 6753e8380..69650ab0f 100644
--- a/src/net/udp/tftp.c
+++ b/src/net/udp/tftp.c
@@ -142,7 +142,7 @@ static void tftp_done ( struct tftp_session *tftp, int rc ) {
udp_close ( &tftp->udp );
/* Mark async operation as complete */
- async_done ( &tftp->aop, rc );
+ async_done ( &tftp->async, rc );
}
/**
@@ -477,7 +477,7 @@ struct async_operation * tftp_get ( struct tftp_session *tftp ) {
/* Open UDP connection */
if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) {
- async_done ( &tftp->aop, rc );
+ async_done ( &tftp->async, rc );
goto out;
}
@@ -485,5 +485,5 @@ struct async_operation * tftp_get ( struct tftp_session *tftp ) {
tftp_send_packet ( tftp );
out:
- return &tftp->aop;
+ return &tftp->async;
}
diff --git a/src/tests/aoeboot.c b/src/tests/aoeboot.c
index 9d3013b24..0fa9ba822 100644
--- a/src/tests/aoeboot.c
+++ b/src/tests/aoeboot.c
@@ -1,5 +1,6 @@
#include <stdint.h>
#include <stdlib.h>
+#include <errno.h>
#include <vsprintf.h>
#include <console.h>
#include <gpxe/netdevice.h>
diff --git a/src/tests/dhcptest.c b/src/tests/dhcptest.c
index 5d6f52dc2..f35a34786 100644
--- a/src/tests/dhcptest.c
+++ b/src/tests/dhcptest.c
@@ -1,5 +1,6 @@
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#include <vsprintf.h>
#include <byteswap.h>
#include <gpxe/ip.h>
@@ -7,6 +8,8 @@
#include <gpxe/iscsi.h>
#include <gpxe/netdevice.h>
+#if 0
+
static int test_dhcp_aoe_boot ( struct net_device *netdev,
char *aoename ) {
unsigned int drivenum;
@@ -263,3 +266,5 @@ int test_dhcp ( struct net_device *netdev ) {
out_no_del_ipv4:
return rc;
}
+
+#endif
diff --git a/src/tests/ftptest.c b/src/tests/ftptest.c
deleted file mode 100644
index fe08ec522..000000000
--- a/src/tests/ftptest.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <console.h>
-#include <vsprintf.h>
-#include <gpxe/async.h>
-#include <gpxe/buffer.h>
-#include <gpxe/ftp.h>
-
-static void print_ftp_response ( char *data, size_t len ) {
- unsigned int i;
- char c;
-
- for ( i = 0 ; i < len ; i++ ) {
- c = data[i];
- if ( c == '\r' ) {
- /* Print nothing */
- } else if ( ( c == '\n' ) || ( c >= 32 ) || ( c <= 126 ) ) {
- putchar ( c );
- } else {
- putchar ( '.' );
- }
- }
-}
-
-void test_ftp ( struct sockaddr_tcpip *server, const char *filename ) {
- char data[256];
- struct buffer buffer;
- struct ftp_request ftp;
- int rc;
-
- printf ( "FTP fetching %s\n", filename );
-
- memset ( &buffer, 0, sizeof ( buffer ) );
- buffer.addr = virt_to_user ( data );
- buffer.len = sizeof ( data );
-
- memset ( &ftp, 0, sizeof ( ftp ) );
- memcpy ( &ftp.server, server, sizeof ( ftp.server ) );
- ftp.filename = filename;
- ftp.buffer = &buffer;
-
- rc = async_wait ( ftp_get ( &ftp ) );
- if ( rc ) {
- printf ( "FTP fetch failed\n" );
- return;
- }
-
- printf ( "FTP received %d bytes\n", buffer.fill );
-
- print_ftp_response ( data, buffer.fill );
-}
diff --git a/src/tests/hellotest.c b/src/tests/hellotest.c
deleted file mode 100644
index a873c03e2..000000000
--- a/src/tests/hellotest.c
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <console.h>
-#include <vsprintf.h>
-#include <gpxe/async.h>
-#include <gpxe/hello.h>
-
-static void test_hello_callback ( char *data, size_t len ) {
- unsigned int i;
- char c;
-
- for ( i = 0 ; i < len ; i++ ) {
- c = data[i];
- if ( c == '\r' ) {
- /* Print nothing */
- } else if ( ( c == '\n' ) || ( c >= 32 ) || ( c <= 126 ) ) {
- putchar ( c );
- } else {
- putchar ( '.' );
- }
- }
-}
-
-void test_hello ( struct sockaddr_tcpip *server, const char *message ) {
- /* Quick and dirty hack */
- struct sockaddr_in *sin = ( struct sockaddr_in * ) server;
- struct hello_request hello;
- int rc;
-
- printf ( "Saying \"%s\" to %s:%d\n", message,
- inet_ntoa ( sin->sin_addr ), ntohs ( sin->sin_port ) );
-
- memset ( &hello, 0, sizeof ( hello ) );
- memcpy ( &hello.server, server, sizeof ( hello.server ) );
- hello.message = message;
- hello.callback = test_hello_callback;
-
- rc = async_wait ( say_hello ( &hello ) );
- if ( rc ) {
- printf ( "HELLO fetch failed\n" );
- }
-}
diff --git a/src/usr/dhcpmgmt.c b/src/usr/dhcpmgmt.c
index 6042f8b2c..0e36d5557 100644
--- a/src/usr/dhcpmgmt.c
+++ b/src/usr/dhcpmgmt.c
@@ -23,6 +23,7 @@
#include <gpxe/dhcp.h>
#include <gpxe/async.h>
#include <gpxe/netdevice.h>
+#include <usr/ifmgmt.h>
#include <usr/dhcpmgmt.h>
/** @file
@@ -43,6 +44,7 @@ int dhcp ( struct net_device *netdev ) {
struct in_addr address = { htonl ( 0 ) };
struct in_addr netmask = { htonl ( 0 ) };
struct in_addr gateway = { INADDR_NONE };
+ struct async async;
int rc;
/* Check we can open the interface first */
@@ -60,8 +62,14 @@ int dhcp ( struct net_device *netdev ) {
printf ( "DHCP (%s %s)...", netdev->name, netdev_hwaddr ( netdev ) );
memset ( &dhcp, 0, sizeof ( dhcp ) );
dhcp.netdev = netdev;
- if ( ( rc = async_wait ( start_dhcp ( &dhcp ) ) ) != 0 ) {
- printf ( "failed\n" );
+ async_init_orphan ( &async );
+ if ( ( rc = start_dhcp ( &dhcp, &async ) ) != 0 ) {
+ printf ( "could not start (%s)\n", strerror ( rc ) );
+ return rc;
+ }
+ async_wait ( &async, &rc, 1 );
+ if ( rc != 0 ) {
+ printf ( "failed (%s)\n", strerror ( rc ) );
return rc;
}
printf ( "done\n" );
diff --git a/src/usr/fetch.c b/src/usr/fetch.c
index ce0fea954..9e3c2de6f 100644
--- a/src/usr/fetch.c
+++ b/src/usr/fetch.c
@@ -23,10 +23,12 @@
*
*/
+#include <errno.h>
#include <vsprintf.h>
#include <gpxe/emalloc.h>
#include <gpxe/ebuffer.h>
#include <gpxe/image.h>
+#include <gpxe/uri.h>
#include <usr/fetch.h>
#include <byteswap.h>
@@ -46,32 +48,30 @@
* caller is responsible for eventually freeing the buffer with
* efree().
*/
-int fetch ( const char *filename, userptr_t *data, size_t *len ) {
+int fetch ( const char *uri_string, userptr_t *data, size_t *len ) {
+ struct uri *uri;
struct buffer buffer;
int rc;
+ /* Parse the URI */
+ uri = parse_uri ( uri_string );
+ if ( ! uri ) {
+ rc = -ENOMEM;
+ goto err_parse_uri;
+ }
+
/* Allocate an expandable buffer to hold the file */
- if ( ( rc = ebuffer_alloc ( &buffer, 0 ) ) != 0 )
- return rc;
+ if ( ( rc = ebuffer_alloc ( &buffer, 0 ) ) != 0 ) {
+ goto err_ebuffer_alloc;
+ }
#warning "Temporary pseudo-URL parsing code"
/* Retrieve the file */
- union {
- struct sockaddr_tcpip st;
- struct sockaddr_in sin;
- } server;
- struct tftp_session tftp;
- struct http_request http;
- struct async_operation *aop;
-
- memset ( &tftp, 0, sizeof ( tftp ) );
- memset ( &http, 0, sizeof ( http ) );
- memset ( &server, 0, sizeof ( server ) );
- server.sin.sin_family = AF_INET;
- find_global_dhcp_ipv4_option ( DHCP_EB_SIADDR,
- &server.sin.sin_addr );
+ struct async async;
+ int ( * download ) ( struct uri *uri, struct buffer *buffer,
+ struct async *parent );
#if 0
server.sin.sin_port = htons ( TFTP_PORT );
@@ -80,22 +80,31 @@ int fetch ( const char *filename, userptr_t *data, size_t *len ) {
tftp.buffer = &buffer;
aop = tftp_get ( &tftp );
#else
- server.sin.sin_port = htons ( HTTP_PORT );
- memcpy ( &http.server, &server, sizeof ( http.server ) );
- http.hostname = inet_ntoa ( server.sin.sin_addr );
- http.filename = filename;
- http.buffer = &buffer;
- aop = http_get ( &http );
+ download = http_get;
#endif
- if ( ( rc = async_wait ( aop ) ) != 0 ) {
- efree ( buffer.addr );
- return rc;
- }
+ async_init_orphan ( &async );
+ if ( ( rc = download ( uri, &buffer, &async ) ) != 0 )
+ goto err;
+ uri = NULL;
+ async_wait ( &async, &rc, 1 );
+ if ( rc != 0 )
+ goto err;
/* Fill in buffer address and length */
*data = buffer.addr;
*len = buffer.fill;
+ /* Release temporary resources. The ebuffer storage is now
+ * owned by our caller, so we don't free it.
+ */
+
return 0;
+
+ err:
+ efree ( buffer.addr );
+ err_ebuffer_alloc:
+ free_uri ( uri );
+ err_parse_uri:
+ return rc;
}