|
| 1 | +/* |
| 2 | + +----------------------------------------------------------------------+ |
| 3 | + | Copyright (c) The PHP Group | |
| 4 | + +----------------------------------------------------------------------+ |
| 5 | + | This source file is subject to version 3.01 of the PHP license, | |
| 6 | + | that is bundled with this package in the file LICENSE, and is | |
| 7 | + | available through the world-wide-web at the following url: | |
| 8 | + | https://www.php.net/license/3_01.txt | |
| 9 | + | If you did not receive a copy of the PHP license and are unable to | |
| 10 | + | obtain it through the world-wide-web, please send a note to | |
| 11 | + | license@php.net so we can mail you a copy immediately. | |
| 12 | + +----------------------------------------------------------------------+ |
| 13 | + | Author: Kévin Dunglas <kevin@dunglas.dev> | |
| 14 | + +----------------------------------------------------------------------+ |
| 15 | + */ |
| 16 | + |
| 17 | +#ifdef ZEND_MAX_EXECUTION_TIMERS |
| 18 | + |
| 19 | +#include <stdio.h> |
| 20 | +#include <signal.h> |
| 21 | +#include <time.h> |
| 22 | +#include <unistd.h> |
| 23 | +#include <errno.h> |
| 24 | +#include <sys/syscall.h> |
| 25 | +#include <sys/types.h> |
| 26 | + |
| 27 | +#include "zend.h" |
| 28 | +#include "zend_globals.h" |
| 29 | + |
| 30 | +// Musl Libc defines this macro, glibc does not |
| 31 | +// According to "man 2 timer_create" this field should always be available, but it's not: https://sourceware.org/bugzilla/show_bug.cgi?id=27417 |
| 32 | +# ifndef sigev_notify_thread_id |
| 33 | +# define sigev_notify_thread_id _sigev_un._tid |
| 34 | +# endif |
| 35 | + |
| 36 | +ZEND_API void zend_max_execution_timer_init(void) /* {{{ */ |
| 37 | +{ |
| 38 | + struct sigevent sev; |
| 39 | + sev.sigev_notify = SIGEV_THREAD_ID; |
| 40 | + sev.sigev_value.sival_ptr = &EG(max_execution_timer_timer); |
| 41 | + sev.sigev_signo = SIGRTMIN; |
| 42 | + sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid); |
| 43 | + |
| 44 | + EG(pid) = getpid(); |
| 45 | + // Measure wall time instead of CPU time as originally planned now that it is possible https://github.com/php/php-src/pull/6504#issuecomment-1370303727 |
| 46 | + if (timer_create(CLOCK_BOOTTIME, &sev, &EG(max_execution_timer_timer)) != 0) { |
| 47 | + zend_strerror_noreturn(E_ERROR, errno, "Could not create timer"); |
| 48 | + } |
| 49 | + |
| 50 | +# ifdef MAX_EXECUTION_TIMERS_DEBUG |
| 51 | + fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(max_execution_timer_timer), sev.sigev_notify_thread_id); |
| 52 | +# endif |
| 53 | + |
| 54 | + sigaction(sev.sigev_signo, NULL, &EG(oldact)); |
| 55 | +} |
| 56 | +/* }}} */ |
| 57 | + |
| 58 | +void zend_max_execution_timer_settime(zend_long seconds) /* {{{ }*/ |
| 59 | +{ |
| 60 | + /* Timer not initialized or shutdown. */ |
| 61 | + if (!EG(pid)) { |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + timer_t timer = EG(max_execution_timer_timer); |
| 66 | + |
| 67 | + struct itimerspec its; |
| 68 | + its.it_value.tv_sec = seconds; |
| 69 | + its.it_value.tv_nsec = its.it_interval.tv_sec = its.it_interval.tv_nsec = 0; |
| 70 | + |
| 71 | +# ifdef MAX_EXECUTION_TIMERS_DEBUG |
| 72 | + fprintf(stderr, "Setting timer %#jx on thread %d (%ld seconds)...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid), seconds); |
| 73 | +# endif |
| 74 | + |
| 75 | + if (timer_settime(timer, 0, &its, NULL) != 0) { |
| 76 | + zend_strerror_noreturn(E_ERROR, errno, "Could not set timer"); |
| 77 | + } |
| 78 | +} |
| 79 | +/* }}} */ |
| 80 | + |
| 81 | +void zend_max_execution_timer_shutdown(void) /* {{{ */ |
| 82 | +{ |
| 83 | + /* Don't try to delete a timer created before a call to fork() */ |
| 84 | + if (EG(pid) != getpid()) { |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + EG(pid) = 0; |
| 89 | + |
| 90 | + timer_t timer = EG(max_execution_timer_timer); |
| 91 | + |
| 92 | +# ifdef MAX_EXECUTION_TIMERS_DEBUG |
| 93 | + fprintf(stderr, "Deleting timer %#jx on thread %d...\n", (uintmax_t) timer, (pid_t) syscall(SYS_gettid)); |
| 94 | +# endif |
| 95 | + |
| 96 | + int err = timer_delete(timer); |
| 97 | + if (err != 0) { |
| 98 | + zend_strerror_noreturn(E_ERROR, errno, "Could not delete timer"); |
| 99 | + } |
| 100 | +} |
| 101 | +/* }}}} */ |
| 102 | + |
| 103 | +#endif |
0 commit comments