ProFTPD Developer's Guide: Timers

ProFTPD Version 1.2


Table of Contents

Timers
The core code provides a mechanism for setting timers, which can be used to set time limits on actions, or to perform actions on a recurring basis. The main functions to use when dealing with timers are defined in timers.h:

  int add_timer(int seconds, int timerno, module *mod, callback_t cb);
  int remove_timer(int timerno, module *mod);
  int reset_timer(int timerno, module *mod);
  void pr_alarms_block(void);
  void pr_alarms_unblock(void);

For the add_timer() function, seconds is the length of the timer, timerno is an arbitrary timer ID, mod is a pointer to the module registering the timer, and cb is the function to be invoked once the timer has expired. If timerno is -1, the timer will be assigned a "dynamic" timer number greater than 1024. This "dynamic" number is not random; rather, it starts at 1024, as is incremented for every timer created with a timerno of -1. This function returns the given timerno, and has no error return value.

The remove_timer() function does just that, removing every timer with the given ID timerno registered by module mod. Note that for this function, and for reset_timer, the macro ANY_MODULE may be given for mod, in which case every timer with the given timerno will be removed. If a matching timer is found, it's timerno is returned, otherwise 0 is returned.

The reset_timer() function is also pretty self-explanatory, resetting the timer with the given timerno from module mod. If a matching timer is found, that timer's timerno is returned, otherwise 0 is returned.

The pr_alarms_block() function is used to prevent timers from firing. Timers that would have fired while blocked are noted, and will be fired once alarms are unblocked. This is to be used in critical sections of code which cannot stand interruption.

As it notes in the comments in timers.c, the blocking of alarms is done manually, rather than via system calls, to allow for easier signal handling, portability, detection of the number of blocked alarms, and to allow for nested block/unblock calls.

The pr_alarms_unblock() function allows alarms to once again be delivered. Any pending alarms that should have been delivered while alarms were blocked are handled.

Example Timer Callback
This excerpt from mod_auth.c shows the TimeoutLogin timer implementation:

int login_timeout_cb(CALLBACK_FRAME) {
  /* Is this the proper behavior when timing out? */
  pr_response_send_async(R_421,
                      "Login Timeout (%d seconds): "
                      "closing control connection.",
                      TimeoutLogin);
  
  session_exit(PR_LOG_NOTICE, "FTP login timed out, disconnected.", 0, NULL);
 
  /* Don't restart the timer */ 
  return 0;
}

The return value of the timer callback function is critical: if 0 is returned, the timer is done, and will not fire again. If non-zero, the timer is restarted, and will again be invoked once the configured length of time has passed.

The CALLBACK_FRAME macro is #defined to be:

  #define CALLBACK_FRAME          LPARAM p1,LPARAM p2,LPARAM p3,void *data
where LPARAM is typedefd to be:
  typedef unsigned long LPARAM;

Ostensibly, LPARAM is the way it is because it is the " longest bitsize compatible with a scalar and largest pointer". However, it does evoke compiler warnings if pointers and data are passed this way.

Another thing of which to be aware is the use of the CALLBACK_FRAME macro: it defines your function argument variable names a priori, which can be easy to overlook when you're trying to figure out the name of the variables to use in your timer callback function. Unfortunately, most of the current timer callback functions in the core code ignore the callback variables. When used, p1 will always be zero (with the current code), p2 will be the timerno of the timer, p3 will be time elapsed on the timer (which should be less than or equal to zero), and data will be NULL.

static int auth_init_child(void) {
  /* Start the login timer */
  if (TimeoutLogin)
    add_timer(TimeoutLogin, TIMER_LOGIN, &auth_module, login_timer_cb);
   
  if (get_param_ptr(main_server->conf, "DisplayConnect", FALSE) != NULL)
    _do_user_counts();
   
  return 0;
}

This use of add_timer() illustrate how one registers one's timer callback function, and starts the timer running. Once the child process has forked and mod_auth's child initialization function called, the client has until the timer expires to successfully authenticate.

When adding a timer, watch out for these predefined timer number/IDs:

  #define TIMER_LOGIN               1
  #define TIMER_IDLE                2
  #define TIMER_NOXFER              3
  #define TIMER_STALLED             4
Note that there is no requirement that your timer number be a preprocessor macro. The author has successfully used randomly generated numbers as unique timer identifiers, so that each timer could have custom actions and values.

How Timers Work
The timer mechanism is based on SIGALRM and a single signal handler for that signal. How then can multiple timers all use one signal handler? A linked list of timers is maintained, and that list is checked every time SIGARLM is received. For each timer, the amount of time that has expired is calculated, and if the specifier timer length has been reached, that timer's callback is invoked.

One caveat to keep in mind is that timers do not provide fine-grained time resolution, and are not meant for precise work. They do well for approximate timing-based sorts of activities.

When add_timer() is called, a list of old, recyclable timers is checked, to see if any are available. If none are avaiable, a new timer is allocated. The timer is given the caller's parameters, then inserted into the active timer list.

At present, the callback function is invoked thus, from src/timers.c:86:

  t->callback(t->interval, t->timerno, t->interval - t->count, t->mod) == 0 {
This shows what is actually passed as the arguments in the callback_t.

Be aware that the pending signal set for a process is reset when fork() is called. This is why modules should and do register timers for the child process in their respective child initialization functions.

Table of Contents



Author: $Author: castaglia $
Last Updated: $Date: 2003/01/02 18:36:35 $


© Copyright 2000-2003 TJ Saunders
All Rights Reserved