david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

Gave asynchronous operations approximate POSIX signal semantics. This

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.
This commit is contained in:
Michael Brown 2007-01-15 08:49:10 +00:00
parent ec75b269d3
commit 4e20d73bb5
26 changed files with 656 additions and 248 deletions

View 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>
@ -27,26 +29,316 @@
*/
/**
* Wait for asynchronous operation to complete
* Name signal
*
* @v aop Asynchronous operation
* @ret rc Return status code
*
* Blocks until the specified asynchronous operation has completed.
* The return status is the return status of the asynchronous
* operation itself.
* @v signal Signal number
* @ret name Name of signal
*/
int async_wait ( struct async_operation *aop ) {
int rc;
/* 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 );
return rc;
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
*
* It is valid to create an asynchronous operation with no parent
* operation; see async_init_orphan().
*/
aid_t async_init ( struct async *async, struct async_operations *aop,
struct async *parent ) {
static aid_t aid = 1;
/* 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;
}
/**
* 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,
},
};

View 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;
}
/**

View 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;
}
/**

View File

@ -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 */

View 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 {

View File

@ -7,56 +7,164 @@
*
*/
#include <errno.h>
#include <assert.h>
#include <gpxe/list.h>
/** An asynchronous operation */
struct async_operation {
/** Operation status
struct async;
/** An asynchronous operation ID
*
* Only positive identifiers are valid; negative values are used to
* indicate errors.
*/
typedef long aid_t;
/** Signals that can be delivered to asynchronous operations */
enum signal {
/** A child asynchronous operation has completed
*
* 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".
* 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.
*/
int status;
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
};
/**
* Set asynchronous operation status
* A signal handler
*
* @v aop Asynchronous operation
* @v rc Return status code
* @v async Asynchronous operation
* @v signal Signal received
*/
static inline __attribute__ (( always_inline )) void
async_set_status ( struct async_operation *aop, int rc ) {
aop->status = ( rc + 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
/**
* Get asynchronous operation status
* Initialise orphan asynchronous operation
*
* @v aop Asynchronous operation
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) int
async_status ( struct async_operation *aop ) {
return ( aop->status - EINPROGRESS );
}
/**
* Flag asynchronous operation as complete
* @v async Asynchronous operation
* @ret aid Asynchronous operation ID
*
* @v aop Asynchronous operation
* @v rc Return status code
* 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 */

View 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 */

View 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 );

View 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 );

View 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 */

View 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 */

View 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;
};

View 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 */

View 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;
}

View 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;
}

View 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;
}

View File

@ -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 ) );
}
@ -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;
}

View 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;
}
/**

View 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;
}

View 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;
}

View File

@ -1,5 +1,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <vsprintf.h>
#include <console.h>
#include <gpxe/netdevice.h>

View 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

View File

@ -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 );
}

View File

@ -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" );
}
}

View 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" );

View File

@ -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;
}