xref: /unit/src/python/nxt_python.c (revision 2077)
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 
2331980Smax.romanov@nginx.com     nxt_unit_default_init(task, &python_init, data->app);
2341592Smax.romanov@nginx.com 
2351681Smax.romanov@nginx.com     python_init.data = c;
2361681Smax.romanov@nginx.com     python_init.callbacks.ready_handler = nxt_python_ready_handler;
2371592Smax.romanov@nginx.com 
2381697Smax.romanov@nginx.com     proto = c->protocol;
2391697Smax.romanov@nginx.com 
2401697Smax.romanov@nginx.com     if (proto.length == 0) {
2411872So.canty@f5.com         proto = nxt_python_asgi_check(targets->target[0].application)
2421872So.canty@f5.com                 ? asgi : wsgi;
2431872So.canty@f5.com 
2441872So.canty@f5.com         for (i = 1; i < targets->count; i++) {
2451872So.canty@f5.com             probe_proto = nxt_python_asgi_check(targets->target[i].application)
2461872So.canty@f5.com                           ? asgi : wsgi;
2471872So.canty@f5.com             if (probe_proto.start != proto.start) {
2481872So.canty@f5.com                 nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
2491872So.canty@f5.com                                 "specify protocol in config if incorrect");
2501872So.canty@f5.com                 goto fail;
2511872So.canty@f5.com             }
2521872So.canty@f5.com         }
2531697Smax.romanov@nginx.com     }
2541697Smax.romanov@nginx.com 
2551697Smax.romanov@nginx.com     if (nxt_strstr_eq(&proto, &asgi)) {
2561681Smax.romanov@nginx.com         rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
2571624Smax.romanov@nginx.com 
2581624Smax.romanov@nginx.com     } else {
2591681Smax.romanov@nginx.com         rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
2601681Smax.romanov@nginx.com     }
2611681Smax.romanov@nginx.com 
2621681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
2631681Smax.romanov@nginx.com         goto fail;
2641624Smax.romanov@nginx.com     }
2651624Smax.romanov@nginx.com 
2661918Smax.romanov@nginx.com     rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
2671681Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
2681592Smax.romanov@nginx.com         goto fail;
2691592Smax.romanov@nginx.com     }
2701592Smax.romanov@nginx.com 
2711681Smax.romanov@nginx.com     rc = nxt_python_init_threads(c);
2721681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
2731681Smax.romanov@nginx.com         goto fail;
2741681Smax.romanov@nginx.com     }
2751681Smax.romanov@nginx.com 
2761681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
2771681Smax.romanov@nginx.com         if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
2781681Smax.romanov@nginx.com             goto fail;
2791681Smax.romanov@nginx.com         }
2801681Smax.romanov@nginx.com     }
2811681Smax.romanov@nginx.com 
2821592Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&python_init);
2831592Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
2841592Smax.romanov@nginx.com         goto fail;
2851592Smax.romanov@nginx.com     }
2861592Smax.romanov@nginx.com 
2871681Smax.romanov@nginx.com     rc = nxt_py_proto.run(unit_ctx);
2881624Smax.romanov@nginx.com 
2891681Smax.romanov@nginx.com     nxt_python_join_threads(unit_ctx, c);
2901592Smax.romanov@nginx.com 
2911592Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
2921592Smax.romanov@nginx.com 
2931681Smax.romanov@nginx.com     nxt_py_proto.ctx_data_free(python_init.ctx_data);
2941681Smax.romanov@nginx.com 
2951592Smax.romanov@nginx.com     nxt_python_atexit();
2961592Smax.romanov@nginx.com 
2971592Smax.romanov@nginx.com     exit(rc);
2981592Smax.romanov@nginx.com 
2991592Smax.romanov@nginx.com     return NXT_OK;
3001592Smax.romanov@nginx.com 
3011592Smax.romanov@nginx.com fail:
3021592Smax.romanov@nginx.com 
3031681Smax.romanov@nginx.com     nxt_python_join_threads(NULL, c);
3041681Smax.romanov@nginx.com 
3051681Smax.romanov@nginx.com     if (python_init.ctx_data != NULL) {
3061681Smax.romanov@nginx.com         nxt_py_proto.ctx_data_free(python_init.ctx_data);
3071681Smax.romanov@nginx.com     }
3081681Smax.romanov@nginx.com 
3091592Smax.romanov@nginx.com     Py_XDECREF(obj);
3101592Smax.romanov@nginx.com     Py_XDECREF(module);
3111592Smax.romanov@nginx.com 
3121592Smax.romanov@nginx.com     nxt_python_atexit();
3131592Smax.romanov@nginx.com 
3141592Smax.romanov@nginx.com     return NXT_ERROR;
3151592Smax.romanov@nginx.com }
3161592Smax.romanov@nginx.com 
3171592Smax.romanov@nginx.com 
3181759Svbart@nginx.com static nxt_int_t
3191872So.canty@f5.com nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
3201872So.canty@f5.com     nxt_conf_value_t *conf)
3211872So.canty@f5.com {
3221872So.canty@f5.com     char              *callable, *module_name;
3231872So.canty@f5.com     PyObject          *module, *obj;
3241872So.canty@f5.com     nxt_str_t         str;
3251872So.canty@f5.com     nxt_conf_value_t  *value;
3261872So.canty@f5.com 
3271872So.canty@f5.com     static nxt_str_t  module_str = nxt_string("module");
3281872So.canty@f5.com     static nxt_str_t  callable_str = nxt_string("callable");
3291872So.canty@f5.com 
3301872So.canty@f5.com     module = obj = NULL;
3311872So.canty@f5.com 
3321872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &module_str, NULL);
3331872So.canty@f5.com     if (nxt_slow_path(value == NULL)) {
3341872So.canty@f5.com         goto fail;
3351872So.canty@f5.com     }
3361872So.canty@f5.com 
3371872So.canty@f5.com     nxt_conf_get_string(value, &str);
3381872So.canty@f5.com 
3391872So.canty@f5.com     module_name = nxt_alloca(str.length + 1);
3401872So.canty@f5.com     nxt_memcpy(module_name, str.start, str.length);
3411872So.canty@f5.com     module_name[str.length] = '\0';
3421872So.canty@f5.com 
3431872So.canty@f5.com     module = PyImport_ImportModule(module_name);
3441872So.canty@f5.com     if (nxt_slow_path(module == NULL)) {
3451872So.canty@f5.com         nxt_alert(task, "Python failed to import module \"%s\"", module_name);
3461872So.canty@f5.com         nxt_python_print_exception();
3471872So.canty@f5.com         goto fail;
3481872So.canty@f5.com     }
3491872So.canty@f5.com 
3501872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &callable_str, NULL);
3511872So.canty@f5.com     if (value == NULL) {
3521872So.canty@f5.com         callable = nxt_alloca(12);
3531872So.canty@f5.com         nxt_memcpy(callable, "application", 12);
3541872So.canty@f5.com 
3551872So.canty@f5.com     } else {
3561872So.canty@f5.com         nxt_conf_get_string(value, &str);
3571872So.canty@f5.com 
3581872So.canty@f5.com         callable = nxt_alloca(str.length + 1);
3591872So.canty@f5.com         nxt_memcpy(callable, str.start, str.length);
3601872So.canty@f5.com         callable[str.length] = '\0';
3611872So.canty@f5.com     }
3621872So.canty@f5.com 
3631872So.canty@f5.com     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
3641872So.canty@f5.com     if (nxt_slow_path(obj == NULL)) {
3651872So.canty@f5.com         nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
3661932Smax.romanov@nginx.com                   callable, module_name);
3671872So.canty@f5.com         goto fail;
3681872So.canty@f5.com     }
3691872So.canty@f5.com 
3701872So.canty@f5.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
3711872So.canty@f5.com         nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
3721932Smax.romanov@nginx.com                   callable, module_name);
3731872So.canty@f5.com         goto fail;
3741872So.canty@f5.com     }
3751872So.canty@f5.com 
3761872So.canty@f5.com     target->application = obj;
3771872So.canty@f5.com     obj = NULL;
3781872So.canty@f5.com 
3791872So.canty@f5.com     Py_INCREF(target->application);
3801872So.canty@f5.com     Py_CLEAR(module);
3811872So.canty@f5.com 
3821872So.canty@f5.com     return NXT_OK;
3831872So.canty@f5.com 
3841872So.canty@f5.com fail:
3851872So.canty@f5.com 
3861872So.canty@f5.com     Py_XDECREF(obj);
3871872So.canty@f5.com     Py_XDECREF(module);
3881872So.canty@f5.com 
3891872So.canty@f5.com     return NXT_ERROR;
3901872So.canty@f5.com }
3911872So.canty@f5.com 
3921872So.canty@f5.com 
3931872So.canty@f5.com static nxt_int_t
3941759Svbart@nginx.com nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
3951759Svbart@nginx.com {
3961759Svbart@nginx.com     int               ret;
3971759Svbart@nginx.com     PyObject          *path, *sys;
3981759Svbart@nginx.com     nxt_str_t         str;
3991759Svbart@nginx.com     nxt_uint_t        n;
4001759Svbart@nginx.com     nxt_conf_value_t  *array;
4011759Svbart@nginx.com 
4021759Svbart@nginx.com     if (value == NULL) {
4031759Svbart@nginx.com         return NXT_OK;
4041759Svbart@nginx.com     }
4051759Svbart@nginx.com 
4061759Svbart@nginx.com     sys = PySys_GetObject((char *) "path");
4071759Svbart@nginx.com     if (nxt_slow_path(sys == NULL)) {
4081759Svbart@nginx.com         nxt_alert(task, "Python failed to get \"sys.path\" list");
4091759Svbart@nginx.com         return NXT_ERROR;
4101759Svbart@nginx.com     }
4111759Svbart@nginx.com 
4121759Svbart@nginx.com     /* sys is a Borrowed reference. */
4131759Svbart@nginx.com 
4141759Svbart@nginx.com     array = value;
415*2077Salx.manpages@gmail.com     n = nxt_conf_array_elements_count_or_1(array);
4161759Svbart@nginx.com 
4171759Svbart@nginx.com     while (n != 0) {
4181759Svbart@nginx.com         n--;
4191759Svbart@nginx.com 
4201759Svbart@nginx.com         /*
4211759Svbart@nginx.com          * Insertion in front of existing paths starting from the last element
4221759Svbart@nginx.com          * to preserve original order while giving priority to the values
4231759Svbart@nginx.com          * specified in the "path" option.
4241759Svbart@nginx.com          */
4251759Svbart@nginx.com 
426*2077Salx.manpages@gmail.com         value = nxt_conf_get_array_element_or_itself(array, n);
4271759Svbart@nginx.com 
4281759Svbart@nginx.com         nxt_conf_get_string(value, &str);
4291759Svbart@nginx.com 
4301759Svbart@nginx.com         path = PyString_FromStringAndSize((char *) str.start, str.length);
4311759Svbart@nginx.com         if (nxt_slow_path(path == NULL)) {
4321759Svbart@nginx.com             nxt_alert(task, "Python failed to create string object \"%V\"",
4331759Svbart@nginx.com                       &str);
4341759Svbart@nginx.com             return NXT_ERROR;
4351759Svbart@nginx.com         }
4361759Svbart@nginx.com 
4371759Svbart@nginx.com         ret = PyList_Insert(sys, 0, path);
4381759Svbart@nginx.com 
4391759Svbart@nginx.com         Py_DECREF(path);
4401759Svbart@nginx.com 
4411759Svbart@nginx.com         if (nxt_slow_path(ret != 0)) {
4421759Svbart@nginx.com             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
4431759Svbart@nginx.com                       &str);
4441759Svbart@nginx.com             return NXT_ERROR;
4451759Svbart@nginx.com         }
4461759Svbart@nginx.com     }
4471759Svbart@nginx.com 
4481759Svbart@nginx.com     return NXT_OK;
4491759Svbart@nginx.com }
4501759Svbart@nginx.com 
4511759Svbart@nginx.com 
4521681Smax.romanov@nginx.com static int
4531681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c)
4541681Smax.romanov@nginx.com {
4551681Smax.romanov@nginx.com     int                    res;
4561681Smax.romanov@nginx.com     uint32_t               i;
4571681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
4581681Smax.romanov@nginx.com     static pthread_attr_t  attr;
4591681Smax.romanov@nginx.com 
4601681Smax.romanov@nginx.com     if (c->threads <= 1) {
4611681Smax.romanov@nginx.com         return NXT_UNIT_OK;
4621681Smax.romanov@nginx.com     }
4631681Smax.romanov@nginx.com 
4641681Smax.romanov@nginx.com     if (c->thread_stack_size > 0) {
4651681Smax.romanov@nginx.com         res = pthread_attr_init(&attr);
4661681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
4671681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
4681681Smax.romanov@nginx.com                            strerror(res), res);
4691681Smax.romanov@nginx.com 
4701681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
4711681Smax.romanov@nginx.com         }
4721681Smax.romanov@nginx.com 
4731681Smax.romanov@nginx.com         res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
4741681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
4751681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
4761681Smax.romanov@nginx.com                            strerror(res), res);
4771681Smax.romanov@nginx.com 
4781681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
4791681Smax.romanov@nginx.com         }
4801681Smax.romanov@nginx.com 
4811681Smax.romanov@nginx.com         nxt_py_thread_attr = &attr;
4821681Smax.romanov@nginx.com     }
4831681Smax.romanov@nginx.com 
4841681Smax.romanov@nginx.com     nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
4851681Smax.romanov@nginx.com                                            * (c->threads - 1));
4861681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_threads == NULL)) {
4871681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate thread info array");
4881681Smax.romanov@nginx.com 
4891681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
4901681Smax.romanov@nginx.com     }
4911681Smax.romanov@nginx.com 
4921681Smax.romanov@nginx.com     memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
4931681Smax.romanov@nginx.com 
4941681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
4951681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
4961681Smax.romanov@nginx.com 
4971918Smax.romanov@nginx.com         res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
4981681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
4991681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
5001681Smax.romanov@nginx.com         }
5011681Smax.romanov@nginx.com     }
5021681Smax.romanov@nginx.com 
5031681Smax.romanov@nginx.com     return NXT_UNIT_OK;
5041681Smax.romanov@nginx.com }
5051681Smax.romanov@nginx.com 
5061681Smax.romanov@nginx.com 
5071681Smax.romanov@nginx.com static int
5081681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
5091681Smax.romanov@nginx.com {
5101681Smax.romanov@nginx.com     int                    res;
5111681Smax.romanov@nginx.com     uint32_t               i;
5121681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
5131681Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
5141681Smax.romanov@nginx.com 
5151681Smax.romanov@nginx.com     c = ctx->unit->data;
5161681Smax.romanov@nginx.com 
5171681Smax.romanov@nginx.com     if (c->threads <= 1) {
5181681Smax.romanov@nginx.com         return NXT_UNIT_OK;
5191681Smax.romanov@nginx.com     }
5201681Smax.romanov@nginx.com 
5211681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
5221681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
5231681Smax.romanov@nginx.com 
5241681Smax.romanov@nginx.com         ti->ctx = ctx;
5251681Smax.romanov@nginx.com 
5261681Smax.romanov@nginx.com         res = pthread_create(&ti->thread, nxt_py_thread_attr,
5271681Smax.romanov@nginx.com                              nxt_python_thread_func, ti);
5281681Smax.romanov@nginx.com 
5291681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
5301681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
5311681Smax.romanov@nginx.com 
5321681Smax.romanov@nginx.com         } else {
5331681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
5341681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
5351681Smax.romanov@nginx.com         }
5361681Smax.romanov@nginx.com     }
5371681Smax.romanov@nginx.com 
5381681Smax.romanov@nginx.com     return NXT_UNIT_OK;
5391681Smax.romanov@nginx.com }
5401681Smax.romanov@nginx.com 
5411681Smax.romanov@nginx.com 
5421681Smax.romanov@nginx.com static void *
5431681Smax.romanov@nginx.com nxt_python_thread_func(void *data)
5441681Smax.romanov@nginx.com {
5451681Smax.romanov@nginx.com     nxt_unit_ctx_t        *ctx;
5461681Smax.romanov@nginx.com     PyGILState_STATE      gstate;
5471681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
5481681Smax.romanov@nginx.com 
5491681Smax.romanov@nginx.com     ti = data;
5501681Smax.romanov@nginx.com 
5511681Smax.romanov@nginx.com     nxt_unit_debug(ti->ctx, "worker thread #%d start",
5521681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
5531681Smax.romanov@nginx.com 
5541681Smax.romanov@nginx.com     gstate = PyGILState_Ensure();
5551681Smax.romanov@nginx.com 
5561681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
5571681Smax.romanov@nginx.com         if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
5581681Smax.romanov@nginx.com             goto fail;
5591681Smax.romanov@nginx.com         }
5601681Smax.romanov@nginx.com     }
5611681Smax.romanov@nginx.com 
5621681Smax.romanov@nginx.com     ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
5631681Smax.romanov@nginx.com     if (nxt_slow_path(ctx == NULL)) {
5641681Smax.romanov@nginx.com         goto fail;
5651681Smax.romanov@nginx.com     }
5661681Smax.romanov@nginx.com 
5671681Smax.romanov@nginx.com     (void) nxt_py_proto.run(ctx);
5681681Smax.romanov@nginx.com 
5691681Smax.romanov@nginx.com     nxt_unit_done(ctx);
5701681Smax.romanov@nginx.com 
5711681Smax.romanov@nginx.com fail:
5721681Smax.romanov@nginx.com 
5731681Smax.romanov@nginx.com     PyGILState_Release(gstate);
5741681Smax.romanov@nginx.com 
5751681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "worker thread #%d end",
5761681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
5771681Smax.romanov@nginx.com 
5781681Smax.romanov@nginx.com     return NULL;
5791681Smax.romanov@nginx.com }
5801681Smax.romanov@nginx.com 
5811681Smax.romanov@nginx.com 
5821681Smax.romanov@nginx.com static void
5831681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
5841681Smax.romanov@nginx.com {
5851681Smax.romanov@nginx.com     int                   res;
5861681Smax.romanov@nginx.com     uint32_t              i;
5871681Smax.romanov@nginx.com     PyThreadState         *thread_state;
5881681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
5891681Smax.romanov@nginx.com 
5901681Smax.romanov@nginx.com     if (nxt_py_threads == NULL) {
5911681Smax.romanov@nginx.com         return;
5921681Smax.romanov@nginx.com     }
5931681Smax.romanov@nginx.com 
5941681Smax.romanov@nginx.com     thread_state = PyEval_SaveThread();
5951681Smax.romanov@nginx.com 
5961681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
5971681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
5981681Smax.romanov@nginx.com 
5991681Smax.romanov@nginx.com         if ((uintptr_t) ti->thread == 0) {
6001681Smax.romanov@nginx.com             continue;
6011681Smax.romanov@nginx.com         }
6021681Smax.romanov@nginx.com 
6031681Smax.romanov@nginx.com         res = pthread_join(ti->thread, NULL);
6041681Smax.romanov@nginx.com 
6051681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
6061681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
6071681Smax.romanov@nginx.com 
6081681Smax.romanov@nginx.com         } else {
6091681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
6101681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
6111681Smax.romanov@nginx.com         }
6121681Smax.romanov@nginx.com     }
6131681Smax.romanov@nginx.com 
6141681Smax.romanov@nginx.com     PyEval_RestoreThread(thread_state);
6151681Smax.romanov@nginx.com 
6161681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
6171681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
6181681Smax.romanov@nginx.com 
6191681Smax.romanov@nginx.com         if (ti->ctx_data != NULL) {
6201681Smax.romanov@nginx.com             nxt_py_proto.ctx_data_free(ti->ctx_data);
6211681Smax.romanov@nginx.com         }
6221681Smax.romanov@nginx.com     }
6231681Smax.romanov@nginx.com 
6241681Smax.romanov@nginx.com     nxt_unit_free(NULL, nxt_py_threads);
6251681Smax.romanov@nginx.com }
6261681Smax.romanov@nginx.com 
6271681Smax.romanov@nginx.com 
6281681Smax.romanov@nginx.com int
6291592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr)
6301592Smax.romanov@nginx.com {
6311592Smax.romanov@nginx.com     PyObject  *obj;
6321592Smax.romanov@nginx.com 
6331592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
6341592Smax.romanov@nginx.com         obj = PyString_FromStringAndSize((char *) pstr->string.start,
6351592Smax.romanov@nginx.com                                          pstr->string.length);
6361592Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
6371681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
6381592Smax.romanov@nginx.com         }
6391592Smax.romanov@nginx.com 
6401592Smax.romanov@nginx.com         PyUnicode_InternInPlace(&obj);
6411592Smax.romanov@nginx.com 
6421592Smax.romanov@nginx.com         *pstr->object_p = obj;
6431592Smax.romanov@nginx.com 
6441592Smax.romanov@nginx.com         pstr++;
6451592Smax.romanov@nginx.com     }
6461592Smax.romanov@nginx.com 
6471681Smax.romanov@nginx.com     return NXT_UNIT_OK;
6481592Smax.romanov@nginx.com }
6491592Smax.romanov@nginx.com 
6501592Smax.romanov@nginx.com 
6511592Smax.romanov@nginx.com void
6521592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr)
6531592Smax.romanov@nginx.com {
6541592Smax.romanov@nginx.com     PyObject  *obj;
6551592Smax.romanov@nginx.com 
6561592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
6571592Smax.romanov@nginx.com         obj = *pstr->object_p;
6581592Smax.romanov@nginx.com 
6591592Smax.romanov@nginx.com         Py_XDECREF(obj);
6601592Smax.romanov@nginx.com         *pstr->object_p = NULL;
6611592Smax.romanov@nginx.com 
6621592Smax.romanov@nginx.com         pstr++;
6631592Smax.romanov@nginx.com     }
6641592Smax.romanov@nginx.com }
6651592Smax.romanov@nginx.com 
6661592Smax.romanov@nginx.com 
6671592Smax.romanov@nginx.com static void
6681592Smax.romanov@nginx.com nxt_python_atexit(void)
6691592Smax.romanov@nginx.com {
6701872So.canty@f5.com     nxt_int_t  i;
6711872So.canty@f5.com 
6721681Smax.romanov@nginx.com     if (nxt_py_proto.done != NULL) {
6731681Smax.romanov@nginx.com         nxt_py_proto.done();
6741681Smax.romanov@nginx.com     }
6751592Smax.romanov@nginx.com 
6761592Smax.romanov@nginx.com     Py_XDECREF(nxt_py_stderr_flush);
6771872So.canty@f5.com 
6781872So.canty@f5.com     if (nxt_py_targets != NULL) {
6791872So.canty@f5.com         for (i = 0; i < nxt_py_targets->count; i++) {
6801872So.canty@f5.com             Py_XDECREF(nxt_py_targets->target[i].application);
6811872So.canty@f5.com         }
6821872So.canty@f5.com 
6831872So.canty@f5.com         nxt_unit_free(NULL, nxt_py_targets);
6841872So.canty@f5.com     }
6851592Smax.romanov@nginx.com 
6861592Smax.romanov@nginx.com     Py_Finalize();
6871592Smax.romanov@nginx.com 
6881592Smax.romanov@nginx.com     if (nxt_py_home != NULL) {
6891592Smax.romanov@nginx.com         nxt_free(nxt_py_home);
6901592Smax.romanov@nginx.com     }
6911592Smax.romanov@nginx.com }
6921592Smax.romanov@nginx.com 
6931592Smax.romanov@nginx.com 
6941592Smax.romanov@nginx.com void
6951592Smax.romanov@nginx.com nxt_python_print_exception(void)
6961592Smax.romanov@nginx.com {
6971592Smax.romanov@nginx.com     PyErr_Print();
6981592Smax.romanov@nginx.com 
6991592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
7001592Smax.romanov@nginx.com     /* The backtrace may be buffered in sys.stderr file object. */
7011592Smax.romanov@nginx.com     {
7021592Smax.romanov@nginx.com         PyObject  *result;
7031592Smax.romanov@nginx.com 
7041592Smax.romanov@nginx.com         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
7051592Smax.romanov@nginx.com         if (nxt_slow_path(result == NULL)) {
7061592Smax.romanov@nginx.com             PyErr_Clear();
7071592Smax.romanov@nginx.com             return;
7081592Smax.romanov@nginx.com         }
7091592Smax.romanov@nginx.com 
7101592Smax.romanov@nginx.com         Py_DECREF(result);
7111592Smax.romanov@nginx.com     }
7121592Smax.romanov@nginx.com #endif
7131592Smax.romanov@nginx.com }
714