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