]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/init.c
Don't set bdb yield with NO_THREADS
[openldap] / servers / slapd / back-bdb / init.c
1 /* init.c - initialize bdb backend */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12 #include <ac/unistd.h>
13 #include <ac/stdlib.h>
14
15 #include <lutil.h>
16
17 #include "back-bdb.h"
18 #include "external.h"
19
20 static struct bdbi_database {
21         char *file;
22         char *name;
23         int type;
24         int flags;
25 } bdbi_databases[] = {
26         { "id2entry" BDB_SUFFIX, "id2entry", DB_BTREE, 0 },
27 #ifdef BDB_HIER
28         { "id2parent" BDB_SUFFIX, "id2parent", DB_BTREE, 0 },
29 #else
30         { "dn2id" BDB_SUFFIX, "dn2id", DB_BTREE, 0 },
31 #endif
32         { NULL, NULL, 0, 0 }
33 };
34
35 struct berval bdb_uuid = { 0, NULL };
36
37 typedef void * db_malloc(size_t);
38 typedef void * db_realloc(void *, size_t);
39
40 static int
41 bdb_open( BackendInfo *bi )
42 {
43         return 0;
44 }
45
46 #if 0
47 static int
48 bdb_destroy( BackendInfo *bi )
49 {
50         return 0;
51 }
52
53 static int
54 bdb_close( BackendInfo *bi )
55 {
56         /* terminate the underlying database system */
57         return 0;
58 }
59 #endif
60
61 static int
62 bdb_db_init( BackendDB *be )
63 {
64         struct bdb_info *bdb;
65
66 #ifdef NEW_LOGGING
67         LDAP_LOG( BACK_BDB, ENTRY, "bdb_db_init", 0, 0, 0 );
68 #else
69         Debug( LDAP_DEBUG_ANY,
70                 "bdb_db_init: Initializing BDB database\n",
71                 0, 0, 0 );
72 #endif
73
74         /* indicate system schema supported */
75         be->be_flags |=
76 #ifdef BDB_SUBENTRIES
77                 SLAP_BFLAG_SUBENTRIES |
78 #endif
79 #ifdef BDB_ALIASES
80                 SLAP_BFLAG_ALIASES |
81 #endif
82                 SLAP_BFLAG_REFERRALS;
83
84         /* allocate backend-database-specific stuff */
85         bdb = (struct bdb_info *) ch_calloc( 1, sizeof(struct bdb_info) );
86
87         /* DBEnv parameters */
88         bdb->bi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
89         bdb->bi_dbenv_xflags = 0;
90         bdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
91
92         bdb->bi_cache.c_maxsize = DEFAULT_CACHE_SIZE;
93
94         bdb->bi_lock_detect = DB_LOCK_DEFAULT;
95
96         ldap_pvt_thread_mutex_init( &bdb->bi_database_mutex );
97         ldap_pvt_thread_mutex_init( &bdb->bi_lastid_mutex );
98         ldap_pvt_thread_mutex_init( &bdb->bi_cache.lru_mutex );
99         ldap_pvt_thread_rdwr_init ( &bdb->bi_cache.c_rwlock );
100 #ifdef BDB_HIER
101         ldap_pvt_thread_rdwr_init( &bdb->bi_tree_rdwr );
102 #endif
103
104         be->be_private = bdb;
105         return 0;
106 }
107
108 int
109 bdb_bt_compare(
110         DB *db, 
111         const DBT *usrkey,
112         const DBT *curkey
113 )
114 {
115         unsigned char *u, *c;
116         int i;
117
118         u = usrkey->data;
119         c = curkey->data;
120
121 #ifdef WORDS_BIGENDIAN
122         for( i = 0; i < sizeof(ID); i++)
123 #else
124         for( i = sizeof(ID)-1; i >= 0; i--)
125 #endif
126         {
127                 if( u[i] - c[i] )
128                         return u[i] - c[i];
129         }
130         return 0;
131 }
132
133 static int
134 bdb_db_open( BackendDB *be )
135 {
136         int rc, i;
137         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
138         u_int32_t flags;
139 #ifdef HAVE_EBCDIC
140         char path[MAXPATHLEN];
141 #endif
142
143 #ifdef NEW_LOGGING
144         LDAP_LOG( BACK_BDB, ARGS, 
145                 "bdb_db_open: %s\n", be->be_suffix[0].bv_val, 0, 0 );
146 #else
147         Debug( LDAP_DEBUG_ARGS,
148                 "bdb_db_open: %s\n",
149                 be->be_suffix[0].bv_val, 0, 0 );
150 #endif
151
152         /* we should check existance of dbenv_home and db_directory */
153
154         rc = db_env_create( &bdb->bi_dbenv, 0 );
155         if( rc != 0 ) {
156 #ifdef NEW_LOGGING
157                 LDAP_LOG( BACK_BDB, ERR, 
158                         "bdb_db_open: db_env_create failed: %s (%d)\n", 
159                         db_strerror(rc), rc, 0 );
160 #else
161                 Debug( LDAP_DEBUG_ANY,
162                         "bdb_db_open: db_env_create failed: %s (%d)\n",
163                         db_strerror(rc), rc, 0 );
164 #endif
165                 return rc;
166         }
167
168         flags = DB_INIT_MPOOL | DB_THREAD | DB_CREATE
169                 | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN;
170         
171 #if 0
172         /* Never do automatic recovery, must perform it manually.
173          * Otherwise restarting with gentlehup will corrupt the
174          * database.
175          */
176         if( !(slapMode & SLAP_TOOL_MODE) ) flags |= DB_RECOVER;
177 #endif
178
179         bdb->bi_dbenv->set_errpfx( bdb->bi_dbenv, be->be_suffix[0].bv_val );
180         bdb->bi_dbenv->set_errcall( bdb->bi_dbenv, bdb_errcall );
181 #ifndef NO_THREADS
182         bdb->bi_dbenv->set_lk_detect( bdb->bi_dbenv, bdb->bi_lock_detect );
183 #endif
184
185 #ifdef BDB_SUBDIRS
186         {
187                 char dir[MAXPATHLEN];
188                 size_t len = strlen( bdb->bi_dbenv_home );
189
190                 strcpy( dir, bdb->bi_dbenv_home );
191                 strcat( &dir[len], BDB_TMP_SUBDIR );
192                 
193                 rc = bdb->bi_dbenv->set_tmp_dir( bdb->bi_dbenv, dir );
194                 if( rc != 0 ) {
195 #ifdef NEW_LOGGING
196                         LDAP_LOG( BACK_BDB, ERR, 
197                                 "bdb_db_open: set_tmp_dir(%s) failed: %s (%d)\n", 
198                                 dir, db_strerror(rc), rc );
199 #else
200                         Debug( LDAP_DEBUG_ANY,
201                                 "bdb_db_open: set_tmp_dir(%s) failed: %s (%d)\n",
202                                 dir, db_strerror(rc), rc );
203 #endif
204                         return rc;
205                 }
206
207                 strcat( &dir[len], BDB_LG_SUBDIR );
208
209                 rc = bdb->bi_dbenv->set_lg_dir( bdb->bi_dbenv, dir );
210                 if( rc != 0 ) {
211 #ifdef NEW_LOGGING
212                         LDAP_LOG( BACK_BDB, ERR, 
213                                 "bdb_db_open: set_lg_dir(%s) failed: %s (%d)\n", 
214                                 dir, db_strerror(rc), rc );
215 #else
216                         Debug( LDAP_DEBUG_ANY,
217                                 "bdb_db_open: set_lg_dir(%s) failed: %s (%d)\n",
218                                 dir, db_strerror(rc), rc );
219 #endif
220                         return rc;
221                 }
222
223                 strcat( &dir[len], BDB_DATA_SUBDIR );
224
225                 rc = bdb->bi_dbenv->set_data_dir( bdb->bi_dbenv, dir );
226                 if( rc != 0 ) {
227 #ifdef NEW_LOGGING
228                         LDAP_LOG( BACK_BDB, ERR, 
229                                 "bdb_db_open: set_data_dir(%s) failed: %s (%d)\n",
230                                 dir, db_strerror(rc), rc );
231 #else
232                         Debug( LDAP_DEBUG_ANY,
233                                 "bdb_db_open: set_data_dir(%s) failed: %s (%d)\n",
234                                 dir, db_strerror(rc), rc );
235 #endif
236                         return rc;
237                 }
238         }
239 #endif
240
241 #ifdef NEW_LOGGING
242         LDAP_LOG( BACK_BDB, DETAIL1, 
243                 "bdb_db_open: dbenv_open %s\n", bdb->bi_dbenv_home, 0, 0 );
244 #else
245         Debug( LDAP_DEBUG_TRACE,
246                 "bdb_db_open: dbenv_open(%s)\n",
247                 bdb->bi_dbenv_home, 0, 0);
248 #endif
249
250 #ifdef HAVE_EBCDIC
251         strcpy( path, bdb->bi_dbenv_home );
252         __atoe( path );
253         rc = bdb->bi_dbenv->open( bdb->bi_dbenv,
254                 path,
255                 flags,
256                 bdb->bi_dbenv_mode );
257 #else
258         rc = bdb->bi_dbenv->open( bdb->bi_dbenv,
259                 bdb->bi_dbenv_home,
260                 flags,
261                 bdb->bi_dbenv_mode );
262 #endif
263         if( rc != 0 ) {
264 #ifdef NEW_LOGGING
265                 LDAP_LOG( BACK_BDB, ERR, 
266                         "bdb_db_open: dbenv_open failed: %s (%d)\n", 
267                         db_strerror(rc), rc, 0 );
268 #else
269                 Debug( LDAP_DEBUG_ANY,
270                         "bdb_db_open: dbenv_open failed: %s (%d)\n",
271                         db_strerror(rc), rc, 0 );
272 #endif
273                 return rc;
274         }
275
276         if( bdb->bi_dbenv_xflags != 0 ) {
277                 rc = bdb->bi_dbenv->set_flags( bdb->bi_dbenv,
278                         bdb->bi_dbenv_xflags, 1);
279                 if( rc != 0 ) {
280 #ifdef NEW_LOGGING
281                         LDAP_LOG( BACK_BDB, ERR, 
282                                 "bdb_db_open: dbenv_set_flags failed: %s (%d)\n", 
283                                 db_strerror(rc), rc, 0 );
284 #else
285                         Debug( LDAP_DEBUG_ANY,
286                                 "bdb_db_open: dbenv_set_flags failed: %s (%d)\n",
287                                 db_strerror(rc), rc, 0 );
288 #endif
289                         return rc;
290                 }
291         }
292
293         flags = DB_THREAD | DB_CREATE | bdb->bi_db_opflags;
294
295         bdb->bi_databases = (struct bdb_db_info **) ch_malloc(
296                 BDB_INDICES * sizeof(struct bdb_db_info *) );
297
298         /* open (and create) main database */
299         for( i = 0; bdbi_databases[i].name; i++ ) {
300                 struct bdb_db_info *db;
301
302                 db = (struct bdb_db_info *) ch_calloc(1, sizeof(struct bdb_db_info));
303
304                 rc = db_create( &db->bdi_db, bdb->bi_dbenv, 0 );
305                 if( rc != 0 ) {
306 #ifdef NEW_LOGGING
307                         LDAP_LOG( BACK_BDB, ERR, 
308                                 "bdb_db_open: db_create(%s) failed: %s (%d)\n", 
309                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
310 #else
311                         Debug( LDAP_DEBUG_ANY,
312                                 "bdb_db_open: db_create(%s) failed: %s (%d)\n",
313                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
314 #endif
315                         return rc;
316                 }
317
318                 if( i == BDB_ID2ENTRY ) {
319                         rc = db->bdi_db->set_bt_compare( db->bdi_db,
320                                 bdb_bt_compare );
321                         rc = db->bdi_db->set_pagesize( db->bdi_db,
322                                 BDB_ID2ENTRY_PAGESIZE );
323                 } else {
324 #ifdef BDB_HIER
325                         rc = db->bdi_db->set_bt_compare( db->bdi_db,
326                                 bdb_bt_compare );
327 #else
328                         rc = db->bdi_db->set_flags( db->bdi_db, 
329                                 DB_DUP | DB_DUPSORT );
330                         rc = db->bdi_db->set_dup_compare( db->bdi_db,
331                                 bdb_bt_compare );
332 #endif
333                         rc = db->bdi_db->set_pagesize( db->bdi_db,
334                                 BDB_PAGESIZE );
335                 }
336
337 #ifdef HAVE_EBCDIC
338                 strcpy( path, bdbi_databases[i].file );
339                 __atoe( path );
340                 rc = DB_OPEN( db->bdi_db, 
341                         path,
342                 /*      bdbi_databases[i].name, */ NULL,
343                         bdbi_databases[i].type,
344                         bdbi_databases[i].flags | flags,
345                         bdb->bi_dbenv_mode );
346 #else
347                 rc = DB_OPEN( db->bdi_db, 
348                         bdbi_databases[i].file,
349                 /*      bdbi_databases[i].name, */ NULL,
350                         bdbi_databases[i].type,
351                         bdbi_databases[i].flags | flags,
352                         bdb->bi_dbenv_mode );
353 #endif
354
355                 if( rc != 0 ) {
356 #ifdef NEW_LOGGING
357                         LDAP_LOG( BACK_BDB, ERR, 
358                                 "bdb_db_open: db_create(%s) failed: %s (%d)\n", 
359                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
360 #else
361                         Debug( LDAP_DEBUG_ANY,
362                                 "bdb_db_open: db_open(%s) failed: %s (%d)\n",
363                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
364 #endif
365                         return rc;
366                 }
367
368                 db->bdi_name = bdbi_databases[i].name;
369                 bdb->bi_databases[i] = db;
370         }
371
372         bdb->bi_databases[i] = NULL;
373         bdb->bi_ndatabases = i;
374
375         /* get nextid */
376         rc = bdb_last_id( be, NULL );
377         if( rc != 0 ) {
378 #ifdef NEW_LOGGING
379                         LDAP_LOG( BACK_BDB, ERR, 
380                                 "bdb_db_open: last_id(%s) failed: %s (%d)\n", 
381                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
382 #else
383                 Debug( LDAP_DEBUG_ANY,
384                         "bdb_db_open: last_id(%s) failed: %s (%d)\n",
385                         bdb->bi_dbenv_home, db_strerror(rc), rc );
386 #endif
387                 return rc;
388         }
389
390         /* <insert> open (and create) index databases */
391 #ifdef BDB_HIER
392         rc = bdb_build_tree( be );
393 #endif
394         return 0;
395 }
396
397 static int
398 bdb_db_close( BackendDB *be )
399 {
400         int rc;
401         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
402         struct bdb_db_info *db;
403
404         while( bdb->bi_ndatabases-- ) {
405                 db = bdb->bi_databases[bdb->bi_ndatabases];
406                 rc = db->bdi_db->close( db->bdi_db, 0 );
407                 /* Lower numbered names are not strdup'd */
408                 if( bdb->bi_ndatabases >= BDB_NDB )
409                         free( db->bdi_name );
410                 free( db );
411         }
412         free( bdb->bi_databases );
413         bdb_attr_index_destroy( bdb->bi_attrs );
414
415         bdb_cache_release_all (&bdb->bi_cache);
416
417 #if defined(NO_THREADS) && defined(BDB_REUSE_LOCKERS)
418         if ( bdb->bi_locker_id ) {
419                 bdb_locker_id_free( bdb->bi_dbenv, bdb->bi_locker_id );
420         }
421 #endif
422         return 0;
423 }
424
425 static int
426 bdb_db_destroy( BackendDB *be )
427 {
428         int rc;
429         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
430
431         /* close db environment */
432         if( bdb->bi_dbenv ) {
433                 /* force a checkpoint */
434                 rc = TXN_CHECKPOINT( bdb->bi_dbenv, 0, 0, DB_FORCE );
435                 if( rc != 0 ) {
436 #ifdef NEW_LOGGING
437                         LDAP_LOG( BACK_BDB, ERR, 
438                                 "bdb_db_destroy: txn_checkpoint failed: %s (%d)\n",
439                                 db_strerror(rc), rc, 0 );
440 #else
441                         Debug( LDAP_DEBUG_ANY,
442                                 "bdb_db_destroy: txn_checkpoint failed: %s (%d)\n",
443                                 db_strerror(rc), rc, 0 );
444 #endif
445                 }
446
447                 bdb_cache_release_all (&bdb->bi_cache);
448
449                 rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 );
450                 bdb->bi_dbenv = NULL;
451                 if( rc != 0 ) {
452 #ifdef NEW_LOGGING
453                         LDAP_LOG( BACK_BDB, ERR, 
454                                 "bdb_db_destroy: close failed: %s (%d)\n", 
455                                 db_strerror(rc), rc, 0 );
456 #else
457                         Debug( LDAP_DEBUG_ANY,
458                                 "bdb_db_destroy: close failed: %s (%d)\n",
459                                 db_strerror(rc), rc, 0 );
460 #endif
461                         return rc;
462                 }
463         }
464
465         if( bdb->bi_dbenv_home ) ch_free( bdb->bi_dbenv_home );
466
467 #ifdef BDB_HIER
468         ldap_pvt_thread_rdwr_destroy( &bdb->bi_tree_rdwr );
469 #endif
470         ldap_pvt_thread_rdwr_destroy ( &bdb->bi_cache.c_rwlock );
471         ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.lru_mutex );
472         ldap_pvt_thread_mutex_destroy( &bdb->bi_lastid_mutex );
473         ldap_pvt_thread_mutex_destroy( &bdb->bi_database_mutex );
474
475         ch_free( bdb );
476         be->be_private = NULL;
477
478         return 0;
479 }
480
481 #ifdef SLAPD_BDB_DYNAMIC
482 int back_bdb_LTX_init_module( int argc, char *argv[] ) {
483         BackendInfo bi;
484
485         memset( &bi, '\0', sizeof(bi) );
486         bi.bi_type = "bdb";
487         bi.bi_init = bdb_initialize;
488
489         backend_add( &bi );
490         return 0;
491 }
492 #endif /* SLAPD_BDB_DYNAMIC */
493
494 int
495 bdb_initialize(
496         BackendInfo     *bi
497 )
498 {
499         static char *controls[] = {
500                 LDAP_CONTROL_MANAGEDSAIT,
501 #ifdef LDAP_CONTROL_SUBENTRIES
502                 LDAP_CONTROL_SUBENTRIES,
503 #endif
504 #ifdef LDAP_CONTROL_NOOP
505                 LDAP_CONTROL_NOOP,
506 #endif
507 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
508                 LDAP_CONTROL_VALUESRETURNFILTER,
509 #endif
510 #ifdef LDAP_CLIENT_UPDATE
511                 LDAP_CONTROL_CLIENT_UPDATE,
512 #endif
513                 NULL
514         };
515
516         bi->bi_controls = controls;
517
518         /* initialize the underlying database system */
519 #ifdef NEW_LOGGING
520         LDAP_LOG( BACK_BDB, ENTRY, "bdb_db_initialize\n", 0, 0, 0 );
521 #else
522         Debug( LDAP_DEBUG_TRACE, "bdb_open: initialize BDB backend\n",
523                 0, 0, 0 );
524 #endif
525
526         {       /* version check */
527                 int major, minor, patch;
528                 char *version = db_version( &major, &minor, &patch );
529 #ifdef HAVE_EBCDIC
530                 char v2[1024];
531
532                 /* All our stdio does an ASCII to EBCDIC conversion on
533                  * the output. Strings from the BDB library are already
534                  * in EBCDIC; we have to go back and forth...
535                  */
536                 strcpy( v2, version );
537                 __etoa( v2 );
538                 version = v2;
539 #endif
540
541                 if( major != DB_VERSION_MAJOR ||
542                         minor != DB_VERSION_MINOR ||
543                         patch < DB_VERSION_PATCH )
544                 {
545 #ifdef NEW_LOGGING
546                         LDAP_LOG( BACK_BDB, ERR, 
547                                 "bdb_db_initialize: version mismatch: "
548                                 "\texpected: %s \tgot: %s\n", DB_VERSION_STRING, version, 0 );
549 #else
550                         Debug( LDAP_DEBUG_ANY,
551                                 "bdb_open: version mismatch\n"
552                                 "\texpected: " DB_VERSION_STRING "\n"
553                                 "\tgot: %s \n", version, 0, 0 );
554 #endif
555                 }
556
557 #ifdef NEW_LOGGING
558                 LDAP_LOG( BACK_BDB, DETAIL1, 
559                         "bdb_db_initialize: bdb_open: %s\n", version, 0, 0 );
560 #else
561                 Debug( LDAP_DEBUG_ANY, "bdb_open: %s\n",
562                         version, 0, 0 );
563 #endif
564         }
565
566         db_env_set_func_free( ber_memfree );
567         db_env_set_func_malloc( (db_malloc *)ber_memalloc );
568         db_env_set_func_realloc( (db_realloc *)ber_memrealloc );
569 #ifndef NO_THREAD
570         /* This is a no-op on a NO_THREAD build. Leave the default
571          * alone so that BDB will sleep on interprocess conflicts.
572          */
573         db_env_set_func_yield( ldap_pvt_thread_yield );
574 #endif
575
576         {
577                 static char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
578
579                 bdb_uuid.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ));
580                 bdb_uuid.bv_val = uuidbuf;
581         }
582
583         bi->bi_open = 0;
584         bi->bi_close = 0;
585         bi->bi_config = 0;
586         bi->bi_destroy = 0;
587
588         bi->bi_db_init = bdb_db_init;
589         bi->bi_db_config = bdb_db_config;
590         bi->bi_db_open = bdb_db_open;
591         bi->bi_db_close = bdb_db_close;
592         bi->bi_db_destroy = bdb_db_destroy;
593
594         bi->bi_op_add = bdb_add;
595         bi->bi_op_bind = bdb_bind;
596         bi->bi_op_compare = bdb_compare;
597         bi->bi_op_delete = bdb_delete;
598         bi->bi_op_modify = bdb_modify;
599         bi->bi_op_modrdn = bdb_modrdn;
600         bi->bi_op_search = bdb_search;
601
602         bi->bi_op_unbind = 0;
603         bi->bi_op_abandon = 0;
604
605         bi->bi_extended = bdb_extended;
606
607 #if 1
608         /*
609          * these routines (and their callers) are not yet designed
610          * to work with transaction.  Using them may cause deadlock.
611          */
612         bi->bi_acl_group = bdb_group;
613         bi->bi_acl_attribute = bdb_attribute;
614 #else
615         bi->bi_acl_group = 0;
616         bi->bi_acl_attribute = 0;
617 #endif
618
619         bi->bi_chk_referrals = bdb_referrals;
620         bi->bi_operational = bdb_operational;
621         bi->bi_entry_release_rw = bdb_entry_release;
622
623         /*
624          * hooks for slap tools
625          */
626         bi->bi_tool_entry_open = bdb_tool_entry_open;
627         bi->bi_tool_entry_close = bdb_tool_entry_close;
628         bi->bi_tool_entry_first = bdb_tool_entry_next;
629         bi->bi_tool_entry_next = bdb_tool_entry_next;
630         bi->bi_tool_entry_get = bdb_tool_entry_get;
631         bi->bi_tool_entry_put = bdb_tool_entry_put;
632         bi->bi_tool_entry_reindex = bdb_tool_entry_reindex;
633         bi->bi_tool_sync = 0;
634
635         bi->bi_connection_init = 0;
636         bi->bi_connection_destroy = 0;
637
638         return 0;
639 }