Fawkes API Fawkes Development Version
openprs_kernel_manager.cpp
1
2/***************************************************************************
3 * clips_kernel_manager.cpp - OpenPRS kernel manager
4 *
5 * Created: Mon Aug 18 15:20:20 2014
6 * Copyright 2006-2014 Tim Niemueller [www.niemueller.de]
7 ****************************************************************************/
8
9/* This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version. A runtime exception applies to
13 * this software (see LICENSE.GPL_WRE file mentioned below for details).
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
21 */
22
23#include <config/config.h>
24#include <logging/logger.h>
25#include <plugins/openprs/aspect/openprs_kernel_manager.h>
26#include <utils/misc/string_commands.h>
27#include <utils/misc/string_split.h>
28#include <utils/sub_process/proc.h>
29#include <utils/time/time.h>
30
31#include <boost/format.hpp>
32#include <cstring>
33
34namespace fawkes {
35
36/** @class OpenPRSKernelManager <plugins/openprs/aspect/openprs_kernel_manager.h>
37 * OpenPRS kernel manager.
38 * The OpenPRS kernel manager creates and maintains OpenPRS
39 * kernels and provides them to the OpenPRS aspects.
40 * @author Tim Niemueller
41 */
42
43/** Constructor.
44 * @param server_host OpenPRS server hostname
45 * @param server_tcp_port TCP port where OpenPRS server listens on
46 * @param mp_host OpenPRS message passer hostname
47 * @param mp_tcp_port TCP port where OpenPRS message passer listens on
48 * @param logger logger to log messages from created kernels
49 * @param clock clock to get time from for (now)
50 * @param config configuration
51 */
52OpenPRSKernelManager::OpenPRSKernelManager(const std::string &server_host,
53 unsigned short server_tcp_port,
54 const std::string &mp_host,
55 unsigned short mp_tcp_port,
56 Logger * logger,
57 Clock * clock,
58 Configuration * config)
59: server_host_(server_host), server_port_(server_tcp_port), mp_host_(mp_host), mp_port_(mp_tcp_port)
60{
61 logger_ = logger;
62 clock_ = clock;
63 config_ = config;
64}
65
66/** Destructor. */
68{
69}
70
71/** Create a new kernel.
72 * The kernel is registered internally under the specified name.
73 * It must be destroyed when done with it. Only a single kernel
74 * can be created for a particular kernel name.
75 * @param kernel_name name by which to register kernel
76 * @param use_xoprs run X-OPRS (with graphical user interface) instead
77 * of oprs.
78 * @param extra_data_path extra directories to add to the OPRS_DATA_PATH
79 * environment variable which should be searched for files.
80 * @param utils_gdb_delay if true, will set the FAWKES_OPRS_GDB_DELAY environment
81 * variable to "true". If mod_utils is loaded it will wait for 10 seconds
82 * and print a gdb command to start debugging the kernel process.
83 */
84void
85OpenPRSKernelManager::create_kernel(const std::string & kernel_name,
86 bool use_xoprs,
87 std::list<std::string> &extra_data_path,
88 bool utils_gdb_delay)
89{
90 if (kernels_.find(kernel_name) != kernels_.end()) {
91 throw Exception("OpenPRS kernel '%s' already exists", kernel_name.c_str());
92 }
93
94 std::string server_port = boost::str(boost::format("%u") % server_port_);
95 std::string mp_port = boost::str(boost::format("%u") % mp_port_);
96
97 const char *argv[] = {use_xoprs ? "xoprs" : "oprs",
98 "-s",
99 server_host_.c_str(),
100 "-i",
101 server_port.c_str(),
102 "-m",
103 mp_host_.c_str(),
104 "-j",
105 mp_port.c_str(),
106 "-l",
107 "lower",
108 "-n",
109 kernel_name.c_str(),
110 NULL};
111
112 std::list<std::string> data_path;
113 try {
114 if (config_->is_list("/openprs/kernels/data-path")) {
115 std::vector<std::string> pl = config_->get_strings("/openprs/kernels/data-path");
116 std::for_each(pl.begin(), pl.end(), [&data_path](std::string &p) { data_path.push_back(p); });
117 } else {
118 std::string cfg_data_path = config_->get_string("/openprs/kernels/data-path");
119 data_path = str_split_list(cfg_data_path, ':');
120 }
121 } catch (Exception &e) {
122 } // ignored
123 std::list<std::string>::iterator ins_pos = data_path.begin();
124 for (auto p : extra_data_path) {
125 ins_pos = data_path.insert(ins_pos, p);
126 }
127 const std::string env_HOME = getenv("HOME");
128 for (auto &p : data_path) {
129 std::string::size_type pos = 0;
130 while ((pos = p.find("$HOME", pos)) != std::string::npos) {
131 p.replace(pos, 5, env_HOME);
132 pos += env_HOME.length();
133 }
134
135 if ((pos = p.find("@BASEDIR@")) != std::string::npos) {
136 p.replace(pos, 9, BASEDIR);
137 }
138 if ((pos = p.find("@FAWKES_BASEDIR@")) != std::string::npos) {
139 p.replace(pos, 16, FAWKES_BASEDIR);
140 }
141 if ((pos = p.find("@RESDIR@")) != std::string::npos) {
142 p.replace(pos, 8, RESDIR);
143 }
144 if ((pos = p.find("@CONFDIR@")) != std::string::npos) {
145 p.replace(pos, 9, CONFDIR);
146 }
147 }
148 std::string oprs_data_path = str_join(data_path, ':');
149
150 const char *envp_path_ext[] = {
151 "LD_LIBRARY_PATH", OPENPRS_MOD_DIR, "OPRS_DATA_PATH", oprs_data_path.c_str(), NULL};
152 std::vector<std::string> envp_v = envp_copy_expand(environ, envp_path_ext);
153
154 envp_v.push_back(
155 boost::str(boost::format("FAWKES_OPRS_GDB_DELAY=%s") % (utils_gdb_delay ? "true" : "false")));
156
157 const char *envp[envp_v.size() + 1];
158 for (unsigned int i = 0; i < envp_v.size(); ++i) {
159 envp[i] = envp_v[i].c_str();
160 if (envp_v[i].compare(0, 15, "OPRS_DATA_PATH=") == 0) {
161 logger_->log_info("OpenPRSKernelMgr", "%s data path: %s", kernel_name.c_str(), envp[i]);
162 }
163 }
164 envp[envp_v.size()] = NULL;
165
166 std::string command = command_args_tostring(argv);
167 logger_->log_info("OpenPRSKernelMgr", "Running: %s", command.c_str());
168
169 std::string progname = std::string(use_xoprs ? "XOPRS" : "OPRS") + "-" + kernel_name;
170
171 SubProcess *oprs = NULL;
172 std::string oprs_error;
173 try {
174 oprs = new SubProcess(progname.c_str(), argv[0], argv, (const char **)envp, logger_);
175 } catch (Exception &e) {
176 oprs = NULL;
177 oprs_error = e.what_no_backtrace();
178 }
179
180 if (oprs) {
181 // give some time for OpenPRS to come up
182 usleep(500000);
183
184 kernels_[kernel_name] = oprs;
185 } else {
186 throw Exception("Failed to initialize OpenPRS kernel '%s' (%s)",
187 kernel_name.c_str(),
188 oprs_error.c_str());
189 }
190}
191
192/** Destroy the named kernel.
193 * Only ever destroy kernels which you have created yourself.
194 * @param kernel_name name of the kernel to destroy
195 */
196void
197OpenPRSKernelManager::destroy_kernel(const std::string &kernel_name)
198{
199 if (kernels_.find(kernel_name) != kernels_.end()) {
200 delete kernels_[kernel_name];
201 kernels_.erase(kernel_name);
202 }
203}
204
205/** Get map of kernels.
206 * @return map from kernel name to kernel lock ptr
207 */
208std::list<std::string>
210{
211 std::list<std::string> rv;
212 for (auto k : kernels_) {
213 rv.push_back(k.first);
214 }
215 return rv;
216}
217
218} // end namespace fawkes
This is supposed to be the central clock in Fawkes.
Definition: clock.h:35
Interface for configuration handling.
Definition: config.h:68
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual bool is_list(const char *path)=0
Check if a value is a list.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
Interface for logging.
Definition: logger.h:42
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
unsigned short mp_port() const
Get mp-oprs TCP port.
virtual ~OpenPRSKernelManager()
Destructor.
OpenPRSKernelManager(const std::string &server_host, unsigned short server_tcp_port, const std::string &mp_host, unsigned short mp_tcp_port, Logger *logger, Clock *clock, Configuration *config)
Constructor.
std::list< std::string > kernels() const
Get map of kernels.
void destroy_kernel(const std::string &kernel_name)
Destroy the named kernel.
void create_kernel(const std::string &kernel_name, bool use_xoprs, std::list< std::string > &extra_data_path, bool utils_gdb_delay)
Create a new kernel.
unsigned short server_port() const
Get oprs-server TCP port.
Sub-process execution with stdin/stdout/stderr redirection.
Definition: proc.h:37
Fawkes library namespace.
static std::list< std::string > str_split_list(const std::string &s, char delim='/')
Split string by delimiter.
Definition: string_split.h:81
std::string command_args_tostring(const char *argv[])
Convert command args to string.
static std::string str_join(const std::vector< std::string > &v, char delim='/')
Join vector of strings string using given delimiter.
Definition: string_split.h:99
std::vector< std::string > envp_copy_expand(char *environ[], const char *path_ext[])
Copy an environment and extend certain paths.