48#define HAVE_MSGHDR_MSG_CONTROL
63#ifdef HAVE_SYS_TYPES_H
68#include <sys/socket.h>
71#include <netinet/in.h>
89#define LSCRIU_PATH 256
93static int s_initial_start_reqs = 0;
94static int s_requests_count = 0;
95static int s_restored = 0;
96static int (*s_lscapi_dump_me)(
void) =
NULL;
97static int (*s_lscapi_prepare_me)(
void) =
NULL;
98static int s_native = 0;
99static int s_tried_checkpoint = 0;
100#ifdef LSAPILIB_DEBUG_CRIU
101static int s_criu_debug = 0;
103static int s_fd_native = -1;
104static char *s_criu_image_path =
NULL;
123#ifdef LSAPILIB_DEBUG_CRIU
124#define lscriu_dbg(...) \
125 do { if (s_criu_debug) fprintf(stderr, __VA_ARGS__); } while(0)
127#define lscriu_dbg(...)
130#define lscriu_err(...) fprintf(stderr, __VA_ARGS__)
133#define SUN_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
143typedef sem_t * (*psem_open_t) (
const char *__name,
int __oflag, ...);
155 sigaction(signo,
NULL, &sa);
157 if (sa.sa_handler ==
SIG_DFL) {
158 sigemptyset(&sa.sa_mask);
161 sigaction(signo, &sa,
NULL);
166static void lsapi_siguser2(
int sig)
173static void LSCRIU_Set_Initial_Start_Reqs(
int reqs)
175 s_initial_start_reqs = reqs;
183 s_global_counter_type = tp;
191static int LSCRIU_Get_Global_Counter_Type(
void)
193 return s_global_counter_type;
197static int LSCRIU_Init_Global_Counter(
int value)
200 || !s_initial_start_reqs) {
208static void LSCRIU_Increase_Global_Counter(
void)
211 || !s_initial_start_reqs) {
219static void LSCRIU_Get_Global_Counter(
void)
221 if (!s_initial_start_reqs) {
229static int LSCRIU_need_checkpoint(
void)
231 if (!s_initial_start_reqs) {
236 && s_requests_count <= s_initial_start_reqs) {
237 LSCRIU_Get_Global_Counter();
239 if (s_initial_start_reqs > 0
240 && s_requests_count >= s_initial_start_reqs) {
248static int LSCRIU_load_liblscapi(
void)
250 void *lib_handle =
NULL;
251 void *pthread_lib_handle =
NULL;
259 if (!(lib_handle = DL_LOAD(
last =
"liblscapi.so"))
261 fprintf(stderr,
"LSCRIU (%d): failed to dlopen %s: %s - ignore CRIU\n",
262 s_pid,
last, dlerror());
263 else if (!(s_lscapi_dump_me = dlsym(lib_handle,
last =
"lscapi_dump_me")) ||
264 !(s_lscapi_prepare_me = dlsym(lib_handle,
last =
"lscapi_prepare_me")) ||
265 !(
psem_open = dlsym(pthread_lib_handle,
last =
"sem_open")) ||
266 !(
psem_post = dlsym(pthread_lib_handle,
last =
"sem_post")) ||
268 fprintf(stderr,
"LSCRIU (%d): failed to dlsym %s: %s - ignore CRIU\n",
269 s_pid,
last, dlerror());
276 if (pthread_lib_handle)
277 dlclose(pthread_lib_handle);
284static void LSCRIU_Wink_Server_is_Ready(
void)
292 if (
getenv(
"LSAPI_UNIQE"))
293 snprintf(sem_name,
sizeof sem_name - 1,
"lsphp[hash=%s].is_ready",
296 snprintf(sem_name,
sizeof sem_name - 1,
"lsphp[euid=0x%x].is_ready",
299 sem_t *is_ready_sem =
psem_open(sem_name, O_RDWR);
307 else if (
errno != ENOENT)
312#ifdef LSAPILIB_DEBUG_CRIU
313static char *LSCRIU_Error_File_Name(
char *pchFile,
int max_len)
315 const char *pchDefaultSocketPath =
"/tmp/";
316 const char *pchDefaultLogFileName =
"lsws_error.log";
317 snprintf(pchFile, max_len,
"%s%s", pchDefaultSocketPath,
318 pchDefaultLogFileName);
323static void LSCRIU_Debugging(
void) {
325 pchCRIUDebug =
getenv(
"LSAPI_CRIU_DEBUG");
327 pchCRIUDebug =
getenv(
"LSCAPI_CRIU_DEBUG");
332 if ((!pchCRIUDebug) ||
334 (*pchCRIUDebug ==
'0') ||
335 (*pchCRIUDebug ==
'f') ||
336 (*pchCRIUDebug ==
'F') ||
337 (((*pchCRIUDebug ==
'O') ||
338 (*pchCRIUDebug ==
'o')) &&
339 ((*(pchCRIUDebug + 1)) &&
340 ((*(pchCRIUDebug + 1) ==
'F') || (*(pchCRIUDebug + 1) ==
'f'))))))
342 lscriu_dbg(
"LSCRIU (%d): CRIU Debugging disabled by environment\n", s_pid);
347 lscriu_dbg(
"LSCRIU (%d): CRIU Debugging enabled by environment\n", s_pid);
348 fprintf(stderr,
"LSCRIU (%d): CRIU debug environment variable: %s\n",
349 s_pid, pchCRIUDebug);
354static void LSCRIU_Restored_Error(
int iFatal,
char *format, ...) {
367 if (!LSCRIU_Error_File_Name(chFile,
sizeof(chFile))) {
371 iOldUMask =
umask(0);
372 iFd = open( chFile, O_WRONLY | O_APPEND | O_CREAT,
373 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
376 char chFullMessage[0x1000];
380 localtime_r(&sTimeb.time,&sTm);
382 va_start(ap, format);
387 int n =
snprintf(chFullMessage,
sizeof(chFullMessage),
388 "%04d-%02d-%02d %02d:%02d:%02d.%03d: LSCRIU (%d): %s %s\n",
397 iFatal ?
"FATAL! " :
"(debug) ",
399 if (
n > (
int)
sizeof(chFullMessage))
400 n =
sizeof(chFullMessage);
401 write(iFd, chFullMessage,
n);
406static inline void LSCRIU_Debugging(
void) {}
407static inline void LSCRIU_Restored_Error(
int iFatal,
char *format, ...) {}
411static int LSCRIU_Native_Dump(pid_t iPid,
417 memset(&criu_native_dump, 0,
sizeof(criu_native_dump));
428 lscriu_dbg(
"LSCRIU (%d): Sent the dump request to the listener\n", s_pid);
431 sizeof(criu_native_dump)) == -1) {
432 lscriu_err(
"LSCRIU (%d): Error sending dump request to the listener: %s\n",
433 s_pid, strerror(
errno));
440static void LSCRIU_CloudLinux_Checkpoint(
void)
444 if ((!s_native) && (!s_lscapi_dump_me)) {
445 lscriu_dbg(
"LSCRIU (%d): Not native and unable to dump - abandon one-time "
450 iRet = s_lscapi_dump_me();
452 lscriu_err(
"LSCRIU: CloudLinux dump of PID: %d, error: %s\n",
453 s_pid, strerror(
errno));
457 lscriu_err(
"LSCRIU: Successful CloudLinux dump of PID: %d\n", s_pid);
466 LSCRIU_Wink_Server_is_Ready();
467 lscriu_err(
"LSCRIU: Successful CloudLinux restore of PID: %d, parent: %d.\n",
468 getpid(), getppid());
470 LSCRIU_Set_Initial_Start_Reqs(0);
474static void LSCRIU_Wait_Dump_Finish_Or_Restored(
int pid_parent)
478 time_t iTimeStart = 0;
485 iTimeStart = iTimeNow;
487 else if ((pid_parent != getppid()) ||
488 (iTimeNow - iTimeStart > 10)) {
491 else if (iTimeNow - iTimeStart > 5) {
492 LSCRIU_Restored_Error(1,
"Timed out waiting to be dumped");
495 }
while (!iRestored);
499static void LSCRIU_try_checkpoint(
int *forked_pid)
502 pid_t iPidDump = getpid();
504 if (s_tried_checkpoint) {
505 lscriu_dbg(
"LSCRIU (%d): Already tried checkpoint - one time per customer\n",
509 lscriu_dbg(
"LSCRIU (%d): Trying checkpoint\n", iPidDump);
510 s_tried_checkpoint = 1;
512 LSCRIU_CloudLinux_Checkpoint();
519 lscriu_err(
"LSCRIU (%d): Can't checkpoint due to a fork error: %s\n",
520 iPidDump, strerror(
errno));
524 pid_t iPidParent = getppid();
527 (
void)LSCRIU_Native_Dump(iPidDump,
532 LSCRIU_Wait_Dump_Finish_Or_Restored(iPidParent);
533 LSCRIU_Restored_Error(0,
"Restored!");
541 LSCRIU_Set_Initial_Start_Reqs(0);
545static int init_native_env(
void)
548 pchFd =
getenv(
"LSAPI_CRIU_SYNC_FD");
550 pchFd =
getenv(
"LSCAPI_CRIU_SYNC_FD");
552 const char *image_path;
553 image_path =
getenv(
"LSAPI_CRIU_IMAGE_PATH");
555 lscriu_err(
"LSCRIU (%d): LSAPI_CRIU_SYNC_FD internal environment "
556 "variable not set - contact Litespeed tech support\n", getpid());
560 lscriu_err(
"LSCRIU (%d): LSAPI_CRIU_IMAGE_PATH internal environment "
561 "variable not set - contact Litespeed tech support\n", getpid());
564 lscriu_dbg(
"LSCRIU (%d): Checkpoint dump. ImagePath: %s\n",
565 getpid(), image_path);
567 s_fd_native = atoi(pchFd);
568 lscriu_dbg(
"LSCRIU (%d): Native checkpoint. Use filepointer %d (%s) to send "
569 "pid %d\n", getpid(), s_fd_native, pchFd, iPidDump);
570 s_criu_image_path = strdup(image_path);
574static int LSCRIU_Init_Env_Parameters(
void)
579 p =
getenv(
"LSAPI_INITIAL_START");
581 p =
getenv(
"LSAPI_BACKEND_INITIAL_START");
586 lscriu_dbg(
"LSCRIU (%d): Set start requests based on environment (%d)\n",
588 LSCRIU_Set_Initial_Start_Reqs(
n);
590 lscriu_dbg(
"LSCRIU (%d): LSAPI_INITIAL_START set to 0 disabled\n",
595 lscriu_dbg(
"LSCRIU (%d): LSAPI_INITIAL_START NOT set - disabled\n",
600 lscriu_dbg(
"LSCRIU (%d): Listening...\n", getpid());
603 if ((env =
getenv(
"LSAPI_CRIU_USE_SHM"))) {
608 if ((*env ==
'S') || (*env ==
's'))
612 else if ((env =
getenv(
"LSAPI_SIGNALS"))) {
618 (((*env ==
'O') || (*env ==
'o')) &&
619 ((*(env + 1) ==
'N') || (*(env + 1) ==
'n'))))
628 lsapi_criu_signal(
SIGUSR2, lsapi_siguser2);
631 lscriu_dbg(
"LSCRIU (%d): Use shared memory\n", getpid());
633 LSCRIU_Set_Global_Counter_Type(gc_type);
636 lscriu_dbg(
"LSCRIU (%d): NOT Listening\n", getpid());
639 char *criu_mode =
NULL;
640 criu_mode =
getenv(
"LSAPI_CRIU");
645 if (*criu_mode ==
'0') {
646 lscriu_dbg(
"LSCRIU (%d): Disabled by environment.\n", getpid());
647 LSCRIU_Set_Initial_Start_Reqs(0);
649 else if (*criu_mode ==
'2') {
650 lscriu_dbg(
"LSCRIU (%d): Disabled by environment.\n", getpid());
655 if (s_native && init_native_env() == -1)
656 LSCRIU_Set_Initial_Start_Reqs(0);
665 if (!LSCRIU_Get_Global_Counter_Type()) {
669 lscriu_dbg(
"LSCRIU (%d): s_requests_count %d counter %d\n", getpid(),
670 s_requests_count, s_initial_start_reqs);
672 if (s_initial_start_reqs > 0 && s_requests_count <= s_initial_start_reqs) {
674 LSCRIU_Increase_Global_Counter();
675 if (s_requests_count >= s_initial_start_reqs) {
679 lscriu_dbg(
"LSCRIU (%d): Time to dump main process with semaphore\n",
684 lscriu_dbg(
"LSCRIU (%d): Send kill to main process with signals\n",
691static void LSCRIU_on_timer(
int *forked_pid)
693 lscriu_dbg(
"LSCRIU (%d): LSCRIU_on_timer\n", getpid());
694 if (LSCRIU_need_checkpoint()) {
695 LSCRIU_try_checkpoint(forked_pid);
704 LSCRIU_Init_Env_Parameters();
705 if (s_initial_start_reqs && !s_native) {
706 if (LSCRIU_load_liblscapi() == -1)
707 s_initial_start_reqs = 0;
709 if (s_initial_start_reqs) {
710 LSCRIU_Wink_Server_is_Ready();
711 lscriu_dbg(
"LSCRIU (%d): LSAPI_Register_Pgrp_Timer_Callback\n", s_pid);
713 LSCRIU_Init_Global_Counter(0);
715 return s_initial_start_reqs > 0;
usleep(int $microseconds)
fprintf($stream, string $format, mixed ... $values)
getenv(?string $name=null, bool $local_only=false)
strrchr(string $haystack, string $needle, bool $before_needle=false)
memset(ptr, 0, type->size)
zend_ffi_ctype_name_buf buf
int LSAPI_Is_Listen(void)
void(* sighandler_t)(int)
int LSAPI_Inc_Req_Processed(int cnt)
void LSAPI_Register_Pgrp_Timer_Callback(LSAPI_On_Timer_pf cb)
void LSCRIU_inc_req_processed()
sem_t *(* psem_open_t)(const char *__name, int __oflag,...)
int(* psem_close_t)(sem_t *__sem)
int(* psem_post_t)(sem_t *__sem)
void LSAPI_reset_server_state(void)
void lsapi_perror(const char *pMessage, int err_no)
char m_chImageDirectory[1024]
char m_chServiceAddress[SUN_PATH_MAX]
char m_chSocketDir[SUN_PATH_MAX]
exit(string|int $status=0)
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
fbc internal_function handler(call, ret)