merge JSON-75 into default, pending new version name
authorClaudio Luck <claudio.luck@pep.foundation>
Tue, 10 Apr 2018 16:51:12 +0200
changeset 461a032fea0a134
parent 456 84d11d4b6adb
parent 457 51f56ffd4c65
child 462 f37d69ec9384
merge JSON-75 into default, pending new version name
     1.1 --- a/server/Makefile	Thu Apr 05 18:25:00 2018 +0200
     1.2 +++ b/server/Makefile	Tue Apr 10 16:51:12 2018 +0200
     1.3 @@ -90,7 +90,7 @@
     1.4  	ar rcs $@ $^
     1.5  
     1.6  prefix-config.cc:
     1.7 -	echo '// This file is generated by make. Edit the Makefile, not this file!\n#include "prefix-config.hh" \nconst char* const html_directory = "'$(HTML_DIRECTORY)'";\n' > $@
     1.8 +	echo '// This file is generated by make. Edit the Makefile, not this file!\n#include "prefix-config.hh" \nconst char* const html_directory = "html";\n' > $@
     1.9  
    1.10  %.o: %.cc %.hh
    1.11  	$(CXX) $(CXXFLAGS) -c $<
     2.1 --- a/server/daemonize.cc	Thu Apr 05 18:25:00 2018 +0200
     2.2 +++ b/server/daemonize.cc	Tue Apr 10 16:51:12 2018 +0200
     2.3 @@ -2,64 +2,356 @@
     2.4  
     2.5  #ifdef _WIN32
     2.6  
     2.7 -	void daemonize(std::function<void()> child_fn )
     2.8 -	{
     2.9 -		if(child_fn)
    2.10 -			child_fn();
    2.11 -	}
    2.12 +#include <windows.h>
    2.13 +#include <tchar.h>
    2.14 +#include <stdio.h>
    2.15 +#include <strsafe.h>
    2.16 +#include <inttypes.h>
    2.17 +
    2.18 +#include <thread>
    2.19 +#include <iostream>
    2.20 +#include <stdexcept>
    2.21 +#include <system_error>
    2.22 +
    2.23 +HANDLE gPipeRd = NULL;
    2.24 +HANDLE gPipeWr = NULL;
    2.25 +
    2.26 +void daemonize (const bool daemonize, const uintptr_t status_handle)
    2.27 +{
    2.28 +    TCHAR * szWinFrontCmdline, * szWinSrvCmdline;
    2.29 +    DWORD dwWinFrontCmdlineLen;
    2.30 +    SECURITY_ATTRIBUTES saAttr;
    2.31 +    PROCESS_INFORMATION piProcInfo;
    2.32 +    STARTUPINFO siStartInfo;
    2.33 +    BOOL bStatusReceived = FALSE;
    2.34 +    DWORD dwRead = 0, dwTotRead = 0;
    2.35 +    DWORD dwRetVal = 1, dwNewRetVal = 0;
    2.36 +    BOOL bSuccess = FALSE;
    2.37 +
    2.38 +    /*
    2.39 +     * On Windows there is no need and possibility for a daemonization fork-dance.
    2.40 +     * Instead we launch the same binary ("image") again, passing it
    2.41 +     * additional open file handles. But the new process must learn about the
    2.42 +     * value of the filehandles somehow; only handles dedicated to stderr/stdout/stdin
    2.43 +     * redirection can be passed "internally" via system call. So we simply pass the
    2.44 +     * handle as a string over the command line; #defined STATUS_HANDLE "status-handle".
    2.45 +     * See CreateProcess documentation for more information.
    2.46 +     */
    2.47 +
    2.48 +    gPipeWr = (HANDLE) status_handle;
    2.49 +
    2.50 +    if (gPipeWr == NULL)
    2.51 +    {
    2.52 +        ZeroMemory(&saAttr, sizeof(SECURITY_ATTRIBUTES));
    2.53 +        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    2.54 +        saAttr.bInheritHandle = TRUE;
    2.55 +        saAttr.lpSecurityDescriptor = NULL;
    2.56 +
    2.57 +        if (!CreatePipe(&gPipeRd, &gPipeWr, &saAttr, 0))
    2.58 +            throw std::runtime_error("Cannot create pipe!");
    2.59 +
    2.60 +        if (!SetHandleInformation(gPipeRd, HANDLE_FLAG_INHERIT, 0))
    2.61 +            throw std::runtime_error("Cannot configure pipe read handle!");
    2.62 +
    2.63 +        /*
    2.64 +         * Create new command line with appended " --status-handle <handle>"
    2.65 +         */
    2.66 +        szWinFrontCmdline = GetCommandLine();           // FIXME: Unicode GetCommandLineW, and wmain()
    2.67 +        dwWinFrontCmdlineLen = _tcslen(szWinFrontCmdline) + _tcslen(_T(" -- ")) + _tcslen(_T(STATUS_HANDLE));
    2.68 +        if (dwWinFrontCmdlineLen + 40 >= MAX_PATH)      // + 40 to accommodate PRIuPRT
    2.69 +            throw std::runtime_error("Command line too long to be extend!");
    2.70 +
    2.71 +        if (!(szWinSrvCmdline = (TCHAR *)calloc(dwWinFrontCmdlineLen + 40 + 1, sizeof(TCHAR))))
    2.72 +            throw std::runtime_error("Memory allocation for background process command line failed!");
    2.73 +
    2.74 +        _stprintf_s(szWinSrvCmdline, dwWinFrontCmdlineLen + 40 + 0,
    2.75 +                    _T("%s --" STATUS_HANDLE " %") PRIuPTR, szWinFrontCmdline,
    2.76 +                                                            ((uintptr_t)gPipeWr));
    2.77 +
    2.78 +        ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    2.79 +
    2.80 +        ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    2.81 +        siStartInfo.cb = sizeof(STARTUPINFO);
    2.82 +        siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    2.83 +        siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    2.84 +        siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    2.85 +        siStartInfo.dwFlags |= /* STARTF_USESTDHANDLES | */ STARTF_USESHOWWINDOW;
    2.86 +        siStartInfo.wShowWindow = SW_HIDE;
    2.87 +
    2.88 +        bSuccess = CreateProcess(
    2.89 +            NULL,               // application name
    2.90 +            szWinSrvCmdline,    // command line (len+NULL <= MAX_PATH | if appl. name == NULL)
    2.91 +            &saAttr,            // process security attributes
    2.92 +            NULL,               // primary thread security attributes
    2.93 +            TRUE,               // handles are inherited
    2.94 +            0,                  // creation flags, e.g. CREATE_UNICODE_ENVIRONMENT (FIXME: wmain)
    2.95 +            NULL,               // use parent's environment
    2.96 +            NULL,               // use parent's current directory
    2.97 +            &siStartInfo,       // STARTUPINFO pointer
    2.98 +            &piProcInfo         // receives PROCESS_INFORMATION
    2.99 +        );
   2.100 +
   2.101 +        free(szWinSrvCmdline);
   2.102 +
   2.103 +        if (!bSuccess)
   2.104 +            throw std::runtime_error("Failed to start background process!");
   2.105 +
   2.106 +        /*
   2.107 +         * Now wait for the background process to give status feedback
   2.108 +         * via the pipe or for the process to exit (unexpectendly).
   2.109 +         *
   2.110 +         * Close the writing side in the foreground process so that we get EOF when
   2.111 +         * the background process crashes.
   2.112 +         */
   2.113 +        if (!CloseHandle(gPipeWr))
   2.114 +            throw std::runtime_error("Foreground process could not close pipe's write end!");
   2.115 +
   2.116 +        do
   2.117 +        {
   2.118 +            bSuccess = ReadFile(gPipeRd, &dwNewRetVal + dwTotRead, sizeof(DWORD) - dwTotRead, &dwRead, NULL);
   2.119 +            dwTotRead += dwRead;
   2.120 +            if (bSuccess == FALSE) {
   2.121 +                if (GetLastError() == ERROR_BROKEN_PIPE)
   2.122 +                    break;
   2.123 +
   2.124 +                throw std::runtime_error("Error in foreground process while reading pipe!");
   2.125 +            }
   2.126 +
   2.127 +            if (dwTotRead < sizeof(DWORD)) continue;
   2.128 +
   2.129 +            dwRetVal = dwNewRetVal;
   2.130 +            bStatusReceived = TRUE;
   2.131 +        } while (1);
   2.132 +
   2.133 +        /*
   2.134 +         * If status reporting was not complete, wait for the process to exit,
   2.135 +         * and proxy the return value.
   2.136 +         */
   2.137 +        if (bStatusReceived == FALSE)
   2.138 +        {
   2.139 +            dwNewRetVal = STILL_ACTIVE;   /* STILL_ACTIVE = 259 */
   2.140 +            while (dwNewRetVal == STILL_ACTIVE)
   2.141 +            {
   2.142 +                if (!(bSuccess = GetExitCodeProcess(piProcInfo.hProcess, &dwNewRetVal)))
   2.143 +                    throw std::runtime_error("Can not read exit code from background process!");
   2.144 +
   2.145 +                if (dwNewRetVal == STILL_ACTIVE)
   2.146 +                    std::this_thread::sleep_for(std::chrono::milliseconds(250));
   2.147 +            }
   2.148 +            dwRetVal = dwNewRetVal;
   2.149 +        }
   2.150 +
   2.151 +        exit(dwRetVal);
   2.152 +    }
   2.153 +
   2.154 +}
   2.155 +
   2.156 +void daemonize_commit (int retval)
   2.157 +{
   2.158 +    DWORD dwWritten = 0;
   2.159 +    DWORD dwRetVal;
   2.160 +    BOOL bSuccess = FALSE;
   2.161 +
   2.162 +    dwRetVal = (DWORD) retval;
   2.163 +
   2.164 +    if (gPipeWr == NULL)
   2.165 +        return;
   2.166 +
   2.167 +    bSuccess = WriteFile(gPipeWr, &dwRetVal, sizeof(DWORD), &dwWritten, NULL);
   2.168 +
   2.169 +    if (bSuccess == FALSE && GetLastError() != ERROR_BROKEN_PIPE)
   2.170 +        throw std::runtime_error("Can not write to pipe's write end in background process!");
   2.171 +
   2.172 +    if (!(bSuccess = FreeConsole()))
   2.173 +        throw std::runtime_error("Can not detach from console in background process!");
   2.174 +
   2.175 +    if (!CloseHandle(gPipeWr))
   2.176 +        throw std::runtime_error("Can not close pipe's write end in background process!");
   2.177 +}
   2.178  
   2.179  #else
   2.180  
   2.181  // Unix/Linux implementation
   2.182  #include <cstdlib>
   2.183  #include <unistd.h>
   2.184 +#include <stdio.h>
   2.185  #include <sys/types.h>
   2.186  #include <sys/stat.h>
   2.187  #include <fcntl.h>
   2.188 +#include <signal.h>
   2.189 +#include <sys/wait.h>
   2.190  
   2.191 +#include <thread>
   2.192 +#include <iostream>
   2.193  #include <stdexcept>
   2.194 +#include <system_error>
   2.195  
   2.196 -void daemonize(std::function<void()> child_fn )
   2.197 +
   2.198 +volatile sig_atomic_t sig_term_recv = 0;
   2.199 +int retval_pipefd[2];   /* read fd, write fd */
   2.200 +pid_t fpid = 1;         /* frontend process PID */
   2.201 +
   2.202 +void handle_sig_term (int signal)
   2.203  {
   2.204 -	/* already a daemon */
   2.205 -	if ( getppid() == 1 ) return;
   2.206 -	
   2.207 -	/* Fork off the parent process */
   2.208 -	pid_t pid = fork();
   2.209 -	if (pid < 0)
   2.210 -	{
   2.211 -		throw std::runtime_error("Cannot fork!");
   2.212 -	}
   2.213 -	
   2.214 -	if (pid > 0)
   2.215 -	{
   2.216 -		exit(EXIT_SUCCESS); /*Killing the Parent Process*/
   2.217 -	}
   2.218 -	
   2.219 -	/* At this point we are executing as the child process */
   2.220 -	if(child_fn)
   2.221 -		child_fn();
   2.222 -	
   2.223 -	/* Create a new SID for the child process */
   2.224 -	pid_t sid = setsid();
   2.225 -	if (sid < 0)
   2.226 -	{
   2.227 -		throw std::runtime_error("Cannot call setsid()");
   2.228 -	}
   2.229 -	
   2.230 -	const int fd = open("/dev/null",O_RDWR, 0);
   2.231 -	
   2.232 -	if (fd != -1)
   2.233 -	{
   2.234 -		dup2 (fd, STDIN_FILENO);
   2.235 -		dup2 (fd, STDOUT_FILENO);
   2.236 -		dup2 (fd, STDERR_FILENO);
   2.237 -		
   2.238 -		if (fd > 2)
   2.239 -		{
   2.240 -			close (fd);
   2.241 -		}
   2.242 -	}
   2.243 +    sig_term_recv = 1;
   2.244 +}
   2.245 +
   2.246 +void daemonize (const bool daemonize, const uintptr_t status_handle)
   2.247 +{
   2.248 +    int retval = EXIT_FAILURE;
   2.249 +    pid_t pid, sid, daemon_pid;
   2.250 +
   2.251 +    struct sigaction sig_action = {};
   2.252 +    sig_action.sa_handler = handle_sig_term;
   2.253 +    bool this_process_should_term = 0;
   2.254 +    int child_status = 0;
   2.255 +
   2.256 +    FILE * ipipe;
   2.257 +    int c;
   2.258 +
   2.259 +    pid = getpid();
   2.260 +    if ( daemonize == true && fpid == 1 ) fpid = pid;
   2.261 +
   2.262 +    /* only the frontend process shall continue */
   2.263 +    if ( fpid == 1 || fpid != pid ) return;
   2.264 +
   2.265 +    if (pipe (retval_pipefd))
   2.266 +        throw std::runtime_error ("Cannot create return-value pipe!");
   2.267 +
   2.268 +    /* now fork the intermediate process */
   2.269 +    pid = fork ();
   2.270 +    if (pid < 0)
   2.271 +        throw std::runtime_error ("Cannot fork child process!");
   2.272 +
   2.273 +    if (pid > 0)
   2.274 +    {
   2.275 +        /*
   2.276 +         * This is the foreground process which needs to stick around until the
   2.277 +         * background process has comminicated success or failure or has crashed.
   2.278 +         * As soon as a feedback is obtained from the background process, inform
   2.279 +         * the intermediate process to exit (kill TERM) and eventually "copy up"
   2.280 +         * the exit value of the background process.
   2.281 +         */
   2.282 +
   2.283 +        /* close the write end of the return value pipe */
   2.284 +        close (retval_pipefd[1]);
   2.285 +
   2.286 +        /* read pipe until closed or crashed on writing side */
   2.287 +        ipipe = fdopen (retval_pipefd[0], "r");
   2.288 +        while ((c = getw (ipipe)) != EOF)
   2.289 +        {
   2.290 +            retval = c;
   2.291 +            this_process_should_term = 1;
   2.292 +        }
   2.293 +        fclose (ipipe);
   2.294 +        if (c == EOF)
   2.295 +            this_process_should_term = 1;
   2.296 +
   2.297 +        do
   2.298 +        {
   2.299 +            kill (pid, SIGTERM);
   2.300 +            if (!this_process_should_term && (waitpid (pid, &child_status, WUNTRACED) > 0))
   2.301 +            {
   2.302 +                if (WIFEXITED(child_status) || WIFSIGNALED(child_status))
   2.303 +                {
   2.304 +                    retval = WEXITSTATUS(child_status);
   2.305 +                    this_process_should_term = 1;
   2.306 +                }
   2.307 +            }
   2.308 +        } while (!this_process_should_term);
   2.309 +
   2.310 +        exit (retval); /* exit the foreground process */
   2.311 +    }
   2.312 +
   2.313 +    /*
   2.314 +     * At this point we are the intermediate child process,
   2.315 +     * which now needs to fork the daemon process.
   2.316 +     */
   2.317 +
   2.318 +    /* close the read end of the pipe, belonging to the foreground process */
   2.319 +    close (retval_pipefd[0]);
   2.320 +
   2.321 +    /* create a new SID for the child process */
   2.322 +    sid = setsid();
   2.323 +    if (sid < 0)
   2.324 +        throw std::runtime_error ("Cannot call setsid()");
   2.325 +
   2.326 +    daemon_pid = fork();
   2.327 +    if (daemon_pid < 0)
   2.328 +        throw std::runtime_error ("Cannot fork again!");
   2.329 +
   2.330 +    if (daemon_pid > 0)
   2.331 +    {
   2.332 +        /*
   2.333 +         * This now sticks around until it gets the TERM signal from the
   2.334 +         * foreground process. At that point, it will see if the daemon
   2.335 +         * process is still running, and if not, proxy it's retval (which
   2.336 +         * will be again proxied by the frontend process).
   2.337 +         */
   2.338 +
   2.339 +        close (retval_pipefd[1]);
   2.340 +
   2.341 +        do {
   2.342 +            if (!this_process_should_term && (waitpid (daemon_pid, &child_status, WUNTRACED) > 0))
   2.343 +            {
   2.344 +                if ((WIFEXITED(child_status) || WIFSIGNALED(child_status)))
   2.345 +                {
   2.346 +                    retval = WEXITSTATUS(child_status);
   2.347 +                    this_process_should_term = 1;
   2.348 +                }
   2.349 +            }
   2.350 +        }
   2.351 +        while (!(sig_term_recv || this_process_should_term));
   2.352 +
   2.353 +        _exit (retval);
   2.354 +    }
   2.355 +
   2.356 +    /* At this point we are a daemon process */
   2.357 +
   2.358 +    sig_action.sa_handler = SIG_DFL;
   2.359 +    sigaction (SIGTERM, &sig_action, NULL);
   2.360 +
   2.361 +    umask (0007);
   2.362 +
   2.363 +    /* daemons normally chdir("/"), we don't do this until our code has evolved */
   2.364 +    /* chdir("/"); */
   2.365 +
   2.366 +    /* We delay closing the terminal to daemonize_commit(). */
   2.367 +
   2.368 +    const int fd = open ("/dev/null", O_RDONLY, 0);
   2.369 +    if (fd == -1)
   2.370 +        throw std::runtime_error ("Cannot open /dev/null!");
   2.371 +    
   2.372 +    dup2 (fd, STDIN_FILENO);
   2.373 +}
   2.374 +
   2.375 +
   2.376 +void daemonize_commit (const int retval)
   2.377 +{
   2.378 +    static int commited = 0;
   2.379 +    FILE * retvalfd;
   2.380 +    pid_t pid;
   2.381 +
   2.382 +    pid = getpid();
   2.383 +
   2.384 +    /* nothing to do if not the daemon process */
   2.385 +    if ( fpid == 1 || fpid == pid )
   2.386 +        return;
   2.387 +
   2.388 +    if (commited) return;
   2.389 +    commited = 1;
   2.390 +
   2.391 +    const int fd = open ("/dev/null", O_WRONLY, 0);
   2.392 +    if (fd == -1)
   2.393 +        throw std::runtime_error ("Cannot open /dev/null!");
   2.394 +
   2.395 +    retvalfd = fdopen (retval_pipefd[1], "a");
   2.396 +    putw (retval, retvalfd);
   2.397 +    fclose (retvalfd);
   2.398 +    close (retval_pipefd[1]);
   2.399 +
   2.400 +    dup2 (fd, STDOUT_FILENO);
   2.401 +    dup2 (fd, STDERR_FILENO);
   2.402  }
   2.403  
   2.404  #endif // ! _WIN32
     3.1 --- a/server/daemonize.hh	Thu Apr 05 18:25:00 2018 +0200
     3.2 +++ b/server/daemonize.hh	Tue Apr 10 16:51:12 2018 +0200
     3.3 @@ -1,11 +1,13 @@
     3.4  #ifndef JSON_SERVER_ADAPTER_DAEMONIZE_HH
     3.5  #define JSON_SERVER_ADAPTER_DAEMONIZE_HH
     3.6  
     3.7 -#include <functional>
     3.8 +#include <cstdint>
     3.9 +
    3.10 +#define STATUS_HANDLE "status-handle"
    3.11  
    3.12  // fork(), go into background, close all ttys etc...
    3.13 -// child_fn is called in the child process, before it detaches from the tty.
    3.14  // system-specific! (POSIX, Windows, ...?)
    3.15 -void daemonize( std::function<void()> child_fn = std::function<void()>() );
    3.16 +void daemonize(const bool daemonize, const uintptr_t status_handle);
    3.17 +void daemonize_commit(int retval);
    3.18  
    3.19  #endif
     4.1 --- a/server/json-adapter.cc	Thu Apr 05 18:25:00 2018 +0200
     4.2 +++ b/server/json-adapter.cc	Tue Apr 10 16:51:12 2018 +0200
     4.3 @@ -16,6 +16,7 @@
     4.4  #include <mutex>
     4.5  
     4.6  #include "json-adapter.hh"
     4.7 +#include "daemonize.hh"
     4.8  #include "pep-types.hh"
     4.9  #include "json_rpc.hh"
    4.10  #include "security-token.hh"
     5.1 --- a/server/main.cc	Thu Apr 05 18:25:00 2018 +0200
     5.2 +++ b/server/main.cc	Tue Apr 10 16:51:12 2018 +0200
     5.3 @@ -16,6 +16,7 @@
     5.4  bool debug_mode = false;
     5.5  bool do_sync    = false;
     5.6  bool ignore_missing_session = false;
     5.7 +uintptr_t status_handle = 0;
     5.8  
     5.9  std::string address = "127.0.0.1";
    5.10  std::string logfile = "";
    5.11 @@ -55,6 +56,9 @@
    5.12  		("html-directory,H", po::value<boost::filesystem::path>(&ev_server::path_to_html)->default_value(ev_server::path_to_html), "Path to the HTML and JavaScript files")
    5.13  		("logfile,l", po::value<std::string>(&logfile)->default_value(logfile),   "Name of the logfile. Can be \"stderr\" for log to stderr or empty for no log.")
    5.14  		("ignore-missing-session", po::bool_switch(&ignore_missing_session), "Ignore when no PEP_SESSION can be created.")
    5.15 +#ifdef _WIN32
    5.16 +		((STATUS_HANDLE), po::value<uintptr_t>(&status_handle)->default_value(0), "Status file handle, for internal use.")
    5.17 +#endif	
    5.18  	;
    5.19  	
    5.20  	po::variables_map vm;
    5.21 @@ -90,43 +94,59 @@
    5.22  		my_logfile = real_logfile.get();
    5.23  	}
    5.24  	
    5.25 +	if( debug_mode == false )
    5.26 +		daemonize (!debug_mode, (const uintptr_t) status_handle);
    5.27 +
    5.28  	JsonAdapter ja( my_logfile );
    5.29  	ja.do_sync( do_sync)
    5.30  	  .ignore_session_errors( ignore_missing_session)
    5.31  	  ;
    5.32 -	  
    5.33 -	auto prepare_run = [&](){ ja.prepare_run(address, start_port, end_port); };
    5.34 +	/*
    5.35 +	 * FIXME: why are exceptions risen after the instantiation of JsonAdapter
    5.36 +	 *        not catched in the outer try/catch?
    5.37 +	 */
    5.38  
    5.39 -	if( debug_mode )
    5.40 +	try
    5.41  	{
    5.42 -		prepare_run();
    5.43 -		ja.run();
    5.44 -		// run until "Q" from stdin
    5.45 -		int input = 0;
    5.46 -		do{
    5.47 -			std::cout << "Press <Q> <Enter> to quit." << std::endl;
    5.48 -			input = std::cin.get();
    5.49 -			std::cout << "Oh, I got a '" << input << "'. \n";
    5.50 -		}while(std::cin && input != 'q' && input != 'Q');
    5.51 -	}else{
    5.52 -		daemonize(prepare_run);
    5.53 -		ja.run();
    5.54 -		do{
    5.55 -			std::this_thread::sleep_for(std::chrono::seconds(3));
    5.56 -		}while(ja.running());
    5.57 +		ja.prepare_run(address, start_port, end_port);
    5.58 +
    5.59 +		if( debug_mode )
    5.60 +		{
    5.61 +			ja.run();
    5.62 +			// run until "Q" from stdin
    5.63 +			int input = 0;
    5.64 +			do{
    5.65 +				std::cout << "Press <Q> <Enter> to quit." << std::endl;
    5.66 +				input = std::cin.get();
    5.67 +				std::cout << "Oh, I got a '" << input << "'. \n";
    5.68 +			}while(std::cin && input != 'q' && input != 'Q');
    5.69 +		}else{
    5.70 +			ja.run();
    5.71 +			daemonize_commit(0);
    5.72 +			do{
    5.73 +				std::this_thread::sleep_for(std::chrono::seconds(3));
    5.74 +			}while(ja.running());
    5.75 +		}
    5.76 +		ja.shutdown(nullptr);
    5.77 +		ja.Log() << "Good bye. :-)" << std::endl;
    5.78 +		JsonAdapter::global_shutdown();
    5.79  	}
    5.80 -	ja.shutdown(nullptr);
    5.81 -	ja.Log() << "Good bye. :-)" << std::endl;
    5.82 -	JsonAdapter::global_shutdown();
    5.83 +	catch (...)
    5.84 +	{
    5.85 +		daemonize_commit(1);
    5.86 +		exit(1);
    5.87 +	}
    5.88  }
    5.89  catch(std::exception const& e)
    5.90  {
    5.91  	std::cerr << "Exception caught in main(): \"" << e.what() << "\"" << std::endl;
    5.92 +	daemonize_commit(1);
    5.93  	return 1;
    5.94  }
    5.95  catch (...)
    5.96  {
    5.97  	std::cerr << "Unknown Exception caught in main()." << std::endl;
    5.98 +	daemonize_commit(20);
    5.99  	return 20;
   5.100  }
   5.101  
     6.1 --- a/server/server_version.cc	Thu Apr 05 18:25:00 2018 +0200
     6.2 +++ b/server/server_version.cc	Tue Apr 10 16:51:12 2018 +0200
     6.3 @@ -52,7 +52,8 @@
     6.4  //	"(33) Hilchenbach";      // JSON-71: Setup C++11 Multi-threading in libevent properly to avoid deadlocks in MT server code"
     6.5  //	"(34) Erndtebrück";      // remove apiVersion(), change version() to return a semver-compatible version number in a JSON object.
     6.6  //	"(35) Bad Berleburg";    // fix the fork() problem on MacOS. daemonize() now got a function parameter. \o/
     6.7 -	"(36) Hatzfeld";         // JSON-81: add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
     6.8 +//	"(36) Hatzfeld";         // JSON-81: add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
     6.9 +	"(37) Test";             // JSON-75: FIXME
    6.10  
    6.11  //const ServerVersion sv{0, 10, 0, version_name};  // first version defined.
    6.12  //const ServerVersion sv{0, 11, 0, version_name};  // add set_own_key()
    6.13 @@ -60,7 +61,8 @@
    6.14  //const ServerVersion sv{0, 12, 1, version_name};  // add assert_utf8() for every string to/from the Engine (except blobdata)
    6.15  //const ServerVersion sv{0, 12, 2, version_name};  // fix the fork() problem on MacOS. daemonize() now got a function parameter.
    6.16  //const ServerVersion sv(0,13,0);  // add package_version, rename "version" into "api_version" in ServerVersion, add versions from the Engine, too
    6.17 -const ServerVersion sv(0,13,1);  // JSON-91: add MIME_encrypt_message_for_self() and encrypt_message_for_self()
    6.18 +//const ServerVersion sv(0,13,1);  // JSON-91: add MIME_encrypt_message_for_self() and encrypt_message_for_self()
    6.19 +const ServerVersion sv(0,14,0);  // FIXME
    6.20  
    6.21  } // end of anonymous namespace
    6.22  ////////////////////////////////////////////////////////////////////////////