Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pyutils.c 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "pyutils.h"
  2. void pyinit()
  3. {
  4. update_python_path();
  5. Py_Initialize();
  6. }
  7. void update_python_path()
  8. {
  9. wchar_t *ppath, *wcwd, *wtmp;
  10. char *cwd, *ncwd, *err_fmt;
  11. size_t wcwd_sz, ppath_sz;
  12. int err;
  13. // Add a ':' in front of CWD and convert to wchar_t*
  14. cwd = get_current_dir_name();
  15. if(!cwd)
  16. {
  17. err = errno;
  18. err_fmt = "Unable to fecth CWD : %s";
  19. goto update_python_path_err;
  20. }
  21. ncwd = malloc(sizeof(char) * (strlen(cwd) + 2));
  22. if(!ncwd)
  23. {
  24. err = errno;
  25. err_fmt = "Unable to allocate memory for new CWD : %s";
  26. goto update_python_path_err_cwd;
  27. }
  28. ncwd[0] = ':';
  29. strcpy(ncwd+1, cwd);
  30. free(cwd);
  31. cwd = ncwd;
  32. wcwd_sz = mbstowcs(NULL, cwd, 0);
  33. if(wcwd_sz == (size_t)-1)
  34. {
  35. err = errno;
  36. err_fmt = "Unable to convert CWD to wchar : %s";
  37. goto update_python_path_err_cwd;
  38. }
  39. wcwd_sz++;
  40. wcwd = malloc(sizeof(wchar_t) * wcwd_sz);
  41. if(!wcwd)
  42. {
  43. err = errno;
  44. err_fmt = "Unable to allocate wchar for CWD : %s";
  45. goto update_python_path_err_cwd;
  46. }
  47. if(mbstowcs(wcwd, cwd, wcwd_sz) == -1)
  48. {
  49. err = errno;
  50. err_fmt = "Unable to convert CWD to wchar : %s";
  51. goto update_python_path_err_wcwd;
  52. }
  53. // Fetch, dup & update the python path
  54. ppath = Py_GetPath();
  55. if(!ppath)
  56. {
  57. if(PyErr_Occurred())
  58. {
  59. log_expt(LOG_ALERT);
  60. }
  61. else
  62. {
  63. pyfcgi_log(LOG_ALERT, "Unable to fetch python path, got NULL.");
  64. }
  65. err_fmt = NULL;
  66. err = 0;
  67. goto update_python_path_err_wcwd;
  68. }
  69. ppath = wcsdup(ppath);
  70. if(!wcwd)
  71. {
  72. err = errno;
  73. err_fmt = "Unable to dup the python PATH : %s";
  74. goto update_python_path_err_wcwd;
  75. }
  76. ppath_sz = wcslen(ppath);
  77. wtmp = realloc(ppath, sizeof(wchar_t) * (ppath_sz + wcwd_sz + 1));
  78. if(!wtmp)
  79. {
  80. err = errno;
  81. err_fmt = "Unable reallocate new python path : %s";
  82. goto update_python_path_err_ppath;
  83. }
  84. ppath = wtmp;
  85. wcsncpy(ppath+ppath_sz, wcwd, wcwd_sz);
  86. Py_SetPath(ppath);
  87. free(ppath);
  88. free(wcwd);
  89. free(cwd);
  90. return;
  91. update_python_path_err_ppath:
  92. free(ppath);
  93. update_python_path_err_wcwd:
  94. free(wcwd);
  95. update_python_path_err_cwd:
  96. free(cwd);
  97. update_python_path_err:
  98. if(err_fmt)
  99. {
  100. pyfcgi_log( LOG_ALERT, err_fmt, strerror(err));
  101. }
  102. exit(err);
  103. }
  104. PyObject* import_entrypoint()
  105. {
  106. PyObject *entry_fname, *entry_module, *entry_fun;
  107. entry_fname = PyUnicode_DecodeFSDefault(PyFCGI_conf.py_entrymod);
  108. entry_module = PyImport_Import(entry_fname);
  109. Py_DECREF(entry_fname);
  110. if(!entry_module)
  111. {
  112. //TODO syslog python error / traceback
  113. pyfcgi_log( LOG_CRIT,
  114. "Unable to import python file '%s'",
  115. PyFCGI_conf.py_entrymod);
  116. pyfcgi_log( LOG_INFO,
  117. "%s", getcwd(NULL, 256));
  118. log_expt(LOG_ERR);
  119. sleep(1);
  120. return NULL;
  121. }
  122. // getting entrypoint function
  123. entry_fun = PyObject_GetAttrString(entry_module, PyFCGI_conf.py_entryfun);
  124. Py_DECREF(entry_module);
  125. if(!entry_fun)
  126. {
  127. //TODO syslog python error / traceback
  128. pyfcgi_log( LOG_CRIT,
  129. "Unable to import object '%s' from '%s'",
  130. PyFCGI_conf.py_entryfun, PyFCGI_conf.py_entrymod);
  131. return NULL;
  132. }
  133. if(!PyCallable_Check(entry_fun))
  134. {
  135. pyfcgi_log( LOG_CRIT,
  136. "When imported from '%s', '%s' was not a callable",
  137. PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
  138. return NULL;
  139. }
  140. return entry_fun;
  141. }
  142. void log_expt(int priority)
  143. {
  144. if(!PyErr_Occurred())
  145. {
  146. pyfcgi_log(priority, "No exception");
  147. return;
  148. }
  149. PyObject *expt, *expt_str, *expt_bytes, *expt_cls,
  150. *expt_val, *expt_type, *traceback;
  151. char *msg, *type, *val;
  152. int msg_sz;
  153. char msg_fmt[] = "%s: %s";
  154. PyErr_Fetch(&expt_cls, &expt, &traceback);
  155. // Fetching exception message using __str__()
  156. expt_str = PyObject_GetAttrString(expt, "__str__");
  157. if(!expt_str)
  158. {
  159. pyfcgi_log(LOG_ERR, "Unable to fetch __str__ from exception");
  160. }
  161. expt_val = PyObject_CallObject(expt_str, NULL);
  162. expt_bytes = PyUnicode_AsUTF8String(expt_val);
  163. msg = PyBytes_AsString(expt_bytes);
  164. val = strndup(msg, 1024); // TODO check out of mem if !val
  165. // Fetching exception class name
  166. expt_type = PyObject_GetAttrString(expt_cls, "__name__");
  167. expt_bytes = PyUnicode_AsUTF8String(expt_type);
  168. msg = PyBytes_AsString(expt_bytes);
  169. type = strndup(msg, 1024); // TODO check out of mem if !type
  170. msg_sz = snprintf(NULL, 0, msg_fmt, type, val);
  171. msg_sz++;
  172. msg = malloc(sizeof(char) * msg_sz);
  173. snprintf(msg, msg_sz, msg_fmt, type, val);
  174. pyfcgi_log(priority, msg);
  175. }
  176. void pyfcgi_python_version(char version[16])
  177. {
  178. snprintf(version, 16, "%d.%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION,
  179. PY_MICRO_VERSION);
  180. }