pva2pva 1.4.2
Loading...
Searching...
No Matches
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
34extern "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
53namespace {
54
55bool verbose = false;
56
57static void exitSubroutine(subRecord *precord) {
58 epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
59}
60
61void 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
105void errIf(int ret, const std::string& msg)
106{
107 if(ret)
108 throw std::runtime_error(msg);
109}
110
111bool lazy_dbd_loaded;
112
113void 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
130int 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 // Non-interactive IOC, spin forever
265 while(true) {
266 epicsThreadSleep(1000.0);
267 }
268
269 } else {
270 usage(argv[0], dbd_file);
271 std::cerr<<"Nothing to do!\n";
272 epicsExit(1);
273 return 1;
274 }
275 }
276
277 epicsExit(0);
278 return 0;
279
280 }catch(std::exception& e){
281 std::cerr<<"Error: "<<e.what()<<"\n";
282 epicsExit(2);
283 return 2;
284 }
285}