xref: /haiku/src/kits/support/Archivable.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
1 /*
2  * Copyright (c) 2001-2006, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Erik Jaesler (erik@cgsoftware.com)
7  */
8 
9 /**	Description:	BArchivable mix-in class defines the archiving
10 					protocol.  Also some global archiving functions.*/
11 
12 
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string>
18 #include <typeinfo>
19 #include <vector>
20 
21 #include <AppFileInfo.h>
22 #include <Archivable.h>
23 #include <Entry.h>
24 #include <List.h>
25 #include <OS.h>
26 #include <Path.h>
27 #include <Roster.h>
28 #include <String.h>
29 #include <SupportDefs.h>
30 #include <syslog.h>
31 
32 
33 using std::string;
34 using std::vector;
35 
36 const char* B_CLASS_FIELD = "class";
37 const char* B_ADD_ON_FIELD = "add_on";
38 const int32 FUNC_NAME_LEN = 1024;
39 
40 // TODO: consider moving these
41 //		 to a separate module, and making them more full-featured (e.g., taking
42 //		 NS::ClassName::Function(Param p) instead of just NS::ClassName)
43 static void Demangle(const char *name, BString &out);
44 static void Mangle(const char *name, BString &out);
45 static instantiation_func FindFuncInImage(BString& funcName, image_id id,
46 										  status_t& err);
47 static bool CheckSig(const char* sig, image_info& info);
48 
49 /*
50 // TODO: Where do these get triggered from?
51 Log entries graciously coughed up by the Be implementation:
52 	Nov 28 01:40:45 instantiate_object failed: NULL BMessage argument
53 	Nov 28 01:40:45 instantiate_object failed: Failed to find an entrydefining the class name (Name not found).
54 	Nov 28 01:40:45 instantiate_object failed: No signature specified in archive, looking for class "TInvalidClassName".
55 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
56 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
57 	Nov 28 01:40:45 instantiate_object failed: Failed to find exported Instantiate static function for class TInvalidClassName.
58 	Nov 28 01:40:45 instantiate_object failed: Invalid argument (80000005)
59 	Nov 28 01:40:45 instantiate_object failed: No signature specified in archive, looking for class "TRemoteTestObject".
60 	Nov 28 01:40:45 instantiate_object - couldn't get mime sig for /boot/home/src/projects/OpenBeOS/app_kit/test/lib/support/BArchivable/./BArchivableSystemTester
61 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
62 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
63 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
64 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
65 	Nov 28 01:40:45 instantiate_object failed: Error finding app with signature "application/x-vnd.InvalidSignature" (Application could not be found)
66 	Nov 28 01:40:45 instantiate_object failed: Application could not be found (8000200b)
67 */
68 
69 
70 BArchivable::BArchivable()
71 {
72 }
73 
74 
75 BArchivable::BArchivable(BMessage* from)
76 {
77 }
78 
79 
80 BArchivable::~BArchivable()
81 {
82 }
83 
84 
85 status_t
86 BArchivable::Archive(BMessage* into, bool deep) const
87 {
88 	if (!into) {
89 		// TODO: logging/other error reporting?
90 		return B_BAD_VALUE;
91 	}
92 
93 	BString name;
94 	Demangle(typeid(*this).name(), name);
95 
96 	return into->AddString(B_CLASS_FIELD, name);
97 }
98 
99 
100 BArchivable*
101 BArchivable::Instantiate(BMessage* from)
102 {
103 	debugger("Can't create a plain BArchivable object");
104 	return NULL;
105 }
106 
107 
108 status_t
109 BArchivable::Perform(perform_code d, void* arg)
110 {
111 	// TODO: Check against original
112 	return B_ERROR;
113 }
114 
115 
116 void BArchivable::_ReservedArchivable1() {}
117 void BArchivable::_ReservedArchivable2() {}
118 void BArchivable::_ReservedArchivable3() {}
119 
120 
121 // #pragma mark -
122 
123 
124 void
125 BuildFuncName(const char* className, BString& funcName)
126 {
127 	funcName = "";
128 
129 	//	This is what we're after:
130 	//		Instantiate__Q28OpenBeOS11BArchivableP8BMessage
131 	Mangle(className, funcName);
132 #if __GNUC__ >= 4
133 	funcName.Prepend("_ZN");
134 	funcName.Append("11InstantiateE");
135 #else
136 	funcName.Prepend("Instantiate__");
137 #endif
138 	funcName.Append("P8BMessage");
139 }
140 
141 
142 BArchivable*
143 instantiate_object(BMessage* archive, image_id* id)
144 {
145 	errno = B_OK;
146 
147 	// Check our params
148 	if (id) {
149 		*id = B_BAD_VALUE;
150 	}
151 
152 	if (!archive) {
153 		// TODO: extended error handling
154 		errno = B_BAD_VALUE;
155 		syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
156 		return NULL;
157 	}
158 
159 	// Get class name from archive
160 	const char* name = NULL;
161 	status_t err = archive->FindString(B_CLASS_FIELD, &name);
162 	if (err) {
163 		// TODO: extended error handling
164 		syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
165 			   "defining the class name (%s).", strerror(err));
166 		return NULL;
167 	}
168 
169 	// Get sig from archive
170 	const char* sig = NULL;
171 	bool hasSig = (archive->FindString(B_ADD_ON_FIELD, &sig) == B_OK);
172 
173 	instantiation_func iFunc = find_instantiation_func(name, sig);
174 
175 	//	if find_instantiation_func() can't locate Class::Instantiate()
176 	//		and a signature was specified
177 	if (!iFunc && hasSig) {
178 		//	use BRoster::FindApp() to locate an app or add-on with the symbol
179 		BRoster Roster;
180 		entry_ref ref;
181 		err = Roster.FindApp(sig, &ref);
182 
183 		BEntry entry;
184 
185 		//	if an entry_ref is obtained
186 		if (!err)
187 			err = entry.SetTo(&ref);
188 
189 		if (err) {
190 			syslog(LOG_ERR, "instantiate_object failed: Error finding app "
191 							"with signature \"%s\" (%s)", sig, strerror(err));
192 		}
193 
194 		if (!err) {
195 			BPath path;
196 			err = entry.GetPath(&path);
197 			if (!err) {
198 				//	load the app/add-on
199 				image_id theImage = load_add_on(path.Path());
200 				if (theImage < 0) {
201 					// TODO: extended error handling
202 					return NULL;
203 				}
204 
205 				// Save the image_id
206 				if (id) {
207 					*id = theImage;
208 				}
209 
210 				BString funcName;
211 				BuildFuncName(name, funcName);
212 				iFunc = FindFuncInImage(funcName, theImage, err);
213 				if (!iFunc)
214 				{
215 					syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
216 						   "Instantiate static function for class %s.", name);
217 				}
218 			}
219 		}
220 	} else if (!iFunc) {
221 		syslog(LOG_ERR, "instantiate_object failed: No signature specified "
222 			   "in archive, looking for class \"%s\".", name);
223 		errno = B_BAD_VALUE;
224 	}
225 
226 	if (err) {
227 		// TODO: extended error handling
228 		syslog(LOG_ERR, "instantiate_object failed: %s (%x)",
229 			   strerror(err), err);
230 		errno = err;
231 		return NULL;
232 	}
233 
234 	//	if Class::Instantiate(BMessage*) was found
235 	if (iFunc) {
236 		//	use to create and return an object instance
237 		return iFunc(archive);
238 	}
239 
240 	return NULL;
241 }
242 
243 
244 BArchivable*
245 instantiate_object(BMessage* from)
246 {
247 	return instantiate_object(from, NULL);
248 }
249 
250 
251 bool
252 validate_instantiation(BMessage* from, const char* class_name)
253 {
254 	errno = B_OK;
255 
256 	// Make sure our params are kosher -- original skimped here =P
257 	if (!from) {
258 		// Not standard; Be implementation has a segment
259 		// violation on this error mode
260 		errno = B_BAD_VALUE;
261 
262 		return false;
263 	}
264 
265 	status_t err = B_OK;
266 	const char* data;
267 	for (int32 index = 0; err == B_OK; ++index) {
268 		err = from->FindString(B_CLASS_FIELD, index, &data);
269 		if (!err && strcmp(data, class_name) == 0) {
270 			return true;
271 		}
272 	}
273 
274 	errno = B_MISMATCHED_VALUES;
275 	syslog(LOG_ERR, "validate_instantiation failed on class %s.", class_name);
276 
277 	return false;
278 }
279 
280 
281 instantiation_func
282 find_instantiation_func(const char* class_name, const char* sig)
283 {
284 	errno = B_OK;
285 	if (!class_name) {
286 		errno = B_BAD_VALUE;
287 		return NULL;
288 	}
289 
290 	instantiation_func theFunc = NULL;
291 	BString funcName;
292 
293 	BuildFuncName(class_name, funcName);
294 
295 //printf("find_instantiation_func() - looking for '%s'\n", funcName.String());
296 
297 	thread_id tid = find_thread(NULL);
298 	thread_info ti;
299 	status_t err = get_thread_info(tid, &ti);
300 	if (!err) {
301 		//	for each image_id in team_id
302 		image_info info;
303 		int32 cookie = 0;
304 		while (!theFunc && (get_next_image_info(ti.team, &cookie, &info) == B_OK)) {
305 			theFunc = FindFuncInImage(funcName, info.id, err);
306 		}
307 
308 		if (theFunc && !CheckSig(sig, info)) {
309 			// TODO: extended error handling
310 			theFunc = NULL;
311 		}
312 	}
313 
314 //printf("find_instantiation_func(): %p\n", theFunc);
315 
316 	return theFunc;
317 }
318 
319 
320 instantiation_func
321 find_instantiation_func(const char* class_name)
322 {
323 	return find_instantiation_func(class_name, NULL);
324 }
325 
326 
327 instantiation_func
328 find_instantiation_func(BMessage* archive_data)
329 {
330 	errno = B_OK;
331 
332 	if (!archive_data) {
333 		// TODO:  extended error handling
334 		errno = B_BAD_VALUE;
335 		return NULL;
336 	}
337 
338 	const char* name = NULL;
339 	const char* sig = NULL;
340 	status_t err;
341 
342 	err = archive_data->FindString(B_CLASS_FIELD, &name);
343 	if (err) {
344 		// TODO:  extended error handling
345 		return NULL;
346 	}
347 
348 	err = archive_data->FindString(B_ADD_ON_FIELD, &sig);
349 
350 	return find_instantiation_func(name, sig);
351 }
352 
353 
354 // #pragma mark -
355 
356 
357 int
358 GetNumber(const char*& name)
359 {
360 	int val = atoi(name);
361 	while (isdigit(*name)) {
362 		++name;
363 	}
364 
365 	return val;
366 }
367 
368 
369 void
370 Demangle(const char* name, BString& out)
371 {
372 // TODO: add support for template classes
373 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
374 
375 	out = "";
376 
377 	// Are we in a namespace?
378 	if (*name == 'Q') {
379 		// Yessir, we are; how many deep are we?
380 		int nsCount = 0;
381 		++name;
382 		if (*name == '_') {
383 			// more than 10 deep
384 			++name;
385 			if (!isdigit(*name))
386 				;	// TODO: error handling
387 
388 			nsCount = GetNumber(name);
389 			if (*name == '_')	// more than 10 deep
390 				++name;
391 			else
392 				;	// this should be an error condition
393 		} else {
394 			nsCount = *name - '0';
395 			++name;
396 		}
397 
398 		int nameLen = 0;
399 		for (int i = 0; i < nsCount - 1; ++i) {
400 			if (!isdigit(*name))
401 				;	// TODO: error handling
402 
403 			nameLen = GetNumber(name);
404 			out.Append(name, nameLen);
405 			out += "::";
406 			name += nameLen;
407 		}
408 	}
409 
410 	out.Append(name, GetNumber(name));
411 }
412 
413 
414 void
415 Mangle(const char* name, BString& out)
416 {
417 // TODO: add support for template classes
418 //	_find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
419 
420 	//	Chop this:
421 	//		testthree::testfour::Testthree::Testfour
422 	//	up into little bite-sized pieces
423 	int count = 0;
424 	string origName(name);
425 	vector<string> spacenames;
426 
427 	string::size_type pos = 0;
428 	string::size_type oldpos = 0;
429 	while (pos != string::npos) {
430 		pos = origName.find_first_of("::", oldpos);
431 		spacenames.push_back(string(origName, oldpos, pos - oldpos));
432 		pos = origName.find_first_not_of("::", pos);
433 		oldpos = pos;
434 		++count;
435 	}
436 
437 	//	Now mangle it into this:
438 	//		Q49testthree8testfour9Testthree8Testfour
439 	out = "";
440 	if (count > 1) {
441 		out += 'Q';
442 		if (count > 10)
443 			out += '_';
444 		out << count;
445 		if (count > 10)
446 			out += '_';
447 	}
448 
449 	for (unsigned int i = 0; i < spacenames.size(); ++i) {
450 		out << (int)spacenames[i].length();
451 		out += spacenames[i].c_str();
452 	}
453 }
454 
455 
456 instantiation_func
457 FindFuncInImage(BString& funcName, image_id id, status_t& err)
458 {
459 	err = B_OK;
460 	instantiation_func theFunc = NULL;
461 
462 	// Don't need to do it this way ...
463 #if 0
464 	char foundFuncName[FUNC_NAME_LEN];
465 	int32 symbolType;
466 	int32 funcNameLen;
467 
468 	//	for each B_SYMBOL_TYPE_TEXT in image_id
469 	for (int32 i = 0; !err; ++i) {
470 		funcNameLen = FUNC_NAME_LEN;
471 		err = get_nth_image_symbol(id, i, foundFuncName, &funcNameLen,
472 								   &symbolType, (void**)&theFunc);
473 
474 		if (!err && symbolType == B_SYMBOL_TYPE_TEXT) {
475 			//	try to match Class::Instantiate(BMessage*) signature
476 			if (funcName.ICompare(foundFuncName, funcNameLen) == 0)
477 				break;
478 			else
479 				theFunc = NULL;
480 		}
481 	}
482 #endif
483 
484 	err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT,
485 						   (void**)&theFunc);
486 
487 	if (err) {
488 		// TODO: error handling
489 		theFunc = NULL;
490 	}
491 
492 	return theFunc;
493 }
494 
495 
496 bool
497 CheckSig(const char* sig, image_info& info)
498 {
499 	if (!sig) {
500 		// If it wasn't specified, anything "matches"
501 		return true;
502 	}
503 
504 	status_t err = B_OK;
505 
506 	// Get image signature
507 	BFile ImageFile(info.name, B_READ_ONLY);
508 	err = ImageFile.InitCheck();
509 	if (err) {
510 		// TODO: extended error handling
511 		return false;
512 	}
513 
514 	char imageSig[B_MIME_TYPE_LENGTH];
515 	BAppFileInfo AFI(&ImageFile);
516 	err = AFI.GetSignature(imageSig);
517 	if (err) {
518 		// TODO: extended error handling
519 		syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s",
520 			   info.name);
521 		return false;
522 	}
523 
524 	return strcmp(sig, imageSig) == 0;
525 }
526 
527