1 /*
2 notify  --  primitive notification service implementing a subset of the SunOS
3             (sunview/XView) notifier.
4 
5 Copyright 1993 by AT&T Bell Laboratories; all rights reserved
6 */
7 
8 #include <sys/time.h>
9 #include <signal.h>
10 #include <stdio.h>     /*DEBUG, fprintf() */
11 #include <stdlib.h>    /* malloc() */
12 #include <unistd.h>    /* select() */
13 #include <errno.h>     /* EINTR,    Added by Akira 12/11/01 */
14 #include <string.h>    /* memset(), Added by Akira 12/11/01 */
15 #include "sysdep.h"
16 #include "notify.h"
17 #include "multimer.h"
18 #include "ansi.h"
19 
20 #ifdef hp
21 #define CAST int *
22 #else
23 #define CAST fd_set *
24 #endif
25 
26 typedef struct event_t {
27   struct event_t *next;
28   Notify_client client;
29   Notify_func_input func;
30   int fd;
31   enum type_t {N_input, N_itimer} type;
32 } event_t;
33 
34 static event_t *el;   /* event list */
35 static int max_fd;    /* highest file descriptor used */
36 static fd_set Readfds, Writefds, Exceptfds;
37 static int stop;
38 
39 /* signal list */
40 static struct {
41     Notify_client client;
42     Notify_func_signal signal_func;
43     Notify_signal_mode when;
44 } s[NSIG];
45 
46 
search(Notify_client client,int fd,enum type_t type,event_t ** prev)47 static event_t *search(
48   Notify_client client,  /* ignored if fd >= 0 */
49   int fd,
50   enum type_t type,
51   event_t **prev         /* element before target */
52 )
53 {
54   event_t *e;
55 
56   *prev = 0;
57   for (e = el; e; *prev = e, e = e->next) {
58     if (e->type == type && e->fd == fd && (fd >= 0 || e->client == client))
59       return e;
60   }
61   return 0;
62 } /* search */
63 
64 /* Clear all three "fd-set"s.
65  * This is called only once in the first call for initilaization.
66  */
check_clr_fd(void)67 void check_clr_fd(void)
68 {
69   static int cleared = -1;
70 
71   if (cleared == -1) {
72     FD_ZERO(&Readfds);
73     FD_ZERO(&Writefds);
74     FD_ZERO(&Exceptfds);
75     cleared = 0;  /* set for only once */
76   }
77 }
78 
79 /*
80 * Find maximum file descriptor number used.
81 */
set_max_fd(void)82 static void set_max_fd(void)
83 {
84   event_t *e;
85 
86   max_fd = 0;
87   for (e = el; e; e = e->next) {
88     if (e->fd > max_fd) max_fd = e->fd;
89   }
90 } /* set_max */
91 
92 
93 /*
94 * Install input handler function 'func' for file descriptor 'fd'.
95 * func=NOTIFY_FUNC_NULL removes handler.
96 */
notify_set_input_func(Notify_client client,Notify_func_input func,int fd)97 Notify_func_input notify_set_input_func(
98   Notify_client client,   /* argument passed to function */
99   Notify_func_input func, /* function to be called: func(client, fd) */
100   int fd)                 /* file descriptor */
101 {
102   event_t *e, *prev;
103 
104   check_clr_fd();
105   e = search(client, fd, N_input, &prev);
106   if (!e) {  /* create new event */
107     if (func == NOTIFY_FUNC_INPUT_NULL) return func;
108     e = (event_t *)malloc(sizeof(event_t));
109     if (!e) return 0;
110     e->next = el;   /* put at head of list */
111     el = e;
112     if (fd > max_fd) max_fd = fd;
113     e->type   = N_input;
114     e->client = client;
115     e->func   = func;
116     e->fd     = fd;
117     FD_SET(fd, &Readfds);
118   }
119   else {
120     if (func == NOTIFY_FUNC_INPUT_NULL) {
121       FD_CLR(fd, &Readfds);
122       if (prev) prev->next = e->next;
123       else el = e->next;
124       free(e);
125       set_max_fd();  /* find new maximum fd */
126       return func;
127     }
128     else e->func = func;
129   }
130   return 0;
131 } /* notify_set_input_func */
132 
133 
134 /*
135 * Interval timer initialization.
136 */
notify_set_itimer_func(Notify_client client,Notify_func timer_func,int which,struct itimerval * value,struct itimerval * ovalue)137 Notify_func notify_set_itimer_func(
138   Notify_client client,      /* value passed to function */
139   Notify_func timer_func,    /* function to be called */
140   int which,                 /* not used */
141   struct itimerval *value,   /* interval */
142   struct itimerval *ovalue)  /* not used */
143 {
144   struct timeval *t;
145 
146   /* set relative to now */
147   t = timer_set(value ? &(value->it_value) : 0, timer_func, client, 1);
148   *t = value->it_interval;
149   return 0;   /* kludge */
150 } /* notify_set_itimer_func */
151 
152 
153 /*
154 * Don't wait if there are no other events.
155 */
timer_get_pending(struct timeval * timeout,int max_fd)156 static struct timeval *timer_get_pending(struct timeval *timeout, int max_fd)
157 {
158   struct timeval *tvp;
159 
160   tvp = timer_get(timeout);
161   if (!tvp && !max_fd) {
162     timeout->tv_sec = timeout->tv_usec = 0;
163     notify_stop();
164     return timeout;     /* added by Akira 12/11/01 */
165   }
166   if (!tvp)
167     return timeout;     /* return 0.100000 sec, by Akira 12/11/01 */
168 
169   return tvp;           /* return first timer event, by Akira 12/11/01 */
170 } /* timer_get_pending */
171 
172 
173 /*
174 * Main loop. Return 0 if stopped, -1 if error.
175 */
notify_start(void)176 Notify_error notify_start(void)
177 {
178   struct timeval timeout;
179   event_t *e, *prev;
180   int fd;
181   int found;
182   fd_set readfds, writefds, exceptfds;
183 
184   stop = 0;
185   while (!stop) {
186     readfds   = Readfds;
187     writefds  = Writefds;
188     exceptfds = Exceptfds;
189     timeout.tv_sec  = 0;
190     timeout.tv_usec = 100000;     /* modified from 0 by Akira 12/11/01 */
191 
192     found = select(max_fd+1, (CAST)&readfds, (CAST)&writefds, (CAST)&exceptfds,
193                     timer_get_pending(&timeout, max_fd));
194 
195 #if defined(WIN32)
196     if (found < 0 && WSAGetLastError() != WSAEINVAL) {
197       fprintf(stderr, "select(): WSAErrono: %d\n", WSAGetLastError());
198       return -1;
199     }
200 #else
201     /* For not to catch signal as an error
202      * EINTR added by Akira T. 12/11/01 */
203     if (found < 0 && errno != EINTR) {
204       fprintf(stderr, "Timeout: %lu.%06lu\n", timeout.tv_sec, timeout.tv_usec);
205       return -1;
206     }
207 #endif
208 
209     /* found = 0: just a timer -> do nothing,
210                   timer_get() will execute the handler */
211     /* found > 0: scan the fd_event */
212     if (found) {
213       for (fd = 0; fd <= max_fd && found > 0; fd++) {
214         if (FD_ISSET(fd, &readfds)) {
215           e = search((Notify_client)0, fd, N_input, &prev);
216           if (e) {
217             if (e->func) (e->func)(e->client, fd);
218           }
219           else {
220             fprintf(stderr, "No handler for fd %d\n", fd);
221           }
222         }
223       } /* for() */
224     }
225 
226   } /* while() */
227 
228   return 0;
229 } /* notify_start */
230 
231 
232 /*
233 * Stop the event loop. Noticed only at next event.
234 */
notify_stop(void)235 Notify_error notify_stop(void)
236 {
237   stop = 1;
238   return 0;  /* kludge */
239 } /* notify_stop */
240 
241 
242 /*
243 * Actually invoked by signal(). Calls user-defined handler.
244 */
sig_handler(int sig)245 static void sig_handler(int sig)
246 {
247   (*s[sig].signal_func)(s[sig].client, sig, s[sig].when);
248 } /* sig_handler */
249 
250 
251 /*
252 * Install signal handler.
253 */
notify_set_signal_func(Notify_client client,Notify_func_signal signal_func,int sig,Notify_signal_mode when)254 Notify_func_signal notify_set_signal_func(Notify_client client,
255   Notify_func_signal signal_func, int sig, Notify_signal_mode when)
256 {
257   Notify_func_signal old_func = s[sig].signal_func;
258   s[sig].signal_func = signal_func;
259   s[sig].when        = when;
260   s[sig].client      = client;
261 
262   signal(sig, sig_handler);
263   return old_func;
264 } /* notify_set_signal_func */
265 
266 
267 /*
268  * Sets the value of fd_sets Rreadfds, Writefds, Exceptfds.
269  */
notify_set_socket(int sock,int flag)270 void notify_set_socket(int sock, int flag)
271 {
272   check_clr_fd();
273   switch (flag) {
274   case 0:
275     FD_SET(sock, &Readfds);
276     break;
277   case 1:
278     FD_SET(sock, &Writefds);
279     break;
280   case 2:
281     FD_SET(sock, &Exceptfds);
282     break;
283   default:
284     break;
285   }
286 } /* notify_set_socket */
287