2 Unix SMB/Netbios implementation.
4 shared memory locking implementation
5 Copyright (C) Andrew Tridgell 1992-1997
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 12 aug 96: Erik.Devriendt@te6.siemens.be
24 added support for shared memory implementation of share mode locking
26 May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
27 locking to deal with multiple share modes per open file.
29 September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
32 October 1997 - split into separate file (tridge)
35 #ifdef FAST_SHARE_MODES
38 extern int DEBUGLEVEL;
39 extern connection_struct Connections[];
40 extern files_struct Files[];
42 /* share mode record pointed to in shared memory hash bucket */
45 smb_shm_offset_t next_offset; /* offset of next record in chain from hash bucket */
49 int num_share_mode_entries;
50 smb_shm_offset_t share_mode_entries; /* Chain of share mode entries for this file */
54 /* share mode entry pointed to by share_mode_record struct */
57 smb_shm_offset_t next_share_mode_entry;
59 } shm_share_mode_entry;
62 /*******************************************************************
63 deinitialize the shared memory for share_mode management
64 ******************************************************************/
65 static BOOL shm_stop_share_mode_mgmt(void)
67 return smb_shm_close();
70 /*******************************************************************
71 lock a hash bucket entry in shared memory for share_mode management
72 ******************************************************************/
73 static BOOL shm_lock_share_entry(int cnum, uint32 dev, uint32 inode, int *ptok)
75 return smb_shm_lock_hash_entry(HASH_ENTRY(dev, inode));
78 /*******************************************************************
79 unlock a hash bucket entry in shared memory for share_mode management
80 ******************************************************************/
81 static BOOL shm_unlock_share_entry(int cnum, uint32 dev, uint32 inode, int token)
83 return smb_shm_unlock_hash_entry(HASH_ENTRY(dev, inode));
86 /*******************************************************************
87 get all share mode entries in shared memory for a dev/inode pair.
88 ********************************************************************/
89 static int shm_get_share_modes(int cnum, int token, uint32 dev, uint32 inode,
90 share_mode_entry **old_shares)
92 smb_shm_offset_t *mode_array;
93 unsigned int hash_entry = HASH_ENTRY(dev, inode);
94 share_mode_record *file_scanner_p;
95 share_mode_record *file_prev_p;
96 shm_share_mode_entry *entry_scanner_p;
97 shm_share_mode_entry *entry_prev_p;
99 int num_entries_copied;
101 share_mode_entry *share_array = (share_mode_entry *)0;
105 if(hash_entry > lp_shmem_hash_size() )
108 ("PANIC ERROR : get_share_modes (FAST_SHARE_MODES): hash_entry %d too large \
110 hash_entry, lp_shmem_hash_size() ));
114 mode_array = (smb_shm_offset_t *)smb_shm_offset2addr(smb_shm_get_userdef_off());
116 if(mode_array[hash_entry] == NULL_OFFSET)
118 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): hash bucket %d empty\n", hash_entry));
122 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(mode_array[hash_entry]);
123 file_prev_p = file_scanner_p;
124 while(file_scanner_p)
126 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
133 file_prev_p = file_scanner_p ;
134 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(
135 file_scanner_p->next_offset);
141 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): no entry for \
142 file dev = %d, ino = %d in hash_bucket %d\n", dev, inode, hash_entry));
146 if(file_scanner_p->locking_version != LOCKING_VERSION)
148 DEBUG(0,("ERROR:get_share_modes (FAST_SHARE_MODES): Deleting old share mode \
149 record due to old locking version %d for file dev = %d, inode = %d in hash \
150 bucket %d\n", file_scanner_p->locking_version, dev, inode, hash_entry));
151 if(file_prev_p == file_scanner_p)
152 mode_array[hash_entry] = file_scanner_p->next_offset;
154 file_prev_p->next_offset = file_scanner_p->next_offset;
155 smb_shm_free(smb_shm_addr2offset(file_scanner_p));
159 /* Allocate the old_shares array */
160 num_entries = file_scanner_p->num_share_mode_entries;
163 *old_shares = share_array = (share_mode_entry *)
164 malloc(num_entries * sizeof(share_mode_entry));
167 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): malloc fail !\n"));
172 num_entries_copied = 0;
174 entry_scanner_p = (shm_share_mode_entry*)smb_shm_offset2addr(
175 file_scanner_p->share_mode_entries);
176 entry_prev_p = entry_scanner_p;
177 while(entry_scanner_p)
179 int pid = entry_scanner_p->e.pid;
181 if (pid && !process_exists(pid))
183 /* Delete this share mode entry */
184 shm_share_mode_entry *delete_entry_p = entry_scanner_p;
185 int share_mode = entry_scanner_p->e.share_mode;
187 if(entry_prev_p == entry_scanner_p)
189 /* We are at start of list */
190 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
191 entry_scanner_p = (shm_share_mode_entry*)smb_shm_offset2addr(
192 file_scanner_p->share_mode_entries);
193 entry_prev_p = entry_scanner_p;
197 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
198 entry_scanner_p = (shm_share_mode_entry*)
199 smb_shm_offset2addr(entry_scanner_p->next_share_mode_entry);
201 /* Decrement the number of share mode entries on this share mode record */
202 file_scanner_p->num_share_mode_entries -= 1;
205 if(file_scanner_p->num_share_mode_entries < 0)
207 DEBUG(0,("PANIC ERROR:get_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
208 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
209 dev, inode, hash_entry));
213 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): process %d no longer exists and \
214 it left a share mode entry with mode 0x%X for file dev = %d, ino = %d in hash \
215 bucket %d (number of entries now = %d)\n",
216 pid, share_mode, dev, inode, hash_entry,
217 file_scanner_p->num_share_mode_entries));
219 smb_shm_free(smb_shm_addr2offset(delete_entry_p));
223 /* This is a valid share mode entry and the process that
224 created it still exists. Copy it into the output array.
226 share_array[num_entries_copied].pid = entry_scanner_p->e.pid;
227 share_array[num_entries_copied].share_mode = entry_scanner_p->e.share_mode;
228 share_array[num_entries_copied].op_port = entry_scanner_p->e.op_port;
229 share_array[num_entries_copied].op_type = entry_scanner_p->e.op_type;
230 memcpy(&share_array[num_entries_copied].time, &entry_scanner_p->e.time,
231 sizeof(struct timeval));
232 num_entries_copied++;
233 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): Read share mode \
234 record mode 0x%X pid=%d\n", entry_scanner_p->e.share_mode, entry_scanner_p->e.pid));
235 entry_prev_p = entry_scanner_p;
236 entry_scanner_p = (shm_share_mode_entry *)
237 smb_shm_offset2addr(entry_scanner_p->next_share_mode_entry);
241 /* If no valid share mode entries were found then this record shouldn't exist ! */
242 if(num_entries_copied == 0)
244 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): file with dev %d, inode %d in \
245 hash bucket %d has a share mode record but no entries - deleting\n",
246 dev, inode, hash_entry));
248 free((char *)*old_shares);
251 if(file_prev_p == file_scanner_p)
252 mode_array[hash_entry] = file_scanner_p->next_offset;
254 file_prev_p->next_offset = file_scanner_p->next_offset;
255 smb_shm_free(smb_shm_addr2offset(file_scanner_p));
258 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): file with dev %d, inode %d in \
259 hash bucket %d returning %d entries\n", dev, inode, hash_entry, num_entries_copied));
261 return(num_entries_copied);
264 /*******************************************************************
265 del the share mode of a file.
266 ********************************************************************/
267 static void shm_del_share_mode(int token, int fnum)
270 smb_shm_offset_t *mode_array;
271 unsigned int hash_entry;
272 share_mode_record *file_scanner_p;
273 share_mode_record *file_prev_p;
274 shm_share_mode_entry *entry_scanner_p;
275 shm_share_mode_entry *entry_prev_p;
279 dev = Files[fnum].fd_ptr->dev;
280 inode = Files[fnum].fd_ptr->inode;
282 hash_entry = HASH_ENTRY(dev, inode);
284 if(hash_entry > lp_shmem_hash_size() )
287 ("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): hash_entry %d too large \
289 hash_entry, lp_shmem_hash_size() ));
293 mode_array = (smb_shm_offset_t *)smb_shm_offset2addr(smb_shm_get_userdef_off());
295 if(mode_array[hash_entry] == NULL_OFFSET)
297 DEBUG(0,("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): hash bucket %d empty\n",
302 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(mode_array[hash_entry]);
303 file_prev_p = file_scanner_p;
305 while(file_scanner_p)
307 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
314 file_prev_p = file_scanner_p ;
315 file_scanner_p = (share_mode_record *)
316 smb_shm_offset2addr(file_scanner_p->next_offset);
322 DEBUG(0,("ERROR:del_share_mode (FAST_SHARE_MODES): no entry found for dev %d, \
323 inode %d in hash bucket %d\n", dev, inode, hash_entry));
327 if(file_scanner_p->locking_version != LOCKING_VERSION)
329 DEBUG(0,("ERROR: del_share_modes (FAST_SHARE_MODES): Deleting old share mode \
330 record due to old locking version %d for file dev %d, inode %d hash bucket %d\n",
331 file_scanner_p->locking_version, dev, inode, hash_entry ));
332 if(file_prev_p == file_scanner_p)
333 mode_array[hash_entry] = file_scanner_p->next_offset;
335 file_prev_p->next_offset = file_scanner_p->next_offset;
336 smb_shm_free(smb_shm_addr2offset(file_scanner_p));
341 entry_scanner_p = (shm_share_mode_entry*)smb_shm_offset2addr(
342 file_scanner_p->share_mode_entries);
343 entry_prev_p = entry_scanner_p;
344 while(entry_scanner_p)
346 if( (pid == entry_scanner_p->e.pid) &&
347 (memcmp(&entry_scanner_p->e.time,
348 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
355 entry_prev_p = entry_scanner_p;
356 entry_scanner_p = (shm_share_mode_entry *)
357 smb_shm_offset2addr(entry_scanner_p->next_share_mode_entry);
363 /* Decrement the number of entries in the record. */
364 file_scanner_p->num_share_mode_entries -= 1;
366 DEBUG(2,("del_share_modes (FAST_SHARE_MODES): \
367 Deleting share mode entry dev = %d, inode = %d in hash bucket %d (num entries now = %d)\n",
368 dev, inode, hash_entry, file_scanner_p->num_share_mode_entries));
369 if(entry_prev_p == entry_scanner_p)
370 /* We are at start of list */
371 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
373 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
374 smb_shm_free(smb_shm_addr2offset(entry_scanner_p));
377 if(file_scanner_p->num_share_mode_entries < 0)
379 DEBUG(0,("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
380 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
381 dev, inode, hash_entry));
385 /* If we deleted the last share mode entry then remove the share mode record. */
386 if(file_scanner_p->num_share_mode_entries == 0)
388 DEBUG(2,("del_share_modes (FAST_SHARE_MODES): num entries = 0, deleting share_mode \
389 record dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
390 if(file_prev_p == file_scanner_p)
391 mode_array[hash_entry] = file_scanner_p->next_offset;
393 file_prev_p->next_offset = file_scanner_p->next_offset;
394 smb_shm_free(smb_shm_addr2offset(file_scanner_p));
399 DEBUG(0,("ERROR: del_share_modes (FAST_SHARE_MODES): No share mode record found \
400 dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
404 /*******************************************************************
405 set the share mode of a file. Return False on fail, True on success.
406 ********************************************************************/
407 static BOOL shm_set_share_mode(int token, int fnum, uint16 port, uint16 op_type)
409 files_struct *fs_p = &Files[fnum];
411 smb_shm_offset_t *mode_array;
412 unsigned int hash_entry;
413 share_mode_record *file_scanner_p;
414 share_mode_record *file_prev_p;
415 shm_share_mode_entry *new_entry_p;
416 smb_shm_offset_t new_entry_offset;
419 dev = fs_p->fd_ptr->dev;
420 inode = fs_p->fd_ptr->inode;
422 hash_entry = HASH_ENTRY(dev, inode);
423 if(hash_entry > lp_shmem_hash_size() )
426 ("PANIC ERROR:set_share_mode (FAST_SHARE_MODES): hash_entry %d too large \
428 hash_entry, lp_shmem_hash_size() ));
432 mode_array = (smb_shm_offset_t *)smb_shm_offset2addr(smb_shm_get_userdef_off());
434 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(mode_array[hash_entry]);
435 file_prev_p = file_scanner_p;
437 while(file_scanner_p)
439 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
446 file_prev_p = file_scanner_p ;
447 file_scanner_p = (share_mode_record *)
448 smb_shm_offset2addr(file_scanner_p->next_offset);
454 /* We must create a share_mode_record */
455 share_mode_record *new_mode_p = NULL;
456 smb_shm_offset_t new_offset = smb_shm_alloc( sizeof(share_mode_record) +
457 strlen(fs_p->name) + 1);
458 if(new_offset == NULL_OFFSET)
460 DEBUG(0,("ERROR:set_share_mode (FAST_SHARE_MODES): smb_shm_alloc fail !\n"));
463 new_mode_p = smb_shm_offset2addr(new_offset);
464 new_mode_p->locking_version = LOCKING_VERSION;
465 new_mode_p->st_dev = dev;
466 new_mode_p->st_ino = inode;
467 new_mode_p->num_share_mode_entries = 0;
468 new_mode_p->share_mode_entries = NULL_OFFSET;
469 strcpy(new_mode_p->file_name, fs_p->name);
471 /* Chain onto the start of the hash chain (in the hope we will be used first). */
472 new_mode_p->next_offset = mode_array[hash_entry];
473 mode_array[hash_entry] = new_offset;
475 file_scanner_p = new_mode_p;
477 DEBUG(3,("set_share_mode (FAST_SHARE_MODES): Created share record for %s (dev %d \
478 inode %d in hash bucket %d\n", fs_p->name, dev, inode, hash_entry));
481 /* Now create the share mode entry */
482 new_entry_offset = smb_shm_alloc( sizeof(shm_share_mode_entry));
483 if(new_entry_offset == NULL_OFFSET)
485 smb_shm_offset_t delete_offset = mode_array[hash_entry];
486 DEBUG(0,("ERROR:set_share_mode (FAST_SHARE_MODES): smb_shm_alloc fail 1!\n"));
487 /* Unlink the damaged record */
488 mode_array[hash_entry] = file_scanner_p->next_offset;
490 smb_shm_free( delete_offset );
494 new_entry_p = smb_shm_offset2addr(new_entry_offset);
496 new_entry_p->e.pid = getpid();
497 new_entry_p->e.share_mode = fs_p->share_mode;
498 new_entry_p->e.op_port = port;
499 new_entry_p->e.op_type = op_type;
500 memcpy( (char *)&new_entry_p->e.time, (char *)&fs_p->open_time, sizeof(struct timeval));
502 /* Chain onto the share_mode_record */
503 new_entry_p->next_share_mode_entry = file_scanner_p->share_mode_entries;
504 file_scanner_p->share_mode_entries = new_entry_offset;
507 if(file_scanner_p->num_share_mode_entries < 0)
509 DEBUG(0,("PANIC ERROR:set_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
510 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
511 dev, inode, hash_entry));
515 /* Increment the share_mode_entries counter */
516 file_scanner_p->num_share_mode_entries += 1;
518 DEBUG(3,("set_share_mode (FAST_SHARE_MODES): Created share entry for %s with mode \
519 0x%X pid=%d (num_entries now = %d)\n",fs_p->name, fs_p->share_mode, new_entry_p->e.pid,
520 file_scanner_p->num_share_mode_entries));
525 /*******************************************************************
526 Remove an oplock port and mode entry from a share mode.
527 ********************************************************************/
528 static BOOL shm_remove_share_oplock(int fnum, int token)
531 smb_shm_offset_t *mode_array;
532 unsigned int hash_entry;
533 share_mode_record *file_scanner_p;
534 share_mode_record *file_prev_p;
535 shm_share_mode_entry *entry_scanner_p;
536 shm_share_mode_entry *entry_prev_p;
540 dev = Files[fnum].fd_ptr->dev;
541 inode = Files[fnum].fd_ptr->inode;
543 hash_entry = HASH_ENTRY(dev, inode);
545 if(hash_entry > lp_shmem_hash_size() )
548 ("PANIC ERROR:remove_share_oplock (FAST_SHARE_MODES): hash_entry %d too large \
550 hash_entry, lp_shmem_hash_size() ));
554 mode_array = (smb_shm_offset_t *)smb_shm_offset2addr(smb_shm_get_userdef_off());
556 if(mode_array[hash_entry] == NULL_OFFSET)
558 DEBUG(0,("PANIC ERROR:remove_share_oplock (FAST_SHARE_MODES): hash bucket %d empty\n",
563 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(mode_array[hash_entry]);
564 file_prev_p = file_scanner_p;
566 while(file_scanner_p)
568 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
575 file_prev_p = file_scanner_p ;
576 file_scanner_p = (share_mode_record *)
577 smb_shm_offset2addr(file_scanner_p->next_offset);
583 DEBUG(0,("ERROR:remove_share_oplock (FAST_SHARE_MODES): no entry found for dev %d, \
584 inode %d in hash bucket %d\n", dev, inode, hash_entry));
588 if(file_scanner_p->locking_version != LOCKING_VERSION)
590 DEBUG(0,("ERROR: remove_share_oplock (FAST_SHARE_MODES): Deleting old share mode \
591 record due to old locking version %d for file dev %d, inode %d hash bucket %d\n",
592 file_scanner_p->locking_version, dev, inode, hash_entry ));
593 if(file_prev_p == file_scanner_p)
594 mode_array[hash_entry] = file_scanner_p->next_offset;
596 file_prev_p->next_offset = file_scanner_p->next_offset;
597 smb_shm_free(smb_shm_addr2offset(file_scanner_p));
602 entry_scanner_p = (shm_share_mode_entry*)smb_shm_offset2addr(
603 file_scanner_p->share_mode_entries);
604 entry_prev_p = entry_scanner_p;
605 while(entry_scanner_p)
607 if( (pid == entry_scanner_p->e.pid) &&
608 (entry_scanner_p->e.share_mode == Files[fnum].share_mode) &&
609 (memcmp(&entry_scanner_p->e.time,
610 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
612 /* Delete the oplock info. */
613 entry_scanner_p->e.op_port = 0;
614 entry_scanner_p->e.op_type = 0;
620 entry_prev_p = entry_scanner_p;
621 entry_scanner_p = (shm_share_mode_entry *)
622 smb_shm_offset2addr(entry_scanner_p->next_share_mode_entry);
628 DEBUG(0,("ERROR: remove_share_oplock (FAST_SHARE_MODES): No oplock granted share \
629 mode record found dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
637 /*******************************************************************
638 call the specified function on each entry under management by the
640 ********************************************************************/
641 static int shm_share_forall(void (*fn)(share_mode_entry *, char *))
644 smb_shm_offset_t *mode_array;
645 share_mode_record *file_scanner_p;
647 mode_array = (smb_shm_offset_t *)smb_shm_offset2addr(smb_shm_get_userdef_off());
649 for( i = 0; i < lp_shmem_hash_size(); i++) {
650 smb_shm_lock_hash_entry(i);
651 if(mode_array[i] == NULL_OFFSET) {
652 smb_shm_unlock_hash_entry(i);
656 file_scanner_p = (share_mode_record *)smb_shm_offset2addr(mode_array[i]);
657 while((file_scanner_p != 0) &&
658 (file_scanner_p->num_share_mode_entries != 0)) {
659 shm_share_mode_entry *entry_scanner_p =
660 (shm_share_mode_entry *)
661 smb_shm_offset2addr(file_scanner_p->share_mode_entries);
663 while(entry_scanner_p != 0) {
665 fn(&entry_scanner_p->e,
666 file_scanner_p->file_name);
669 (shm_share_mode_entry *)
671 entry_scanner_p->next_share_mode_entry);
673 } /* end while entry_scanner_p */
674 file_scanner_p = (share_mode_record *)
675 smb_shm_offset2addr(file_scanner_p->next_offset);
676 } /* end while file_scanner_p */
677 smb_shm_unlock_hash_entry(i);
684 /*******************************************************************
685 dump the state of the system
686 ********************************************************************/
687 static void shm_share_status(FILE *f)
689 int bytes_free, bytes_used, bytes_overhead, bytes_total;
691 smb_shm_get_usage(&bytes_free, &bytes_used, &bytes_overhead);
692 bytes_total = bytes_free + bytes_used + bytes_overhead;
694 fprintf(f, "Share mode memory usage (bytes):\n");
695 fprintf(f, " %d(%d%%) free + %d(%d%%) used + %d(%d%%) overhead = %d(100%%) total\n",
696 bytes_free, (bytes_free * 100)/bytes_total,
697 bytes_used, (bytes_used * 100)/bytes_total,
698 bytes_overhead, (bytes_overhead * 100)/bytes_total,
703 static struct share_ops share_ops = {
704 shm_stop_share_mode_mgmt,
705 shm_lock_share_entry,
706 shm_unlock_share_entry,
710 shm_remove_share_oplock,
715 /*******************************************************************
716 initialize the shared memory for share_mode management
717 ******************************************************************/
718 struct share_ops *locking_shm_init(void)
720 pstring shmem_file_name;
722 pstrcpy(shmem_file_name,lp_lockdir());
723 if (!directory_exist(shmem_file_name,NULL))
724 mkdir(shmem_file_name,0755);
725 trim_string(shmem_file_name,"","/");
726 if (!*shmem_file_name) return(False);
727 strcat(shmem_file_name, "/SHARE_MEM_FILE");
728 if (smb_shm_open(shmem_file_name, lp_shmem_size()))
734 int locking_shm_dummy_procedure(void)