1/**
2 * Copyright 2021 Alibaba, Inc. and its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15
16 * \author Haichao.chc
17 * \date Oct 2020
18 * \brief Entry function of proxima search engine execution
19 */
20
21#include <signal.h>
22#include <string.h>
23#include <iostream>
24#include <ailego/debug/bug_report.h>
25#include <ailego/utility/process_helper.h>
26#include <ailego/utility/string_helper.h>
27#include <aitheta2/index_plugin.h>
28#include <gflags/gflags.h>
29#include "common/config.h"
30#include "common/logger.h"
31#include "common/version.h"
32#include "server/proxima_search_engine.h"
33
34DEFINE_string(config, "", "Read configuration from this file");
35DEFINE_string(plugin, "", "Load proxima plugins from so files");
36DEFINE_string(pidfile, "", "Write the pid into this file");
37DEFINE_bool(daemon, false, "Run this app in daemon mode");
38
39static bool ValidateConfig(const char *flagname, const std::string &config) {
40 return !config.empty();
41}
42
43DEFINE_validator(config, ValidateConfig);
44
45static inline std::string GetUsage() {
46 std::string usage =
47 "Usage: \n"
48 " proxima_be [options]\n\n"
49 "Options: \n"
50 " --config <file_path> Read configuration from this file.\n"
51 " --plugin <so_path> Load proxima plugins, split with ','.\n"
52 " --daemon Run this app in daemon mode.\n"
53 " --pidfile <file_path> Write the pid into this file.\n"
54 " --version, -v Display version information.\n"
55 " --help, -h Display available options.\n";
56 return usage;
57}
58
59static bool LoadPlugins(const std::string &plugin_paths) {
60 aitheta2::IndexPluginBroker broker;
61 std::vector<std::string> so_list;
62 ailego::StringHelper::Split<std::string>(plugin_paths, ',', &so_list);
63
64 std::string error;
65 for (auto &so_path : so_list) {
66 if (!broker.emplace(so_path, &error)) {
67 std::cerr << "Failed to load plugin: " << so_path << "(" << error << ")"
68 << std::endl;
69 return false;
70 } else {
71 std::cout << "Loaded plugin: " << so_path << std::endl;
72 }
73 }
74 return true;
75}
76
77void ShutdownHandler(int sig) {
78 LOG_INFO("Receive stop signal: %d", sig);
79 auto &engine = proxima::be::server::ProximaSearchEngine::Instance();
80 engine.stop();
81}
82
83static inline void SetupSignals() {
84 ailego::ProcessHelper::IgnoreSignal(SIGHUP);
85 ailego::ProcessHelper::IgnoreSignal(SIGPIPE);
86 ailego::ProcessHelper::IgnoreSignal(SIGCHLD);
87
88 ailego::ProcessHelper::RegisterSignal(SIGINT, ShutdownHandler);
89 ailego::ProcessHelper::RegisterSignal(SIGTERM, ShutdownHandler);
90
91 // These two signals are reserved for other usage
92 ailego::ProcessHelper::RegisterSignal(SIGUSR1, ShutdownHandler);
93 ailego::ProcessHelper::RegisterSignal(SIGUSR2, ShutdownHandler);
94}
95
96int main(int argc, char *argv[]) {
97 // Parse arguments
98 for (int i = 1; i < argc; ++i) {
99 const char *arg = argv[i];
100
101 if (!strcmp(arg, "-help") || !strcmp(arg, "--help") || !strcmp(arg, "-h")) {
102 std::cout << GetUsage() << std::endl;
103 exit(0);
104 } else if (!strcmp(arg, "-version") || !strcmp(arg, "--version") ||
105 !strcmp(arg, "-v")) {
106 std::cout << proxima::be::Version::Details() << std::endl;
107 exit(0);
108 }
109 }
110 gflags::ParseCommandLineNonHelpFlags(&argc, &argv, false);
111
112 // Load config
113 proxima::be::Config &config = proxima::be::Config::Instance();
114 int ret = config.load_config(FLAGS_config);
115 if (ret != 0) {
116 std::cerr << "ProximaSE load configuration failed." << std::endl;
117 exit(1);
118 }
119 if (!config.validate_config()) {
120 std::cerr << "ProximaSE validate configuration failed." << std::endl;
121 exit(1);
122 }
123
124 // Load plugins
125 if (!FLAGS_plugin.empty()) {
126 if (!LoadPlugins(FLAGS_plugin)) {
127 std::cerr << "ProximaSE load plugins failed." << std::endl;
128 exit(1);
129 }
130 }
131
132 // Initialize bug report
133 ailego::BugReport::Bootstrap(argc, argv, config.get_log_dir().c_str());
134
135 // Start engine
136 auto &engine = proxima::be::server::ProximaSearchEngine::Instance();
137 ret = engine.init(FLAGS_daemon, FLAGS_pidfile);
138 if (ret != 0) {
139 std::cerr << "ProximaSE init failed." << std::endl;
140 exit(1);
141 }
142 engine.set_version(proxima::be::Version::String());
143
144 ret = engine.start();
145 if (ret != 0) {
146 std::cerr << "ProximaSE start failed." << std::endl;
147 engine.stop();
148 engine.cleanup();
149 exit(1);
150 } else {
151 std::cout << "ProximaSE start successfully." << std::endl;
152 }
153
154 // Handle signals
155 SetupSignals();
156
157 // Wait for signals
158 pause();
159
160 // Stop and cleanup engine
161 engine.stop();
162 engine.cleanup();
163 return 0;
164}
165