コード例 #1
0
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;
}
コード例 #2
0
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;
}
コード例 #3
0
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;
    }
}