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 
34 namespace 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  */
52 OpenPRSKernelManager::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  */
84 void
85 OpenPRSKernelManager::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  */
196 void
197 OpenPRSKernelManager::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  */
208 std::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:65
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
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.