Annotation of sys/kern/init_main.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: init_main.c,v 1.143 2007/07/25 23:11:52 art Exp $ */
2: /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */
3:
4: /*
5: * Copyright (c) 1995 Christopher G. Demetriou. All rights reserved.
6: * Copyright (c) 1982, 1986, 1989, 1991, 1992, 1993
7: * The Regents of the University of California. All rights reserved.
8: * (c) UNIX System Laboratories, Inc.
9: * All or some portions of this file are derived from material licensed
10: * to the University of California by American Telephone and Telegraph
11: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
12: * the permission of UNIX System Laboratories, Inc.
13: *
14: * Redistribution and use in source and binary forms, with or without
15: * modification, are permitted provided that the following conditions
16: * are met:
17: * 1. Redistributions of source code must retain the above copyright
18: * notice, this list of conditions and the following disclaimer.
19: * 2. Redistributions in binary form must reproduce the above copyright
20: * notice, this list of conditions and the following disclaimer in the
21: * documentation and/or other materials provided with the distribution.
22: * 3. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: *
38: * @(#)init_main.c 8.9 (Berkeley) 1/21/94
39: */
40:
41: #include <sys/param.h>
42: #include <sys/filedesc.h>
43: #include <sys/file.h>
44: #include <sys/errno.h>
45: #include <sys/exec.h>
46: #include <sys/kernel.h>
47: #include <sys/kthread.h>
48: #include <sys/mount.h>
49: #include <sys/proc.h>
50: #include <sys/resourcevar.h>
51: #include <sys/signalvar.h>
52: #include <sys/systm.h>
53: #include <sys/namei.h>
54: #include <sys/vnode.h>
55: #include <sys/tty.h>
56: #include <sys/conf.h>
57: #include <sys/buf.h>
58: #include <sys/device.h>
59: #include <sys/socketvar.h>
60: #include <sys/lockf.h>
61: #include <sys/protosw.h>
62: #include <sys/reboot.h>
63: #include <sys/user.h>
64: #ifdef SYSVSHM
65: #include <sys/shm.h>
66: #endif
67: #ifdef SYSVSEM
68: #include <sys/sem.h>
69: #endif
70: #ifdef SYSVMSG
71: #include <sys/msg.h>
72: #endif
73: #include <sys/domain.h>
74: #include <sys/mbuf.h>
75: #include <sys/pipe.h>
76: #include <sys/workq.h>
77:
78: #include <sys/syscall.h>
79: #include <sys/syscallargs.h>
80:
81: #include <dev/rndvar.h>
82:
83: #include <ufs/ufs/quota.h>
84:
85: #include <machine/cpu.h>
86:
87: #include <uvm/uvm.h>
88:
89: #include <net/if.h>
90: #include <net/raw_cb.h>
91:
92: #if defined(CRYPTO)
93: #include <crypto/cryptodev.h>
94: #include <crypto/cryptosoft.h>
95: #endif
96:
97: #if defined(NFSSERVER) || defined(NFSCLIENT)
98: extern void nfs_init(void);
99: #endif
100:
101: #include "softraid.h"
102:
103: const char copyright[] =
104: "Copyright (c) 1982, 1986, 1989, 1991, 1993\n"
105: "\tThe Regents of the University of California. All rights reserved.\n"
106: "Copyright (c) 1995-2007 OpenBSD. All rights reserved. http://www.OpenBSD.org\n";
107:
108: /* Components of the first process -- never freed. */
109: struct session session0;
110: struct pgrp pgrp0;
111: struct proc proc0;
112: struct process process0;
113: struct pcred cred0;
114: struct plimit limit0;
115: struct vmspace vmspace0;
116: struct sigacts sigacts0;
117: struct proc *initproc;
118:
119: int cmask = CMASK;
120: extern struct user *proc0paddr;
121:
122: struct vnode *rootvp, *swapdev_vp;
123: int boothowto;
124: struct timeval boottime;
125: int ncpus = 1;
126: __volatile int start_init_exec; /* semaphore for start_init() */
127:
128: #if !defined(NO_PROPOLICE)
129: long __guard[8];
130: #endif
131:
132: /* XXX return int so gcc -Werror won't complain */
133: int main(void *);
134: void check_console(struct proc *);
135: void start_init(void *);
136: void start_cleaner(void *);
137: void start_update(void *);
138: void start_reaper(void *);
139: void start_crypto(void *);
140: void init_exec(void);
141: void kqueue_init(void);
142: void workq_init(void);
143:
144: extern char sigcode[], esigcode[];
145: #ifdef SYSCALL_DEBUG
146: extern char *syscallnames[];
147: #endif
148:
149: struct emul emul_native = {
150: "native",
151: NULL,
152: sendsig,
153: SYS_syscall,
154: SYS_MAXSYSCALL,
155: sysent,
156: #ifdef SYSCALL_DEBUG
157: syscallnames,
158: #else
159: NULL,
160: #endif
161: 0,
162: copyargs,
163: setregs,
164: NULL,
165: sigcode,
166: esigcode,
167: EMUL_ENABLED | EMUL_NATIVE,
168: };
169:
170: /*
171: * System startup; initialize the world, create process 0, mount root
172: * filesystem, and fork to create init and pagedaemon. Most of the
173: * hard work is done in the lower-level initialization routines including
174: * startup(), which does memory initialization and autoconfiguration.
175: */
176: /* XXX return int, so gcc -Werror won't complain */
177: int
178: main(void *framep)
179: {
180: struct proc *p;
181: struct pdevinit *pdev;
182: struct timeval rtv;
183: quad_t lim;
184: int s, i;
185: extern struct pdevinit pdevinit[];
186: extern void scheduler_start(void);
187: extern void disk_init(void);
188: extern void endtsleep(void *);
189: extern void realitexpire(void *);
190:
191: /*
192: * Initialize the current process pointer (curproc) before
193: * any possible traps/probes to simplify trap processing.
194: */
195: curproc = p = &proc0;
196: p->p_cpu = curcpu();
197:
198: /*
199: * Initialize timeouts.
200: */
201: timeout_startup();
202:
203: /*
204: * Attempt to find console and initialize
205: * in case of early panic or other messages.
206: */
207: config_init(); /* init autoconfiguration data structures */
208: consinit();
209:
210: printf("%s\n", copyright);
211:
212: KERNEL_LOCK_INIT();
213:
214: uvm_init();
215: disk_init(); /* must come before autoconfiguration */
216: tty_init(); /* initialise tty's */
217: cpu_startup();
218:
219: /*
220: * Initialize mbuf's. Do this now because we might attempt to
221: * allocate mbufs or mbuf clusters during autoconfiguration.
222: */
223: mbinit();
224:
225: /* Initialize sockets. */
226: soinit();
227:
228: /*
229: * Initialize process and pgrp structures.
230: */
231: procinit();
232:
233: /* Initialize file locking. */
234: lf_init();
235:
236: /*
237: * Initialize filedescriptors.
238: */
239: filedesc_init();
240:
241: /*
242: * Initialize pipes.
243: */
244: pipe_init();
245:
246: /*
247: * Initialize kqueues.
248: */
249: kqueue_init();
250:
251: /*
252: * Create process 0 (the swapper).
253: */
254:
255: process0.ps_mainproc = p;
256: TAILQ_INIT(&process0.ps_threads);
257: TAILQ_INSERT_TAIL(&process0.ps_threads, p, p_thr_link);
258: p->p_p = &process0;
259:
260: LIST_INSERT_HEAD(&allproc, p, p_list);
261: p->p_pgrp = &pgrp0;
262: LIST_INSERT_HEAD(PIDHASH(0), p, p_hash);
263: LIST_INSERT_HEAD(PGRPHASH(0), &pgrp0, pg_hash);
264: LIST_INIT(&pgrp0.pg_members);
265: LIST_INSERT_HEAD(&pgrp0.pg_members, p, p_pglist);
266:
267: pgrp0.pg_session = &session0;
268: session0.s_count = 1;
269: session0.s_leader = p;
270:
271: atomic_setbits_int(&p->p_flag, P_SYSTEM | P_NOCLDWAIT);
272: p->p_stat = SONPROC;
273: p->p_nice = NZERO;
274: p->p_emul = &emul_native;
275: bcopy("swapper", p->p_comm, sizeof ("swapper"));
276:
277: /* Init timeouts. */
278: timeout_set(&p->p_sleep_to, endtsleep, p);
279: timeout_set(&p->p_realit_to, realitexpire, p);
280:
281: /* Create credentials. */
282: cred0.p_refcnt = 1;
283: p->p_cred = &cred0;
284: p->p_ucred = crget();
285: p->p_ucred->cr_ngroups = 1; /* group 0 */
286:
287: /* Initialize signal state for process 0. */
288: signal_init();
289: p->p_sigacts = &sigacts0;
290: siginit(p);
291:
292: /* Create the file descriptor table. */
293: p->p_fd = fdinit(NULL);
294:
295: /* Create the limits structures. */
296: p->p_p->ps_limit = &limit0;
297: for (i = 0; i < sizeof(p->p_rlimit)/sizeof(p->p_rlimit[0]); i++)
298: limit0.pl_rlimit[i].rlim_cur =
299: limit0.pl_rlimit[i].rlim_max = RLIM_INFINITY;
300: limit0.pl_rlimit[RLIMIT_NOFILE].rlim_cur = NOFILE;
301: limit0.pl_rlimit[RLIMIT_NOFILE].rlim_max = MIN(NOFILE_MAX,
302: (maxfiles - NOFILE > NOFILE) ? maxfiles - NOFILE : NOFILE);
303: limit0.pl_rlimit[RLIMIT_NPROC].rlim_cur = MAXUPRC;
304: lim = ptoa(uvmexp.free);
305: limit0.pl_rlimit[RLIMIT_RSS].rlim_max = lim;
306: limit0.pl_rlimit[RLIMIT_MEMLOCK].rlim_max = lim;
307: limit0.pl_rlimit[RLIMIT_MEMLOCK].rlim_cur = lim / 3;
308: limit0.p_refcnt = 1;
309:
310: /* Allocate a prototype map so we have something to fork. */
311: uvmspace_init(&vmspace0, pmap_kernel(), round_page(VM_MIN_ADDRESS),
312: trunc_page(VM_MAX_ADDRESS), TRUE);
313: p->p_vmspace = &vmspace0;
314:
315: p->p_addr = proc0paddr; /* XXX */
316:
317: /*
318: * We continue to place resource usage info in the
319: * user struct so they're pageable.
320: */
321: p->p_stats = &p->p_addr->u_stats;
322:
323: /*
324: * Charge root for one process.
325: */
326: (void)chgproccnt(0, 1);
327:
328: /* Initialize run queues */
329: rqinit();
330:
331: /* Initialize work queues */
332: workq_init();
333:
334: /* Configure the devices */
335: cpu_configure();
336:
337: /* Configure virtual memory system, set vm rlimits. */
338: uvm_init_limits(p);
339:
340: /* Initialize the file systems. */
341: #if defined(NFSSERVER) || defined(NFSCLIENT)
342: nfs_init(); /* initialize server/shared data */
343: #endif
344: vfsinit();
345:
346: /* Start real time and statistics clocks. */
347: initclocks();
348:
349: /* Lock the kernel on behalf of proc0. */
350: KERNEL_PROC_LOCK(p);
351:
352: #ifdef SYSVSHM
353: /* Initialize System V style shared memory. */
354: shminit();
355: #endif
356:
357: #ifdef SYSVSEM
358: /* Initialize System V style semaphores. */
359: seminit();
360: #endif
361:
362: #ifdef SYSVMSG
363: /* Initialize System V style message queues. */
364: msginit();
365: #endif
366:
367: /* Attach pseudo-devices. */
368: randomattach();
369: for (pdev = pdevinit; pdev->pdev_attach != NULL; pdev++)
370: if (pdev->pdev_count > 0)
371: (*pdev->pdev_attach)(pdev->pdev_count);
372:
373: #ifdef CRYPTO
374: swcr_init();
375: #endif /* CRYPTO */
376:
377: /*
378: * Initialize protocols. Block reception of incoming packets
379: * until everything is ready.
380: */
381: s = splnet();
382: ifinit();
383: domaininit();
384: if_attachdomain();
385: splx(s);
386:
387: #ifdef GPROF
388: /* Initialize kernel profiling. */
389: kmstartup();
390: #endif
391:
392: #if !defined(NO_PROPOLICE)
393: {
394: volatile long newguard[8];
395: int i;
396:
397: arc4random_bytes((long *)newguard, sizeof(newguard));
398:
399: for (i = sizeof(__guard)/sizeof(__guard[0]) - 1; i; i--)
400: __guard[i] = newguard[i];
401: }
402: #endif
403:
404: /* init exec and emul */
405: init_exec();
406:
407: /* Start the scheduler */
408: scheduler_start();
409:
410: /*
411: * Create process 1 (init(8)). We do this now, as Unix has
412: * historically had init be process 1, and changing this would
413: * probably upset a lot of people.
414: *
415: * Note that process 1 won't immediately exec init(8), but will
416: * wait for us to inform it that the root file system has been
417: * mounted.
418: */
419: if (fork1(p, SIGCHLD, FORK_FORK, NULL, 0, start_init, NULL, NULL,
420: &initproc))
421: panic("fork init");
422:
423: /*
424: * Create any kernel threads whose creation was deferred because
425: * initproc had not yet been created.
426: */
427: kthread_run_deferred_queue();
428:
429: /*
430: * Now that device driver threads have been created, wait for
431: * them to finish any deferred autoconfiguration. Note we don't
432: * need to lock this semaphore, since we haven't booted any
433: * secondary processors, yet.
434: */
435: while (config_pending)
436: (void) tsleep((void *)&config_pending, PWAIT, "cfpend", 0);
437:
438: dostartuphooks();
439:
440: #if NSOFTRAID > 0
441: config_rootfound("softraid", NULL);
442: #endif
443:
444: /* Configure root/swap devices */
445: diskconf();
446:
447: /* Mount the root file system. */
448: if (vfs_mountroot())
449: panic("cannot mount root");
450: CIRCLEQ_FIRST(&mountlist)->mnt_flag |= MNT_ROOTFS;
451:
452: /* Get the vnode for '/'. Set p->p_fd->fd_cdir to reference it. */
453: if (VFS_ROOT(CIRCLEQ_FIRST(&mountlist), &rootvnode))
454: panic("cannot find root vnode");
455: p->p_fd->fd_cdir = rootvnode;
456: VREF(p->p_fd->fd_cdir);
457: VOP_UNLOCK(rootvnode, 0, p);
458: p->p_fd->fd_rdir = NULL;
459:
460: /*
461: * Now that root is mounted, we can fixup initproc's CWD
462: * info. All other processes are kthreads, which merely
463: * share proc0's CWD info.
464: */
465: initproc->p_fd->fd_cdir = rootvnode;
466: VREF(initproc->p_fd->fd_cdir);
467: initproc->p_fd->fd_rdir = NULL;
468:
469: /*
470: * Now can look at time, having had a chance to verify the time
471: * from the file system. Reset p->p_rtime as it may have been
472: * munched in mi_switch() after the time got set.
473: */
474: #ifdef __HAVE_TIMECOUNTER
475: microtime(&boottime);
476: #else
477: boottime = mono_time = time;
478: #endif
479: LIST_FOREACH(p, &allproc, p_list) {
480: p->p_stats->p_start = boottime;
481: microuptime(&p->p_cpu->ci_schedstate.spc_runtime);
482: p->p_rtime.tv_sec = p->p_rtime.tv_usec = 0;
483: }
484:
485: uvm_swap_init();
486:
487: /* Create the pageout daemon kernel thread. */
488: if (kthread_create(uvm_pageout, NULL, NULL, "pagedaemon"))
489: panic("fork pagedaemon");
490:
491: /* Create the reaper daemon kernel thread. */
492: if (kthread_create(start_reaper, NULL, NULL, "reaper"))
493: panic("fork reaper");
494:
495: /* Create the cleaner daemon kernel thread. */
496: if (kthread_create(start_cleaner, NULL, NULL, "cleaner"))
497: panic("fork cleaner");
498:
499: /* Create the update daemon kernel thread. */
500: if (kthread_create(start_update, NULL, NULL, "update"))
501: panic("fork update");
502:
503: /* Create the aiodone daemon kernel thread. */
504: if (kthread_create(uvm_aiodone_daemon, NULL, NULL, "aiodoned"))
505: panic("fork aiodoned");
506:
507: #ifdef CRYPTO
508: /* Create the crypto kernel thread. */
509: if (kthread_create(start_crypto, NULL, NULL, "crypto"))
510: panic("crypto thread");
511: #endif /* CRYPTO */
512:
513: microtime(&rtv);
514: srandom((u_long)(rtv.tv_sec ^ rtv.tv_usec));
515:
516: randompid = 1;
517:
518: #if defined(MULTIPROCESSOR)
519: /* Boot the secondary processors. */
520: cpu_boot_secondary_processors();
521: #endif
522:
523: domountroothooks();
524:
525: /*
526: * Okay, now we can let init(8) exec! It's off to userland!
527: */
528: start_init_exec = 1;
529: wakeup((void *)&start_init_exec);
530:
531: /* The scheduler is an infinite loop. */
532: uvm_scheduler();
533: /* NOTREACHED */
534: }
535:
536: /*
537: * List of paths to try when searching for "init".
538: */
539: static char *initpaths[] = {
540: "/sbin/init",
541: "/sbin/oinit",
542: "/sbin/init.bak",
543: NULL,
544: };
545:
546: void
547: check_console(struct proc *p)
548: {
549: struct nameidata nd;
550: int error;
551:
552: NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, "/dev/console", p);
553: error = namei(&nd);
554: if (error) {
555: if (error == ENOENT)
556: printf("warning: /dev/console does not exist\n");
557: else
558: printf("warning: /dev/console error %d\n", error);
559: } else
560: vrele(nd.ni_vp);
561: }
562:
563: /*
564: * Start the initial user process; try exec'ing each pathname in "initpaths".
565: * The program is invoked with one argument containing the boot flags.
566: */
567: void
568: start_init(void *arg)
569: {
570: struct proc *p = arg;
571: vaddr_t addr;
572: struct sys_execve_args /* {
573: syscallarg(const char *) path;
574: syscallarg(char *const *) argp;
575: syscallarg(char *const *) envp;
576: } */ args;
577: int options, error;
578: long i;
579: register_t retval[2];
580: char flags[4], *flagsp;
581: char **pathp, *path, *ucp, **uap, *arg0, *arg1 = NULL;
582:
583: /*
584: * Now in process 1.
585: */
586:
587: /*
588: * Wait for main() to tell us that it's safe to exec.
589: */
590: while (start_init_exec == 0)
591: (void) tsleep((void *)&start_init_exec, PWAIT, "initexec", 0);
592:
593: check_console(p);
594:
595: /*
596: * Need just enough stack to hold the faked-up "execve()" arguments.
597: */
598: #ifdef MACHINE_STACK_GROWS_UP
599: addr = USRSTACK;
600: #else
601: addr = USRSTACK - PAGE_SIZE;
602: #endif
603: if (uvm_map(&p->p_vmspace->vm_map, &addr, PAGE_SIZE,
604: NULL, UVM_UNKNOWN_OFFSET, 0,
605: UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_ALL, UVM_INH_COPY,
606: UVM_ADV_NORMAL, UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW)))
607: panic("init: couldn't allocate argument space");
608: p->p_vmspace->vm_maxsaddr = (caddr_t)addr;
609:
610: for (pathp = &initpaths[0]; (path = *pathp) != NULL; pathp++) {
611: #ifdef MACHINE_STACK_GROWS_UP
612: ucp = (char *)addr;
613: #else
614: ucp = (char *)(addr + PAGE_SIZE);
615: #endif
616: /*
617: * Construct the boot flag argument.
618: */
619: flagsp = flags;
620: *flagsp++ = '-';
621: options = 0;
622:
623: if (boothowto & RB_SINGLE) {
624: *flagsp++ = 's';
625: options = 1;
626: }
627: #ifdef notyet
628: if (boothowto & RB_FASTBOOT) {
629: *flagsp++ = 'f';
630: options = 1;
631: }
632: #endif
633:
634: /*
635: * Move out the flags (arg 1), if necessary.
636: */
637: if (options != 0) {
638: *flagsp++ = '\0';
639: i = flagsp - flags;
640: #ifdef DEBUG
641: printf("init: copying out flags `%s' %d\n", flags, i);
642: #endif
643: #ifdef MACHINE_STACK_GROWS_UP
644: arg1 = ucp;
645: (void)copyout((caddr_t)flags, (caddr_t)ucp, i);
646: ucp += i;
647: #else
648: (void)copyout((caddr_t)flags, (caddr_t)(ucp -= i), i);
649: arg1 = ucp;
650: #endif
651: }
652:
653: /*
654: * Move out the file name (also arg 0).
655: */
656: i = strlen(path) + 1;
657: #ifdef DEBUG
658: printf("init: copying out path `%s' %d\n", path, i);
659: #endif
660: #ifdef MACHINE_STACK_GROWS_UP
661: arg0 = ucp;
662: (void)copyout((caddr_t)path, (caddr_t)ucp, i);
663: ucp += i;
664: ucp = (caddr_t)ALIGN((u_long)ucp);
665: uap = (char **)ucp + 3;
666: #else
667: (void)copyout((caddr_t)path, (caddr_t)(ucp -= i), i);
668: arg0 = ucp;
669: uap = (char **)((u_long)ucp & ~ALIGNBYTES);
670: #endif
671:
672: /*
673: * Move out the arg pointers.
674: */
675: i = 0;
676: copyout(&i, (caddr_t)--uap, sizeof(register_t)); /* terminator */
677: if (options != 0)
678: copyout(&arg1, (caddr_t)--uap, sizeof(register_t));
679: copyout(&arg0, (caddr_t)--uap, sizeof(register_t));
680:
681: /*
682: * Point at the arguments.
683: */
684: SCARG(&args, path) = arg0;
685: SCARG(&args, argp) = uap;
686: SCARG(&args, envp) = NULL;
687:
688: /*
689: * Now try to exec the program. If can't for any reason
690: * other than it doesn't exist, complain.
691: */
692: if ((error = sys_execve(p, &args, retval)) == 0) {
693: KERNEL_PROC_UNLOCK(p);
694: return;
695: }
696: if (error != ENOENT)
697: printf("exec %s: error %d\n", path, error);
698: }
699: printf("init: not found\n");
700: panic("no init");
701: }
702:
703: void
704: start_update(void *arg)
705: {
706: sched_sync(curproc);
707: /* NOTREACHED */
708: }
709:
710: void
711: start_cleaner(void *arg)
712: {
713: buf_daemon(curproc);
714: /* NOTREACHED */
715: }
716:
717: void
718: start_reaper(void *arg)
719: {
720: reaper();
721: /* NOTREACHED */
722: }
723:
724: #ifdef CRYPTO
725: void
726: start_crypto(void *arg)
727: {
728: crypto_thread();
729: /* NOTREACHED */
730: }
731: #endif /* CRYPTO */
CVSweb