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