1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 1997, 1998 Public Flood Software
4 * Copyright (c) 2001-2016 The ProFTPD Project team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * BUT witHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 *
20 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21 * and other respective copyright holders give permission to link this program
22 * with OpenSSL, and distribute the resulting executable, without including
23 * the source code for OpenSSL in the source distribution.
24 */
25
26 /* Timer system, based on alarm() and SIGALRM. */
27
28 #include "conf.h"
29
30 /* From src/main.c */
31 extern volatile unsigned int recvd_signal_flags;
32
33 struct timer {
34 struct timer *next, *prev;
35
36 long count; /* Amount of time remaining */
37 long interval; /* Original length of timer */
38
39 int timerno; /* Caller dependent timer number */
40 module *mod; /* Module owning this timer */
41 callback_t callback; /* Function to callback */
42 char remove; /* Internal use */
43
44 const char *desc; /* Description of timer, provided by caller */
45 };
46
47 #define PR_TIMER_DYNAMIC_TIMERNO 1024
48
49 static int _current_timeout = 0;
50 static int _total_time = 0;
51 static int _sleep_sem = 0;
52 static int alarms_blocked = 0, alarm_pending = 0;
53 static xaset_t *timers = NULL;
54 static xaset_t *recycled = NULL;
55 static xaset_t *free_timers = NULL;
56 static int _indispatch = 0;
57 static int dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO;
58 static unsigned int nalarms = 0;
59 static time_t _alarmed_time = 0;
60
61 static pool *timer_pool = NULL;
62
63 static const char *trace_channel = "timer";
64
timer_cmp(struct timer * t1,struct timer * t2)65 static int timer_cmp(struct timer *t1, struct timer *t2) {
66 if (t1->count < t2->count) {
67 return -1;
68 }
69
70 if (t1->count > t2->count) {
71 return 1;
72 }
73
74 return 0;
75 }
76
77 /* This function does the work of iterating through the list of registered
78 * timers, checking to see if their callbacks should be invoked and whether
79 * they should be removed from the registration list. Its return value is
80 * the amount of time remaining on the first timer in the list.
81 */
process_timers(int elapsed)82 static int process_timers(int elapsed) {
83 struct timer *t = NULL, *next = NULL;
84 int res = 0;
85
86 if (recycled == NULL) {
87 recycled = xaset_create(timer_pool, NULL);
88 }
89
90 if (elapsed == 0 &&
91 recycled->xas_list == NULL) {
92 if (timers == NULL) {
93 return 0;
94 }
95
96 if (timers->xas_list != NULL) {
97 /* The value we return is a proposed timeout, for the next call to
98 * alarm(3). We start with the simple count of timers in our list.
99 *
100 * But then we reduce the number; some of the timers' intervals may
101 * less than the number of total timers.
102 */
103 res = ((struct timer *) timers->xas_list)->count;
104 if (res > 5) {
105 res = 5;
106 }
107 }
108
109 return res;
110 }
111
112 /* Critical code, no interruptions please */
113 if (_indispatch) {
114 return 0;
115 }
116
117 pr_alarms_block();
118 _indispatch++;
119
120 if (elapsed) {
121 for (t = (struct timer *) timers->xas_list; t; t = next) {
122 /* If this timer has already been handled, skip */
123 next = t->next;
124
125 if (t->remove) {
126 /* Move the timer onto the free_timers chain, for later reuse. */
127 xaset_remove(timers, (xasetmember_t *) t);
128 xaset_insert(free_timers, (xasetmember_t *) t);
129
130 } else if ((t->count -= elapsed) <= 0) {
131 /* This timer's interval has elapsed, so trigger its callback. */
132
133 pr_trace_msg(trace_channel, 4,
134 "%ld %s for timer ID %d ('%s', for module '%s') elapsed, invoking "
135 "callback (%p)", t->interval,
136 t->interval != 1 ? "seconds" : "second", t->timerno,
137 t->desc ? t->desc : "<unknown>",
138 t->mod ? t->mod->name : "<none>", t->callback);
139
140 if (t->callback(t->interval, t->timerno, t->interval - t->count,
141 t->mod) == 0) {
142
143 /* A return value of zero means this timer is done, and can be
144 * removed.
145 */
146 xaset_remove(timers, (xasetmember_t *) t);
147 xaset_insert(free_timers, (xasetmember_t *) t);
148
149 } else {
150 /* A non-zero return value from a timer callback signals that
151 * the timer should be reused/restarted.
152 */
153 pr_trace_msg(trace_channel, 6,
154 "restarting timer ID %d ('%s'), as per callback", t->timerno,
155 t->desc ? t->desc : "<unknown>");
156
157 xaset_remove(timers, (xasetmember_t *) t);
158 t->count = t->interval;
159 xaset_insert(recycled, (xasetmember_t *) t);
160 }
161 }
162 }
163 }
164
165 /* Put the recycled timers back into the main timer list. */
166 t = (struct timer *) recycled->xas_list;
167 while (t != NULL) {
168 xaset_remove(recycled, (xasetmember_t *) t);
169 xaset_insert_sort(timers, (xasetmember_t *) t, TRUE);
170 t = (struct timer *) recycled->xas_list;
171 }
172
173 _indispatch--;
174 pr_alarms_unblock();
175
176 /* If no active timers remain in the list, there is no reason to set the
177 * SIGALRM handle.
178 */
179
180 if (timers->xas_list != NULL) {
181 /* The value we return is a proposed timeout, for the next call to
182 * alarm(3). We start with the simple count of timers in our list.
183 *
184 * But then we reduce the number; some of the timers' intervals may
185 * less than the number of total timers.
186 */
187 res = ((struct timer *) timers->xas_list)->count;
188 if (res > 5) {
189 res = 5;
190 }
191 }
192
193 return res;
194 }
195
sig_alarm(int signo)196 static RETSIGTYPE sig_alarm(int signo) {
197 struct sigaction act;
198
199 act.sa_handler = sig_alarm;
200 sigemptyset(&act.sa_mask);
201 act.sa_flags = 0;
202
203 #ifdef SA_INTERRUPT
204 act.sa_flags |= SA_INTERRUPT;
205 #endif
206
207 /* Install this handler for SIGALRM. */
208 if (sigaction(SIGALRM, &act, NULL) < 0) {
209 pr_log_pri(PR_LOG_WARNING,
210 "unable to install SIGALRM handler via sigaction(2): %s",
211 strerror(errno));
212 }
213
214 #ifdef HAVE_SIGINTERRUPT
215 if (siginterrupt(SIGALRM, 1) < 0) {
216 pr_log_pri(PR_LOG_WARNING,
217 "unable to allow SIGALRM to interrupt system calls: %s", strerror(errno));
218 }
219 #endif
220
221 recvd_signal_flags |= RECEIVED_SIG_ALRM;
222 nalarms++;
223
224 /* Reset the alarm */
225 _total_time += _current_timeout;
226 if (_current_timeout) {
227 _alarmed_time = time(NULL);
228 alarm(_current_timeout);
229 }
230 }
231
set_sig_alarm(void)232 static void set_sig_alarm(void) {
233 struct sigaction act;
234
235 act.sa_handler = sig_alarm;
236 sigemptyset(&act.sa_mask);
237 act.sa_flags = 0;
238 #ifdef SA_INTERRUPT
239 act.sa_flags |= SA_INTERRUPT;
240 #endif
241
242 /* Install this handler for SIGALRM. */
243 if (sigaction(SIGALRM, &act, NULL) < 0) {
244 pr_log_pri(PR_LOG_WARNING,
245 "unable to install SIGALRM handler via sigaction(2): %s",
246 strerror(errno));
247 }
248
249 #ifdef HAVE_SIGINTERRUPT
250 if (siginterrupt(SIGALRM, 1) < 0) {
251 pr_log_pri(PR_LOG_WARNING,
252 "unable to allow SIGALRM to interrupt system calls: %s", strerror(errno));
253 }
254 #endif
255 }
256
handle_alarm(void)257 void handle_alarm(void) {
258 int new_timeout = 0;
259
260 /* We need to adjust for any time that might be remaining on the alarm,
261 * in case we were called in order to change alarm durations. Note
262 * that rapid-fire calling of this function will probably screw
263 * up the already poor resolution of alarm() _horribly_. Oh well,
264 * this shouldn't be used for any precise work anyway, it's only
265 * for modules to perform approximate timing.
266 */
267
268 /* It's possible that alarms are blocked when this function is
269 * called, if so, increment alarm_pending and exit swiftly.
270 */
271 while (nalarms) {
272 nalarms = 0;
273
274 if (!alarms_blocked) {
275 int alarm_elapsed;
276 time_t now;
277
278 /* Clear any pending ALRM signals. */
279 alarm(0);
280
281 /* Determine how much time has elapsed since we last processed timers. */
282 time(&now);
283 alarm_elapsed = _alarmed_time > 0 ? (int) (now - _alarmed_time) : 0;
284
285 new_timeout = _total_time + alarm_elapsed;
286 _total_time = 0;
287 new_timeout = process_timers(new_timeout);
288
289 _alarmed_time = now;
290 alarm(_current_timeout = new_timeout);
291
292 } else {
293 alarm_pending++;
294 }
295 }
296 }
297
pr_timer_reset(int timerno,module * mod)298 int pr_timer_reset(int timerno, module *mod) {
299 struct timer *t = NULL;
300
301 if (timers == NULL) {
302 errno = EPERM;
303 return -1;
304 }
305
306 if (_indispatch) {
307 errno = EINTR;
308 return -1;
309 }
310
311 pr_alarms_block();
312
313 if (recycled == NULL) {
314 recycled = xaset_create(timer_pool, NULL);
315 }
316
317 for (t = (struct timer *) timers->xas_list; t; t = t->next) {
318 if (t->timerno == timerno &&
319 (t->mod == mod || mod == ANY_MODULE)) {
320 t->count = t->interval;
321 xaset_remove(timers, (xasetmember_t *) t);
322 xaset_insert(recycled, (xasetmember_t *) t);
323 nalarms++;
324
325 /* The handle_alarm() function also readjusts the timers lists
326 * as part of its processing, so it needs to be called when a timer
327 * is reset.
328 */
329 handle_alarm();
330 break;
331 }
332 }
333
334 pr_alarms_unblock();
335
336 if (t != NULL) {
337 pr_trace_msg(trace_channel, 7, "reset timer ID %d ('%s', for module '%s')",
338 t->timerno, t->desc, t->mod ? t->mod->name : "[none]");
339 return t->timerno;
340 }
341
342 return 0;
343 }
344
pr_timer_remove(int timerno,module * mod)345 int pr_timer_remove(int timerno, module *mod) {
346 struct timer *t = NULL, *tnext = NULL;
347 int nremoved = 0;
348
349 /* If there are no timers currently registered, do nothing. */
350 if (!timers)
351 return 0;
352
353 pr_alarms_block();
354
355 for (t = (struct timer *) timers->xas_list; t; t = tnext) {
356 tnext = t->next;
357
358 if ((timerno < 0 || t->timerno == timerno) &&
359 (mod == ANY_MODULE || t->mod == mod)) {
360 nremoved++;
361
362 if (_indispatch) {
363 t->remove++;
364
365 } else {
366 xaset_remove(timers, (xasetmember_t *) t);
367 xaset_insert(free_timers, (xasetmember_t *) t);
368 nalarms++;
369
370 /* The handle_alarm() function also readjusts the timers lists
371 * as part of its processing, so it needs to be called when a timer
372 * is removed.
373 */
374 handle_alarm();
375 }
376
377 pr_trace_msg(trace_channel, 7,
378 "removed timer ID %d ('%s', for module '%s')", t->timerno, t->desc,
379 t->mod ? t->mod->name : "[none]");
380 }
381
382 /* If we are removing a specific timer, break out of the loop now.
383 * Otherwise, keep removing any matching timers.
384 */
385 if (nremoved > 0 &&
386 timerno >= 0) {
387 break;
388 }
389 }
390
391 pr_alarms_unblock();
392
393 if (nremoved == 0) {
394 errno = ENOENT;
395 return -1;
396 }
397
398 /* If we removed a specific timer because of the given timerno, return
399 * that timerno value.
400 */
401 if (timerno >= 0) {
402 return timerno;
403 }
404
405 return nremoved;
406 }
407
pr_timer_add(int seconds,int timerno,module * mod,callback_t cb,const char * desc)408 int pr_timer_add(int seconds, int timerno, module *mod, callback_t cb,
409 const char *desc) {
410 struct timer *t = NULL;
411
412 if (seconds <= 0 ||
413 cb == NULL ||
414 desc == NULL) {
415 errno = EINVAL;
416 return -1;
417 }
418
419 if (!timers)
420 timers = xaset_create(timer_pool, (XASET_COMPARE) timer_cmp);
421
422 /* Check to see that, if specified, the timerno is not already in use. */
423 if (timerno >= 0) {
424 for (t = (struct timer *) timers->xas_list; t; t = t->next) {
425 if (t->timerno == timerno) {
426 errno = EPERM;
427 return -1;
428 }
429 }
430 }
431
432 if (!free_timers)
433 free_timers = xaset_create(timer_pool, NULL);
434
435 /* Try to use an old timer first */
436 pr_alarms_block();
437 t = (struct timer *) free_timers->xas_list;
438 if (t != NULL) {
439 xaset_remove(free_timers, (xasetmember_t *) t);
440
441 } else {
442 if (timer_pool == NULL) {
443 timer_pool = make_sub_pool(permanent_pool);
444 pr_pool_tag(timer_pool, "Timer Pool");
445 }
446
447 /* Must allocate a new one */
448 t = palloc(timer_pool, sizeof(struct timer));
449 }
450
451 if (timerno < 0) {
452 /* Dynamic timer */
453 if (dynamic_timerno < PR_TIMER_DYNAMIC_TIMERNO) {
454 dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO;
455 }
456
457 timerno = dynamic_timerno++;
458 }
459
460 t->timerno = timerno;
461 t->count = t->interval = seconds;
462 t->callback = cb;
463 t->mod = mod;
464 t->remove = 0;
465 t->desc = desc;
466
467 /* If called while _indispatch, add to the recycled list to prevent
468 * list corruption
469 */
470
471 if (_indispatch) {
472 if (!recycled)
473 recycled = xaset_create(timer_pool, NULL);
474 xaset_insert(recycled, (xasetmember_t *) t);
475
476 } else {
477 xaset_insert_sort(timers, (xasetmember_t *) t, TRUE);
478 nalarms++;
479 set_sig_alarm();
480
481 /* The handle_alarm() function also readjusts the timers lists
482 * as part of its processing, so it needs to be called when a timer
483 * is added.
484 */
485 handle_alarm();
486 }
487
488 pr_alarms_unblock();
489
490 pr_trace_msg(trace_channel, 7, "added timer ID %d ('%s', for module '%s'), "
491 "triggering in %ld %s", t->timerno, t->desc,
492 t->mod ? t->mod->name : "[none]", t->interval,
493 t->interval != 1 ? "seconds" : "second");
494 return timerno;
495 }
496
497 /* Alarm blocking. This is done manually rather than with syscalls,
498 * so as to allow for easier signal handling, portability and
499 * detecting the number of blocked alarms, as well as nesting the
500 * block/unblock functions.
501 */
502
pr_alarms_block(void)503 void pr_alarms_block(void) {
504 ++alarms_blocked;
505 }
506
pr_alarms_unblock(void)507 void pr_alarms_unblock(void) {
508 --alarms_blocked;
509 if (alarms_blocked == 0 && alarm_pending) {
510 alarm_pending = 0;
511 nalarms++;
512 handle_alarm();
513 }
514 }
515
sleep_cb(CALLBACK_FRAME)516 static int sleep_cb(CALLBACK_FRAME) {
517 _sleep_sem++;
518 return 0;
519 }
520
pr_timer_sleep(int seconds)521 int pr_timer_sleep(int seconds) {
522 int timerno = 0;
523 sigset_t oset;
524
525 _sleep_sem = 0;
526
527 if (alarms_blocked || _indispatch) {
528 errno = EPERM;
529 return -1;
530 }
531
532 timerno = pr_timer_add(seconds, -1, NULL, sleep_cb, "sleep");
533 if (timerno == -1)
534 return -1;
535
536 sigemptyset(&oset);
537 while (!_sleep_sem) {
538 sigsuspend(&oset);
539 handle_alarm();
540 }
541
542 return 0;
543 }
544
pr_timer_usleep(unsigned long usecs)545 int pr_timer_usleep(unsigned long usecs) {
546 struct timeval tv;
547
548 if (usecs == 0) {
549 errno = EINVAL;
550 return -1;
551 }
552
553 tv.tv_sec = (usecs / 1000000);
554 tv.tv_usec = (usecs - (tv.tv_sec * 1000000));
555
556 pr_signals_block();
557 (void) select(0, NULL, NULL, NULL, &tv);
558 pr_signals_unblock();
559
560 return 0;
561 }
562
timers_init(void)563 void timers_init(void) {
564
565 /* Reset some of the key static variables. */
566 _current_timeout = 0;
567 _total_time = 0;
568 nalarms = 0;
569 _alarmed_time = 0;
570 dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO;
571
572 /* Don't inherit the parent's timer lists. */
573 timers = NULL;
574 recycled = NULL;
575 free_timers = NULL;
576
577 /* Reset the timer pool. */
578 if (timer_pool) {
579 destroy_pool(timer_pool);
580 }
581
582 timer_pool = make_sub_pool(permanent_pool);
583 pr_pool_tag(timer_pool, "Timer Pool");
584
585 return;
586 }
587