8d9c68f73a
the string length is knowable in advance everywhere, so we can use that for strdups and short-circuiting comparisons.
961 lines
17 KiB
C
961 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 );
|
|
}
|
|
|
|
static int
|
|
equals_upper_impl( const char *str, const char *cmp, uint cmpl )
|
|
{
|
|
for (uint i = 0; i < cmpl; i++)
|
|
if (toupper( str[i] ) != cmp[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
return equals_upper_impl( str, cmp, cmpl );
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
int
|
|
equals_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;
|
|
return equals_upper_impl( 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();
|
|
}
|