/* * Copyright (C) 2019 Weber Yann * * This file is part of PyFCGI. * * PyFCGI is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * PyFCGI is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PyFCGI. If not, see . */ /**@defgroup processes Process organisation of PyFCGI * * PyFCGI is organised in three layer : * A @ref main_proc : simple, that keep running and spawn a * @ref work_master_proc . This process handles @ref worker_process creation * and try to maintain a pool able to reply efficiently to CGI requests. */ /**@defgroup main_proc Main process * @brief The main process in the @ref main() function * @ingroup processes */ #include /* fcgi library; put it first*/ #include #include #include #include #include #include #include #include #include "conf.h" #include "logger.h" #include "responder.h" #define IDENT_FMT "pyfcgi[%d]" #define MAX_REQS 1024 #define EARLY_ERR(err_str) write(2, err_str, strlen(err_str)) extern pyfcgi_conf_t PyFCGI_conf; pid_t pool_handler_pid; void sighandler(int signum) { int ret; if(signum == SIGINT) { pyfcgi_log(LOG_INFO, "Master process received ctrl+c, exiting..."); } else { pyfcgi_log(LOG_INFO, "Master process received signal %s(%d), exiting...", strsignal(signum), signum); } if(pool_handler_pid) { kill(pool_handler_pid, SIGTERM); waitpid(pool_handler_pid, &ret, 0); } pyfcgi_log(LOG_INFO, "Master process exiting."); exit(0); } int main(int argc, char **argv) { int child_ret; unsigned int emerg_sleep = 3; struct sigaction act; act.sa_handler = sighandler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGTERM); act.sa_flags = 0; act.sa_restorer = NULL; if(sigaction(SIGINT, &act, NULL)) { perror("Sigaction error"); exit(4); } pool_handler_pid = 0; default_conf(); pyfcgi_logger_init(); pyfcgi_logger_set_ident("pyfcgi(main)"); if(parse_args(argc, argv)) { return 1; } pyfcgi_log(LOG_INFO, "New server started"); while(1) { pool_handler_pid = fork(); if(pool_handler_pid == -1) { // TODO : Error pyfcgi_log(LOG_EMERG, "Failed to fork : '%s' sleeping %ds", strerror(errno), emerg_sleep); sleep(emerg_sleep); continue; } else if(!pool_handler_pid) { responder_loop(); exit((unsigned char)-1); } waitpid(pool_handler_pid, &child_ret, 0); pool_handler_pid = 0; if(child_ret) { pyfcgi_log(LOG_ERR, "Responder loop function exits with error code '%d'", WEXITSTATUS(child_ret)); if(WIFSIGNALED(child_ret)) { pyfcgi_log(LOG_ERR, "Responder loop function terminated by sig '%d'", WTERMSIG(child_ret)); } } else { pyfcgi_log(LOG_NOTICE, "Restarting main process after %d requests", MAX_REQS); } } closelog(); } /**@mainpage PyFCGI * @section main_what What is PyFCGI ? * PyFCGI is a simple python3 fastcgi runner. * * Usage : Usage : spawn-fcgi [OPTIONS] -- pyfcgi -e PYMODULE [-E PYFUN] [OPTIONS] * * To run foo.entrypoint() python function. * * @subsection main_how_use How to use it ? * * @warning For the moment PyFCGI is under heavy developpement and in early * stage. Everything will change ;o) * * PyFCGI should be runned with spawn-fcgi (or something similar), allowing * to configure & forward environnement variables to libFCGI. * * For the moment no configuration files exists. You have to pass arguments * to pyfcgi using -- argument of spawnn-fcgi * * When called this function will have to send valid CGI data to the webserver * using sys.stdin (the print() function for exemple) like : *Content-type: text/html\\r\\n\\r\\nHello world !\\n * * The function will have access to updated CGI os.environ containing * all informations about a request * * @subsubsection main_how_use_syslog Logging, using syslog * * Right now PyFCGI uses pyfcgi_log() to log stuff using a pyfcgi ident. * PyFCGI logs can be filtered using /etc/rsyslog.d/pyfcgi.conf : *
if ($programname contains 'pyfcgi') then {
	-/var/log/pyfcgi/pyfcgi.log
	stop
}
* * @subsubsection main_how_use_hardcode Hardcoded stuffs * * Right now there is a lot of hardcodd stuff. Fortunatly a vast majority * is in @ref main.c like the minimum and maximum number of workers, or * the number of requests before a worker restart. Some timers and stuff * are harcoded but should not in @ref pyworker.c & @ref responder.c * * @subsection main_how_works How it works ? * * - @ref processes * - @ref main_proc * - @ref work_master_proc * - @ref worker_process * * @subsection main_how_todo TODO * @todo Add another logging facility * @todo Add a configuration mecanism * @todo Implement a watchdog (request too long, or too much memory ?) */