static bool check_if_forked() { if (forked_child) { forked_child = false; log(LOG_INFO, "Fork detected (child %u)", (unsigned) getpid()); if (Py_IsInitialized()) { PyOS_AfterFork(); } if (!call_after_fork_fn(false)) { return false; } } if (forked_parent) { forked_parent = false; /* No logging here, as it could generate a lot of spam in syslog, * because zabbix spawns new processes periodically for some metrics. */ if (!call_after_fork_fn(true)) { return false; } } return true; }
static PyObject * subprocess_fork_exec(PyObject* self, PyObject *args) { PyObject *gc_module = NULL; PyObject *executable_list, *py_fds_to_keep; PyObject *env_list, *preexec_fn; PyObject *process_args, *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; int p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite; int errpipe_read, errpipe_write, close_fds, restore_signals; int call_setsid; PyObject *cwd_obj, *cwd_obj2; const char *cwd; pid_t pid; int need_to_reenable_gc = 0; char *const *exec_array, *const *argv = NULL, *const *envp = NULL; Py_ssize_t arg_num; if (!PyArg_ParseTuple( args, "OOpOOOiiiiiiiiiiO:fork_exec", &process_args, &executable_list, &close_fds, &py_fds_to_keep, &cwd_obj, &env_list, &p2cread, &p2cwrite, &c2pread, &c2pwrite, &errread, &errwrite, &errpipe_read, &errpipe_write, &restore_signals, &call_setsid, &preexec_fn)) return NULL; if (close_fds && errpipe_write < 3) { /* precondition */ PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3"); return NULL; } if (PySequence_Length(py_fds_to_keep) < 0) { PyErr_SetString(PyExc_ValueError, "cannot get length of fds_to_keep"); return NULL; } if (_sanity_check_python_fd_sequence(py_fds_to_keep)) { PyErr_SetString(PyExc_ValueError, "bad value(s) in fds_to_keep"); return NULL; } /* We need to call gc.disable() when we'll be calling preexec_fn */ if (preexec_fn != Py_None) { PyObject *result; _Py_IDENTIFIER(isenabled); _Py_IDENTIFIER(disable); gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; result = _PyObject_CallMethodId(gc_module, &PyId_isenabled, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; } need_to_reenable_gc = PyObject_IsTrue(result); Py_DECREF(result); if (need_to_reenable_gc == -1) { Py_DECREF(gc_module); return NULL; } result = _PyObject_CallMethodId(gc_module, &PyId_disable, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; } Py_DECREF(result); } exec_array = _PySequence_BytesToCharpArray(executable_list); if (!exec_array) { Py_XDECREF(gc_module); return NULL; } /* Convert args and env into appropriate arguments for exec() */ /* These conversions are done in the parent process to avoid allocating or freeing memory in the child process. */ if (process_args != Py_None) { Py_ssize_t num_args; /* Equivalent to: */ /* tuple(PyUnicode_FSConverter(arg) for arg in process_args) */ fast_args = PySequence_Fast(process_args, "argv must be a tuple"); if (fast_args == NULL) goto cleanup; num_args = PySequence_Fast_GET_SIZE(fast_args); converted_args = PyTuple_New(num_args); if (converted_args == NULL) goto cleanup; for (arg_num = 0; arg_num < num_args; ++arg_num) { PyObject *borrowed_arg, *converted_arg; borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num); if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) goto cleanup; PyTuple_SET_ITEM(converted_args, arg_num, converted_arg); } argv = _PySequence_BytesToCharpArray(converted_args); Py_CLEAR(converted_args); Py_CLEAR(fast_args); if (!argv) goto cleanup; } if (env_list != Py_None) { envp = _PySequence_BytesToCharpArray(env_list); if (!envp) goto cleanup; } if (preexec_fn != Py_None) { preexec_fn_args_tuple = PyTuple_New(0); if (!preexec_fn_args_tuple) goto cleanup; _PyImport_AcquireLock(); } if (cwd_obj != Py_None) { if (PyUnicode_FSConverter(cwd_obj, &cwd_obj2) == 0) goto cleanup; cwd = PyBytes_AsString(cwd_obj2); } else { cwd = NULL; cwd_obj2 = NULL; } pid = fork(); if (pid == 0) { /* Child process */ /* * Code from here to _exit() must only use async-signal-safe functions, * listed at `man 7 signal` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. */ if (preexec_fn != Py_None) { /* We'll be calling back into Python later so we need to do this. * This call may not be async-signal-safe but neither is calling * back into Python. The user asked us to use hope as a strategy * to avoid deadlock... */ PyOS_AfterFork(); } child_exec(exec_array, argv, envp, cwd, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); _exit(255); return NULL; /* Dead code to avoid a potential compiler warning. */ } Py_XDECREF(cwd_obj2); if (pid == -1) { /* Capture the errno exception before errno can be clobbered. */ PyErr_SetFromErrno(PyExc_OSError); } if (preexec_fn != Py_None && _PyImport_ReleaseLock() < 0 && !PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "not holding the import lock"); } /* Parent process */ if (envp) _Py_FreeCharPArray(envp); if (argv) _Py_FreeCharPArray(argv); _Py_FreeCharPArray(exec_array); /* Reenable gc in the parent process (or if fork failed). */ if (need_to_reenable_gc && _enable_gc(gc_module)) { Py_XDECREF(gc_module); return NULL; } Py_XDECREF(preexec_fn_args_tuple); Py_XDECREF(gc_module); if (pid == -1) return NULL; /* fork() failed. Exception set earlier. */ return PyLong_FromPid(pid); cleanup: if (envp) _Py_FreeCharPArray(envp); if (argv) _Py_FreeCharPArray(argv); _Py_FreeCharPArray(exec_array); Py_XDECREF(converted_args); Py_XDECREF(fast_args); Py_XDECREF(preexec_fn_args_tuple); /* Reenable gc if it was disabled. */ if (need_to_reenable_gc) _enable_gc(gc_module); Py_XDECREF(gc_module); return NULL; }
static PyObject *new_miactions_object(int queuespace, int bufspace, int resultspace) { unsigned queue_offset, data_offset, result_offset, total_shmlen; shmstruct newss; PyMIActions *self; /* Fill the newss. */ newss.queuespace = queuespace; newss.bufspace = bufspace; newss.resultspace = resultspace; queue_offset = _round4(sizeof(newss)); data_offset = _round4(queue_offset + queuespace * sizeof(operqueue_entry)); result_offset = _round4(data_offset + bufspace); total_shmlen = _round4(result_offset + resultspace); newss.oper_id = -1; newss.oper_step = 0; newss.oper_stepnum = 0; newss.bufdata_start = 0; newss.bufdata_len = 0; self = PyObject_New(PyMIActions, &PyMIActionsType); if (self == NULL) return NULL; self->oper_cnt = 0; self->pid = -1; self->semid = -1; self->shmid = -1; self->ss = (shmstruct *) -1; self->shmid = shmget(IPC_PRIVATE, total_shmlen, 0600 | IPC_CREAT | IPC_EXCL); if (self->shmid < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } self->ss = (shmstruct *)shmat(self->shmid, NULL, 0); if (self->ss == (shmstruct *)-1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } newss.queue = (operqueue_entry *)(((char *)(self->ss)) + queue_offset); newss.data = ((char *)(self->ss)) + data_offset; newss.result = ((char *)(self->ss)) + result_offset; newss.resultlen = 0; memcpy(self->ss, &newss, sizeof(shmstruct)); self->semid = semget(IPC_PRIVATE, SEMMAX, 0600 | IPC_CREAT | IPC_EXCL); if (self->semid < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } if (!_set_sem(self->semid, SEMQLOCK, 1)) return NULL; if (!_set_sem(self->semid, SEMQUSED, 0)) return NULL; if (!_set_sem(self->semid, SEMQFREE, queuespace)) return NULL; if (!_set_sem(self->semid, SEMRLOCK, 1)) return NULL; if (!_set_sem(self->semid, SEMEXIT, 0)) return NULL; self->pid = fork(); if (self->pid < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } else if (self->pid > 0) { /* Control process. */ PyOS_AfterFork(); return (PyObject *)self; } else { /* Action process. */ PyOS_AfterFork(); return (PyObject *)self; } }