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