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