summaryrefslogtreecommitdiff
path: root/TAO/docs/implrepo.html
blob: fbbe67f1f46c99ad6dfe1de80e8725191ac57946 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
<!-- $Id$ -->
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>TAO Implementation Repository</title>
</head>

<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#CC0000">

<hr>

<h1>TAO Implementation Repository </h1>

<p>Revision 3.02 - July 10, 1998</p>

<hr>

<h2>Table of Contents</h2>

<ul>
  <li><a href="#Overview">Overview</a> <ul>
      <li><a href="#PersistentandTransientIORs">Persistent and Transient IORs</a> </li>
      <li><a href="#TheImplementationRepository">The Implementation Repository</a> </li>
    </ul>
  </li>
  <li><a href="#TAOsImplementationRepository">TAO's Implementation Repository</a> <ul>
      <li><a href="#VirtualServerName">Virtual Server Name</a> </li>
      <li><a href="#NewIORs">New IORs</a> <ul>
          <li><a href="#WhatwaswrongwiththeoldIOR">What was wrong with the old IOR?</a> </li>
          <li><a href="#WhydoesImplRepocontainanOBJKey">Why does the Implementation Repository profile
            contain an Object Key?</a> </li>
        </ul>
      </li>
      <li><a href="#POAExtensions">POA Extensions</a> </li>
      <li><a href="#PossibleFutureGoals">Possible Future Goals</a> </li>
      <li><a href="#ServerRestrictions">Server Restrictions</a> </li>
      <li><a href="#PreliminaryInterface">Preliminary Interface</a> </li>
    </ul>
  </li>
  <li><a href="#AlternateImplementations">Alternate Implementations</a> </li>
  <li><a href="#AccessingtheImplementationRepository">Accessing the Implementation Repository</a>
    <ul>
      <li><a href="#HelperApplication">Helper Application</a> </li>
      <li><a href="#LocatinganinstanceofImplRepo">Locating an instance of the Implementation
        Repository</a> <ul>
          <li><a href="#Serverside">Server Side</a> </li>
          <li><a href="#Clientside">Client Side</a> </li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#Howitworks">How It Works</a> <ul>
      <li><a href="#HowServerProducesPersistentIORdefault">How a server produces a Persistent IOR
        (in the default case)</a> </li>
      <li><a href="#HowServerProducesPersistentIORcomplex">How a server produces a Persistent IOR
        (in the complex case)</a> </li>
      <li><a href="#HowClientUsesPersistentIOR">How a client uses a Persistent IOR</a> </li>
    </ul>
  </li>
</ul>

<hr>

<h2><a name="Overview">Overview</a></h2>

<p>This document describes the proposed design of the TAO Implementation Repository
(previously known as the reactivator or activation service). </p>

<h3><a name="PersistentandTransientIORs">Persistent and Transient IORs</a></h3>

<p>CORBA defines two types of object references, persistent and transient. The difference
between the two is the lifetime of the reference in relation to the lifetime of the server
thread or process that created it. A transient object reference lifetime is tied to the
lifetime of its server. If the server stops or exits, the transient object reference no
longer exists. All references to this object should now be invalid, even if the server is
restarted. Persistent object references can outlive its server. The server can be stopped
and restarted without invalidating all object references to it. This enables the
implementation of features like load-on-demand servers and object migration.</p>

<p>Note that both persistent and transient object references can refer to objects on
persistent servers. A persistent server is a server that is always running. It can
generate transient references, persistent references, or even both. Users should be aware
that persistance of the object reference does not imply any persistance on the object
implementation state. Servant implementors should take the necessary measures to preserve
the state of their servants, using, for instance, databases or files; it is certainly
possible to provide persistent object references for objects whose state is not
persistent. </p>

<h3><a name="TheImplementationRepository">The Implementation Repository</a></h3>

<p>According to the CORBA specification, &quot;The Implementation Repository contains
information that allows the ORB to locate and activate implementations of objects&quot;
[CORBA Spec Rev. 2.2: 2.1.14] In earlier revisions of the specification, there was a
method <code>get_implementation()</code> in the CORBA Object interface. This has been
deprecated as of 2.2, leaving both the interface and implementation of the Implementation
Repository up to the ORB vendor.</p>

<p>A very good paper describing an implementation of the Implementation Repository is
&quot;<a href="http://www.cs.wustl.edu/~schmidt/michi.pdf">Binding, Migration, and
Scalability in CORBA</a>&quot; [Henning]. In it he states that the Implementation
Repository has three functions: 

<ol>
  <li>Maintain a registry of known servers</li>
  <li>Record which server is currently running (and which port and host)</li>
  <li>Starts servers on demand if they are registered for it</li>
</ol>

<p>The TAO implementation will be implemented much like how this paper describes. The next
section details our goals and plans for the implementation.</p>

<hr>

<h2><a name="TAOsImplementationRepository">TAO's Implementation Repository</a></h2>

<p>Here is an overview of our plans for the TAO'S Implementation Repository. 

<ul>
  <li>Use of the Implementation Repository will be optional. Real-time applications can choose
    not to use the Implementation Repository for performance reasons.</li>
  <li>Use of the Implementation Repository will be invisible to clients and servers for the
    common use-case. For more complicated behavior, programs can use Implementation Repository
    extensions of the POA.</li>
  <li>The Implementation will work with any CORBA client that supports LOCATION_FORWARD
    messages and multiple profiles in IORs, even if the client is not implemented using TAO.</li>
  <li>The Implementation Repository will keep track of all process that have registered with
    it by using a &quot;ping&quot; object located in the server. This ensures that multiple
    instances of the same server are not started.</li>
  <li>TAO will be fork safe. Because the Implementation Repository will need to use fork/exec
    (or CreateProcess on NT) to start servers, then the open connection to the client will
    need to be preserved over the server creation.</li>
<!-- COR: Maybe I didn't explain myself properly when we discussed -->
<!-- COR: this, I would expect that after fork all connections are -->
<!-- COR: closed on the server, otherwise we will end up leaking -->
<!-- COR: resources (file descriptors or handles) -->
<!-- DB: Then I'm not sure how are we supposed to return a LOCATION -->
<!-- DB: FORWARD if we have to close the connection? -->
  <li>TAO will exploit features of IIOP 1.1 to safely and efficiently verify if an IOR was
    generated by TAO itself on the client side. The server will still determine this through
    the object key, since that is all that is passed in a request.</li>
<!-- COR: As you point out below IIOP 1.1 is not needed, so why -->
<!-- COR: include it here? -->
<!-- COR: Anyway I would say: TAO will exploit features of IIOP 1.1 to -->
<!-- COR: safely and efficiently verify if an IOR was generated by TAO -->
<!-- COR: itself. -->
<!-- DB: Good point.  And also added a bit about how this only applies -->
<!-- DB: to the client side. -->
  <li>Multiple Profiles support. Our goal is to have the clients contact the object first, and
    if that fails, then contact the Implementation Repository. </li>
</ul>

<h3><a name="VirtualServerName">Virtual Server Name</a></h3>

<p>The Implementation Repository must keep track of which servers are started and stopped.
The question is at what resolution? It cannot contain an entry for every object, since
that would take up too much room. It could contain an entry for every server, and then
group objects there. But that is sort of inflexible for object migration, since objects
are tied to their executable. Instead, we want it to be something between object and
executable. In the <a href="http://www.cs.wustl.edu/~schmidt/michi.pdf">Henning</a> paper,
he mentions the use of a server name as the index for the table in the Implementation
Repository. </p>

<p>We will use a virtual server name for a group of objects. So the virtual server isn't
restricted to being tied to an executable or to an object. Since it isn't tied to an
executable, the virtual servers can be migrated to other servers.</p>

<h3><a name="NewIORs">New IORs</a></h3>

<p>IORs contain two sections:</p>

<table border="1">
  <tr>
    <td>Type ID</td>
    <td>Sequence of Tagged Profiles</td>
  </tr>
</table>

<p>Currently, TAO uses only one IIOP 1.0 Tagged Profile, and that is set up as the
following:</p>

<table border="1">
  <tr>
    <td>Version</td>
    <td>Host</td>
    <td>Port</td>
    <td>Object Key</td>
  </tr>
</table>

<table border="0">
  <tr>
    <td>Object Key: </td>
    <td><table border="1">
      <tr>
        <td>Transient/Persistent Flag</td>
        <td>TimeStamp</td>
        <td>POA ID</td>
        <td>OBJ ID</td>
      </tr>
    </table>
    </td>
  </tr>
</table>

<p>To accomodate the Implementation Repository and IIOP 1.1, the Profile was changed to
look like the following:</p>

<table border="1">
  <tr>
    <td>Version</td>
    <td>Host</td>
    <td>Port</td>
    <td>Object Key</td>
    <td>Components</td>
  </tr>
</table>

<table border="0">
  <tr>
    <td>Object Key: </td>
    <td><table border="1">
      <tr>
        <td>TAO</td>
        <td>TAO version</td>
        <td>TimeStamp/Server Name</td>
        <td>POA ID</td>
        <td>OBJ ID</td>
      </tr>
    </table>
    </td>
  </tr>
</table>

<p>Transient objects will have a TimeStamp and Persistent object have a server name.</p>

<p>For Persistent IORs, a second tagged profile will be added to point to the
Implementation Repository:</p>

<table border="1">
  <tr>
    <td>Version</td>
    <td>Host</td>
    <td>Port</td>
    <td>Object Key</td>
    <td>Components</td>
  </tr>
</table>

<table border="0">
  <tr>
    <td>Object Key: </td>
    <td><table border="1">
      <tr>
        <td>TAO</td>
        <td>TAO version</td>
        <td>Server Name</td>
        <td>POA ID</td>
        <td>OBJ ID (actually the OBJ Key of the Server)</td>
      </tr>
    </table>
    </td>
  </tr>
</table>

<p>If the profile was meant for just calls to the Implementation Repository, then the OBJ
ID would be that of the Implementation Repository Object.</p>

<h4><a name="WhatwaswrongwiththeoldIOR">What was wrong with the old IOR?</a></h4>

<p>We need a place to put a TAO marker in it so TAO servers can differentiate TAO IORs and
IORs from other vendors. In the old scheme, Persistent IORs had a null timestamp. To
support virtual servers, we need to use that spot to store the name (so the Implementation
Repository knows which server to start up).</p>

<h4><a name="WhydoesImplRepocontainanOBJKey">Why does the Implementation Repository
profile contain an Object Key?</a></h4>

<p>It needs to know what the object key of the object when forwarding. A server may
contain more than one </p>

<h3><a name="POAExtensions">POA Extensions</a></h3>

<p>POA will contain <code>create_reference_with_virtual_server[_and_id] (...)</code> that
can take in additional arguments for a virtual server name and a sequence of
Implementation Repository IORs. This will then communicate with each Implementation
Repository and get its generated profile back. Then all the profiles will be combined to
produce an Persistent IOR.</p>

<h3><a name="PossibleFutureGoals">Possible Future Goals</a></h3>

<p>Some things that may be implemented in TAO relating to the Implementation Repository
domain: 

<ul>
  <li>Optimization on TAO clients to recognize when a server is restarted, and change all
    other IORs which contain that server instead of going through the Implementation
    Repository</li>
  <li>Some sort of server security that checks the executable to make sure it is the correct
    executable (checksum, signatures, etc).</li>
  <li>Add the ability to put servers into DLLs or Shared Object files so the Implementation
    Repository can load it via those methods.</li>
  <li>GUI interface for such things as the helper application.</li>
  <li>Federations of Implementation Repositories.</li>
  <li>The ability to start a remote server (possibly with rsh, ssh, rexec, etc)</li>
</ul>
<!-- How are we planning to start a "remote" server? Isn't federations -->
<!-- of ImplRepo a solution for that? What are the drawbacks or -->
<!-- advantages of using rsh ssh, rexec or a similar NT service? -->
<!-- DB: Hmm, I reworded this, but where would a discussion of this -->
<!-- DB: be put? -->
<!-- COR: What about a "Future extensions" -->
<!-- COR: or "Things we will not implement on the first release" -->
<!-- DB: Added a note here. -->

<h3><a name="ServerRestrictions">Server Restrictions</a></h3>

<p>Servers that are restarted by the Implementation Repository must be able to recreate
enough state to deal with requests from a client. Unless dynamic servant and POA
activation is used, the restarted server must also recreate the POA in which the
Persistent object was registered and register the persistent object with that POA. </p>

<h3><a name="PreliminaryInterface">Preliminary Interface</a></h3>

<p>Here is a preliminary interface of the Implementation Repository in IDL: </p>

<pre>module TAO
{
  // ....

  exception Already_Registered {};
  // Object already bound in the Implementation Repository

  exception Cannot_Activate
  {
    string reason_;
  };

  exception Not_Found {};
  // Object not found in the Implementation Repository  
  
  struct Environment_Variable
  {
    string name_;
    string value_;
  };
  // One environment variable
  
  struct INET_Addr
  {
    unsigned short port_;
    unsigned long host_;
  };      
  // The location of a server

  typedef sequence&lt;Environment_Variable&gt; Environment;
  // Complete environment

  typedef sequence&lt;string&gt; Command_Line_Options;
  // Command line options

  struct Process_Options
  {
    string executable_name_;
    // Executable name

    Command_Line_Options command_line_options_; 
    // Command line options

    Environment environment_; 
    // Environment

    string working_directory_;  
    // Working directory

    unsigned long creation_flags_;
    // Creation flags
  };

  interface Ping_Object
  {
    void ping ();
    // Used for checking for liveness of a server.
  };

  interface Implementation_Repository
  {
    Object activate_object (in Object obj)
      raises (Not_Found,
              Cannot_Activate);
      // Restart server that will contain this persistent object and return the
      // new Object reference.
      //
      // The &lt;Not_Found&gt; exception is raised when &lt;obj&gt; is not found
      // in the Implementation Repository.  The &lt;Cannot_Activate&gt; exception
      // is raised when &lt;obj&gt; is found in the Repository but could not be
      // activated.

    INET_Addr activate_server (in string server)
      raises (Not_Found,
              Cannot_Activate);
      // Restart server that is named &lt;server&gt; and return the host/port
      // 
      //
      // The &lt;Not_Found&gt; exception is raised when &lt;server&gt; is not found
      // in the Implementation Repository.  The &lt;Cannot_Activate&gt; exception
      // is raised when &lt;server&gt; is found in the Repository but could not be
      // activated.

    void register_server (in string server,
                          in Process_Options options)
      raises (Already_Registered);
      // Restart server process when client is looking for &lt;server&gt;.
      //
      // The &lt;Already_Registered&gt; exception is raised when &lt;server&gt; has
      // already been registered with the Implementation Repository.
      // 
      // The &lt;Object_Not_Persistent&gt; exception is raised when &lt;server&gt; is
      // not a Persistent Object Reference.

    void reregister_server (in string server,
                            in Process_Options options)
      raises (Already_Registered);
      // Restart server process when client is looking for &lt;server&gt;.
      //
      // The &lt;Already_Registered&gt; exception is raised when &lt;server&gt; has
      // already been registered with the Implementation Repository.
      // 
      // The &lt;Object_Not_Persistent&gt; exception is raised when &lt;server&gt; is
      // not a Persistent Object Reference.

    void remove_server (in string server)
      raises (Not_Found);
      // Remove &lt;server&gt; from the Implementation Repository.
      //
      // The &lt;Not_Found&gt; exception is raised when &lt;server&gt; is not found
      // in the Implementation Repository.

    Profile server_is_running (in string server,
                               in INET_Addr addr, 
                               in Ping_Object ping);
      // Used to notify the Implementation Repository that &lt;server&gt; is alive and
      // well at &lt;addr&gt;.

    void server_is_shutting_down (in string server);
      // Used to tell the Implementation Repository that &lt;server&gt; is shutting
      // down.
    };
};</pre>

<hr>

<h2><a name="AlternateImplementations">Alternate Implementations</a></h2>

<p>Other ORB vendors use alternative techniques for Implementation Repository. These
techniques usually require new naming techniques for persistent objects and new
client-side APIs to bind to persistent objects. TAO's Implementation Repository will not
require such extensions. </p>

<p>Another implementation of an Implementation Repository uses an Object Reference that
points to the Implementation Repository instead of pointing directly to the persistent
object. This extra level of indirection is used by the Implementation Repository to start
the server (if needed), and then use the Location Forwarding mechanism to forward the
client request to the server. This technique forces clients to use the Implementation
Repository (at least once) even when the server is already running. </p>

<hr>

<h2><a name="AccessingtheImplementationRepository">Accessing the Implementation Repository</a>
</h2>

<p>The Implementation Repository will be transparent to the clients and the servers. </p>

<h3><a name="HelperApplication">Helper Application</a></h3>

<p>A helper application will be included with the Implementation Repository. It will be a
command-line utility that will assist users with adding and removing server records
(containing virtual server names and executable name/options) from the Implementation
Repository. </p>

<h3><a name="LocatinganinstanceofImplRepo">Locating an instance of the Implementation
Repository </a></h3>

<h4><a name="Serverside">Server side</a></h4>

<p>In the default case, the Implementation Repository will be found via the command-line,
environment variables, and multicast (in that order). Using the POA extensions, other
Implementation Repositories can be specified in the call to <code>POA::create_reference_with_virtual_server()</code>.
The default port of the Implementation Repository can be overridden through command line
options or environment variables. </p>

<h4><a name="Clientside">Client side</a></h4>

<p>One or more Implementation Repositories will be stored in additional profiles in the
IOR. Other Implementation Repositories can also be located by multicasting (on a default
multicast group) the server name of the Persistent Object the client is interested in. The
default multicast group and default port of the Implementation Repository can be
overridden through command line options or environment variables. </p>

<hr>

<h2><a name="Howitworks">How It Works</a></h2>

<h3><a name="HowServerProducesPersistentIORdefault">How a server produces a Persistent IOR
(in the default case)</a></h3>

<p>Before the server starts, it must be registered (via a command-line utility) with an
implementation repository that supports multicast. </p>
<!-- COR: is multicast support mandatory? OTOH it will be there -->
<!-- COR: always, right? -->
<!-- DB: I'm taking the default case to be that you wrote your -->
<!-- DB: before all this Impl Repo stuff came about.  So you -->
<!-- DB: won't have any environment variables or command line -->
<!-- DB: options that deal with the Impl Repo, so multicast is -->
<!-- DB: the only way we can do it without any work from the -->
<!-- DB: the user. -->

<ol>
  <li>Now the server will start up and call <code>ORB_init</code>. <code>ORB_init</code>, if
    not passed a server name, will take argv[0] and use that as a default server name (TAO
    expects this to be the executable name). </li>
  <li><code>ORB_init</code> will create a ping object.</li>
  <li><code>ORB_init</code> will look for Implementation Repositories on the command line,
    environmental variables, and then through multicast -- in that order. Once it finds one it
    registers itself and passes the ping object to the implementation repository with
    server_is_running () call.</li>
  <li>The profile returned by registration will be stored for later use.</li>
  <li>Client later can call <code>POA::create_reference</code> ().</li>
  <li>First, <code>create_reference</code> () will create the local profile.</li>
  <li>The stored Implementation Repository profile will have its object id changed to be the
    object key just created.</li>
  <li>Both profiles will be joined together in a multiple profile IOR and returned.</li>
</ol>

<h3><a name="HowServerProducesPersistentIORcomplex">How a server produces a Persistent IOR
(in complex cases)</a></h3>

<p>As in the default case, the server must be registered with an Implementation Repository
already, although it does not need to be multicast aware. 

<ol>
  <li><code>ORB_init</code> is called and does the default work (if it has Implementation
    Repositories to contact).</li>
  <li><code>POA::create_reference_with_virtual_server[_and_id]</code> () will be called with a
    server name and list of Implementation Repositories. </li>
  <li>The profile for the object is created.</li>
  <li>The ping object created in <code>ORB_init</code> and the object key is passed to the
    Implementation Repositories, and their profiles are returned.</li>
  <li>The full IOR is built and returned.</li>
</ol>

<h3><a name="HowClientUsesPersistentIOR">How a client uses a Persistent IOR</a></h3>

<p>For all Clients: 

<ul>
  <li>Client obtains a Persistent Object Reference, which contains multiple profiles to both
    regular objects and Implementation Repositories.</li>
  <li>It will now make a request on the first profile, which is the last known profile for the
    object.</li>
  <li>If this fails, then the next one will be tried. And if that fails, the next. One of the
    other profiles will be a reference to the Implementation Repository, in which it will
    either return NOT_FOUND or will start up the server and return a Location Forward message.</li>
</ul>

<p>If everything fails, then most clients will return failure for the request. TAO clients
will also have added functionality where other Implementation Repositories can be
specified on the command line, in environment variables, or found through multicast will
also be contacted. 

<ul>
  <li>If all of the profiles fail, then contact the other Implementation Repositories. First
    get those specified on the command line or in environment variables.</li>
  <li>Then, if multicast is available: <ul>
      <li>Multicast the Object Reference to a group of Implementation Repositories&nbsp; </li>
      <li>Wait until response or a timeout. The response will contain the Object Reference of a
        Implementation Repository that knows about the Object Reference </li>
    </ul>
  </li>
  <li>Now connect to any Implementation Repositories that have been found.</li>
  <li>Call <i>activate ()</i> passing the Persistent Object Reference.</li>
  <li>If a new Object Reference was sent back then retry the request using the it. If this
    request fails, then fail (no more retries).</li>
  <li>If a null reference was sent back, then fail.</li>
</ul>

<p>Another possible TAO clients will have an optimization where if there are several IORs
that have the same server name, and one of them gets forwarded, then the client will be
able to change its other IORs without going through the overhead of contacting
Implementation Repository.</p>

<hr>

<p>Back to the <a href="index.html">TAO documentation</a> page.</p>
<!--#include virtual="/~schmidt/cgi-sig.html" -->
</body>
</html>