Gave asynchronous operations approximate POSIX signal semantics. This
authorMichael Brown <mcb30@etherboot.org>
Mon, 15 Jan 2007 08:49:10 +0000 (08:49 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 15 Jan 2007 08:49:10 +0000 (08:49 +0000)
will enable us to cascade async operations, which is necessary in order to
properly support DNS.  (For example, an HTTP request may have to redirect
to a new location and will have to perform a new DNS lookup, so we can't
just rely on doing the name lookup at the time of parsing the initial
URL).

Anything other than HTTP is probably broken right now; I'll fix the others
up asap.

26 files changed:
src/core/async.c
src/drivers/ata/aoedev.c
src/drivers/scsi/iscsidev.c
src/include/errno.h
src/include/gpxe/aoe.h
src/include/gpxe/async.h
src/include/gpxe/dhcp.h
src/include/gpxe/ftp.h
src/include/gpxe/hello.h
src/include/gpxe/http.h
src/include/gpxe/iscsi.h
src/include/gpxe/tftp.h
src/include/usr/fetch.h
src/net/aoe.c
src/net/tcp/ftp.c
src/net/tcp/hello.c
src/net/tcp/http.c
src/net/tcp/iscsi.c
src/net/udp/dhcp.c
src/net/udp/tftp.c
src/tests/aoeboot.c
src/tests/dhcptest.c
src/tests/ftptest.c [deleted file]
src/tests/hellotest.c [deleted file]
src/usr/dhcpmgmt.c
src/usr/fetch.c

index 8bd5d7c..95bbc05 100644 (file)
@@ -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>
 
  */
 
 /**
- * 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,
+       },
+};
index 9679fc1..6658827 100644 (file)
@@ -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;
 }
 
 /**
index b92d4bc..75b857e 100644 (file)
@@ -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;
 }
 
 /**
index dc4e4c1..3025fa9 100644 (file)
 #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 */
 #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 */
index 8b3e0ca..3c54b6b 100644 (file)
@@ -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 {
index 8a68197..d3b075b 100644 (file)
  *
  */
 
-#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 */
index 60991e7..a3311d1 100644 (file)
@@ -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 */
index 2c51036..06799d2 100644 (file)
@@ -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 );
index 18d0b37..31e035c 100644 (file)
@@ -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 );
index ca0afb9..49922e5 100644 (file)
@@ -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 */
index bcb2737..7456064 100644 (file)
@@ -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 */
index 2359dcc..551a6fc 100644 (file)
@@ -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;
 };
index 372f6f8..85b8563 100644 (file)
@@ -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 */
index 2dc5ed4..1c3cd1d 100644 (file)
@@ -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;
 }
index a857f7b..8a27f8c 100644 (file)
@@ -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;
 }
index 4de7e87..25c0793 100644 (file)
@@ -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;
 }
index 22e2408..dfa39f5 100644 (file)
 #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 ) );
 }
 
@@ -346,17 +357,72 @@ static struct tcp_operations http_tcp_operations = {
        .senddata       = http_senddata,
 };
 
+/**
+ * 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;
 }
index ae23cfe..09edf69 100644 (file)
@@ -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;
 }
 
 /**
index 8f82970..a2d3db6 100644 (file)
@@ -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;
 }
index 6753e83..69650ab 100644 (file)
@@ -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;
 }
index 9d3013b..0fa9ba8 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdint.h>
 #include <stdlib.h>
+#include <errno.h>
 #include <vsprintf.h>
 #include <console.h>
 #include <gpxe/netdevice.h>
index 5d6f52d..f35a347 100644 (file)
@@ -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 (file)
index fe08ec5..0000000
+++ /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 (file)
index a873c03..0000000
+++ /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" );
-       }
-}
index 6042f8b..0e36d55 100644 (file)
@@ -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" );
index ce0fea9..9e3c2de 100644 (file)
  *
  */
 
+#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>
  * 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;
 }