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 | |
34 | DEFINE_string(config, "" , "Read configuration from this file" ); |
35 | DEFINE_string(plugin, "" , "Load proxima plugins from so files" ); |
36 | DEFINE_string(pidfile, "" , "Write the pid into this file" ); |
37 | DEFINE_bool(daemon, false, "Run this app in daemon mode" ); |
38 | |
39 | static bool ValidateConfig(const char *flagname, const std::string &config) { |
40 | return !config.empty(); |
41 | } |
42 | |
43 | DEFINE_validator(config, ValidateConfig); |
44 | |
45 | static 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 | |
59 | static 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 | |
77 | void ShutdownHandler(int sig) { |
78 | LOG_INFO("Receive stop signal: %d" , sig); |
79 | auto &engine = proxima::be::server::ProximaSearchEngine::Instance(); |
80 | engine.stop(); |
81 | } |
82 | |
83 | static 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 | |
96 | int 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 | |