xref: /unit/src/python/nxt_python.c (revision 1918)
11592Smax.romanov@nginx.com 
21592Smax.romanov@nginx.com /*
31592Smax.romanov@nginx.com  * Copyright (C) NGINX, Inc.
41592Smax.romanov@nginx.com  */
51592Smax.romanov@nginx.com 
61592Smax.romanov@nginx.com 
71592Smax.romanov@nginx.com #include <Python.h>
81592Smax.romanov@nginx.com 
91592Smax.romanov@nginx.com #include <nxt_main.h>
101592Smax.romanov@nginx.com #include <nxt_router.h>
111592Smax.romanov@nginx.com #include <nxt_unit.h>
121592Smax.romanov@nginx.com 
131592Smax.romanov@nginx.com #include <python/nxt_python.h>
141592Smax.romanov@nginx.com 
151592Smax.romanov@nginx.com #include NXT_PYTHON_MOUNTS_H
161592Smax.romanov@nginx.com 
171592Smax.romanov@nginx.com 
181681Smax.romanov@nginx.com typedef struct {
191681Smax.romanov@nginx.com     pthread_t       thread;
201681Smax.romanov@nginx.com     nxt_unit_ctx_t  *ctx;
211681Smax.romanov@nginx.com     void            *ctx_data;
221681Smax.romanov@nginx.com } nxt_py_thread_info_t;
231681Smax.romanov@nginx.com 
241681Smax.romanov@nginx.com 
251592Smax.romanov@nginx.com static nxt_int_t nxt_python_start(nxt_task_t *task,
261592Smax.romanov@nginx.com     nxt_process_data_t *data);
271872So.canty@f5.com static nxt_int_t nxt_python_set_target(nxt_task_t *task,
281872So.canty@f5.com     nxt_python_target_t *target, nxt_conf_value_t *conf);
291759Svbart@nginx.com static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
301681Smax.romanov@nginx.com static int nxt_python_init_threads(nxt_python_app_conf_t *c);
311681Smax.romanov@nginx.com static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
321681Smax.romanov@nginx.com static void *nxt_python_thread_func(void *main_ctx);
331681Smax.romanov@nginx.com static void nxt_python_join_threads(nxt_unit_ctx_t *ctx,
341681Smax.romanov@nginx.com     nxt_python_app_conf_t *c);
351592Smax.romanov@nginx.com static void nxt_python_atexit(void);
361592Smax.romanov@nginx.com 
371592Smax.romanov@nginx.com static uint32_t  compat[] = {
381592Smax.romanov@nginx.com     NXT_VERNUM, NXT_DEBUG,
391592Smax.romanov@nginx.com };
401592Smax.romanov@nginx.com 
411592Smax.romanov@nginx.com 
421592Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t  nxt_app_module = {
431592Smax.romanov@nginx.com     sizeof(compat),
441592Smax.romanov@nginx.com     compat,
451592Smax.romanov@nginx.com     nxt_string("python"),
461592Smax.romanov@nginx.com     PY_VERSION,
471592Smax.romanov@nginx.com     nxt_python_mounts,
481592Smax.romanov@nginx.com     nxt_nitems(nxt_python_mounts),
491592Smax.romanov@nginx.com     NULL,
501592Smax.romanov@nginx.com     nxt_python_start,
511592Smax.romanov@nginx.com };
521592Smax.romanov@nginx.com 
531592Smax.romanov@nginx.com static PyObject           *nxt_py_stderr_flush;
541872So.canty@f5.com nxt_python_targets_t      *nxt_py_targets;
551592Smax.romanov@nginx.com 
561592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
571592Smax.romanov@nginx.com static wchar_t            *nxt_py_home;
581592Smax.romanov@nginx.com #else
591592Smax.romanov@nginx.com static char               *nxt_py_home;
601592Smax.romanov@nginx.com #endif
611592Smax.romanov@nginx.com 
621681Smax.romanov@nginx.com static pthread_attr_t        *nxt_py_thread_attr;
631681Smax.romanov@nginx.com static nxt_py_thread_info_t  *nxt_py_threads;
641681Smax.romanov@nginx.com static nxt_python_proto_t    nxt_py_proto;
651681Smax.romanov@nginx.com 
661592Smax.romanov@nginx.com 
671592Smax.romanov@nginx.com static nxt_int_t
681592Smax.romanov@nginx.com nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
691592Smax.romanov@nginx.com {
701681Smax.romanov@nginx.com     int                    rc;
711872So.canty@f5.com     size_t                 len, size;
721872So.canty@f5.com     uint32_t               next;
731759Svbart@nginx.com     PyObject               *obj, *module;
741872So.canty@f5.com     nxt_str_t              proto, probe_proto, name;
751872So.canty@f5.com     nxt_int_t              ret, n, i;
761592Smax.romanov@nginx.com     nxt_unit_ctx_t         *unit_ctx;
771592Smax.romanov@nginx.com     nxt_unit_init_t        python_init;
781872So.canty@f5.com     nxt_conf_value_t       *cv;
791872So.canty@f5.com     nxt_python_targets_t   *targets;
801592Smax.romanov@nginx.com     nxt_common_app_conf_t  *app_conf;
811592Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
821592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
831592Smax.romanov@nginx.com     char                   *path;
841592Smax.romanov@nginx.com     nxt_int_t              pep405;
851592Smax.romanov@nginx.com 
861592Smax.romanov@nginx.com     static const char pyvenv[] = "/pyvenv.cfg";
871592Smax.romanov@nginx.com     static const char bin_python[] = "/bin/python";
881592Smax.romanov@nginx.com #endif
891592Smax.romanov@nginx.com 
901697Smax.romanov@nginx.com     static const nxt_str_t  wsgi = nxt_string("wsgi");
911697Smax.romanov@nginx.com     static const nxt_str_t  asgi = nxt_string("asgi");
921697Smax.romanov@nginx.com 
931592Smax.romanov@nginx.com     app_conf = data->app;
941592Smax.romanov@nginx.com     c = &app_conf->u.python;
951592Smax.romanov@nginx.com 
961592Smax.romanov@nginx.com     if (c->home != NULL) {
971592Smax.romanov@nginx.com         len = nxt_strlen(c->home);
981592Smax.romanov@nginx.com 
991592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
1001592Smax.romanov@nginx.com 
1011592Smax.romanov@nginx.com         path = nxt_malloc(len + sizeof(pyvenv));
1021592Smax.romanov@nginx.com         if (nxt_slow_path(path == NULL)) {
1031592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1041592Smax.romanov@nginx.com             return NXT_ERROR;
1051592Smax.romanov@nginx.com         }
1061592Smax.romanov@nginx.com 
1071592Smax.romanov@nginx.com         nxt_memcpy(path, c->home, len);
1081592Smax.romanov@nginx.com         nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
1091592Smax.romanov@nginx.com 
1101592Smax.romanov@nginx.com         pep405 = (access(path, R_OK) == 0);
1111592Smax.romanov@nginx.com 
1121592Smax.romanov@nginx.com         nxt_free(path);
1131592Smax.romanov@nginx.com 
1141592Smax.romanov@nginx.com         if (pep405) {
1151592Smax.romanov@nginx.com             size = (len + sizeof(bin_python)) * sizeof(wchar_t);
1161592Smax.romanov@nginx.com 
1171592Smax.romanov@nginx.com         } else {
1181592Smax.romanov@nginx.com             size = (len + 1) * sizeof(wchar_t);
1191592Smax.romanov@nginx.com         }
1201592Smax.romanov@nginx.com 
1211592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(size);
1221592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
1231592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1241592Smax.romanov@nginx.com             return NXT_ERROR;
1251592Smax.romanov@nginx.com         }
1261592Smax.romanov@nginx.com 
1271592Smax.romanov@nginx.com         if (pep405) {
1281592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len);
1291592Smax.romanov@nginx.com             mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
1301592Smax.romanov@nginx.com             Py_SetProgramName(nxt_py_home);
1311592Smax.romanov@nginx.com 
1321592Smax.romanov@nginx.com         } else {
1331592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len + 1);
1341592Smax.romanov@nginx.com             Py_SetPythonHome(nxt_py_home);
1351592Smax.romanov@nginx.com         }
1361592Smax.romanov@nginx.com 
1371592Smax.romanov@nginx.com #else
1381592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(len + 1);
1391592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
1401592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1411592Smax.romanov@nginx.com             return NXT_ERROR;
1421592Smax.romanov@nginx.com         }
1431592Smax.romanov@nginx.com 
1441592Smax.romanov@nginx.com         nxt_memcpy(nxt_py_home, c->home, len + 1);
1451592Smax.romanov@nginx.com         Py_SetPythonHome(nxt_py_home);
1461592Smax.romanov@nginx.com #endif
1471592Smax.romanov@nginx.com     }
1481592Smax.romanov@nginx.com 
1491592Smax.romanov@nginx.com     Py_InitializeEx(0);
1501592Smax.romanov@nginx.com 
1511681Smax.romanov@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
1521681Smax.romanov@nginx.com     if (c->threads > 1) {
1531681Smax.romanov@nginx.com         PyEval_InitThreads();
1541681Smax.romanov@nginx.com     }
1551681Smax.romanov@nginx.com #endif
1561681Smax.romanov@nginx.com 
1571592Smax.romanov@nginx.com     module = NULL;
1581592Smax.romanov@nginx.com     obj = NULL;
1591592Smax.romanov@nginx.com 
1601681Smax.romanov@nginx.com     python_init.ctx_data = NULL;
1611681Smax.romanov@nginx.com 
1621592Smax.romanov@nginx.com     obj = PySys_GetObject((char *) "stderr");
1631592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
1641592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"sys.stderr\" object");
1651592Smax.romanov@nginx.com         goto fail;
1661592Smax.romanov@nginx.com     }
1671592Smax.romanov@nginx.com 
1681592Smax.romanov@nginx.com     nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
1691759Svbart@nginx.com 
1701759Svbart@nginx.com     /* obj is a Borrowed reference. */
1711759Svbart@nginx.com     obj = NULL;
1721759Svbart@nginx.com 
1731592Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
1741592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"flush\" attribute of "
1751592Smax.romanov@nginx.com                         "\"sys.stderr\" object");
1761592Smax.romanov@nginx.com         goto fail;
1771592Smax.romanov@nginx.com     }
1781592Smax.romanov@nginx.com 
1791759Svbart@nginx.com     if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) {
1801759Svbart@nginx.com         goto fail;
1811592Smax.romanov@nginx.com     }
1821592Smax.romanov@nginx.com 
1831592Smax.romanov@nginx.com     obj = Py_BuildValue("[s]", "unit");
1841592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
1851592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to create the \"sys.argv\" list");
1861592Smax.romanov@nginx.com         goto fail;
1871592Smax.romanov@nginx.com     }
1881592Smax.romanov@nginx.com 
1891592Smax.romanov@nginx.com     if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
1901592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to set the \"sys.argv\" list");
1911592Smax.romanov@nginx.com         goto fail;
1921592Smax.romanov@nginx.com     }
1931592Smax.romanov@nginx.com 
1941592Smax.romanov@nginx.com     Py_CLEAR(obj);
1951592Smax.romanov@nginx.com 
1961872So.canty@f5.com     n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
1971872So.canty@f5.com 
1981872So.canty@f5.com     size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
1991592Smax.romanov@nginx.com 
2001872So.canty@f5.com     targets = nxt_unit_malloc(NULL, size);
2011872So.canty@f5.com     if (nxt_slow_path(targets == NULL)) {
2021872So.canty@f5.com         nxt_alert(task, "Could not allocate targets");
2031592Smax.romanov@nginx.com         goto fail;
2041592Smax.romanov@nginx.com     }
2051592Smax.romanov@nginx.com 
2061872So.canty@f5.com     memset(targets, 0, size);
2071601Smax.romanov@nginx.com 
2081872So.canty@f5.com     targets->count = n;
2091872So.canty@f5.com     nxt_py_targets = targets;
2101872So.canty@f5.com 
2111872So.canty@f5.com     if (c->targets != NULL) {
2121872So.canty@f5.com         next = 0;
2131592Smax.romanov@nginx.com 
2141872So.canty@f5.com         for (i = 0; /* void */; i++) {
2151872So.canty@f5.com             cv = nxt_conf_next_object_member(c->targets, &name, &next);
2161872So.canty@f5.com             if (cv == NULL) {
2171872So.canty@f5.com                 break;
2181872So.canty@f5.com             }
2191592Smax.romanov@nginx.com 
2201872So.canty@f5.com             ret = nxt_python_set_target(task, &targets->target[i], cv);
2211872So.canty@f5.com             if (nxt_slow_path(ret != NXT_OK)) {
2221872So.canty@f5.com                 goto fail;
2231872So.canty@f5.com             }
2241872So.canty@f5.com         }
2251592Smax.romanov@nginx.com 
2261872So.canty@f5.com     } else {
2271872So.canty@f5.com         ret = nxt_python_set_target(task, &targets->target[0], app_conf->self);
2281872So.canty@f5.com         if (nxt_slow_path(ret != NXT_OK)) {
2291872So.canty@f5.com             goto fail;
2301872So.canty@f5.com         }
2311872So.canty@f5.com     }
2321592Smax.romanov@nginx.com 
2331592Smax.romanov@nginx.com     nxt_unit_default_init(task, &python_init);
2341592Smax.romanov@nginx.com 
2351681Smax.romanov@nginx.com     python_init.data = c;
2361592Smax.romanov@nginx.com     python_init.shm_limit = data->app->shm_limit;
2371681Smax.romanov@nginx.com     python_init.callbacks.ready_handler = nxt_python_ready_handler;
2381592Smax.romanov@nginx.com 
2391697Smax.romanov@nginx.com     proto = c->protocol;
2401697Smax.romanov@nginx.com 
2411697Smax.romanov@nginx.com     if (proto.length == 0) {
2421872So.canty@f5.com         proto = nxt_python_asgi_check(targets->target[0].application)
2431872So.canty@f5.com                 ? asgi : wsgi;
2441872So.canty@f5.com 
2451872So.canty@f5.com         for (i = 1; i < targets->count; i++) {
2461872So.canty@f5.com             probe_proto = nxt_python_asgi_check(targets->target[i].application)
2471872So.canty@f5.com                           ? asgi : wsgi;
2481872So.canty@f5.com             if (probe_proto.start != proto.start) {
2491872So.canty@f5.com                 nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
2501872So.canty@f5.com                                 "specify protocol in config if incorrect");
2511872So.canty@f5.com                 goto fail;
2521872So.canty@f5.com             }
2531872So.canty@f5.com         }
2541697Smax.romanov@nginx.com     }
2551697Smax.romanov@nginx.com 
2561697Smax.romanov@nginx.com     if (nxt_strstr_eq(&proto, &asgi)) {
2571681Smax.romanov@nginx.com         rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
2581624Smax.romanov@nginx.com 
2591624Smax.romanov@nginx.com     } else {
2601681Smax.romanov@nginx.com         rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
2611681Smax.romanov@nginx.com     }
2621681Smax.romanov@nginx.com 
2631681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
2641681Smax.romanov@nginx.com         goto fail;
2651624Smax.romanov@nginx.com     }
2661624Smax.romanov@nginx.com 
267*1918Smax.romanov@nginx.com     rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
2681681Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
2691592Smax.romanov@nginx.com         goto fail;
2701592Smax.romanov@nginx.com     }
2711592Smax.romanov@nginx.com 
2721681Smax.romanov@nginx.com     rc = nxt_python_init_threads(c);
2731681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
2741681Smax.romanov@nginx.com         goto fail;
2751681Smax.romanov@nginx.com     }
2761681Smax.romanov@nginx.com 
2771681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
2781681Smax.romanov@nginx.com         if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
2791681Smax.romanov@nginx.com             goto fail;
2801681Smax.romanov@nginx.com         }
2811681Smax.romanov@nginx.com     }
2821681Smax.romanov@nginx.com 
2831592Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&python_init);
2841592Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
2851592Smax.romanov@nginx.com         goto fail;
2861592Smax.romanov@nginx.com     }
2871592Smax.romanov@nginx.com 
2881681Smax.romanov@nginx.com     rc = nxt_py_proto.run(unit_ctx);
2891624Smax.romanov@nginx.com 
2901681Smax.romanov@nginx.com     nxt_python_join_threads(unit_ctx, c);
2911592Smax.romanov@nginx.com 
2921592Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
2931592Smax.romanov@nginx.com 
2941681Smax.romanov@nginx.com     nxt_py_proto.ctx_data_free(python_init.ctx_data);
2951681Smax.romanov@nginx.com 
2961592Smax.romanov@nginx.com     nxt_python_atexit();
2971592Smax.romanov@nginx.com 
2981592Smax.romanov@nginx.com     exit(rc);
2991592Smax.romanov@nginx.com 
3001592Smax.romanov@nginx.com     return NXT_OK;
3011592Smax.romanov@nginx.com 
3021592Smax.romanov@nginx.com fail:
3031592Smax.romanov@nginx.com 
3041681Smax.romanov@nginx.com     nxt_python_join_threads(NULL, c);
3051681Smax.romanov@nginx.com 
3061681Smax.romanov@nginx.com     if (python_init.ctx_data != NULL) {
3071681Smax.romanov@nginx.com         nxt_py_proto.ctx_data_free(python_init.ctx_data);
3081681Smax.romanov@nginx.com     }
3091681Smax.romanov@nginx.com 
3101592Smax.romanov@nginx.com     Py_XDECREF(obj);
3111592Smax.romanov@nginx.com     Py_XDECREF(module);
3121592Smax.romanov@nginx.com 
3131592Smax.romanov@nginx.com     nxt_python_atexit();
3141592Smax.romanov@nginx.com 
3151592Smax.romanov@nginx.com     return NXT_ERROR;
3161592Smax.romanov@nginx.com }
3171592Smax.romanov@nginx.com 
3181592Smax.romanov@nginx.com 
3191759Svbart@nginx.com static nxt_int_t
3201872So.canty@f5.com nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
3211872So.canty@f5.com     nxt_conf_value_t *conf)
3221872So.canty@f5.com {
3231872So.canty@f5.com     char              *callable, *module_name;
3241872So.canty@f5.com     PyObject          *module, *obj;
3251872So.canty@f5.com     nxt_str_t         str;
3261872So.canty@f5.com     nxt_conf_value_t  *value;
3271872So.canty@f5.com 
3281872So.canty@f5.com     static nxt_str_t  module_str = nxt_string("module");
3291872So.canty@f5.com     static nxt_str_t  callable_str = nxt_string("callable");
3301872So.canty@f5.com 
3311872So.canty@f5.com     module = obj = NULL;
3321872So.canty@f5.com 
3331872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &module_str, NULL);
3341872So.canty@f5.com     if (nxt_slow_path(value == NULL)) {
3351872So.canty@f5.com         goto fail;
3361872So.canty@f5.com     }
3371872So.canty@f5.com 
3381872So.canty@f5.com     nxt_conf_get_string(value, &str);
3391872So.canty@f5.com 
3401872So.canty@f5.com     module_name = nxt_alloca(str.length + 1);
3411872So.canty@f5.com     nxt_memcpy(module_name, str.start, str.length);
3421872So.canty@f5.com     module_name[str.length] = '\0';
3431872So.canty@f5.com 
3441872So.canty@f5.com     module = PyImport_ImportModule(module_name);
3451872So.canty@f5.com     if (nxt_slow_path(module == NULL)) {
3461872So.canty@f5.com         nxt_alert(task, "Python failed to import module \"%s\"", module_name);
3471872So.canty@f5.com         nxt_python_print_exception();
3481872So.canty@f5.com         goto fail;
3491872So.canty@f5.com     }
3501872So.canty@f5.com 
3511872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &callable_str, NULL);
3521872So.canty@f5.com     if (value == NULL) {
3531872So.canty@f5.com         callable = nxt_alloca(12);
3541872So.canty@f5.com         nxt_memcpy(callable, "application", 12);
3551872So.canty@f5.com 
3561872So.canty@f5.com     } else {
3571872So.canty@f5.com         nxt_conf_get_string(value, &str);
3581872So.canty@f5.com 
3591872So.canty@f5.com         callable = nxt_alloca(str.length + 1);
3601872So.canty@f5.com         nxt_memcpy(callable, str.start, str.length);
3611872So.canty@f5.com         callable[str.length] = '\0';
3621872So.canty@f5.com     }
3631872So.canty@f5.com 
3641872So.canty@f5.com     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
3651872So.canty@f5.com     if (nxt_slow_path(obj == NULL)) {
3661872So.canty@f5.com         nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
3671872So.canty@f5.com                   callable, module);
3681872So.canty@f5.com         goto fail;
3691872So.canty@f5.com     }
3701872So.canty@f5.com 
3711872So.canty@f5.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
3721872So.canty@f5.com         nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
3731872So.canty@f5.com                   callable, module);
3741872So.canty@f5.com         goto fail;
3751872So.canty@f5.com     }
3761872So.canty@f5.com 
3771872So.canty@f5.com     target->application = obj;
3781872So.canty@f5.com     obj = NULL;
3791872So.canty@f5.com 
3801872So.canty@f5.com     Py_INCREF(target->application);
3811872So.canty@f5.com     Py_CLEAR(module);
3821872So.canty@f5.com 
3831872So.canty@f5.com     return NXT_OK;
3841872So.canty@f5.com 
3851872So.canty@f5.com fail:
3861872So.canty@f5.com 
3871872So.canty@f5.com     Py_XDECREF(obj);
3881872So.canty@f5.com     Py_XDECREF(module);
3891872So.canty@f5.com 
3901872So.canty@f5.com     return NXT_ERROR;
3911872So.canty@f5.com }
3921872So.canty@f5.com 
3931872So.canty@f5.com 
3941872So.canty@f5.com static nxt_int_t
3951759Svbart@nginx.com nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
3961759Svbart@nginx.com {
3971759Svbart@nginx.com     int               ret;
3981759Svbart@nginx.com     PyObject          *path, *sys;
3991759Svbart@nginx.com     nxt_str_t         str;
4001759Svbart@nginx.com     nxt_uint_t        n;
4011759Svbart@nginx.com     nxt_conf_value_t  *array;
4021759Svbart@nginx.com 
4031759Svbart@nginx.com     if (value == NULL) {
4041759Svbart@nginx.com         return NXT_OK;
4051759Svbart@nginx.com     }
4061759Svbart@nginx.com 
4071759Svbart@nginx.com     sys = PySys_GetObject((char *) "path");
4081759Svbart@nginx.com     if (nxt_slow_path(sys == NULL)) {
4091759Svbart@nginx.com         nxt_alert(task, "Python failed to get \"sys.path\" list");
4101759Svbart@nginx.com         return NXT_ERROR;
4111759Svbart@nginx.com     }
4121759Svbart@nginx.com 
4131759Svbart@nginx.com     /* sys is a Borrowed reference. */
4141759Svbart@nginx.com 
4151759Svbart@nginx.com     if (nxt_conf_type(value) == NXT_CONF_STRING) {
4161759Svbart@nginx.com         n = 0;
4171759Svbart@nginx.com         goto value_is_string;
4181759Svbart@nginx.com     }
4191759Svbart@nginx.com 
4201759Svbart@nginx.com     /* NXT_CONF_ARRAY */
4211759Svbart@nginx.com     array = value;
4221759Svbart@nginx.com 
4231759Svbart@nginx.com     n = nxt_conf_array_elements_count(array);
4241759Svbart@nginx.com 
4251759Svbart@nginx.com     while (n != 0) {
4261759Svbart@nginx.com         n--;
4271759Svbart@nginx.com 
4281759Svbart@nginx.com         /*
4291759Svbart@nginx.com          * Insertion in front of existing paths starting from the last element
4301759Svbart@nginx.com          * to preserve original order while giving priority to the values
4311759Svbart@nginx.com          * specified in the "path" option.
4321759Svbart@nginx.com          */
4331759Svbart@nginx.com 
4341759Svbart@nginx.com         value = nxt_conf_get_array_element(array, n);
4351759Svbart@nginx.com 
4361759Svbart@nginx.com     value_is_string:
4371759Svbart@nginx.com 
4381759Svbart@nginx.com         nxt_conf_get_string(value, &str);
4391759Svbart@nginx.com 
4401759Svbart@nginx.com         path = PyString_FromStringAndSize((char *) str.start, str.length);
4411759Svbart@nginx.com         if (nxt_slow_path(path == NULL)) {
4421759Svbart@nginx.com             nxt_alert(task, "Python failed to create string object \"%V\"",
4431759Svbart@nginx.com                       &str);
4441759Svbart@nginx.com             return NXT_ERROR;
4451759Svbart@nginx.com         }
4461759Svbart@nginx.com 
4471759Svbart@nginx.com         ret = PyList_Insert(sys, 0, path);
4481759Svbart@nginx.com 
4491759Svbart@nginx.com         Py_DECREF(path);
4501759Svbart@nginx.com 
4511759Svbart@nginx.com         if (nxt_slow_path(ret != 0)) {
4521759Svbart@nginx.com             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
4531759Svbart@nginx.com                       &str);
4541759Svbart@nginx.com             return NXT_ERROR;
4551759Svbart@nginx.com         }
4561759Svbart@nginx.com     }
4571759Svbart@nginx.com 
4581759Svbart@nginx.com     return NXT_OK;
4591759Svbart@nginx.com }
4601759Svbart@nginx.com 
4611759Svbart@nginx.com 
4621681Smax.romanov@nginx.com static int
4631681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c)
4641681Smax.romanov@nginx.com {
4651681Smax.romanov@nginx.com     int                    res;
4661681Smax.romanov@nginx.com     uint32_t               i;
4671681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
4681681Smax.romanov@nginx.com     static pthread_attr_t  attr;
4691681Smax.romanov@nginx.com 
4701681Smax.romanov@nginx.com     if (c->threads <= 1) {
4711681Smax.romanov@nginx.com         return NXT_UNIT_OK;
4721681Smax.romanov@nginx.com     }
4731681Smax.romanov@nginx.com 
4741681Smax.romanov@nginx.com     if (c->thread_stack_size > 0) {
4751681Smax.romanov@nginx.com         res = pthread_attr_init(&attr);
4761681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
4771681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
4781681Smax.romanov@nginx.com                            strerror(res), res);
4791681Smax.romanov@nginx.com 
4801681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
4811681Smax.romanov@nginx.com         }
4821681Smax.romanov@nginx.com 
4831681Smax.romanov@nginx.com         res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
4841681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
4851681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
4861681Smax.romanov@nginx.com                            strerror(res), res);
4871681Smax.romanov@nginx.com 
4881681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
4891681Smax.romanov@nginx.com         }
4901681Smax.romanov@nginx.com 
4911681Smax.romanov@nginx.com         nxt_py_thread_attr = &attr;
4921681Smax.romanov@nginx.com     }
4931681Smax.romanov@nginx.com 
4941681Smax.romanov@nginx.com     nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
4951681Smax.romanov@nginx.com                                            * (c->threads - 1));
4961681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_threads == NULL)) {
4971681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate thread info array");
4981681Smax.romanov@nginx.com 
4991681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
5001681Smax.romanov@nginx.com     }
5011681Smax.romanov@nginx.com 
5021681Smax.romanov@nginx.com     memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
5031681Smax.romanov@nginx.com 
5041681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
5051681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
5061681Smax.romanov@nginx.com 
507*1918Smax.romanov@nginx.com         res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
5081681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
5091681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
5101681Smax.romanov@nginx.com         }
5111681Smax.romanov@nginx.com     }
5121681Smax.romanov@nginx.com 
5131681Smax.romanov@nginx.com     return NXT_UNIT_OK;
5141681Smax.romanov@nginx.com }
5151681Smax.romanov@nginx.com 
5161681Smax.romanov@nginx.com 
5171681Smax.romanov@nginx.com static int
5181681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
5191681Smax.romanov@nginx.com {
5201681Smax.romanov@nginx.com     int                    res;
5211681Smax.romanov@nginx.com     uint32_t               i;
5221681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
5231681Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
5241681Smax.romanov@nginx.com 
5251681Smax.romanov@nginx.com     if (nxt_py_proto.ready != NULL) {
5261681Smax.romanov@nginx.com         res = nxt_py_proto.ready(ctx);
5271681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
5281681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
5291681Smax.romanov@nginx.com         }
5301681Smax.romanov@nginx.com     }
5311681Smax.romanov@nginx.com 
5321681Smax.romanov@nginx.com     /* Worker thread context. */
5331681Smax.romanov@nginx.com     if (!nxt_unit_is_main_ctx(ctx)) {
5341681Smax.romanov@nginx.com         return NXT_UNIT_OK;
5351681Smax.romanov@nginx.com     }
5361681Smax.romanov@nginx.com 
5371681Smax.romanov@nginx.com     c = ctx->unit->data;
5381681Smax.romanov@nginx.com 
5391681Smax.romanov@nginx.com     if (c->threads <= 1) {
5401681Smax.romanov@nginx.com         return NXT_UNIT_OK;
5411681Smax.romanov@nginx.com     }
5421681Smax.romanov@nginx.com 
5431681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
5441681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
5451681Smax.romanov@nginx.com 
5461681Smax.romanov@nginx.com         ti->ctx = ctx;
5471681Smax.romanov@nginx.com 
5481681Smax.romanov@nginx.com         res = pthread_create(&ti->thread, nxt_py_thread_attr,
5491681Smax.romanov@nginx.com                              nxt_python_thread_func, ti);
5501681Smax.romanov@nginx.com 
5511681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
5521681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
5531681Smax.romanov@nginx.com 
5541681Smax.romanov@nginx.com         } else {
5551681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
5561681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
5571681Smax.romanov@nginx.com         }
5581681Smax.romanov@nginx.com     }
5591681Smax.romanov@nginx.com 
5601681Smax.romanov@nginx.com     return NXT_UNIT_OK;
5611681Smax.romanov@nginx.com }
5621681Smax.romanov@nginx.com 
5631681Smax.romanov@nginx.com 
5641681Smax.romanov@nginx.com static void *
5651681Smax.romanov@nginx.com nxt_python_thread_func(void *data)
5661681Smax.romanov@nginx.com {
5671681Smax.romanov@nginx.com     nxt_unit_ctx_t        *ctx;
5681681Smax.romanov@nginx.com     PyGILState_STATE      gstate;
5691681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
5701681Smax.romanov@nginx.com 
5711681Smax.romanov@nginx.com     ti = data;
5721681Smax.romanov@nginx.com 
5731681Smax.romanov@nginx.com     nxt_unit_debug(ti->ctx, "worker thread #%d start",
5741681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
5751681Smax.romanov@nginx.com 
5761681Smax.romanov@nginx.com     gstate = PyGILState_Ensure();
5771681Smax.romanov@nginx.com 
5781681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
5791681Smax.romanov@nginx.com         if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
5801681Smax.romanov@nginx.com             goto fail;
5811681Smax.romanov@nginx.com         }
5821681Smax.romanov@nginx.com     }
5831681Smax.romanov@nginx.com 
5841681Smax.romanov@nginx.com     ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
5851681Smax.romanov@nginx.com     if (nxt_slow_path(ctx == NULL)) {
5861681Smax.romanov@nginx.com         goto fail;
5871681Smax.romanov@nginx.com     }
5881681Smax.romanov@nginx.com 
5891681Smax.romanov@nginx.com     (void) nxt_py_proto.run(ctx);
5901681Smax.romanov@nginx.com 
5911681Smax.romanov@nginx.com     nxt_unit_done(ctx);
5921681Smax.romanov@nginx.com 
5931681Smax.romanov@nginx.com fail:
5941681Smax.romanov@nginx.com 
5951681Smax.romanov@nginx.com     PyGILState_Release(gstate);
5961681Smax.romanov@nginx.com 
5971681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "worker thread #%d end",
5981681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
5991681Smax.romanov@nginx.com 
6001681Smax.romanov@nginx.com     return NULL;
6011681Smax.romanov@nginx.com }
6021681Smax.romanov@nginx.com 
6031681Smax.romanov@nginx.com 
6041681Smax.romanov@nginx.com static void
6051681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
6061681Smax.romanov@nginx.com {
6071681Smax.romanov@nginx.com     int                   res;
6081681Smax.romanov@nginx.com     uint32_t              i;
6091681Smax.romanov@nginx.com     PyThreadState         *thread_state;
6101681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
6111681Smax.romanov@nginx.com 
6121681Smax.romanov@nginx.com     if (nxt_py_threads == NULL) {
6131681Smax.romanov@nginx.com         return;
6141681Smax.romanov@nginx.com     }
6151681Smax.romanov@nginx.com 
6161681Smax.romanov@nginx.com     thread_state = PyEval_SaveThread();
6171681Smax.romanov@nginx.com 
6181681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
6191681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
6201681Smax.romanov@nginx.com 
6211681Smax.romanov@nginx.com         if ((uintptr_t) ti->thread == 0) {
6221681Smax.romanov@nginx.com             continue;
6231681Smax.romanov@nginx.com         }
6241681Smax.romanov@nginx.com 
6251681Smax.romanov@nginx.com         res = pthread_join(ti->thread, NULL);
6261681Smax.romanov@nginx.com 
6271681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
6281681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
6291681Smax.romanov@nginx.com 
6301681Smax.romanov@nginx.com         } else {
6311681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
6321681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
6331681Smax.romanov@nginx.com         }
6341681Smax.romanov@nginx.com     }
6351681Smax.romanov@nginx.com 
6361681Smax.romanov@nginx.com     PyEval_RestoreThread(thread_state);
6371681Smax.romanov@nginx.com 
6381681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
6391681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
6401681Smax.romanov@nginx.com 
6411681Smax.romanov@nginx.com         if (ti->ctx_data != NULL) {
6421681Smax.romanov@nginx.com             nxt_py_proto.ctx_data_free(ti->ctx_data);
6431681Smax.romanov@nginx.com         }
6441681Smax.romanov@nginx.com     }
6451681Smax.romanov@nginx.com 
6461681Smax.romanov@nginx.com     nxt_unit_free(NULL, nxt_py_threads);
6471681Smax.romanov@nginx.com }
6481681Smax.romanov@nginx.com 
6491681Smax.romanov@nginx.com 
6501681Smax.romanov@nginx.com int
6511592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr)
6521592Smax.romanov@nginx.com {
6531592Smax.romanov@nginx.com     PyObject  *obj;
6541592Smax.romanov@nginx.com 
6551592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
6561592Smax.romanov@nginx.com         obj = PyString_FromStringAndSize((char *) pstr->string.start,
6571592Smax.romanov@nginx.com                                          pstr->string.length);
6581592Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
6591681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
6601592Smax.romanov@nginx.com         }
6611592Smax.romanov@nginx.com 
6621592Smax.romanov@nginx.com         PyUnicode_InternInPlace(&obj);
6631592Smax.romanov@nginx.com 
6641592Smax.romanov@nginx.com         *pstr->object_p = obj;
6651592Smax.romanov@nginx.com 
6661592Smax.romanov@nginx.com         pstr++;
6671592Smax.romanov@nginx.com     }
6681592Smax.romanov@nginx.com 
6691681Smax.romanov@nginx.com     return NXT_UNIT_OK;
6701592Smax.romanov@nginx.com }
6711592Smax.romanov@nginx.com 
6721592Smax.romanov@nginx.com 
6731592Smax.romanov@nginx.com void
6741592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr)
6751592Smax.romanov@nginx.com {
6761592Smax.romanov@nginx.com     PyObject  *obj;
6771592Smax.romanov@nginx.com 
6781592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
6791592Smax.romanov@nginx.com         obj = *pstr->object_p;
6801592Smax.romanov@nginx.com 
6811592Smax.romanov@nginx.com         Py_XDECREF(obj);
6821592Smax.romanov@nginx.com         *pstr->object_p = NULL;
6831592Smax.romanov@nginx.com 
6841592Smax.romanov@nginx.com         pstr++;
6851592Smax.romanov@nginx.com     }
6861592Smax.romanov@nginx.com }
6871592Smax.romanov@nginx.com 
6881592Smax.romanov@nginx.com 
6891592Smax.romanov@nginx.com static void
6901592Smax.romanov@nginx.com nxt_python_atexit(void)
6911592Smax.romanov@nginx.com {
6921872So.canty@f5.com     nxt_int_t  i;
6931872So.canty@f5.com 
6941681Smax.romanov@nginx.com     if (nxt_py_proto.done != NULL) {
6951681Smax.romanov@nginx.com         nxt_py_proto.done();
6961681Smax.romanov@nginx.com     }
6971592Smax.romanov@nginx.com 
6981592Smax.romanov@nginx.com     Py_XDECREF(nxt_py_stderr_flush);
6991872So.canty@f5.com 
7001872So.canty@f5.com     if (nxt_py_targets != NULL) {
7011872So.canty@f5.com         for (i = 0; i < nxt_py_targets->count; i++) {
7021872So.canty@f5.com             Py_XDECREF(nxt_py_targets->target[i].application);
7031872So.canty@f5.com         }
7041872So.canty@f5.com 
7051872So.canty@f5.com         nxt_unit_free(NULL, nxt_py_targets);
7061872So.canty@f5.com     }
7071592Smax.romanov@nginx.com 
7081592Smax.romanov@nginx.com     Py_Finalize();
7091592Smax.romanov@nginx.com 
7101592Smax.romanov@nginx.com     if (nxt_py_home != NULL) {
7111592Smax.romanov@nginx.com         nxt_free(nxt_py_home);
7121592Smax.romanov@nginx.com     }
7131592Smax.romanov@nginx.com }
7141592Smax.romanov@nginx.com 
7151592Smax.romanov@nginx.com 
7161592Smax.romanov@nginx.com void
7171592Smax.romanov@nginx.com nxt_python_print_exception(void)
7181592Smax.romanov@nginx.com {
7191592Smax.romanov@nginx.com     PyErr_Print();
7201592Smax.romanov@nginx.com 
7211592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
7221592Smax.romanov@nginx.com     /* The backtrace may be buffered in sys.stderr file object. */
7231592Smax.romanov@nginx.com     {
7241592Smax.romanov@nginx.com         PyObject  *result;
7251592Smax.romanov@nginx.com 
7261592Smax.romanov@nginx.com         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
7271592Smax.romanov@nginx.com         if (nxt_slow_path(result == NULL)) {
7281592Smax.romanov@nginx.com             PyErr_Clear();
7291592Smax.romanov@nginx.com             return;
7301592Smax.romanov@nginx.com         }
7311592Smax.romanov@nginx.com 
7321592Smax.romanov@nginx.com         Py_DECREF(result);
7331592Smax.romanov@nginx.com     }
7341592Smax.romanov@nginx.com #endif
7351592Smax.romanov@nginx.com }
736