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)
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
21 *------------------------------------------------------------------
23 #include "e_cardlist.h"
32 #include <sys/types.h>
35 // #include "boost/filesystem/operations.hpp"
36 // #include "boost/filesystem/fstream.hpp"
37 // namespace fs = boost::filesystem;
38 /*--------------------------------------------------------------------------*/
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/") };
53 for(int i
=1; i
<4 ; i
++) {
54 if ( FILE* tmp
= fopen( (pathlist
[i
] + "/" + name
).c_str(), "r" ) ) {
56 name
= pathlist
[i
]+"/"+name
;
59 trace0( (" not found " + pathlist
[i
] + "/" + name
).c_str());
65 /*--------------------------------------------------------------------------*/
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);
75 void do_it(CS
& cmd
, CARD_LIST
*)
77 unsigned here
= cmd
.cursor();
78 int dl_scope
= RTLD_LOCAL
;
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
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")) {
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.
94 Get(cmd
,"make{file}", &make
);
96 } while (cmd
.more() && !cmd
.stuck(&here
));
97 trace1("attach::do_it", make
);
102 OMSTREAM _out
= IO::mstdout
;
107 for (std::map
<std::string
, void*>::iterator
108 ii
= attach_list
.begin(); ii
!= attach_list
.end(); ++ii
) {
110 _out
<< comma
<< ii
->first
;
119 void*& handle
= attach_list
[file_name
];
120 trace2("...", file_name
, handle
);
122 if (CARD_LIST::card_list
.is_empty()) {
123 cmd
.warn(bDANGER
, here
, "\"" + file_name
+ "\": already loaded, replacing");
128 throw Exception_CS("already loaded, cannot replace when there is a circuit", cmd
);
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();
144 char* cwd
= get_current_dir_name(); // POSIX, no C++ implementation available
145 source_filename
= string(cwd
) + "/" + source_filename
;
149 source_filename
= "";
152 if (source_filename
!="") {
153 trace1("attach", source_filename
);
154 assert(source_filename
[0]=='/');
156 compile(file_name
, source_filename
, make
);
157 }catch(Exception
& e
){
159 throw Exception_CS(e
.message(), cmd
);
164 handle
= dlopen(file_name
.c_str(), check
| dl_scope
);
165 const char* e
= dlerror();
166 if (check
== RTLD_LAZY
) {
168 const char* (*name
)() = (const char*(*)()) dlsym(handle
, "interface_name");
173 throw Exception_CS("missing interface", cmd
);
178 throw Exception_CS(e
, cmd
);
183 handle
= do_attach(file_name
, check
| dl_scope
, force
);
184 } catch (Exception
& e
) {
185 trace0("do_attach threw");
187 throw Exception_CS(e
.message(), cmd
);
190 trace0("done attach");
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
)
198 void* handle
= dlopen(file_name
.c_str(), flags
);
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");
205 if (force
) { untested();
206 trace1("forced load...", file_name
);
207 } else if (e
&& !force
) { untested();
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");
216 unsigned (*version
)() = (unsigned(*)()) dlsym(handle
, "interface_version");
219 } else if ((e
|| !version
) && !force
) { untested();
221 throw Exception("lacks interface version");
222 } else if (strcmp(name(), interface_name())) {
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()) + ")");
232 throw Exception(string("plugin too ") + ((interface_version() < version())?"new":"old"));
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
);
246 throw Exception("cannot compile: " + source_filename
+
247 " does not exist (" + to_string(ccstat
) + ")\n");
251 struct stat soattrib
;
252 int sostat
= stat(filename
.c_str(), &soattrib
);
253 string gnucap_cppflags
= OS::getenv("GNUCAP_CPPFLAGS", GNUCAP_CPPFLAGS
);
256 char* sf2
= strdup(filename
.c_str());
257 string
f1(basename(sf2
)); free(sf2
);
261 if (filename
[0]!='/') {
262 filename
= "./" + filename
;
265 if (!sostat
&& ccattrib
.st_mtime
< soattrib
.st_mtime
) {
266 trace0("so exists and is newer");
269 trace0("so needs to be refreshed");
270 f
= fopen(filename
.c_str(),"a");
272 trace0("so is writable");
275 tmplt
= strdup("/tmp/soXXXXXX");
277 if (!t
) throw Exception("cannot create temporary file");
278 filename
= string(t
) + "/" + f1
;
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
));
293 waitpid(p
, &childret
, 0);
295 error(bDEBUG
, "calling " + make
+ " -C" + d1
+ " VPATH=" + d2
+ " " + f1
+ "\n");
296 int ret
= execlp( make
.c_str(), make
.c_str(),
298 ("GNUCAP_CPPFLAGS="+gnucap_cppflags
+"").c_str(),
299 ("VPATH=" + d2
).c_str(),
300 f1
.c_str(), (char*) NULL
);
305 // if (t) { incomplete();
310 throw Exception("cannot make " + filename
+ "(" + to_string(childret
) + ")");
313 /*--------------------------------------------------------------------------*/
314 /*--------------------------------------------------------------------------*/
315 class CMD_DETACH
: public CMD
{
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
;
324 void* handle
= attach_list
[file_name
];
327 attach_list
[file_name
] = NULL
;
330 throw Exception_CS("plugin not attached", cmd
);
333 throw Exception_CS("detach prohibited when there is a circuit", cmd
);
337 DISPATCHER
<CMD
>::INSTALL
d2(&command_dispatcher
, "detach|unload", &p2
);
338 /*--------------------------------------------------------------------------*/
339 class CMD_DETACH_ALL
: public CMD
{
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
;
354 throw Exception_CS("detach prohibited when there is a circuit", cmd
);
358 DISPATCHER
<CMD
>::INSTALL
d3(&command_dispatcher
, "detach_all", &p3
);
359 /*--------------------------------------------------------------------------*/
361 /*--------------------------------------------------------------------------*/
362 /*--------------------------------------------------------------------------*/
363 // vim:ts=8:sw=2:noet: