Esempio n. 1
0
/*
   あるfdを指定して、ネットワークに書きこみたいデータの次のかたまりの
   位置を教える。タイムアウトの処理もここでできる。
   書きこみたい次のかたまりとは、WLOG_SENDWAITの状態の場所のことである。

   それに長さの制限をSEGMENTSIZでかけるので、
   

   
 */
static int getwfdpart(int fd , unsigned long *start , unsigned long *end ,unsigned long siz)
{
	int i;
	int find=0;

	CHECKFD(fd);

	for(i=0;i<BUFFERSIZ;i++){
		if( find == 0 && stb[fd].wlog[i] == WLOG_SENDWAIT ){
			*start = i;
			find = 1;
		}
		if( find == 1 && stb[fd].wlog[i] != WLOG_SENDWAIT ){	/* WLOG_NOUSEだろう */
			*end = i;
			if( ( *end - *start ) > siz ){
				*end = *start + siz;
			}
			udptcp.errno = 0;
			return 0;
		}
	}

	if( find == 1 ){
		*end = i;
		udptcp.errno = 0;
		return 0;
	} else {
		*start = *end = BUFFERSIZ;
		udptcp.errno = 0;
		return -0;
	}
}
Esempio n. 2
0
/*

   writeする。fdのバッファにたまるだけなので、このあとuprocしなければならない。

   return values:
   0 OK
   <0 Error
   */
int uwrite( int fd , char *data , int len )
{

	char tmpdata[SEGMENTSIZ];
	int i;
	int write_total = 0;
	int cpsz;
	
	CHECKFD(fd);

	/* サイズが変 */
	if(  len < 0 ){
		udptcp.errno = EBADDATALEN;
		return -1;
	}


	/* ソケットのバッファにたす。単にアペンドするのみ */
	if( (stb[fd].wb_use + len) >= BUFFERSIZ){
		/* BUFFERSIZをこえてるぞ */
		cpsz = BUFFERSIZ - stb[fd].wb_use;
	} else {
		cpsz = len;
	}
	
	bcopy( data , stb[fd].wb + stb[fd].wb_use , cpsz );
	memset( stb[fd].wlog + stb[fd].wb_use , WLOG_SENDWAIT , cpsz );
	stb[fd].wb_use += cpsz;
	udptcp.errno = 0;
	fprintf( stderr, "copied %d byte to rb\n" , cpsz );
	return cpsz;
}
Esempio n. 3
0
/*
   readする。fdのバッファにたまっている分を読みこむだけなので、uprocしたあとでないと何も
   起こらない。
 */
int uread( int fd, char *data , int len )
{

	int i ,k;
	int read_total = 0;
	int rs_end;
	int completesiz;

	CHECKFD(fd);

	/* ストリームバッファのどこまでが完成したのかを計算する */
	rs_end = getrsend(fd);
	completesiz = rs_end - stb[fd].rs_top;

	udptcp.errno =0;
	if( completesiz > len ){
		;
	} else {
		len = completesiz;
	}
	
	bcopy( stb[fd].rb , data , len );
	shift( stb[fd].rb , len , BUFFERSIZ);
	shift( stb[fd].rlog , len ,BUFFERSIZ);
	memset( stb[fd].rlog+len,RLOG_NOUSE,BUFFERSIZ-len );
	stb[fd].rb_use -= len;
	fprintf( stderr, "RBUSE:%d\n",	stb[fd].rb_use );
	return len;
}
Esempio n. 4
0
static void CAInitializeNetlink()
{
#ifdef __linux__
    // create NETLINK fd for interface change notifications
    struct sockaddr_nl sa = { AF_NETLINK, 0, 0, RTMGRP_LINK };

    caglobals.ip.netlinkFd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
    if (caglobals.ip.netlinkFd == -1)
    {
        OIC_LOG_V(ERROR, TAG, "netlink socket failed: %s", strerror(errno));
    }
    else
    {
        int r = bind(caglobals.ip.netlinkFd, (struct sockaddr *)&sa, sizeof (sa));
        if (r)
        {
            OIC_LOG_V(ERROR, TAG, "netlink bind failed: %s", strerror(errno));
            close(caglobals.ip.netlinkFd);
            caglobals.ip.netlinkFd = -1;
        }
        else
        {
            CHECKFD(caglobals.ip.netlinkFd);
        }
    }
#endif
}
Esempio n. 5
0
/*
   ackがきたときの処理
*/
static int setack( int fd , unsigned long ack_start , unsigned long ack_length )
{
	int i;
	int ai;
	
	CHECKFD( fd );

	ai = ack_start - stb[fd].ws_top;
	if( ai < 0 || ai >= BUFFERSIZ ){
		/* そりゃおかしいなり。*/
		return -1;
	}
	if( (ai+ack_length) >= BUFFERSIZ ){
		return -1;
	}
	
	for(i=ai;i<ai+ack_length;i++ ){
		stb[fd].wlog[i] = WLOG_ACKOK;
	}

	/* aiが0のときは、バッファーの最初の部分をACKできたということだから、
	   シフトしてよい。 */
	if( ai == 0 ){
		stb[fd].wb_use -= ack_length;
		shift( stb[fd].wb , ack_length ,BUFFERSIZ );
		shift( stb[fd].wlog , ack_length ,BUFFERSIZ );
		memset( stb[fd].wlog + ack_length , WLOG_NOUSE , BUFFERSIZ - ack_length );
	}
	
	return 0;
}
Esempio n. 6
0
/*
	listenする

   int fd : socket表へのindex

   return values:
   0 OK
   <0 Error
   
*/
int ulisten( int fd )
{
	CHECKFD(fd);

	stb[fd].listen = 1;

	return 0;

}
Esempio n. 7
0
/*
   書きこんだときの処理
 */
static int setsend( int fd , unsigned long start , unsigned long end )
{
	int i;

	CHECKFD(fd);


	for(i=start;i<end;i++){
		stb[fd].wlog[i] = WLOG_ACKWAIT;
	}
	return 0;
		
}
Esempio n. 8
0
/*
   ソケットが初期化されてるかどうかしらべる

   return values:
   0 Not yet
   1 OK
   -1 Error
   
 */
static issockinited( int fd )
{
	CHECKFD(fd);

/*	fprintf( stderr, "ISSINI: fd:%d syn:%d\n",
			fd,stb[fd].read_syn );*/
	
	if( stb[fd].read_syn ){
		return 1;
	} else {
		return 0;
	}

}
Esempio n. 9
0
// Return the fd of the new tun device
// Sets dev to the actual device name
int tun_alloc(char *dev, int devtype = IFF_TUN) 
{
  assert(dev != NULL);
  int fd = open("/dev/net/tun", O_RDWR);
  CHECKFD(fd);

  struct ifreq ifr; 
  memset(&ifr, 0, sizeof(ifr)); 
  //ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  ifr.ifr_flags = devtype | IFF_NO_PI;
  strncpy(ifr.ifr_name, dev, IFNAMSIZ); 
  CHECKSYS(ioctl(fd, TUNSETIFF, (void *) &ifr));
  strncpy(dev, ifr.ifr_name, IFNAMSIZ); 
  return fd;
}
Esempio n. 10
0
/*
   受信ログをみて、ストリームがどこまで完成しているのかをしらべる
   かならず有効な値をかえす。
 */
static unsigned long getrsend( int fd )
{
	unsigned long end = 0;
	unsigned long now = stb[fd].rs_top;
	int i ,j ;

	CHECKFD(fd);
	
	for(i=0;i<BUFFERSIZ;i++){
		if( stb[fd].rlog[i] != RLOG_ACKOK ){
			break;
		}
	}
	return now + i;
}
Esempio n. 11
0
/*
 	ソケットが読みこみ可能かどうかしらべる

   return values:
   0 Not readable
   1 OK
   -1 エラー
   
   
 */
int ureadable( int fd )
{
	int i;

	CHECKFD(fd);

	if( issockinited(fd ) != 1 ){
		udptcp.errno = ENOTINIT;
		return 0;
	}


	/*	fprintf( stderr, "UREADABLE:%d END:%d TOP:%d\n" ,fd,getrsend(fd) , stb[fd].rs_top );*/

	/* トップの位置と終わりの位置が同じでないとき、読みこみ可能 */
	return !( ( getrsend(fd) - stb[fd].rs_top ) ==0 );

}
Esempio n. 12
0
/*



   addrの値は、 htonl後の値である。


   return values:
   0 OK
   <0 Error

   uprocは、何かパケットがきたら返り値でしらせるので、
   uprocが何かうけとるたびにテストして、ackがきたらconnect成功、
   というようにしなければならない。ブロックする。
   
*/
int uconnect( int fd , unsigned long addr )
{

	int sr;
	char tmpdata[HEADERSIZE];

	CHECKFD(fd);

	/* 自分の設定をする */
	stb[fd].addr_dest = addr;
	
	/* connect用のパケットを送信。ヘッダのみ */
	makeheader( tmpdata, 
			   0 ,	/* seq */
			   ACK_NOUSE ,	/* ack */
			   0 ,	/* rst */
			   1 ,	/* syn */
			   0 ,	/* fin */
			   0 ,	/* ref */
			   0 	/* len */
	);
	sender( tmpdata, HEADERSIZE , addr );


	if( sr < 0 ){
		/* errnoはセットされてる */
		return -1;
	}

	/* ブロックして、返事がくるまで待つ */
	while(1){
		usleep(100*1000);
		fprintf( stderr, "c ");
		if( uproc() > 0 ){
			if( issockinited( fd ) ){
				break;
			}
		}
	}
	
	udptcp.errno = 0;
	return 0;
}
Esempio n. 13
0
/*
   read読みこんだときのしょり

   読みこみログをRLOG_ACKOKにする
   成功したら0をかえす。

   startからlen分だけ.
   
 */
int addrb( int fd, char *buf , unsigned long start ,unsigned long len )
{
	int i , ai;
	
	CHECKFD(fd);

	ai = start - stb[fd].rs_top;
	if( ai < 0 || ai >= BUFFERSIZ ){
		return -1;
	}
	if( ( ai + len )>= BUFFERSIZ ){
		return -1;
	}
	for(i=ai;i<ai+len;i++){
		stb[fd].rb[i]=buf[i-ai];
		stb[fd].rlog[i]=RLOG_ACKOK;
	}
	stb[fd].rb_use += len;
	return 0;
}
Esempio n. 14
0
/*
   usockでアクセプト

   int fd : socket表へのindex
   unsigned long cliaddr : クライアントのIPアドレス

   fdはlistenしていなければならない。
   

   return value:
   >= 0 OK
   <0 Error
   

   ソケット表から検索して、acceptwaitの状態になっているのをさがす

   cliaddr は htonl後、 
   
   
 */
int uaccept( int fd , unsigned long *cliaddr )
{
	int i;

	CHECKFD(fd);

	if( ! stb[fd].listen ){
		udptcp.errno = ENOLISTEN;
		return -1;
	}
	
	for(i=0;i<MAXCON;i++){
		if( stb[i].use && stb[i].acceptwait ){
			stb[i].acceptwait = 0;
			*cliaddr = stb[i].addr_dest;
			return i;
		}
	}

	udptcp.errno = ENOACCEPTWAIT;
	return -1;
}
Esempio n. 15
0
static void CAAcceptConnection()
{
    struct sockaddr_storage clientaddr;
    socklen_t clientlen = sizeof (struct sockaddr_in);

    int sockfd = accept(g_acceptServerFD, (struct sockaddr *)&clientaddr,
                        &clientlen);
    if (-1 != sockfd)
    {
        CATCPSessionInfo_t *svritem =
                (CATCPSessionInfo_t *) OICCalloc(1, sizeof (*svritem));
        if (!svritem)
        {
            OIC_LOG(ERROR, TAG, "Out of memory");
            close(sockfd);
            return;
        }

        svritem->fd = sockfd;
        CAConvertAddrToName((struct sockaddr_storage *)&clientaddr, clientlen,
                            (char *) &svritem->sep.endpoint.addr, &svritem->sep.endpoint.port);

        ca_mutex_lock(g_mutexObjectList);
        bool result = u_arraylist_add(caglobals.tcp.svrlist, svritem);
        if (!result)
        {
            OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
            close(sockfd);
            OICFree(svritem);
            ca_mutex_unlock(g_mutexObjectList);
            return;
        }
        ca_mutex_unlock(g_mutexObjectList);

        CHECKFD(sockfd);
    }
}
Esempio n. 16
0
int
select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
	   struct timeval *timeout)
{
  unsigned int wait_ticks = 0;  
  int fd;
#define WK_SELECT_SZ 1024
  struct wk_term t[WK_SELECT_SZ];  
  int next;
  int total = 0;
  fd_set newreadfds, newwritefds, newexceptfds;
#define DID_FDREADY 1		
#define DID_TIMEOUT 2
#define DID_SIGNAL 3
  struct file *filp;
  int had_prev_term;

  OSCALLENTER(OSCALL_select);
  width = MIN (width+1, NR_OPEN);

  /* make sure that all fd's set to be polled are valid fd's */

  for (fd = 0; fd < width; fd++) {
    if ((readfds && FD_ISSET (fd, readfds)) || 
	(writefds && FD_ISSET (fd, writefds)) ||
	(exceptfds && FD_ISSET (fd, exceptfds))) {
      CHECKFD(fd, OSCALL_select);
      assert (CHECKOP (__current->fd[fd], select));
      assert (CHECKOP (__current->fd[fd], select_pred));
    }
  }

  FD_ZERO(&newreadfds);
  FD_ZERO(&newwritefds);
  FD_ZERO(&newexceptfds);

  /* Our basic algorithm is poll the fd's once. If any fd's are found
     ready return. Otherwise sleep until one of them might be ready
     and repeat the process. In practice we don't expect to go
     through this loop more than once, but theoretically we could
     wakeup for some reason other than haveing fd's ready. Am I just
     being paranoid? */

  /* Note: we can _definitely_ go thru this loop more than once, because
     in some cases (e.g., TCP), we can only sleep on an "indication" that
     select might pass (e.g., a packet arrived).  We have to then call
     select to find out if it does in fact make the socket ready, and rebuild
     the sleep predicate otherwise. */

  do {

    had_prev_term = 0;

    /* do a poll on the fd's. We want to make sure that we do this
       before sleeping so on the first time through the do loop we
       avoid descheduling and having to wait till we're re-scheduled
       before noticing that there're fd's ready. */

    for (fd = 0; fd < width; fd++) {
      if (readfds && FD_ISSET (fd, readfds))
	if (DOOP (__current->fd[fd], select, (__current->fd[fd], SELECT_READ))) {
	  total++;
	  FD_SET (fd, &newreadfds);
	}
      if (writefds && FD_ISSET (fd, writefds))
	if (DOOP (__current->fd[fd], select, (__current->fd[fd], SELECT_WRITE))) {
	  total++;
	  FD_SET (fd, &newwritefds);
	}	
      if (SELECT_EXCEPT_CONDITIONS && exceptfds && FD_ISSET (fd, exceptfds))
	if (DOOP (__current->fd[fd], select, (__current->fd[fd], SELECT_EXCEPT))) {
	  total++;
	  FD_SET (fd, &newexceptfds);
	}	
    }

    /* ok, we found some fd's that we need to report. Replace the
       fdsets the user passed in with fdsets containing which
       fd's are ready and return the total number of fd's ready. */

    if (total) {
      if (readfds)
	copyfds (readfds, &newreadfds, width);
      if (writefds)
	copyfds (writefds, &newwritefds, width);
      if (exceptfds)
	copyfds (exceptfds, &newexceptfds, width);
      /* XXX */
      OSCALLEXIT(OSCALL_select);
      return total;
    }

    /* if the user is just polling, handle that now before going through
       all the work to construct a predicate */

    if (timeout) {
      wait_ticks = ((1000000/RATE) * timeout->tv_sec) +
	(timeout->tv_usec + RATE - 1)/RATE;
      if (!wait_ticks)
	{
	  if (readfds) FD_ZERO(readfds);
	  if (writefds) FD_ZERO(writefds);
	  if (exceptfds) FD_ZERO(exceptfds);
	  OSCALLEXIT(OSCALL_select);
	  return 0;
	}
    }

    /* now construct a wakeup-predicate that will wake us when something
       interesting happens on these fd's. We call each fd's select_pred
       operation which returns a clause of the final predicate. All
       clauses are combined into one large predicate that we'll sleep on. */

    next = 0;
    had_prev_term = 0;
    next = wk_mktag (next, t, DID_FDREADY);
    for (fd = 0; fd < width; fd++) {
      filp = __current->fd[fd];
      if (readfds && FD_ISSET (fd, readfds)) {
	if (had_prev_term)
	  next = wk_mkop (next, t, WK_OR);	
	next += DOOP (filp,select_pred,(filp,SELECT_READ,&t[next]));
	had_prev_term = 1;
      }
	  
      if (writefds && FD_ISSET (fd, writefds)) {
	if (had_prev_term)
	  next = wk_mkop (next, t, WK_OR);	
	next += DOOP (filp,select_pred,(filp,SELECT_WRITE,&t[next]));	
	had_prev_term = 1;
      }

      if (SELECT_EXCEPT_CONDITIONS && exceptfds && FD_ISSET (fd, exceptfds)) {
	if (had_prev_term)
	  next = wk_mkop (next, t, WK_OR);	
	next += DOOP (filp,select_pred,(filp,SELECT_EXCEPT,&t[next]));	
	had_prev_term = 1;
      }
    }
    /* slap on a final term to wake us when the timeout occurrs, if there
       is one */

    if (timeout) {
      if (had_prev_term)
	next = wk_mkop (next, t, WK_OR);
      next = wk_mktag (next, t, DID_TIMEOUT);
      next += wk_mksleep_pred (&t[next], wait_ticks + __sysinfo.si_system_ticks);
      had_prev_term = 1;
    }

    /* we need to wakeup if a signal comes in */
    if (had_prev_term) {
       next = wk_mkop (next, t, WK_OR);
    }
    next = wk_mktag (next, t, DID_SIGNAL);
    next += wk_mksig_pred (&t[next]);
    had_prev_term = 1;
    
    /* wait for predicate to evaluate to true */
    wk_waitfor_pred (t, next);

    /* u_pred_tag is set to the piece of the predicate that caused
       us to wake up */

    if (UAREA.u_pred_tag == DID_TIMEOUT) {
      if (readfds) FD_ZERO(readfds);
      if (writefds) FD_ZERO(writefds);
      if (exceptfds) FD_ZERO(exceptfds);
      OSCALLEXIT(OSCALL_select);
      return 0;
    }
    if (UAREA.u_pred_tag == DID_SIGNAL) {
      //kprintf("%d select interrupted by signal\n",getpid());
      errno = EINTR;
      OSCALLEXIT(OSCALL_select);
      return -1;
    }
  
  } while (1);
}
Esempio n. 17
0
CAResult_t CAIPStartServer(const ca_thread_pool_t threadPool)
{
    CAResult_t res = CA_STATUS_OK;

    if (caglobals.ip.started)
    {
        return res;
    }

    if (!IPv4MulticastAddress.s_addr)
    {
        (void)inet_aton(IPv4_MULTICAST, &IPv4MulticastAddress);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_INT, &IPv6MulticastAddressInt);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_LNK, &IPv6MulticastAddressLnk);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_RLM, &IPv6MulticastAddressRlm);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_ADM, &IPv6MulticastAddressAdm);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_SIT, &IPv6MulticastAddressSit);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_ORG, &IPv6MulticastAddressOrg);
        (void)inet_pton(AF_INET6, IPv6_MULTICAST_GLB, &IPv6MulticastAddressGlb);
    }

    if (!caglobals.ip.ipv6enabled && !caglobals.ip.ipv4enabled)
    {
        caglobals.ip.ipv4enabled = true;  // only needed to run CA tests
    }

    if (caglobals.ip.ipv6enabled)
    {
        NEWSOCKET(AF_INET6, u6)
        NEWSOCKET(AF_INET6, u6s)
        NEWSOCKET(AF_INET6, m6)
        NEWSOCKET(AF_INET6, m6s)
        OIC_LOG_V(INFO, TAG, "IPv6 unicast port: %u", caglobals.ip.u6.port);
    }
    if (caglobals.ip.ipv4enabled)
    {
        NEWSOCKET(AF_INET, u4)
        NEWSOCKET(AF_INET, u4s)
        NEWSOCKET(AF_INET, m4)
        NEWSOCKET(AF_INET, m4s)
        OIC_LOG_V(INFO, TAG, "IPv4 unicast port: %u", caglobals.ip.u4.port);
    }

    OIC_LOG_V(DEBUG, TAG,
              "socket summary: u6=%d, u6s=%d, u4=%d, u4s=%d, m6=%d, m6s=%d, m4=%d, m4s=%d",
                             caglobals.ip.u6.fd, caglobals.ip.u6s.fd,
                             caglobals.ip.u4.fd, caglobals.ip.u4s.fd,
                             caglobals.ip.m6.fd, caglobals.ip.m6s.fd,
                             caglobals.ip.m4.fd, caglobals.ip.m4s.fd);

    // create pipe for fast shutdown
    CAInitializePipe();
    CHECKFD(caglobals.ip.shutdownFds[0]);
    CHECKFD(caglobals.ip.shutdownFds[1]);

    // create source of network interface change notifications
    CAInitializeNetlink();

    caglobals.ip.selectTimeout = CAGetPollingInterval(caglobals.ip.selectTimeout);

    CAApplyInterfaces();

    caglobals.ip.terminate = false;
    res = ca_thread_pool_add_task(threadPool, CAReceiveHandler, NULL);
    if (CA_STATUS_OK != res)
    {
        OIC_LOG(ERROR, TAG, "thread_pool_add_task failed");
        return res;
    }
    OIC_LOG(DEBUG, TAG, "CAReceiveHandler thread started successfully.");

    caglobals.ip.started = true;
    return CA_STATUS_OK;
}
Esempio n. 18
0
int 
old_select(int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       struct timeval *timeout)
{
  int fd;
  fd_set newreadfds, newwritefds, newexceptfds;
  unsigned int wait_ticks = 0;
  unsigned int t,s;
  int total = 0;
  struct file *filp;
  int error;
  int traversed = 0;
  DPRINTF(SYS_LEVEL,("select: entering, timeout %d\n",
		     (timeout != NULL) ? (int) timeout->tv_sec : 0));

  if (timeout != NULL) {
	DPRINTF(SYS_LEVEL,
		("setting waiting time: sec: %d, usec: %d\n",
		 (int) timeout->tv_sec, (int)timeout->tv_usec));
	wait_ticks = ((1000000/RATE) * timeout->tv_sec)
	  + timeout->tv_usec/RATE;
  }
  
  FD_ZERO(&newreadfds);
  FD_ZERO(&newwritefds);
  FD_ZERO(&newexceptfds);

  t = TICKS;
  width = MIN(width,NR_OPEN);
  do {
    DPRINTF(SYS_LEVEL,("polling: %d ticks\n",wait_ticks));
    for (fd = 0; fd <= width; fd++) {
      /* maybe should be using min(width,fdtable_max) */
      /*	DPRINTF(SYS_LEVEL,("testing fd.%d\n",fd));*/
      
      if ((readfds  && FD_ISSET(fd,readfds))) {
	if (!traversed) {
	  CHECKFD(fd);
	}
	filp = __current->fd[fd];
	error = CHECKOP(filp,select);
	assert(error);
	error = DOOP(filp,select,(filp,SELECT_READ));
	if (error) {FD_SET(fd,&newreadfds); total++;}
      }
      if ((writefds  && FD_ISSET(fd,writefds))) {
	if (!traversed) {
	  CHECKFD(fd);
	}
	filp = __current->fd[fd];
	error = CHECKOP(filp,select);
	assert(error);
	error = DOOP(filp,select,(filp,SELECT_WRITE));
	if (error) {FD_SET(fd,&newwritefds); total++;}
      }
    }
    traversed++;

    s = TICKS;
    
    if (total > 0) {
      if (readfds) copyfds(readfds,&newreadfds,width);
      if (writefds) copyfds(writefds,&newwritefds,width);
      return(total);
    }

    usleep(100000);
    
  } while(s < t + wait_ticks || timeout == NULL);
  return(total);
}