1 // -*- Mode: C++; -*-
2 //                          Package   : omniNames
3 // log.cc                   Author    : Tristan Richardson (tjr)
4 //
5 //    Copyright (C) 2002-2013 Apasphere Ltd
6 //    Copyright (C) 1997-1999 AT&T Laboratories Cambridge
7 //
8 //  This file is part of omniNames.
9 //
10 //  omniNames is free software; you can redistribute it and/or modify
11 //  it under the terms of the GNU General Public License as published by
12 //  the Free Software Foundation; either version 2 of the License, or
13 //  (at your option) any later version.
14 //
15 //  This program is distributed in the hope that it will be useful,
16 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 //  GNU General Public License for more details.
19 //
20 //  You should have received a copy of the GNU General Public License
21 //  along with this program.  If not, see http://www.gnu.org/licenses/
22 //
23 
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <fcntl.h>
32 #if defined(__VMS) && __VMS_VER < 70000000
33 #  include <omniVMS/unlink.hxx>
34 #  include <omniVms/utsname.hxx>
35 #endif
36 #include <NamingContext_i.h>
37 #include <ObjectBinding.h>
38 #include <INSMapper.h>
39 #include <log.h>
40 
41 #ifdef HAVE_STD
42 #  include <iostream>
43 #  include <iomanip>
44    using namespace std;
45 #else
46 #  include <iostream.h>
47 #  include <iomanip.h>
48 #endif
49 
50 #ifdef __WIN32__
51 #  include <io.h>
52 #  include <winbase.h>
53 #  define stat(x,y) _stat(x,y)
54 #  define unlink(x) _unlink(x)
55 #else
56 #  include <unistd.h>
57 #  include <sys/utsname.h>
58 #endif
59 
60 #if defined(__nextstep__)
61 #  include <libc.h>
62 #  include <sys/param.h>
63 #endif
64 
65 #ifndef O_SYNC
66 #  ifdef  O_FSYNC              // FreeBSD 3.2 does not have O_SYNC???
67 #    define O_SYNC O_FSYNC
68 #  endif
69 #endif
70 
71 #ifdef HAVE_STD
72 #  define USE_STREAM_OPEN
73 #  define OPEN(name,mode,perm) open(name,mode)
74 #elif defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x500
75 #  define USE_STREAM_OPEN
76 #  define OPEN(name,mode,perm) open(name,mode,perm)
77 #elif defined(__DMC__)
78 #  define USE_STREAM_OPEN
79 #  define OPEN(name,mode,perm) open(name,mode,perm)
80 #elif defined(__ICC)
81 #  define USE_STREAM_OPEN
82 #  define OPEN(name,mode,perm) open(name,mode,perm)
83 #endif
84 
85 #ifndef HAVE_STRDUP
86 
87 // we have no strdup
88 static char *
strdup(char * str)89 strdup (char* str)
90 {
91   char *newstr;
92 
93   newstr = (char *) malloc (strlen (str) + 1);
94   if (newstr)
95     strcpy (newstr, str);
96   return newstr;
97 }
98 #endif  // not HAVE_STRDUP
99 
100 
101 static
reallyFlush(ofstream & f)102 inline void reallyFlush(ofstream& f) {
103     f.flush();
104 #ifdef needsExplicitFsync
105     ::fsync(f.rdbuf()->fd());
106 #endif
107 }
108 
109 extern void usage();
110 
111 
112 //
113 // Constructor for class omniNameslog.  There will normally be only one
114 // instance of this class.  Unfortunately the initialisation of the class
115 // cannot be completed in the constructor - the rest is done in the init()
116 // member function.  This is because the main program cannot give us
117 // pointers to the ORB and the BOA until it has worked out which port to
118 // listen on, but it normally finds out the port from the log file.  So in
119 // this constructor we initialise as much as we can, find out the port from
120 // the log file if there is one and return.  Then the main program
121 // initialises the ORB and the BOA and calls init().
122 //
123 
omniNameslog(int & p,const char * arg_logdir,int nohostname,int always)124 omniNameslog::omniNameslog(int& p, const char* arg_logdir,
125 			   int nohostname, int always)
126   : port(p)
127 {
128   startingUp = 1;
129   checkpointNeeded = 1;
130   line = 1;
131 
132 #ifdef __WIN32__
133   struct _stat sb;
134 #else
135   struct stat sb;
136 #endif
137 
138   CORBA::String_var logdir;
139 
140   if (!arg_logdir)
141     arg_logdir = getenv(DATADIR_ENV_VAR);
142 
143   if (!arg_logdir)
144     arg_logdir = getenv(LOGDIR_ENV_VAR);
145 
146   if (arg_logdir)
147     logdir = CORBA::string_dup(arg_logdir);
148   else
149     logdir = CORBA::string_dup(DEFAULT_LOGDIR);
150 
151   CORBA::String_var logname;
152 
153 #if !defined(__WIN32__) && !defined(__VMS)
154   if (logdir[strlen(logdir)-1] == '/') {
155     logdir[strlen(logdir)-1] = '\0';		// strip trailing '/'
156   }
157 #endif
158 
159   if (nohostname) {
160     logname = CORBA::string_alloc(strlen(logdir) + strlen("/omninames"));
161     sprintf(logname, "%s/omninames", (const char*)logdir);
162   }
163   else {
164 
165 #if !defined(__WIN32__) && !defined(__VMS)
166 #  ifdef HAVE_UNAME
167 
168     struct utsname un;
169     if (uname(&un) < 0) {
170       LOG(1, "Error: cannot get the name of this host.");
171       exit(1);
172     }
173 
174     logname = CORBA::string_alloc(strlen(logdir) + strlen("/omninames-") +
175 				  strlen(un.nodename));
176     sprintf(logname, "%s/omninames-%s", (const char*)logdir, un.nodename);
177 
178 #  elif HAVE_GETHOSTNAME
179 
180     // Apparently on some AIX versions, MAXHOSTNAMELEN is too small (32) to
181     // reflect the true size a hostname can be. Check and fix the value.
182 
183 #    ifndef MAXHOSTNAMELEN
184 #      define MAXHOSTNAMELEN 256
185 #    elif   MAXHOSTNAMELEN < 64
186 #      undef  MAXHOSTNAMELEN
187 #      define MAXHOSTNAMELEN 256
188 #    endif
189 
190     char hostname[MAXHOSTNAMELEN+1];
191 
192     if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
193       LOG(1, "Error: cannot get the name of this host.");
194       exit(1);
195     }
196     logname = CORBA::string_alloc(strlen(logdir) + strlen("/omninames-") +
197 				  strlen(hostname));
198     sprintf(logname, "%s/omninames-%s", (const char*)logdir, hostname);
199 
200 #  endif // HAVE_UNAME
201 
202 #elif defined(__WIN32__)
203 
204     // Get host name:
205 
206     DWORD machineName_buflen = MAX_COMPUTERNAME_LENGTH+1;
207     CORBA::String_var machineName = CORBA::string_alloc(machineName_buflen-1);
208     if (!GetComputerName((LPTSTR)(char*)machineName, &machineName_buflen)) {
209       LOG(1, "Error: cannot get the name of this host.");
210       exit(1);
211     }
212 
213     logname = CORBA::string_alloc(strlen(logdir) + strlen("\\omninames-") +
214 				  + strlen(machineName));
215     sprintf(logname, "%s\\omninames-%s",
216 	    (const char*)logdir, (const char*)machineName);
217 
218 #else // VMS
219     char last(logdir[strlen(logdir)-1]);
220     if (last != ':' && last != ']') {
221       LOG(1, "Error: " << LOGDIR_ENV_VAR << " (" << logdir <<
222           ") is not a directory name.");
223       exit(1);
224     }
225 
226     struct utsname un;
227     if (uname(&un) < 0) {
228       LOG(1, "Error: cannot get the name of this host.");
229       exit(1);
230     }
231 
232     logname = CORBA::string_alloc(strlen(logdir) + strlen("/omninames-") +
233 				  strlen(un.nodename));
234     sprintf(logname, "%somninames-%s", (const char*)logdir, un.nodename);
235 #endif
236   }
237 
238 #ifndef __VMS
239   active_new = CORBA::string_alloc(strlen(logname)+strlen(".dat"));
240   sprintf(active_new,"%s.dat", (const char*)logname);
241 
242   active_old = CORBA::string_alloc(strlen(logname)+strlen(".log"));
243   sprintf(active_old,"%s.log", (const char*)logname);
244 
245   backup = CORBA::string_alloc(strlen(logname)+strlen(".bak"));
246   sprintf(backup,"%s.bak", (const char*)logname);
247 
248   checkpt = CORBA::string_alloc(strlen(logname)+strlen(".ckp"));
249   sprintf(checkpt,"%s.ckp", (const char*)logname);
250 #else
251   // specify latest version:
252   active_new = CORBA::string_alloc(strlen(logname)+strlen(".dat;"));
253   sprintf(active_new,"%s.dat;", (const char*)logname);
254 
255   active_old = CORBA::string_alloc(strlen(logname)+strlen(".log;"));
256   sprintf(active_old,"%s.log;", (const char*)logname);
257 
258   backup = CORBA::string_alloc(strlen(logname)+strlen(".bak;"));
259   sprintf(backup,"%s.bak;", (const char*)logname);
260 
261   checkpt = CORBA::string_alloc(strlen(logname)+strlen(".ckp;"));
262   sprintf(checkpt,"%s.ckp;", (const char*)logname);
263 #endif
264 
265   if (stat(active_old,&sb) == 0) {
266     if (stat(active_new,&sb) == 0) {
267       LOG(1, "Data file '" << active_new << "' and old log file '" <<
268           active_old << "' both exist. Cannot start.");
269       exit(1);
270     }
271     LOG(1, "Using old data file name '" << active_old << "'.");
272     active = active_old;
273   }
274   else {
275     active = active_new;
276   }
277 
278   if (port == 0) {
279     firstTime = 0;
280   }
281   else {
282     if (always) {
283       if (stat(active,&sb) == 0) {
284 	// Log file exists -- not first time
285 	firstTime = 0;
286       }
287       else {
288 	firstTime = 1;
289       }
290     }
291     else {
292       firstTime = 1;
293     }
294   }
295   if (firstTime) {
296     //
297     // Starting for the first time - make sure log file doesn't exist, and
298     // for safety, that there is no backup file either.
299     //
300 
301     if (stat(active,&sb) == 0) {
302       LOG(1, "Error: data file '" << active <<
303           "' exists.  Can't use -start option.");
304       exit(1);
305     }
306     if (stat(backup,&sb) == 0) {
307       LOG(1, "Error: backup file '" << backup <<
308           "' exists.  Can't use -start option.");
309       exit(1);
310     }
311 
312   }
313   else {
314     //
315     // Restart - get port info from log file.
316     //
317 
318 #ifdef __WIN32__
319     ifstream initf(active,ios::in);
320 #else
321     ifstream initf(active);
322 #endif
323     if (!initf) {
324       LOG(1, "Error: cannot open data file '" << active << "': " <<
325           strerror(errno));
326 
327       if (stat(backup,&sb) == 0) {
328 	if (always) {
329 	  if (unlink(backup) == 0) {
330 	    LOG(1, "Info: backup file '" << backup << "' removed.");
331 	  }
332 	  else {
333 	    LOG(1, "Backup file '" << backup <<
334                 "' exists and cannot be removed.");
335 	    exit(1);
336 	  }
337 	}
338 	else {
339 	  LOG(1, "Backup file '" << backup <<
340               "' exists. Refusing to start. "
341               "Remove the backup file to start omniNames.");
342 	  exit(1);
343 	}
344       }
345       else {
346         exit(1);
347       }
348     }
349 
350     try {
351       getPort(initf);
352     }
353     catch (IOError&) {
354       LOG(1, "Error: reading data file '" << active << "' failed: " <<
355           strerror(errno));
356       initf.close();
357       exit(1);
358 
359     }
360     catch (ParseError&) {
361       LOG(1, "Error: parse error in data file '" << active << "' at line " <<
362           line << ".");
363       initf.close();
364       exit(1);
365     }
366 
367     p = port;
368 
369     initf.close();
370   }
371 }
372 
373 
374 void
init(CORBA::ORB_ptr the_orb,PortableServer::POA_ptr the_poa,PortableServer::POA_ptr the_ins_poa)375 omniNameslog::init(CORBA::ORB_ptr          the_orb,
376 		   PortableServer::POA_ptr the_poa,
377 		   PortableServer::POA_ptr the_ins_poa)
378 {
379   orb     = the_orb;
380   poa     = the_poa;
381   ins_poa = the_ins_poa;
382 
383 #ifdef __WIN32__
384   // This allows the path to contain multi-byte characters.
385   setlocale(LC_ALL, "");
386 #endif
387 
388   LOG(1, "Data file: '" << active << "'.");
389 
390   if (firstTime) {
391 
392     //
393     // starting for the first time - create an initial log file with the
394     // port specification and the root context.
395     //
396 
397     LOG(1, "Starting omniNames for the first time.");
398 
399     try {
400 #ifdef USE_STREAM_OPEN
401       logf.OPEN(active,ios::out|ios::trunc,0666);
402       if (!logf)
403 	throw IOError();
404 #else
405 #  ifdef __WIN32__
406       int fd = _open(active, O_WRONLY | O_CREAT | O_TRUNC, _S_IWRITE);
407 #  else
408       int fd = open(active, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0666);
409 #  endif
410       if (fd < 0)
411 	throw IOError();
412       logf.attach(fd);
413 #endif
414       putPort(port, logf);
415 
416       // Build persistent identifier
417       {
418 	CORBA::Object_var ref = the_poa->create_reference("");
419 	PortableServer::ObjectId_var pid = the_poa->reference_to_id(ref);
420 	persistentId = pid;
421 
422 	// POA's ids are 12 bytes, the last 4 incrementing with each
423 	// activated object. We use the first 8 as the persistent
424 	// identifier.
425 	persistentId.length(8);
426 	putPersistent(persistentId, logf);
427       }
428       reallyFlush(logf);
429 
430       {
431 	PortableServer::ObjectId_var refid =
432 	  PortableServer::string_to_ObjectId("NameService");
433 
434 	putCreate(refid, logf);
435       }
436 
437       logf.close();
438       if (!logf)
439 	throw IOError();
440 
441 // a bug in sparcworks C++ means that the fd doesn't get closed.
442 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
443       if (close(fd) < 0)
444 	throw IOError();
445 #endif
446 
447     }
448     catch (IOError& ex) {
449       LOG(1, "Error: cannot create initial data file '" << active << "': " <<
450           strerror(errno));
451       LOG(1, "You can set the environment variable " << DATADIR_ENV_VAR <<
452           " to specify the directory where the data files are kept.");
453       logf.close();
454       unlink(active);
455       exit(1);
456     }
457 
458     LOG(1, "Wrote initial data file '" << active << "'.");
459   }
460 
461   // claim the global lock as a writer (ie exclusive).
462 
463   NamingContext_i::lock.writerIn();
464 
465   ifstream initf(active);
466 
467   if (!initf) {
468     LOG(1, "Error: cannot open data file '" << active << "'.");
469     exit(1);
470   }
471 
472   try {
473     line = 1;
474 
475     while (initf && (initf.peek() != EOF)) {
476 
477       char* cmd;
478 
479       getNonfinalString(cmd, initf);
480 
481       if (strcmp(cmd, "port") == 0) {
482 	while (initf && (initf.get() != '\n'));	// ignore rest of line
483 	line++;
484       }
485       else if (strcmp(cmd, "persistent") == 0) {
486 	getPersistent(initf);
487       }
488       else if (strcmp(cmd, "create") == 0) {
489 	getCreate(initf);
490       }
491       else if (strcmp(cmd, "destroy") == 0) {
492 	getDestroy(initf);
493       }
494       else if (strcmp(cmd, "bind") == 0) {
495 	getBind(initf);
496       }
497       else if (strcmp(cmd, "unbind") == 0) {
498 	getUnbind(initf);
499       }
500       else {
501 	LOG(1, "Error: unknown command '" << cmd << "' in data file '" <<
502             active << "'.");
503 	throw ParseError();
504       }
505 
506       delete [] cmd;
507     }
508     initf.close();
509   }
510   catch (IOError&) {
511     LOG(1, "Error: reading data file '" << active << "' failed: " <<
512         strerror(errno));
513     initf.close();
514     exit(1);
515 
516   }
517   catch (ParseError&) {
518     LOG(1, "Error: parse error in data file '" << active << "' at line " <<
519         line << ".");
520     initf.close();
521   }
522 
523 
524   LOG(1, "Read data file '" << active << "' successfully.");
525 
526   CosNaming::NamingContext_ptr rootContext
527     = NamingContext_i::headContext->_this();
528 
529   {
530     // Check to see if we need an INS forwarding agent
531     omniIOR_var ior;
532     ior = rootContext->_getIOR();
533 
534     IIOP::ProfileBody iiop;
535     const IOP::TaggedProfileList& profiles = ior->iopProfiles();
536     for (CORBA::ULong index = 0; index < profiles.length(); index++) {
537       if (profiles[index].tag == IOP::TAG_INTERNET_IOP) {
538 	IIOP::unmarshalProfile(profiles[index],iiop);
539 	break;
540       }
541     }
542 
543     if (strncmp((const char*)iiop.object_key.get_buffer(),
544 		"NameService", 11)) {
545       LOG(1, "Pre-INS data file.");
546       new INSMapper(the_ins_poa, rootContext);
547     }
548   }
549 
550   CORBA::String_var p = orb->object_to_string(rootContext);
551   LOG(1, "Root context is " << p);
552 
553   // Now use the backdoor to tell the bootstrap agent in this
554   // address space to return this root context in response to
555   // CORBA::InitialReferences::get("NameService");
556   _omni_set_NameService(rootContext);
557 
558   CORBA::release(rootContext);	// dispose of the object reference
559 
560 #ifdef USE_STREAM_OPEN
561   logf.OPEN(active,ios::out|ios::app,0666);
562   if (!logf) {
563     LOG(1, "Error: cannot open data file '" << active << "' for writing.");
564     exit(1);
565   }
566 #else
567 #  ifdef __WIN32__
568   int fd = _open(active, O_WRONLY | O_APPEND);
569 #  else
570   int fd = open(active, O_WRONLY | O_APPEND | O_SYNC);
571 #  endif
572 
573   if (fd < 0) {
574     LOG(1, "Error: cannot open data file '" << active << "' for writing.");
575     exit(1);
576   }
577   logf.attach(fd);
578 #endif
579 
580   startingUp = 0;
581 
582   NamingContext_i::lock.writerOut();
583 
584   // remove checkpoint. This will protect us from trouble if the previous
585   // incarnation crashed during checkpointing and at the
586   // time when both active and checkpoint are linked to the same file.
587 
588   unlink(checkpt);
589 }
590 
591 
592 void
create(const PortableServer::ObjectId & id)593 omniNameslog::create(const PortableServer::ObjectId& id)
594 {
595   if (!startingUp) {
596     try {
597       putCreate(id, logf);
598       reallyFlush(logf);
599     }
600     catch (IOError& ex) {
601       LOG(1, "I/O error writing data file: " << strerror(errno));
602       logf.clear();
603       throw CORBA::PERSIST_STORE();
604     }
605     checkpointNeeded = 1;
606   }
607 }
608 
609 void
destroy(CosNaming::NamingContext_ptr nc)610 omniNameslog::destroy(CosNaming::NamingContext_ptr nc)
611 {
612   if (!startingUp) {
613     try {
614       putDestroy(nc, logf);
615       reallyFlush(logf);
616     }
617     catch (IOError& ex) {
618       LOG(1, "I/O error writing data file: " << strerror(errno));
619       logf.clear();
620       throw CORBA::PERSIST_STORE();
621     }
622     checkpointNeeded = 1;
623   }
624 }
625 
626 void
bind(CosNaming::NamingContext_ptr nc,const CosNaming::Name & n,CORBA::Object_ptr obj,CosNaming::BindingType t)627 omniNameslog::bind(CosNaming::NamingContext_ptr nc, const CosNaming::Name& n,
628                    CORBA::Object_ptr obj, CosNaming::BindingType t)
629 {
630   if (!startingUp) {
631     try {
632       putBind(nc, n, obj, t, logf);
633       reallyFlush(logf);
634     }
635     catch (IOError& ex) {
636       LOG(1, "I/O error writing data file: " << strerror(errno));
637       logf.clear();
638       throw CORBA::PERSIST_STORE();
639     }
640     checkpointNeeded = 1;
641   }
642 }
643 
644 void
unbind(CosNaming::NamingContext_ptr nc,const CosNaming::Name & n)645 omniNameslog::unbind(CosNaming::NamingContext_ptr nc, const CosNaming::Name& n)
646 {
647   if (!startingUp) {
648     try {
649       putUnbind(nc, n, logf);
650       reallyFlush(logf);
651     }
652     catch (IOError& ex) {
653       LOG(1, "I/O error writing data file: " << strerror(errno));
654       logf.clear();
655       throw CORBA::PERSIST_STORE();
656     }
657     checkpointNeeded = 1;
658   }
659 }
660 
661 
662 void
checkpoint()663 omniNameslog::checkpoint()
664 {
665   if (!checkpointNeeded) {
666     LOG(5, "No checkpoint needed.");
667     return;
668   }
669 
670   LOG(1, "Checkpointing Phase 1: Prepare.");
671 
672   //
673   // Get the global lock as a reader.  This means clients will still be able
674   // to do "resolve" and "list" operations, but anyone who tries to alter
675   // the state in any way will block until we've finished.
676   //
677 
678   NamingContext_i::lock.readerIn();
679 
680   ofstream ckpf;
681   int fd = -1;
682 
683   try {
684 
685 #ifdef USE_STREAM_OPEN
686     ckpf.OPEN(checkpt, ios::out|ios::trunc, 0666);
687     if (!ckpf) {
688       LOG(1, "Error: cannot open checkpoint file '" << checkpt <<
689           "' for writing.");
690       throw IOError();
691     }
692 #else
693 #  ifdef __WIN32__
694     fd = _open(checkpt, O_WRONLY | O_CREAT | O_TRUNC, _S_IWRITE);
695 #  else
696     fd = open(checkpt, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0666);
697 #  endif
698 
699     if (fd < 0) {
700       LOG(1, "Error: cannot open checkpoint file '" << checkpt <<
701           "' for writing.");
702       throw IOError();
703     }
704 
705     ckpf.attach(fd);
706 #endif
707     putPort(port, ckpf);
708 
709     if (persistentId.length())
710       putPersistent(persistentId, ckpf);
711 
712     NamingContext_i* nci;
713 
714     for (nci = NamingContext_i::headContext; nci; nci = nci->next) {
715       PortableServer::ObjectId_var id = nci->PR_id();
716       putCreate(id, ckpf);
717     }
718 
719     for (nci = NamingContext_i::headContext; nci; nci = nci->next) {
720       for (ObjectBinding* ob = nci->headBinding; ob; ob = ob->next) {
721 	putBind(nci->_this(), ob->binding.binding_name, ob->object,
722 		ob->binding.binding_type, ckpf);
723       }
724     }
725 
726     ckpf.close();
727     if (!ckpf)
728       throw IOError();
729 
730 // a bug in sparcworks C++ means that the fd doesn't get closed.
731 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
732     if (close(fd) < 0)
733       throw IOError();
734 #endif
735 
736   }
737   catch (IOError& ex) {
738     LOG(1, "I/O error writing checkpoint file: " << strerror(errno));
739     LOG(1, "Abandoning checkpoint");
740     ckpf.close();
741 
742 // a bug in sparcworks C++ means that the fd doesn't get closed.
743 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
744     close(fd);
745 #endif
746     NamingContext_i::lock.readerOut();
747     unlink(checkpt);
748     return;
749   }
750 
751 
752   //
753   // Now commit the checkpoint to become the active log.
754   //
755 
756   LOG(1, "Checkpointing Phase 2: Commit.");
757 
758 // a bug in sparcworks C++ means that the fd doesn't get closed.
759 #if defined(__sunos__) && defined(__SUNPRO_CC) && __SUNPRO_CC < 0x500
760   close(logf.rdbuf()->fd());
761 #endif
762 
763   logf.close();
764 
765   unlink(backup);
766 
767 #if defined(__WIN32__)
768   if (!CopyFile(active,backup,TRUE)) {
769 #elif defined(__VMS)
770   if (rename(active, backup) < 0) {
771 #else
772   if (link(active,backup) < 0) {
773 #endif
774     // Failure here leaves old active and checkpoint file.
775     LOG(1, "Error: failed to link backup file '" << backup <<
776         "' to old data file '" << active << "'.");
777     exit(1);
778   }
779 
780 #ifndef __VMS
781   if (unlink(active) < 0) {
782     // Failure here leaves active and backup pointing to the same (old) file.
783     LOG(1, "Error: failed to unlink old data file '" << active << "'.");
784     exit(1);
785   }
786 #endif
787 
788   active = active_new;
789 
790 #if defined(__WIN32__)
791   if (!CopyFile(checkpt,active,TRUE)) {
792 #elif defined(__VMS)
793   if (rename(checkpt,active) < 0) {
794 #else
795   if (link(checkpt,active) < 0) {
796 #endif
797     // Failure here leaves no active but backup points to the old file.
798     LOG(1, "Error: failed to link data file '" << active <<
799         "' to checkpoint file '" << checkpt << "'.");
800     exit(1);
801   }
802 
803 #ifndef __VMS
804   if (unlink(checkpt) < 0) {
805     // Failure here leaves active and checkpoint pointing to the same file.
806     LOG(1, "Error: failed to unlink checkpoint file '" << checkpt << "'.");
807     exit(1);
808   }
809 #endif
810 
811 #ifdef USE_STREAM_OPEN
812   logf.OPEN(active,ios::out|ios::app,0666);
813   if (!logf) {
814     LOG(1, "Error: cannot open data file '" << active << "' for writing.");
815     exit(1);
816   }
817 #else
818 #  ifdef __WIN32__
819   fd = _open(active, O_WRONLY | O_APPEND);
820 #  else
821   fd = open(active, O_WRONLY | O_APPEND | O_SYNC);
822 #  endif
823 
824   if (fd < 0) {
825     LOG(1, "Error: cannot open new data file '" << active << "' for writing.");
826     exit(1);
827   }
828   logf.attach(fd);
829 #endif
830 
831   NamingContext_i::lock.readerOut();
832 
833   LOG(1, "Checkpointing completed.");
834   checkpointNeeded = 0;
835 }
836 
837 
838 void
839 omniNameslog::putPort(int p, ostream& file)
840 {
841   file << "port " << p << '\n';
842   if (!file) throw IOError();
843 }
844 
845 
846 //
847 // getPort is different from the other get... functions in that the "command"
848 // ("port") hasn't been read yet, and also we cannot use any CORBA stuff
849 // since ORB_init may not have been called yet.
850 //
851 
852 void
853 omniNameslog::getPort(istream& file)
854 {
855   char* str;
856 
857   getNonfinalString(str, file);
858 
859   if (strcmp(str, "port") != 0) {
860     LOG(1, "Error: data file doesn't start with \"port\".");
861     throw ParseError();
862   }
863 
864   delete [] str;
865 
866   getFinalString(str, file);
867 
868   port = atoi(str);
869 
870   delete [] str;
871 
872   if (port == 0) {
873     LOG(1, "Error: invalid port specified in data file.");
874     throw ParseError();
875   }
876 }
877 
878 
879 void
880 omniNameslog::putPersistent(const PortableServer::ObjectId& id, ostream& file)
881 {
882   file << "persistent ";
883   putKey(id, file);
884   file << '\n';
885   if (!file) throw IOError();
886 }
887 
888 
889 void
890 omniNameslog::getPersistent(istream& file)
891 {
892   getKey(persistentId, file);
893   omniORB::setPersistentServerIdentifier(persistentId);
894 }
895 
896 
897 void
898 omniNameslog::putCreate(const PortableServer::ObjectId& id, ostream& file)
899 {
900   file << "create ";
901   putKey(id, file);
902   file << '\n';
903   if (!file) throw IOError();
904 }
905 
906 
907 void
908 omniNameslog::getCreate(istream& file)
909 {
910   //
911   // Argument to "create" is the object key of the naming context.
912   //
913 
914   PortableServer::ObjectId id;
915   NamingContext_i*         rc;
916 
917   getKey(id, file);
918 
919   if (id.length() == 12) // SYS_ASSIGNED_ID_SIZE + TRANSIENT_SUFFIX_SIZE
920     rc = new NamingContext_i(poa, id, this);
921   else
922     rc = new NamingContext_i(ins_poa, id, this);
923 
924   rc->_remove_ref();
925 }
926 
927 
928 void
929 omniNameslog::putDestroy(CosNaming::NamingContext_ptr nc, ostream& file)
930 {
931   file << "destroy ";
932   CORBA::String_var s = orb->object_to_string(nc);
933   putString(s, file);
934   file << '\n';
935   if (!file) throw IOError();
936 }
937 
938 
939 void
940 omniNameslog::getDestroy(istream& file)
941 {
942   //
943   // Argument to "destroy" is NamingContext IOR.
944   //
945 
946   char* str;
947   getFinalString(str, file);
948   CORBA::Object_var o;
949   try {
950     o = orb->string_to_object(str);
951   }
952   catch (...) {
953     LOG(1, "getDestroy: invalid IOR.");
954     delete [] str;
955     throw ParseError();
956   }
957 
958   delete [] str;
959 
960   CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(o);
961   if (CORBA::is_nil(nc)) {
962     LOG(1, "getDestroy: IOR not a NamingContext.");
963     throw ParseError();
964   }
965   nc->destroy();
966 }
967 
968 
969 void
970 omniNameslog::putBind(CosNaming::NamingContext_ptr nc, const CosNaming::Name& n,
971 	     CORBA::Object_ptr obj, CosNaming::BindingType t, ostream& file)
972 {
973   file << "bind ";
974   CORBA::String_var s = orb->object_to_string(nc);
975   putString(s, file);
976   file << ' ';
977   putString(n[0].id, file);
978   file << ' ';
979   putString(n[0].kind, file);
980   if (t == CosNaming::nobject)
981     file << " nobject ";
982   else
983     file << " ncontext ";
984   s = orb->object_to_string(obj);
985   putString(s, file);
986   file << '\n';
987   if (!file) throw IOError();
988 }
989 
990 
991 void
992 omniNameslog::getBind(istream& file)
993 {
994   //
995   // First arg is NamingContext IOR.
996   //
997 
998   char* str;
999   getNonfinalString(str, file);
1000   CORBA::Object_var o;
1001   try {
1002     o = orb->string_to_object(str);
1003   }
1004   catch (...) {
1005     LOG(1, "getBind: invalid IOR.");
1006     delete [] str;
1007     throw ParseError();
1008   }
1009   delete [] str;
1010 
1011   CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(o);
1012   if (CORBA::is_nil(nc)) {
1013     LOG(1, "getBind: IOR not a NamingContext.");
1014     throw ParseError();
1015   }
1016 
1017   //
1018   // 2nd & 3rd args are name id and kind.
1019   //
1020 
1021   CosNaming::Name name(1);
1022   name.length(1);
1023 
1024   getNonfinalString(str, file);
1025   name[0].id = str;
1026 
1027   getNonfinalString(str, file);
1028   name[0].kind = str;
1029 
1030   //
1031   // 4th arg is binding type.
1032   //
1033 
1034   char* bindingType;
1035   getNonfinalString(bindingType, file);
1036 
1037   //
1038   // 5th arg is object IOR.
1039   //
1040 
1041   getFinalString(str, file);
1042   try {
1043     o = orb->string_to_object(str);
1044   }
1045   catch (...) {
1046     LOG(1, "getDestroy: invalid IOR.");
1047     delete [] str;
1048     throw ParseError();
1049   }
1050   delete [] str;
1051 
1052   if (strcmp(bindingType, "ncontext") == 0) {
1053 
1054     CosNaming::NamingContext_var nc2
1055       = CosNaming::NamingContext::_narrow(o);
1056     if (CORBA::is_nil(nc2)) {
1057       LOG(1, "bind: IOR not a NamingContext.");
1058       throw ParseError();
1059     }
1060     nc->rebind_context(name, nc2);
1061   }
1062   else {
1063     nc->rebind(name, o);
1064   }
1065 
1066   delete [] bindingType;
1067 }
1068 
1069 
1070 
1071 void
1072 omniNameslog::putUnbind(CosNaming::NamingContext_ptr nc, const CosNaming::Name& n,
1073 	       ostream& file)
1074 {
1075   file << "unbind ";
1076   CORBA::String_var s = orb->object_to_string(nc);
1077   putString(s, file);
1078   file << ' ';
1079   putString(n[0].id, file);
1080   file << ' ';
1081   putString(n[0].kind, file);
1082   file << '\n';
1083   if (!file) throw IOError();
1084 }
1085 
1086 
1087 void
1088 omniNameslog::getUnbind(istream& file)
1089 {
1090   //
1091   // First arg is NamingContext IOR.
1092   //
1093 
1094   char* str;
1095   getNonfinalString(str, file);
1096   CORBA::Object_var o;
1097   try {
1098     o = orb->string_to_object(str);
1099   }
1100   catch (...) {
1101     LOG(1, "getUnbind: invalid IOR.");
1102     delete [] str;
1103     throw ParseError();
1104   }
1105   delete [] str;
1106 
1107   CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(o);
1108   if (CORBA::is_nil(nc)) {
1109     LOG(1, "getUnbind: IOR not a NamingContext.");
1110     throw ParseError();
1111   }
1112 
1113   //
1114   // 2nd & 3rd args are name id and kind.
1115   //
1116 
1117   CosNaming::Name name(1);
1118   name.length(1);
1119 
1120   getNonfinalString(str, file);
1121   name[0].id = str;
1122 
1123   getFinalString(str, file);
1124   name[0].kind = str;
1125 
1126   nc->unbind(name);
1127 }
1128 
1129 
1130 void
1131 omniNameslog::putKey(const PortableServer::ObjectId& id, ostream& file)
1132 {
1133   file << hex;
1134   for (unsigned int i = 0; i < id.length(); i++) {
1135     file << setfill('0') << setw(2) << (int)id[i];
1136   }
1137   file << dec;
1138 }
1139 
1140 
1141 void
1142 omniNameslog::getKey(PortableServer::ObjectId& id, istream& file)
1143 {
1144   char* str;
1145   getFinalString(str, file);
1146 
1147   int l = strlen(str) / 2;
1148   id.length(l);
1149   char* p = str;
1150   for (int i = 0; i < l; i++) {
1151     int n;
1152     sscanf(p,"%02x",&n);
1153     id[i] = n;
1154     p += 2;
1155   }
1156   delete [] str;
1157 }
1158 
1159 
1160 void
1161 omniNameslog::putString(const char* str, ostream& file)
1162 {
1163   for (int i = strlen(str); i > 0; i--, str++) {
1164     switch (*str) {
1165       case '\\':
1166 	file.put('\\');
1167 	file.put('\\');
1168 	break;
1169       case ' ':
1170 	file.put('\\');
1171 	file.put(' ');
1172 	break;
1173       case '\n':
1174 	file.put('\\');
1175 	file.put('n');
1176 	break;
1177       case '\t':
1178 	file.put('\\');
1179 	file.put('t');
1180 	break;
1181       case '\r':
1182 	file.put('\\');
1183 	file.put('r');
1184 	break;
1185       default:
1186 	file.put(*str);
1187 	break;
1188     }
1189   }
1190 }
1191 
1192 
1193 void
1194 omniNameslog::getFinalString(char*& buf, istream& file)
1195 {
1196   if (getString(buf, file) != '\n')
1197     throw ParseError();
1198   line++;
1199 }
1200 
1201 void
1202 omniNameslog::getNonfinalString(char*& buf, istream& file)
1203 {
1204   if (getString(buf, file) != ' ')
1205     throw ParseError();
1206 }
1207 
1208 int
1209 omniNameslog::getString(char*& buf, istream& file)
1210 {
1211   int bufsz = 512;
1212   buf = new char[bufsz];
1213 
1214   char* p = buf;
1215 
1216   int backslash = 0;
1217 
1218   while (1) {
1219 
1220     char c;
1221 
1222     if (!file.get(c)) {	// I/O problem or EOF
1223       delete [] buf;
1224       if (!file.eof())
1225 	throw IOError();
1226       throw ParseError();
1227     }
1228 
1229     if (backslash) {
1230 
1231       backslash = 0;
1232 
1233       switch (c) {
1234       case '\\':
1235 	*p++ = '\\';
1236 	break;
1237       case ' ':
1238 	*p++ = ' ';
1239 	break;
1240       case 'n':
1241 	*p++ = '\n';
1242 	break;
1243       case 't':
1244 	*p++ = '\t';
1245 	break;
1246       case 'r':
1247 	*p++ = '\r';
1248 	break;
1249       default:
1250         LOG(1, "Unknown character following '\\' in data file.");
1251       }
1252     }
1253     else {
1254       switch (c) {
1255       case '\\':
1256 	backslash = 1;
1257 	break;
1258       case ' ':
1259       case '\n':
1260 	*p = '\0';
1261 	return (int)c;
1262 	break;
1263       default:
1264 	*p++ = c;
1265       }
1266     }
1267 
1268     if (p == (buf + bufsz)) {
1269 
1270       // buffer is too small
1271 
1272       char *obuf = buf;
1273       buf = new char[bufsz+bufsz];
1274       memcpy(buf, obuf, bufsz);
1275       delete [] obuf;
1276       p = buf + bufsz;
1277       bufsz += bufsz;
1278     }
1279   }
1280 }
1281