Use YAZ log reopen reature on SIGHUP
[metaproxy-moved-to-github.git] / src / metaproxy_prog.cpp
index 3bf08c4..a4bb72b 100644 (file)
@@ -1,7 +1,5 @@
-/* $Id: metaproxy_prog.cpp,v 1.10 2007-05-09 21:23:09 adam Exp $
-   Copyright (c) 2005-2007, Index Data.
-
-This file is part of Metaproxy.
+/* This file is part of Metaproxy.
+   Copyright (C) Index Data
 
 Metaproxy is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
@@ -14,129 +12,300 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with Metaproxy; see the file LICENSE.  If not, write to the
-Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.
- */
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
 
 #include "config.hpp"
 
-#include <boost/program_options.hpp>
-namespace po = boost::program_options;
+#include <yaz/log.h>
+#include <yaz/options.h>
+#include <yaz/daemon.h>
 
+#include <yaz/sc.h>
+#include <yaz/backtrace.h>
 #include <iostream>
 #include <stdexcept>
 #include <libxml/xinclude.h>
 
-#include "filter.hpp"
-#include "package.hpp"
-#include "router_flexml.hpp"
-#include "factory_static.hpp"
+#include <metaproxy/filter.hpp>
+#include <metaproxy/package.hpp>
+#include <metaproxy/util.hpp>
+#include <metaproxy/router_xml.hpp>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <signal.h>
+#ifdef WIN32
+#include <direct.h>
+#include <io.h>
+#include <process.h>
+#endif
 
 namespace mp = metaproxy_1;
 
-int main(int argc, char **argv)
+mp::RouterXML *routerp = 0;
+
+static void set_log_prefix(void)
+{
+#if HAVE_UNISTD_H
+    char str[80];
+
+    sprintf(str, "%lld", (long long) getpid());
+    yaz_log_init_prefix(str);
+#endif
+}
+
+#if HAVE_UNISTD_H
+static pid_t process_group = 0;
+static int sig_received = 0;
+static pid_t my_pid = 0;
+
+static void sig_x_handler(int signo)
+{
+    if (sig_received)
+        return;
+    if ( getpid() != my_pid ) // can happen in unlikely cases
+        return; // when the sig hits a child just after fork(), before it
+                // clears its sighandler. MP-559.
+    sig_received = signo;
+    if (routerp)
+        routerp->stop(signo);
+    if (signo == SIGTERM)
+        kill(-process_group, SIGTERM); /* kill all children processes as well */
+}
+#endif
+
+static void work_common(void *data)
 {
-    try 
+    set_log_prefix();
+#if HAVE_UNISTD_H
+    process_group = getpgid(0); // save process group ID
+    my_pid = getpid();
+
+    signal(SIGTERM, sig_x_handler);
+    signal(SIGUSR1, sig_x_handler);
+#endif
+    routerp = (mp::RouterXML*) data;
+    routerp->start();
+
+    mp::Package pack;
+    pack.router(*routerp).move();
+    yaz_log(YLOG_LOG, "metaproxy stop");
+    delete routerp;
+    routerp = 0;
+    _exit(0);
+}
+
+static void work_debug(void *data)
+{
+    work_common(data);
+}
+
+static void work_normal(void *data)
+{
+#if HAVE_UNISTD_H
+    /* make the current working process group leader */
+    setpgid(0, 0);
+#endif
+    work_common(data);
+}
+
+static int sc_main(
+    yaz_sc_t s,
+    int argc, char **argv)
+{
+    bool test_config = false;
+    const char *fname = 0;
+    int ret;
+    char *arg;
+    unsigned mode = 0;
+    const char *pidfile = 0;
+    const char *uid = 0;
+
+    yaz_enable_panic_backtrace(argv[0]);
+    set_log_prefix();
+
+    while ((ret = options("c{config}:Dh{help}l:m:p:tu:v:V{version}w:X",
+                          argv, argc, &arg)) != -2)
     {
-        po::options_description desc("Allowed options");
-        desc.add_options()
-            ("help,h", "produce help message")
-            ("version,V", "show version")
-            ("config", po::value< std::vector<std::string> >(), "xml config")
-            ;
-        
-        po::positional_options_description p;
-        p.add("config", -1);
-        
-        po::variables_map vm;        
-        po::store(po::command_line_parser(argc, argv).
-                  options(desc).positional(p).run(), vm);
-        po::notify(vm);
-        
-        if (vm.count("help")) {
-            std::cout << desc << "\n";
-            return 1;
-        }
-        if (vm.count("version")) {
-            std::cout << "Metaproxy " VERSION "\n";
-            return 0;
-        }
-        xmlDocPtr doc = 0;
-        if (vm.count("config"))
+        switch (ret)
         {
-            std::vector<std::string> config_fnames = 
-                vm["config"].as< std::vector<std::string> >();
+        case 'c':
+            fname = arg;
+            break;
+        case 'D':
+            mode = YAZ_DAEMON_FORK|YAZ_DAEMON_KEEPALIVE;
+            break;
+        case 'h':
+            std::cerr << "metaproxy\n"
+                " -h|--help     help\n"
+                " -V|--version  version\n"
+                " -v level\n"
+                " -c|--config f config filename\n"
+                " -D            daemon and keepalive operation\n"
+                " -l f          log file f\n"
+                " -m logformat  log time format (strftime)\n"
+                " -p f          pid file f\n"
+                " -t            test configuration\n"
+                " -u id         change uid to id\n"
+                " -w dir        changes working directory to dir\n"
+                " -X            debug mode (no fork/daemon mode)\n"
+#ifdef WIN32
+                " -install      install windows service\n"
+                " -remove       remove windows service\n"
+#endif
 
-            if (config_fnames.size() != 1)
-            {
-                std::cerr << "Only one configuration must be given\n";
-                std::exit(1);
-            }
-            
-            // need to parse with xinclude tags 
-            // XML_PARSE_XINCLUDE XML_PARSE_NOBLANKS  
-            // XML_PARSE_NSCLEAN XML_PARSE_NONET 
-            doc = xmlReadFile(config_fnames[0].c_str(), 
-                              NULL, 
-                              XML_PARSE_XINCLUDE + XML_PARSE_NOBLANKS
-                              + XML_PARSE_NSCLEAN + XML_PARSE_NONET );
-
-            if (!doc)
+                      << std::endl;
+            break;
+        case 'l':
+            yaz_log_init_file(arg);
+            break;
+        case 'm':
+            yaz_log_time_format(arg);
+            break;
+        case 'p':
+            pidfile = arg;
+            break;
+        case 't':
+            test_config = true;
+            break;
+        case 'u':
+            uid = arg;
+            break;
+        case 'v':
+            yaz_log_init_level(yaz_log_mask_str(arg));
+            break;
+        case 'V':
+            std::cout << VERSION;
+#ifdef VERSION_SHA1
+            std::cout << " " VERSION_SHA1;
+#endif
+            std::cout << "\n";
+            return 0;
+            break;
+        case 'w':
+            if (
+#ifdef WIN32
+                _chdir(arg)
+#else
+                chdir(arg)
+#endif
+                )
             {
-                std::cerr << "XML parsing failed\n";
-                std::exit(1);
+                std::cerr << "chdir " << arg << " failed" << std::endl;
+                return 1;
             }
-            // and perform Xinclude then
-            if (xmlXIncludeProcess(doc) > 0) {
-                std::cerr << "processing XInclude directive\n";
-            }
-        }
-        else
-        {
-            std::cerr << "No configuration given\n";
-            std::exit(1);
+        case 'X':
+            mode = YAZ_DAEMON_DEBUG;
+            break;
+        case -1:
+            std::cerr << "bad option: " << arg << std::endl;
+            return 1;
         }
-        if (doc)
+    }
+    if (!fname)
+    {
+        std::cerr << "No configuration given; use -h for help\n";
+        return 1;
+    }
+
+    yaz_log(YLOG_LOG, "metaproxy %s " VERSION
+#ifdef VERSION_SHA1
+                " " VERSION_SHA1
+#endif
+        , test_config ? "test" : "start"
+            );
+
+    xmlInitParser();
+    LIBXML_TEST_VERSION
+
+    yaz_log_xml_errors(0, YLOG_LOG);
+    xmlDocPtr doc = xmlReadFile(fname,
+                                NULL,
+                                XML_PARSE_XINCLUDE + XML_PARSE_NOBLANKS
+                                + XML_PARSE_NSCLEAN + XML_PARSE_NONET );
+
+    if (!doc)
+    {
+        yaz_log(YLOG_FATAL,"XML parsing failed");
+        return 1;
+    }
+    // and perform Xinclude then
+    int r = xmlXIncludeProcess(doc);
+    if (r == -1)
+    {
+        yaz_log(YLOG_FATAL, "XInclude processing failed");
+        return 1;
+    }
+    mp::wrbuf base_path;
+    const char *last_p = strrchr(fname,
+#ifdef WIN32
+                                 '\\'
+#else
+                                 '/'
+#endif
+        );
+    if (last_p)
+        wrbuf_write(base_path, fname, last_p - fname);
+    else
+        wrbuf_puts(base_path, ".");
+    ret = 0;
+    try {
+        mp::RouterXML *router =
+            new mp::RouterXML(doc, test_config, wrbuf_cstr(base_path));
+        if (!test_config)
         {
-            try {
-                mp::FactoryStatic factory;
-                mp::RouterFleXML router(doc, factory);
-                mp::Package pack;
-                pack.router(router).move();
-            }
-            catch (std::runtime_error &e) {
-                std::cerr << "std::runtime error: " << e.what() << "\n";
-                std::exit(1);
-            }
-            xmlFreeDoc(doc);
+
+            yaz_sc_running(s);
+
+            yaz_daemon("metaproxy", mode | YAZ_DAEMON_LOG_REOPEN,
+                       mode == YAZ_DAEMON_DEBUG ? work_debug : work_normal,
+                       router, pidfile, uid);
         }
-    }
-    catch (po::unknown_option &e) {
-        std::cerr << e.what() << "; use --help for list of options\n";
-        std::exit(1);
+        delete router;
     }
     catch (std::logic_error &e) {
-        std::cerr << "std::logic error: " << e.what() << "\n";
-        std::exit(1);
+        yaz_log(YLOG_FATAL,"std::logic error: %s" , e.what() );
+        ret = 1;
     }
     catch (std::runtime_error &e) {
-        std::cerr << "std::runtime error: " << e.what() << "\n";
-        std::exit(1);
+        yaz_log(YLOG_FATAL, "std::runtime error: %s" , e.what() );
+        ret = 1;
     }
     catch ( ... ) {
-        std::cerr << "Unknown Exception" << std::endl;
-        std::exit(1);
+        yaz_log(YLOG_FATAL, "Unknown Exception");
+        ret = 1;
     }
-    std::exit(0);
+    xmlFreeDoc(doc);
+    if (test_config)
+        yaz_log(YLOG_LOG, "metaproxy test exit code %d", ret);
+    return ret;
 }
 
+static void sc_stop(yaz_sc_t s)
+{
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+    yaz_sc_t s = yaz_sc_create("metaproxy", "metaproxy");
+
+    ret = yaz_sc_program(s, argc, argv, sc_main, sc_stop);
+
+    yaz_sc_destroy(&s);
+    exit(ret);
+}
 
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
- * c-file-style: "stroustrup"
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab
  */
+