Browse Source

make server connection a cancellable operation

this entails splitting drv->open_store() into alloc_store() and
connect_store().
wip/server-refactor
Oswald Buddenhagen 10 years ago
parent
commit
9d22641b62
  1. 21
      src/driver.h
  2. 102
      src/drv_imap.c
  3. 42
      src/drv_maildir.c
  4. 96
      src/main.c
  5. 8
      src/socket.c

21
src/driver.h

@ -121,8 +121,10 @@ typedef struct {
#define DRV_MSG_BAD 1 #define DRV_MSG_BAD 1
/* Something is wrong with the current mailbox - probably it is somehow inaccessible. */ /* Something is wrong with the current mailbox - probably it is somehow inaccessible. */
#define DRV_BOX_BAD 2 #define DRV_BOX_BAD 2
/* Failed to connect store. */
#define DRV_STORE_BAD 3
/* The command has been cancel()ed or cancel_store()d. */ /* The command has been cancel()ed or cancel_store()d. */
#define DRV_CANCELED 3 #define DRV_CANCELED 4
/* All memory belongs to the driver's user, unless stated otherwise. */ /* All memory belongs to the driver's user, unless stated otherwise. */
@ -146,16 +148,19 @@ struct driver {
/* Parse configuration. */ /* Parse configuration. */
int (*parse_store)( conffile_t *cfg, store_conf_t **storep ); int (*parse_store)( conffile_t *cfg, store_conf_t **storep );
/* Close remaining server connections. All stores must be disowned first. */ /* Close remaining server connections. All stores must be discarded first. */
void (*cleanup)( void ); void (*cleanup)( void );
/* Open a store with the given configuration. This may recycle existing /* Allocate a store with the given configuration. This is expected to
* server connections. Upon failure, a null store is passed to the callback. */ * return quickly, and must not fail. */
void (*open_store)( store_conf_t *conf, const char *label, store_t *(*alloc_store)( store_conf_t *conf, const char *label );
void (*cb)( store_t *ctx, void *aux ), void *aux );
/* Mark the store as available for recycling. Server connection may be kept alive. */ /* Open/connect the store. This may recycle existing server connections. */
void (*disown_store)( store_t *ctx ); void (*connect_store)( store_t *ctx,
void (*cb)( int sts, void *aux ), void *aux );
/* Discard the store. Underlying server connection may be kept alive. */
void (*free_store)( store_t *ctx );
/* Discard the store after a bad_callback. The server connections will be closed. /* Discard the store after a bad_callback. The server connections will be closed.
* Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */ * Pending commands will have their callbacks synchronously invoked with DRV_CANCELED. */

102
src/drv_imap.c

@ -100,6 +100,7 @@ typedef struct imap_store {
const char *prefix; const char *prefix;
const char *name; const char *name;
int ref_count; int ref_count;
enum { SST_BAD, SST_HALF, SST_GOOD } state;
/* trash folder's existence is not confirmed yet */ /* trash folder's existence is not confirmed yet */
enum { TrashUnknown, TrashChecking, TrashKnown } trashnc; enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
uint got_namespace:1; uint got_namespace:1;
@ -121,7 +122,7 @@ typedef struct imap_store {
int expectEOF; /* received LOGOUT's OK or unsolicited BYE */ int expectEOF; /* received LOGOUT's OK or unsolicited BYE */
int canceling; /* imap_cancel() is in progress */ int canceling; /* imap_cancel() is in progress */
union { union {
void (*imap_open)( store_t *srv, void *aux ); void (*imap_open)( int sts, void *aux );
void (*imap_cancel)( void *aux ); void (*imap_cancel)( void *aux );
} callbacks; } callbacks;
void *callback_aux; void *callback_aux;
@ -1423,6 +1424,15 @@ get_cmd_result_p2( imap_store_t *ctx, struct imap_cmd *cmd, int response )
/******************* imap_cancel_store *******************/ /******************* imap_cancel_store *******************/
static void
imap_cleanup_store( imap_store_t *ctx )
{
free_generic_messages( ctx->gen.msgs );
free_string_list( ctx->gen.boxes );
free( ctx->delimiter );
}
static void static void
imap_cancel_store( store_t *gctx ) imap_cancel_store( store_t *gctx )
{ {
@ -1434,13 +1444,11 @@ imap_cancel_store( store_t *gctx )
socket_close( &ctx->conn ); socket_close( &ctx->conn );
cancel_sent_imap_cmds( ctx ); cancel_sent_imap_cmds( ctx );
cancel_pending_imap_cmds( ctx ); cancel_pending_imap_cmds( ctx );
free_generic_messages( ctx->gen.msgs );
free_string_list( ctx->gen.boxes );
free_list( ctx->ns_personal ); free_list( ctx->ns_personal );
free_list( ctx->ns_other ); free_list( ctx->ns_other );
free_list( ctx->ns_shared ); free_list( ctx->ns_shared );
free_string_list( ctx->auth_mechs ); free_string_list( ctx->auth_mechs );
free( ctx->delimiter ); imap_cleanup_store( ctx );
imap_deref( ctx ); imap_deref( ctx );
} }
@ -1460,7 +1468,7 @@ imap_invoke_bad_callback( imap_store_t *ctx )
ctx->gen.bad_callback( ctx->gen.bad_callback_aux ); ctx->gen.bad_callback( ctx->gen.bad_callback_aux );
} }
/******************* imap_disown_store *******************/ /******************* imap_free_store *******************/
static store_t *unowned; static store_t *unowned;
@ -1478,7 +1486,7 @@ imap_cancel_unowned( void *gctx )
} }
static void static void
imap_disown_store( store_t *gctx ) imap_free_store( store_t *gctx )
{ {
free_generic_messages( gctx->msgs ); free_generic_messages( gctx->msgs );
gctx->msgs = 0; gctx->msgs = 0;
@ -1542,62 +1550,65 @@ static void imap_open_store_ssl_bail( imap_store_t * );
#endif #endif
static void imap_open_store_bail( imap_store_t *, int ); static void imap_open_store_bail( imap_store_t *, int );
static void static store_t *
imap_open_store_bad( void *aux ) imap_alloc_store( store_conf_t *conf, const char *label )
{
imap_open_store_bail( (imap_store_t *)aux, FAIL_TEMP );
}
static void
imap_open_store( store_conf_t *conf, const char *label,
void (*cb)( store_t *srv, void *aux ), void *aux )
{ {
imap_store_conf_t *cfg = (imap_store_conf_t *)conf; imap_store_conf_t *cfg = (imap_store_conf_t *)conf;
imap_server_conf_t *srvc = cfg->server; imap_server_conf_t *srvc = cfg->server;
imap_store_t *ctx; imap_store_t *ctx;
store_t **ctxp; store_t **ctxp;
/* First try to recycle a whole store. */
for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp = &ctx->gen.next) for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp = &ctx->gen.next)
if (ctx->gen.conf == conf) { if (ctx->state == SST_GOOD && ctx->gen.conf == conf) {
*ctxp = ctx->gen.next; *ctxp = ctx->gen.next;
ctx->label = label; ctx->label = label;
cb( &ctx->gen, aux ); return &ctx->gen;
return;
} }
/* Then try to recycle a server connection. */
for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp = &ctx->gen.next) for (ctxp = &unowned; (ctx = (imap_store_t *)*ctxp); ctxp = &ctx->gen.next)
if (((imap_store_conf_t *)ctx->gen.conf)->server == srvc) { if (ctx->state != SST_BAD && ((imap_store_conf_t *)ctx->gen.conf)->server == srvc) {
*ctxp = ctx->gen.next; *ctxp = ctx->gen.next;
ctx->label = label; imap_cleanup_store( ctx );
/* One could ping the server here, but given that the idle timeout /* One could ping the server here, but given that the idle timeout
* is at least 30 minutes, this sounds pretty pointless. */ * is at least 30 minutes, this sounds pretty pointless. */
free_string_list( ctx->gen.boxes ); ctx->state = SST_HALF;
ctx->gen.boxes = 0; goto gotsrv;
ctx->gen.listed = 0;
ctx->gen.conf = conf;
free( ctx->delimiter );
ctx->delimiter = 0;
ctx->callbacks.imap_open = cb;
ctx->callback_aux = aux;
set_bad_callback( &ctx->gen, imap_open_store_bad, ctx );
imap_open_store_namespace( ctx );
return;
} }
/* Finally, schedule opening a new server connection. */
ctx = nfcalloc( sizeof(*ctx) ); ctx = nfcalloc( sizeof(*ctx) );
socket_init( &ctx->conn, &srvc->sconf,
(void (*)( void * ))imap_invoke_bad_callback,
imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
ctx->in_progress_append = &ctx->in_progress;
ctx->pending_append = &ctx->pending;
gotsrv:
ctx->gen.conf = conf; ctx->gen.conf = conf;
ctx->label = label; ctx->label = label;
ctx->ref_count = 1; ctx->ref_count = 1;
return &ctx->gen;
}
static void
imap_connect_store( store_t *gctx,
void (*cb)( int sts, void *aux ), void *aux )
{
imap_store_t *ctx = (imap_store_t *)gctx;
if (ctx->state == SST_GOOD) {
cb( DRV_OK, aux );
} else {
ctx->callbacks.imap_open = cb; ctx->callbacks.imap_open = cb;
ctx->callback_aux = aux; ctx->callback_aux = aux;
set_bad_callback( &ctx->gen, imap_open_store_bad, ctx ); if (ctx->state == SST_HALF)
ctx->in_progress_append = &ctx->in_progress; imap_open_store_namespace( ctx );
ctx->pending_append = &ctx->pending; else
socket_init( &ctx->conn, &srvc->sconf,
(void (*)( void * ))imap_invoke_bad_callback,
imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
socket_connect( &ctx->conn, imap_open_store_connected ); socket_connect( &ctx->conn, imap_open_store_connected );
} }
}
static void static void
imap_open_store_connected( int ok, void *aux ) imap_open_store_connected( int ok, void *aux )
@ -2091,6 +2102,7 @@ imap_open_store_namespace( imap_store_t *ctx )
{ {
imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf; imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf;
ctx->state = SST_HALF;
ctx->prefix = cfg->gen.path; ctx->prefix = cfg->gen.path;
ctx->delimiter = cfg->delimiter ? nfstrdup( cfg->delimiter ) : 0; ctx->delimiter = cfg->delimiter ? nfstrdup( cfg->delimiter ) : 0;
if (((!ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && CAP(NAMESPACE)) { if (((!ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && CAP(NAMESPACE)) {
@ -2140,11 +2152,11 @@ imap_open_store_namespace2( imap_store_t *ctx )
static void static void
imap_open_store_finalize( imap_store_t *ctx ) imap_open_store_finalize( imap_store_t *ctx )
{ {
set_bad_callback( &ctx->gen, 0, 0 ); ctx->state = SST_GOOD;
if (!ctx->prefix) if (!ctx->prefix)
ctx->prefix = ""; ctx->prefix = "";
ctx->trashnc = TrashUnknown; ctx->trashnc = TrashUnknown;
ctx->callbacks.imap_open( &ctx->gen, ctx->callback_aux ); ctx->callbacks.imap_open( DRV_OK, ctx->callback_aux );
} }
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
@ -2160,11 +2172,8 @@ imap_open_store_ssl_bail( imap_store_t *ctx )
static void static void
imap_open_store_bail( imap_store_t *ctx, int failed ) imap_open_store_bail( imap_store_t *ctx, int failed )
{ {
void (*cb)( store_t *srv, void *aux ) = ctx->callbacks.imap_open;
void *aux = ctx->callback_aux;
((imap_store_conf_t *)ctx->gen.conf)->server->failed = failed; ((imap_store_conf_t *)ctx->gen.conf)->server->failed = failed;
imap_cancel_store( &ctx->gen ); ctx->callbacks.imap_open( DRV_STORE_BAD, ctx->callback_aux );
cb( 0, aux );
} }
/******************* imap_open_box *******************/ /******************* imap_open_box *******************/
@ -2945,8 +2954,9 @@ struct driver imap_driver = {
DRV_CRLF | DRV_VERBOSE, DRV_CRLF | DRV_VERBOSE,
imap_parse_store, imap_parse_store,
imap_cleanup, imap_cleanup,
imap_open_store, imap_alloc_store,
imap_disown_store, imap_connect_store,
imap_free_store,
imap_cancel_store, imap_cancel_store,
imap_list_store, imap_list_store,
imap_select_box, imap_select_box,

42
src/drv_maildir.c

@ -194,35 +194,40 @@ maildir_validate_path( maildir_store_conf_t *conf )
static void lcktmr_timeout( void *aux ); static void lcktmr_timeout( void *aux );
static void static store_t *
maildir_open_store( store_conf_t *gconf, const char *label ATTR_UNUSED, maildir_alloc_store( store_conf_t *gconf, const char *label ATTR_UNUSED )
void (*cb)( store_t *ctx, void *aux ), void *aux )
{ {
maildir_store_conf_t *conf = (maildir_store_conf_t *)gconf;
maildir_store_t *ctx; maildir_store_t *ctx;
ctx = nfcalloc( sizeof(*ctx) ); ctx = nfcalloc( sizeof(*ctx) );
ctx->gen.conf = gconf; ctx->gen.conf = gconf;
ctx->uvfd = -1; ctx->uvfd = -1;
init_wakeup( &ctx->lcktmr, lcktmr_timeout, ctx ); init_wakeup( &ctx->lcktmr, lcktmr_timeout, ctx );
if (gconf->path && maildir_validate_path( conf ) < 0) { return &ctx->gen;
free( ctx ); }
cb( 0, aux );
static void
maildir_connect_store( store_t *gctx,
void (*cb)( int sts, void *aux ), void *aux )
{
maildir_store_t *ctx = (maildir_store_t *)gctx;
maildir_store_conf_t *conf = (maildir_store_conf_t *)ctx->gen.conf;
if (conf->gen.path && maildir_validate_path( conf ) < 0) {
cb( DRV_STORE_BAD, aux );
return; return;
} }
if (gconf->trash) { if (conf->gen.trash) {
if (maildir_ensure_path( conf ) < 0) { if (maildir_ensure_path( conf ) < 0) {
free( ctx ); cb( DRV_STORE_BAD, aux );
cb( 0, aux );
return; return;
} }
if (!(ctx->trash = maildir_join_path( conf, gconf->path, gconf->trash ))) { if (!(ctx->trash = maildir_join_path( conf, conf->gen.path, conf->gen.trash ))) {
free( ctx ); cb( DRV_STORE_BAD, aux );
cb( 0, aux );
return; return;
} }
} }
cb( &ctx->gen, aux ); cb( DRV_OK, aux );
} }
static void static void
@ -256,7 +261,7 @@ maildir_cleanup( store_t *gctx )
} }
static void static void
maildir_disown_store( store_t *gctx ) maildir_free_store( store_t *gctx )
{ {
maildir_store_t *ctx = (maildir_store_t *)gctx; maildir_store_t *ctx = (maildir_store_t *)gctx;
@ -1761,9 +1766,10 @@ struct driver maildir_driver = {
0, /* XXX DRV_CRLF? */ 0, /* XXX DRV_CRLF? */
maildir_parse_store, maildir_parse_store,
maildir_cleanup_drv, maildir_cleanup_drv,
maildir_open_store, maildir_alloc_store,
maildir_disown_store, maildir_connect_store,
maildir_disown_store, /* _cancel_, but it's the same */ maildir_free_store,
maildir_free_store, /* _cancel_, but it's the same */
maildir_list_store, maildir_list_store,
maildir_select_box, maildir_select_box,
maildir_create_box, maildir_create_box,

96
src/main.c

@ -727,10 +727,33 @@ main( int argc, char **argv )
} }
#define ST_FRESH 0 #define ST_FRESH 0
#define ST_OPEN 1 #define ST_CONNECTED 1
#define ST_CLOSED 2 #define ST_OPEN 2
#define ST_CANCELING 3
#define ST_CLOSED 4
static void store_opened( store_t *ctx, void *aux ); static void
cancel_prep_done( void *aux )
{
MVARS(aux)
mvars->drv[t]->free_store( mvars->ctx[t] );
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
}
static void
store_bad( void *aux )
{
MVARS(aux)
mvars->drv[t]->cancel_store( mvars->ctx[t] );
mvars->state[t] = ST_CLOSED;
mvars->ret = mvars->skip = 1;
sync_chans( mvars, E_OPEN );
}
static void store_connected( int sts, void *aux );
static void store_listed( int sts, void *aux ); static void store_listed( int sts, void *aux );
static int sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox ); static int sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox );
static void done_sync_2_dyn( int sts, void *aux ); static void done_sync_2_dyn( int sts, void *aux );
@ -771,17 +794,18 @@ sync_chans( main_vars_t *mvars, int ent )
labels[M] = "M: ", labels[S] = "S: "; labels[M] = "M: ", labels[S] = "S: ";
else else
labels[M] = labels[S] = ""; labels[M] = labels[S] = "";
for (t = 0; t < 2; t++) {
mvars->drv[t] = mvars->chan->stores[t]->driver;
mvars->ctx[t] = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] );
set_bad_callback( mvars->ctx[t], store_bad, AUX );
}
for (t = 0; ; t++) { for (t = 0; ; t++) {
info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name ); info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
mvars->drv[t] = mvars->chan->stores[t]->driver; mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
mvars->drv[t]->open_store( mvars->chan->stores[t], labels[t], store_opened, AUX ); if (t || mvars->skip)
if (t)
break;
if (mvars->skip) {
mvars->state[1] = ST_CLOSED;
break; break;
} }
}
mvars->cben = 1; mvars->cben = 1;
opened: opened:
if (mvars->skip) if (mvars->skip)
@ -856,13 +880,19 @@ sync_chans( main_vars_t *mvars, int ent )
} }
next: next:
mvars->cben = 0;
for (t = 0; t < 2; t++) for (t = 0; t < 2; t++)
if (mvars->state[t] == ST_OPEN) { if (mvars->state[t] == ST_FRESH) {
mvars->drv[t]->disown_store( mvars->ctx[t] ); /* An unconnected store may be only cancelled. */
mvars->state[t] = ST_CLOSED; mvars->state[t] = ST_CLOSED;
mvars->drv[t]->cancel_store( mvars->ctx[t] );
} else if (mvars->state[t] == ST_CONNECTED || mvars->state[t] == ST_OPEN) {
mvars->state[t] = ST_CANCELING;
mvars->drv[t]->cancel_cmds( mvars->ctx[t], cancel_prep_done, AUX );
} }
mvars->cben = 1;
if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) { if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
mvars->skip = mvars->cben = 1; mvars->skip = 1;
return; return;
} }
if (mvars->chanptr->boxlist == 2) { if (mvars->chanptr->boxlist == 2) {
@ -885,31 +915,17 @@ sync_chans( main_vars_t *mvars, int ent )
} }
static void static void
store_bad( void *aux ) store_connected( int sts, void *aux )
{
MVARS(aux)
mvars->drv[t]->cancel_store( mvars->ctx[t] );
mvars->ret = mvars->skip = 1;
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
}
static void
store_opened( store_t *ctx, void *aux )
{ {
MVARS(aux) MVARS(aux)
string_list_t *cpat; string_list_t *cpat;
int cflags; int cflags;
if (!ctx) { switch (sts) {
mvars->ret = mvars->skip = 1; case DRV_CANCELED:
mvars->state[t] = ST_CLOSED;
sync_chans( mvars, E_OPEN );
return; return;
} case DRV_OK:
mvars->ctx[t] = ctx; if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns && !mvars->ctx[t]->listed) {
if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns && !ctx->listed) {
for (cflags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) { for (cflags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
const char *pat = cpat->string; const char *pat = cpat->string;
if (*pat != '!') { if (*pat != '!') {
@ -925,7 +941,7 @@ store_opened( store_t *ctx, void *aux )
flags |= LIST_INBOX; flags |= LIST_INBOX;
} else if (c == '/') { } else if (c == '/') {
/* Flattened sub-folders of INBOX actually end up in Path. */ /* Flattened sub-folders of INBOX actually end up in Path. */
if (ctx->conf->flat_delim) if (mvars->ctx[t]->conf->flat_delim)
flags |= LIST_PATH; flags |= LIST_PATH;
else else
flags |= LIST_INBOX; flags |= LIST_INBOX;
@ -945,12 +961,18 @@ store_opened( store_t *ctx, void *aux )
cflags |= flags; cflags |= flags;
} }
} }
set_bad_callback( ctx, store_bad, AUX ); mvars->state[t] = ST_CONNECTED;
mvars->drv[t]->list_store( ctx, cflags, store_listed, AUX ); mvars->drv[t]->list_store( mvars->ctx[t], cflags, store_listed, AUX );
} else { return;
}
mvars->state[t] = ST_OPEN; mvars->state[t] = ST_OPEN;
sync_chans( mvars, E_OPEN ); break;
default:
mvars->ret = mvars->skip = 1;
mvars->state[t] = ST_OPEN;
break;
} }
sync_chans( mvars, E_OPEN );
} }
static void static void

8
src/socket.c

@ -516,6 +516,7 @@ socket_connected( conn_t *conn )
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
freeaddrinfo( conn->addrs ); freeaddrinfo( conn->addrs );
conn->addrs = 0;
#endif #endif
conf_notifier( &conn->notify, 0, POLLIN ); conf_notifier( &conn->notify, 0, POLLIN );
socket_expect_read( conn, 0 ); socket_expect_read( conn, 0 );
@ -528,6 +529,7 @@ socket_connect_bail( conn_t *conn )
{ {
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
freeaddrinfo( conn->addrs ); freeaddrinfo( conn->addrs );
conn->addrs = 0;
#endif #endif
free( conn->name ); free( conn->name );
conn->name = 0; conn->name = 0;
@ -543,6 +545,12 @@ socket_close( conn_t *sock )
socket_close_internal( sock ); socket_close_internal( sock );
free( sock->name ); free( sock->name );
sock->name = 0; sock->name = 0;
#ifdef HAVE_IPV6
if (sock->addrs) {
freeaddrinfo( sock->addrs );
sock->addrs = 0;
}
#endif
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
if (sock->ssl) { if (sock->ssl) {
SSL_free( sock->ssl ); SSL_free( sock->ssl );

Loading…
Cancel
Save