]> Pileus Git - ~andy/fetchmail/blob - idle.c
Add new gai.c debug source.
[~andy/fetchmail] / idle.c
1 /*****************************************************************************
2
3 NAME:
4    idle.c -- code for interruptible delays without sleep(3).
5
6 ENTRY POINTS:
7    interruptible_idle() -- delay for some time, interruptible by signal.
8
9 THEORY:
10    Sometimes you need more than one time delay per program, so alarm(3)
11 won't cut it.  This code illustrates time delays with select(2).
12
13 AUTHOR:
14    Eric S. Raymond <esr@thyrsus.com>, 1997.  This source code example
15 is part of fetchmail and the Unix Cookbook, and are released under the
16 MIT license.  Compile with -DMAIN to build the demonstrator.
17
18 ******************************************************************************/
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <fetchmail.h>  /* for ROOT_UID */
25
26 #ifndef TRUE
27 #define TRUE 1
28 #define FALSE 0
29 #endif
30
31 volatile int lastsig;           /* last signal received */
32
33 #ifdef SLEEP_WITH_ALARM
34 /*
35  * The function of this variable is to remove the window during which a
36  * SIGALRM can hose the code (ALARM is triggered *before* pause() is called).
37  * This is a bit of a kluge; the real right thing would use sigprocmask(),
38  * sigsuspend().  This workaround lets the interval timer trigger the first
39  * alarm after the required interval and will then generate alarms
40  * seconds until it is certain that the critical section (ie., the window)
41  * is exited.
42  */
43 static sig_atomic_t     alarm_latch = FALSE;
44
45 RETSIGTYPE gotsigalrm(int sig)
46 {
47     set_signal_handler(sig, gotsigalrm);
48     lastsig = sig;
49     alarm_latch = TRUE;
50 }
51 #endif /* SLEEP_WITH_ALARM */
52
53 #ifdef __EMX__
54 /* Various EMX-specific definitions */
55 static int itimerflag;
56
57 void itimerthread(void* dummy)
58 {
59     if (outlevel >= O_VERBOSE)
60         report(stderr, 
61                GT_("fetchmail: thread sleeping for %d sec.\n"), poll_interval);
62     while(1)
63     {
64         _sleep2(poll_interval*1000);
65         kill((getpid()), SIGALRM);
66     }
67 }
68 #endif
69
70 int interruptible_idle(int seconds)
71 /* time for a pause in the action; return TRUE if awakened by signal */
72 {
73     int awoken = FALSE;
74
75 #ifndef __EMX__
76 #ifdef SLEEP_WITH_ALARM         /* not normally on */
77     /*
78      * We can't use sleep(3) here because we need an alarm(3)
79      * equivalent in order to implement server nonresponse timeout.
80      * We'll just assume setitimer(2) is available since fetchmail
81      * has to have a BSDoid socket layer to work at all.
82      */
83     /* 
84      * This code stopped working under glibc-2, apparently due
85      * to the change in signal(2) semantics.  (The siginterrupt
86      * line, added later, should fix this problem.) John Stracke
87      * <francis@netscape.com> wrote:
88      *
89      * The problem seems to be that, after hitting the interval
90      * timer while talking to the server, the process no longer
91      * responds to SIGALRM.  I put in printf()s to see when it
92      * reached the pause() for the poll interval, and I checked
93      * the return from setitimer(), and everything seemed to be
94      * working fine, except that the pause() just ignored SIGALRM.
95      * I thought maybe the itimer wasn't being fired, so I hit
96      * it with a SIGALRM from the command line, and it ignored
97      * that, too.  SIGUSR1 woke it up just fine, and it proceeded
98      * to repoll--but, when the dummy server didn't respond, it
99      * never timed out, and SIGALRM wouldn't make it.
100      *
101      * (continued below...)
102      */
103     {
104     struct itimerval ntimeout;
105
106     ntimeout.it_interval.tv_sec = 5; /* repeat alarm every 5 secs */
107     ntimeout.it_interval.tv_usec = 0;
108     ntimeout.it_value.tv_sec  = seconds;
109     ntimeout.it_value.tv_usec = 0;
110
111     alarm_latch = FALSE;
112     set_signal_handler(SIGALRM, gotsigalrm);    /* first trap signals */
113     setitimer(ITIMER_REAL,&ntimeout,NULL);      /* then start timer */
114     /* there is a very small window between the next two lines */
115     /* which could result in a deadlock.  But this will now be  */
116     /* caught by periodic alarms (see it_interval) */
117     if (!alarm_latch)
118         pause();
119     /* stop timer */
120     ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
121     ntimeout.it_value.tv_sec  = ntimeout.it_value.tv_usec = 0;
122     setitimer(ITIMER_REAL,&ntimeout,NULL);      /* now stop timer */
123     set_signal_handler(SIGALRM, SIG_IGN);
124     }
125 #else
126     /* 
127      * So the workaround I used is to make it sleep by using
128      * select() instead of setitimer()/pause().  select() is
129      * perfectly happy being called with a timeout and
130      * no file descriptors; it just sleeps until it hits the
131      * timeout.  The only concern I had was that it might
132      * implement its timeout with SIGALRM--there are some
133      * Unices where this is done, because select() is a library
134      * function--but apparently not.
135      */
136     {
137     struct timeval timeout;
138
139     timeout.tv_sec = seconds;
140     timeout.tv_usec = 0;
141     do {
142         lastsig = 0;
143         select(0,0,0,0, &timeout);
144     } while (lastsig == SIGCHLD);
145     }
146 #endif
147 #else /* EMX */
148     alarm_latch = FALSE;
149     set_signal_handler(SIGALRM, gotsigalrm);
150     _beginthread(itimerthread, NULL, 32768, NULL);
151     /* see similar code above */
152     if (!alarm_latch)
153         pause();
154     set_signal_handler(SIGALRM, SIG_IGN);
155 #endif /* ! EMX */
156     if (lastsig == SIGUSR1 || ((seconds && getuid() == ROOT_UID)
157         && lastsig == SIGHUP))
158        awoken = TRUE;
159
160     /* now lock out interrupts again */
161     set_signal_handler(SIGUSR1, SIG_IGN);
162     if (getuid() == ROOT_UID)
163         set_signal_handler(SIGHUP, SIG_IGN);
164
165     return(awoken ? lastsig : 0);
166 }
167
168 #ifdef MAIN
169 int main(int argc, char **argv)
170 {
171     for (;;)
172     {
173         printf("How may I serve you, master?\n");
174         interruptible_idle(5);
175     }
176 }
177 #endif /* MAIN */
178
179 /* idle.c ends here */