X Tutup
#include "uwsgi_python.h" /* This is the official python plugin, the one with the modifier1 mapped to 0 Since uWSGI 1.0 it is heavily based on Graham Dumpleton mod_wsgi (for Apache) */ extern struct uwsgi_server uwsgi; struct uwsgi_python up; #include extern PyTypeObject uwsgi_InputType; void uwsgi_opt_pythonpath(char *opt, char *value, void *foobar) { int i; glob_t g; if (glob(value, GLOB_MARK, NULL, &g)) { uwsgi_string_new_list(&up.python_path, value); } else { for (i = 0; i < (int) g.gl_pathc; i++) { uwsgi_string_new_list(&up.python_path, g.gl_pathv[i]); } } } void uwsgi_opt_pyshell(char *opt, char *value, void *foobar) { uwsgi.honour_stdin = 1; if (value) { up.pyshell = value; } else { up.pyshell = ""; } if (!strcmp("pyshell-oneshot", opt)) { up.pyshell_oneshot = 1; } } void uwsgi_opt_pyrun(char *opt, char *value, void *foobar) { uwsgi.honour_stdin = 1; uwsgi.command_mode = 1; up.pyrun = value; } void uwsgi_opt_pyver(char *opt, char *foo, void *bar) { const char *version = Py_GetVersion(); const char *space = strchr(version, ' '); if (space) { fprintf(stdout, "%.*s\n", (int) (space-version), version); } else { fprintf(stdout, "%s\n", version); } exit(0); } int uwsgi_python_init(void); void uwsgi_python_preinit_apps(void); static void uwsgi_early_python(char *opt, char *foo, void *bar) { static int early_initialized = 0; if (early_initialized) return; early_initialized = 1; uwsgi_python_init(); uwsgi_python_preinit_apps(); } static void uwsgi_early_python_import(char *opt, char *module, void *bar) { uwsgi_early_python(opt, NULL, NULL); if (strchr(module, '/') || uwsgi_endswith(module, ".py")) { uwsgi_pyimport_by_filename(uwsgi_pythonize(module), module); } else { if (PyImport_ImportModule(module) == NULL) { PyErr_Print(); } } } void uwsgi_opt_ini_paste(char *opt, char *value, void *foobar) { uwsgi_opt_load_ini(opt, value, NULL); if (value[0] != '/') { up.paste = uwsgi_concat4("config:", uwsgi.cwd, "/", value); } else { up.paste = uwsgi_concat2("config:", value); } if (!strcmp("ini-paste-logged", opt)) { up.paste_logger = 1; } } struct uwsgi_option uwsgi_python_options[] = { {"wsgi-file", required_argument, 0, "load .wsgi file", uwsgi_opt_set_str, &up.file_config, 0}, {"file", required_argument, 0, "load .wsgi file", uwsgi_opt_set_str, &up.file_config, 0}, {"eval", required_argument, 0, "eval python code", uwsgi_opt_set_str, &up.eval, 0}, {"module", required_argument,'w', "load a WSGI module", uwsgi_opt_set_str, &up.wsgi_config, 0}, {"wsgi", required_argument, 'w', "load a WSGI module", uwsgi_opt_set_str, &up.wsgi_config, 0}, {"callable", required_argument, 0, "set default WSGI callable name", uwsgi_opt_set_str, &up.callable, 0}, {"test", required_argument, 'J', "test a module import", uwsgi_opt_set_str, &up.test_module, 0}, {"home", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0}, {"virtualenv", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0}, {"venv", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0}, {"pyhome", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0}, {"py-programname", required_argument, 0, "set python program name", uwsgi_opt_set_str, &up.programname, 0}, {"py-program-name", required_argument, 0, "set python program name", uwsgi_opt_set_str, &up.programname, 0}, {"pythonpath", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0}, {"python-path", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0}, {"pp", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0}, {"pymodule-alias", required_argument, 0, "add a python alias module", uwsgi_opt_add_string_list, &up.pymodule_alias, 0}, {"post-pymodule-alias", required_argument, 0, "add a python module alias after uwsgi module initialization", uwsgi_opt_add_string_list, &up.post_pymodule_alias, 0}, {"import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0}, {"pyimport", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0}, {"py-import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0}, {"python-import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0}, {"shared-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0}, {"shared-pyimport", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0}, {"shared-py-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0}, {"shared-python-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0}, {"spooler-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list}, {"spooler-pyimport", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list}, {"spooler-py-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list}, {"spooler-python-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list}, {"pyargv", required_argument, 0, "manually set sys.argv", uwsgi_opt_set_str, &up.argv, 0}, {"optimize", required_argument, 'O', "set python optimization level", uwsgi_opt_set_int, &up.optimize, 0}, {"pecan", required_argument, 0, "load a pecan config file", uwsgi_opt_set_str, &up.pecan, 0}, {"paste", required_argument, 0, "load a paste.deploy config file", uwsgi_opt_set_str, &up.paste, 0}, {"paste-logger", no_argument, 0, "enable paste fileConfig logger", uwsgi_opt_true, &up.paste_logger, 0}, {"paste-name", required_argument, 0, "specify the name of the paste section", uwsgi_opt_set_str, &up.paste_name, 0}, {"web3", required_argument, 0, "load a web3 app", uwsgi_opt_set_str, &up.web3, 0}, {"pump", required_argument, 0, "load a pump app", uwsgi_opt_set_str, &up.pump, 0}, {"wsgi-lite", required_argument, 0, "load a wsgi-lite app", uwsgi_opt_set_str, &up.wsgi_lite, 0}, {"ini-paste", required_argument, 0, "load a paste.deploy config file containing uwsgi section", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE}, {"ini-paste-logged", required_argument, 0, "load a paste.deploy config file containing uwsgi section (load loggers too)", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE}, {"reload-os-env", no_argument, 0, "force reload of os.environ at each request", uwsgi_opt_true, &up.reload_os_env, 0}, #ifndef __CYGWIN__ {"no-site", no_argument, 0, "do not import site module", uwsgi_opt_true, &Py_NoSiteFlag, 0}, #endif {"pyshell", optional_argument, 0, "run an interactive python shell in the uWSGI environment", uwsgi_opt_pyshell, NULL, 0}, {"pyshell-oneshot", optional_argument, 0, "run an interactive python shell in the uWSGI environment (one-shot variant)", uwsgi_opt_pyshell, NULL, 0}, {"python", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0}, {"py", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0}, {"pyrun", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0}, {"py-tracebacker", required_argument, 0, "enable the uWSGI python tracebacker", uwsgi_opt_set_str, &up.tracebacker, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"py-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"py-autoreload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"python-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"python-autoreload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"py-auto-reload-ignore", required_argument, 0, "ignore the specified module during auto-reload scan (can be specified multiple times)", uwsgi_opt_add_string_list, &up.auto_reload_ignore, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"wsgi-env-behaviour", required_argument, 0, "set the strategy for allocating/deallocating the WSGI env", uwsgi_opt_set_str, &up.wsgi_env_behaviour, 0}, {"wsgi-env-behavior", required_argument, 0, "set the strategy for allocating/deallocating the WSGI env", uwsgi_opt_set_str, &up.wsgi_env_behaviour, 0}, {"start_response-nodelay", no_argument, 0, "send WSGI http headers as soon as possible (PEP violation)", uwsgi_opt_true, &up.start_response_nodelay, 0}, {"wsgi-strict", no_argument, 0, "try to be fully PEP compliant disabling optimizations", uwsgi_opt_true, &up.wsgi_strict, 0}, {"wsgi-accept-buffer", no_argument, 0, "accept CPython buffer-compliant objects as WSGI response in addition to string/bytes", uwsgi_opt_true, &up.wsgi_accept_buffer, 0}, {"wsgi-accept-buffers", no_argument, 0, "accept CPython buffer-compliant objects as WSGI response in addition to string/bytes", uwsgi_opt_true, &up.wsgi_accept_buffer, 0}, {"wsgi-disable-file-wrapper", no_argument, 0, "disable wsgi.file_wrapper feature", uwsgi_opt_true, &up.wsgi_disable_file_wrapper, 0}, {"python-version", no_argument, 0, "report python version", uwsgi_opt_pyver, NULL, UWSGI_OPT_IMMEDIATE}, {"python-raw", required_argument, 0, "load a python file for managing raw requests", uwsgi_opt_set_str, &up.raw, 0}, #if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER) {"py-sharedarea", required_argument, 0, "create a sharedarea from a python bytearray object of the specified size", uwsgi_opt_add_string_list, &up.sharedarea, 0}, #endif {"py-call-osafterfork", no_argument, 0, "enable child processes running cpython to trap OS signals", uwsgi_opt_true, &up.call_osafterfork, 0}, {"early-python", no_argument, 0, "load the python VM as soon as possible (useful for the fork server)", uwsgi_early_python, NULL, UWSGI_OPT_IMMEDIATE}, {"early-pyimport", required_argument, 0, "import a python module in the early phase", uwsgi_early_python_import, NULL, UWSGI_OPT_IMMEDIATE}, {"early-python-import", required_argument, 0, "import a python module in the early phase", uwsgi_early_python_import, NULL, UWSGI_OPT_IMMEDIATE}, {"early-pythonpath", required_argument, 0, "add directory (or glob) to pythonpath (immediate version)", uwsgi_opt_pythonpath, NULL, UWSGI_OPT_IMMEDIATE}, {"early-python-path", required_argument, 0, "add directory (or glob) to pythonpath (immediate version)", uwsgi_opt_pythonpath, NULL, UWSGI_OPT_IMMEDIATE}, {"python-worker-override", required_argument, 0, "override worker with the specified python script", uwsgi_opt_set_str, &up.worker_override, 0}, {"wsgi-manage-chunked-input", no_argument, 0, "manage chunked input via the wsgi.input_terminated extension", uwsgi_opt_true, &up.wsgi_manage_chunked_input, 0}, {"py-master-check-signals", no_argument, 0, "enable python signal handlers in master", uwsgi_opt_true, &up.master_check_signals, 0}, {0, 0, 0, 0, 0, 0, 0}, }; /* this routine will be called after each fork to reinitialize the various locks */ void uwsgi_python_pthread_prepare(void) { if (!up.is_dynamically_loading_an_app) pthread_mutex_lock(&up.lock_pyloaders); } void uwsgi_python_pthread_parent(void) { if (!up.is_dynamically_loading_an_app) pthread_mutex_unlock(&up.lock_pyloaders); } void uwsgi_python_pthread_child(void) { if (!up.is_dynamically_loading_an_app) pthread_mutex_init(&up.lock_pyloaders, NULL); } PyMethodDef uwsgi_spit_method[] = { {"uwsgi_spit", py_uwsgi_spit, METH_VARARGS, ""} }; PyMethodDef uwsgi_write_method[] = { {"uwsgi_write", py_uwsgi_write, METH_VARARGS, ""} }; int uwsgi_python_init() { char *pyversion = strchr(Py_GetVersion(), '\n'); if (!pyversion) { uwsgi_log_initial("Python version: %s\n", Py_GetVersion()); } else { uwsgi_log_initial("Python version: %.*s %s\n", pyversion-Py_GetVersion(), Py_GetVersion(), Py_GetCompiler()+1); } if (Py_IsInitialized()) { uwsgi_log("--- Python VM already initialized ---\n"); PyGILState_Ensure(); goto ready; } if (up.home != NULL) { if (!uwsgi_is_dir(up.home)) { uwsgi_log("Python Home is not a directory: %s\n", up.home); exit(1); } #ifdef PYTHREE // check for PEP 405 virtualenv (starting from python 3.3) char *pep405_env = uwsgi_concat2(up.home, "/pyvenv.cfg"); if (uwsgi_file_exists(pep405_env)) { uwsgi_log("PEP 405 virtualenv detected: %s\n", up.home); free(pep405_env); goto pep405; } free(pep405_env); // build the PYTHONHOME wchar path wchar_t *wpyhome; size_t len = strlen(up.home) + 1; wpyhome = uwsgi_calloc(sizeof(wchar_t) * len ); if (!wpyhome) { uwsgi_error("malloc()"); exit(1); } mbstowcs(wpyhome, up.home, len); Py_SetPythonHome(wpyhome); // do not free this memory !!! //free(wpyhome); pep405: #else Py_SetPythonHome(up.home); #endif uwsgi_log("Set PythonHome to %s\n", up.home); } char *program_name = up.programname; if (!program_name) { program_name = uwsgi.binary_path; } #ifdef PYTHREE if (!up.programname) { if (up.home) { program_name = uwsgi_concat2(up.home, "/bin/python"); } } wchar_t *pname = uwsgi_calloc(sizeof(wchar_t) * (strlen(program_name)+1)); mbstowcs(pname, program_name, strlen(program_name)+1); Py_SetProgramName(pname); #else Py_SetProgramName(program_name); #endif Py_OptimizeFlag = up.optimize; Py_Initialize(); ready: if (!uwsgi.has_threads) { uwsgi_log_initial("*** Python threads support is disabled. You can enable it with --enable-threads ***\n"); } up.wsgi_spitout = PyCFunction_New(uwsgi_spit_method, NULL); up.wsgi_writeout = PyCFunction_New(uwsgi_write_method, NULL); up.main_thread = PyThreadState_Get(); // by default set a fake GIL (little impact on performance) up.gil_get = gil_fake_get; up.gil_release = gil_fake_release; up.swap_ts = simple_swap_ts; up.reset_ts = simple_reset_ts; #if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER) struct uwsgi_string_list *usl = NULL; uwsgi_foreach(usl, up.sharedarea) { uint64_t len = uwsgi_n64(usl->value); PyObject *obj = PyByteArray_FromStringAndSize(NULL, len); char *storage = PyByteArray_AsString(obj); Py_INCREF(obj); struct uwsgi_sharedarea *sa = uwsgi_sharedarea_init_ptr(storage, len); sa->obj = obj; } #endif uwsgi_log_initial("Python main interpreter initialized at %p\n", up.main_thread); return 1; } void uwsgi_python_reset_random_seed() { PyObject *random_module, *random_dict, *random_seed; // reinitialize the random seed (thanks Jonas Borgström) random_module = PyImport_ImportModule("random"); if (random_module) { random_dict = PyModule_GetDict(random_module); if (random_dict) { random_seed = PyDict_GetItemString(random_dict, "seed"); if (random_seed) { PyObject *random_args = PyTuple_New(1); // pass no args PyTuple_SetItem(random_args, 0, Py_None); PyEval_CallObject(random_seed, random_args); if (PyErr_Occurred()) { PyErr_Print(); } } } } } void uwsgi_python_atexit() { if (uwsgi.mywid == 0) goto realstuff; // if hijacked do not run atexit hooks if (uwsgi.workers[uwsgi.mywid].hijacked) return; // if busy do not run atexit hooks if (uwsgi_worker_is_busy(uwsgi.mywid)) return; // managing atexit in async mode is a real pain...skip it for now if (uwsgi.async > 0) return; realstuff: // this time we use this higher level function // as this code can be executed in a signal handler #ifdef __GNU_kFreeBSD__ return; #endif if (!Py_IsInitialized()) { return; } // always call it PyGILState_Ensure(); // no need to worry about freeing memory PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi"); if (uwsgi_dict) { PyObject *ae = PyDict_GetItemString(uwsgi_dict, "atexit"); if (ae) { python_call(ae, PyTuple_New(0), 0, NULL); } } // this part is a 1:1 copy of mod_wsgi 3.x // it is required to fix some atexit bug with python 3 // and to shutdown useless threads complaints PyObject *module = PyImport_ImportModule("atexit"); Py_XDECREF(module); if (uwsgi.has_threads) { if (!PyImport_AddModule("dummy_threading")) PyErr_Clear(); } // For the reasons explained in // https://github.com/unbit/uwsgi/issues/1384, tearing down // the interpreter can be very expensive. if (uwsgi.skip_atexit_teardown) return; Py_Finalize(); } void uwsgi_python_post_fork() { if (uwsgi.i_am_a_spooler) { UWSGI_GET_GIL } // reset python signal flags so child processes can trap signals if (up.call_osafterfork) { #ifdef HAS_NOT_PyOS_AfterFork_Child PyOS_AfterFork(); #else PyOS_AfterFork_Child(); #endif } uwsgi_python_reset_random_seed(); // call the post_fork_hook PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi"); if (uwsgi_dict) { PyObject *pfh = PyDict_GetItemString(uwsgi_dict, "post_fork_hook"); if (pfh) { python_call(pfh, PyTuple_New(0), 0, NULL); } } PyErr_Clear(); if (uwsgi.mywid > 0) { uwsgi_python_set_thread_name(0); if (up.auto_reload) { // spawn the reloader thread pthread_t par_tid; pthread_create(&par_tid, NULL, uwsgi_python_autoreloader_thread, up.main_thread->interp); } if (up.tracebacker) { // spawn the tracebacker thread pthread_t ptb_tid; pthread_create(&ptb_tid, NULL, uwsgi_python_tracebacker_thread, NULL); } } UWSGI_RELEASE_GIL } PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) { FILE *pyfile; struct _node *py_file_node = NULL; PyObject *py_compiled_node, *py_file_module; int is_a_package = 0; struct stat pystat; char *real_filename = filename; if (!uwsgi_check_scheme(filename)) { pyfile = fopen(filename, "r"); if (!pyfile) { uwsgi_log("failed to open python file %s\n", filename); return NULL; } if (fstat(fileno(pyfile), &pystat)) { fclose(pyfile); uwsgi_error("fstat()"); return NULL; } if (S_ISDIR(pystat.st_mode)) { is_a_package = 1; fclose(pyfile); real_filename = uwsgi_concat2(filename, "/__init__.py"); pyfile = fopen(real_filename, "r"); if (!pyfile) { uwsgi_error_open(real_filename); free(real_filename); return NULL; } } py_file_node = PyParser_SimpleParseFile(pyfile, real_filename, Py_file_input); if (!py_file_node) { PyErr_Print(); uwsgi_log("failed to parse file %s\n", real_filename); if (is_a_package) free(real_filename); fclose(pyfile); return NULL; } fclose(pyfile); } else { size_t pycontent_size = 0; char *pycontent = uwsgi_open_and_read(filename, &pycontent_size, 1, NULL); if (pycontent) { py_file_node = PyParser_SimpleParseString(pycontent, Py_file_input); if (!py_file_node) { PyErr_Print(); uwsgi_log("failed to parse url %s\n", real_filename); return NULL; } } } py_compiled_node = (PyObject *) PyNode_Compile(py_file_node, real_filename); if (!py_compiled_node) { PyErr_Print(); uwsgi_log("failed to compile python file %s\n", real_filename); return NULL; } if (is_a_package) { py_file_module = PyImport_AddModule(name); if (py_file_module) { PyModule_AddObject(py_file_module, "__path__", Py_BuildValue("[O]", PyString_FromString(filename))); } free(real_filename); } py_file_module = PyImport_ExecCodeModule(name, py_compiled_node); if (!py_file_module) { PyErr_Print(); return NULL; } Py_DECREF(py_compiled_node); return py_file_module; } void init_uwsgi_vars() { PyObject *pysys, *pysys_dict, *pypath; PyObject *modules = PyImport_GetModuleDict(); PyObject *tmp_module; /* add cwd to pythonpath */ pysys = PyImport_ImportModule("sys"); if (!pysys) { PyErr_Print(); exit(1); } pysys_dict = PyModule_GetDict(pysys); #ifdef PYTHREE // In python3, stdout / stderr was changed to be buffered (a bug according // to many): // - https://bugs.python.org/issue13597 // - https://bugs.python.org/issue13601 // We'll fix this by manually restore the unbuffered behaviour. // In the case of a tty, this fix breaks readline support in interactive // debuggers so we'll only do this in the non-tty case. if (!Py_FdIsInteractive(stdin, NULL)) { #ifdef HAS_NO_ERRORS_IN_PyFile_FromFd PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, 0); #else PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, NULL, 0); #endif PyDict_SetItemString(pysys_dict, "stdout", new_stdprint); PyDict_SetItemString(pysys_dict, "__stdout__", new_stdprint); PyDict_SetItemString(pysys_dict, "stderr", new_stdprint); PyDict_SetItemString(pysys_dict, "__stderr__", new_stdprint); } #endif pypath = PyDict_GetItemString(pysys_dict, "path"); if (!pypath) { PyErr_Print(); exit(1); } if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(".")) != 0) { PyErr_Print(); } struct uwsgi_string_list *uppp = up.python_path; while(uppp) { if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(uppp->value)) != 0) { PyErr_Print(); } else { uwsgi_log("added %s to pythonpath.\n", uppp->value); } uppp = uppp->next; } struct uwsgi_string_list *uppma = up.pymodule_alias; while(uppma) { // split key=value char *value = strchr(uppma->value, '='); if (!value) { uwsgi_log("invalid pymodule-alias syntax\n"); goto next; } value[0] = 0; if (!strchr(value + 1, '/')) { // this is a standard pymodule tmp_module = PyImport_ImportModule(value + 1); if (!tmp_module) { PyErr_Print(); exit(1); } PyDict_SetItemString(modules, uppma->value, tmp_module); } else { // this is a filepath that need to be mapped tmp_module = uwsgi_pyimport_by_filename(uppma->value, value + 1); if (!tmp_module) { PyErr_Print(); exit(1); } } uwsgi_log("mapped virtual pymodule \"%s\" to real pymodule \"%s\"\n", uppma->value, value + 1); // reset original value value[0] = '='; next: uppma = uppma->next; } } PyDoc_STRVAR(uwsgi_py_doc, "uWSGI api module."); #ifdef PYTHREE static PyModuleDef uwsgi_module3 = { PyModuleDef_HEAD_INIT, "uwsgi", uwsgi_py_doc, -1, NULL, }; PyObject *init_uwsgi3(void) { return PyModule_Create(&uwsgi_module3); } #endif void init_uwsgi_embedded_module() { PyObject *new_uwsgi_module, *zero; int i; if (PyType_Ready(&uwsgi_InputType) < 0) { PyErr_Print(); uwsgi_log("could not initialize the uwsgi python module\n"); exit(1); } /* initialize for stats */ up.workers_tuple = PyTuple_New(uwsgi.numproc); for (i = 0; i < uwsgi.numproc; i++) { zero = PyDict_New(); Py_INCREF(zero); PyTuple_SetItem(up.workers_tuple, i, zero); } #ifdef PYTHREE PyImport_AppendInittab("uwsgi", init_uwsgi3); new_uwsgi_module = PyImport_AddModule("uwsgi"); #else new_uwsgi_module = Py_InitModule3("uwsgi", NULL, uwsgi_py_doc); #endif if (new_uwsgi_module == NULL) { uwsgi_log("could not initialize the uwsgi python module\n"); exit(1); } Py_INCREF((PyObject *) &uwsgi_InputType); up.embedded_dict = PyModule_GetDict(new_uwsgi_module); if (!up.embedded_dict) { uwsgi_log("could not get uwsgi module __dict__\n"); exit(1); } // just for safety Py_INCREF(up.embedded_dict); if (PyDict_SetItemString(up.embedded_dict, "version", PyString_FromString(UWSGI_VERSION))) { PyErr_Print(); exit(1); } PyObject *uwsgi_py_version_info = PyTuple_New(5); PyTuple_SetItem(uwsgi_py_version_info, 0, PyInt_FromLong(UWSGI_VERSION_BASE)); PyTuple_SetItem(uwsgi_py_version_info, 1, PyInt_FromLong(UWSGI_VERSION_MAJOR)); PyTuple_SetItem(uwsgi_py_version_info, 2, PyInt_FromLong(UWSGI_VERSION_MINOR)); PyTuple_SetItem(uwsgi_py_version_info, 3, PyInt_FromLong(UWSGI_VERSION_REVISION)); PyTuple_SetItem(uwsgi_py_version_info, 4, PyString_FromString(UWSGI_VERSION_CUSTOM)); if (PyDict_SetItemString(up.embedded_dict, "version_info", uwsgi_py_version_info)) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "hostname", PyString_FromStringAndSize(uwsgi.hostname, uwsgi.hostname_len))) { PyErr_Print(); exit(1); } if (uwsgi.mode) { if (PyDict_SetItemString(up.embedded_dict, "mode", PyString_FromString(uwsgi.mode))) { PyErr_Print(); exit(1); } } if (uwsgi.pidfile) { if (PyDict_SetItemString(up.embedded_dict, "pidfile", PyString_FromString(uwsgi.pidfile))) { PyErr_Print(); exit(1); } } if (uwsgi.spoolers) { int sc = 0; struct uwsgi_spooler *uspool = uwsgi.spoolers; while(uspool) { sc++; uspool = uspool->next;} PyObject *py_spooler_tuple = PyTuple_New(sc); uspool = uwsgi.spoolers; sc = 0; while(uspool) { PyTuple_SetItem(py_spooler_tuple, sc, PyString_FromString(uspool->dir)); sc++; uspool = uspool->next; } if (PyDict_SetItemString(up.embedded_dict, "spoolers", py_spooler_tuple)) { PyErr_Print(); exit(1); } } if (PyDict_SetItemString(up.embedded_dict, "SPOOL_RETRY", PyInt_FromLong(-1))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "SPOOL_OK", PyInt_FromLong(-2))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "SPOOL_IGNORE", PyInt_FromLong(0))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "numproc", PyInt_FromLong(uwsgi.numproc))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "has_threads", PyInt_FromLong(uwsgi.has_threads))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "cores", PyInt_FromLong(uwsgi.cores))) { PyErr_Print(); exit(1); } if (uwsgi.loop) { if (PyDict_SetItemString(up.embedded_dict, "loop", PyString_FromString(uwsgi.loop))) { PyErr_Print(); exit(1); } } else { PyDict_SetItemString(up.embedded_dict, "loop", Py_None); } PyObject *py_opt_dict = PyDict_New(); for (i = 0; i < uwsgi.exported_opts_cnt; i++) { if (PyDict_Contains(py_opt_dict, PyString_FromString(uwsgi.exported_opts[i]->key))) { PyObject *py_opt_item = PyDict_GetItemString(py_opt_dict, uwsgi.exported_opts[i]->key); if (PyList_Check(py_opt_item)) { if (uwsgi.exported_opts[i]->value == NULL) { PyList_Append(py_opt_item, Py_True); } else { PyList_Append(py_opt_item, PyString_FromString(uwsgi.exported_opts[i]->value)); } } else { PyObject *py_opt_list = PyList_New(0); PyList_Append(py_opt_list, py_opt_item); if (uwsgi.exported_opts[i]->value == NULL) { PyList_Append(py_opt_list, Py_True); } else { PyList_Append(py_opt_list, PyString_FromString(uwsgi.exported_opts[i]->value)); } PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, py_opt_list); } } else { if (uwsgi.exported_opts[i]->value == NULL) { PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, Py_True); } else { PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, PyString_FromString(uwsgi.exported_opts[i]->value)); } } } if (PyDict_SetItemString(up.embedded_dict, "opt", py_opt_dict)) { PyErr_Print(); exit(1); } PyObject *py_sockets_list = PyList_New(0); struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; while(uwsgi_sock) { if (uwsgi_sock->bound) { PyList_Append(py_sockets_list, PyInt_FromLong(uwsgi_sock->fd)); } uwsgi_sock = uwsgi_sock->next; } if (PyDict_SetItemString(up.embedded_dict, "sockets", py_sockets_list)) { PyErr_Print(); exit(1); } PyObject *py_magic_table = PyDict_New(); uint8_t mtk; for (i = 0; i <= 0xff; i++) { // a bit of magic :P mtk = i; if (uwsgi.magic_table[i]) { if (uwsgi.magic_table[i][0] != 0) { PyDict_SetItem(py_magic_table, PyString_FromStringAndSize((char *) &mtk, 1), PyString_FromString(uwsgi.magic_table[i])); } } } if (PyDict_SetItemString(up.embedded_dict, "magic_table", py_magic_table)) { PyErr_Print(); exit(1); } #ifdef UNBIT if (PyDict_SetItemString(up.embedded_dict, "unbit", Py_True)) { #else if (PyDict_SetItemString(up.embedded_dict, "unbit", Py_None)) { #endif PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "buffer_size", PyInt_FromLong(uwsgi.buffer_size))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "started_on", PyInt_FromLong(uwsgi.start_tv.tv_sec))) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "start_response", up.wsgi_spitout)) { PyErr_Print(); exit(1); } if (PyDict_SetItemString(up.embedded_dict, "applications", Py_None)) { PyErr_Print(); exit(1); } if (uwsgi.is_a_reload) { if (PyDict_SetItemString(up.embedded_dict, "is_a_reload", Py_True)) { PyErr_Print(); exit(1); } } else { if (PyDict_SetItemString(up.embedded_dict, "is_a_reload", Py_False)) { PyErr_Print(); exit(1); } } init_uwsgi_module_advanced(new_uwsgi_module); if (uwsgi.spoolers) { init_uwsgi_module_spooler(new_uwsgi_module); } if (uwsgi.sharedareas) { init_uwsgi_module_sharedarea(new_uwsgi_module); } init_uwsgi_module_cache(new_uwsgi_module); if (uwsgi.queue_size > 0) { init_uwsgi_module_queue(new_uwsgi_module); } if (uwsgi.snmp) { init_uwsgi_module_snmp(new_uwsgi_module); } if (up.extension) { up.extension(); } } int uwsgi_python_magic(char *mountpoint, char *lazy) { char *qc = strchr(lazy, ':'); if (qc) { qc[0] = 0; up.callable = qc + 1; } if (!strcmp(lazy + strlen(lazy) - 3, ".py")) { up.file_config = lazy; return 1; } else if (!strcmp(lazy + strlen(lazy) - 5, ".wsgi")) { up.file_config = lazy; return 1; } else if (qc && strchr(lazy, '.')) { up.wsgi_config = lazy; return 1; } // reset lazy if (qc) { qc[0] = ':'; } return 0; } int uwsgi_python_mount_app(char *mountpoint, char *app) { int id; if (strchr(app, ':') || uwsgi_endswith(app, ".py") || uwsgi_endswith(app, ".wsgi")) { uwsgi.wsgi_req->appid = mountpoint; uwsgi.wsgi_req->appid_len = strlen(mountpoint); // lazy ? if (uwsgi.mywid > 0) UWSGI_GET_GIL if (uwsgi.single_interpreter) { id = init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } else { id = init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, NULL, PYTHON_APP_TYPE_WSGI); } // lazy ? if (uwsgi.mywid > 0) UWSGI_RELEASE_GIL return id; } return -1; } char *uwsgi_pythonize(char *orig) { char *name; size_t i, len, offset = 0; if (!strncmp(orig, "sym://", 6)) { offset = 6; } else if (!strncmp(orig, "http://", 7)) { offset = 7; } else if (!strncmp(orig, "data://", 7)) { offset = 7; } name = uwsgi_concat2(orig+offset, ""); len = strlen(name); for(i=0;ivalue, '/') || uwsgi_endswith(upli->value, ".py")) { uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value); } else { if (PyImport_ImportModule(upli->value) == NULL) { PyErr_Print(); } } upli = upli->next; } UWSGI_RELEASE_GIL } // this is the OLD default (fake) allocator for WSGI's env // the dictionary is created on app loading (one for each async core/thread) and reused (clearing it after each request, constantly) // // from a python-programmer point of view it is a hack/cheat but it does not violate the WSGI standard // and it is a bit faster than the "holy" allocator // Starting from uWSGI 2.1 we changed the default to be the holy one in the process of having saner python defaults. void *uwsgi_python_create_env_cheat(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { wsgi_req->async_args = wi->args[wsgi_req->async_id]; Py_INCREF((PyObject *)wi->environ[wsgi_req->async_id]); return wi->environ[wsgi_req->async_id]; } void uwsgi_python_destroy_env_cheat(struct wsgi_request *wsgi_req) { PyDict_Clear((PyObject *)wsgi_req->async_environ); } // this is the "holy" allocator for WSGI's env (now the default one) // Armin Ronacher told me this is what most of python programmers expect // I cannot speak for that as i am a perl guy, and i expect only black-magic things :P // // it is only slightly (better: irrelevant) slower, so no fear in enabling it... void *uwsgi_python_create_env_holy(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) { wsgi_req->async_args = PyTuple_New(2); // set start_response() Py_INCREF(Py_None); Py_INCREF(up.wsgi_spitout); PyTuple_SetItem((PyObject *)wsgi_req->async_args, 0, Py_None); PyTuple_SetItem((PyObject *)wsgi_req->async_args, 1, up.wsgi_spitout); PyObject *env = PyDict_New(); return env; } void uwsgi_python_destroy_env_holy(struct wsgi_request *wsgi_req) { if (uwsgi.threads < 2) { // in non-multithread modes, we set uwsgi.env, so let's remove it now // to equalise the refcount of the environ PyDict_DelItemString(up.embedded_dict, "env"); } Py_DECREF((PyObject *) wsgi_req->async_args); Py_DECREF((PyObject *)wsgi_req->async_environ); } // this hook will be executed by master (or worker1 when master is not requested, so COW is in place) void uwsgi_python_preinit_apps() { struct uwsgi_string_list *upli = up.shared_import_list; if (up.pre_initialized) goto ready; up.pre_initialized = 1; // setup app loaders up.loaders[LOADER_DYN] = uwsgi_dyn_loader; up.loaders[LOADER_UWSGI] = uwsgi_uwsgi_loader; up.loaders[LOADER_FILE] = uwsgi_file_loader; up.loaders[LOADER_PECAN] = uwsgi_pecan_loader; up.loaders[LOADER_PASTE] = uwsgi_paste_loader; up.loaders[LOADER_EVAL] = uwsgi_eval_loader; up.loaders[LOADER_MOUNT] = uwsgi_mount_loader; up.loaders[LOADER_CALLABLE] = uwsgi_callable_loader; up.loaders[LOADER_STRING_CALLABLE] = uwsgi_string_callable_loader; init_pyargv(); init_uwsgi_embedded_module(); #ifdef __linux__ uwsgi_init_symbol_import(); #endif if (up.test_module != NULL) { if (PyImport_ImportModule(up.test_module)) { exit(0); } exit(1); } if (!up.wsgi_env_behaviour) { up.wsgi_env_create = uwsgi_python_create_env_holy; up.wsgi_env_destroy = uwsgi_python_destroy_env_holy; } else if (!strcmp(up.wsgi_env_behaviour, "holy")) { up.wsgi_env_create = uwsgi_python_create_env_holy; up.wsgi_env_destroy = uwsgi_python_destroy_env_holy; } else if (!strcmp(up.wsgi_env_behaviour, "cheat")) { up.wsgi_env_create = uwsgi_python_create_env_cheat; up.wsgi_env_destroy = uwsgi_python_destroy_env_cheat; } init_uwsgi_vars(); ready: // load shared imports while(upli) { if (strchr(upli->value, '/') || uwsgi_endswith(upli->value, ".py")) { uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value); } else { if (PyImport_ImportModule(upli->value) == NULL) { PyErr_Print(); } } upli = upli->next; } } void uwsgi_python_init_apps() { // lazy ? if (uwsgi.mywid > 0) { UWSGI_GET_GIL; } // prepare for stack suspend/resume if (uwsgi.async > 0) { up.current_recursion_depth = uwsgi_malloc(sizeof(int)*uwsgi.async); up.current_frame = uwsgi_malloc(sizeof(struct _frame)*uwsgi.async); } struct uwsgi_string_list *upli = up.import_list; while(upli) { if (strchr(upli->value, '/') || uwsgi_endswith(upli->value, ".py")) { uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value); } else { if (PyImport_ImportModule(upli->value) == NULL) { PyErr_Print(); } } upli = upli->next; } struct uwsgi_string_list *uppa = up.post_pymodule_alias; PyObject *modules = PyImport_GetModuleDict(); PyObject *tmp_module; while(uppa) { // split key=value char *value = strchr(uppa->value, '='); if (!value) { uwsgi_log("invalid pymodule-alias syntax\n"); goto next; } value[0] = 0; if (!strchr(value + 1, '/')) { // this is a standard pymodule tmp_module = PyImport_ImportModule(value + 1); if (!tmp_module) { PyErr_Print(); exit(1); } PyDict_SetItemString(modules, uppa->value, tmp_module); } else { // this is a filepath that need to be mapped tmp_module = uwsgi_pyimport_by_filename(uppa->value, value + 1); if (!tmp_module) { PyErr_Print(); exit(1); } } uwsgi_log("mapped virtual pymodule \"%s\" to real pymodule \"%s\"\n", uppa->value, value + 1); // reset original value value[0] = '='; next: uppa = uppa->next; } if (up.raw) { up.raw_callable = uwsgi_file_loader(up.raw); if (up.raw_callable) { Py_INCREF(up.raw_callable); } } if (up.wsgi_config != NULL) { init_uwsgi_app(LOADER_UWSGI, up.wsgi_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.file_config != NULL) { init_uwsgi_app(LOADER_FILE, up.file_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.pecan != NULL) { init_uwsgi_app(LOADER_PECAN, up.pecan, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.paste != NULL) { init_uwsgi_app(LOADER_PASTE, up.paste, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.eval != NULL) { init_uwsgi_app(LOADER_EVAL, up.eval, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI); } if (up.web3 != NULL) { init_uwsgi_app(LOADER_UWSGI, up.web3, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WEB3); } if (up.pump != NULL) { init_uwsgi_app(LOADER_UWSGI, up.pump, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_PUMP); } if (up.wsgi_lite != NULL) { init_uwsgi_app(LOADER_UWSGI, up.wsgi_lite, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI_LITE); } if (uwsgi.profiler) { if (!strcmp(uwsgi.profiler, "pycall")) { PyEval_SetProfile(uwsgi_python_profiler_call, NULL); } else if (!strcmp(uwsgi.profiler, "pyline")) { PyEval_SetTrace(uwsgi_python_tracer, NULL); } } PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi"); if (uwsgi_dict) { up.after_req_hook = PyDict_GetItemString(uwsgi_dict, "after_req_hook"); if (up.after_req_hook) { Py_INCREF(up.after_req_hook); up.after_req_hook_args = PyTuple_New(0); Py_INCREF(up.after_req_hook_args); } } // lazy ? if (uwsgi.mywid > 0) { UWSGI_RELEASE_GIL; } } void uwsgi_python_master_fixup(int step) { static int master_fixed = 0; static int worker_fixed = 0; if (!uwsgi.master_process) return; if (uwsgi.has_threads) { if (step == 0) { if (!master_fixed) { UWSGI_RELEASE_GIL; master_fixed = 1; } } else { if (!worker_fixed) { UWSGI_GET_GIL; worker_fixed = 1; } } } } void uwsgi_python_enable_threads() { PyEval_InitThreads(); if (pthread_key_create(&up.upt_save_key, NULL)) { uwsgi_error("pthread_key_create()"); exit(1); } if (pthread_key_create(&up.upt_gil_key, NULL)) { uwsgi_error("pthread_key_create()"); exit(1); } pthread_setspecific(up.upt_save_key, (void *) PyThreadState_Get()); pthread_setspecific(up.upt_gil_key, (void *) PyThreadState_Get()); pthread_mutex_init(&up.lock_pyloaders, NULL); pthread_atfork(uwsgi_python_pthread_prepare, uwsgi_python_pthread_parent, uwsgi_python_pthread_child); up.gil_get = gil_real_get; up.gil_release = gil_real_release; up.swap_ts = simple_threaded_swap_ts; up.reset_ts = simple_threaded_reset_ts; if (uwsgi.threads > 1) { up.swap_ts = threaded_swap_ts; up.reset_ts = threaded_reset_ts; } uwsgi_log("python threads support enabled\n"); } void uwsgi_python_set_thread_name(int core_id) { // call threading.currentThread (taken from mod_wsgi, but removes DECREFs as thread in uWSGI are fixed) PyObject *threading_module = PyImport_ImportModule("threading"); if (threading_module) { PyObject *threading_module_dict = PyModule_GetDict(threading_module); if (threading_module_dict) { #ifdef PYTHREE PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread"); #else PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); #endif if (threading_current) { PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { // ignore the error PyErr_Clear(); } else { #ifdef PYTHREE PyObject_SetAttrString(current_thread, "name", PyUnicode_FromFormat("uWSGIWorker%dCore%d", uwsgi.mywid, core_id)); #else PyObject_SetAttrString(current_thread, "name", PyString_FromFormat("uWSGIWorker%dCore%d", uwsgi.mywid, core_id)); #endif Py_INCREF(current_thread); } } } } } void uwsgi_python_init_thread(int core_id) { // set a new ThreadState for each thread PyThreadState *pts; pts = PyThreadState_New(up.main_thread->interp); pthread_setspecific(up.upt_save_key, (void *) pts); pthread_setspecific(up.upt_gil_key, (void *) pts); #ifdef UWSGI_DEBUG uwsgi_log("python ThreadState %d = %p\n", core_id, pts); #endif UWSGI_GET_GIL; uwsgi_python_set_thread_name(core_id); UWSGI_RELEASE_GIL; } int uwsgi_check_python_mtime(PyObject *times_dict, char *filename) { struct stat st; PyObject *py_mtime = PyDict_GetItemString(times_dict, filename); if (!py_mtime) { if (stat(filename, &st)) { return 0; } PyDict_SetItemString(times_dict, filename, PyLong_FromLong(st.st_mtime)); } // the record is already tracked; else { long mtime = PyLong_AsLong(py_mtime); if (stat(filename, &st)) { return 0; } if ((long) st.st_mtime != mtime) { uwsgi_log("[uwsgi-python-reloader] module/file %s has been modified\n", filename); kill(uwsgi.workers[0].pid, SIGHUP); return 1; } } return 0; } PyObject *uwsgi_python_setup_thread(char *name, PyInterpreterState *interpreter) { // block signals on this thread sigset_t smask; sigfillset(&smask); #ifndef UWSGI_DEBUG sigdelset(&smask, SIGSEGV); #endif pthread_sigmask(SIG_BLOCK, &smask, NULL); PyThreadState *pts = PyThreadState_New(interpreter); pthread_setspecific(up.upt_save_key, (void *) pts); pthread_setspecific(up.upt_gil_key, (void *) pts); UWSGI_GET_GIL; PyObject *threading_module = PyImport_ImportModule("threading"); if (threading_module) { PyObject *threading_module_dict = PyModule_GetDict(threading_module); if (threading_module_dict) { #ifdef PYTHREE PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread"); #else PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread"); #endif if (threading_current) { PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL); if (!current_thread) { // ignore the error PyErr_Clear(); } else { PyObject_SetAttrString(current_thread, "name", PyString_FromString(name)); Py_INCREF(current_thread); return current_thread; } } } } return NULL; } void *uwsgi_python_autoreloader_thread(void *interpreter) { PyObject *new_thread = uwsgi_python_setup_thread("uWSGIAutoReloader", interpreter); if (!new_thread) return NULL; PyObject *modules = PyImport_GetModuleDict(); if (uwsgi.mywid == 1) { uwsgi_log("Python auto-reloader enabled on interpreter %p (pid %d)\n", interpreter, getpid()); } PyObject *times_dict = PyDict_New(); char *filename; for(;;) { UWSGI_RELEASE_GIL; sleep(up.auto_reload); UWSGI_GET_GIL; if (uwsgi.lazy || uwsgi.lazy_apps) { // do not start monitoring til the first app is loaded (required for lazy mode) if (uwsgi_apps_cnt == 0) continue; } #ifdef UWSGI_PYTHON_OLD int pos = 0; #else Py_ssize_t pos = 0; #endif char *app_dir = NULL; int i; for (i = 0; i < uwsgi_apps_cnt; i++) { if (((PyThreadState *) uwsgi_apps[i].interpreter)->interp == interpreter && uwsgi_apps[i].chdir[0]) { app_dir = uwsgi_apps[i].chdir; break; } } PyObject *mod_name, *mod; while (PyDict_Next(modules, &pos, &mod_name, &mod)) { if (mod == NULL) continue; int found = 0; struct uwsgi_string_list *usl = up.auto_reload_ignore; while(usl) { #ifdef PYTHREE PyObject *zero = PyUnicode_AsUTF8String(mod_name); char *str_mod_name = PyString_AsString(zero); int ret_cmp = strcmp(usl->value, str_mod_name); Py_DECREF(zero); if (!ret_cmp) { #else if (!strcmp(usl->value, PyString_AsString(mod_name))) { #endif found = 1; break; } usl = usl->next; } if (found) continue; if (!PyObject_HasAttrString(mod, "__file__")) continue; PyObject *mod_file = PyObject_GetAttrString(mod, "__file__"); if (!mod_file) continue; if (mod_file == Py_None) continue; #ifdef PYTHREE PyObject *zero = PyUnicode_AsUTF8String(mod_file); char *mod_filename = PyString_AsString(zero); #else char *mod_filename = PyString_AsString(mod_file); #endif if (!mod_filename) { #ifdef PYTHREE Py_DECREF(zero); #endif continue; } char *ext = strrchr(mod_filename, '.'); if (ext && (!strcmp(ext+1, "pyc") || !strcmp(ext+1, "pyd") || !strcmp(ext+1, "pyo"))) { filename = uwsgi_concat2n(mod_filename, strlen(mod_filename)-1, "", 0); } else { filename = uwsgi_concat2(mod_filename, ""); } if (filename[0] != '/' && app_dir) { char *tmp = uwsgi_concat3(app_dir, "/", filename); free(filename); filename = tmp; } if (uwsgi_check_python_mtime(times_dict, filename)) { UWSGI_RELEASE_GIL; return NULL; } free(filename); #ifdef PYTHREE Py_DECREF(zero); #endif } } return NULL; } void uwsgi_python_suspend(struct wsgi_request *wsgi_req) { PyGILState_STATE pgst = PyGILState_Ensure(); PyThreadState *tstate = PyThreadState_GET(); PyGILState_Release(pgst); if (wsgi_req) { up.current_recursion_depth[wsgi_req->async_id] = tstate->recursion_depth; up.current_frame[wsgi_req->async_id] = tstate->frame; } else { up.current_main_recursion_depth = tstate->recursion_depth; up.current_main_frame = tstate->frame; } } char *uwsgi_python_code_string(char *id, char *code, char *function, char *key, uint16_t keylen) { PyObject *cs_module = NULL; PyObject *cs_dict = NULL; UWSGI_GET_GIL; cs_module = PyImport_ImportModule(id); if (!cs_module) { PyErr_Clear(); cs_module = uwsgi_pyimport_by_filename(id, code); } if (!cs_module) { UWSGI_RELEASE_GIL; return NULL; } cs_dict = PyModule_GetDict(cs_module); if (!cs_dict) { PyErr_Print(); UWSGI_RELEASE_GIL; return NULL; } PyObject *func = PyDict_GetItemString(cs_dict, function); if (!func) { uwsgi_log("function %s not available in %s\n", function, code); PyErr_Print(); UWSGI_RELEASE_GIL; return NULL; } PyObject *args = PyTuple_New(1); PyTuple_SetItem(args, 0, PyString_FromStringAndSize(key, keylen)); PyObject *ret = python_call(func, args, 0, NULL); Py_DECREF(args); if (ret && PyString_Check(ret)) { char *val = PyString_AsString(ret); UWSGI_RELEASE_GIL; return val; } UWSGI_RELEASE_GIL; return NULL; } int uwsgi_python_signal_handler(uint8_t sig, void *handler) { UWSGI_GET_GIL; PyObject *args = PyTuple_New(1); PyObject *ret; if (!args) goto clear; if (!handler) goto clear; PyTuple_SetItem(args, 0, PyInt_FromLong(sig)); ret = python_call(handler, args, 0, NULL); Py_DECREF(args); if (ret) { Py_DECREF(ret); UWSGI_RELEASE_GIL; return 0; } clear: UWSGI_RELEASE_GIL; return -1; } uint64_t uwsgi_python_rpc(void *func, uint8_t argc, char **argv, uint16_t argvs[], char **buffer) { UWSGI_GET_GIL; uint8_t i; char *rv; size_t rl; PyObject *pyargs = PyTuple_New(argc); PyObject *ret; if (!pyargs) return 0; for (i = 0; i < argc; i++) { PyTuple_SetItem(pyargs, i, PyString_FromStringAndSize(argv[i], argvs[i])); } ret = python_call((PyObject *) func, pyargs, 0, NULL); Py_DECREF(pyargs); if (ret) { if (PyString_Check(ret)) { rv = PyString_AsString(ret); rl = PyString_Size(ret); if (rl > 0) { *buffer = uwsgi_malloc(rl); memcpy(*buffer, rv, rl); Py_DECREF(ret); UWSGI_RELEASE_GIL; return rl; } } Py_DECREF(ret); } if (PyErr_Occurred()) PyErr_Print(); UWSGI_RELEASE_GIL; return 0; } void uwsgi_python_add_item(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) { PyObject *pydict = (PyObject *) data; PyObject *o_key = PyString_FromStringAndSize(key, keylen); PyObject *zero = PyString_FromStringAndSize(val, vallen); PyDict_SetItem(pydict, o_key, zero); Py_DECREF(o_key); Py_DECREF(zero); } PyObject *uwsgi_python_dict_from_spooler_content(char *filename, char *buf, uint16_t len, char *body, size_t body_len) { PyObject *spool_dict = PyDict_New(); PyObject *value = PyString_FromString(filename); PyDict_SetItemString(spool_dict, "spooler_task_name", value); Py_DECREF(value); if (uwsgi_hooked_parse(buf, len, uwsgi_python_add_item, spool_dict)) return NULL; if (body && body_len > 0) { PyObject *value = PyString_FromStringAndSize(body, body_len); PyDict_SetItemString(spool_dict, "body", value); Py_DECREF(value); } return spool_dict; } int uwsgi_python_spooler(char *filename, char *buf, uint16_t len, char *body, size_t body_len) { static int random_seed_reset = 0; UWSGI_GET_GIL; if (!random_seed_reset) { uwsgi_python_reset_random_seed(); random_seed_reset = 1; } if (!up.embedded_dict) { // ignore UWSGI_RELEASE_GIL; return 0; } PyObject *spool_func = PyDict_GetItemString(up.embedded_dict, "spooler"); if (!spool_func) { // ignore UWSGI_RELEASE_GIL; return 0; } int retval = -1; PyObject *pyargs = PyTuple_New(1); PyObject *ret = NULL; PyObject *spool_dict = uwsgi_python_dict_from_spooler_content(filename, buf, len, body, body_len); if (!spool_dict) { retval = -2; goto clear; } // PyTuple_SetItem steals a reference !!! Py_INCREF(spool_dict); PyTuple_SetItem(pyargs, 0, spool_dict); ret = python_call(spool_func, pyargs, 0, NULL); if (ret) { if (!PyInt_Check(ret)) { retval = -1; // error, retry } else { retval = (int) PyInt_AsLong(ret); } goto clear; } if (PyErr_Occurred()) PyErr_Print(); // error, retry retval = -1; clear: Py_XDECREF(ret); Py_XDECREF(pyargs); Py_XDECREF(spool_dict); UWSGI_RELEASE_GIL; return retval; } void uwsgi_python_resume(struct wsgi_request *wsgi_req) { PyGILState_STATE pgst = PyGILState_Ensure(); PyThreadState *tstate = PyThreadState_GET(); PyGILState_Release(pgst); if (wsgi_req) { tstate->recursion_depth = up.current_recursion_depth[wsgi_req->async_id]; tstate->frame = up.current_frame[wsgi_req->async_id]; } else { tstate->recursion_depth = up.current_main_recursion_depth; tstate->frame = up.current_main_frame; } } void uwsgi_python_fixup() { // set hacky modifier 30 uwsgi.p[30] = uwsgi_malloc( sizeof(struct uwsgi_plugin) ); memcpy(uwsgi.p[30], uwsgi.p[0], sizeof(struct uwsgi_plugin) ); uwsgi.p[30]->init_thread = NULL; uwsgi.p[30]->atexit = NULL; } void uwsgi_python_hijack(void) { // the pyshell will be execute only in the first worker FILE *pyfile; if (up.pyrun) { uwsgi.workers[uwsgi.mywid].hijacked = 1; UWSGI_GET_GIL; pyfile = fopen(up.pyrun, "r"); if (!pyfile) { uwsgi_error_open(up.pyrun); exit(1); } PyRun_SimpleFile(pyfile, up.pyrun); // could be never executed exit(0); } if (up.pyshell_oneshot && uwsgi.workers[uwsgi.mywid].hijacked_count > 0) { uwsgi.workers[uwsgi.mywid].hijacked = 0; return; } if (up.pyshell && uwsgi.mywid == 1) { uwsgi.workers[uwsgi.mywid].hijacked = 1; uwsgi.workers[uwsgi.mywid].hijacked_count++; // re-map stdin to stdout and stderr if we are logging to a file if (uwsgi.logfile) { if (dup2(0, 1) < 0) { uwsgi_error("dup2()"); } if (dup2(0, 2) < 0) { uwsgi_error("dup2()"); } } UWSGI_GET_GIL; int ret = -1; if (up.pyshell[0] != 0) { ret = PyRun_SimpleString(up.pyshell); } else { PyImport_ImportModule("readline"); ret = PyRun_InteractiveLoop(stdin, "uwsgi"); } if (up.pyshell_oneshot) { exit(UWSGI_DE_HIJACKED_CODE); } if (ret == 0) { exit(UWSGI_QUIET_CODE); } exit(0); } } int uwsgi_python_mule(char *opt) { if (uwsgi_endswith(opt, ".py")) { UWSGI_GET_GIL; uwsgi_pyimport_by_filename("__main__", opt); UWSGI_RELEASE_GIL; return 1; } else if (strchr(opt, ':')) { UWSGI_GET_GIL; PyObject *result = NULL; PyObject *arglist = Py_BuildValue("()"); PyObject *callable = up.loaders[LOADER_MOUNT](opt); if (callable) { result = PyEval_CallObject(callable, arglist); } Py_XDECREF(result); Py_XDECREF(arglist); Py_XDECREF(callable); UWSGI_RELEASE_GIL; return 1; } return 0; } int uwsgi_python_mule_msg(char *message, size_t len) { UWSGI_GET_GIL; PyObject *mule_msg_hook = PyDict_GetItemString(up.embedded_dict, "mule_msg_hook"); PyObject *mule_msg_extra_hooks = PyDict_GetItemString(up.embedded_dict, "mule_msg_extra_hooks"); if (!mule_msg_hook && !mule_msg_extra_hooks) { // ignore UWSGI_RELEASE_GIL; return 0; } PyObject *pyargs = PyTuple_New(1); PyTuple_SetItem(pyargs, 0, PyString_FromStringAndSize(message, len)); // Maintain compatibility with old hook plugin behavior if (mule_msg_hook) { PyObject *ret = python_call(mule_msg_hook, pyargs, 0, NULL); if (ret) { Py_DECREF(ret); } if (PyErr_Occurred()) PyErr_Print(); } if (mule_msg_extra_hooks) { Py_ssize_t listlen = PyList_Size(mule_msg_extra_hooks); Py_ssize_t idx = 0; for (; idx < listlen; idx++) { PyObject *hook = PyList_GET_ITEM(mule_msg_extra_hooks, idx); PyObject *ret = python_call(hook, pyargs, 0, NULL); if (ret) { Py_DECREF(ret); } if (PyErr_Occurred()) PyErr_Print(); } } Py_DECREF(pyargs); UWSGI_RELEASE_GIL; return 1; } static void uwsgi_python_harakiri(int wid) { if (up.tracebacker) { char buf[8192]; char *wid_str = uwsgi_num2str(wid); char *address = uwsgi_concat2(up.tracebacker, wid_str); int fd = uwsgi_connect(address, -1, 0); if (fd < 1) goto exit; for(;;) { int ret = uwsgi_waitfd(fd, uwsgi.socket_timeout); if (ret <= 0) goto cleanup; ssize_t len = read(fd, buf, 8192); if (len <= 0) goto cleanup; uwsgi_log("%.*s", (int) len, buf); } cleanup: close(fd); exit: free(wid_str); free(address); } } /* # you can use this logger to offload logging to python # be sure to configure it to not log to stderr otherwise you will generate a loop import logging logging.basicConfig(filename='/tmp/pippo.log') */ static ssize_t uwsgi_python_logger(struct uwsgi_logger *ul, char *message, size_t len) { if (!Py_IsInitialized()) return -1; UWSGI_GET_GIL if (!ul->configured) { PyObject *py_logging = PyImport_ImportModule("logging"); if (!py_logging) goto clear; PyObject *py_logging_dict = PyModule_GetDict(py_logging); if (!py_logging_dict) goto clear; PyObject *py_getLogger = PyDict_GetItemString(py_logging_dict, "getLogger"); if (!py_getLogger) goto clear; PyObject *py_getLogger_args = NULL; if (ul->arg) { py_getLogger_args = PyTuple_New(1); PyTuple_SetItem(py_getLogger_args, 0, UWSGI_PYFROMSTRING(ul->arg)); } ul->data = (void *) PyEval_CallObject(py_getLogger, py_getLogger_args); if (PyErr_Occurred()) { PyErr_Clear(); } Py_XDECREF(py_getLogger_args); if (!ul->data) goto clear; ul->configured = 1; } PyObject_CallMethod((PyObject *) ul->data, "error", "(s#)", message, len); if (PyErr_Occurred()) { PyErr_Clear(); } UWSGI_RELEASE_GIL return len; clear: UWSGI_RELEASE_GIL return -1; } static void uwsgi_python_on_load() { uwsgi_register_logger("python", uwsgi_python_logger); } static int uwsgi_python_worker() { if (!up.worker_override) return 0; UWSGI_GET_GIL; // ensure signals can be used again from python if (!up.call_osafterfork) #ifdef HAS_NOT_PyOS_AfterFork_Child PyOS_AfterFork(); #else PyOS_AfterFork_Child(); #endif FILE *pyfile = fopen(up.worker_override, "r"); if (!pyfile) { uwsgi_error_open(up.worker_override); exit(1); } PyRun_SimpleFile(pyfile, up.worker_override); return 1; } static void uwsgi_python_master_cycle() { if (up.master_check_signals) { UWSGI_GET_GIL; // run python signal handlers if any scheduled if (PyErr_CheckSignals()) { uwsgi_log("exception in python signal handler\n"); PyErr_Print(); UWSGI_RELEASE_GIL; exit(1); } UWSGI_RELEASE_GIL; } } struct uwsgi_plugin python_plugin = { .name = "python", .alias = "python", .modifier1 = 0, .init = uwsgi_python_init, .post_fork = uwsgi_python_post_fork, .options = uwsgi_python_options, .request = uwsgi_request_wsgi, .after_request = uwsgi_after_request_wsgi, .preinit_apps = uwsgi_python_preinit_apps, .init_apps = uwsgi_python_init_apps, .fixup = uwsgi_python_fixup, .master_fixup = uwsgi_python_master_fixup, .master_cycle = uwsgi_python_master_cycle, .mount_app = uwsgi_python_mount_app, .enable_threads = uwsgi_python_enable_threads, .init_thread = uwsgi_python_init_thread, .magic = uwsgi_python_magic, .suspend = uwsgi_python_suspend, .resume = uwsgi_python_resume, .harakiri = uwsgi_python_harakiri, .hijack_worker = uwsgi_python_hijack, .spooler_init = uwsgi_python_spooler_init, .signal_handler = uwsgi_python_signal_handler, .rpc = uwsgi_python_rpc, .mule = uwsgi_python_mule, .mule_msg = uwsgi_python_mule_msg, .on_load = uwsgi_python_on_load, .spooler = uwsgi_python_spooler, .atexit = uwsgi_python_atexit, .code_string = uwsgi_python_code_string, .exception_class = uwsgi_python_exception_class, .exception_msg = uwsgi_python_exception_msg, .exception_repr = uwsgi_python_exception_repr, .exception_log = uwsgi_python_exception_log, .backtrace = uwsgi_python_backtrace, .worker = uwsgi_python_worker, };
X Tutup