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