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