1 /* $Id: ns_db_dump.cpp 551762 2017-11-22 14:16:14Z satskyse $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Sergey Satskiy
27 *
28 * File Description:
29 * NetSchedule database dumping support.
30 *
31 */
32
33 #include <ncbi_pch.hpp>
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include "ns_db_dump.hpp"
38 #include "ns_types.hpp"
39 #include "netschedule_version.hpp"
40
41
42 BEGIN_NCBI_SCOPE
43
SCommonDumpHeader()44 SCommonDumpHeader::SCommonDumpHeader() :
45 magic(kDumpMagic),
46 storage_ver_major(NETSCHEDULED_STORAGE_VERSION_MAJOR),
47 storage_ver_minor(NETSCHEDULED_STORAGE_VERSION_MINOR),
48 storage_ver_patch(NETSCHEDULED_STORAGE_VERSION_PATCH)
49 {
50 CNcbiApplication * app = CNcbiApplication::Instance();
51 CVersionInfo ver_info = app->GetVersion();
52
53 server_ver_major = ver_info.GetMajor();
54 server_ver_minor = ver_info.GetMinor();
55 server_ver_patch = ver_info.GetPatchLevel();
56 }
57
58
SJobDumpHeader()59 SJobDumpHeader::SJobDumpHeader() :
60 common_header(),
61 job_props_fixed_size(sizeof(SJobDump)),
62 job_io_fixed_size(sizeof(SJobIODump)),
63 job_event_fixed_size(sizeof(SJobEventsDump))
64 {}
65
66
Write(FILE * f)67 void SJobDumpHeader::Write(FILE * f)
68 {
69 errno = 0;
70 if (fwrite(this, sizeof(SJobDumpHeader), 1, f) != 1)
71 throw runtime_error(strerror(errno));
72 }
73
74
Read(FILE * f)75 int SJobDumpHeader::Read(FILE * f)
76 {
77 size_t bytes = fread(this, 1, sizeof(SJobDumpHeader), f);
78 if (bytes != sizeof(SJobDumpHeader)) {
79 if (bytes > 0)
80 throw runtime_error("Incomplete dump file header");
81 if (feof(f))
82 return 1;
83 if (errno != 0)
84 throw runtime_error(strerror(errno));
85 throw runtime_error("Unknown dump file header reading error");
86 }
87
88 if (common_header.magic != kDumpMagic)
89 throw runtime_error("Dump file header magic does not match");
90 return 0;
91 }
92
93
SOneStructDumpHeader()94 SOneStructDumpHeader::SOneStructDumpHeader() :
95 common_header(),
96 fixed_size(0)
97 {}
98
99
Write(FILE * f)100 void SOneStructDumpHeader::Write(FILE * f)
101 {
102 errno = 0;
103 if (fwrite(this, sizeof(SOneStructDumpHeader), 1, f) != 1)
104 throw runtime_error(strerror(errno));
105 }
106
107
Read(FILE * f)108 int SOneStructDumpHeader::Read(FILE * f)
109 {
110 size_t bytes = fread(this, 1, sizeof(SOneStructDumpHeader), f);
111 if (bytes != sizeof(SOneStructDumpHeader)) {
112 if (bytes > 0)
113 throw runtime_error("Incomplete dump file header");
114 if (feof(f))
115 return 1;
116 if (errno != 0)
117 throw runtime_error(strerror(errno));
118 throw runtime_error("Unknown dump file header reading error");
119 }
120
121 if (common_header.magic != kDumpMagic)
122 throw runtime_error("Dump file header magic does not match");
123 return 0;
124 }
125
126
SJobDump()127 SJobDump::SJobDump()
128 {
129 memset(this, 0, sizeof(SJobDump));
130 }
131
Write(FILE * f,const char * progress_msg)132 void SJobDump::Write(FILE * f, const char * progress_msg)
133 {
134 total_size = sizeof(SJobDump) + progress_msg_size;
135
136 errno = 0;
137 if (fwrite(this, sizeof(SJobDump), 1, f) != 1)
138 throw runtime_error(strerror(errno));
139
140 if (progress_msg_size > 0) {
141 errno = 0;
142 if (fwrite(progress_msg, progress_msg_size, 1, f) != 1)
143 throw runtime_error(strerror(errno));
144 }
145 }
146
Read(FILE * f,size_t fixed_size_from_header,char * progress_msg)147 int SJobDump::Read(FILE * f,
148 size_t fixed_size_from_header,
149 char * progress_msg)
150 {
151 memset(this, 0, sizeof(SJobDump));
152
153 size_t size_to_read = min(sizeof(SJobDump), fixed_size_from_header);
154
155 errno = 0;
156 size_t bytes = fread(this, 1, size_to_read, f);
157 if (bytes != size_to_read) {
158 if (bytes > 0)
159 throw runtime_error("Incomplete job record reading");
160 if (feof(f))
161 return 1;
162 if (errno != 0)
163 throw runtime_error(strerror(errno));
164 throw runtime_error("Unknown job record reading error");
165 }
166 if (fixed_size_from_header > size_to_read) {
167 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
168
169 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
170 if (errno != 0)
171 throw runtime_error(strerror(errno));
172 throw runtime_error("Unknown job record skipping error");
173 }
174 }
175
176 if (client_ip_size > kMaxClientIpSize)
177 throw runtime_error("Job client ip size is more "
178 "than max allowed");
179 if (client_sid_size > kMaxSessionIdSize)
180 throw runtime_error("Job client session id size is more "
181 "than max allowed");
182 if (ncbi_phid_size > kMaxHitIdSize)
183 throw runtime_error("Job page hit id size is more "
184 "than max allowed");
185 if (progress_msg_size > kNetScheduleMaxDBDataSize)
186 throw runtime_error("Job progress message size is more "
187 "than max allowed");
188 if (progress_msg_size > 0) {
189 errno = 0;
190 bytes = fread(progress_msg, 1, progress_msg_size, f);
191 if (bytes != progress_msg_size) {
192 if (bytes > 0)
193 throw runtime_error("Incomplete job progress message reading");
194 if (feof(f))
195 throw runtime_error("Unexpected end of job info file. "
196 "Expected a progress message, got EOF");
197 if (errno != 0)
198 throw runtime_error(strerror(errno));
199 throw runtime_error("Unknown job progress message reading error");
200 }
201 }
202
203 if (total_size > fixed_size_from_header + progress_msg_size) {
204 size_t bytes_to_skip = total_size -
205 fixed_size_from_header - progress_msg_size;
206 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
207 if (errno != 0)
208 throw runtime_error(strerror(errno));
209 throw runtime_error("Unknown job record skipping error");
210 }
211 }
212
213 return 0;
214 }
215
216
SJobIODump()217 SJobIODump::SJobIODump()
218 {
219 memset(this, 0, sizeof(SJobIODump));
220 }
221
222
223 // It is supposed that the input and/or output follow each instance of the
224 // structure below. The limit of the input/output is too large to have
225 // it as a member of this structure
Write(FILE * f,const char * input,const char * output)226 void SJobIODump::Write(FILE * f, const char * input,
227 const char * output)
228 {
229 total_size = sizeof(SJobIODump) + input_size + output_size;
230
231 errno = 0;
232 if (fwrite(this, sizeof(SJobIODump), 1, f) != 1)
233 throw runtime_error(strerror(errno));
234
235 if (input_size > 0) {
236 errno = 0;
237 if (fwrite(input, input_size, 1, f) != 1)
238 throw runtime_error(strerror(errno));
239 }
240
241 if (output_size > 0) {
242 errno = 0;
243 if (fwrite(output, output_size, 1, f) != 1)
244 throw runtime_error(strerror(errno));
245 }
246 }
247
248
Read(FILE * f,size_t fixed_size_from_header,char * input,char * output)249 int SJobIODump::Read(FILE * f, size_t fixed_size_from_header,
250 char * input, char * output)
251 {
252 memset(this, 0, sizeof(SJobIODump));
253
254 size_t size_to_read = min(sizeof(SJobIODump), fixed_size_from_header);
255
256 errno = 0;
257 size_t bytes = fread(this, 1, size_to_read, f);
258 if (bytes != size_to_read) {
259 if (bytes > 0)
260 throw runtime_error("Incomplete job i/o record reading");
261 if (feof(f))
262 throw runtime_error("Unexpected end of job info file. "
263 "Expected a job i/o, got EOF");
264 if (errno != 0)
265 throw runtime_error(strerror(errno));
266 throw runtime_error("Unknown job i/o reading error");
267 }
268 if (fixed_size_from_header > size_to_read) {
269 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
270
271 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
272 if (errno != 0)
273 throw runtime_error(strerror(errno));
274 throw runtime_error("Unknown job i/o record skipping error");
275 }
276 }
277
278 if (input_size > kNetScheduleMaxOverflowSize)
279 throw runtime_error("Job input size is more "
280 "than max allowed");
281 if (input_size > 0) {
282 errno = 0;
283 bytes = fread(input, 1, input_size, f);
284 if (bytes != input_size) {
285 if (bytes > 0)
286 throw runtime_error("Incomplete job input reading");
287 if (feof(f))
288 throw runtime_error("Unexpected end of job info file. "
289 "Expected an input, got EOF");
290 if (errno != 0)
291 throw runtime_error(strerror(errno));
292 throw runtime_error("Unknown job input reading error");
293 }
294 }
295
296 if (output_size > kNetScheduleMaxOverflowSize)
297 throw runtime_error("Job output size is more "
298 "than max allowed");
299 if (output_size > 0) {
300 errno = 0;
301 bytes = fread(output, 1, output_size, f);
302 if (bytes != output_size) {
303 if (bytes > 0)
304 throw runtime_error("Incomplete job output reading");
305 if (feof(f))
306 throw runtime_error("Unexpected end of job info file. "
307 "Expected an output, got EOF");
308 if (errno != 0)
309 throw runtime_error(strerror(errno));
310 throw runtime_error("Unknown job output reading error");
311 }
312 }
313
314 if (total_size > fixed_size_from_header + input_size + output_size) {
315 size_t bytes_to_skip = total_size -
316 fixed_size_from_header -
317 input_size - output_size;
318 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
319 if (errno != 0)
320 throw runtime_error(strerror(errno));
321 throw runtime_error("Unknown job i/o record skipping error");
322 }
323 }
324
325 return 0;
326 }
327
328
SJobEventsDump()329 SJobEventsDump::SJobEventsDump()
330 {
331 memset(this, 0, sizeof(SJobEventsDump));
332 }
333
Write(FILE * f,const char * client_node,const char * client_session,const char * err_msg)334 void SJobEventsDump::Write(FILE * f, const char * client_node,
335 const char * client_session,
336 const char * err_msg)
337 {
338 total_size = sizeof(SJobEventsDump) + client_node_size +
339 client_session_size + err_msg_size;
340
341 errno = 0;
342 if (fwrite(this, sizeof(SJobEventsDump), 1, f) != 1)
343 throw runtime_error(strerror(errno));
344
345 if (client_node_size > 0) {
346 errno = 0;
347 if (fwrite(client_node, client_node_size, 1, f) != 1)
348 throw runtime_error(strerror(errno));
349 }
350 if (client_session_size > 0) {
351 errno = 0;
352 if (fwrite(client_session, client_session_size, 1, f) != 1)
353 throw runtime_error(strerror(errno));
354 }
355 if (err_msg_size > 0) {
356 errno = 0;
357 if (fwrite(err_msg, err_msg_size, 1, f) != 1)
358 throw runtime_error(strerror(errno));
359 }
360 }
361
Read(FILE * f,size_t fixed_size_from_header,char * client_node,char * client_session,char * err_msg)362 int SJobEventsDump::Read(FILE * f, size_t fixed_size_from_header,
363 char * client_node,
364 char * client_session,
365 char * err_msg)
366 {
367 memset(this, 0, sizeof(SJobEventsDump));
368
369 size_t size_to_read = min(sizeof(SJobEventsDump),
370 fixed_size_from_header);
371
372 errno = 0;
373 size_t bytes = fread(this, 1, size_to_read, f);
374 if (bytes != size_to_read) {
375 if (bytes > 0)
376 throw runtime_error("Incomplete job event reading");
377 if (feof(f))
378 throw runtime_error("Unexpected end of job info file. "
379 "Expected a job event, got EOF");
380 if (errno != 0)
381 throw runtime_error(strerror(errno));
382 throw runtime_error("Unknown job event reading error");
383 }
384 if (fixed_size_from_header > size_to_read) {
385 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
386
387 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
388 if (errno != 0)
389 throw runtime_error(strerror(errno));
390 throw runtime_error("Unknown job event record skipping error");
391 }
392 }
393
394 if (client_node_size > kMaxWorkerNodeIdSize)
395 throw runtime_error("Job event client node size is more "
396 "than max allowed");
397 if (client_node_size > 0) {
398 errno = 0;
399 bytes = fread(client_node, 1, client_node_size, f);
400 if (bytes != client_node_size) {
401 if (bytes > 0)
402 throw runtime_error("Incomplete job event client node reading");
403 if (feof(f))
404 throw runtime_error("Unexpected end of job info file. "
405 "Expected a client node, got EOF");
406 if (errno != 0)
407 throw runtime_error(strerror(errno));
408 throw runtime_error("Unknown job event client node reading error");
409 }
410 }
411
412 if (client_session_size > kMaxWorkerNodeIdSize)
413 throw runtime_error("Job event client session size is more "
414 "than max allowed");
415 if (client_session_size > 0) {
416 errno = 0;
417 bytes = fread(client_session, 1, client_session_size, f);
418 if (bytes != client_session_size) {
419 if (bytes > 0)
420 throw runtime_error("Incomplete job event "
421 "client session reading");
422 if (feof(f))
423 throw runtime_error("Unexpected end of job info file. "
424 "Expected a client session, got EOF");
425 if (errno != 0)
426 throw runtime_error(strerror(errno));
427 throw runtime_error("Unknown job event "
428 "client session reading error");
429 }
430 }
431
432 if (err_msg_size > kNetScheduleMaxDBErrSize)
433 throw runtime_error("Job event error message size is more "
434 "than max allowed");
435 if (err_msg_size > 0) {
436 errno = 0;
437 bytes = fread(err_msg, 1, err_msg_size, f);
438 if (bytes != err_msg_size) {
439 if (bytes > 0)
440 throw runtime_error("Incomplete job event "
441 "error message reading");
442 if (feof(f))
443 throw runtime_error("Unexpected end of job info file. "
444 "Expected an error message, got EOF");
445 if (errno != 0)
446 throw runtime_error(strerror(errno));
447 throw runtime_error("Unknown job event "
448 "error message reading error");
449 }
450 }
451
452 if (total_size > fixed_size_from_header + client_node_size +
453 client_session_size + err_msg_size) {
454 size_t bytes_to_skip = total_size -
455 fixed_size_from_header -
456 client_node_size - client_session_size -
457 err_msg_size;
458 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
459 if (errno != 0)
460 throw runtime_error(strerror(errno));
461 throw runtime_error("Unknown job event record skipping error");
462 }
463 }
464
465 return 0;
466 }
467
468
SAffinityDictDump()469 SAffinityDictDump::SAffinityDictDump()
470 {
471 memset(this, 0, sizeof(SAffinityDictDump));
472 }
473
474
Write(FILE * f)475 void SAffinityDictDump::Write(FILE * f)
476 {
477 total_size = sizeof(SAffinityDictDump);
478
479 errno = 0;
480 if (fwrite(this, sizeof(SAffinityDictDump), 1, f) != 1)
481 throw runtime_error(strerror(errno));
482 }
483
Read(FILE * f,size_t fixed_size_from_header)484 int SAffinityDictDump::Read(FILE * f, size_t fixed_size_from_header)
485 {
486 memset(this, 0, sizeof(SAffinityDictDump));
487
488 size_t size_to_read = min(sizeof(SAffinityDictDump),
489 fixed_size_from_header);
490
491 errno = 0;
492 size_t bytes = fread(this, 1, size_to_read, f);
493 if (bytes != size_to_read) {
494 if (bytes > 0)
495 throw runtime_error("Incomplete affinity reading");
496 if (feof(f))
497 return 1;
498 if (errno != 0)
499 throw runtime_error(strerror(errno));
500 throw runtime_error("Unknown affinity reading error");
501 }
502
503 if (token_size > kNetScheduleMaxDBDataSize)
504 throw runtime_error("Affinity token size is more "
505 "than max allowed");
506
507 if (fixed_size_from_header > size_to_read) {
508 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
509
510 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
511 if (errno != 0)
512 throw runtime_error(strerror(errno));
513 throw runtime_error("Unknown affinity skipping error");
514 }
515 }
516 return 0;
517 }
518
519
SGroupDictDump()520 SGroupDictDump::SGroupDictDump()
521 {
522 memset(this, 0, sizeof(SGroupDictDump));
523 }
524
525
Write(FILE * f)526 void SGroupDictDump::Write(FILE * f)
527 {
528 total_size = sizeof(SGroupDictDump);
529
530 errno = 0;
531 if (fwrite(this, sizeof(SGroupDictDump), 1, f) != 1)
532 throw runtime_error(strerror(errno));
533 }
534
Read(FILE * f,size_t fixed_size_from_header)535 int SGroupDictDump::Read(FILE * f, size_t fixed_size_from_header)
536 {
537 memset(this, 0, sizeof(SGroupDictDump));
538
539 size_t size_to_read = min(sizeof(SGroupDictDump),
540 fixed_size_from_header);
541
542 errno = 0;
543 size_t bytes = fread(this, 1, size_to_read, f);
544 if (bytes != size_to_read) {
545 if (bytes > 0)
546 throw runtime_error("Incomplete job group reading");
547 if (feof(f))
548 return 1;
549 if (errno != 0)
550 throw runtime_error(strerror(errno));
551 throw runtime_error("Unknown job group reading error");
552 }
553
554 if (token_size > kNetScheduleMaxDBDataSize)
555 throw runtime_error("Group token size is more "
556 "than max allowed");
557
558 if (fixed_size_from_header > size_to_read) {
559 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
560
561 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
562 if (errno != 0)
563 throw runtime_error(strerror(errno));
564 throw runtime_error("Unknown group skipping error");
565 }
566 }
567 return 0;
568 }
569
570
SQueueDescriptionDump()571 SQueueDescriptionDump::SQueueDescriptionDump()
572 {
573 memset(this, 0, sizeof(SQueueDescriptionDump));
574 }
575
576
Write(FILE * f)577 void SQueueDescriptionDump::Write(FILE * f)
578 {
579 total_size = sizeof(SQueueDescriptionDump);
580
581 errno = 0;
582 if (fwrite(this, sizeof(SQueueDescriptionDump), 1, f) != 1)
583 throw runtime_error(strerror(errno));
584 }
585
Read(FILE * f,size_t fixed_size_from_header)586 int SQueueDescriptionDump::Read(FILE * f, size_t fixed_size_from_header)
587 {
588 memset(this, 0, sizeof(SQueueDescriptionDump));
589
590 size_t size_to_read = min(sizeof(SQueueDescriptionDump),
591 fixed_size_from_header);
592
593 errno = 0;
594 size_t bytes = fread(this, 1, size_to_read, f);
595 if (bytes != size_to_read) {
596 if (bytes > 0)
597 throw runtime_error("Incomplete queue description reading");
598 if (feof(f))
599 return 1;
600 if (errno != 0)
601 throw runtime_error(strerror(errno));
602 throw runtime_error("Unknown queue description reading error");
603 }
604
605 if (qname_size > kMaxQueueNameSize)
606 throw runtime_error("Queue name size is more "
607 "than max allowed");
608 if (qclass_size > kMaxQueueNameSize)
609 throw runtime_error("Queue class name size is more "
610 "than max allowed");
611 if (program_name_size > kMaxQueueLimitsSize)
612 throw runtime_error("Program name size is more "
613 "than max allowed");
614 if (subm_hosts_size > kMaxQueueLimitsSize)
615 throw runtime_error("Submitter hosts size is more "
616 "than max allowed");
617 if (wnode_hosts_size > kMaxQueueLimitsSize)
618 throw runtime_error("Worker node hosts size is more "
619 "than max allowed");
620 if (reader_hosts_size > kMaxQueueLimitsSize)
621 throw runtime_error("Reader hosts size is more "
622 "than max allowed");
623 if (description_size > kMaxDescriptionSize)
624 throw runtime_error("Description size is more "
625 "than max allowed");
626 if (linked_section_prefixes_size > kLinkedSectionsList)
627 throw runtime_error("Linked section prefixes size is more "
628 "than max allowed");
629 if (linked_section_names_size > kLinkedSectionsList)
630 throw runtime_error("Linked section names size is more "
631 "than max allowed");
632
633 if (fixed_size_from_header > size_to_read) {
634 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
635
636 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
637 if (errno != 0)
638 throw runtime_error(strerror(errno));
639 throw runtime_error("Unknown queue description skipping error");
640 }
641 }
642 return 0;
643 }
644
645
SLinkedSectionDump()646 SLinkedSectionDump::SLinkedSectionDump()
647 {
648 memset(this, 0, sizeof(SLinkedSectionDump));
649 }
650
651
Write(FILE * f)652 void SLinkedSectionDump::Write(FILE * f)
653 {
654 total_size = sizeof(SLinkedSectionDump);
655
656 if (fwrite(this, sizeof(SLinkedSectionDump), 1, f) != 1)
657 throw runtime_error(strerror(errno));
658 }
659
Read(FILE * f,size_t fixed_size_from_header)660 int SLinkedSectionDump::Read(FILE * f, size_t fixed_size_from_header)
661 {
662 memset(this, 0, sizeof(SLinkedSectionDump));
663
664 size_t size_to_read = min(sizeof(SLinkedSectionDump),
665 fixed_size_from_header);
666
667 errno = 0;
668 size_t bytes = fread(this, 1, size_to_read, f);
669 if (bytes != size_to_read) {
670 if (bytes > 0)
671 throw runtime_error("Incomplete linked section reading");
672 if (feof(f))
673 return 1;
674 if (errno != 0)
675 throw runtime_error(strerror(errno));
676 throw runtime_error("Unknown linked section reading error");
677 }
678
679 if (section_size > kLinkedSectionValueNameSize)
680 throw runtime_error("Linked section name size is more "
681 "than max allowed");
682 if (value_name_size > kLinkedSectionValueNameSize)
683 throw runtime_error("Linked section value name size is more "
684 "than max allowed");
685 if (value_size > kLinkedSectionValueSize)
686 throw runtime_error("Linked section value size is more "
687 "than max allowed");
688
689 if (fixed_size_from_header > size_to_read) {
690 size_t bytes_to_skip = fixed_size_from_header - size_to_read;
691
692 if (fseek(f, bytes_to_skip, SEEK_CUR) != 0) {
693 if (errno != 0)
694 throw runtime_error(strerror(errno));
695 throw runtime_error("Unknown linked section skipping error");
696 }
697 }
698 return 0;
699 }
700
701
702 END_NCBI_SCOPE
703