#include "pyutils.h" void pyinit() { char *venv_path; PyObject *sitemod, *mainfun, *wsgi; if( (venv_path = getenv("VIRTUAL_ENV")) ) { setenv("PYTHONHOME", venv_path, 1); swprintf(PyFCGI_conf.context.venv_path, PATH_MAX, L"%s", venv_path); Py_SetPythonHome(PyFCGI_conf.context.venv_path); pyfcgi_log(LOG_INFO, "Virtualenv found : '%s'", venv_path); swprintf(PyFCGI_conf.context.python_path, PATH_MAX, L"%s/bin/python3", venv_path); Py_SetProgramName(PyFCGI_conf.context.python_path); } Py_Initialize(); sitemod = PyImport_ImportModule("site"); mainfun = PyObject_GetAttrString(sitemod, "main"); PyObject_CallObject(mainfun, NULL); //Append "." to sys.path sitemod = PySys_GetObject("path"); PyList_Append(sitemod, PyUnicode_FromString(".")); //Initialize the wsgi PEP333 dict wsgi = PyFCGI_conf.context.wsgi_dict = PyDict_New(); PyDict_SetItemString(wsgi, "wsgi.version", Py_BuildValue("ii", 1,0)); PyDict_SetItemString(wsgi, "wsgi.multithread", Py_False); PyDict_SetItemString(wsgi, "wsgi.multiprocess", Py_True); PyDict_SetItemString(wsgi, "wsgi.run_once", Py_False); } void update_python_path() { wchar_t *ppath, *wcwd, *wtmp; char *cwd, *ncwd, *err_fmt; size_t wcwd_sz, ppath_sz; int err; // Add a ':' in front of CWD and convert to wchar_t* cwd = get_current_dir_name(); if(!cwd) { err = errno; err_fmt = "Unable to fecth CWD : %s"; goto update_python_path_err; } ncwd = malloc(sizeof(char) * (strlen(cwd) + 2)); if(!ncwd) { err = errno; err_fmt = "Unable to allocate memory for new CWD : %s"; goto update_python_path_err_cwd; } ncwd[0] = ':'; strcpy(ncwd+1, cwd); free(cwd); cwd = ncwd; wcwd_sz = mbstowcs(NULL, cwd, 0); if(wcwd_sz == (size_t)-1) { err = errno; err_fmt = "Unable to convert CWD to wchar : %s"; goto update_python_path_err_cwd; } wcwd_sz++; wcwd = malloc(sizeof(wchar_t) * wcwd_sz); if(!wcwd) { err = errno; err_fmt = "Unable to allocate wchar for CWD : %s"; goto update_python_path_err_cwd; } if(mbstowcs(wcwd, cwd, wcwd_sz) == -1) { err = errno; err_fmt = "Unable to convert CWD to wchar : %s"; goto update_python_path_err_wcwd; } // Fetch, dup & update the python path ppath = Py_GetPath(); if(!ppath) { if(PyErr_Occurred()) { log_expt(LOG_ALERT); } else { pyfcgi_log(LOG_ALERT, "Unable to fetch python path, got NULL."); } err_fmt = NULL; err = 0; goto update_python_path_err_wcwd; } ppath = wcsdup(ppath); dprintf(2, "%ls\n", ppath); if(!wcwd) { err = errno; err_fmt = "Unable to dup the python PATH : %s"; goto update_python_path_err_wcwd; } ppath_sz = wcslen(ppath); wtmp = realloc(ppath, sizeof(wchar_t) * (ppath_sz + wcwd_sz + 1)); if(!wtmp) { err = errno; err_fmt = "Unable reallocate new python path : %s"; goto update_python_path_err_ppath; } ppath = wtmp; wcsncpy(ppath+ppath_sz, wcwd, wcwd_sz); dprintf(2, "%ls\n", ppath); Py_SetPath(ppath); free(ppath); free(wcwd); free(cwd); return; update_python_path_err_ppath: free(ppath); update_python_path_err_wcwd: free(wcwd); update_python_path_err_cwd: free(cwd); update_python_path_err: if(err_fmt) { pyfcgi_log( LOG_ALERT, err_fmt, strerror(err)); } exit(err); } static PyObject* _fetch_pyflush(const char *fdname) { PyObject *pyfd, *pyflush; pyfd = PySys_GetObject(fdname); if(!pyfd) { pyfcgi_log(LOG_ALERT, "Unable to fetch sys.%s", fdname); log_expt(LOG_ALERT); Py_Exit(EXIT_PYERR); } pyflush = PyObject_GetAttrString(pyfd, "flush"); Py_DECREF(pyfd); if(!pyflush) { pyfcgi_log(LOG_ALERT, "Unable to fetch sys.%s.flush", fdname); log_expt(LOG_ALERT); Py_Exit(EXIT_PYERR); } if(!PyCallable_Check(pyflush)) { pyfcgi_log(LOG_ALERT, "sys.%s.flush is not callable !", fdname); Py_Exit(EXIT_PYERR); } return pyflush; } void fetch_pyflush(PyObject** pystdout_flush, PyObject** pystderr_flush) { *pystdout_flush = _fetch_pyflush("stdout"); *pystderr_flush = _fetch_pyflush("stderr"); } void update_python_fd(int pipe_out[2], int pipe_err[2]) { int pri, err; char *err_fmt; PyObject *os_mod, *pyfdopen, *args, *new_fd; pri = LOG_ALERT; if(pipe2(pipe_out, O_DIRECT) == -1) { err = errno; err_fmt = "Unable to create pipe for python stdout : %s"; goto update_python_fd_err; } if(pipe2(pipe_err, O_DIRECT) == -1) { err = errno; err_fmt = "Unable to create pipe for python stderr : %s"; goto update_python_fd_err_pipeout; } os_mod = PyImport_ImportModule("os"); if(!os_mod) { if(PyErr_Occurred()) { log_expt(LOG_ALERT); } else { pyfcgi_log( LOG_ALERT, "Unable to import python os module, got NULL."); } err_fmt = NULL; goto update_python_fd_err_pipes; } pyfdopen = PyObject_GetAttrString(os_mod, "fdopen"); Py_DECREF(os_mod); if(!pyfdopen) { if(PyErr_Occurred()) { log_expt(LOG_ALERT); } else { pyfcgi_log( LOG_ALERT, "Unable to fetch os.fdopen() , got NULL."); } err_fmt = NULL; goto update_python_fd_err_pipes; } args = Py_BuildValue("is", pipe_out[1], "w"); if(!args) { pyfcgi_log( LOG_ERR, "Error building values with '%d', '%s' for stdout", pipe_out[1], "w"); log_expt(LOG_ALERT); err_fmt = NULL; goto update_python_fd_err_fdopen; } new_fd = PyObject_CallObject(pyfdopen, args); if(!new_fd || PyErr_Occurred()) { pyfcgi_log( LOG_ERR, "Error calling fdopen(%d, '%s')", pipe_out[1], "w"); log_expt(LOG_ALERT); err_fmt = NULL; goto update_python_fd_err_args; } Py_DECREF(args); if(PySys_SetObject("stdout", new_fd)) { pyfcgi_log(LOG_ERR, "Unable to set sys.stdout"); log_expt(LOG_ALERT); goto update_python_fd_err_newfd; } Py_DECREF(new_fd); args = Py_BuildValue("is", pipe_err[1], "w"); if(!args) { pyfcgi_log( LOG_ERR, "Error building values with '%d', '%s' for stderr", pipe_out[1], "w"); log_expt(LOG_ALERT); err_fmt = NULL; goto update_python_fd_err_fdopen; } new_fd = PyObject_CallObject(pyfdopen, args); if(!new_fd || PyErr_Occurred()) { pyfcgi_log( LOG_ERR, "Error calling fdopen(%d, '%s')", pipe_out[1], "w"); log_expt(LOG_ALERT); err_fmt = NULL; goto update_python_fd_err_args; } Py_DECREF(args); PyDict_SetItemString(PyFCGI_conf.context.wsgi_dict, "wsgi.errors", new_fd); if(PySys_SetObject("stderr", new_fd)) { pyfcgi_log(LOG_ERR, "Unable to set sys.stderr"); log_expt(LOG_ALERT); goto update_python_fd_err_newfd; } Py_DECREF(new_fd); return; update_python_fd_err_newfd: Py_DECREF(new_fd); update_python_fd_err_args: Py_DECREF(args); update_python_fd_err_fdopen: Py_DECREF(fdopen); update_python_fd_err_pipes: close(pipe_err[0]); close(pipe_err[1]); update_python_fd_err_pipeout: close(pipe_out[0]); close(pipe_out[1]); update_python_fd_err: if(err_fmt) { pyfcgi_log(pri, err_fmt, strerror(err)); } exit(1); } PyObject* update_pyenv(PyObject *py_osmod, char **environ) { PyObject *pyenv, *pykey, *pyval; char *key, *value, **cur; cur = environ; pyenv = PyObject_GetAttrString(py_osmod, "environ"); if(!pyenv) { pyfcgi_log(LOG_WARNING, "Unable to get os.environ"); log_expt(LOG_ALERT); } else { Py_DECREF(pyenv); } pyenv = PyDict_New(); while(*cur) { key = value = *cur; while(*value && *value != '=') { value++; } if(!*value) { pyfcgi_log(LOG_WARNING, "Droping environment value without value : '%s'", key); cur++; continue; } value++; *(value-1) = '\0'; // dirty modification of **environ //pyfcgi_log(LOG_DEBUG, "PySetEnv '%s'='%s'", key, value); pykey = PyUnicode_DecodeLocale(key, "surrogateescape"); if(!pykey) { *(value-1) = '='; // **environ restore pyfcgi_log(LOG_ALERT, "Unable to parse environ key string '%s'", key); log_expt(LOG_ALERT); Py_Exit(EXIT_PYERR); } *(value-1) = '='; // **environ restore pyval = PyUnicode_DecodeFSDefault(value); if(!pyval) { pyfcgi_log(LOG_ALERT, "Unable to parse environ val string '%s'", value); log_expt(LOG_ALERT); Py_Exit(EXIT_PYERR); } if(PyDict_SetItem(pyenv, pykey, pyval) == -1) { pyfcgi_log(LOG_ERR, "Unable to set environ '%s'='%s'", key, value); log_expt(LOG_ERR); } Py_DECREF(pyval); Py_DECREF(pykey); cur++; } /**@todo set given headers */ PyDict_SetItemString(PyFCGI_conf.context.wsgi_dict, "wsgi.url_scheme", Py_BuildValue("U", "http")); PyDict_Update(pyenv, PyFCGI_conf.context.wsgi_dict); PyObject_SetAttrString(py_osmod, "environ", pyenv); /*//Debug print env on stderr PyObject* repr = PyObject_ASCII(pyenv); Py_INCREF(repr); dprintf(2, "%s\n", PyUnicode_AsUTF8(repr)); Py_DECREF(repr); */ return pyenv; } PyObject* import_entrypoint() { PyObject *entry_fname, *entry_module, *entry_fun; entry_fname = PyUnicode_DecodeFSDefault(PyFCGI_conf.py_entrymod); entry_module = PyImport_Import(entry_fname); Py_DECREF(entry_fname); if(!entry_module) { //TODO syslog python error / traceback pyfcgi_log(LOG_CRIT, "Unable to import python file '%s'", PyFCGI_conf.py_entrymod); //pyfcgi_log( LOG_INFO, "%s", getcwd(NULL, 256)); log_expt(LOG_ERR); return NULL; } // getting entrypoint function entry_fun = PyObject_GetAttrString(entry_module, PyFCGI_conf.py_entryfun); Py_DECREF(entry_module); if(!entry_fun) { //TODO syslog python error / traceback pyfcgi_log(LOG_CRIT, "Unable to import object '%s' from '%s'", PyFCGI_conf.py_entryfun, PyFCGI_conf.py_entrymod); log_expt(LOG_ERR); return NULL; } if(!PyCallable_Check(entry_fun)) { pyfcgi_log( LOG_CRIT, "When imported from '%s', '%s' was not a callable", PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun); return NULL; } return entry_fun; } PyObject* get_start_response() { PyObject *module; if(!libpyfcgi.self) { module = PyInit_libpyfcgi(); if(module == NULL) { pyfcgi_log(LOG_ERR, "Unable to create libpyfcgi python module"); return NULL; } libpyfcgi.self = module; } return PyObject_GetAttrString(libpyfcgi.self, "start_response"); } void log_expt(int priority) { if(!PyErr_Occurred()) { pyfcgi_log(priority, "No exception"); return; } PyObject *expt, *expt_bytes, *expt_cls, *expt_val, *expt_type, *traceback, *tbmod, *tbfmt, *tbfmt_args, *tbstr, **lines; Py_ssize_t i, nlines; char *msg, *type, *val; int msg_sz; char msg_fmt[] = "%s: %s"; PyErr_Fetch(&expt_cls, &expt, &traceback); // Fetching exception message using __str__() expt_val = PyObject_ASCII(expt); expt_bytes = PyUnicode_AsUTF8String(expt_val); msg = PyBytes_AsString(expt_bytes); val = strndup(msg, 1024); // TODO check out of mem if !val // Fetching exception class name expt_type = PyObject_GetAttrString(expt_cls, "__name__"); expt_bytes = PyUnicode_AsUTF8String(expt_type); msg = PyBytes_AsString(expt_bytes); type = strndup(msg, 1024); // TODO check out of mem if !type msg_sz = snprintf(NULL, 0, msg_fmt, type, val); msg_sz++; msg = malloc(sizeof(char) * msg_sz); snprintf(msg, msg_sz, msg_fmt, type, val); pyfcgi_log(priority, msg); //Fetching & logging traceback tbmod = PyImport_ImportModule("traceback"); tbfmt = PyObject_GetAttrString(tbmod, "format_tb"); tbfmt_args = Py_BuildValue("(O)", traceback); tbstr = PyObject_CallObject(tbfmt, tbfmt_args); if(!tbstr) { pyfcgi_log(LOG_ALERT,"FAILS TO FOMAT"); PyErr_Fetch(&expt_cls, &expt, &traceback); pyfcgi_log(LOG_ALERT, "%s", PyUnicode_AsUTF8(PyObject_ASCII(expt))); return; } nlines = PySequence_Fast_GET_SIZE(tbstr); lines = PySequence_Fast_ITEMS(tbstr); for(i=0; i