Skip to content

Commit

Permalink
idle: don't lose socket after first use
Browse files Browse the repository at this point in the history
Also - clean up after self by using idle_init()/idle_done()
rather than the braindead "idle_enabled()" multipurpose
interface.
  • Loading branch information
Bron Gondwana authored and Greg Banks committed May 16, 2012
1 parent 22e294a commit c024aa3
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 96 deletions.
167 changes: 85 additions & 82 deletions imap/idle.c
Expand Up @@ -57,24 +57,30 @@
#include <string.h>
#include <errno.h>

#include "assert.h"
#include "idle.h"
#include "idlemsg.h"
#include "global.h"

const char *idle_method_desc = "no";

/* how often to poll the mailbox */
static time_t idle_period = -1;
static int idle_started = 0;

/* UNIX socket variables */
/* link to idled */
static struct sockaddr_un idle_remote;

/* track idle state */
static int idle_started;

static int idle_send_msg(int which, const char *mboxname)
{
idle_message_t msg;

/* do we have a local socket? */
if (!idle_get_sock())
return 0;

/* maybe the idled came along, so we always send anyway, because
* polled idle is too awful to contemplate */

/* fill the structure */
msg.which = which;
strncpy(msg.mboxname, mboxname ? mboxname : ".", sizeof(msg.mboxname));
Expand All @@ -97,107 +103,99 @@ void idle_notify(const char *mboxname)
/*
* Create connection to idled for sending notifications
*/
int idle_enabled(void)
void idle_init(void)
{
if (idle_period == -1) {
int s;
int fdflags;
struct stat sbuf;
struct sockaddr_un local;

/* get polling period in case we can't connect to idled
* NOTE: if used, a period of zero disables IDLE
*/
idle_period = config_getint(IMAPOPT_IMAPIDLEPOLL);
if (idle_period < 0) idle_period = 0;

if (!idle_period) return 0;

idle_method_desc = "poll";

if (!idle_make_client_address(&local) ||
!idle_init_sock(&local))
return idle_period;
s = idle_get_sock();

if (!idle_make_server_address(&idle_remote))
return idle_period;

/* check that the socket exists */
if (stat(idle_remote.sun_path, &sbuf) < 0) {
idle_done_sock();
return idle_period;
}
struct sockaddr_un local;
int fdflags;
int s;

/* put us in non-blocking mode */
fdflags = fcntl(s, F_GETFD, 0);
if (fdflags != -1) fdflags = fcntl(s, F_SETFL, O_NONBLOCK | fdflags);
if (fdflags == -1) { idle_done_sock(); return idle_period; }
if (!idle_enabled()) return;

if (!idle_send_msg(IDLE_MSG_NOOP, NULL)) {
idle_done_sock();
return idle_period;
}
assert(idle_make_client_address(&local));
assert(idle_make_server_address(&idle_remote));

/* set the mailbox update notifier */
mailbox_set_updatenotifier(idle_notify);
idle_method_desc = "poll";

idle_method_desc = "idled";
/* set the mailbox update notifier */
mailbox_set_updatenotifier(idle_notify);

return 1;
}
else if (idle_get_sock() != -1) {
/* if the idle socket is already open, we're enabled */
return 1;
}
else {
return idle_period;
if (!idle_init_sock(&local))
return;

s = idle_get_sock();

/* put us in non-blocking mode */
fdflags = fcntl(s, F_GETFD, 0);
if (fdflags != -1)
fdflags = fcntl(s, F_SETFL, O_NONBLOCK | fdflags);
if (fdflags == -1) {
idle_done_sock();
return;
}

idle_method_desc = "idled";
}

int idle_enabled(void)
{
int idle_period = config_getint(IMAPOPT_IMAPIDLEPOLL);

/* only enabled if a positive period */
return (idle_period > 0);
}

void idle_start(const char *mboxname)
{
idle_started = 1;
if (!idle_enabled()) return;

/* Tell idled that we're idling. It doesn't
* matter if it fails, we'll still poll */
idle_send_msg(IDLE_MSG_INIT, mboxname);
if (idle_send_msg(IDLE_MSG_INIT, mboxname))
idle_started = 1;
}

int idle_wait(int otherfd)
{
int s = idle_get_sock();
fd_set rfds;
int maxfd;
int maxfd = -1;
int s = -1;
struct timeval timeout;
int r;
int flags = 0;
int idle_timeout = config_getint(IMAPOPT_IMAPIDLEPOLL);

if (!idle_started)
return 0;
if (!idle_enabled()) return;

do {
FD_ZERO(&rfds);
maxfd = -1;
if (s >= 0) {
FD_SET(s, &rfds);
maxfd = MAX(maxfd, s);
}
if (otherfd >= 0) {
FD_SET(otherfd, &rfds);
maxfd = MAX(maxfd, otherfd);
}
/* we still listen on the socket, because we might get ALERTs,
* but we won't get mailbox notifications */
if (!idle_started) {
/* XXX - shorter poll interval? */
syslog(LOG_NOTICE, "IDLE: poll fallback - %d seconds", idle_timeout);
}

/* Note: it's technically valid for there to be no fds to listen
* to, in the case where @otherfd is passed as -1 and we failed
* to talk to idled. It shouldn't happen though as we're always
* called with a valid otherfd. */
FD_ZERO(&rfds);
s = idle_get_sock();
if (s >= 0) {
FD_SET(s, &rfds);
maxfd = MAX(maxfd, s);
}
if (otherfd >= 0) {
FD_SET(otherfd, &rfds);
maxfd = MAX(maxfd, otherfd);
}

/* TODO: this is wrong, we actually want a rolling period */
timeout.tv_sec = idle_period;
timeout.tv_usec = 0;
/* Note: it's technically valid for there to be no fds to listen
* to, in the case where @otherfd is passed as -1 and we failed
* to talk to idled. It shouldn't happen though as we're always
* called with a valid otherfd. */

/* maximum possible timeout before we double-check anyway */
timeout.tv_sec = idle_timeout;
timeout.tv_usec = 0;

do {
r = select(maxfd+1, &rfds, NULL, NULL, &timeout);

if (r < 0) {
if (errno == EAGAIN || errno == EINTR) {
signals_poll();
Expand Down Expand Up @@ -232,13 +230,18 @@ int idle_wait(int otherfd)
return flags;
}

void idle_done(const char *mboxname)
void idle_stop(const char *mboxname)
{
if (!idle_started) return;

/* Tell idled that we're done idling */
idle_send_msg(IDLE_MSG_DONE, mboxname);

/* close the AF_UNIX socket */
idle_done_sock();

idle_started = 0;
}

void idle_done(void)
{
/* close the local socket */
idle_done_sock();
}
11 changes: 8 additions & 3 deletions imap/idle.h
Expand Up @@ -62,8 +62,10 @@ typedef enum {

typedef void idle_updateproc_t(idle_flags_t flags);

/* set up the link to the idled for notifications */
void idle_init(void);

/* Is IDLE enabled? Can also do initial setup, if necessary */
/* Is IDLE enabled? */
int idle_enabled(void);

/* Start IDLEing on 'mailbox'. */
Expand All @@ -79,7 +81,10 @@ void idle_start(const char *mboxname);
*/
int idle_wait(int otherfd);

/* Cleanup when IDLE is completed. */
void idle_done(const char *mboxname);
/* Stop IDLEing on 'mailbox'. */
void idle_stop(const char *mboxname);

/* Clean up when IDLE is completed. */
void idle_done(void);

#endif
40 changes: 35 additions & 5 deletions imap/idlemsg.c
Expand Up @@ -84,7 +84,6 @@ int idle_make_server_address(struct sockaddr_un *mysun)
return 1;
}


int idle_make_client_address(struct sockaddr_un *mysun)
{
memset(mysun, 0, sizeof(*mysun));
Expand All @@ -109,14 +108,14 @@ const char *idle_id_from_addr(const struct sockaddr_un *mysun)
return (p ? p+1 : tail);
}



int idle_init_sock(const struct sockaddr_un *local)
{
int len;
int s;
mode_t oldumask;

assert(idle_sock == -1);

/* create socket we are going to use for listening */
if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
perror("socket");
Expand Down Expand Up @@ -148,7 +147,9 @@ void idle_done_sock(void)
if (idle_sock >= 0) {
close(idle_sock);
unlink(idle_local.sun_path);
memset(&idle_local, 0, sizeof(struct sockaddr_un));
}

idle_sock = -1;
}

Expand All @@ -169,7 +170,8 @@ int idle_send(const struct sockaddr_un *remote,
if (sendto(idle_sock, (void *) msg,
IDLE_MESSAGE_BASE_SIZE+strlen(msg->mboxname)+1, /* 1 for NULL */
0, (struct sockaddr *) remote, sizeof(*remote)) == -1) {
syslog(LOG_ERR, "error sending to idled: %lx", msg->which);
syslog(LOG_ERR, "IDLE: error sending message: %s (%s)",
idle_msg_string(msg->which), msg->mboxname);
return 0;
}

Expand All @@ -185,6 +187,8 @@ int idle_recv(struct sockaddr_un *remote, idle_message_t *msg)
return 0;

memset(remote, 0, remote_len);
memset(msg, 0, sizeof(idle_message_t));

n = recvfrom(idle_sock, (void *) msg, sizeof(idle_message_t), 0,
(struct sockaddr *) remote, &remote_len);

Expand All @@ -193,10 +197,36 @@ int idle_recv(struct sockaddr_un *remote, idle_message_t *msg)

if (n <= IDLE_MESSAGE_BASE_SIZE ||
msg->mboxname[n - 1 - IDLE_MESSAGE_BASE_SIZE] != '\0') {
syslog(LOG_ERR, "Invalid message received, size=%d\n", n);
syslog(LOG_ERR, "IDLE: invalid message received: size=%d, type=%s\n",
n, idle_msg_string(msg->which));
return 0;
}

return 1;
}

const char *idle_msg_string(unsigned long which)
{
const char *msg = "unknown";

switch(which) {
case IDLE_MSG_INIT:
msg = "init";
break;
case IDLE_MSG_DONE:
msg = "done";
break;
case IDLE_MSG_NOTIFY:
msg = "notify";
break;
case IDLE_MSG_NOOP:
msg = "noop";
break;
case IDLE_MSG_ALERT:
msg = "alert";
break;
}

return msg;
}

1 change: 1 addition & 0 deletions imap/idlemsg.h
Expand Up @@ -82,6 +82,7 @@ int idle_get_sock(void);
int idle_send(const struct sockaddr_un *remote,
const idle_message_t *msg);
int idle_recv(struct sockaddr_un *remote, idle_message_t *msg);
const char *idle_msg_string(unsigned long which);


#endif
8 changes: 5 additions & 3 deletions imap/imapd.c
Expand Up @@ -800,7 +800,7 @@ int service_init(int argc, char **argv, char **envp)
denydb_open(NULL);

/* setup for sending IMAP IDLE notifications */
idle_enabled();
idle_init();

/* create connection to the SNMP listener, if available. */
snmp_connect(); /* ignore return code */
Expand Down Expand Up @@ -1009,7 +1009,7 @@ void shut_down(int code)
if (backend_cached) free(backend_cached);

if (idling)
idle_done(imapd_index ? imapd_index->mailbox->name : NULL);
idle_stop(imapd_index ? imapd_index->mailbox->name : NULL);

if (imapd_index) index_close(&imapd_index);

Expand All @@ -1028,6 +1028,8 @@ void shut_down(int code)
annotatemore_close();
annotatemore_done();

idle_done();

if (config_getswitch(IMAPOPT_STATUSCACHE)) {
statuscache_close();
statuscache_done();
Expand Down Expand Up @@ -2798,7 +2800,7 @@ void cmd_idle(char *tag)

/* Stop updates and do any necessary cleanup */
idling = 0;
idle_done(imapd_index ? imapd_index->mailbox->name : NULL);
idle_stop(imapd_index ? imapd_index->mailbox->name : NULL);
}
else { /* Remote mailbox */
int done = 0, shutdown = 0;
Expand Down

0 comments on commit c024aa3

Please sign in to comment.