bump to -rc12
[gnucap-felix.git] / lib / c_attach.cc
blob51e8da1cd11c3c6d0cca16da4e1b0590597770ad
1 /* -*- C++ -*-
2 * Copyright (C) 2007 Albert Davis
3 * Author: Albert Davis <aldavis@gnu.org>
5 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *------------------------------------------------------------------
23 #include "e_cardlist.h"
24 #include "c_comand.h"
25 #include "constant.h"
26 #include "globals.h"
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <sys/stat.h>
31 #include <libgen.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
35 // #include "boost/filesystem/operations.hpp"
36 // #include "boost/filesystem/fstream.hpp"
37 // namespace fs = boost::filesystem;
38 /*--------------------------------------------------------------------------*/
39 namespace {
41 #if 0
42 bool search_file( std::string &name ){
43 const char* h ="HOME";
44 const char* home= getenv(h);
46 if (name[0] == '/') return true; // fixme
48 std::string pathlist[4] = { OPT::libpath , "./", LIBDIR,
49 std::string(home) + std::string("/.gnucap/lib/") };
51 // FIXME. use libpath
53 for(int i=1; i<4 ; i++) {
54 if ( FILE* tmp = fopen( (pathlist[i] + "/" + name).c_str(), "r" ) ) {
55 fclose(tmp);
56 name = pathlist[i]+"/"+name;
57 return true;
59 trace0( (" not found " + pathlist[i] + "/" + name).c_str());
61 return false;
64 #endif
65 /*--------------------------------------------------------------------------*/
66 using std::string;
67 /*--------------------------------------------------------------------------*/
68 std::map<const std::string, void*> attach_list;
69 /*--------------------------------------------------------------------------*/
70 /*--------------------------------------------------------------------------*/
71 class CMD_ATTACH : public CMD {
72 static void compile(string& filename, string source, string make);
73 static void* do_attach(string filename, int flags, bool force=false);
74 public:
75 void do_it(CS& cmd, CARD_LIST*)
77 unsigned here = cmd.cursor();
78 int dl_scope = RTLD_LOCAL;
79 int check = RTLD_NOW;
80 string make = OS::getenv("GNUCAP_MAKE", GNUCAP_MAKE);
81 // RTLD_NOW means to resolve symbols on loading
82 // RTLD_LOCAL means symbols defined in a plugin are local
83 do {
84 if (cmd.umatch("public ")) {
85 dl_scope = RTLD_GLOBAL;
86 // RTLD_GLOBAL means symbols defined in a plugin are global
87 // Use this when a plugin depends on another.
88 }else if (cmd.umatch("lazy|force")) {
89 check = RTLD_LAZY;
90 // RTLD_LAZY means to defer resolving symbols until needed
91 // Use when a plugin will not load because of unresolved symbols,
92 // but it may work without it.
93 }else{
94 Get(cmd,"make{file}", &make);
96 } while (cmd.more() && !cmd.stuck(&here));
97 trace1("attach::do_it", make);
99 string file_name;
100 cmd >> file_name;
102 OMSTREAM _out = IO::mstdout;
103 _out.outset(cmd);
105 if(file_name==""){
106 string comma;
107 for (std::map<std::string, void*>::iterator
108 ii = attach_list.begin(); ii != attach_list.end(); ++ii) {
109 if (ii->second) {
110 _out << comma << ii->first;
111 comma = ",\n";
112 }else{
115 _out << "\n";
116 return;
119 void*& handle = attach_list[file_name];
120 trace2("...", file_name, handle);
121 if (handle) {
122 if (CARD_LIST::card_list.is_empty()) {
123 cmd.warn(bDANGER, here, "\"" + file_name + "\": already loaded, replacing");
124 dlclose(handle);
125 handle = NULL;
126 }else{untested();
127 cmd.reset(here);
128 throw Exception_CS("already loaded, cannot replace when there is a circuit", cmd);
130 }else{
133 string source_filename(file_name);
134 // FIXME: incomplete... some more control...
135 // global list of supported suffixes?
136 if (file_name.size()>3 && !strcmp(file_name.c_str()+file_name.size()-3,".so")) {
137 source_filename = "";
138 }else if (file_name.size()>3 && file_name.c_str()[file_name.size()-3] == '.') {
139 file_name[file_name.size()-2]='s';
140 file_name[file_name.size()-1]='o';
142 if(file_name[0]=='/') { itested();
143 } else {
144 char* cwd = get_current_dir_name(); // POSIX, no C++ implementation available
145 source_filename = string(cwd) + "/" + source_filename;
146 free(cwd);
148 } else {
149 source_filename = "";
152 if (source_filename!="") {
153 trace1("attach", source_filename);
154 assert(source_filename[0]=='/');
155 try {
156 compile(file_name, source_filename, make);
157 }catch(Exception& e){
158 cmd.reset(here);
159 throw Exception_CS(e.message(), cmd);
161 }else{
164 handle = dlopen(file_name.c_str(), check | dl_scope);
165 const char* e = dlerror();
166 if (check == RTLD_LAZY) {
167 }else if (handle) {
168 const char* (*name)() = (const char*(*)()) dlsym(handle, "interface_name");
169 if (name){
170 }else{
171 dlclose(handle);
172 handle = NULL;
173 throw Exception_CS("missing interface", cmd);
176 if (e){
177 cmd.reset(here);
178 throw Exception_CS(e, cmd);
180 #if 0
181 try {
182 assert(!handle);
183 handle = do_attach(file_name, check | dl_scope, force);
184 } catch (Exception& e) {
185 trace0("do_attach threw");
186 cmd.reset(here);
187 throw Exception_CS(e.message(), cmd);
189 #endif
190 trace0("done attach");
192 } p1;
193 DISPATCHER<CMD>::INSTALL d1(&command_dispatcher, "attach|load", &p1);
194 /*--------------------------------------------------------------------------*/
195 // overengineered gncap-uf approach
196 void* CMD_ATTACH::do_attach(string file_name, int flags, bool force)
197 { untested();
198 void* handle = dlopen(file_name.c_str(), flags);
199 char* e = dlerror();
200 if(e || !handle ) { untested();
201 throw Exception("cannot attach (" + string(e) + ")");
202 }else if (handle) { untested();
203 const char* (*name)() = (const char*(*)()) dlsym(handle, "interface_name");
204 e = dlerror();
205 if (force) { untested();
206 trace1("forced load...", file_name);
207 } else if (e && !force) { untested();
208 dlclose(handle);
209 throw Exception(file_name + " lacks interface information");
210 } else if ((e || !name) && !force) { untested();
211 dlclose(handle); untested();
212 throw Exception("lacks interface name");
213 } else { untested();
216 unsigned (*version)() = (unsigned(*)()) dlsym(handle, "interface_version");
217 e = dlerror();
218 if (force) {
219 } else if ((e || !version) && !force) { untested();
220 dlclose(handle);
221 throw Exception("lacks interface version");
222 } else if (strcmp(name(), interface_name())) {
223 string n(name());
224 dlclose(handle);
225 throw Exception(file_name + ": wrong interface ("+ n +
226 ", not " + string(interface_name()) + ")");
227 } else if (interface_version() == version()) {
228 } else if (HAVE_GIT_REPO) { untested();
229 throw Exception("loading " + file_name + ": plugin (" + to_string(version()) +
230 ") doesnt match git revision (" + to_string(interface_version()) + ")");
231 } else { untested();
232 throw Exception(string("plugin too ") + ((interface_version() < version())?"new":"old"));
234 assert(handle);
235 return handle;
236 }else{itested();
237 throw Exception(dlerror());
240 /*--------------------------------------------------------------------------*/
241 void CMD_ATTACH::compile(string &filename, string source_filename, string make)
243 struct stat ccattrib;
244 int ccstat = stat(source_filename.c_str(), &ccattrib);
245 if (ccstat) {
246 throw Exception("cannot compile: " + source_filename +
247 " does not exist (" + to_string(ccstat) + ")\n");
248 } else {
251 struct stat soattrib;
252 int sostat = stat(filename.c_str(), &soattrib);
253 string gnucap_cppflags = OS::getenv("GNUCAP_CPPFLAGS", GNUCAP_CPPFLAGS);
255 FILE* f = NULL;
256 char* sf2 = strdup(filename.c_str());
257 string f1(basename(sf2)); free(sf2);
258 char* tmplt = NULL;
259 char* t = NULL;
261 if (filename[0]!='/') {
262 filename = "./" + filename;
265 if (!sostat && ccattrib.st_mtime < soattrib.st_mtime) {
266 trace0("so exists and is newer");
267 return;
268 } else if(!sostat) {
269 trace0("so needs to be refreshed");
270 f = fopen(filename.c_str(),"a");
271 if (f) {
272 trace0("so is writable");
273 fclose(f);
274 } else { untested();
275 tmplt = strdup("/tmp/soXXXXXX");
276 t = mkdtemp(tmplt);
277 if (!t) throw Exception("cannot create temporary file");
278 filename = string(t) + "/" + f1;
280 } else {
282 free(tmplt);
283 t = NULL;
285 char* sf1 = strdup(source_filename.c_str());
286 char* fn1 = strdup(filename.c_str());
287 string d1(dirname(fn1));
288 string d2(dirname(sf1));
290 int childret;
291 pid_t p = vfork();
292 if (p) {
293 waitpid(p, &childret, 0);
294 } else {
295 error(bDEBUG, "calling " + make + " -C" + d1 + " VPATH=" + d2 + " " + f1 + "\n");
296 int ret = execlp( make.c_str(), make.c_str(),
297 "-C", d1.c_str(),
298 ("GNUCAP_CPPFLAGS="+gnucap_cppflags+"").c_str(),
299 ("VPATH=" + d2).c_str(),
300 f1.c_str(), (char*) NULL);
301 _exit(ret);
303 free(sf1);
304 free(fn1);
305 // if (t) { incomplete();
306 // // rm -r t;
307 // }
309 if(childret){
310 throw Exception("cannot make " + filename + "(" + to_string(childret) + ")");
313 /*--------------------------------------------------------------------------*/
314 /*--------------------------------------------------------------------------*/
315 class CMD_DETACH : public CMD {
316 public:
317 void do_it(CS& cmd, CARD_LIST*)
319 if (CARD_LIST::card_list.is_empty()) {
320 unsigned here = cmd.cursor();
321 std::string file_name;
322 cmd >> file_name;
324 void* handle = attach_list[file_name];
325 if (handle) {
326 dlclose(handle);
327 attach_list[file_name] = NULL;
328 }else{
329 cmd.reset(here);
330 throw Exception_CS("plugin not attached", cmd);
332 }else{itested();
333 throw Exception_CS("detach prohibited when there is a circuit", cmd);
336 } p2;
337 DISPATCHER<CMD>::INSTALL d2(&command_dispatcher, "detach|unload", &p2);
338 /*--------------------------------------------------------------------------*/
339 class CMD_DETACH_ALL : public CMD {
340 public:
341 void do_it(CS& cmd, CARD_LIST*)
343 if (CARD_LIST::card_list.is_empty()) {
344 for (std::map<std::string, void*>::iterator
345 ii = attach_list.begin(); ii != attach_list.end(); ++ii) {
346 void* handle = ii->second;
347 if (handle) {
348 dlclose(handle);
349 ii->second = NULL;
350 }else{
353 } else {
354 throw Exception_CS("detach prohibited when there is a circuit", cmd);
357 } p3;
358 DISPATCHER<CMD>::INSTALL d3(&command_dispatcher, "detach_all", &p3);
359 /*--------------------------------------------------------------------------*/
361 /*--------------------------------------------------------------------------*/
362 /*--------------------------------------------------------------------------*/
363 // vim:ts=8:sw=2:noet: