This is Unofficial EPICS BASE Doxygen Site
softMain.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2003 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to the Software License Agreement
7 * found in the file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 
10 /* Author: Andrew Johnson Date: 2003-04-08 */
11 
12 #include <iostream>
13 #include <string>
14 #include <list>
15 #include <stdexcept>
16 
17 #include <epicsGetopt.h>
18 #include "registryFunction.h"
19 #include "epicsThread.h"
20 #include "epicsExit.h"
21 #include "epicsStdio.h"
22 #include "epicsString.h"
23 #include "dbStaticLib.h"
24 #include "subRecord.h"
25 #include "dbAccess.h"
26 #include "asDbLib.h"
27 #include "iocInit.h"
28 #include "iocsh.h"
29 #include "osiFileName.h"
30 #include "epicsInstallDir.h"
31 
32 extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
33 
34 #ifndef EPICS_BASE
35 // so IDEs knows EPICS_BASE is a string constant
36 # define EPICS_BASE "/"
37 # error -DEPICS_BASE required
38 #endif
39 
40 #define DBD_BASE "dbd" OSI_PATH_SEPARATOR "softIoc.dbd"
41 #define EXIT_BASE "db" OSI_PATH_SEPARATOR "softIocExit.db"
42 #define DBD_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR DBD_BASE
43 #define EXIT_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR EXIT_BASE
44 #define DBD_FILE EPICS_BASE OSI_PATH_SEPARATOR DBD_BASE
45 #define EXIT_FILE EPICS_BASE OSI_PATH_SEPARATOR EXIT_BASE
46 
47 namespace {
48 
49 static void exitSubroutine(subRecord *precord) {
50  epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
51 }
52 
53 void usage(const char *arg0, const std::string& base_dbd) {
54  std::cout<<"Usage: "<<arg0<<
55  " [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]\n"
56  "[-m macro=value,macro2=value2] [-d file.db]\n"
57  "[-x prefix] [st.cmd]\n"
58  "\n"
59  " -D <dbd> If used, must come first. Specify the path to the softIoc.dbdfile."
60  " The compile-time install location is saved in the binary as a default.\n"
61  "\n"
62  " -h Print this mesage and exit.\n"
63  "\n"
64  " -S Prevents an interactive shell being started.\n"
65  "\n"
66  " -s Previously caused a shell to be started. Now accepted and ignored.\n"
67  "\n"
68  " -a <acf> Access Security configuration file. Macro substitution is\n"
69  " performed.\n"
70  "\n"
71  " -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
72  " -a.\n"
73  "\n"
74  " -d <db> Load records from file (dbLoadRecords). Macro substitution is\n"
75  " performed.\n"
76  "\n"
77  " -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n"
78  " Put 0 to exit with success, or non-zero to exit with an error.\n"
79  "\n"
80  "Any number of -m and -d arguments can be interspersed; the macros are applied\n"
81  "to the following .db files. Each later -m option causes earlier macros to be\n"
82  "discarded.\n"
83  "\n"
84  "A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
85  "be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
86  "loading must be performed by the script itself, or by the user from the\n"
87  "interactive IOC shell.\n"
88  "\n"
89  "Compiled-in path to softIoc.dbd is:\n"
90  "\t"<<base_dbd.c_str()<<"\n";
91 }
92 
93 void errIf(int ret, const std::string& msg)
94 {
95  if(ret)
96  throw std::runtime_error(msg);
97 }
98 
99 bool lazy_dbd_loaded;
100 
101 void lazy_dbd(const std::string& dbd_file) {
102  if(lazy_dbd_loaded) return;
103  lazy_dbd_loaded = true;
104 
105  errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
106  std::string("Failed to load DBD file: ")+dbd_file);
107  std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
108 
110  std::cout<<"softIoc_registerRecordDeviceDriver(pdbbase)\n";
111  registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
112 }
113 
114 } // namespace
115 
116 int main(int argc, char *argv[])
117 {
118  try {
119  std::string dbd_file(DBD_FILE),
120  exit_file(EXIT_FILE),
121  macros, // scratch space for macros (may be given more than once)
122  xmacro;
123  bool interactive = true;
124  bool loadedDb = false;
125 
126  // attempt to compute relative paths
127  {
128  std::string prefix;
129  char *cprefix = epicsGetExecDir();
130  if(cprefix) {
131  try {
132  prefix = cprefix;
133  free(cprefix);
134  } catch(...) {
135  free(cprefix);
136  throw;
137  }
138  }
139 
140  dbd_file = prefix + DBD_FILE_REL;
141  exit_file = prefix + EXIT_FILE_REL;
142  }
143 
144  int opt;
145 
146  while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:")) != -1) {
147  switch (opt) {
148  case 'h': /* Print usage */
149  usage(argv[0], dbd_file);
150  epicsExit(0);
151  return 0;
152  default:
153  usage(argv[0], dbd_file);
154  std::cerr<<"Unknown argument: -"<<char(opt)<<"\n";
155  epicsExit(2);
156  return 2;
157  case 'a':
158  lazy_dbd(dbd_file);
159  if (!macros.empty()) {
160  if(asSetSubstitutions(macros.c_str()))
161  throw std::bad_alloc();
162  std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
163  }
164  if(asSetFilename(optarg))
165  throw std::bad_alloc();
166  std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
167  break;
168  case 'D':
169  if(lazy_dbd_loaded) {
170  throw std::runtime_error("-D specified too late. softIoc.dbd already loaded.\n");
171  }
172  dbd_file = optarg;
173  break;
174  case 'd':
175  lazy_dbd(dbd_file);
176  errIf(dbLoadRecords(optarg, macros.c_str()),
177  std::string("Failed to load: ")+optarg);
178  std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
179  if(!macros.empty())
180  std::cout<<", \""<<macros<<"\"";
181  std::cout<<")\n";
182  loadedDb = true;
183  break;
184  case 'm':
185  macros = optarg;
186  break;
187  case 'S':
188  interactive = false;
189  break;
190  case 's':
191  break; // historical
192  case 'x':
193  lazy_dbd(dbd_file);
194  xmacro = "IOC=";
195  xmacro += optarg;
196  errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()),
197  std::string("Failed to load: ")+exit_file);
198  loadedDb = true;
199  break;
200  }
201  }
202 
203  lazy_dbd(dbd_file);
204 
205  if(optind<argc) {
206  // run script
207  // ignore any extra positional args (historical)
208 
209  std::cout<<"# Begin "<<argv[optind]<<"\n";
210  errIf(iocsh(argv[optind]),
211  std::string("Error in ")+argv[optind]);
212  std::cout<<"# End "<<argv[optind]<<"\n";
213 
214  epicsThreadSleep(0.2);
215  loadedDb = true; /* Give it the benefit of the doubt... */
216  }
217 
218  if (loadedDb) {
219  std::cout<<"iocInit()\n";
220  iocInit();
221  epicsThreadSleep(0.2);
222  }
223 
224  if(interactive) {
225  std::cout.flush();
226  std::cerr.flush();
227  if(iocsh(NULL)) {
228  epicsExit(1);
229  return 1;
230  }
231 
232  } else {
233  if (loadedDb) {
235 
236  } else {
237  usage(argv[0], dbd_file);
238  std::cerr<<"Nothing to do!\n";
239  epicsExit(1);
240  return 1;
241  }
242  }
243 
244  epicsExit(0);
245  return 0;
246 
247  }catch(std::exception& e){
248  std::cerr<<"Error: "<<e.what()<<"\n";
249  epicsExit(2);
250  return 2;
251  }
252 }
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: epicsGetopt.c:65
void(* REGISTRYFUNCTION)(void)
int optind
Definition: epicsGetopt.c:50
int interactive
Definition: flex.c:67
int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase)
LIBCOM_API void epicsExitLater(int status)
Arrange to call epicsExit() later from a low priority thread.
Definition: epicsExit.c:204
#define NULL
Definition: catime.c:38
LIBCOM_API char * epicsGetExecDir(void)
Definition: osdgetexec.c:38
int asSetSubstitutions(const char *substitutions)
Definition: asDbLib.c:91
int epicsStdCall iocsh(const char *pathname)
Definition: iocsh.cpp:1011
#define EXIT_FILE_REL
Definition: softMain.cpp:43
LIBCOM_API void epicsExit(int status)
Calls epicsExitCallAtExits(), then the OS exit() routine.
Definition: epicsExit.c:182
Extended replacement for the Posix exit and atexit routines.
LIBCOM_API void epicsStdCall epicsThreadExitMain(void)
Definition: osdThread.c:683
const char * arg0
Definition: softMain.cpp:89
epicsShareFunc int registryFunctionAdd(const char *name, REGISTRYFUNCTION func)
const char * base_dbd
Definition: softMain.cpp:90
int main(int argc, char *argv[])
Definition: softMain.cpp:116
int iocInit(void)
Definition: iocInit.c:108
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
Definition: dbBase.h:170
C++ and C descriptions for a thread.
#define EXIT_FILE
Definition: softMain.cpp:45
int asSetFilename(const char *acf)
Definition: asDbLib.c:70
char * optarg
Definition: epicsGetopt.c:55
void usage(void)
Definition: cainfo.c:36
#define DBD_FILE_REL
Definition: softMain.cpp:42
#define DBD_FILE
Definition: softMain.cpp:44