* 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,
+ },
+};
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;
}
/**
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;
}
/**
#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 */
/** 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;
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 {
*
*/
-#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 */
*/
int state;
/** Asynchronous operation for this DHCP session */
- struct async_operation aop;
+ struct async async;
/** Retransmission timer */
struct retry_timer timer;
};
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 */
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 );
struct tcp_application tcp;
/** Asynchronous operation */
- struct async_operation aop;
+ struct async async;
};
extern struct async_operation * say_hello ( struct hello_request *hello );
#include <gpxe/tcp.h>
#include <gpxe/async.h>
#include <gpxe/linebuf.h>
+#include <gpxe/uri.h>
/** HTTP default port */
#define HTTP_PORT 80
*
*/
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 */
*/
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
/** 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 */
int state;
/** Asynchronous operation for this session */
- struct async_operation aop;
+ struct async async;
/** Retransmission timer */
struct retry_timer timer;
};
#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 */
aoe->command = NULL;
/* Mark async operation as complete */
- async_done ( &aoe->aop, rc );
+ async_done ( &aoe->async, rc );
}
/**
*
* @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;
}
tcp_close ( &ftp->tcp_data );
/* Mark asynchronous operation as complete */
- async_done ( &ftp->aop, rc );
+ async_done ( &ftp->async, rc );
}
/**
if ( ( rc = tcp_connect ( &ftp->tcp, &ftp->server, 0 ) ) != 0 )
ftp_done ( ftp, rc );
- return &ftp->aop;
+ return &ftp->async;
}
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 ) {
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;
}
#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>
}
/* Mark async operation as complete */
- async_done ( &http->aop, rc );
+ async_done ( &http->async, rc );
}
/**
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 ) );
}
.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;
}
iscsi->command = NULL;
/* Mark asynchronous operation as complete */
- async_done ( &iscsi->aop, rc );
+ async_done ( &iscsi->async, rc );
}
/****************************************************************************
*
* @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 );
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 );
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;
}
/**
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 */
* 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 */
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;
/* 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;
}
udp_close ( &tftp->udp );
/* Mark async operation as complete */
- async_done ( &tftp->aop, rc );
+ async_done ( &tftp->async, rc );
}
/**
/* Open UDP connection */
if ( ( rc = udp_open ( &tftp->udp, 0 ) ) != 0 ) {
- async_done ( &tftp->aop, rc );
+ async_done ( &tftp->async, rc );
goto out;
}
tftp_send_packet ( tftp );
out:
- return &tftp->aop;
+ return &tftp->async;
}
#include <stdint.h>
#include <stdlib.h>
+#include <errno.h>
#include <vsprintf.h>
#include <console.h>
#include <gpxe/netdevice.h>
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#include <vsprintf.h>
#include <byteswap.h>
#include <gpxe/ip.h>
#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;
out_no_del_ipv4:
return rc;
}
+
+#endif
+++ /dev/null
-#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 );
-}
+++ /dev/null
-#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" );
- }
-}
#include <gpxe/dhcp.h>
#include <gpxe/async.h>
#include <gpxe/netdevice.h>
+#include <usr/ifmgmt.h>
#include <usr/dhcpmgmt.h>
/** @file
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 */
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" );
*
*/
+#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 );
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;
}