pva2pva  1.3.1
softMain.cpp
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 <epicsVersion.h>
18 #include <epicsGetopt.h>
19 #include "registryFunction.h"
20 #include "epicsThread.h"
21 #include "epicsExit.h"
22 #include "epicsStdio.h"
23 #include "epicsString.h"
24 #include "dbStaticLib.h"
25 #include "subRecord.h"
26 #include "dbAccess.h"
27 #include "asDbLib.h"
28 #include "iocInit.h"
29 #include "iocsh.h"
30 #include "osiFileName.h"
31 
32 #include <pv/qsrv.h>
33 
34 extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
35 
36 #ifndef EPICS_BASE
37 // so IDEs knows EPICS_BASE is a string constant
38 # define EPICS_BASE "/"
39 # error -DEPICS_BASE required
40 #endif
41 
42 #if EPICS_VERSION_INT>=VERSION_INT(7,0,2,0)
43 # define USE_EXECDIR
44 #endif
45 
46 #define DBD_BASE "dbd" OSI_PATH_SEPARATOR "softIocPVA.dbd"
47 #define EXIT_BASE "db" OSI_PATH_SEPARATOR "softIocExit.db"
48 #define DBD_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR DBD_BASE
49 #define EXIT_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR EXIT_BASE
50 #define DBD_FILE EPICS_BASE OSI_PATH_SEPARATOR DBD_BASE
51 #define EXIT_FILE EPICS_BASE OSI_PATH_SEPARATOR EXIT_BASE
52 
53 namespace {
54 
55 bool verbose = false;
56 
57 static void exitSubroutine(subRecord *precord) {
58  epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
59 }
60 
61 void usage(const char *arg0, const std::string& base_dbd) {
62  std::cout<<"Usage: "<<arg0<<
63  " [-D softIocPVA.dbd] [-h] [-S] [-s] [-v] [-a ascf]\n"
64  "[-m macro=value,macro2=value2] [-d file.db]\n"
65  "[-x prefix] [st.cmd]\n"
66  "\n"
67  " -D <dbd> If used, must come first. Specify the path to the softIocPVA.dbdfile."
68  " The compile-time install location is saved in the binary as a default.\n"
69  "\n"
70  " -h Print this mesage and exit.\n"
71  "\n"
72  " -S Prevents an interactive shell being started.\n"
73  "\n"
74  " -s Previously caused a shell to be started. Now accepted and ignored.\n"
75  "\n"
76  " -v Verbose, display steps taken during startup.\n"
77  "\n"
78  " -a <acf> Access Security configuration file. Macro substitution is\n"
79  " performed.\n"
80  "\n"
81  " -G <json> DB Group definition file in JSON format.\n"
82  "\n"
83  " -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
84  " -a.\n"
85  "\n"
86  " -d <db> Load records from file (dbLoadRecords). Macro substitution is\n"
87  " performed.\n"
88  "\n"
89  " -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n"
90  " Put 0 to exit with success, or non-zero to exit with an error.\n"
91  "\n"
92  "Any number of -m and -d arguments can be interspersed; the macros are applied\n"
93  "to the following .db files. Each later -m option causes earlier macros to be\n"
94  "discarded.\n"
95  "\n"
96  "A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
97  "be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
98  "loading must be performed by the script itself, or by the user from the\n"
99  "interactive IOC shell.\n"
100  "\n"
101  "Compiled-in path to softIocPVA.dbd is:\n"
102  "\t"<<base_dbd.c_str()<<"\n";
103 }
104 
105 void errIf(int ret, const std::string& msg)
106 {
107  if(ret)
108  throw std::runtime_error(msg);
109 }
110 
111 bool lazy_dbd_loaded;
112 
113 void lazy_dbd(const std::string& dbd_file) {
114  if(lazy_dbd_loaded) return;
115  lazy_dbd_loaded = true;
116 
117  if (verbose)
118  std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
119  errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
120  std::string("Failed to load DBD file: ")+dbd_file);
121 
122  if (verbose)
123  std::cout<<"softIocPVA_registerRecordDeviceDriver(pdbbase)\n";
124  softIocPVA_registerRecordDeviceDriver(pdbbase);
125  registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
126 }
127 
128 } // namespace
129 
130 int main(int argc, char *argv[])
131 {
132  try {
133  std::string dbd_file(DBD_FILE),
134  exit_file(EXIT_FILE),
135  macros, // scratch space for macros (may be given more than once)
136  xmacro;
137  bool interactive = true;
138  bool loadedDb = false;
139  bool ranScript = false;
140 
141 #ifdef USE_EXECDIR
142  // attempt to compute relative paths
143  {
144  std::string prefix;
145  char *cprefix = epicsGetExecDir();
146  if(cprefix) {
147  try {
148  prefix = cprefix;
149  free(cprefix);
150  } catch(...) {
151  free(cprefix);
152  throw;
153  }
154  }
155 
156  dbd_file = prefix + DBD_FILE_REL;
157  exit_file = prefix + EXIT_FILE_REL;
158  }
159 #endif
160 
161  int opt;
162 
163  while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:G:v")) != -1) {
164  switch (opt) {
165  case 'h': /* Print usage */
166  usage(argv[0], dbd_file);
167  epicsExit(0);
168  return 0;
169  default:
170  usage(argv[0], dbd_file);
171  std::cerr<<"Unknown argument: -"<<char(opt)<<"\n";
172  epicsExit(2);
173  return 2;
174  case 'a':
175  lazy_dbd(dbd_file);
176  if (!macros.empty()) {
177  if (verbose)
178  std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
179  if(asSetSubstitutions(macros.c_str()))
180  throw std::bad_alloc();
181  }
182  if (verbose)
183  std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
184  if(asSetFilename(optarg))
185  throw std::bad_alloc();
186  break;
187  case 'D':
188  if(lazy_dbd_loaded) {
189  throw std::runtime_error("-D specified too late. softIocPVA.dbd already loaded.\n");
190  }
191  dbd_file = optarg;
192  break;
193  case 'd':
194  lazy_dbd(dbd_file);
195  if (verbose) {
196  std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
197  if(!macros.empty())
198  std::cout<<", \""<<macros<<"\"";
199  std::cout<<")\n";
200  }
201  errIf(dbLoadRecords(optarg, macros.c_str()),
202  std::string("Failed to load: ")+optarg);
203  loadedDb = true;
204  break;
205  case 'm':
206  macros = optarg;
207  break;
208  case 'S':
209  interactive = false;
210  break;
211  case 's':
212  break; // historical
213  case 'v':
214  verbose = true;
215  break;
216  case 'x':
217  lazy_dbd(dbd_file);
218  xmacro = "IOC=";
219  xmacro += optarg;
220  errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()),
221  std::string("Failed to load: ")+exit_file);
222  loadedDb = true;
223  break;
224  case 'G':
225  dbLoadGroup(optarg);
226  break;
227  }
228  }
229 
230  lazy_dbd(dbd_file);
231 
232  if(optind<argc) {
233  // run script
234  // ignore any extra positional args (historical)
235 
236  if (verbose)
237  std::cout<<"# Begin "<<argv[optind]<<"\n";
238  errIf(iocsh(argv[optind]),
239  std::string("Error in ")+argv[optind]);
240  if (verbose)
241  std::cout<<"# End "<<argv[optind]<<"\n";
242 
243  epicsThreadSleep(0.2);
244  ranScript = true; /* Assume the script has done any necessary initialization */
245  }
246 
247  if (loadedDb) {
248  if (verbose)
249  std::cout<<"iocInit()\n";
250  iocInit();
251  epicsThreadSleep(0.2);
252  }
253 
254  if(interactive) {
255  std::cout.flush();
256  std::cerr.flush();
257  if(iocsh(NULL)) {
258  epicsExit(1);
259  return 1;
260  }
261 
262  } else {
263  if (loadedDb || ranScript) {
264  epicsThreadExitMain();
265 
266  } else {
267  usage(argv[0], dbd_file);
268  std::cerr<<"Nothing to do!\n";
269  epicsExit(1);
270  return 1;
271  }
272  }
273 
274  epicsExit(0);
275  return 0;
276 
277  }catch(std::exception& e){
278  std::cerr<<"Error: "<<e.what()<<"\n";
279  epicsExit(2);
280  return 2;
281  }
282 }