d74af51fa1
instead of doing two runs for each journal entry, do one run for each
"write" operation, be it a journal entry or a writing driver call. this
saves runs between which no visible change occurred, which yields a 33%
improvement in runtime.
we now also exclude the final entry purge from the test, as it's really
kinda pointless, and we'd have to jump through additional hoops
(simulate an atomic commit of the state) to make it reliable in all
cases.
note that this also adds a few steps, which actually uncovered a bug in
the expunge sequencing.
amends efd72b85
.
945 lines
17 KiB
C
945 lines
17 KiB
C
// SPDX-FileCopyrightText: 2000-2002 Michael R. Elkins <me@mutt.org>
|
|
// SPDX-FileCopyrightText: 2002-2022 Oswald Buddenhagen <ossi@users.sf.net>
|
|
// SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-isync-GPL-exception
|
|
/*
|
|
* mbsync - mailbox synchronizer
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <pwd.h>
|
|
|
|
int Verbosity = TERSE;
|
|
int DFlags;
|
|
int JLimit, JCount;
|
|
int UseFSync = 1;
|
|
|
|
int Pid;
|
|
char Hostname[256];
|
|
const char *Home;
|
|
|
|
static int need_nl, need_del;
|
|
|
|
void
|
|
flushn( void )
|
|
{
|
|
if (need_nl) {
|
|
putchar( '\n' );
|
|
fflush( stdout );
|
|
need_nl = 0;
|
|
} else if (need_del) {
|
|
static const char delstr[] =
|
|
" "
|
|
" ";
|
|
if (need_del > (int)sizeof(delstr) - 1)
|
|
need_del = (int)sizeof(delstr) - 1;
|
|
// We could use ^[[K instead, but we assume a dumb terminal.
|
|
printf( "\r%.*s\r", need_del, delstr );
|
|
fflush( stdout );
|
|
need_del = 0;
|
|
}
|
|
}
|
|
|
|
static void ATTR_PRINTFLIKE(1, 0)
|
|
vprint( const char *msg, va_list va )
|
|
{
|
|
vprintf( msg, va );
|
|
fflush( stdout );
|
|
need_nl = 0;
|
|
}
|
|
|
|
void
|
|
print( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
va_start( va, msg );
|
|
vprint( msg, va );
|
|
va_end( va );
|
|
}
|
|
|
|
static void ATTR_PRINTFLIKE(1, 0)
|
|
vprintn( const char *msg, va_list va )
|
|
{
|
|
vprint( msg, va );
|
|
need_nl = 1;
|
|
}
|
|
|
|
void
|
|
printn( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
va_start( va, msg );
|
|
vprintn( msg, va );
|
|
va_end( va );
|
|
}
|
|
|
|
void
|
|
progress( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
va_start( va, msg );
|
|
need_del = vprintf( msg, va ) - 1;
|
|
va_end( va );
|
|
fflush( stdout );
|
|
}
|
|
|
|
static void ATTR_PRINTFLIKE(1, 0)
|
|
nvprint( const char *msg, va_list va )
|
|
{
|
|
if (*msg == '\v')
|
|
msg++;
|
|
else
|
|
flushn();
|
|
vprint( msg, va );
|
|
}
|
|
|
|
void
|
|
info( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
if (Verbosity >= VERBOSE) {
|
|
va_start( va, msg );
|
|
nvprint( msg, va );
|
|
va_end( va );
|
|
}
|
|
}
|
|
|
|
void
|
|
infon( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
if (Verbosity >= VERBOSE) {
|
|
va_start( va, msg );
|
|
nvprint( msg, va );
|
|
va_end( va );
|
|
need_nl = 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
notice( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
if (Verbosity >= TERSE) {
|
|
va_start( va, msg );
|
|
nvprint( msg, va );
|
|
va_end( va );
|
|
}
|
|
}
|
|
|
|
void
|
|
warn( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
if (Verbosity >= QUIET) {
|
|
flushn();
|
|
va_start( va, msg );
|
|
vfprintf( stderr, msg, va );
|
|
va_end( va );
|
|
}
|
|
}
|
|
|
|
void
|
|
error( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
flushn();
|
|
va_start( va, msg );
|
|
vfprintf( stderr, msg, va );
|
|
va_end( va );
|
|
}
|
|
|
|
void
|
|
vsys_error( const char *msg, va_list va )
|
|
{
|
|
char buf[1024];
|
|
|
|
int errno_bak = errno;
|
|
flushn();
|
|
if ((uint)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
|
|
oob();
|
|
errno = errno_bak;
|
|
perror( buf );
|
|
}
|
|
|
|
void
|
|
sys_error( const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
va_start( va, msg );
|
|
vsys_error( msg, va );
|
|
va_end( va );
|
|
}
|
|
|
|
void
|
|
vFprintf( FILE *f, const char *msg, va_list va )
|
|
{
|
|
int r;
|
|
|
|
r = vfprintf( f, msg, va );
|
|
if (r < 0) {
|
|
sys_error( "Error: cannot write file" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
void
|
|
Fprintf( FILE *f, const char *msg, ... )
|
|
{
|
|
va_list va;
|
|
|
|
va_start( va, msg );
|
|
vFprintf( f, msg, va );
|
|
va_end( va );
|
|
}
|
|
|
|
void
|
|
Fclose( FILE *f, int safe )
|
|
{
|
|
if ((safe && (fflush( f ) || (UseFSync && fdatasync( fileno( f ) )))) || fclose( f ) == EOF) {
|
|
sys_error( "Error: cannot close file" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
void
|
|
add_string_list_n( string_list_t **list, const char *str, uint len )
|
|
{
|
|
string_list_t *elem;
|
|
|
|
elem = nfmalloc( offsetof(string_list_t, string) + len + 1 );
|
|
elem->next = *list;
|
|
*list = elem;
|
|
memcpy( elem->string, str, len );
|
|
elem->string[len] = 0;
|
|
}
|
|
|
|
void
|
|
add_string_list( string_list_t **list, const char *str )
|
|
{
|
|
add_string_list_n( list, str, strlen( str ) );
|
|
}
|
|
|
|
void
|
|
free_string_list( string_list_t *list )
|
|
{
|
|
string_list_t *tlist;
|
|
|
|
for (; list; list = tlist) {
|
|
tlist = list->next;
|
|
free( list );
|
|
}
|
|
}
|
|
|
|
#ifndef HAVE_VASPRINTF
|
|
static int
|
|
vasprintf( char **strp, const char *fmt, va_list ap )
|
|
{
|
|
int len;
|
|
char tmp[1024];
|
|
|
|
if ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = malloc( len + 1 )))
|
|
return -1;
|
|
if (len >= (int)sizeof(tmp))
|
|
vsprintf( *strp, fmt, ap );
|
|
else
|
|
memcpy( *strp, tmp, (size_t)len + 1 );
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_MEMRCHR
|
|
void *
|
|
memrchr( const void *s, int c, size_t n )
|
|
{
|
|
u_char *b = (u_char *)s, *e = b + n;
|
|
|
|
while (--e >= b)
|
|
if (*e == c)
|
|
return (void *)e;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_STRNLEN
|
|
size_t
|
|
strnlen( const char *str, size_t maxlen )
|
|
{
|
|
const char *estr = memchr( str, 0, maxlen );
|
|
return estr ? (size_t)(estr - str) : maxlen;
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
starts_with( const char *str, int strl, const char *cmp, uint cmpl )
|
|
{
|
|
if (strl < 0)
|
|
strl = strnlen( str, cmpl + 1 );
|
|
return ((uint)strl >= cmpl) && !memcmp( str, cmp, cmpl );
|
|
}
|
|
|
|
int
|
|
starts_with_upper( const char *str, int strl, const char *cmp, uint cmpl )
|
|
{
|
|
if (strl < 0)
|
|
strl = strnlen( str, cmpl + 1 );
|
|
if ((uint)strl < cmpl)
|
|
return 0;
|
|
for (uint i = 0; i < cmpl; i++)
|
|
if (toupper( str[i] ) != cmp[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
equals( const char *str, int strl, const char *cmp, uint cmpl )
|
|
{
|
|
if (strl < 0)
|
|
strl = strnlen( str, cmpl + 1 );
|
|
return ((uint)strl == cmpl) && !memcmp( str, cmp, cmpl );
|
|
}
|
|
|
|
#ifndef HAVE_TIMEGM
|
|
/*
|
|
Converts struct tm to time_t, assuming the data in tm is UTC rather
|
|
than local timezone.
|
|
|
|
mktime is similar but assumes struct tm, also known as the
|
|
"broken-down" form of time, is in local time zone. timegm
|
|
uses mktime to make the conversion understanding that an offset
|
|
will be introduced by the local time assumption.
|
|
|
|
mktime_from_utc then measures the introduced offset by applying
|
|
gmtime to the initial result and applying mktime to the resulting
|
|
"broken-down" form. The difference between the two mktime results
|
|
is the measured offset which is then subtracted from the initial
|
|
mktime result to yield a calendar time which is the value returned.
|
|
|
|
tm_isdst in struct tm is set to 0 to force mktime to introduce a
|
|
consistent offset (the non DST offset) since tm and tm+o might be
|
|
on opposite sides of a DST change.
|
|
|
|
Some implementations of mktime return -1 for the nonexistent
|
|
localtime hour at the beginning of DST. In this event, use
|
|
mktime(tm - 1hr) + 3600.
|
|
|
|
Schematically
|
|
mktime(tm) --> t+o
|
|
gmtime(t+o) --> tm+o
|
|
mktime(tm+o) --> t+2o
|
|
t+o - (t+2o - t+o) = t
|
|
|
|
Contributed by Roger Beeman <beeman@cisco.com>, with the help of
|
|
Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
|
|
Further improved by Roger with assistance from Edward J. Sabol
|
|
based on input by Jamie Zawinski.
|
|
*/
|
|
|
|
static time_t
|
|
my_mktime( struct tm *t )
|
|
{
|
|
time_t tl = mktime( t );
|
|
if (tl == -1) {
|
|
t->tm_hour--;
|
|
tl = mktime( t );
|
|
if (tl != -1)
|
|
tl += 3600;
|
|
}
|
|
return tl;
|
|
}
|
|
|
|
time_t
|
|
timegm( struct tm *t )
|
|
{
|
|
time_t tl, tb;
|
|
struct tm *tg;
|
|
|
|
if ((tl = my_mktime( t )) == -1)
|
|
return tl;
|
|
tg = gmtime( &tl );
|
|
tg->tm_isdst = 0;
|
|
if ((tb = my_mktime( tg )) == -1)
|
|
return tb;
|
|
return tl - (tb - tl);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
fmt_bits( uint bits, uint num_bits, const char *bit_str, const int *bit_off, char *buf )
|
|
{
|
|
uint d = 0;
|
|
for (uint i = 0, val = 1; i < num_bits; i++, val <<= 1) {
|
|
if (bits & val) {
|
|
if (d)
|
|
buf[d++] = ',';
|
|
for (const char *s = bit_str + bit_off[i]; *s; s++)
|
|
buf[d++] = *s;
|
|
}
|
|
}
|
|
buf[d] = 0;
|
|
}
|
|
|
|
void
|
|
oob( void )
|
|
{
|
|
fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
|
|
abort();
|
|
}
|
|
|
|
int
|
|
nfsnprintf( char *buf, int blen, const char *fmt, ... )
|
|
{
|
|
int ret;
|
|
va_list va;
|
|
|
|
va_start( va, fmt );
|
|
if (blen <= 0 || (uint)(ret = vsnprintf( buf, (size_t)blen, fmt, va )) >= (uint)blen)
|
|
oob();
|
|
va_end( va );
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
oom( void )
|
|
{
|
|
fputs( "Fatal: Out of memory\n", stderr );
|
|
abort();
|
|
}
|
|
|
|
void *
|
|
nfmalloc( size_t sz )
|
|
{
|
|
void *ret;
|
|
|
|
if (!(ret = malloc( sz )))
|
|
oom();
|
|
return ret;
|
|
}
|
|
|
|
void *
|
|
nfzalloc( size_t sz )
|
|
{
|
|
void *ret;
|
|
|
|
if (!(ret = calloc( sz, 1 )))
|
|
oom();
|
|
return ret;
|
|
}
|
|
|
|
void *
|
|
nfrealloc( void *mem, size_t sz )
|
|
{
|
|
char *ret;
|
|
|
|
if (!(ret = realloc( mem, sz )) && sz)
|
|
oom();
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
nfstrndup( const char *str, size_t nchars )
|
|
{
|
|
char *ret = nfmalloc( nchars + 1 );
|
|
memcpy( ret, str, nchars );
|
|
ret[nchars] = 0;
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
nfstrdup( const char *str )
|
|
{
|
|
return nfstrndup( str, strlen( str ) );
|
|
}
|
|
|
|
int
|
|
nfvasprintf( char **str, const char *fmt, va_list va )
|
|
{
|
|
int ret = vasprintf( str, fmt, va );
|
|
if (ret < 0)
|
|
oom();
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
nfasprintf( char **str, const char *fmt, ... )
|
|
{
|
|
int ret;
|
|
va_list va;
|
|
|
|
va_start( va, fmt );
|
|
ret = nfvasprintf( str, fmt, va );
|
|
va_end( va );
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
static struct passwd *
|
|
cur_user( void )
|
|
{
|
|
char *p;
|
|
struct passwd *pw;
|
|
uid_t uid;
|
|
|
|
uid = getuid();
|
|
if ((!(p = getenv("LOGNAME")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
|
|
(!(p = getenv("USER")) || !(pw = getpwnam( p )) || pw->pw_uid != uid) &&
|
|
!(pw = getpwuid( uid )))
|
|
{
|
|
fputs ("Cannot determinate current user\n", stderr);
|
|
return 0;
|
|
}
|
|
return pw;
|
|
}
|
|
*/
|
|
|
|
/* Return value: 0 = ok, -1 = out found in arg, -2 = in found in arg but no out specified */
|
|
int
|
|
map_name( const char *arg, char **result, uint reserve, const char *in, const char *out )
|
|
{
|
|
char *p;
|
|
uint i, l, ll, num, inl, outl;
|
|
|
|
assert( arg );
|
|
l = strlen( arg );
|
|
assert( in );
|
|
inl = strlen( in );
|
|
if (!inl) {
|
|
copy:
|
|
*result = nfmalloc( reserve + l + 1 );
|
|
memcpy( *result + reserve, arg, l + 1 );
|
|
return 0;
|
|
}
|
|
assert( out );
|
|
outl = strlen( out );
|
|
if (equals( in, (int)inl, out, outl ))
|
|
goto copy;
|
|
for (num = 0, i = 0; i < l; ) {
|
|
for (ll = 0; ll < inl; ll++)
|
|
if (arg[i + ll] != in[ll])
|
|
goto fout;
|
|
num++;
|
|
i += inl;
|
|
continue;
|
|
fout:
|
|
if (outl) {
|
|
for (ll = 0; ll < outl; ll++)
|
|
if (arg[i + ll] != out[ll])
|
|
goto fnexti;
|
|
return -1;
|
|
}
|
|
fnexti:
|
|
i++;
|
|
}
|
|
if (!num)
|
|
goto copy;
|
|
if (!outl)
|
|
return -2;
|
|
*result = nfmalloc( reserve + l + num * (outl - inl) + 1 );
|
|
p = *result + reserve;
|
|
for (i = 0; i < l; ) {
|
|
for (ll = 0; ll < inl; ll++)
|
|
if (arg[i + ll] != in[ll])
|
|
goto rnexti;
|
|
memcpy( p, out, outl );
|
|
p += outl;
|
|
i += inl;
|
|
continue;
|
|
rnexti:
|
|
*p++ = arg[i++];
|
|
}
|
|
*p = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mkdir_p( char *path, int len )
|
|
{
|
|
if (!mkdir( path, 0700 ) || errno == EEXIST)
|
|
return 0;
|
|
char *p = memrchr( path, '/', (size_t)len );
|
|
*p = 0;
|
|
if (mkdir_p( path, (int)(p - path) )) {
|
|
*p = '/';
|
|
return -1;
|
|
}
|
|
*p = '/';
|
|
return mkdir( path, 0700 );
|
|
}
|
|
|
|
static int
|
|
compare_uints( const void *l, const void *r )
|
|
{
|
|
uint li = *(const uint *)l, ri = *(const uint *)r;
|
|
if (li != ri) // Can't subtract, the result might not fit into signed int.
|
|
return li > ri ? 1 : -1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sort_uint_array( uint_array_t array )
|
|
{
|
|
qsort( array.data, array.size, sizeof(uint), compare_uints );
|
|
}
|
|
|
|
int
|
|
find_uint_array( uint_array_t array, uint value )
|
|
{
|
|
uint bot = 0, top = array.size;
|
|
while (bot < top) {
|
|
uint i = (bot + top) / 2;
|
|
uint elt = array.data[i];
|
|
if (elt == value)
|
|
return 1;
|
|
if (elt < value)
|
|
bot = i + 1;
|
|
else
|
|
top = i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct {
|
|
uchar i, j, s[256];
|
|
} rs;
|
|
|
|
void
|
|
arc4_init( void )
|
|
{
|
|
int i, fd;
|
|
uchar j, si, dat[128];
|
|
|
|
if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
|
|
error( "Fatal: no random number source available.\n" );
|
|
exit( 3 );
|
|
}
|
|
if (read( fd, dat, 128 ) != 128) {
|
|
error( "Fatal: cannot read random number source.\n" );
|
|
exit( 3 );
|
|
}
|
|
close( fd );
|
|
|
|
for (i = 0; i < 256; i++)
|
|
rs.s[i] = (uchar)i;
|
|
for (i = j = 0; i < 256; i++) {
|
|
si = rs.s[i];
|
|
j += si + dat[i & 127];
|
|
rs.s[i] = rs.s[j];
|
|
rs.s[j] = si;
|
|
}
|
|
rs.i = rs.j = 0;
|
|
|
|
for (i = 0; i < 256; i++)
|
|
arc4_getbyte();
|
|
}
|
|
|
|
uchar
|
|
arc4_getbyte( void )
|
|
{
|
|
uchar si, sj;
|
|
|
|
rs.i++;
|
|
si = rs.s[rs.i];
|
|
rs.j += si;
|
|
sj = rs.s[rs.j];
|
|
rs.s[rs.i] = sj;
|
|
rs.s[rs.j] = si;
|
|
return rs.s[(si + sj) & 0xff];
|
|
}
|
|
|
|
static const uchar prime_deltas[] = {
|
|
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 17, 27, 3,
|
|
1, 29, 3, 21, 7, 17, 15, 9, 43, 35, 15, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
uint
|
|
bucketsForSize( uint size )
|
|
{
|
|
uint base = 4, bits = 2;
|
|
|
|
for (;;) {
|
|
uint prime = base + prime_deltas[bits];
|
|
if (prime >= size)
|
|
return prime;
|
|
base <<= 1;
|
|
bits++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
list_prepend( list_head_t *head, list_head_t *to )
|
|
{
|
|
assert( !head->next );
|
|
assert( to->next );
|
|
assert( to->prev->next == to );
|
|
head->next = to;
|
|
head->prev = to->prev;
|
|
head->prev->next = head;
|
|
to->prev = head;
|
|
}
|
|
|
|
static void
|
|
list_unlink( list_head_t *head )
|
|
{
|
|
assert( head->next );
|
|
assert( head->next->prev == head);
|
|
assert( head->prev->next == head);
|
|
head->next->prev = head->prev;
|
|
head->prev->next = head->next;
|
|
head->next = head->prev = NULL;
|
|
}
|
|
|
|
static notifier_t *notifiers;
|
|
static int changed; /* Iterator may be invalid now. */
|
|
#ifdef HAVE_POLL_H
|
|
static struct pollfd *pollfds;
|
|
static uint npolls, rpolls;
|
|
#else
|
|
# ifdef HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
# endif
|
|
#endif
|
|
|
|
void
|
|
init_notifier( notifier_t *sn, int fd, void (*cb)( int, void * ), void *aux )
|
|
{
|
|
#ifdef HAVE_POLL_H
|
|
uint idx = npolls++;
|
|
if (rpolls < npolls) {
|
|
rpolls = npolls;
|
|
pollfds = nfrealloc( pollfds, npolls * sizeof(*pollfds) );
|
|
}
|
|
pollfds[idx].fd = fd;
|
|
pollfds[idx].events = 0; /* POLLERR & POLLHUP implicit */
|
|
sn->index = idx;
|
|
#else
|
|
sn->fd = fd;
|
|
sn->events = 0;
|
|
#endif
|
|
sn->cb = cb;
|
|
sn->aux = aux;
|
|
sn->next = notifiers;
|
|
notifiers = sn;
|
|
}
|
|
|
|
void
|
|
conf_notifier( notifier_t *sn, short and_events, short or_events )
|
|
{
|
|
#ifdef HAVE_POLL_H
|
|
uint idx = sn->index;
|
|
pollfds[idx].events = (pollfds[idx].events & and_events) | or_events;
|
|
#else
|
|
sn->events = (sn->events & and_events) | or_events;
|
|
#endif
|
|
}
|
|
|
|
short
|
|
notifier_config( notifier_t *sn )
|
|
{
|
|
#ifdef HAVE_POLL_H
|
|
return pollfds[sn->index].events;
|
|
#else
|
|
return sn->events;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
wipe_notifier( notifier_t *sn )
|
|
{
|
|
notifier_t **snp;
|
|
#ifdef HAVE_POLL_H
|
|
uint idx;
|
|
#endif
|
|
|
|
for (snp = ¬ifiers; *snp != sn; snp = &(*snp)->next)
|
|
assert( *snp );
|
|
*snp = sn->next;
|
|
sn->next = NULL;
|
|
changed = 1;
|
|
|
|
#ifdef HAVE_POLL_H
|
|
idx = sn->index;
|
|
memmove( pollfds + idx, pollfds + idx + 1, (--npolls - idx) * sizeof(*pollfds) );
|
|
for (sn = notifiers; sn; sn = sn->next) {
|
|
if (sn->index > idx)
|
|
sn->index--;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static time_t
|
|
get_now( void )
|
|
{
|
|
return time( NULL );
|
|
}
|
|
|
|
static list_head_t timers = { &timers, &timers };
|
|
|
|
void
|
|
init_wakeup( wakeup_t *tmr, void (*cb)( void * ), void *aux )
|
|
{
|
|
tmr->cb = cb;
|
|
tmr->aux = aux;
|
|
tmr->links.next = tmr->links.prev = NULL;
|
|
}
|
|
|
|
void
|
|
wipe_wakeup( wakeup_t *tmr )
|
|
{
|
|
if (tmr->links.next)
|
|
list_unlink( &tmr->links );
|
|
}
|
|
|
|
void
|
|
conf_wakeup( wakeup_t *tmr, int to )
|
|
{
|
|
list_head_t *head, *succ;
|
|
|
|
if (to < 0) {
|
|
if (tmr->links.next)
|
|
list_unlink( &tmr->links );
|
|
} else {
|
|
time_t timeout = to;
|
|
if (!to) {
|
|
/* We always prepend null timers, to cluster related events. */
|
|
succ = timers.next;
|
|
} else {
|
|
timeout += get_now();
|
|
/* We start at the end in the expectation that the newest timer is likely to fire last
|
|
* (which will be true only if all timeouts are equal, but it's an as good guess as any). */
|
|
for (succ = &timers; (head = succ->prev) != &timers; succ = head) {
|
|
if (head != &tmr->links && timeout > ((wakeup_t *)head)->timeout)
|
|
break;
|
|
}
|
|
assert( head != &tmr->links );
|
|
}
|
|
tmr->timeout = timeout;
|
|
if (succ != &tmr->links) {
|
|
if (tmr->links.next)
|
|
list_unlink( &tmr->links );
|
|
list_prepend( &tmr->links, succ );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
event_wait( void )
|
|
{
|
|
list_head_t *head;
|
|
notifier_t *sn;
|
|
int m;
|
|
|
|
#ifdef HAVE_POLL_H
|
|
int timeout = -1;
|
|
if ((head = timers.next) != &timers) {
|
|
wakeup_t *tmr = (wakeup_t *)head;
|
|
time_t delta = tmr->timeout;
|
|
if (!delta || (delta -= get_now()) <= 0) {
|
|
list_unlink( head );
|
|
tmr->cb( tmr->aux );
|
|
return;
|
|
}
|
|
timeout = (int)delta * 1000;
|
|
}
|
|
switch (poll( pollfds, npolls, timeout )) {
|
|
case 0:
|
|
return;
|
|
case -1:
|
|
perror( "poll() failed in event loop" );
|
|
abort();
|
|
default:
|
|
break;
|
|
}
|
|
for (sn = notifiers; sn; sn = sn->next) {
|
|
uint n = sn->index;
|
|
if ((m = pollfds[n].revents)) {
|
|
assert( !(m & POLLNVAL) );
|
|
sn->cb( m | shifted_bit( m, POLLHUP, POLLIN ), sn->aux );
|
|
if (changed) {
|
|
changed = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
struct timeval *timeout = 0;
|
|
struct timeval to_tv;
|
|
fd_set rfds, wfds, efds;
|
|
int fd;
|
|
|
|
if ((head = timers.next) != &timers) {
|
|
wakeup_t *tmr = (wakeup_t *)head;
|
|
time_t delta = tmr->timeout;
|
|
if (!delta || (delta -= get_now()) <= 0) {
|
|
list_unlink( head );
|
|
tmr->cb( tmr->aux );
|
|
return;
|
|
}
|
|
to_tv.tv_sec = delta;
|
|
to_tv.tv_usec = 0;
|
|
timeout = &to_tv;
|
|
}
|
|
FD_ZERO( &rfds );
|
|
FD_ZERO( &wfds );
|
|
FD_ZERO( &efds );
|
|
m = -1;
|
|
for (sn = notifiers; sn; sn = sn->next) {
|
|
fd = sn->fd;
|
|
if (sn->events & POLLIN)
|
|
FD_SET( fd, &rfds );
|
|
if (sn->events & POLLOUT)
|
|
FD_SET( fd, &wfds );
|
|
FD_SET( fd, &efds );
|
|
if (fd > m)
|
|
m = fd;
|
|
}
|
|
switch (select( m + 1, &rfds, &wfds, &efds, timeout )) {
|
|
case 0:
|
|
return;
|
|
case -1:
|
|
perror( "select() failed in event loop" );
|
|
abort();
|
|
default:
|
|
break;
|
|
}
|
|
for (sn = notifiers; sn; sn = sn->next) {
|
|
fd = sn->fd;
|
|
m = 0;
|
|
if (FD_ISSET( fd, &rfds ))
|
|
m |= POLLIN;
|
|
if (FD_ISSET( fd, &wfds ))
|
|
m |= POLLOUT;
|
|
if (FD_ISSET( fd, &efds ))
|
|
m |= POLLERR;
|
|
if (m) {
|
|
sn->cb( m, sn->aux );
|
|
if (changed) {
|
|
changed = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
main_loop( void )
|
|
{
|
|
while (notifiers || timers.next != &timers)
|
|
event_wait();
|
|
}
|