#include #include #include #include #include "../../projal.h" #define _REST_USE_SSL 0 #define _REST_LINEAR_GET 1 #if _REST_USE_SSL #include "keys.c" #endif // _REST_USE_SSL #if _REST_LINEAR_GET #define GetShmPostResponseCallback GetShmPostResponseCallback_M #else // _REST_LINEAR_GET #define GetShmPostResponseCallback GetShmPostResponseCallback_O #endif // _REST_LINEAR_GET //////////////////////////////////////////////////////////////////////////////////////////////////// // app control #define _APPID GFA_APPCTRL_APPID_REST #define _APPNAME "REST" #define _DEPENDENCIES ((appid_t)(GFA_APPCTRL_APPID_REMANENT)) #define _REST_CYCLE_INTV_MS 200 ///////////////////////////////////////////////////////////////////////////// static volatile bool g_fRun = false; static volatile bool g_fPauseImp = false; static volatile bool g_fPauseCmd = false; static volatile bool g_fZombie = false; static appid_t g_nDepRunning = 0; static sigset_t g_set; int g_nLastSig = -1; ///////////////////////////////////////////////////////////////////////////// static void _SigHandler(int sig) { g_nLastSig = sig; g_fRun = g_fPauseImp = g_fPauseCmd = g_fZombie = false; } ///////////////////////////////////////////////////////////////////////////// static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI) { ctrlmsg_t nCtrlMsg; while(g_fRun && (nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI))) { switch(nCtrlMsg) { case GFA_APPCTRL_CTRLMSG_STOP: g_fRun = false; g_fPauseImp = false; g_fPauseCmd = false; g_fZombie = false; TRACE("Received Control Message 'Stop'\n"); return; case GFA_APPCTRL_CTRLMSG_PAUSE: if(!g_fPauseCmd) { g_fPauseCmd = true; if(!g_fPauseImp) { ::GfaIpcAppCtrlSetState(hAC, GIAS_Paused); TRACE("Received Control Message 'Pause'\n"); TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Paused)); } } break; case GFA_APPCTRL_CTRLMSG_RESUME: if(g_fPauseCmd) { g_fPauseCmd = false; if(!g_fPauseImp) { TRACE("Received Control Message 'Resume'\n"); ::GfaIpcAppCtrlSetState(hAC, GIAS_Running); TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Running)); } } break; default: break; } } } ///////////////////////////////////////////////////////////////////////////// static void _ProcessStateEvents(HAPPCTRL hAC, HAPPINFO hAI) { appid_t nAppIdSrc; bool fOldPaused = g_fPauseImp; char szDispName[128]; while(g_fRun && (nAppIdSrc = ::GfaIpcAppCtrlGetNextStateEvtSrc(hAI))) { GfaIpcAppStates state = ::GfaIpcAppCtrlGetState(hAC, nAppIdSrc); GfaIpcAppCtrlGetDisplayName(hAC, nAppIdSrc, szDispName, sizeof(szDispName)); TRACE("%-8s: State: %s\n", szDispName, ::GfaIpcAppCtrlGetStateText(state)); if(nAppIdSrc & _DEPENDENCIES) { if(state == GIAS_Running) { TRACE("%s -> %s.\n", szDispName, ::GfaIpcAppCtrlGetStateText(state)); g_nDepRunning |= nAppIdSrc; } else { TRACE("%s -> %s.\n", szDispName, ::GfaIpcAppCtrlGetStateText(state)); g_nDepRunning &= ~nAppIdSrc; } } } if(g_fRun) { g_fPauseImp = (g_nDepRunning != _DEPENDENCIES); if(!g_fPauseCmd && (fOldPaused != g_fPauseImp)) { fOldPaused = g_fPauseImp; GfaIpcAppStates newState = g_fPauseImp ? GIAS_Paused : GIAS_Running; ::GfaIpcAppCtrlSetState(hAC, newState); if(g_fPauseImp) { TRACE("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(newState)); } else { TRACE("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(newState)); } } } } ///////////////////////////////////////////////////////////////////////////// // main int main(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); int nRet = 0; HSHM hShm = NULL; void *pShm = NULL; HAPPCTRL hAC = NULL; HAPPINFO hAI; struct _u_instance instance; CURLcode cuGlobInit = CURL_LAST; int ulfInit = U_ERROR, ulfStart = U_ERROR; bool bUlfFrmwrkStarted = false; char szRootDir[PATH_MAX]; const char *pszRootDir = nullptr; json_error_t jerr; ///////////////////////////////////////////////////////////////////////// // check for multiple instances CProcessInstance pi; if(!pi.LockInstance(UUID_SHM)) { ETRACE("Failed to start instance!\n"); return -1; } ///////////////////////////////////////////////////////////////////////// do { g_fZombie = true; ///////////////////////////////////////////////////////////// // configure signal handling struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigfillset(&g_set); // handle signals sa.sa_handler = _SigHandler; sigaction(SIGHUP, &sa, NULL); // handles user's terminal disconnect sigaction(SIGQUIT, &sa, NULL); // handles Ctrl + '\' sigaction(SIGTERM, &sa, NULL); // handles normal termination sigaction(SIGABRT, &sa, NULL); // handles abnormal termination (i.e. abort()) sigaction(SIGINT, &sa, NULL); // handles Ctrl + 'C' // ignore signals sa.sa_handler = SIG_IGN; sigaction(SIGTSTP, &sa, NULL); // ignores Ctrl + 'Z' sigaction(SIGSTOP, &sa, NULL); // ignores Stop sigaction(SIGCONT, &sa, NULL); // ignores Continue sigaction(SIGCHLD, &sa, NULL); // ignores child process termination sigaction(0, &sa, NULL); // ignores shell termination ///////////////////////////////////////////////////////////// // initialize app control if(!(hAC = ::GfaIpcAppCtrlAcquire(_APPID, _APPNAME, _REST_CYCLE_INTV_MS * 1000, _REST_CYCLE_INTV_MS * 3000))) { ETRACE("Failed to acquire AppCtrl-Handle!\n"); nRet = -1; break; } ::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing); TRACE("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Initializing)); if(!::GfaIpcAppCtrlSubscribeStateEvents(hAC, _DEPENDENCIES)) { ETRACE("Failed to subscribe state event notifications!\n"); nRet = -1; break; } ///////////////////////////////////////////////////////////////////// pszRootDir = GetAppDirectory(szRootDir, _COUNTOF(szRootDir)); ///////////////////////////////////////////////////////////////////// // initialize CURL if((cuGlobInit = curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK) { ETRACE("Failed to initialize CURL!\n"); nRet = -1; break; } if(!(hShm = acquire_shm(sizeof(shm_t)))) { ETRACE("GfaIpcAcquireSHM failed!\n"); nRet = -1; break; } if(!(pShm = GfaIpcAcquirePointer(hShm))) { ETRACE("GfaIpcAcquirePointer failed!\n"); nRet = -1; break; } #ifdef _DEBUG GfaIpcDumpSHMROT(); fflush(stdout); #endif // _DEBUG ///////////////////////////////////////////////////////////////////// if((ulfInit = ulfius_init_instance(&instance, _REST_PORT, NULL, NULL)) != U_OK) { ETRACE("ulfius_init_instance failed!\n"); nRet = -1; break; } } while(false); CShm_t shm(pShm, hShm); CRestVarTable map; do { ///////////////////////////////////////////////////////////////////// // SHM and SHM variables table shm.InitPath(NULL, NULL); shm.CreateMembersTable(map); ///////////////////////////////////////////////////////////////////// // initialize request parameters SHM_REQUEST_PARAMS srp; srp.pMap = ↦ srp.pszUuid = UUID_SHM; REST_PLUGIN_REQUEST_HANDLER_PARAMS hp; hp.hShm = hShm; hp.pShm = pShm; hp.pParam = reinterpret_cast(&shm); _uuid_parse(UUID_SHM, &hp.uuidShm); ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // add handler functions // initialize static files and plugins, if any if(InitializeStaticFiles(pszRootDir, &instance, jerr) < 0) { ETRACE("InitializeStaticFiles failed!\n"); nRet = -1; break; } if(LoadPlugins(pszRootDir, &instance, jerr, &hp) < 0) { ETRACE("LoadPlugins failed!\n"); nRet = -1; break; } ///////////////////////////////////////////////////////////////////// // POST if(ulfius_add_endpoint_by_val(&instance, "POST", GET_SHM_PREFIX, NULL, 0, &GetShmPostResponseCallback, &srp) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "POST", SET_SHM_PREFIX, NULL, 0, &SetShmPostResponseCallback, &srp) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } #if _REST_IMPLEMENT_GET ///////////////////////////////////////////////////////////////////// // GET if(ulfius_add_endpoint_by_val(&instance, "GET", GET_SHM_PREFIX, "/*", 0, &GetShmGetResponseCallback, &srp) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } #endif // _REST_IMPLEMENT_GET ///////////////////////////////////////////////////////////////////// // OPTIONS if(ulfius_add_endpoint_by_val(&instance, "OPTIONS", NULL, "/*", 0, &OptionsResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } ///////////////////////////////////////////////////////////////////// // handler for HTTP verbs that are not allowed if(ulfius_add_endpoint_by_val(&instance, "HEAD", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "PUT", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "DELETE", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "CONNECT", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "TRACE", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } if(ulfius_add_endpoint_by_val(&instance, "PATCH", NULL, "/*", 0, &NotAllowedResponseCallback, NULL) != U_OK) { ETRACE("ulfius_add_endpoint_by_val failed!\n"); nRet = -1; break; } ///////////////////////////////////////////////////////////////////// // start service #if _REST_USE_SSL if((ulfStart = ulfius_start_secure_framework(&instance, g_pszKeyPem, g_pszCertPem)) != U_OK) #else // _REST_USE_SSL if((ulfStart = ulfius_start_framework(&instance)) != U_OK) #endif // _REST_USE_SSL { ETRACE("ulfius_start_framework failed!\n"); nRet = -1; break; } TRACE("Service started at port %hu.\n", instance.port); bUlfFrmwrkStarted = true; g_fZombie = false; g_fRun = true; ::GfaIpcAppCtrlSetState(hAC, GIAS_Running); } while(false); ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// while(g_fRun) { //////////////////////////////////////////////////////////////////////////////////////// // update app control info if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, 0))) { _ProcessCtrlMessages(hAC, hAI); if(!g_fRun) break; _ProcessStateEvents(hAC, hAI); } if(bUlfFrmwrkStarted && (g_fPauseImp || g_fPauseCmd)) { ulfius_stop_framework(&instance); bUlfFrmwrkStarted = false; TRACE("Service exit.\n"); } else if(!bUlfFrmwrkStarted && !g_fPauseImp && !g_fPauseCmd) { #if _REST_USE_SSL if((ulfStart = ulfius_start_secure_framework(&instance, g_pszKeyPem, g_pszCertPem)) != U_OK) #else // _REST_USE_SSL if((ulfStart = ulfius_start_framework(&instance)) != U_OK) #endif // _REST_USE_SSL { ETRACE("ulfius_start_framework failed!\n"); g_fZombie = true; g_fRun = false; nRet = -1; break; } bUlfFrmwrkStarted = true; TRACE("Service started at port %hu.\n", instance.port); } usleep(_REST_CYCLE_INTV_MS * 1000); } ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// if(g_nLastSig >= 0) { TRACE("Received signal '%s'.\n", strsignal(g_nLastSig)); g_nLastSig = -1; } if(bUlfFrmwrkStarted) { ulfius_stop_framework(&instance); TRACE("Service exit.\n"); } if(ulfInit == U_OK) ulfius_clean_instance(&instance); UnloadPlugins(); if(hShm) { if(pShm) { TRACE("Releasing SHM Pointer ...\n"); ::GfaIpcReleasePointer(hShm, pShm); } TRACE("Releasing SHM Handle ...\n"); ::GfaIpcReleaseSHM(hShm); } if(cuGlobInit == CURLE_OK) curl_global_cleanup(); if(g_fZombie) { if(hAC) ::GfaIpcAppCtrlSetState(hAC, GIAS_Zombie); TRACE("Enter Zombie state ...\n"); pause(); if(g_nLastSig >= 0) { TRACE("Received signal '%s'.\n", strsignal(g_nLastSig)); } } if(hAC) { // TRACE("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Terminating)); ::GfaIpcAppCtrlSetState(hAC, GIAS_Terminating); TRACE("Releasing App Control ...\n"); ::GfaIpcAppCtrlRelease(hAC); } TRACE("Process exit.\n"); return nRet; }