diff options
author | des <des@FreeBSD.org> | 2010-03-04 13:35:57 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2010-03-04 13:35:57 +0000 |
commit | 834fb25a9ed2240101506d137b5be7d71c75f306 (patch) | |
tree | 4002c72cd1ed11909f7640bea343988cfcf63c5b /lib/libc | |
parent | 98b742f57cafbed05c101e60cc131f5980f044d0 (diff) | |
parent | 787cf8d03f1c58ada088933408f30fd63de85bf2 (diff) | |
download | FreeBSD-src-834fb25a9ed2240101506d137b5be7d71c75f306.zip FreeBSD-src-834fb25a9ed2240101506d137b5be7d71c75f306.tar.gz |
IFH@204581
Diffstat (limited to 'lib/libc')
192 files changed, 7849 insertions, 7021 deletions
diff --git a/lib/libc/Makefile b/lib/libc/Makefile index 4f13f8e..b58b6cb 100644 --- a/lib/libc/Makefile +++ b/lib/libc/Makefile @@ -64,7 +64,7 @@ NOASM= .include "${.CURDIR}/rpc/Makefile.inc" .include "${.CURDIR}/uuid/Makefile.inc" .include "${.CURDIR}/xdr/Makefile.inc" -.if ${MACHINE_ARCH} == "arm" +.if ${MACHINE_ARCH} == "arm" || ${MACHINE_ARCH} == "mips" .include "${.CURDIR}/softfloat/Makefile.inc" .endif .if ${MK_NIS} != "no" diff --git a/lib/libc/compat-43/Makefile.inc b/lib/libc/compat-43/Makefile.inc index 836f0a6..8505ff2 100644 --- a/lib/libc/compat-43/Makefile.inc +++ b/lib/libc/compat-43/Makefile.inc @@ -13,6 +13,11 @@ MAN+= creat.2 killpg.2 sigpause.2 sigsetmask.2 sigvec.2 MAN+= gethostid.3 setruid.3 MLINKS+=gethostid.3 sethostid.3 +MLINKS+=sigpause.2 sighold.2 +MLINKS+=sigpause.2 sigignore.2 +MLINKS+=sigpause.2 sigrelse.2 +MLINKS+=sigpause.2 sigset.2 +MLINKS+=sigpause.2 xsi_sigpause.2 MLINKS+=setruid.3 setrgid.3 MLINKS+=sigsetmask.2 sigblock.2 diff --git a/lib/libc/compat-43/Symbol.map b/lib/libc/compat-43/Symbol.map index e859a31..bd49f99 100644 --- a/lib/libc/compat-43/Symbol.map +++ b/lib/libc/compat-43/Symbol.map @@ -17,6 +17,14 @@ FBSD_1.0 { sigvec; }; +FBSD_1.2 { + sighold; + sigignore; + sigrelse; + sigset; + xsi_sigpause; +}; + FBSDprivate_1.0 { __creat; _creat; diff --git a/lib/libc/compat-43/sigcompat.c b/lib/libc/compat-43/sigcompat.c index 6280183..c3ba30a 100644 --- a/lib/libc/compat-43/sigcompat.c +++ b/lib/libc/compat-43/sigcompat.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include "namespace.h" #include <sys/param.h> #include <signal.h> +#include <string.h> #include "un-namespace.h" #include "libc_private.h" @@ -97,8 +98,7 @@ sigblock(mask) } int -sigpause(mask) - int mask; +sigpause(int mask) { sigset_t set; @@ -106,3 +106,84 @@ sigpause(mask) set.__bits[0] = mask; return (_sigsuspend(&set)); } + +int +xsi_sigpause(int sig) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + return (_sigsuspend(&set)); +} + +int +sighold(int sig) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + return (_sigprocmask(SIG_BLOCK, &set, NULL)); +} + +int +sigignore(int sig) +{ + struct sigaction sa; + + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + return (_sigaction(sig, &sa, NULL)); +} + +int +sigrelse(int sig) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + return (_sigprocmask(SIG_UNBLOCK, &set, NULL)); +} + +void +(*sigset(int sig, void (*disp)(int)))(int) +{ + sigset_t set, pset; + struct sigaction sa, psa; + int error; + + sigemptyset(&set); + sigaddset(&set, sig); + error = _sigprocmask(SIG_BLOCK, NULL, &pset); + if (error == -1) + return (SIG_ERR); + if ((__sighandler_t *)disp == SIG_HOLD) { + error = _sigprocmask(SIG_BLOCK, &set, &pset); + if (error == -1) + return (SIG_ERR); + if (sigismember(&pset, sig)) + return (SIG_HOLD); + else { + error = _sigaction(sig, NULL, &psa); + if (error == -1) + return (SIG_ERR); + return (psa.sa_handler); + } + } else { + error = _sigprocmask(SIG_UNBLOCK, &set, &pset); + if (error == -1) + return (SIG_ERR); + } + + bzero(&sa, sizeof(sa)); + sa.sa_handler = disp; + error = _sigaction(sig, &sa, &psa); + if (error == -1) + return (SIG_ERR); + if (sigismember(&pset, sig)) + return (SIG_HOLD); + else + return (psa.sa_handler); +} diff --git a/lib/libc/compat-43/sigpause.2 b/lib/libc/compat-43/sigpause.2 index 39edb0b..bf3cf0b 100644 --- a/lib/libc/compat-43/sigpause.2 +++ b/lib/libc/compat-43/sigpause.2 @@ -28,21 +28,118 @@ .\" @(#)sigpause.2 8.1 (Berkeley) 6/2/93 .\" $FreeBSD$ .\" +.\" Part of the content of the man page was derived from +.\" The Open Group Base Specifications Issue 7 +.\" IEEE Std 1003.1-2008 +.\" .Dd June 2, 1993 .Dt SIGPAUSE 2 .Os .Sh NAME -.Nm sigpause -.Nd atomically release blocked signals and wait for interrupt +.Nm sighold , +.Nm sigignore , +.Nm sigpause , +.Nm sigrelse , +.Nm sigset +.Nd legacy interface for signal management .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In signal.h .Ft int +.Fn sighold "int sig" +.Ft int +.Fn sigignore "int sig" +.Ft int +.Fn xsi_sigpause "int sigmask" +.Ft int +.Fn sigrelse "int sig" +.Ft void (*)(int) +.Fn sigset "int" "void (*disp)(int)" +.Ft int .Fn sigpause "int sigmask" .Sh DESCRIPTION .Sy This interface is made obsolete by -.Xr sigsuspend 2 . +.Xr sigsuspend 2 +.Sy and +.Xr sigaction 2 +.Pp +The +.Fn sigset +function modifies signal dispositions. +The +.Fa sig +argument specifies the signal, which may be any signal except +.Dv SIGKILL +and +.Dv SIGSTOP . +The +.Fa disp +argument specifies the signal's disposition, +which may be +.Dv SIG_DFL , +.Dv SIG_IGN , +or the address of a signal handler. +If +.Fn sigset +is used, and +.Fa disp +is the address of a signal handler, the +system adds +.Fa sig +to the signal mask of the calling process before executing the signal +handler; when the signal handler returns, the system restores the +signal mask of the calling process to its state prior to the delivery +of the signal. +In addition, if +.Fn sigset +is used, and +.Fa disp +is equal to +.Dv SIG_HOLD , +.Fa sig +is added to the signal +mask of the calling process and +.Fa sig 's +disposition remains unchanged. +If +.Fn sigset +is used, and +.Fa disp +is not equal to +.Dv SIG_HOLD , +.Fa sig +is removed from the signal mask of the calling process. +.Pp +The +.Fn sighold +function adds +.Fa sig +to the signal mask of the calling process. +.Pp +The +.Fn sigrelse +function removes +.Fa sig +from the signal mask of the calling process. +.Pp +The +.Fn sigignore +function sets the disposition of +.Fa sig +to +.Dv SIG_IGN . +.Pp +The +.Fn xsi_sigpause +function removes +.Fa sig +from the signal mask of the calling process and suspend the calling process +until a signal is received. +The +.Fn xsi_sigpause +function restores the signal mask of the process to its original state before +returning. .Pp The .Fn sigpause @@ -57,13 +154,47 @@ The argument is usually 0 to indicate that no signals are to be blocked. +.Sh RETURN VALUES The .Fn sigpause -function -always terminates by being interrupted, returning -1 with +and +.Fn xsi_sigpause +functions +always terminate by being interrupted, returning -1 with .Va errno set to -.Er EINTR +.Er EINTR . +.Pp +Upon successful completion, +.Fn sigset +returns +.Dv SIG_HOLD +if the signal had been blocked and the signal's previous disposition if +it had not been blocked. +Otherwise, +.Dv SIG_ERR is returned and +.Va errno +set to indicate the error. +.Pp +For all other functions, upon successful completion, 0 is returned. +Otherwise, -1 is returned and +.Va errno +is set to indicate the error: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa sig +argument +is not a valid signal number. +.It Bq Er EINVAL +For +.Fn sigset +and +.Fn sigignore +functions, an attempt was made to catch or ignore +.Dv SIGKILL +or +.Dv SIGSTOP . .Sh SEE ALSO .Xr kill 2 , .Xr sigaction 2 , @@ -85,9 +216,26 @@ and was copied from there into the .Pq Tn XSI option of .St -p1003.1-2001 . +.Fx +implements it under the name +.Fn xsi_sigpause . +The +.Fn sighold , +.Fn sigignore , +.Fn sigrelse +and +.Fn sigset +functions are implemented for compatibility with +.Sy System V +and +.Sy XSI +interfaces. .Sh HISTORY The .Fn sigpause function appeared in .Bx 4.2 and has been deprecated. +All other functions appeared in +.Fx 9.0 +and were deprecated before being implemented. diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index b06f846..2f562da 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -5,7 +5,8 @@ .PATH: ${.CURDIR}/${MACHINE_ARCH}/gen ${.CURDIR}/gen SRCS+= __getosreldate.c __xuname.c \ - _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ + _once_stub.c _pthread_stubs.c _rand48.c _spinlock_stub.c \ + _thread_init.c \ alarm.c arc4random.c assert.c basename.c check_utility_compat.c \ clock.c closedir.c confstr.c \ crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \ @@ -17,21 +18,21 @@ SRCS+= __getosreldate.c __xuname.c \ gethostname.c getloadavg.c getlogin.c getmntinfo.c getnetgrent.c \ getosreldate.c getpagesize.c getpagesizes.c \ getpeereid.c getprogname.c getpwent.c getttyent.c \ - getusershell.c getvfsbyname.c glob.c \ + getusershell.c getutxent.c getvfsbyname.c glob.c \ initgroups.c isatty.c isinf.c isnan.c jrand48.c lcong48.c \ lockf.c lrand48.c mrand48.c nftw.c nice.c \ nlist.c nrand48.c opendir.c \ - pause.c pmadvise.c popen.c posix_spawn.c pselect.c \ - psignal.c pw_scan.c pwcache.c \ + pause.c pmadvise.c popen.c posix_spawn.c \ + psignal.c pututxline.c pw_scan.c pwcache.c \ raise.c readdir.c readpassphrase.c rewinddir.c \ - scandir.c seed48.c seekdir.c sem.c semctl.c \ + scandir.c seed48.c seekdir.c sem.c sem_new.c semctl.c \ setdomainname.c sethostname.c setjmperr.c setmode.c \ setproctitle.c setprogname.c siginterrupt.c siglist.c signal.c \ sigsetops.c sleep.c srand48.c statvfs.c stringlist.c strtofflags.c \ sysconf.c sysctl.c sysctlbyname.c sysctlnametomib.c \ syslog.c telldir.c termios.c time.c times.c timezone.c tls.c \ ttyname.c ttyslot.c ualarm.c ulimit.c uname.c unvis.c \ - usleep.c utime.c valloc.c vis.c wait.c wait3.c waitpid.c \ + usleep.c utime.c utxdb.c valloc.c vis.c wait.c wait3.c waitpid.c \ wordexp.c SYM_MAPS+=${.CURDIR}/gen/Symbol.map @@ -53,7 +54,7 @@ MAN+= alarm.3 arc4random.3 \ getgrent.3 getgrouplist.3 gethostname.3 getloadavg.3 \ getmntinfo.3 getnetgrent.3 getosreldate.3 getpagesize.3 \ getpagesizes.3 getpass.3 getpeereid.3 getprogname.3 getpwent.3 \ - getttyent.3 getusershell.3 getvfsbyname.3 \ + getttyent.3 getusershell.3 getutxent.3 getvfsbyname.3 \ glob.3 initgroups.3 isgreater.3 ldexp.3 lockf.3 makecontext.3 \ modf.3 \ nice.3 nlist.3 pause.3 popen.3 \ @@ -62,7 +63,7 @@ MAN+= alarm.3 arc4random.3 \ posix_spawnattr_getpgroup.3 posix_spawnattr_getschedparam.3 \ posix_spawnattr_getschedpolicy.3 posix_spawnattr_init.3 \ posix_spawnattr_getsigdefault.3 posix_spawnattr_getsigmask.3 \ - pselect.3 psignal.3 pwcache.3 \ + psignal.3 pwcache.3 \ raise.3 rand48.3 readpassphrase.3 rfork_thread.3 \ scandir.3 sem_destroy.3 sem_getvalue.3 sem_init.3 \ sem_open.3 sem_post.3 sem_timedwait.3 sem_wait.3 \ @@ -76,6 +77,7 @@ MAN+= alarm.3 arc4random.3 \ MLINKS+=arc4random.3 arc4random_addrandom.3 arc4random.3 arc4random_stir.3 \ arc4random.3 arc4random_buf.3 arc4random.3 arc4random_uniform.3 +MLINKS+=basename.3 basename_r.3 MLINKS+=ctermid.3 ctermid_r.3 MLINKS+=devname.3 devname_r.3 MLINKS+=devname.3 fdevname.3 @@ -124,6 +126,10 @@ MLINKS+=getttyent.3 endttyent.3 getttyent.3 getttynam.3 \ getttyent.3 isdialuptty.3 getttyent.3 isnettty.3 \ getttyent.3 setttyent.3 MLINKS+=getusershell.3 endusershell.3 getusershell.3 setusershell.3 +MLINKS+=getutxent.3 endutxent.3 getutxent.3 getutxid.3 \ + getutxent.3 getutxline.3 getutxent.3 getutxuser.3 \ + getutxent.3 pututxline.3 getutxent.3 setutxdb.3 \ + getutxent.3 setutxent.3 MLINKS+=glob.3 globfree.3 MLINKS+=isgreater.3 isgreaterequal.3 isgreater.3 isless.3 \ isgreater.3 islessequal.3 isgreater.3 islessgreater.3 \ @@ -171,7 +177,7 @@ MLINKS+=tcsetattr.3 cfgetispeed.3 tcsetattr.3 cfgetospeed.3 \ tcsetattr.3 cfmakeraw.3 tcsetattr.3 cfsetispeed.3 \ tcsetattr.3 cfsetospeed.3 tcsetattr.3 cfsetspeed.3 \ tcsetattr.3 tcgetattr.3 -MLINKS+=ttyname.3 isatty.3 ttyname.3 ttyname_r.3 ttyname.3 ttyslot.3 +MLINKS+=ttyname.3 isatty.3 ttyname.3 ttyname_r.3 MLINKS+=tzset.3 tzsetwall.3 MLINKS+=unvis.3 strunvis.3 unvis.3 strunvisx.3 MLINKS+=vis.3 strvis.3 vis.3 strvisx.3 diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index 197430a..42404bc 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -223,7 +223,6 @@ FBSD_1.0 { posix_madvise; popen; pclose; - pselect; psignal; raise; readdir; @@ -237,16 +236,6 @@ FBSD_1.0 { seekdir; user_from_uid; group_from_gid; - sem_init; - sem_destroy; - sem_open; - sem_close; - sem_unlink; - sem_wait; - sem_trywait; - sem_timedwait; - sem_post; - sem_getvalue; setdomainname; sethostname; longjmperror; @@ -283,7 +272,6 @@ FBSD_1.0 { openlog; closelog; setlogmask; - ttyslot; ttyname_r; ttyname; timezone; @@ -364,10 +352,31 @@ FBSD_1.1 { semctl; tcgetsid; tcsetsid; + __pthread_cleanup_pop_imp; + __pthread_cleanup_push_imp; }; FBSD_1.2 { + basename_r; + endutxent; getpagesizes; + getutxent; + getutxid; + getutxline; + getutxuser; + pututxline; + sem_close; + sem_destroy; + sem_getvalue; + sem_init; + sem_open; + sem_post; + sem_timedwait; + sem_trywait; + sem_unlink; + sem_wait; + setutxdb; + setutxent; }; FBSDprivate_1.0 { @@ -453,20 +462,9 @@ FBSDprivate_1.0 { __opendir2; __pause; _pause; - __pselect; __pw_scan; /* Used by (at least) libutil */ __raise; _raise; - __sem_init; - __sem_destroy; - __sem_open; - __sem_close; - __sem_unlink; - __sem_wait; - __sem_trywait; - __sem_timedwait; - __sem_post; - __sem_getvalue; __sleep; _sleep; _rtld_allocate_tls; @@ -483,4 +481,15 @@ FBSDprivate_1.0 { _wait; __waitpid; _waitpid; + + _libc_sem_init_compat; + _libc_sem_destroy_compat; + _libc_sem_open_compat; + _libc_sem_close_compat; + _libc_sem_unlink_compat; + _libc_sem_wait_compat; + _libc_sem_trywait_compat; + _libc_sem_timedwait_compat; + _libc_sem_post_compat; + _libc_sem_getvalue_compat; }; diff --git a/lib/libc/gen/_once_stub.c b/lib/libc/gen/_once_stub.c new file mode 100644 index 0000000..d2acc29 --- /dev/null +++ b/lib/libc/gen/_once_stub.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2009 Advanced Computing Technologies LLC + * Written by: John H. Baldwin <jhb@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <pthread.h> +#include "un-namespace.h" +#include "libc_private.h" + +/* This implements pthread_once() for the single-threaded case. */ +static int +_libc_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + + if (once_control->state == PTHREAD_DONE_INIT) + return (0); + init_routine(); + once_control->state = PTHREAD_DONE_INIT; + return (0); +} + +/* + * This is the internal interface provided to libc. It will use + * pthread_once() from the threading library in a multi-threaded + * process and _libc_once() for a single-threaded library. Because + * _libc_once() uses the same ABI for the values in the pthread_once_t + * structure as the threading library, it is safe for a process to + * switch from _libc_once() to pthread_once() when threading is + * enabled. + */ +int +_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + + if (__isthreaded) + return (_pthread_once(once_control, init_routine)); + return (_libc_once(once_control, init_routine)); +} diff --git a/lib/libc/gen/_pthread_stubs.c b/lib/libc/gen/_pthread_stubs.c index a3e55b0..044e299e 100644 --- a/lib/libc/gen/_pthread_stubs.c +++ b/lib/libc/gen/_pthread_stubs.c @@ -119,6 +119,8 @@ pthread_func_entry_t __thr_jtable[PJT_MAX] = { {PJT_DUAL_ENTRY(stub_zero)}, /* PJT_SETSPECIFIC */ {PJT_DUAL_ENTRY(stub_zero)}, /* PJT_SIGMASK */ {PJT_DUAL_ENTRY(stub_zero)}, /* PJT_TESTCANCEL */ + {PJT_DUAL_ENTRY(stub_zero)}, /* PJT_CLEANUP_POP_IMP */ + {PJT_DUAL_ENTRY(stub_zero)}, /* PJT_CLEANUP_PUSH_IMP */ }; /* @@ -222,7 +224,7 @@ STUB_FUNC1(pthread_mutex_trylock, PJT_MUTEX_TRYLOCK, int, void *) STUB_FUNC1(pthread_mutex_unlock, PJT_MUTEX_UNLOCK, int, void *) STUB_FUNC1(pthread_mutexattr_destroy, PJT_MUTEXATTR_DESTROY, int, void *) STUB_FUNC1(pthread_mutexattr_init, PJT_MUTEXATTR_INIT, int, void *) -STUB_FUNC1(pthread_mutexattr_settype, PJT_MUTEXATTR_SETTYPE, int, void *) +STUB_FUNC2(pthread_mutexattr_settype, PJT_MUTEXATTR_SETTYPE, int, void *, int) STUB_FUNC2(pthread_once, PJT_ONCE, int, void *, void *) STUB_FUNC1(pthread_rwlock_destroy, PJT_RWLOCK_DESTROY, int, void *) STUB_FUNC2(pthread_rwlock_init, PJT_RWLOCK_INIT, int, void *, void *) @@ -265,6 +267,8 @@ STUB_FUNC2(pthread_kill, PJT_KILL, int, void *, int) STUB_FUNC2(pthread_setcancelstate, PJT_SETCANCELSTATE, int, int, void *) STUB_FUNC2(pthread_setcanceltype, PJT_SETCANCELTYPE, int, int, void *) STUB_FUNC(pthread_testcancel, PJT_TESTCANCEL, void) +STUB_FUNC1(__pthread_cleanup_pop_imp, PJT_CLEANUP_POP_IMP, int, int) +STUB_FUNC2(__pthread_cleanup_push_imp, PJT_CLEANUP_PUSH_IMP, void, void*, void *); static int stub_zero(void) diff --git a/lib/libc/gen/basename.3 b/lib/libc/gen/basename.3 index 33a0a71..4a3743a 100644 --- a/lib/libc/gen/basename.3 +++ b/lib/libc/gen/basename.3 @@ -27,7 +27,7 @@ .\" $OpenBSD: basename.3,v 1.12 2000/04/18 03:01:25 aaron Exp $ .\" $FreeBSD$ .\" -.Dd October 12, 2006 +.Dd October 6, 2009 .Dt BASENAME 3 .Os .Sh NAME @@ -37,6 +37,8 @@ .In libgen.h .Ft char * .Fn basename "const char *path" +.Ft char * +.Fn basename_r "const char *path" "char *bname" .Sh DESCRIPTION The .Fn basename @@ -58,6 +60,12 @@ If is a null pointer or the empty string, a pointer to the string .Qq \&. is returned. +.Pp +The +.Fn basename_r +variation accepts a buffer of at least +.Dv MAXPATHLEN +bytes in which to store the resulting component. .Sh IMPLEMENTATION NOTES The .Fn basename @@ -65,15 +73,17 @@ function returns a pointer to internal storage space allocated on the first call that will be overwritten by subsequent calls. +.Fn basename_r +is therefore preferred for threaded applications. .Sh RETURN VALUES On successful completion, .Fn basename -returns a pointer to the last component of +and +.Fn basename_r +return pointers to the last component of .Fa path . .Pp -If -.Fn basename -fails, a null pointer is returned and the global variable +If they fail, a null pointer is returned and the global variable .Va errno is set to indicate the error. .Sh ERRORS diff --git a/lib/libc/gen/basename.c b/lib/libc/gen/basename.c index 9552ab3..9588c28 100644 --- a/lib/libc/gen/basename.c +++ b/lib/libc/gen/basename.c @@ -40,18 +40,12 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> char * -basename(path) +basename_r(path, bname) const char *path; + char *bname; { - static char *bname = NULL; const char *endp, *startp; - if (bname == NULL) { - bname = (char *)malloc(MAXPATHLEN); - if (bname == NULL) - return(NULL); - } - /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { (void)strcpy(bname, "."); @@ -82,3 +76,17 @@ basename(path) bname[endp - startp + 1] = '\0'; return(bname); } + +char * +basename(path) + const char *path; +{ + static char *bname = NULL; + + if (bname == NULL) { + bname = (char *)malloc(MAXPATHLEN); + if (bname == NULL) + return (NULL); + } + return (basename_r(path, bname)); +} diff --git a/lib/libc/gen/devname.c b/lib/libc/gen/devname.c index 21e46e6..65a690f 100644 --- a/lib/libc/gen/devname.c +++ b/lib/libc/gen/devname.c @@ -36,10 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/sysctl.h> -#include <err.h> -#include <fcntl.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> #include <sys/param.h> #include <sys/stat.h> @@ -49,22 +46,22 @@ devname_r(dev_t dev, mode_t type, char *buf, int len) { int i; size_t j; - char *r; - if ((type & S_IFMT) == S_IFCHR) { + if (dev == NODEV || !(S_ISCHR(type) || S_ISBLK(dev))) { + strlcpy(buf, "#NODEV", len); + return (buf); + } + + if (S_ISCHR(type)) { j = len; i = sysctlbyname("kern.devname", buf, &j, &dev, sizeof (dev)); if (i == 0) - return (buf); + return (buf); } /* Finally just format it */ - if (dev == NODEV) - r = "#NODEV"; - else - r = "#%c:%d:0x%x"; - snprintf(buf, len, r, - (type & S_IFMT) == S_IFCHR ? 'C' : 'B', major(dev), minor(dev)); + snprintf(buf, len, "#%c:%d:0x%x", + S_ISCHR(type) ? 'C' : 'B', major(dev), minor(dev)); return (buf); } @@ -73,5 +70,5 @@ devname(dev_t dev, mode_t type) { static char buf[SPECNAMELEN + 1]; - return(devname_r(dev, type, buf, sizeof(buf))); + return (devname_r(dev, type, buf, sizeof(buf))); } diff --git a/lib/libc/gen/errlst.c b/lib/libc/gen/errlst.c index bc3e0c9..db6bfa2 100644 --- a/lib/libc/gen/errlst.c +++ b/lib/libc/gen/errlst.c @@ -150,5 +150,6 @@ const char *const sys_errlist[] = { "Multihop attempted", /* 90 - EMULTIHOP */ "Link has been severed", /* 91 - ENOLINK */ "Protocol error", /* 92 - EPROTO */ + "Capabilities insufficient", /* 93 - ENOTCAPABLE */ }; const int sys_nerr = sizeof(sys_errlist) / sizeof(sys_errlist[0]); diff --git a/lib/libc/gen/exec.c b/lib/libc/gen/exec.c index 985a09a..b83136e 100644 --- a/lib/libc/gen/exec.c +++ b/lib/libc/gen/exec.c @@ -54,7 +54,7 @@ int execl(const char *name, const char *arg, ...) { va_list ap; - char **argv; + const char **argv; int n; va_start(ap, arg); @@ -69,18 +69,19 @@ execl(const char *name, const char *arg, ...) } va_start(ap, arg); n = 1; - argv[0] = (char *)arg; + argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); - return (_execve(name, argv, environ)); + return (_execve(name, __DECONST(char **, argv), environ)); } int execle(const char *name, const char *arg, ...) { va_list ap; - char **argv, **envp; + const char **argv; + char **envp; int n; va_start(ap, arg); @@ -95,19 +96,19 @@ execle(const char *name, const char *arg, ...) } va_start(ap, arg); n = 1; - argv[0] = (char *)arg; + argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; envp = va_arg(ap, char **); va_end(ap); - return (_execve(name, argv, envp)); + return (_execve(name, __DECONST(char **, argv), envp)); } int execlp(const char *name, const char *arg, ...) { va_list ap; - char **argv; + const char **argv; int n; va_start(ap, arg); @@ -122,11 +123,11 @@ execlp(const char *name, const char *arg, ...) } va_start(ap, arg); n = 1; - argv[0] = (char *)arg; + argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); - return (execvp(name, argv)); + return (execvp(name, __DECONST(char **, argv))); } int @@ -145,24 +146,21 @@ execvp(const char *name, char * const *argv) } static int -execvPe(name, path, argv, envp) - const char *name; - const char *path; - char * const *argv; - char * const *envp; +execvPe(const char *name, const char *path, char * const *argv, + char * const *envp) { - char **memp; - int cnt, lp, ln; - char *p; + const char **memp; + size_t cnt, lp, ln; int eacces, save_errno; - char *bp, *cur, buf[MAXPATHLEN]; + char *cur, buf[MAXPATHLEN]; + const char *p, *bp; struct stat sb; eacces = 0; /* If it's an absolute or relative path name, it's easy. */ if (index(name, '/')) { - bp = (char *)name; + bp = name; cur = NULL; goto retry; } @@ -209,7 +207,7 @@ execvPe(name, path, argv, envp) bcopy(name, buf + lp + 1, ln); buf[lp + ln + 1] = '\0'; -retry: (void)_execve(bp, argv, environ); +retry: (void)_execve(bp, argv, envp); switch (errno) { case E2BIG: goto done; @@ -228,7 +226,8 @@ retry: (void)_execve(bp, argv, environ); memp[0] = "sh"; memp[1] = bp; bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); - (void)_execve(_PATH_BSHELL, memp, environ); + (void)_execve(_PATH_BSHELL, + __DECONST(char **, memp), envp); goto done; case ENOMEM: goto done; diff --git a/lib/libc/gen/fdevname.c b/lib/libc/gen/fdevname.c index be235f7..d60da70 100644 --- a/lib/libc/gen/fdevname.c +++ b/lib/libc/gen/fdevname.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "namespace.h" #include <sys/param.h> #include <sys/ioctl.h> +#include <stdlib.h> #include "un-namespace.h" char * diff --git a/lib/libc/gen/fmtmsg.c b/lib/libc/gen/fmtmsg.c index 6caabbb..e6b1b96 100644 --- a/lib/libc/gen/fmtmsg.c +++ b/lib/libc/gen/fmtmsg.c @@ -128,7 +128,7 @@ printfmt(char *msgverb, long class, const char *label, int sev, size += strlen(sevname); if (text != MM_NULLTXT) size += strlen(text); - if (text != MM_NULLACT) + if (act != MM_NULLACT) size += strlen(act); if (tag != MM_NULLTAG) size += strlen(tag); diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 8e1a1e4..4b05157 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -28,7 +28,7 @@ .\" @(#)fts.3 8.5 (Berkeley) 4/16/94 .\" $FreeBSD$ .\" -.Dd January 26, 2008 +.Dd November 25, 2009 .Dt FTS 3 .Os .Sh NAME @@ -198,10 +198,9 @@ A directory being visited in post-order. The contents of the .Vt FTSENT structure will be unchanged from when -it was returned in pre-order, i.e., with the +the directory was visited in pre-order, except for the .Fa fts_info -field set to -.Dv FTS_D . +field. .It Dv FTS_ERR This is an error return, and the .Fa fts_errno @@ -776,7 +775,7 @@ may fail and set as follows: .Bl -tag -width Er .It Bq Er EINVAL -The options were invalid. +The options were invalid, or the list were empty. .El .Sh SEE ALSO .Xr find 1 , diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index 41443c5..1c547a1 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -124,6 +124,12 @@ fts_open(argv, options, compar) return (NULL); } + /* fts_open() requires at least one path */ + if (*argv == NULL) { + errno = EINVAL; + return (NULL); + } + /* Allocate/initialize the stream. */ if ((priv = malloc(sizeof(*priv))) == NULL) return (NULL); @@ -836,11 +842,8 @@ mem1: saved_errno = errno; * If not changing directories, reset the path back to original * state. */ - if (ISSET(FTS_NOCHDIR)) { - if (len == sp->fts_pathlen || nitems == 0) - --cp; - *cp = '\0'; - } + if (ISSET(FTS_NOCHDIR)) + sp->fts_path[cur->fts_pathlen] = '\0'; /* * If descended after called from fts_children or after called from diff --git a/lib/libc/gen/getcap.c b/lib/libc/gen/getcap.c index 32d66d48..2700d3d 100644 --- a/lib/libc/gen/getcap.c +++ b/lib/libc/gen/getcap.c @@ -647,7 +647,7 @@ int cgetnext(char **bp, char **db_array) { size_t len; - int done, hadreaderr, i, savederrno, status; + int done, hadreaderr, savederrno, status; char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; u_int dummy; @@ -658,7 +658,7 @@ cgetnext(char **bp, char **db_array) (void)cgetclose(); return (-1); } - for(;;) { + for (;;) { if (toprec && !gottoprec) { gottoprec = 1; line = toprec; @@ -709,7 +709,6 @@ cgetnext(char **bp, char **db_array) /* * Line points to a name line. */ - i = 0; done = 0; np = nbuf; for (;;) { diff --git a/lib/libc/gen/getcwd.c b/lib/libc/gen/getcwd.c index 0cd3208..c886dde 100644 --- a/lib/libc/gen/getcwd.c +++ b/lib/libc/gen/getcwd.c @@ -62,13 +62,14 @@ getcwd(pt, size) dev_t dev; ino_t ino; int first; - char *bpt, *bup; + char *bpt; struct stat s; dev_t root_dev; ino_t root_ino; - size_t ptsize, upsize; + size_t ptsize; int save_errno; - char *ept, *eup, *up, c; + char *ept, c; + int fd; /* * If no buffer specified by the user, allocate one as necessary. @@ -106,18 +107,6 @@ getcwd(pt, size) bpt = ept - 1; *bpt = '\0'; - /* - * Allocate 1024 bytes for the string of "../"'s. - * Should always be enough. If it's not, allocate - * as necessary. Special case the first stat, it's ".", not "..". - */ - if ((up = malloc(upsize = 1024)) == NULL) - goto err; - eup = up + upsize; - bup = up; - up[0] = '.'; - up[1] = '\0'; - /* Save root values, so know when to stop. */ if (stat("/", &s)) goto err; @@ -128,7 +117,7 @@ getcwd(pt, size) for (first = 1;; first = 0) { /* Stat the current level. */ - if (lstat(up, &s)) + if (dir != NULL ? _fstat(dirfd(dir), &s) : lstat(".", &s)) goto err; /* Save current node values. */ @@ -144,32 +133,22 @@ getcwd(pt, size) * been that way and stuff would probably break. */ bcopy(bpt, pt, ept - bpt); - free(up); + if (dir) + (void) closedir(dir); return (pt); } - /* - * Build pointer to the parent directory, allocating memory - * as necessary. Max length is 3 for "../", the largest - * possible component name, plus a trailing NUL. - */ - while (bup + 3 + MAXNAMLEN + 1 >= eup) { - if ((up = reallocf(up, upsize *= 2)) == NULL) - goto err; - bup = up; - eup = up + upsize; - } - *bup++ = '.'; - *bup++ = '.'; - *bup = '\0'; - /* Open and stat parent directory. */ - if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s)) + fd = _openat(dir != NULL ? dirfd(dir) : AT_FDCWD, + "..", O_RDONLY); + if (fd == -1) goto err; - - /* Add trailing slash for next directory. */ - *bup++ = '/'; - *bup = '\0'; + if (dir) + (void) closedir(dir); + if (!(dir = fdopendir(fd)) || _fstat(dirfd(dir), &s)) { + _close(fd); + goto err; + } /* * If it's a mount point, have to stat each element because @@ -190,10 +169,10 @@ getcwd(pt, size) goto notfound; if (ISDOT(dp)) continue; - bcopy(dp->d_name, bup, dp->d_namlen + 1); /* Save the first error for later. */ - if (lstat(up, &s)) { + if (fstatat(dirfd(dir), dp->d_name, &s, + AT_SYMLINK_NOFOLLOW)) { if (!save_errno) save_errno = errno; errno = 0; @@ -227,11 +206,6 @@ getcwd(pt, size) *--bpt = '/'; bpt -= dp->d_namlen; bcopy(dp->d_name, bpt, dp->d_namlen); - (void) closedir(dir); - dir = NULL; - - /* Truncate any file name. */ - *bup = '\0'; } notfound: @@ -250,7 +224,6 @@ err: free(pt); if (dir) (void) closedir(dir); - free(up); errno = save_errno; return (NULL); diff --git a/lib/libc/gen/getlogin.c b/lib/libc/gen/getlogin.c index dc85d3a..569cf42 100644 --- a/lib/libc/gen/getlogin.c +++ b/lib/libc/gen/getlogin.c @@ -36,7 +36,6 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <errno.h> #include <pwd.h> -#include <utmp.h> #include <stdio.h> #include <string.h> #include <unistd.h> diff --git a/lib/libc/gen/getttyent.3 b/lib/libc/gen/getttyent.3 index 0be7991..18c3f07 100644 --- a/lib/libc/gen/getttyent.3 +++ b/lib/libc/gen/getttyent.3 @@ -188,7 +188,6 @@ zero otherwise. .El .Sh SEE ALSO .Xr login 1 , -.Xr ttyslot 3 , .Xr gettytab 5 , .Xr termcap 5 , .Xr ttys 5 , diff --git a/lib/libc/gen/getttyent.c b/lib/libc/gen/getttyent.c index 9152626..9b9e3ea 100644 --- a/lib/libc/gen/getttyent.c +++ b/lib/libc/gen/getttyent.c @@ -38,25 +38,19 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <ctype.h> #include <string.h> -#include <dirent.h> -#include <paths.h> static char zapchar; static FILE *tf; -static int maxpts = -1; -static int curpts = 0; static size_t lbsize; static char *line; -#define PTS "pts/" #define MALLOCCHUNK 100 static char *skip(char *); static char *value(char *); struct ttyent * -getttynam(tty) - const char *tty; +getttynam(const char *tty) { struct ttyent *t; @@ -71,10 +65,9 @@ getttynam(tty) } struct ttyent * -getttyent() +getttyent(void) { static struct ttyent tty; - static char devpts_name[] = "pts/4294967295"; char *p; int c; size_t i; @@ -82,19 +75,8 @@ getttyent() if (!tf && !setttyent()) return (NULL); for (;;) { - if (!fgets(p = line, lbsize, tf)) { - if (curpts <= maxpts) { - sprintf(devpts_name, "pts/%d", curpts++); - tty.ty_name = devpts_name; - tty.ty_getty = tty.ty_type = NULL; - tty.ty_status = TTY_NETWORK; - tty.ty_window = NULL; - tty.ty_comment = NULL; - tty.ty_group = _TTYS_NOGROUP; - return (&tty); - } + if (!fgets(p = line, lbsize, tf)) return (NULL); - } /* extend buffer if line was too big, and retry */ while (!index(p, '\n') && !feof(tf)) { i = strlen(p); @@ -178,8 +160,7 @@ getttyent() * the next field. */ static char * -skip(p) - char *p; +skip(char *p) { char *t; int c, q; @@ -212,39 +193,21 @@ skip(p) } static char * -value(p) - char *p; +value(char *p) { return ((p = index(p, '=')) ? ++p : NULL); } int -setttyent() +setttyent(void) { - DIR *devpts_dir; if (line == NULL) { if ((line = malloc(MALLOCCHUNK)) == NULL) return (0); lbsize = MALLOCCHUNK; } - devpts_dir = opendir(_PATH_DEV PTS); - if (devpts_dir) { - struct dirent *dp; - - maxpts = -1; - while ((dp = readdir(devpts_dir))) { - if (strcmp(dp->d_name, ".") != 0 && - strcmp(dp->d_name, "..") != 0) { - if (atoi(dp->d_name) > maxpts) { - maxpts = atoi(dp->d_name); - curpts = 0; - } - } - } - closedir(devpts_dir); - } if (tf) { rewind(tf); return (1); @@ -254,11 +217,10 @@ setttyent() } int -endttyent() +endttyent(void) { int rval; - maxpts = -1; /* * NB: Don't free `line' because getttynam() * may still be referencing it @@ -272,9 +234,7 @@ endttyent() } static int -isttystat(tty, flag) - const char *tty; - int flag; +isttystat(const char *tty, int flag) { struct ttyent *t; @@ -283,15 +243,14 @@ isttystat(tty, flag) int -isdialuptty(tty) - const char *tty; +isdialuptty(const char *tty) { return isttystat(tty, TTY_DIALUP); } -int isnettty(tty) - const char *tty; +int +isnettty(const char *tty) { return isttystat(tty, TTY_NETWORK); diff --git a/lib/libc/gen/getusershell.c b/lib/libc/gen/getusershell.c index b0da2a1..d09b50c 100644 --- a/lib/libc/gen/getusershell.c +++ b/lib/libc/gen/getusershell.c @@ -124,7 +124,7 @@ _local_initshells(rv, cb_data, ap) if ((fp = fopen(_PATH_SHELLS, "r")) == NULL) return NS_UNAVAIL; - sp = cp = line; + cp = line; while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) { while (*cp != '#' && *cp != '/' && *cp != '\0') cp++; diff --git a/lib/libc/gen/getutxent.3 b/lib/libc/gen/getutxent.3 new file mode 100644 index 0000000..5ab21ee --- /dev/null +++ b/lib/libc/gen/getutxent.3 @@ -0,0 +1,462 @@ +.\" Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 8, 2010 +.Os +.Dt GETUTXENT 3 +.Sh NAME +.Nm endutxent , +.Nm getutxent , +.Nm getutxid , +.Nm getutxline , +.Nm getutxuser , +.Nm pututxline , +.Nm setutxdb , +.Nm setutxent +.Nd user accounting database functions +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In utmpx.h +.Ft void +.Fn endutxent "void" +.Ft struct utmpx * +.Fn getutxent "void" +.Ft struct utmpx * +.Fn getutxid "const struct utmpx *id" +.Ft struct utmpx * +.Fn getutxline "const struct utmpx *line" +.Ft struct utmpx * +.Fn getutxuser "const char *user" +.Ft struct utmpx * +.Fn pututxline "const struct utmpx *utmpx" +.Ft int +.Fn setutxdb "int type" "const char *file" +.Ft void +.Fn setutxent "void" +.Sh DESCRIPTION +These functions operate on the user accounting database which stores +records of various system activities, such as user login and logouts, +but also system startups and shutdowns and modifications to the system's +clock. +The system stores these records in three databases, each having a +different purpose: +.Bl -tag -width indent +.It Pa /var/run/utx.active +Log of currently active user login sessions. +This file is similar to the traditional +.Pa utmp +file. +This file only contains process related entries, such as user login and +logout records. +.It Pa /var/log/utx.lastlogin +Log of last user login entries per user. +This file is similar to the traditional +.Pa lastlog +file. +This file only contains user login records for users who have at least +logged in once. +.It Pa /var/log/utx.log +Log of all entries, sorted by date of addition. +This file is similar to the traditional +.Pa wtmp +file. +This file may contain any type of record described below. +.El +.Pp +Each entry in these databases is defined by the structure +.Vt utmpx +found in the include file +.In utmpx.h : +.Bd -literal -offset indent +struct utmpx { + short ut_type; /* Type of entry. */ + struct timeval ut_tv; /* Time entry was made. */ + char ut_id[]; /* Record identifier. */ + pid_t ut_pid; /* Process ID. */ + char ut_user[]; /* User login name. */ + char ut_line[]; /* Device name. */ + char ut_host[]; /* Remote hostname. */ +}; +.Ed +.Pp +The +.Fa ut_type +field indicates the type of the log entry, which can have one of the +following values: +.Bl -tag -width LOGIN_PROCESS +.It Dv EMPTY +No valid user accounting information. +.It Dv BOOT_TIME +Identifies time of system boot. +.It Dv SHUTDOWN_TIME +Identifies time of system shutdown. +.It Dv OLD_TIME +Identifies time when system clock changed. +.It Dv NEW_TIME +Identifies time after system clock changed. +.It Dv USER_PROCESS +Identifies a process. +.It Dv INIT_PROCESS +Identifies a process spawned by the init process. +.It Dv LOGIN_PROCESS +Identifies the session leader of a logged-in user. +.It Dv DEAD_PROCESS +Identifies a session leader who has exited. +.El +.Pp +Entries of type +.Dv INIT_PROCESS +and +.Dv LOGIN_PROCESS +are not processed by this implementation. +.Pp +Other fields inside the structure are: +.Bl -tag -width ut_user +.It Fa ut_tv +The time the event occured. +This field is used for all types of entries, except +.Dv EMPTY . +.It Fa ut_id +An identifier that is used to refer to the entry. +This identifier can be used to remove or replace a login entry by +writing a new entry to the database containing the same value for +.Fa ut_id . +This field is only applicable to entries of type +.Dv USER_PROCESS , +.Dv INIT_PROCESS , +.Dv LOGIN_PROCESS +and +.Dv DEAD_PROCESS . +.It Fa ut_pid +The process identifier of the session leader of the login session. +This field is only applicable to entries of type +.Dv USER_PROCESS , +.Dv INIT_PROCESS , +.Dv LOGIN_PROCESS +and +.Dv DEAD_PROCESS . +.It Fa ut_user +The user login name corresponding with the login session. +This field is only applicable to entries of type +.Dv USER_PROCESS +and +.Dv INIT_PROCESS . +For +.Dv INIT_PROCESS +entries this entry typically contains the name of the login process. +.It Fa ut_line +The name of the TTY character device, without the leading +.Pa /dev/ +prefix, corresponding with the device used to facilitate the user login +session. +If no TTY character device is used, this field is left blank. +This field is only applicable to entries of type +.Dv USER_PROCESS +and +.Dv LOGIN_PROCESS . +.It Fa ut_host +The network hostname of the remote system, connecting to perform a user +login. +If the user login session is not performed across a network, this field +is left blank. +This field is only applicable to entries of type +.Dv USER_PROCESS . +.El +.Pp +This implementation guarantees all inapplicable fields are discarded. +The +.Fa ut_user , +.Fa ut_line +and +.Fa ut_host +fields of the structure returned by the library functions are also +guaranteed to be null-terminated in this implementation. +.Pp +The +.Fn getutxent +function can be used to read the next entry from the user accounting +database. +.Pp +The +.Fn getutxid +function searches for the next entry in the database of which the +behaviour is based on the +.Fa ut_type +field of +.Fa id . +If +.Fa ut_type +has a value of +.Dv BOOT_TIME , +.Dv SHUTDOWN_TIME , +.Dv OLD_TIME +or +.Dv NEW_TIME , +it will return the next entry whose +.Fa ut_type +has an equal value. +If +.Fa ut_type +has a value of +.Dv USER_PROCESS , +.Dv INIT_PROCESS , +.Dv LOGIN_PROCESS +or +.Dv DEAD_PROCESS , +it will return the next entry whose +.Fa ut_type +has one of the previously mentioned values and whose +.Fa ut_id +is equal. +.Pp +The +.Fn getutxline +function searches for the next entry in the database whose +.Fa ut_type +has a value of +.Dv USER_PROCESS +or +.Dv LOGIN_PROCESS +and whose +.Fa ut_line +is equal to the the same field in +.Fa line . +.Pp +The +.Fn getutxuser +function searches for the next entry in the database whose +.Fa ut_type +has a value of +.Dv USER_PROCESS +and whose +.Fa ut_user +is equal to +.Fa user . +.Pp +The previously mentioned functions will automatically try to open the +user accounting database if not already done so. +The +.Fn setutxdb +and +.Fn setutxent +functions allow the database to be opened manually, causing the offset +within the user accounting database to be rewound. +The +.Fn endutxent +function closes the database. +.Pp +The +.Fn setutxent +database always opens the active sessions database. +The +.Fn setutxdb +function opens the database identified by +.Fa type , +whose value is either +.Dv UTXDB_ACTIVE , +.Dv UTXDB_LASTLOGIN +or +.Dv UTXDB_LOG . +It will open a custom file with filename +.Fa file +instead of the system-default if +.Fa file +is not null. +Care must be taken that when using a custom filename, +.Fa type +still has to match with the actual format, since each database may use +its own file format. +.Pp +The +.Fn pututxline +function writes record +.Fa utmpx +to the system-default user accounting databases. +The value of +.Fa ut_type +determines which databases are modified. +.Pp +Entries of type +.Dv BOOT_TIME , +.Dv SHUTDOWN_TIME , +.Dv OLD_TIME +and +.Dv NEW_TIME +will only be written to +.Pa /var/log/utx.log . +.Pp +Entries of type +.Dv USER_PROCESS +will also be written to +.Pa /var/run/utx.active +and +.Pa /var/log/utx.lastlogin . +.Pp +Entries of type +.Dv DEAD_PROCESS +will only be written to +.Pa /var/log/utx.log +and +.Pa /var/run/utx.active +if a corresponding +.Dv USER_PROCESS , +.Dv INIT_PROCESS +or +.Dv LOGIN_PROCESS +entry whose +.Fa ut_id +is equal has been found in the latter. +.Pp +In addition, entries of type +.Dv BOOT_TIME +and +.Dv SHUTDOWN_TIME +will cause all entries in +.Pa /var/run/utx.active +to be discarded. +.Pp +All entries whose type has not been mentioned previously, are discarded +by this implementation of +.Fn pututxline . +This implementation also ignores the value of +.Fa ut_tv . +.Sh RETURN VALUES +The +.Fn getutxent , +.Fn getutxid , +.Fn getutxline , +and +.Fn getutxuser +functions return a pointer to an +.Vt utmpx +structure that matches the mentioned constraints on success or +.Dv NULL +when reaching the end-of-file or when an error occurs. +.Pp +The +.Fn pututxline +function returns a pointer to an +.Vt utmpx +structure containing a copy of the structure written to disk upon +success. +It returns +.Dv NULL +when the provided +.Vt utmpx +is invalid. +This may be because +.Fa ut_type +is invalid or +.Fa ut_type +has a value of +.Dv DEAD_PROCESS +and an entry with an identifier with a value equal to the field +.Fa ut_id +was not found. +.Pp +The +.Fn setutxdb +function returns 0 if the user accounting database was opened +successfully. +Otherwise, -1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +In addition to the error conditions described in +.Xr fopen 3 , +the +.Fn setutxdb +function can generate the following errors: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa type +argument contains a value not supported by this implementation. +.It Bq Er EFTYPE +The file format is invalid. +.El +.Sh SEE ALSO +.Xr last 1 , +.Xr write 1 , +.Xr wtmpcvt 1 , +.Xr getpid 2 , +.Xr gettimeofday 2 , +.Xr tty 4 , +.Xr ac 8 , +.Xr newsyslog 8 +.Sh STANDARDS +The +.Fn endutxent , +.Fn getutxent , +.Fn getutxid , +.Fn getutxline +and +.Fn setutxent +functions are expected to conform to +.St -p1003.1-2008 . +.Pp +The +.Fn pututxline +function deviates from the standard by writing its records to multiple +database files, depending on its +.Fa ut_type . +This prevents the need for special utility functions to update the other +databases, such as the +.Fn updlastlogx +and +.Fn updwtmpx +functions which are available in other implementations. +It also tries to replace +.Dv DEAD_PROCESS +entries in the active sessions database when storing +.Dv USER_PROCESS +entries and no entry with the same value for +.Fa ut_id +has been found. +The standard always requires a new entry to be allocated, which could +cause an unbounded growth of the database. +.Pp +The +.Fn getutxuser +and +.Fn setutxdb +functions, +the +.Fa ut_host +field of the +.Vt utmpx +structure and +.Dv SHUTDOWN_TIME +are extensions. +.Sh HISTORY +These functions appeared in +.Fx 9.0 . +They replaced the +.In utmp.h +interface. +.Sh AUTHORS +.An Ed Schouten Aq ed@FreeBSD.org diff --git a/lib/libc/gen/getutxent.c b/lib/libc/gen/getutxent.c new file mode 100644 index 0000000..654a634 --- /dev/null +++ b/lib/libc/gen/getutxent.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/endian.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <utmpx.h> +#include "utxdb.h" +#include "un-namespace.h" + +static FILE *uf = NULL; +static int udb; + +int +setutxdb(int db, const char *file) +{ + struct stat sb; + + switch (db) { + case UTXDB_ACTIVE: + if (file == NULL) + file = _PATH_UTX_ACTIVE; + break; + case UTXDB_LASTLOGIN: + if (file == NULL) + file = _PATH_UTX_LASTLOGIN; + break; + case UTXDB_LOG: + if (file == NULL) + file = _PATH_UTX_LOG; + break; + default: + errno = EINVAL; + return (-1); + } + + if (uf != NULL) + fclose(uf); + uf = fopen(file, "r"); + if (uf == NULL) + return (-1); + + /* Safety check: never use broken files. */ + if (db != UTXDB_LOG && _fstat(fileno(uf), &sb) != -1 && + sb.st_size % sizeof(struct futx) != 0) { + fclose(uf); + uf = NULL; + errno = EFTYPE; + return (-1); + } + + udb = db; + return (0); +} + +void +setutxent(void) +{ + + setutxdb(UTXDB_ACTIVE, NULL); +} + +void +endutxent(void) +{ + + if (uf != NULL) { + fclose(uf); + uf = NULL; + } +} + +static int +getfutxent(struct futx *fu) +{ + + if (uf == NULL) + setutxent(); + if (uf == NULL) + return (-1); + + if (udb == UTXDB_LOG) { + uint16_t len; + + if (fread(&len, sizeof len, 1, uf) != 1) + return (-1); + len = be16toh(len); + if (len > sizeof *fu) { + /* Forward compatibility. */ + if (fread(fu, sizeof *fu, 1, uf) != 1) + return (-1); + fseek(uf, len - sizeof *fu, SEEK_CUR); + } else { + /* Partial record. */ + memset(fu, 0, sizeof *fu); + if (fread(fu, len, 1, uf) != 1) + return (-1); + } + } else { + if (fread(fu, sizeof *fu, 1, uf) != 1) + return (-1); + } + return (0); +} + +struct utmpx * +getutxent(void) +{ + struct futx fu; + + if (getfutxent(&fu) != 0) + return (NULL); + return (futx_to_utx(&fu)); +} + +struct utmpx * +getutxid(const struct utmpx *id) +{ + struct futx fu; + + for (;;) { + if (getfutxent(&fu) != 0) + return (NULL); + + switch (fu.fu_type) { + case USER_PROCESS: + case INIT_PROCESS: + case LOGIN_PROCESS: + case DEAD_PROCESS: + switch (id->ut_type) { + case USER_PROCESS: + case INIT_PROCESS: + case LOGIN_PROCESS: + case DEAD_PROCESS: + if (memcmp(fu.fu_id, id->ut_id, + MIN(sizeof fu.fu_id, sizeof id->ut_id)) == 0) + goto found; + } + break; + default: + if (fu.fu_type == id->ut_type) + goto found; + break; + } + } + +found: + return (futx_to_utx(&fu)); +} + +struct utmpx * +getutxline(const struct utmpx *line) +{ + struct futx fu; + + for (;;) { + if (getfutxent(&fu) != 0) + return (NULL); + + switch (fu.fu_type) { + case USER_PROCESS: + case LOGIN_PROCESS: + if (strncmp(fu.fu_line, line->ut_line, + MIN(sizeof fu.fu_line, sizeof line->ut_line)) == 0) + goto found; + break; + } + } + +found: + return (futx_to_utx(&fu)); +} + +struct utmpx * +getutxuser(const char *user) +{ + struct futx fu; + + for (;;) { + if (getfutxent(&fu) != 0) + return (NULL); + + switch (fu.fu_type) { + case USER_PROCESS: + if (strncmp(fu.fu_user, user, sizeof fu.fu_user) == 0) + goto found; + break; + } + } + +found: + return (futx_to_utx(&fu)); +} diff --git a/lib/libc/gen/nlist.c b/lib/libc/gen/nlist.c index 690e0b0..1361af3 100644 --- a/lib/libc/gen/nlist.c +++ b/lib/libc/gen/nlist.c @@ -209,8 +209,7 @@ static void elf_sym_to_nlist(struct nlist *, Elf_Sym *, Elf_Shdr *, int); * as such its use should be restricted. */ int -__elf_is_okay__(ehdr) - Elf_Ehdr *ehdr; +__elf_is_okay__(Elf_Ehdr *ehdr) { int retval = 0; /* diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 9625ec6..b312c89 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -92,6 +92,14 @@ __opendir2(const char *name, int flags) return __opendir_common(fd, name, flags); } +static int +opendir_compar(const void *p1, const void *p2) +{ + + return (strcmp((*(const struct dirent **)p1)->d_name, + (*(const struct dirent **)p2)->d_name)); +} + /* * Common routine for opendir(3), __opendir2(3) and fdopendir(3). */ @@ -240,7 +248,8 @@ __opendir_common(int fd, const char *name, int flags) /* * This sort must be stable. */ - mergesort(dpv, n, sizeof(*dpv), alphasort); + mergesort(dpv, n, sizeof(*dpv), + opendir_compar); dpv[n] = NULL; xp = NULL; diff --git a/lib/libc/gen/pause.c b/lib/libc/gen/pause.c index 86d3643..51706cf 100644 --- a/lib/libc/gen/pause.c +++ b/lib/libc/gen/pause.c @@ -33,16 +33,22 @@ static char sccsid[] = "@(#)pause.c 8.1 (Berkeley) 6/4/93"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "namespace.h" #include <signal.h> #include <unistd.h> +#include "un-namespace.h" /* * Backwards compatible pause. */ int -__pause() +__pause(void) { - return sigpause(sigblock(0L)); + sigset_t oset; + + if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) + return (-1); + return (_sigsuspend(&oset)); } __weak_reference(__pause, pause); __weak_reference(__pause, _pause); diff --git a/lib/libc/gen/posix_spawn.3 b/lib/libc/gen/posix_spawn.3 index 518be2e..7569239 100644 --- a/lib/libc/gen/posix_spawn.3 +++ b/lib/libc/gen/posix_spawn.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWN 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawn_file_actions_addopen.3 b/lib/libc/gen/posix_spawn_file_actions_addopen.3 index b1fc259..c6fd017 100644 --- a/lib/libc/gen/posix_spawn_file_actions_addopen.3 +++ b/lib/libc/gen/posix_spawn_file_actions_addopen.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWN_FILE_ACTIONS_ADDOPEN 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawn_file_actions_init.3 b/lib/libc/gen/posix_spawn_file_actions_init.3 index 31b677e..d826b8b 100644 --- a/lib/libc/gen/posix_spawn_file_actions_init.3 +++ b/lib/libc/gen/posix_spawn_file_actions_init.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWN_FILE_ACTIONS_INIT 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getflags.3 b/lib/libc/gen/posix_spawnattr_getflags.3 index c3f9ee5..a2dda02 100644 --- a/lib/libc/gen/posix_spawnattr_getflags.3 +++ b/lib/libc/gen/posix_spawnattr_getflags.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETFLAGS 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getpgroup.3 b/lib/libc/gen/posix_spawnattr_getpgroup.3 index 8ba8142..5cd51d6 100644 --- a/lib/libc/gen/posix_spawnattr_getpgroup.3 +++ b/lib/libc/gen/posix_spawnattr_getpgroup.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETPGROUP 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getschedparam.3 b/lib/libc/gen/posix_spawnattr_getschedparam.3 index 59cece8..70009b9 100644 --- a/lib/libc/gen/posix_spawnattr_getschedparam.3 +++ b/lib/libc/gen/posix_spawnattr_getschedparam.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETSCHEDPARAM 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getschedpolicy.3 b/lib/libc/gen/posix_spawnattr_getschedpolicy.3 index 158923a..45c1965 100644 --- a/lib/libc/gen/posix_spawnattr_getschedpolicy.3 +++ b/lib/libc/gen/posix_spawnattr_getschedpolicy.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETSCHEDPOLICY 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getsigdefault.3 b/lib/libc/gen/posix_spawnattr_getsigdefault.3 index f37992a..9e13c37 100644 --- a/lib/libc/gen/posix_spawnattr_getsigdefault.3 +++ b/lib/libc/gen/posix_spawnattr_getsigdefault.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETSIGDEFAULT 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_getsigmask.3 b/lib/libc/gen/posix_spawnattr_getsigmask.3 index 44b5c17..5cee7ec 100644 --- a/lib/libc/gen/posix_spawnattr_getsigmask.3 +++ b/lib/libc/gen/posix_spawnattr_getsigmask.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_GETSIGMASK 3 .Os .Sh NAME diff --git a/lib/libc/gen/posix_spawnattr_init.3 b/lib/libc/gen/posix_spawnattr_init.3 index 7eddc75..66c99cd 100644 --- a/lib/libc/gen/posix_spawnattr_init.3 +++ b/lib/libc/gen/posix_spawnattr_init.3 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd Mar 24, 2008 +.Dd March 24, 2008 .Dt POSIX_SPAWNATTR_INIT 3 .Os .Sh NAME diff --git a/lib/libc/gen/pselect.c b/lib/libc/gen/pselect.c deleted file mode 100644 index 28066a2..0000000 --- a/lib/libc/gen/pselect.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000 Massachusetts Institute of Technology - * - * Permission to use, copy, modify, and distribute this software and - * its documentation for any purpose and without fee is hereby - * granted, provided that both the above copyright notice and this - * permission notice appear in all copies, that both the above - * copyright notice and this permission notice appear in all - * supporting documentation, and that the name of M.I.T. not be used - * in advertising or publicity pertaining to distribution of the - * software without specific, written prior permission. M.I.T. makes - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied - * warranty. - * - * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS - * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT - * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "namespace.h" -#include <sys/select.h> -#include <sys/time.h> - -#include <errno.h> -#include <signal.h> -#include "un-namespace.h" - -__weak_reference(__pselect, pselect); - -/* - * Emulate the POSIX 1003.1g-2000 `pselect' interface. This is the - * same as the traditional BSD `select' function, except that it uses - * a timespec rather than a timeval, doesn't modify the timeout argument, - * and allows the user to specify a signal mask to apply during the select. - */ -int -__pselect(int count, fd_set * __restrict rfds, fd_set * __restrict wfds, - fd_set * __restrict efds, const struct timespec * __restrict timo, - const sigset_t * __restrict mask) -{ - sigset_t omask; - struct timeval tvtimo, *tvp; - int rv, sverrno; - - if (timo) { - TIMESPEC_TO_TIMEVAL(&tvtimo, timo); - tvp = &tvtimo; - } else - tvp = 0; - - if (mask != 0) { - rv = _sigprocmask(SIG_SETMASK, mask, &omask); - if (rv != 0) - return rv; - } - - rv = _select(count, rfds, wfds, efds, tvp); - if (mask != 0) { - sverrno = errno; - _sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); - errno = sverrno; - } - - return rv; -} diff --git a/lib/libc/gen/pututxline.c b/lib/libc/gen/pututxline.c new file mode 100644 index 0000000..ce4dc28 --- /dev/null +++ b/lib/libc/gen/pututxline.c @@ -0,0 +1,263 @@ +/*- + * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/endian.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <utmpx.h> +#include "utxdb.h" +#include "un-namespace.h" + +static FILE * +futx_open(const char *file) +{ + int fd; + FILE *fp; + struct stat sb; + + fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK, 0644); + if (fd < 0) + return (NULL); + + /* Safety check: never use broken files. */ + if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) { + _close(fd); + return (NULL); + } + + fp = fdopen(fd, "r+"); + if (fp == NULL) { + _close(fd); + return (NULL); + } + + return (fp); +} + +static void +utx_active_add(const struct futx *fu) +{ + FILE *fp; + struct futx fe; + off_t partial = -1; + + /* + * Register user login sessions. Overwrite entries of sessions + * that have already been terminated. + */ + fp = futx_open(_PATH_UTX_ACTIVE); + if (fp == NULL) + return; + while (fread(&fe, sizeof fe, 1, fp) == 1) { + switch (fe.fu_type) { + case USER_PROCESS: + case INIT_PROCESS: + case LOGIN_PROCESS: + case DEAD_PROCESS: + /* Overwrite when ut_id matches. */ + if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) == 0) { + fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); + goto exact; + } + if (fe.fu_type != DEAD_PROCESS) + break; + /* FALLTHROUGH */ + default: + /* Allow us to overwrite unused records. */ + if (partial == -1) + partial = ftello(fp) - (off_t)sizeof fe; + break; + } + } + + /* + * No exact match found. Use the partial match. If no partial + * match was found, just append a new record. + */ + if (partial != -1) + fseeko(fp, partial, SEEK_SET); +exact: + fwrite(fu, sizeof *fu, 1, fp); + fclose(fp); +} + +static int +utx_active_remove(struct futx *fu) +{ + FILE *fp; + struct futx fe; + + /* + * Remove user login sessions, having the same ut_id. + */ + fp = futx_open(_PATH_UTX_ACTIVE); + if (fp == NULL) + return (0); + while (fread(&fe, sizeof fe, 1, fp) == 1) { + switch (fe.fu_type) { + case USER_PROCESS: + case INIT_PROCESS: + case LOGIN_PROCESS: + if (memcmp(fu->fu_id, fe.fu_id, sizeof fe.fu_id) != 0) + continue; + + /* Terminate session. */ + fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); + fwrite(fu, sizeof *fu, 1, fp); + fclose(fp); + return (0); + } + } + + fclose(fp); + return (1); +} + +static void +utx_active_purge(void) +{ + + truncate(_PATH_UTX_ACTIVE, 0); +} + +static void +utx_lastlogin_add(const struct futx *fu) +{ + FILE *fp; + struct futx fe; + + /* + * Write an entry to lastlogin. Overwrite the entry if the + * current user already has an entry. If not, append a new + * entry. + */ + fp = futx_open(_PATH_UTX_LASTLOGIN); + if (fp == NULL) + return; + while (fread(&fe, sizeof fe, 1, fp) == 1) { + if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0) + continue; + + /* Found a previous lastlogin entry for this user. */ + fseeko(fp, -(off_t)sizeof fe, SEEK_CUR); + break; + } + fwrite(fu, sizeof *fu, 1, fp); + fclose(fp); +} + +static void +utx_lastlogin_upgrade(void) +{ + int fd; + struct stat sb; + + fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR, 0644); + if (fd < 0) + return; + + /* + * Truncate broken lastlogin files. In the future we should + * check for older versions of the file format here and try to + * upgrade it. + */ + if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) + ftruncate(fd, 0); + _close(fd); +} + +static void +utx_log_add(const struct futx *fu) +{ + int fd; + uint16_t l; + struct iovec vec[2]; + + /* + * Append an entry to the log file. We only need to append + * records to this file, so to conserve space, trim any trailing + * zero-bytes. Prepend a length field, indicating the length of + * the record, excluding the length field itself. + */ + for (l = sizeof *fu; l > 0 && ((const char *)fu)[l - 1] == '\0'; l--); + vec[0].iov_base = &l; + vec[0].iov_len = sizeof l; + vec[1].iov_base = __DECONST(void *, fu); + vec[1].iov_len = l; + l = htobe16(l); + + fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND, 0644); + if (fd < 0) + return; + _writev(fd, vec, 2); + _close(fd); +} + +struct utmpx * +pututxline(const struct utmpx *utmpx) +{ + struct futx fu; + + utx_to_futx(utmpx, &fu); + + switch (fu.fu_type) { + case BOOT_TIME: + case SHUTDOWN_TIME: + utx_active_purge(); + utx_lastlogin_upgrade(); + break; + case OLD_TIME: + case NEW_TIME: + break; + case USER_PROCESS: + utx_active_add(&fu); + utx_lastlogin_add(&fu); + break; +#if 0 /* XXX: Are these records of any use to us? */ + case INIT_PROCESS: + case LOGIN_PROCESS: + utx_active_add(&fu); + break; +#endif + case DEAD_PROCESS: + if (utx_active_remove(&fu) != 0) + return (NULL); + break; + default: + return (NULL); + } + + utx_log_add(&fu); + return (futx_to_utx(&fu)); +} diff --git a/lib/libc/gen/pwcache.c b/lib/libc/gen/pwcache.c index 501d6ba..a8a73f1 100644 --- a/lib/libc/gen/pwcache.c +++ b/lib/libc/gen/pwcache.c @@ -33,13 +33,13 @@ static char sccsid[] = "@(#)pwcache.c 8.1 (Berkeley) 6/4/93"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/param.h> #include <sys/types.h> #include <grp.h> #include <pwd.h> #include <stdio.h> #include <string.h> -#include <utmp.h> #define NCACHE 64 /* power of 2 */ #define MASK (NCACHE - 1) /* bits to store with */ @@ -50,7 +50,7 @@ user_from_uid(uid_t uid, int nouser) static struct ncache { uid_t uid; int found; - char name[UT_NAMESIZE + 1]; + char name[MAXLOGNAME]; } c_uid[NCACHE]; static int pwopen; struct passwd *pw; @@ -66,11 +66,11 @@ user_from_uid(uid_t uid, int nouser) cp->uid = uid; if (pw != NULL) { cp->found = 1; - (void)strncpy(cp->name, pw->pw_name, UT_NAMESIZE); - cp->name[UT_NAMESIZE] = '\0'; + (void)strncpy(cp->name, pw->pw_name, MAXLOGNAME - 1); + cp->name[MAXLOGNAME - 1] = '\0'; } else { cp->found = 0; - (void)snprintf(cp->name, UT_NAMESIZE, "%u", uid); + (void)snprintf(cp->name, MAXLOGNAME - 1, "%u", uid); if (nouser) return (NULL); } @@ -84,7 +84,7 @@ group_from_gid(gid_t gid, int nogroup) static struct ncache { gid_t gid; int found; - char name[UT_NAMESIZE + 1]; + char name[MAXLOGNAME]; } c_gid[NCACHE]; static int gropen; struct group *gr; @@ -100,11 +100,11 @@ group_from_gid(gid_t gid, int nogroup) cp->gid = gid; if (gr != NULL) { cp->found = 1; - (void)strncpy(cp->name, gr->gr_name, UT_NAMESIZE); - cp->name[UT_NAMESIZE] = '\0'; + (void)strncpy(cp->name, gr->gr_name, MAXLOGNAME - 1); + cp->name[MAXLOGNAME - 1] = '\0'; } else { cp->found = 0; - (void)snprintf(cp->name, UT_NAMESIZE, "%u", gid); + (void)snprintf(cp->name, MAXLOGNAME - 1, "%u", gid); if (nogroup) return (NULL); } diff --git a/lib/libc/gen/raise.c b/lib/libc/gen/raise.c index 12bd31f..b3d0aae 100644 --- a/lib/libc/gen/raise.c +++ b/lib/libc/gen/raise.c @@ -40,8 +40,7 @@ __weak_reference(__raise, raise); __weak_reference(__raise, _raise); int -__raise(s) - int s; +__raise(int s) { return(kill(getpid(), s)); } diff --git a/lib/libc/gen/rand48.3 b/lib/libc/gen/rand48.3 index a703ba0..8d325a1 100644 --- a/lib/libc/gen/rand48.3 +++ b/lib/libc/gen/rand48.3 @@ -12,7 +12,7 @@ .\" @(#)rand48.3 V1.0 MB 8 Oct 1993 .\" $FreeBSD$ .\" -.Dd October 8, 1993 +.Dd February 2, 2010 .Dt RAND48 3 .Os .Sh NAME @@ -57,7 +57,7 @@ The particular formula employed is r(n+1) = (a * r(n) + c) mod m where the default values are -for the multiplicand a = 0xfdeece66d = 25214903917 and +for the multiplicand a = 0x5deece66d = 25214903917 and the addend c = 0xb = 11. The modulo is always fixed at m = 2 ** 48. r(n) is called the seed of the random number generator. diff --git a/lib/libc/gen/scandir.3 b/lib/libc/gen/scandir.3 index 42ccac2..e32b0d8 100644 --- a/lib/libc/gen/scandir.3 +++ b/lib/libc/gen/scandir.3 @@ -28,7 +28,7 @@ .\" @(#)scandir.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 4, 1993 +.Dd January 3, 2010 .Dt SCANDIR 3 .Os .Sh NAME @@ -38,12 +38,11 @@ .Sh LIBRARY .Lb libc .Sh SYNOPSIS -.In sys/types.h .In dirent.h .Ft int -.Fn scandir "const char *dirname" "struct dirent ***namelist" "int \\*(lp*select\\*(rp\\*(lpstruct dirent *\\*(rp" "int \\*(lp*compar\\*(rp\\*(lpconst void *, const void *\\*(rp" +.Fn scandir "const char *dirname" "struct dirent ***namelist" "int \\*(lp*select\\*(rp\\*(lpconst struct dirent *\\*(rp" "int \\*(lp*compar\\*(rp\\*(lpconst struct dirent **, const struct dirent **\\*(rp" .Ft int -.Fn alphasort "const void *d1" "const void *d2" +.Fn alphasort "const struct dirent **d1" "const struct dirent **d2" .Sh DESCRIPTION The .Fn scandir @@ -82,7 +81,8 @@ The function is a routine which can be used for the .Fa compar -argument to sort the array alphabetically. +argument to sort the array alphabetically using +.Xr strcoll 3 . .Pp The memory allocated for the array can be deallocated with .Xr free 3 , @@ -95,7 +95,8 @@ cannot allocate enough memory to hold all the data structures. .Xr directory 3 , .Xr malloc 3 , .Xr qsort 3 , -.Xr dir 5 +.Xr dir 5 , +.Xr strcoll 3 .Sh HISTORY The .Fn scandir diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c index 1dae85d..93bc852 100644 --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -46,6 +46,8 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include "un-namespace.h" +static int alphasort_thunk(void *thunk, const void *p1, const void *p2); + /* * The DIRSIZ macro is the minimum record length which will hold the directory * entry. This requires the amount of space in struct dirent without the @@ -58,11 +60,9 @@ __FBSDID("$FreeBSD$"); (((dp)->d_namlen + 1 + 3) &~ 3)) int -scandir(dirname, namelist, select, dcomp) - const char *dirname; - struct dirent ***namelist; - int (*select)(struct dirent *); - int (*dcomp)(const void *, const void *); +scandir(const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, + const struct dirent **)) { struct dirent *d, *p, **names = NULL; size_t nitems = 0; @@ -111,26 +111,35 @@ scandir(dirname, namelist, select, dcomp) } closedir(dirp); if (nitems && dcomp != NULL) - qsort(names, nitems, sizeof(struct dirent *), dcomp); + qsort_r(names, nitems, sizeof(struct dirent *), + &dcomp, alphasort_thunk); *namelist = names; - return(nitems); + return (nitems); fail: while (nitems > 0) free(names[--nitems]); free(names); closedir(dirp); - return -1; + return (-1); } /* * Alphabetic order comparison routine for those who want it. + * POSIX 2008 requires that alphasort() uses strcoll(). */ int -alphasort(d1, d2) - const void *d1; - const void *d2; +alphasort(const struct dirent **d1, const struct dirent **d2) +{ + + return (strcoll((*d1)->d_name, (*d2)->d_name)); +} + +static int +alphasort_thunk(void *thunk, const void *p1, const void *p2) { - return(strcmp((*(struct dirent **)d1)->d_name, - (*(struct dirent **)d2)->d_name)); + int (*dc)(const struct dirent **, const struct dirent **); + + dc = *(int (**)(const struct dirent **, const struct dirent **))thunk; + return (dc((const struct dirent **)p1, (const struct dirent **)p2)); } diff --git a/lib/libc/gen/sem.c b/lib/libc/gen/sem.c index 439f0fe..628e650 100644 --- a/lib/libc/gen/sem.c +++ b/lib/libc/gen/sem.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. * All rights reserved. * @@ -59,34 +60,65 @@ #include "namespace.h" #include <sys/types.h> #include <sys/queue.h> +#include <machine/atomic.h> #include <errno.h> +#include <sys/umtx.h> +#include <sys/_semaphore.h> +#include <limits.h> #include <fcntl.h> #include <pthread.h> -#include <semaphore.h> #include <stdarg.h> #include <stdlib.h> #include <time.h> -#include <_semaphore.h> #include "un-namespace.h" #include "libc_private.h" +/* + * Old semaphore definitions. + */ +struct sem { +#define SEM_MAGIC ((u_int32_t) 0x09fa4012) + u_int32_t magic; + pthread_mutex_t lock; + pthread_cond_t gtzero; + u_int32_t count; + u_int32_t nwaiters; +#define SEM_USER (NULL) + semid_t semid; /* semaphore id if kernel (shared) semaphore */ + int syssem; /* 1 if kernel (shared) semaphore */ + LIST_ENTRY(sem) entry; + struct sem **backpointer; +}; + +typedef struct sem* sem_t; + +#define SEM_FAILED ((sem_t *)0) +#define SEM_VALUE_MAX __INT_MAX + +#define SYM_FB10(sym) __CONCAT(sym, _fb10) +#define WEAK_REF(sym, alias) __weak_reference(sym, alias) +#define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) + +#define FB10_COMPAT(func, sym) \ + WEAK_REF(func, SYM_FB10(sym)); \ + SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) + static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); static void sem_free(sem_t sem); -static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(&named_sems); +static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems); static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; -__weak_reference(__sem_init, sem_init); -__weak_reference(__sem_destroy, sem_destroy); -__weak_reference(__sem_open, sem_open); -__weak_reference(__sem_close, sem_close); -__weak_reference(__sem_unlink, sem_unlink); -__weak_reference(__sem_wait, sem_wait); -__weak_reference(__sem_trywait, sem_trywait); -__weak_reference(__sem_timedwait, sem_timedwait); -__weak_reference(__sem_post, sem_post); -__weak_reference(__sem_getvalue, sem_getvalue); - +FB10_COMPAT(_libc_sem_init_compat, sem_init); +FB10_COMPAT(_libc_sem_destroy_compat, sem_destroy); +FB10_COMPAT(_libc_sem_open_compat, sem_open); +FB10_COMPAT(_libc_sem_close_compat, sem_close); +FB10_COMPAT(_libc_sem_unlink_compat, sem_unlink); +FB10_COMPAT(_libc_sem_wait_compat, sem_wait); +FB10_COMPAT(_libc_sem_trywait_compat, sem_trywait); +FB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait); +FB10_COMPAT(_libc_sem_post_compat, sem_post); +FB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue); static inline int sem_check_validity(sem_t *sem) @@ -104,8 +136,6 @@ static void sem_free(sem_t sem) { - _pthread_mutex_destroy(&sem->lock); - _pthread_cond_destroy(&sem->gtzero); sem->magic = 0; free(sem); } @@ -131,13 +161,11 @@ sem_alloc(unsigned int value, semid_t semid, int system_sem) sem->magic = SEM_MAGIC; sem->semid = semid; sem->syssem = system_sem; - sem->lock = PTHREAD_MUTEX_INITIALIZER; - sem->gtzero = PTHREAD_COND_INITIALIZER; return (sem); } int -__sem_init(sem_t *sem, int pshared, unsigned int value) +_libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value) { semid_t semid; @@ -147,26 +175,27 @@ __sem_init(sem_t *sem, int pshared, unsigned int value) * pthread_cond_wait() is just a stub that doesn't really * wait. */ - if (ksem_init(&semid, value) != 0) + semid = (semid_t)SEM_USER; + if ((pshared != 0) && ksem_init(&semid, value) != 0) return (-1); - (*sem) = sem_alloc(value, semid, 1); + *sem = sem_alloc(value, semid, pshared); if ((*sem) == NULL) { - ksem_destroy(semid); + if (pshared != 0) + ksem_destroy(semid); return (-1); } return (0); } int -__sem_destroy(sem_t *sem) +_libc_sem_destroy_compat(sem_t *sem) { int retval; if (sem_check_validity(sem) != 0) return (-1); - _pthread_mutex_lock(&(*sem)->lock); /* * If this is a system semaphore let the kernel track it otherwise * make sure there are no waiters. @@ -181,18 +210,14 @@ __sem_destroy(sem_t *sem) retval = 0; (*sem)->magic = 0; } - _pthread_mutex_unlock(&(*sem)->lock); - if (retval == 0) { - _pthread_mutex_destroy(&(*sem)->lock); - _pthread_cond_destroy(&(*sem)->gtzero); + if (retval == 0) sem_free(*sem); - } return (retval); } sem_t * -__sem_open(const char *name, int oflag, ...) +_libc_sem_open_compat(const char *name, int oflag, ...) { sem_t *sem; sem_t s; @@ -255,7 +280,7 @@ err: } int -__sem_close(sem_t *sem) +_libc_sem_close_compat(sem_t *sem) { if (sem_check_validity(sem) != 0) @@ -280,68 +305,156 @@ __sem_close(sem_t *sem) } int -__sem_unlink(const char *name) +_libc_sem_unlink_compat(const char *name) { return (ksem_unlink(name)); } -int -__sem_wait(sem_t *sem) +static int +enable_async_cancel(void) { + int old; - if (sem_check_validity(sem) != 0) + _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); + return (old); +} + +static void +restore_async_cancel(int val) +{ + _pthread_setcanceltype(val, NULL); +} + +static int +_umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *timeout) +{ + if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && + timeout->tv_nsec <= 0))) { + errno = ETIMEDOUT; return (-1); + } + return _umtx_op(__DEVOLATILE(void *, mtx), + UMTX_OP_WAIT_UINT_PRIVATE, id, NULL, __DECONST(void*, timeout)); +} - return (ksem_wait((*sem)->semid)); +static int +_umtx_wake(volatile void *mtx) +{ + return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE, + 1, NULL, NULL); +} + +#define TIMESPEC_SUB(dst, src, val) \ + do { \ + (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ + (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ + if ((dst)->tv_nsec < 0) { \ + (dst)->tv_sec--; \ + (dst)->tv_nsec += 1000000000; \ + } \ + } while (0) + + +static void +sem_cancel_handler(void *arg) +{ + sem_t *sem = arg; + + atomic_add_int(&(*sem)->nwaiters, -1); + if ((*sem)->nwaiters && (*sem)->count) + _umtx_wake(&(*sem)->count); } int -__sem_trywait(sem_t *sem) +_libc_sem_timedwait_compat(sem_t * __restrict sem, + const struct timespec * __restrict abstime) { - int retval; + struct timespec ts, ts2; + int val, retval, saved_cancel; if (sem_check_validity(sem) != 0) return (-1); - if ((*sem)->syssem != 0) - retval = ksem_trywait((*sem)->semid); - else { - _pthread_mutex_lock(&(*sem)->lock); - if ((*sem)->count > 0) { - (*sem)->count--; - retval = 0; - } else { - errno = EAGAIN; - retval = -1; + if ((*sem)->syssem != 0) { + saved_cancel = enable_async_cancel(); + retval = ksem_wait((*sem)->semid); + restore_async_cancel(saved_cancel); + return (retval); + } + + retval = 0; + _pthread_testcancel(); + for (;;) { + while ((val = (*sem)->count) > 0) { + if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) + return (0); + } + if (retval) + break; + if (abstime) { + if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { + errno = EINVAL; + return (-1); + } + clock_gettime(CLOCK_REALTIME, &ts); + TIMESPEC_SUB(&ts2, abstime, &ts); } - _pthread_mutex_unlock(&(*sem)->lock); + atomic_add_int(&(*sem)->nwaiters, 1); + pthread_cleanup_push(sem_cancel_handler, sem); + saved_cancel = enable_async_cancel(); + retval = _umtx_wait_uint(&(*sem)->count, 0, abstime ? &ts2 : NULL); + restore_async_cancel(saved_cancel); + pthread_cleanup_pop(0); + atomic_add_int(&(*sem)->nwaiters, -1); } return (retval); } int -__sem_timedwait(sem_t * __restrict sem, - const struct timespec * __restrict abs_timeout) +_libc_sem_wait_compat(sem_t *sem) { + return _libc_sem_timedwait_compat(sem, NULL); +} + +int +_libc_sem_trywait_compat(sem_t *sem) +{ + int val; + if (sem_check_validity(sem) != 0) return (-1); - return (ksem_timedwait((*sem)->semid, abs_timeout)); + if ((*sem)->syssem != 0) + return ksem_trywait((*sem)->semid); + + while ((val = (*sem)->count) > 0) { + if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) + return (0); + } + errno = EAGAIN; + return (-1); } int -__sem_post(sem_t *sem) +_libc_sem_post_compat(sem_t *sem) { if (sem_check_validity(sem) != 0) return (-1); - return (ksem_post((*sem)->semid)); + if ((*sem)->syssem != 0) + return ksem_post((*sem)->semid); + + atomic_add_rel_int(&(*sem)->count, 1); + + if ((*sem)->nwaiters) + return _umtx_wake(&(*sem)->count); + return (0); } int -__sem_getvalue(sem_t * __restrict sem, int * __restrict sval) +_libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) { int retval; @@ -351,10 +464,7 @@ __sem_getvalue(sem_t * __restrict sem, int * __restrict sval) if ((*sem)->syssem != 0) retval = ksem_getvalue((*sem)->semid, sval); else { - _pthread_mutex_lock(&(*sem)->lock); *sval = (int)(*sem)->count; - _pthread_mutex_unlock(&(*sem)->lock); - retval = 0; } return (retval); diff --git a/lib/libc/gen/sem_init.3 b/lib/libc/gen/sem_init.3 index d765a57..52bcb75 100644 --- a/lib/libc/gen/sem_init.3 +++ b/lib/libc/gen/sem_init.3 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 15, 2000 +.Dd January 9, 2010 .Dt SEM_INIT 3 .Os .Sh NAME @@ -46,10 +46,19 @@ function initializes the unnamed semaphore pointed to by .Fa sem to have the value .Fa value . +.Pp A non-zero value for .Fa pshared -specifies a shared semaphore that can be used by multiple processes, which this -implementation is not capable of. +specifies a shared semaphore that can be used by multiple processes, +the semaphore should be located in shared memory region (see +.Xr mmap 2 , +.Xr shm_open 2 , +and +.Xr shmget 2 ) , +any process having read and write access to address +.Fa sem +can perform semaphore operations on +.Fa sem . .Pp Following a successful call to .Fn sem_init , @@ -78,8 +87,6 @@ argument exceeds .Dv SEM_VALUE_MAX . .It Bq Er ENOSPC Memory allocation error. -.It Bq Er EPERM -Unable to initialize a shared semaphore. .El .Sh SEE ALSO .Xr sem_destroy 3 , @@ -93,16 +100,3 @@ The .Fn sem_init function conforms to .St -p1003.1-96 . -.Pp -This implementation does not support shared semaphores, and reports this fact -by setting -.Va errno -to -.Er EPERM . -This is perhaps a stretch of the intention of -.Tn POSIX , -but is -compliant, with the caveat that -.Fn sem_init -always reports a permissions error when an attempt to create a shared semaphore -is made. diff --git a/lib/libc/gen/sem_new.c b/lib/libc/gen/sem_new.c new file mode 100644 index 0000000..ef4ba68 --- /dev/null +++ b/lib/libc/gen/sem_new.c @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "namespace.h" +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <machine/atomic.h> +#include <sys/umtx.h> +#include <limits.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <semaphore.h> +#include <unistd.h> +#include "un-namespace.h" + +__weak_reference(_sem_close, sem_close); +__weak_reference(_sem_destroy, sem_destroy); +__weak_reference(_sem_getvalue, sem_getvalue); +__weak_reference(_sem_init, sem_init); +__weak_reference(_sem_open, sem_open); +__weak_reference(_sem_post, sem_post); +__weak_reference(_sem_timedwait, sem_timedwait); +__weak_reference(_sem_trywait, sem_trywait); +__weak_reference(_sem_unlink, sem_unlink); +__weak_reference(_sem_wait, sem_wait); + +#define SEM_PREFIX "/tmp/SEMD" +#define SEM_MAGIC ((u_int32_t)0x73656d31) + +struct sem_nameinfo { + int open_count; + char *name; + sem_t *sem; + LIST_ENTRY(sem_nameinfo) next; +}; + +static pthread_once_t once = PTHREAD_ONCE_INIT; +static pthread_mutex_t sem_llock; +static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); + +static void +sem_prefork() +{ + + _pthread_mutex_lock(&sem_llock); +} + +static void +sem_postfork() +{ + _pthread_mutex_unlock(&sem_llock); +} + +static void +sem_child_postfork() +{ + _pthread_mutex_unlock(&sem_llock); +} + +static void +sem_module_init(void) +{ + pthread_mutexattr_t ma; + + _pthread_mutexattr_init(&ma); + _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); + _pthread_mutex_init(&sem_llock, &ma); + _pthread_mutexattr_destroy(&ma); + _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); +} + +static inline int +sem_check_validity(sem_t *sem) +{ + + if (sem->_magic == SEM_MAGIC) + return (0); + else { + errno = EINVAL; + return (-1); + } +} + +int +_sem_init(sem_t *sem, int pshared, unsigned int value) +{ + + if (value > SEM_VALUE_MAX) { + errno = EINVAL; + return (-1); + } + + bzero(sem, sizeof(sem_t)); + sem->_magic = SEM_MAGIC; + sem->_kern._count = (u_int32_t)value; + sem->_kern._has_waiters = 0; + sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; + return (0); +} + +sem_t * +_sem_open(const char *name, int flags, ...) +{ + char path[PATH_MAX]; + + struct stat sb; + va_list ap; + struct sem_nameinfo *ni = NULL; + sem_t *sem = NULL; + int fd = -1, mode, len, errsave; + int value = 0; + + if (name[0] != '/') { + errno = EINVAL; + return (SEM_FAILED); + } + name++; + + if (flags & ~(O_CREAT|O_EXCL)) { + errno = EINVAL; + return (SEM_FAILED); + } + + _pthread_once(&once, sem_module_init); + + _pthread_mutex_lock(&sem_llock); + LIST_FOREACH(ni, &sem_list, next) { + if (strcmp(name, ni->name) == 0) { + ni->open_count++; + sem = ni->sem; + _pthread_mutex_unlock(&sem_llock); + return (sem); + } + } + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, int); + value = va_arg(ap, int); + va_end(ap); + } + + len = sizeof(*ni) + strlen(name) + 1; + ni = (struct sem_nameinfo *)malloc(len); + if (ni == NULL) { + errno = ENOSPC; + goto error; + } + + ni->name = (char *)(ni+1); + strcpy(ni->name, name); + + strcpy(path, SEM_PREFIX); + if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { + errno = ENAMETOOLONG; + goto error; + } + + fd = _open(path, flags|O_RDWR, mode); + if (fd == -1) + goto error; + if (flock(fd, LOCK_EX) == -1) + goto error; + if (_fstat(fd, &sb)) { + flock(fd, LOCK_UN); + goto error; + } + if (sb.st_size < sizeof(sem_t)) { + sem_t tmp; + + tmp._magic = SEM_MAGIC; + tmp._kern._has_waiters = 0; + tmp._kern._count = value; + tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; + if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { + flock(fd, LOCK_UN); + goto error; + } + } + flock(fd, LOCK_UN); + sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_NOSYNC, fd, 0); + if (sem == MAP_FAILED) { + sem = NULL; + if (errno == ENOMEM) + errno = ENOSPC; + goto error; + } + if (sem->_magic != SEM_MAGIC) { + errno = EINVAL; + goto error; + } + ni->open_count = 1; + ni->sem = sem; + LIST_INSERT_HEAD(&sem_list, ni, next); + _pthread_mutex_unlock(&sem_llock); + _close(fd); + return (sem); + +error: + errsave = errno; + _pthread_mutex_unlock(&sem_llock); + if (fd != -1) + _close(fd); + if (sem != NULL) + munmap(sem, sizeof(sem_t)); + free(ni); + errno = errsave; + return (SEM_FAILED); +} + +int +_sem_close(sem_t *sem) +{ + struct sem_nameinfo *ni; + + if (sem_check_validity(sem) != 0) + return (-1); + + if (!(sem->_kern._flags & SEM_NAMED)) { + errno = EINVAL; + return (-1); + } + + _pthread_once(&once, sem_module_init); + + _pthread_mutex_lock(&sem_llock); + LIST_FOREACH(ni, &sem_list, next) { + if (sem == ni->sem) { + if (--ni->open_count > 0) { + _pthread_mutex_unlock(&sem_llock); + return (0); + } + else + break; + } + } + + if (ni) { + LIST_REMOVE(ni, next); + _pthread_mutex_unlock(&sem_llock); + munmap(sem, sizeof(*sem)); + free(ni); + return (0); + } + _pthread_mutex_unlock(&sem_llock); + errno = EINVAL; + return (-1); +} + +int +_sem_unlink(const char *name) +{ + char path[PATH_MAX]; + + if (name[0] != '/') { + errno = ENOENT; + return -1; + } + name++; + + strcpy(path, SEM_PREFIX); + if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { + errno = ENAMETOOLONG; + return (-1); + } + return unlink(path); +} + +int +_sem_destroy(sem_t *sem) +{ + + if (sem_check_validity(sem) != 0) + return (-1); + + if (sem->_kern._flags & SEM_NAMED) { + errno = EINVAL; + return (-1); + } + sem->_magic = 0; + return (0); +} + +int +_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) +{ + + if (sem_check_validity(sem) != 0) + return (-1); + + *sval = (int)sem->_kern._count; + return (0); +} + +static __inline int +usem_wake(struct _usem *sem) +{ + if (!sem->_has_waiters) + return (0); + return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); +} + +static __inline int +usem_wait(struct _usem *sem, const struct timespec *timeout) +{ + if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && + timeout->tv_nsec <= 0))) { + errno = ETIMEDOUT; + return (-1); + } + return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, + __DECONST(void*, timeout)); +} + +int +_sem_trywait(sem_t *sem) +{ + int val; + + if (sem_check_validity(sem) != 0) + return (-1); + + while ((val = sem->_kern._count) > 0) { + if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) + return (0); + } + errno = EAGAIN; + return (-1); +} + +static void +sem_cancel_handler(void *arg) +{ + sem_t *sem = arg; + + if (sem->_kern._has_waiters && sem->_kern._count) + usem_wake(&sem->_kern); +} + +#define TIMESPEC_SUB(dst, src, val) \ + do { \ + (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ + (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ + if ((dst)->tv_nsec < 0) { \ + (dst)->tv_sec--; \ + (dst)->tv_nsec += 1000000000; \ + } \ + } while (0) + + +static __inline int +enable_async_cancel(void) +{ + int old; + + _pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); + return (old); +} + +static __inline void +restore_async_cancel(int val) +{ + _pthread_setcanceltype(val, NULL); +} + +int +_sem_timedwait(sem_t * __restrict sem, + const struct timespec * __restrict abstime) +{ + struct timespec ts, ts2; + int val, retval, saved_cancel; + + if (sem_check_validity(sem) != 0) + return (-1); + + retval = 0; + for (;;) { + while ((val = sem->_kern._count) > 0) { + if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) + return (0); + } + + if (retval) + break; + + /* + * The timeout argument is only supposed to + * be checked if the thread would have blocked. + */ + if (abstime != NULL) { + if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { + errno = EINVAL; + return (-1); + } + clock_gettime(CLOCK_REALTIME, &ts); + TIMESPEC_SUB(&ts2, abstime, &ts); + } + pthread_cleanup_push(sem_cancel_handler, sem); + saved_cancel = enable_async_cancel(); + retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); + restore_async_cancel(saved_cancel); + pthread_cleanup_pop(0); + } + return (retval); +} + +int +_sem_wait(sem_t *sem) +{ + return _sem_timedwait(sem, NULL); +} + +/* + * POSIX: + * The sem_post() interface is reentrant with respect to signals and may be + * invoked from a signal-catching function. + * The implementation does not use lock, so it should be safe. + */ +int +_sem_post(sem_t *sem) +{ + + if (sem_check_validity(sem) != 0) + return (-1); + + atomic_add_rel_int(&sem->_kern._count, 1); + return usem_wake(&sem->_kern); +} diff --git a/lib/libc/gen/sem_open.3 b/lib/libc/gen/sem_open.3 index 772bdfb..3527605 100644 --- a/lib/libc/gen/sem_open.3 +++ b/lib/libc/gen/sem_open.3 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 15, 2003 +.Dd January 9, 2010 .Dt SEM_OPEN 3 .Os .Sh NAME @@ -58,6 +58,12 @@ The returned semaphore may be used in subsequent calls to and .Fn sem_close . .Pp +This implementation places strict requirements on the value of +.Fa name : +it must begin with a slash +.Pq Ql / +and contain no other slash characters. +.Pp The following bits may be set in the .Fa oflag argument: @@ -217,11 +223,3 @@ functions conform to .Sh HISTORY Support for named semaphores first appeared in .Fx 5.0 . -.Sh BUGS -This implementation places strict requirements on the value of -.Fa name : -it must begin with a slash -.Pq Ql / , -contain no other slash characters, -and be less than 14 characters in length -not including the terminating null character. diff --git a/lib/libc/gen/sleep.c b/lib/libc/gen/sleep.c index cdf5828..b807c2d 100644 --- a/lib/libc/gen/sleep.c +++ b/lib/libc/gen/sleep.c @@ -41,8 +41,7 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" unsigned int -__sleep(seconds) - unsigned int seconds; +__sleep(unsigned int seconds) { struct timespec time_to_sleep; struct timespec time_remaining; diff --git a/lib/libc/gen/sysconf.c b/lib/libc/gen/sysconf.c index 3c342ee..4618f32 100644 --- a/lib/libc/gen/sysconf.c +++ b/lib/libc/gen/sysconf.c @@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$"); #include <unistd.h> #include "../stdlib/atexit.h" -#include "../stdtime/tzfile.h" +#include "tzfile.h" /* from ../../../contrib/tzcode/stdtime */ #define _PATH_ZONEINFO TZDIR /* from tzfile.h */ diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 75f8668..143be16 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -28,7 +28,7 @@ .\" @(#)sysctl.3 8.4 (Berkeley) 5/9/95 .\" $FreeBSD$ .\" -.Dd January 28, 2009 +.Dd February 21, 2010 .Dt SYSCTL 3 .Os .Sh NAME @@ -42,9 +42,9 @@ .In sys/types.h .In sys/sysctl.h .Ft int -.Fn sysctl "int *name" "u_int namelen" "void *oldp" "size_t *oldlenp" "void *newp" "size_t newlen" +.Fn sysctl "const int *name" "u_int namelen" "void *oldp" "size_t *oldlenp" "const void *newp" "size_t newlen" .Ft int -.Fn sysctlbyname "const char *name" "void *oldp" "size_t *oldlenp" "void *newp" "size_t newlen" +.Fn sysctlbyname "const char *name" "void *oldp" "size_t *oldlenp" "const void *newp" "size_t newlen" .Ft int .Fn sysctlnametomib "const char *name" "int *mibp" "size_t *sizep" .Sh DESCRIPTION diff --git a/lib/libc/gen/sysctl.c b/lib/libc/gen/sysctl.c index eb6418e..fbc2c0c 100644 --- a/lib/libc/gen/sysctl.c +++ b/lib/libc/gen/sysctl.c @@ -43,15 +43,12 @@ __FBSDID("$FreeBSD$"); #include <unistd.h> #include <string.h> -extern int __sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen); +extern int __sysctl(const int *name, u_int namelen, void *oldp, + size_t *oldlenp, const void *newp, size_t newlen); int -sysctl(name, namelen, oldp, oldlenp, newp, newlen) - int *name; - u_int namelen; - void *oldp, *newp; - size_t *oldlenp, newlen; +sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp, + const void *newp, size_t newlen) { if (name[0] != CTL_USER) return (__sysctl(name, namelen, oldp, oldlenp, newp, newlen)); diff --git a/lib/libc/gen/sysctlbyname.c b/lib/libc/gen/sysctlbyname.c index 4510fe0..a2e0d5f 100644 --- a/lib/libc/gen/sysctlbyname.c +++ b/lib/libc/gen/sysctlbyname.c @@ -13,27 +13,19 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/sysctl.h> -#include <string.h> int -sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) +sysctlbyname(const char *name, void *oldp, size_t *oldlenp, + const void *newp, size_t newlen) { - int name2oid_oid[2]; int real_oid[CTL_MAXNAME+2]; int error; size_t oidlen; - name2oid_oid[0] = 0; /* This is magic & undocumented! */ - name2oid_oid[1] = 3; - - oidlen = sizeof(real_oid); - error = sysctl(name2oid_oid, 2, real_oid, &oidlen, (void *)name, - strlen(name)); + oidlen = sizeof(real_oid) / sizeof(int); + error = sysctlnametomib(name, real_oid, &oidlen); if (error < 0) - return error; - oidlen /= sizeof (int); + return (error); error = sysctl(real_oid, oidlen, oldp, oldlenp, newp, newlen); return (error); } - diff --git a/lib/libc/gen/sysctlnametomib.c b/lib/libc/gen/sysctlnametomib.c index 1515784..afca95d 100644 --- a/lib/libc/gen/sysctlnametomib.c +++ b/lib/libc/gen/sysctlnametomib.c @@ -48,8 +48,8 @@ sysctlnametomib(const char *name, int *mibp, size_t *sizep) oid[0] = 0; oid[1] = 3; - *sizep *= sizeof (int); - error = sysctl(oid, 2, mibp, sizep, (void *)name, strlen(name)); - *sizep /= sizeof (int); + *sizep *= sizeof(int); + error = sysctl(oid, 2, mibp, sizep, name, strlen(name)); + *sizep /= sizeof(int); return (error); } diff --git a/lib/libc/gen/termios.c b/lib/libc/gen/termios.c index 85ca4e3..fc21757 100644 --- a/lib/libc/gen/termios.c +++ b/lib/libc/gen/termios.c @@ -45,18 +45,14 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" int -tcgetattr(fd, t) - int fd; - struct termios *t; +tcgetattr(int fd, struct termios *t) { return (_ioctl(fd, TIOCGETA, t)); } int -tcsetattr(fd, opt, t) - int fd, opt; - const struct termios *t; +tcsetattr(int fd, int opt, const struct termios *t) { struct termios localterm; @@ -88,8 +84,7 @@ tcsetpgrp(int fd, pid_t pgrp) } pid_t -tcgetpgrp(fd) - int fd; +tcgetpgrp(int fd) { int s; @@ -123,25 +118,21 @@ tcsetsid(int fd, pid_t pid) } speed_t -cfgetospeed(t) - const struct termios *t; +cfgetospeed(const struct termios *t) { return (t->c_ospeed); } speed_t -cfgetispeed(t) - const struct termios *t; +cfgetispeed(const struct termios *t) { return (t->c_ispeed); } int -cfsetospeed(t, speed) - struct termios *t; - speed_t speed; +cfsetospeed(struct termios *t, speed_t speed) { t->c_ospeed = speed; @@ -149,9 +140,7 @@ cfsetospeed(t, speed) } int -cfsetispeed(t, speed) - struct termios *t; - speed_t speed; +cfsetispeed(struct termios *t, speed_t speed) { t->c_ispeed = speed; @@ -159,9 +148,7 @@ cfsetispeed(t, speed) } int -cfsetspeed(t, speed) - struct termios *t; - speed_t speed; +cfsetspeed(struct termios *t, speed_t speed) { t->c_ispeed = t->c_ospeed = speed; @@ -173,8 +160,7 @@ cfsetspeed(t, speed) * mode with no characters interpreted, 8-bit data path. */ void -cfmakeraw(t) - struct termios *t; +cfmakeraw(struct termios *t) { t->c_iflag &= ~(IMAXBEL|IXOFF|INPCK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IGNPAR); @@ -188,8 +174,7 @@ cfmakeraw(t) } int -tcsendbreak(fd, len) - int fd, len; +tcsendbreak(int fd, int len __unused) { struct timeval sleepytime; @@ -204,8 +189,7 @@ tcsendbreak(fd, len) } int -__tcdrain(fd) - int fd; +__tcdrain(int fd) { return (_ioctl(fd, TIOCDRAIN, 0)); } @@ -214,8 +198,7 @@ __weak_reference(__tcdrain, tcdrain); __weak_reference(__tcdrain, _tcdrain); int -tcflush(fd, which) - int fd, which; +tcflush(int fd, int which) { int com; @@ -237,8 +220,7 @@ tcflush(fd, which) } int -tcflow(fd, action) - int fd, action; +tcflow(int fd, int action) { struct termios term; u_char c; diff --git a/lib/libc/gen/time.3 b/lib/libc/gen/time.3 index 2770e49..d022e06 100644 --- a/lib/libc/gen/time.3 +++ b/lib/libc/gen/time.3 @@ -66,6 +66,7 @@ The function may fail for any of the reasons described in .Xr gettimeofday 2 . .Sh SEE ALSO +.Xr clock_gettime 2 , .Xr gettimeofday 2 , .Xr ctime 3 .Sh STANDARDS diff --git a/lib/libc/gen/time.c b/lib/libc/gen/time.c index 840f9b6..214dfce 100644 --- a/lib/libc/gen/time.c +++ b/lib/libc/gen/time.c @@ -37,13 +37,12 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> time_t -time(t) - time_t *t; +time(time_t *t) { - struct timeval tt; + struct timespec tt; time_t retval; - if (gettimeofday(&tt, (struct timezone *)0) < 0) + if (clock_gettime(CLOCK_SECOND, &tt) < 0) retval = -1; else retval = tt.tv_sec; diff --git a/lib/libc/gen/timezone.c b/lib/libc/gen/timezone.c index c59ae6b..5f0fa66 100644 --- a/lib/libc/gen/timezone.c +++ b/lib/libc/gen/timezone.c @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$"); #include <string.h> #define TZ_MAX_CHARS 255 -char *_tztab(); +char *_tztab(int, int); /* * timezone -- @@ -53,9 +53,7 @@ char *_tztab(); static char czone[TZ_MAX_CHARS]; /* space for zone name */ char * -timezone(zone, dst) - int zone, - dst; +timezone(int zone, int dst) { char *beg, *end; @@ -106,9 +104,7 @@ static struct zone { * STANDARD LIBRARY. */ char * -_tztab(zone,dst) - int zone; - int dst; +_tztab(int zone, int dst) { struct zone *zp; char sign; diff --git a/lib/libc/gen/tls.c b/lib/libc/gen/tls.c index 2756e94..9d84679 100644 --- a/lib/libc/gen/tls.c +++ b/lib/libc/gen/tls.c @@ -137,11 +137,11 @@ __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign __unused) if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE) return (oldtcb); - tcb = calloc(1, tls_static_space + tcbsize); + tcb = calloc(1, tls_static_space + tcbsize - TLS_TCB_SIZE); tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE); if (oldtcb != NULL) { - memcpy(tls, oldtcb, tls_static_space + TLS_TCB_SIZE); + memcpy(tls, oldtcb, tls_static_space); free(oldtcb); /* Adjust the DTV. */ diff --git a/lib/libc/gen/ttyname.3 b/lib/libc/gen/ttyname.3 index c7b8cbc..9420bbe 100644 --- a/lib/libc/gen/ttyname.3 +++ b/lib/libc/gen/ttyname.3 @@ -34,8 +34,7 @@ .Sh NAME .Nm ttyname , .Nm ttyname_r , -.Nm isatty , -.Nm ttyslot +.Nm isatty .Nd get name of associated terminal (tty) from file descriptor .Sh LIBRARY .Lb libc @@ -47,22 +46,8 @@ .Fn ttyname_r "int fd" "char *buf" "size_t len" .Ft int .Fn isatty "int fd" -.Ft int -.Fn ttyslot void .Sh DESCRIPTION -These functions operate on the system file descriptors for terminal -type devices. -These descriptors are not related to the standard -.Tn I/O -.Dv FILE -typedef, but refer to the special device files found in -.Pa /dev -and for which an entry exists -in the initialization file -.Pa /etc/ttys -or pseudo-terminals. -(See -.Xr ttys 5 . ) +These functions operate on file descriptors for terminal type devices. .Pp The .Fn isatty @@ -89,13 +74,6 @@ The .Fn ttyname_r function takes a buffer and length as arguments to avoid this problem. -.Pp -The -.Fn ttyslot -function -fetches the current process' control terminal number from the -.Xr ttys 5 -file entry. .Sh RETURN VALUES The .Fn ttyname @@ -110,17 +88,6 @@ The .Fn ttyname_r function returns 0 if successful. Otherwise an error number is returned. -.Pp -The -.Fn ttyslot -function -returns the unit number of the device file if found; otherwise -the value zero is returned. -.Sh FILES -.Bl -tag -width ".Pa /etc/ttys" -compact -.It Pa /dev/\(** -.It Pa /etc/ttys -.El .Sh ERRORS The .Fn ttyname_r @@ -138,14 +105,15 @@ argument is smaller than the length of the string to be returned. .El .Sh SEE ALSO -.Xr ioctl 2 , -.Xr ttys 5 +.Xr fdevname 3 , +.Xr ptsname 3 , +.Xr tcgetattr 3 , +.Xr tty 4 .Sh HISTORY The -.Fn isatty , -.Fn ttyname , +.Fn isatty and -.Fn ttyslot +.Fn ttyname functions appeared in .At v7 . diff --git a/lib/libc/gen/ttyslot.c b/lib/libc/gen/ttyslot.c index 31cc156..1de0837 100644 --- a/lib/libc/gen/ttyslot.c +++ b/lib/libc/gen/ttyslot.c @@ -33,33 +33,11 @@ static char sccsid[] = "@(#)ttyslot.c 8.1 (Berkeley) 6/4/93"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <paths.h> -#include <ttyent.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - int -ttyslot() +__ttyslot(void) { - struct ttyent *ttyp; - int slot; - int cnt; - char *name; - setttyent(); - for (cnt = 0; cnt < 3; ++cnt) - if ( (name = ttyname(cnt)) ) { - if (strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1) != 0) - break; - name += sizeof _PATH_DEV - 1; - for (slot = 1; (ttyp = getttyent()); ++slot) - if (!strcmp(ttyp->ty_name, name)) { - endttyent(); - return(slot); - } - break; - } - endttyent(); - return(0); + return (0); } + +__sym_compat(ttyslot, __ttyslot, FBSD_1.0); diff --git a/lib/libc/gen/tzset.3 b/lib/libc/gen/tzset.3 index 34ca238..8d37fca 100644 --- a/lib/libc/gen/tzset.3 +++ b/lib/libc/gen/tzset.3 @@ -310,14 +310,14 @@ time zone directory rules for .Tn POSIX Ns -style .Tn TZ Ns 's -.It Pa /usr/share/zoneinfo/GMT +.It Pa /usr/share/zoneinfo/Etc/GMT for .Tn UTC leap seconds .El .Pp If the file -.Pa /usr/share/zoneinfo/GMT +.Pa /usr/share/zoneinfo/UTC does not exist, .Tn UTC leap seconds are loaded from diff --git a/lib/libc/gen/usleep.c b/lib/libc/gen/usleep.c index 0454665..7d6559b 100644 --- a/lib/libc/gen/usleep.c +++ b/lib/libc/gen/usleep.c @@ -39,8 +39,7 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" int -__usleep(useconds) - useconds_t useconds; +__usleep(useconds_t useconds) { struct timespec time_to_sleep; diff --git a/lib/libc/gen/utxdb.c b/lib/libc/gen/utxdb.c new file mode 100644 index 0000000..6a85c05 --- /dev/null +++ b/lib/libc/gen/utxdb.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include <sys/endian.h> +#include <sys/param.h> +#include <sys/time.h> +#include <stdlib.h> +#include <string.h> +#include <utmpx.h> +#include "utxdb.h" +#include "un-namespace.h" + +#define UTOF_STRING(ut, fu, field) do { \ + strncpy((fu)->fu_ ## field, (ut)->ut_ ## field, \ + MIN(sizeof (fu)->fu_ ## field, sizeof (ut)->ut_ ## field)); \ +} while (0) +#define UTOF_ID(ut, fu) do { \ + memcpy((fu)->fu_id, (ut)->ut_id, \ + MIN(sizeof (fu)->fu_id, sizeof (ut)->ut_id)); \ +} while (0) +#define UTOF_PID(ut, fu) do { \ + (fu)->fu_pid = htobe32((ut)->ut_pid); \ +} while (0) +#define UTOF_TYPE(ut, fu) do { \ + (fu)->fu_type = (ut)->ut_type; \ +} while (0) +#define UTOF_TV(fu) do { \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + (fu)->fu_tv = htobe64((uint64_t)tv.tv_sec * 1000000 + \ + (uint64_t)tv.tv_usec); \ +} while (0) + +void +utx_to_futx(const struct utmpx *ut, struct futx *fu) +{ + + memset(fu, 0, sizeof *fu); + + switch (ut->ut_type) { + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + /* Extension: shutdown time. */ + case SHUTDOWN_TIME: + break; + case USER_PROCESS: + UTOF_ID(ut, fu); + UTOF_STRING(ut, fu, user); + UTOF_STRING(ut, fu, line); + /* Extension: host name. */ + UTOF_STRING(ut, fu, host); + UTOF_PID(ut, fu); + break; + case INIT_PROCESS: + UTOF_ID(ut, fu); + UTOF_PID(ut, fu); + break; + case LOGIN_PROCESS: + UTOF_ID(ut, fu); + UTOF_STRING(ut, fu, user); + UTOF_STRING(ut, fu, line); + UTOF_PID(ut, fu); + break; + case DEAD_PROCESS: + UTOF_ID(ut, fu); + UTOF_PID(ut, fu); + break; + default: + fu->fu_type = EMPTY; + return; + } + + UTOF_TYPE(ut, fu); + UTOF_TV(fu); +} + +#define FTOU_STRING(fu, ut, field) do { \ + strncpy((ut)->ut_ ## field, (fu)->fu_ ## field, \ + MIN(sizeof (ut)->ut_ ## field - 1, sizeof (fu)->fu_ ## field)); \ +} while (0) +#define FTOU_ID(fu, ut) do { \ + memcpy((ut)->ut_id, (fu)->fu_id, \ + MIN(sizeof (ut)->ut_id, sizeof (fu)->fu_id)); \ +} while (0) +#define FTOU_PID(fu, ut) do { \ + (ut)->ut_pid = be32toh((fu)->fu_pid); \ +} while (0) +#define FTOU_TYPE(fu, ut) do { \ + (ut)->ut_type = (fu)->fu_type; \ +} while (0) +#define FTOU_TV(fu, ut) do { \ + uint64_t t; \ + t = be64toh((fu)->fu_tv); \ + (ut)->ut_tv.tv_sec = t / 1000000; \ + (ut)->ut_tv.tv_usec = t % 1000000; \ +} while (0) + +struct utmpx * +futx_to_utx(const struct futx *fu) +{ + static struct utmpx *ut; + + if (ut == NULL) { + ut = calloc(1, sizeof *ut); + if (ut == NULL) + return (NULL); + } else { + memset(ut, 0, sizeof *ut); + } + + switch (fu->fu_type) { + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + /* Extension: shutdown time. */ + case SHUTDOWN_TIME: + break; + case USER_PROCESS: + FTOU_ID(fu, ut); + FTOU_STRING(fu, ut, user); + FTOU_STRING(fu, ut, line); + /* Extension: host name. */ + FTOU_STRING(fu, ut, host); + FTOU_PID(fu, ut); + break; + case INIT_PROCESS: + FTOU_ID(fu, ut); + FTOU_PID(fu, ut); + break; + case LOGIN_PROCESS: + FTOU_ID(fu, ut); + FTOU_STRING(fu, ut, user); + FTOU_STRING(fu, ut, line); + FTOU_PID(fu, ut); + break; + case DEAD_PROCESS: + FTOU_ID(fu, ut); + FTOU_PID(fu, ut); + break; + default: + ut->ut_type = EMPTY; + return (ut); + } + + FTOU_TYPE(fu, ut); + FTOU_TV(fu, ut); + return (ut); +} diff --git a/lib/libc/gen/utxdb.h b/lib/libc/gen/utxdb.h new file mode 100644 index 0000000..d9ebc93 --- /dev/null +++ b/lib/libc/gen/utxdb.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UTXDB_H_ +#define _UTXDB_H_ + +#include <stdint.h> + +#define _PATH_UTX_ACTIVE "/var/run/utx.active" +#define _PATH_UTX_LASTLOGIN "/var/log/utx.lastlogin" +#define _PATH_UTX_LOG "/var/log/utx.log" + +/* + * Entries in struct futx are ordered by how often they are used. In + * utx.log only entries will be written until the last non-zero byte, + * which means we want to put the hostname at the end. Most primitive + * records only store a ut_type and ut_tv, which means we want to store + * those at the front. + */ + +struct utmpx; + +struct futx { + uint8_t fu_type; + uint64_t fu_tv; + char fu_id[8]; + uint32_t fu_pid; + char fu_user[32]; + char fu_line[16]; + char fu_host[128]; +} __packed; + +void utx_to_futx(const struct utmpx *, struct futx *); +struct utmpx *futx_to_utx(const struct futx *); + +#endif /* !_UTXDB_H_ */ diff --git a/lib/libc/gen/wordexp.c b/lib/libc/gen/wordexp.c index a437543..bcab1f5 100644 --- a/lib/libc/gen/wordexp.c +++ b/lib/libc/gen/wordexp.c @@ -28,8 +28,10 @@ #include <sys/cdefs.h> #include <sys/types.h> #include <sys/wait.h> +#include <errno.h> #include <fcntl.h> #include <paths.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -73,6 +75,24 @@ wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) return (0); } +static size_t +we_read_fully(int fd, char *buffer, size_t len) +{ + size_t done; + ssize_t nread; + + done = 0; + do { + nread = _read(fd, buffer + done, len - done); + if (nread == -1 && errno == EINTR) + continue; + if (nread <= 0) + break; + done += nread; + } while (done != len); + return done; +} + /* * we_askshell -- * Use the `wordexp' /bin/sh builtin function to do most of the work @@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t *we, int flags) size_t sofs; /* Offset into we->we_strings */ size_t vofs; /* Offset into we->we_wordv */ pid_t pid; /* Process ID of child */ + pid_t wpid; /* waitpid return value */ int status; /* Child exit status */ + int error; /* Our return value */ + int serrno; /* errno to return */ char *ifs; /* IFS env. var. */ char *np, *p; /* Handy pointers */ char *nstrings; /* Temporary for realloc() */ char **nwv; /* Temporary for realloc() */ + sigset_t newsigblock, oldsigblock; + serrno = errno; if ((ifs = getenv("IFS")) == NULL) ifs = " \t\n"; if (pipe(pdes) < 0) return (WRDE_NOSPACE); /* XXX */ + (void)sigemptyset(&newsigblock); + (void)sigaddset(&newsigblock, SIGCHLD); + (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); if ((pid = fork()) < 0) { + serrno = errno; _close(pdes[0]); _close(pdes[1]); + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + errno = serrno; return (WRDE_NOSPACE); /* XXX */ } else if (pid == 0) { @@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t *we, int flags) int devnull; char *cmd; + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); _close(pdes[0]); if (_dup2(pdes[1], STDOUT_FILENO) < 0) _exit(1); @@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t *we, int flags) * the expanded words separated by nulls. */ _close(pdes[1]); - if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); + if (we_read_fully(pdes[0], wbuf, 8) != 8 || + we_read_fully(pdes[0], bbuf, 8) != 8) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; } wbuf[8] = bbuf[8] = '\0'; nwords = strtol(wbuf, NULL, 16); @@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t *we, int flags) if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + (flags & WRDE_DOOFFS ? we->we_offs : 0)) * sizeof(char *))) == NULL) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (WRDE_NOSPACE); + error = WRDE_NOSPACE; + goto cleanup; } we->we_wordv = nwv; if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (WRDE_NOSPACE); + error = WRDE_NOSPACE; + goto cleanup; } for (i = 0; i < vofs; i++) if (we->we_wordv[i] != NULL) we->we_wordv[i] += nstrings - we->we_strings; we->we_strings = nstrings; - if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { - _close(pdes[0]); - _waitpid(pid, &status, 0); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); + if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { + error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX; + serrno = errno; + goto cleanup; } - if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || - WEXITSTATUS(status) != 0) { - _close(pdes[0]); - return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); - } + error = 0; +cleanup: _close(pdes[0]); + do + wpid = _waitpid(pid, &status, 0); + while (wpid < 0 && errno == EINTR); + (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); + if (error != 0) { + errno = serrno; + return (error); + } + if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); /* * Break the null-terminated expanded word strings out into @@ -282,7 +320,7 @@ we_check(const char *words, int flags) if (c == '\0' || level != 0) return (WRDE_SYNTAX); } else - c = *--words; + --words; break; default: break; diff --git a/lib/libc/gmon/gmon.c b/lib/libc/gmon/gmon.c index dd85eec..fe769e8 100644 --- a/lib/libc/gmon/gmon.c +++ b/lib/libc/gmon/gmon.c @@ -132,7 +132,7 @@ monstartup(lowpc, highpc) } void -_mcleanup() +_mcleanup(void) { int fd; int fromindex; diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 50903f3..cb0b407 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -34,6 +34,7 @@ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ +#include <sys/_pthreadtypes.h> /* * This global flag is non-zero when a process has created one @@ -126,6 +127,8 @@ typedef enum { PJT_SETSPECIFIC, PJT_SIGMASK, PJT_TESTCANCEL, + PJT_CLEANUP_POP_IMP, + PJT_CLEANUP_PUSH_IMP, PJT_MAX } pjt_index_t; @@ -147,6 +150,12 @@ int _yp_check(char **); void _init_tls(void); /* + * Provides pthread_once()-like functionality for both single-threaded + * and multi-threaded applications. + */ +int _once(pthread_once_t *, void (*)(void)); + +/* * Set the TLS thread pointer */ void _set_tp(void *tp); diff --git a/lib/libc/include/namespace.h b/lib/libc/include/namespace.h index a65b929..6ba8bab 100644 --- a/lib/libc/include/namespace.h +++ b/lib/libc/include/namespace.h @@ -80,6 +80,7 @@ #define listen _listen #define nanosleep _nanosleep #define open _open +#define openat _openat #define poll _poll #define pthread_atfork _pthread_atfork #define pthread_attr_destroy _pthread_attr_destroy diff --git a/lib/libc/include/un-namespace.h b/lib/libc/include/un-namespace.h index 6b7f49a..00f0df2 100644 --- a/lib/libc/include/un-namespace.h +++ b/lib/libc/include/un-namespace.h @@ -61,6 +61,7 @@ #undef listen #undef nanosleep #undef open +#undef openat #undef poll #undef pthread_atfork #undef pthread_attr_destroy diff --git a/lib/libc/locale/isblank.3 b/lib/libc/locale/isblank.3 index 4cc6bbb..6734dcc 100644 --- a/lib/libc/locale/isblank.3 +++ b/lib/libc/locale/isblank.3 @@ -50,9 +50,9 @@ For any locale, this includes the following standard characters: .It "\&``\et''\t`` ''" .El .Pp -In the "C" locale +In the "C" locale, a successful .Fn isblank -successful test is limited to this characters only. +test is limited to these characters only. The value of the argument must be representable as an .Vt "unsigned char" or the value of diff --git a/lib/libc/locale/isgraph.3 b/lib/libc/locale/isgraph.3 index 4ba78e3..5e06e2e 100644 --- a/lib/libc/locale/isgraph.3 +++ b/lib/libc/locale/isgraph.3 @@ -50,7 +50,7 @@ The function tests for any printing character except space .Pq Ql "\ " and other -locale specific space-like characters. +locale-specific space-like characters. The value of the argument must be representable as an .Vt "unsigned char" or the value of diff --git a/lib/libc/locale/isprint.3 b/lib/libc/locale/isprint.3 index f340850..d8adf1d 100644 --- a/lib/libc/locale/isprint.3 +++ b/lib/libc/locale/isprint.3 @@ -47,7 +47,7 @@ .Sh DESCRIPTION The .Fn isprint -function tests for any printing character including space +function tests for any printing character, including space .Pq Ql "\ " . The value of the argument must be representable as an .Vt "unsigned char" diff --git a/lib/libc/locale/nl_langinfo.3 b/lib/libc/locale/nl_langinfo.3 index 8de1682..789cac2 100644 --- a/lib/libc/locale/nl_langinfo.3 +++ b/lib/libc/locale/nl_langinfo.3 @@ -53,7 +53,7 @@ with a category corresponding to the category of or to the category .Dv LC_ALL , -may overwrite buffer pointed by the return value. +may overwrite the buffer pointed to by the return value. .Sh RETURN VALUES In a locale where langinfo data is not defined, .Fn nl_langinfo diff --git a/lib/libc/locale/nl_langinfo.c b/lib/libc/locale/nl_langinfo.c index 9bd5082..26ca025 100644 --- a/lib/libc/locale/nl_langinfo.c +++ b/lib/libc/locale/nl_langinfo.c @@ -93,6 +93,12 @@ nl_langinfo(nl_item item) case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12: ret = (char*) __get_current_time_locale()->mon[_REL(ABMON_1)]; break; + case ALTMON_1: case ALTMON_2: case ALTMON_3: case ALTMON_4: + case ALTMON_5: case ALTMON_6: case ALTMON_7: case ALTMON_8: + case ALTMON_9: case ALTMON_10: case ALTMON_11: case ALTMON_12: + ret = (char*) + __get_current_time_locale()->alt_month[_REL(ALTMON_1)]; + break; case ERA: /* XXX: need to be implemented */ ret = ""; diff --git a/lib/libc/mips/Symbol.map b/lib/libc/mips/Symbol.map index 2bbdd6d..3f24c11 100644 --- a/lib/libc/mips/Symbol.map +++ b/lib/libc/mips/Symbol.map @@ -56,9 +56,27 @@ FBSDprivate_1.0 { __siglongjmp; __sys_vfork; _vfork; - end; /* XXX - Should this be _end (see sys/brk.S)? */ - curbrk; + _end; + __curbrk; minbrk; _brk; _sbrk; + + /* softfloat */ + __addsf3; + __adddf3; + __subsf3; + __subdf3; + __mulsf3; + __muldf3; + __divsf3; + __divdf3; + __floatsisf; + __floatsidf; + __fixsfsi; + __fixdfsi; + __fixunssfsi; + __fixunsdfsi; + __extendsfdf2; + __truncdfsf2; }; diff --git a/lib/libc/mips/gen/fpgetmask.c b/lib/libc/mips/gen/hardfloat/fpgetmask.c index 505a74c..505a74c 100644 --- a/lib/libc/mips/gen/fpgetmask.c +++ b/lib/libc/mips/gen/hardfloat/fpgetmask.c diff --git a/lib/libc/mips/gen/fpgetround.c b/lib/libc/mips/gen/hardfloat/fpgetround.c index 6d0f11a..6d0f11a 100644 --- a/lib/libc/mips/gen/fpgetround.c +++ b/lib/libc/mips/gen/hardfloat/fpgetround.c diff --git a/lib/libc/mips/gen/fpgetsticky.c b/lib/libc/mips/gen/hardfloat/fpgetsticky.c index 8028261..8028261 100644 --- a/lib/libc/mips/gen/fpgetsticky.c +++ b/lib/libc/mips/gen/hardfloat/fpgetsticky.c diff --git a/lib/libc/mips/gen/fpsetmask.c b/lib/libc/mips/gen/hardfloat/fpsetmask.c index 7abb3fd..7abb3fd 100644 --- a/lib/libc/mips/gen/fpsetmask.c +++ b/lib/libc/mips/gen/hardfloat/fpsetmask.c diff --git a/lib/libc/mips/gen/fpsetround.c b/lib/libc/mips/gen/hardfloat/fpsetround.c index 0205161..0205161 100644 --- a/lib/libc/mips/gen/fpsetround.c +++ b/lib/libc/mips/gen/hardfloat/fpsetround.c diff --git a/lib/libc/mips/gen/fpsetsticky.c b/lib/libc/mips/gen/hardfloat/fpsetsticky.c index e433671..e433671 100644 --- a/lib/libc/mips/gen/fpsetsticky.c +++ b/lib/libc/mips/gen/hardfloat/fpsetsticky.c diff --git a/lib/libc/mips/sys/brk.S b/lib/libc/mips/sys/brk.S index 580d7fa..aeaf791 100644 --- a/lib/libc/mips/sys/brk.S +++ b/lib/libc/mips/sys/brk.S @@ -42,15 +42,12 @@ __FBSDID("$FreeBSD$"); #endif /* LIBC_SCCS and not lint */ .globl _C_LABEL(minbrk) - .globl _C_LABEL(curbrk) + .globl _C_LABEL(__curbrk) .globl _C_LABEL(_end) .data _C_LABEL(minbrk): .word _C_LABEL(_end) -_C_LABEL(curbrk): - .word _C_LABEL(_end) - .text LEAF(__sys_brk) WEAK_ALIAS(brk, __sys_brk) @@ -67,7 +64,7 @@ LEAF(__sys_brk) li v0, SYS_break syscall bne a3, zero, 2f - sw a0, _C_LABEL(curbrk) + sw a0, _C_LABEL(__curbrk) move v0, zero j ra 2: diff --git a/lib/libc/mips/sys/ptrace.S b/lib/libc/mips/sys/ptrace.S index 86bc1e5..09b82c5 100644 --- a/lib/libc/mips/sys/ptrace.S +++ b/lib/libc/mips/sys/ptrace.S @@ -42,14 +42,26 @@ __FBSDID("$FreeBSD$"); #endif /* LIBC_SCCS and not lint */ LEAF(ptrace) + .frame sp, 40, ra + .mask 0x80000000, -8 #ifdef __ABICALLS__ .set noreorder .cpload t9 .set reorder #endif + subu sp, sp, 40 + sw ra, 32(sp) +#ifdef __ABICALLS__ + .cprestore 16 +#endif la t9, _C_LABEL(__error) # locate address of errno jalr t9 +#ifdef __ABICALLS__ + lw gp, 16(sp) +#endif sw zero, 0(v0) + lw ra, 32(sp) + addu sp, sp, 40 li v0, SYS_ptrace syscall bne a3, zero, 1f diff --git a/lib/libc/net/gai_strerror.c b/lib/libc/net/gai_strerror.c index 5611559..4f60f2d 100644 --- a/lib/libc/net/gai_strerror.c +++ b/lib/libc/net/gai_strerror.c @@ -30,7 +30,17 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "namespace.h" #include <netdb.h> +#if defined(NLS) +#include <nl_types.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "reentrant.h" +#endif +#include "un-namespace.h" /* Entries EAI_ADDRFAMILY (1) and EAI_NODATA (7) are obsoleted, but left */ /* for backward compatibility with userland code prior to 2553bis-02 */ @@ -52,9 +62,57 @@ static const char *ai_errlist[] = { "Argument buffer overflow" /* EAI_OVERFLOW */ }; +#if defined(NLS) +static char gai_buf[NL_TEXTMAX]; +static once_t gai_init_once = ONCE_INITIALIZER; +static thread_key_t gai_key; +static int gai_keycreated = 0; + +static void +gai_keycreate(void) +{ + gai_keycreated = (thr_keycreate(&gai_key, free) == 0); +} +#endif + const char * gai_strerror(int ecode) { +#if defined(NLS) + nl_catd catd; + char *buf; + + if (thr_main() != 0) + buf = gai_buf; + else { + if (thr_once(&gai_init_once, gai_keycreate) != 0 || + !gai_keycreated) + goto thr_err; + if ((buf = thr_getspecific(gai_key)) == NULL) { + if ((buf = malloc(sizeof(gai_buf))) == NULL) + goto thr_err; + if (thr_setspecific(gai_key, buf) != 0) { + free(buf); + goto thr_err; + } + } + } + + catd = catopen("libc", NL_CAT_LOCALE); + if (ecode > 0 && ecode < EAI_MAX) + strlcpy(buf, catgets(catd, 3, ecode, ai_errlist[ecode]), + sizeof(gai_buf)); + else if (ecode == 0) + strlcpy(buf, catgets(catd, 3, NL_MSGMAX - 1, "Success"), + sizeof(gai_buf)); + else + strlcpy(buf, catgets(catd, 3, NL_MSGMAX, "Unknown error"), + sizeof(gai_buf)); + catclose(catd); + return buf; + +thr_err: +#endif if (ecode >= 0 && ecode < EAI_MAX) return ai_errlist[ecode]; return "Unknown error"; diff --git a/lib/libc/net/getnameinfo.c b/lib/libc/net/getnameinfo.c index 0b59586..ffd34a1 100644 --- a/lib/libc/net/getnameinfo.c +++ b/lib/libc/net/getnameinfo.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_dl.h> #include <net/if_types.h> +#include <net/firewire.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> @@ -385,6 +386,7 @@ getnameinfo_link(const struct sockaddr *sa, socklen_t salen, { const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)(const void *)sa; + const struct fw_hwaddr *iha; int n; if (serv != NULL && servlen > 0) @@ -400,6 +402,15 @@ getnameinfo_link(const struct sockaddr *sa, socklen_t salen, } switch (sdl->sdl_type) { + case IFT_IEEE1394: + if (sdl->sdl_alen < sizeof(iha->sender_unique_ID_hi) + + sizeof(iha->sender_unique_ID_lo)) + return EAI_FAMILY; + iha = (const struct fw_hwaddr *)(const void *)LLADDR(sdl); + return hexname((const u_int8_t *)&iha->sender_unique_ID_hi, + sizeof(iha->sender_unique_ID_hi) + + sizeof(iha->sender_unique_ID_lo), + host, hostlen); /* * The following have zero-length addresses. * IFT_ATM (net/if_atmsubr.c) diff --git a/lib/libc/net/ip6opt.c b/lib/libc/net/ip6opt.c index 7b65d06..d999fd4 100644 --- a/lib/libc/net/ip6opt.c +++ b/lib/libc/net/ip6opt.c @@ -55,8 +55,7 @@ static void inet6_insert_padopt(u_char *p, int len); * byte, the length byte, and the option data. */ int -inet6_option_space(nbytes) - int nbytes; +inet6_option_space(int nbytes) { nbytes += 2; /* we need space for nxt-hdr and length fields */ return(CMSG_SPACE((nbytes + 7) & ~7)); @@ -68,10 +67,7 @@ inet6_option_space(nbytes) * success or -1 on an error. */ int -inet6_option_init(bp, cmsgp, type) - void *bp; - struct cmsghdr **cmsgp; - int type; +inet6_option_init(void *bp, struct cmsghdr **cmsgp, int type) { struct cmsghdr *ch = (struct cmsghdr *)bp; @@ -98,11 +94,8 @@ inet6_option_init(bp, cmsgp, type) * earlier. It must have a value between 0 and 7, inclusive. */ int -inet6_option_append(cmsg, typep, multx, plusy) - struct cmsghdr *cmsg; - const u_int8_t *typep; - int multx; - int plusy; +inet6_option_append(struct cmsghdr *cmsg, const u_int8_t *typep, int multx, + int plusy) { int padlen, optlen, off; u_char *bp = (u_char *)cmsg + cmsg->cmsg_len; @@ -171,11 +164,7 @@ inet6_option_append(cmsg, typep, multx, plusy) * */ u_int8_t * -inet6_option_alloc(cmsg, datalen, multx, plusy) - struct cmsghdr *cmsg; - int datalen; - int multx; - int plusy; +inet6_option_alloc(struct cmsghdr *cmsg, int datalen, int multx, int plusy) { int padlen, off; u_int8_t *bp = (u_char *)cmsg + cmsg->cmsg_len; @@ -238,9 +227,7 @@ inet6_option_alloc(cmsg, datalen, multx, plusy) * (RFC 2292, 6.3.5) */ int -inet6_option_next(cmsg, tptrp) - const struct cmsghdr *cmsg; - u_int8_t **tptrp; +inet6_option_next(const struct cmsghdr *cmsg, u_int8_t **tptrp) { struct ip6_ext *ip6e; int hdrlen, optlen; @@ -296,10 +283,7 @@ inet6_option_next(cmsg, tptrp) * it's a typo. The variable should be type of u_int8_t **. */ int -inet6_option_find(cmsg, tptrp, type) - const struct cmsghdr *cmsg; - u_int8_t **tptrp; - int type; +inet6_option_find(const struct cmsghdr *cmsg, u_int8_t **tptrp, int type) { struct ip6_ext *ip6e; int hdrlen, optlen; @@ -352,8 +336,7 @@ inet6_option_find(cmsg, tptrp, type) * calculated length and the limitation of the buffer. */ static int -ip6optlen(opt, lim) - u_int8_t *opt, *lim; +ip6optlen(u_int8_t *opt, u_int8_t *lim) { int optlen; diff --git a/lib/libc/net/sctp_bindx.3 b/lib/libc/net/sctp_bindx.3 index a2a2600..d047126 100644 --- a/lib/libc/net/sctp_bindx.3 +++ b/lib/libc/net/sctp_bindx.3 @@ -90,7 +90,7 @@ The call returns 0 on success and -1 upon failure. .Sh ERRORS The .Fn sctp_bindx -can return the following errors. +function can return the following errors: .Bl -tag -width Er .It Bq Er EINVAL This value is returned if the diff --git a/lib/libc/net/sctp_connectx.3 b/lib/libc/net/sctp_connectx.3 index 7b75f4a..d454bb9 100644 --- a/lib/libc/net/sctp_connectx.3 +++ b/lib/libc/net/sctp_connectx.3 @@ -44,7 +44,7 @@ .In sys/socket.h .In netinet/sctp.h .Ft int -.Fn sctp_connectx "int s" "struct sockaddr *" "int addrcnt" "sctp_assoc_t *" +.Fn sctp_connectx "int sd" "struct sockaddr *addrs" "int addrcnt" "sctp_assoc_t *id" .Sh DESCRIPTION The .Fn sctp_connectx @@ -75,7 +75,7 @@ the extra addresses sent in the call will be silently discarded from the association. On successful completion the provided -.Fa "sctp_assoc_t *" +.Fa id will be filled in with the association identification of the newly forming association. @@ -84,7 +84,7 @@ The call returns 0 on success and -1 upon failure. .Sh ERRORS The .Fn sctp_connectx -can return the following errors. +function can return the following errors: .Bl -tag -width Er .It Bq Er EINVAL An address listed has an invalid family or no diff --git a/lib/libc/net/sctp_getaddrlen.3 b/lib/libc/net/sctp_getaddrlen.3 index 325e2b0..ac7bfb3 100644 --- a/lib/libc/net/sctp_getaddrlen.3 +++ b/lib/libc/net/sctp_getaddrlen.3 @@ -76,7 +76,7 @@ system expects for the specific address family or -1. .Sh ERRORS The .Fn sctp_getaddrlen -function can return the following errors. +function can return the following errors: .Bl -tag -width Er .It Bq Er EINVAL The address family specified does NOT exist. diff --git a/lib/libc/net/sctp_getassocid.3 b/lib/libc/net/sctp_getassocid.3 index ee0926a..909b9c7 100644 --- a/lib/libc/net/sctp_getassocid.3 +++ b/lib/libc/net/sctp_getassocid.3 @@ -58,7 +58,7 @@ The call returns the association id upon success and .Sh ERRORS The .Fn sctp_getassocid -function can return the following errors. +function can return the following errors: .Bl -tag -width Er .It Bq Er ENOENT The address does not have an association setup to it. diff --git a/lib/libc/net/sctp_getpaddrs.3 b/lib/libc/net/sctp_getpaddrs.3 index 9f6d632..6ad5ea0 100644 --- a/lib/libc/net/sctp_getpaddrs.3 +++ b/lib/libc/net/sctp_getpaddrs.3 @@ -33,7 +33,7 @@ .\" $FreeBSD$ .\" .Dd December 15, 2006 -.Dt SCTP_GETPADDR 3 +.Dt SCTP_GETPADDRS 3 .Os .Sh NAME .Nm sctp_getpaddrs , @@ -64,7 +64,7 @@ array of socket addresses returned in the argument .Fa addrs upon success. .Pp -After the caller is through the function +After the caller is finished, the function .Fn sctp_freepaddrs or .Fn sctp_freeladdrs @@ -76,7 +76,7 @@ the number of addresses returned in .Fa addrs upon success. .Sh ERRORS -The functions can return the following errors. +The functions can return the following errors: .Bl -tag -width Er .It Bq Er EINVAL An address listed has an invalid family or no diff --git a/lib/libc/net/sctp_opt_info.3 b/lib/libc/net/sctp_opt_info.3 index 560b3e4..f010613 100644 --- a/lib/libc/net/sctp_opt_info.3 +++ b/lib/libc/net/sctp_opt_info.3 @@ -45,7 +45,7 @@ .In sys/socket.h .In netinet/sctp.h .Ft int -.Fn sctp_opt_info "int s" "sctp_assoc_t" "int opt" "void *arg" "socklen_t *size" +.Fn sctp_opt_info "int sd" "sctp_assoc_t id" "int opt" "void *arg" "socklen_t *size" .Sh DESCRIPTION The .Fn sctp_opt_info @@ -87,10 +87,12 @@ socket options. .Dv SCTP_PEER_AUTH_CHUNKS .Pp .Dv SCTP_LOCAL_AUTH_CHUNKS +.Sh RETURN VALUES +The call returns 0 on success and -1 upon error. .Sh ERRORS The .Fn sctp_opt_info -function can return the following errors. +function can return the following errors: .Bl -tag -width Er .It Bq Er EINVAL The argument diff --git a/lib/libc/net/sctp_recvmsg.3 b/lib/libc/net/sctp_recvmsg.3 index b9ccfb8..a5926bf 100644 --- a/lib/libc/net/sctp_recvmsg.3 +++ b/lib/libc/net/sctp_recvmsg.3 @@ -160,7 +160,7 @@ struct sctp_sndrcvinfo { .Pp The .Fa sinfo->sinfo_ppid -is an opaque 32 bit value that is passed transparently +field is an opaque 32 bit value that is passed transparently through the stack from the peer endpoint. Note that the stack passes this value without regard to byte order. @@ -180,12 +180,13 @@ as soon as possible. When this flag is absent the message was delivered in order within the stream it was received. .Pp +The .Fa sinfo->sinfo_stream -is the SCTP stream that the message was received on. +field is the SCTP stream that the message was received on. Streams in SCTP are reliable (or partially reliable) flows of ordered messages. .Pp -The +The .Fa sinfo->sinfo_context field is used only if the local application set an association level context with the @@ -197,7 +198,7 @@ association. .Pp The .Fa sinfo->sinfo_ssn -will hold the stream sequence number assigned +field will hold the stream sequence number assigned by the peer endpoint if the message is .Em not unordered. @@ -205,7 +206,7 @@ For unordered messages this field holds an undefined value. .Pp The .Fa sinfo->sinfo_tsn -holds a transport sequence number (TSN) that was assigned +field holds a transport sequence number (TSN) that was assigned to this message by the peer endpoint. For messages that fit in or less than the path MTU this will be the only TSN assigned. @@ -215,12 +216,12 @@ message. .Pp The .Fa sinfo->sinfo_cumtsn -holds the current cumulative acknowledgment point of +field holds the current cumulative acknowledgment point of the transport association. Note that this may be larger or smaller than the TSN assigned to the message itself. .Pp -The +The .Fa sinfo->sinfo_assoc_id is the unique association identification that was assigned to the association. @@ -232,10 +233,10 @@ setting various socket options on the specific association (see .Xr sctp 4 ) . .Pp -The +The .Fa sinfo->info_timetolive field is not used by -.Fa sctp_recvmsg . +.Fn sctp_recvmsg . .Sh RETURN VALUES The call returns the number of characters sent, or -1 if an error occurred. @@ -268,7 +269,7 @@ This generally indicates that the interface has stopped sending, but may be caused by transient congestion. .It Bq Er EHOSTUNREACH The remote host was unreachable. -.It Bq Er ENOTCON +.It Bq Er ENOTCONN On a one-to-one style socket no association exists. .It Bq Er ECONNRESET An abort was received by the stack while the user was diff --git a/lib/libc/net/sctp_send.3 b/lib/libc/net/sctp_send.3 index 2301730..8a4142c 100644 --- a/lib/libc/net/sctp_send.3 +++ b/lib/libc/net/sctp_send.3 @@ -111,7 +111,7 @@ The argument is an opaque 32 bit value that is passed transparently through the stack to the peer endpoint. It will be available on reception of a message (see -.Xr sctp_recvmsg 2 ) . +.Xr sctp_recvmsg 3 ) . Note that the stack passes this value without regard to byte order. .Pp @@ -294,7 +294,7 @@ if an error occurred. The .Fn sctp_send system call -fail if: +fails if: .Bl -tag -width Er .It Bq Er EBADF An invalid descriptor was specified. @@ -319,7 +319,7 @@ This generally indicates that the interface has stopped sending, but may be caused by transient congestion. .It Bq Er EHOSTUNREACH The remote host was unreachable. -.It Bq Er ENOTCON +.It Bq Er ENOTCONN On a one-to-one style socket no association exists. .It Bq Er ECONNRESET An abort was received by the stack while the user was diff --git a/lib/libc/net/sctp_sendmsg.3 b/lib/libc/net/sctp_sendmsg.3 index 61eec22..c54aaef 100644 --- a/lib/libc/net/sctp_sendmsg.3 +++ b/lib/libc/net/sctp_sendmsg.3 @@ -103,13 +103,13 @@ is set to the message is not transmitted. .Pp No indication of failure to deliver is implicit in a -.Xr sctp_sendmsg 2 +.Xr sctp_sendmsg 3 call. Locally detected errors are indicated by a return value of -1. .Pp If no space is available at the socket to hold the message to be transmitted, then -.Xr sctp_sendmsg 2 +.Xr sctp_sendmsg 3 normally blocks, unless the socket has been placed in non-blocking I/O mode. The @@ -123,7 +123,7 @@ argument is an opaque 32 bit value that is passed transparently through the stack to the peer endpoint. It will be available on reception of a message (see -.Xr sctp_recvmsg 2 ) . +.Xr sctp_recvmsg 3 ) . Note that the stack passes this value without regard to byte order. .Pp @@ -271,7 +271,7 @@ if an error occurred. The .Fn sctp_sendmsg system call -fail if: +fails if: .Bl -tag -width Er .It Bq Er EBADF An invalid descriptor was specified. @@ -296,7 +296,7 @@ This generally indicates that the interface has stopped sending, but may be caused by transient congestion. .It Bq Er EHOSTUNREACH The remote host was unreachable. -.It Bq Er ENOTCON +.It Bq Er ENOTCONN On a one-to-one style socket no association exists. .It Bq Er ECONNRESET An abort was received by the stack while the user was @@ -324,7 +324,7 @@ is not connected and is a one-to-one style socket. .Xr sendmsg 3 , .Xr sctp 4 .Sh BUGS -Because in the one-to-many style socket the +Because in the one-to-many style socket .Fn sctp_sendmsg or .Fn sctp_sendmsgx diff --git a/lib/libc/net/sctp_sys_calls.c b/lib/libc/net/sctp_sys_calls.c index 715fcbd..d876293 100644 --- a/lib/libc/net/sctp_sys_calls.c +++ b/lib/libc/net/sctp_sys_calls.c @@ -784,7 +784,7 @@ sctp_sendx(int sd, const void *msg, size_t msg_len, free(buf); if (ret != 0) { if (errno == EALREADY) { - no_end_cx = 1;; + no_end_cx = 1; goto continue_send; } return (ret); diff --git a/lib/libc/nls/C.msg b/lib/libc/nls/C.msg index aa2c4cc..2210df6 100644 --- a/lib/libc/nls/C.msg +++ b/lib/libc/nls/C.msg @@ -181,6 +181,16 @@ $ ENOATTR 87 Attribute not found $ EDOOFUS 88 Programming error +$ EBADMSG +89 Bad message +$ EMULTIHOP +90 Multihop attempted +$ ENOLINK +91 Link has been severed +$ EPROTO +92 Protocol error +$ ENOTCAPABLE +93 Capabilities insufficient $ $ strsignal() support catalog $ @@ -247,3 +257,39 @@ $ SIGUSR1 30 User defined signal 1 $ SIGUSR2 31 User defined signal 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsolete) +1 Address family for hostname not supported +$ EAI_AGAIN +2 Temporary failure in name resolution +$ EAI_BADFLAGS +3 Invalid value for ai_flags +$ EAI_FAIL +4 Non-recoverable failure in name resolution +$ EAI_FAMILY +5 ai_family not supported +$ EAI_MEMORY +6 Memory allocation failure +$ 7 (obsolete) +7 No address associated with hostname +$ EAI_NONAME +8 hostname nor servname provided, or not known +$ EAI_SERVICE +9 servname not supported for ai_socktype +$ EAI_SOCKTYPE +10 ai_socktype not supported +$ EAI_SYSTEM +11 System error returned in errno +$ EAI_BADHINTS +12 Invalid value for hints +$ EAI_PROTOCOL +13 Resolved protocol is unknown +$ EAI_OVERFLOW +14 Argument buffer overflow +$ 0 +32766 Success +$ NL_MSGMAX +32767 Unknown error diff --git a/lib/libc/nls/Makefile.inc b/lib/libc/nls/Makefile.inc index 9e19e85..f5c6885 100644 --- a/lib/libc/nls/Makefile.inc +++ b/lib/libc/nls/Makefile.inc @@ -20,8 +20,11 @@ NLS+= el_GR.ISO8859-7 NLS+= es_ES.ISO8859-1 NLS+= fi_FI.ISO8859-1 NLS+= fr_FR.ISO8859-1 +NLS+= gl_ES.ISO8859-1 NLS+= hu_HU.ISO8859-2 NLS+= it_IT.ISO8859-15 +NLS+= ja_JP.UTF-8 +NLS+= ja_JP.eucJP NLS+= ko_KR.UTF-8 NLS+= ko_KR.eucKR NLS+= mn_MN.UTF-8 diff --git a/lib/libc/nls/es_ES.ISO8859-1.msg b/lib/libc/nls/es_ES.ISO8859-1.msg index 8fc9c7d..2687c17 100644 --- a/lib/libc/nls/es_ES.ISO8859-1.msg +++ b/lib/libc/nls/es_ES.ISO8859-1.msg @@ -62,7 +62,7 @@ $ EFBIG $ ENOSPC 28 No queda espacio libre en el dispositivo $ ESPIPE -29 Illegal seek +29 Búsqueda ilegal $ EROFS 30 Sistema de ficheros de solo lectura $ EMLINK @@ -88,7 +88,7 @@ $ EMSGSIZE $ EPROTOTYPE 41 Tipo erróneo de protocolo para el socket $ ENOPROTOOPT -42 protocolo no disponible +42 Protocolo no disponible $ EPROTONOSUPPORT 43 Protocolo no contemplado $ ESOCKTNOSUPPORT @@ -176,23 +176,33 @@ $ EOVERFLOW $ ECANCELED 85 Operación cancelada $ EILSEQ -86 Illegal byte sequence +86 Secuencia de bytes ilegal $ ENOATTR 87 Atributo no encontrado $ EDOOFUS 88 Error de programación +$ EBADMSG +89 Mensaje inválido +$ EMULTIHOP +90 Intento de hop multiple +$ ENOLINK +91 El enlace se ha roto +$ EPROTO +92 Fallo de protocolo +$ ENOTCAPABLE +93 Habilidades insuficientes $ $ strsignal() support catalog $ $set 2 $ SIGHUP -1 Fín de línea (Hangup) +1 Fin de línea (Hangup) $ SIGINT 2 Interrumpido $ SIGQUIT 3 Terminado $ SIGILL -4 Illegal instruction +4 Instrucción ilegal $ SIGTRAP 5 Trace/BPT trap $ SIGABRT @@ -247,3 +257,39 @@ $ SIGUSR1 30 Señal definida por el usuario n1 $ SIGUSR2 31 Señal definida por el usuario n2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsoleto) +1 Tipo de dirección no contemplado +$ EAI_AGAIN +2 Error transitorio en la resolución de nombres +$ EAI_BADFLAGS +3 Valor inválido de ai_flags +$ EAI_FAIL +4 Error no recuperable en la resolución de nombres +$ EAI_FAMILY +5 ai_family no contemplado +$ EAI_MEMORY +6 Error en la asignación de memoria +$ 7 (obsoleto) +7 No hay dirección asociada con el nombre de máquina +$ EAI_NONAME +8 No se dispone nombre de máquina, ni nombre de servicio +$ EAI_SERVICE +9 Nombre de servicio no contemplado en ai_socktype +$ EAI_SOCKTYPE +10 ai_socktype no contemplado +$ EAI_SYSTEM +11 Error de sistema devuelto en errno +$ EAI_BADHINTS +12 Valor inválido de hints +$ EAI_PROTOCOL +13 Protocolo resuelto desconocido +$ EAI_OVERFLOW +14 Búfer de argumentos sobrepasado +$ 0 +32766 Éxito +$ NL_MSGMAX +32767 Error desconocido diff --git a/lib/libc/nls/gl_ES.ISO8859-1.msg b/lib/libc/nls/gl_ES.ISO8859-1.msg new file mode 100644 index 0000000..4255dc8 --- /dev/null +++ b/lib/libc/nls/gl_ES.ISO8859-1.msg @@ -0,0 +1,295 @@ +$ $FreeBSD$ +$ +$ Message catalog for gl_ES.ISO8859-1 locale +$ +$ strerror() support catalog +$ +$set 1 +$ EPERM +1 Operación non permitida +$ ENOENT +2 Ficheiro ou directorio inexistente +$ ESRCH +3 Proceso inexistente +$ EINTR +4 Chamada do sistema interrompida +$ EIO +5 Erro de Entrada/Saída +$ ENXIO +6 Dispositivo non configurado +$ E2BIG +7 A lista de argumentos é demasiado larga +$ ENOEXEC +8 Erro no formato do executable +$ EBADF +9 Descriptor incorrecto de ficheiro +$ ECHILD +10 Non hai procesos fillos +$ EDEADLK +11 Evitouse o bloqueo do recurso +$ ENOMEM +12 Non se puido asignar memoria +$ EACCES +13 Permiso denegado +$ EFAULT +14 Dirección incorrecta +$ ENOTBLK +15 Precísase un dispositivo de bloques +$ EBUSY +16 Dispositivo ocupado +$ EEXIST +17 O ficheiro xa existe +$ EXDEV +18 Enlace entre dispositivos +$ ENODEV +19 Operación inadecuada para este dispositivo +$ ENOTDIR +20 Non é un directorio +$ EISDIR +21 É un directorio +$ EINVAL +22 Argumento inadecuado +$ ENFILE +23 Hai demasiados ficheiros abertos no sistema +$ EMFILE +24 Hai demasiados ficheiros abertos +$ ENOTTY +25 ioctl inapropiado para o dispositivo +$ ETXTBSY +26 Ficheiro de texto ocupado +$ EFBIG +27 Ficheiro demasiado grande +$ ENOSPC +28 Non queda espacio libre no dispositivo +$ ESPIPE +29 seek inválido +$ EROFS +30 Sistema de ficheiros de só lectura +$ EMLINK +31 Demasiados enlaces +$ EPIPE +32 Canal (pipe) roto +$ EDOM +33 Argumento numérico fóra de rango +$ ERANGE +34 O resultado é demasiado grande +$ EAGAIN, EWOULDBLOCK +35 O recurso non está dispoñible temporalmente +$ EINPROGRESS +36 Operación en proceso +$ EALREADY +37 A operación xa estase executando +$ ENOTSOCK +38 Operación de socket inaceptable para o dispositivo +$ EDESTADDRREQ +39 Precísase unha dirección de destino +$ EMSGSIZE +40 Mensaxe demasiado largo +$ EPROTOTYPE +41 Tipo malo de protocolo para o socket +$ ENOPROTOOPT +42 Protocolo non dispoñible +$ EPROTONOSUPPORT +43 Protocolo non contemplado +$ ESOCKTNOSUPPORT +44 Tipo de socket non contemplado +$ EOPNOTSUPP +45 Operación non contemplada +$ EPFNOSUPPORT +46 Familia de protocolos non contemplada +$ EAFNOSUPPORT +47 Familia de direcciones non contemplada pola familia de protocolos +$ EADDRINUSE +48 A dirección xa está en uso +$ EADDRNOTAVAIL +49 Non se puido asignar a dirección requerida +$ ENETDOWN +50 A rede non funciona +$ ENETUNREACH +51 Non se puido acceder á rede +$ ENETRESET +52 A conexión de rede interrompiuse ao reinicializar +$ ECONNABORTED +53 Conexión perdida por problemas no software +$ ECONNRESET +54 O interlocutor reinicializou a conexión +$ ENOBUFS +55 Non queda espacio no búfer +$ EISCONN +56 O socket xa estaba conectado +$ ENOTCONN +57 O socket non está conectado +$ ESHUTDOWN +58 Non se pode enviar tras a desconexión do socket +$ ETOOMANYREFS +59 Demasiadas referencias: non poden unirse +$ ETIMEDOUT +60 O tempo de conexión expirou +$ ECONNREFUSED +61 Conexión rexeitada +$ ELOOP +62 Demasiados niveles de enlaces simbólicos +$ ENAMETOOLONG +63 Nome de ficheiro demasiado largo +$ EHOSTDOWN +64 A máquina está fóra de servicio +$ EHOSTUNREACH +65 Non hai ruta ata a máquina +$ ENOTEMPTY +66 Directorio non baleiro +$ EPROCLIM +67 Demasiados procesos +$ EUSERS +68 Demasiados usuarios +$ EDQUOT +69 Cuota de disco sobrepasada +$ ESTALE +70 Descriptor de ficheiro NFS inválido +$ EREMOTE +71 Ruta con demasiados niveles +$ EBADRPC +72 A estructura da RPC é mala +$ ERPCMISMATCH +73 A versión da RPC é mala +$ EPROGUNAVAIL +74 A RPC non está accesible +$ EPROGMISMATCH +75 Versión mala do programa +$ EPROCUNAVAIL +76 Procedemento malo para o programa +$ ENOLCK +77 Non hai bloqueos dispoñibles +$ ENOSYS +78 Función non realizada +$ EFTYPE +79 Tipo de ficheiro ou formato inapropiado +$ EAUTH +80 Erro de autenticación +$ ENEEDAUTH +81 Precísase un autenticador +$ EIDRM +82 Identificador eliminado +$ ENOMSG +83 Non hai mensaxes do tipo desexado +$ EOVERFLOW +84 Valor demasiado grande para se almacenar no tipo desexado +$ ECANCELED +85 Operación cancelada +$ EILSEQ +86 Secuencia inválida de byte +$ ENOATTR +87 Atributo non encontrado +$ EDOOFUS +88 Erro de programación +$ EBADMSG +89 Mensaxe inválido +$ EMULTIHOP +90 Intento de Multihop +$ ENOLINK +91 Enlace fortaleceuse +$ EPROTO +92 Erro de protocolo +$ ENOTCAPABLE +93 Habilidades non suficientes +$ +$ strsignal() support catalog +$ +$set 2 +$ SIGHUP +1 Fin de liña (Hangup) +$ SIGINT +2 Interrompido +$ SIGQUIT +3 Terminado +$ SIGILL +4 Instrucción inválida +$ SIGTRAP +5 Trace/BPT trap +$ SIGABRT +6 Abort trap +$ SIGEMT +7 EMT trap +$ SIGFPE +8 Excepción de coma flotante +$ SIGKILL +9 Matado +$ SIGBUS +10 Erro no bus +$ SIGSEGV +11 Fallo de segmentación +$ SIGSYS +12 Chamada ao sistema mala +$ SIGPIPE +13 Canal (pipe) roto +$ SIGALRM +14 Alarma do reloxo +$ SIGTERM +15 Terminado +$ SIGURG +16 Condición urxente de E/S +$ SIGSTOP +17 Detido (sinal) +$ SIGTSTP +18 Detido +$ SIGCONT +19 Continuando +$ SIGCHLD +20 Proceso fillo terminado +$ SIGTTIN +21 Detido (entrada tty) +$ SIGTTOU +22 Detido (sída tty) +$ SIGIO +23 E/S posible +$ SIGXCPU +24 Sobrepasouse o tempo límite da CPU +$ SIGXFSZ +25 Sobrepasouse o límite de tamaño de ficheiro +$ SIGVTALRM +26 Temporizador virtual caducado +$ SIGPROF +27 Temporizador de perfilación caducado +$ SIGWINCH +28 Cambios no tamaño de xanela +$ SIGINFO +29 Petición de información +$ SIGUSR1 +30 Sinal definida polo usuario no. 1 +$ SIGUSR2 +31 Sinal definida polo usuario no. 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsoleto) +1 Tipo de dirección non soportado +$ EAI_AGAIN +2 Erro temporal na resolución de nomes +$ EAI_BADFLAGS +3 Valor inválido de ai_flags +$ EAI_FAIL +4 Erro non recuperable na resolución de nomes +$ EAI_FAMILY +5 ai_family non soportado +$ EAI_MEMORY +6 Erro na asignación de memoria +$ 7 (obsoleto) +7 Non hai dirección asociada co nome de máquina +$ EAI_NONAME +8 Non se dispón nome de máquina, nin nome de servizo +$ EAI_SERVICE +9 Nome de servizo non soportado en ai_socktype +$ EAI_SOCKTYPE +10 ai_socktype non soportado +$ EAI_SYSTEM +11 Erro de sistema devolto en errno +$ EAI_BADHINTS +12 Valor inválido de hints +$ EAI_PROTOCOL +13 Protocolo resolto descoñecido +$ EAI_OVERFLOW +14 Búfer de argumentos excedido +$ 0 +32766 Éxito +$ NL_MSGMAX +32767 Erro descoñecido diff --git a/lib/libc/nls/hu_HU.ISO8859-2.msg b/lib/libc/nls/hu_HU.ISO8859-2.msg index 90b597b..330d486 100644 --- a/lib/libc/nls/hu_HU.ISO8859-2.msg +++ b/lib/libc/nls/hu_HU.ISO8859-2.msg @@ -181,6 +181,16 @@ $ ENOATTR 87 Attribútum nem található $ EDOOFUS 88 Programozási hiba +$ EBADMSG +89 Helytelen üzenet +$ EMULTIHOP +90 Multihop kísérlet +$ ENOLINK +91 A kapcsolat szigorítva lett +$ EPROTO +92 Protokol hiba +$ ENOTCAPABLE +93 Elégtelen képességek $ $ strsignal() support catalog $ @@ -247,3 +257,39 @@ $ SIGUSR1 30 Felhasználói szignál 1 $ SIGUSR2 31 Felhasználói szignál 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (elavult) +1 A hosztnévhez tartozó címcsalád nem támogatott +$ EAI_AGAIN +2 Ideiglenes hiba a névfeloldáskor +$ EAI_BADFLAGS +3 Érvénytelen ai_flags érték +$ EAI_FAIL +4 Nem helyreállítható hiba a névfeloldásban +$ EAI_FAMILY +5 ai_family nem támogatott +$ EAI_MEMORY +6 Memóriafoglalási hiba +$ 7 (elavult) +7 Nem tartozik cím í hosztnévhez +$ EAI_NONAME +8 Se hosztnév, se szolgáltatásnév nem áll rendelkezésre +$ EAI_SERVICE +9 Nem támogatott ai_socktype szolgáltatásnév +$ EAI_SOCKTYPE +10 ai_socktype nem támogatott +$ EAI_SYSTEM +11 Rendszerhiba jött vissza az errno változóban +$ EAI_BADHINTS +12 Érvénytelen hint érték +$ EAI_PROTOCOL +13 A feloldott protokol ismeretlen +$ EAI_OVERFLOW +14 Az argumentumok puffere túlcsordult +$ 0 +32766 Siker +$ NL_MSGMAX +32767 Ismeretlen hiba diff --git a/lib/libc/nls/ja_JP.UTF-8.msg b/lib/libc/nls/ja_JP.UTF-8.msg new file mode 100644 index 0000000..6be65fb --- /dev/null +++ b/lib/libc/nls/ja_JP.UTF-8.msg @@ -0,0 +1,295 @@ +$ $FreeBSD$ +$ +$ Message catalog for ja_JP.UTF-8 locale +$ +$ strerror() support catalog +$ +$set 1 +$ EPERM +1 許å¯ã•ã‚Œã¦ã„ãªã„æ“作ã§ã™ +$ ENOENT +2 ãã®ã‚ˆã†ãªãƒ•ã‚¡ã‚¤ãƒ«ã¾ãŸã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ã‚ã‚Šã¾ã›ã‚“ +$ ESRCH +3 ãã®ã‚ˆã†ãªãƒ—ãƒã‚»ã‚¹ã¯ã‚ã‚Šã¾ã›ã‚“ +$ EINTR +4 システムコールãŒä¸æ–ã•ã‚Œã¾ã—㟠+$ EIO +5 入出力エラーã§ã™ +$ ENXIO +6 デãƒã‚¤ã‚¹ãŒæº–å‚™ã•ã‚Œã¦ã„ã¾ã›ã‚“ +$ E2BIG +7 引数ã®ãƒªã‚¹ãƒˆãŒé•·ã™ãŽã¾ã™ +$ ENOEXEC +8 無効ãªå®Ÿè¡Œå½¢å¼ã§ã™ +$ EBADF +9 無効ãªãƒ•ã‚¡ã‚¤ãƒ«è¨˜è¿°åã§ã™ +$ ECHILD +10 åプãƒã‚»ã‚¹ãŒã‚ã‚Šã¾ã›ã‚“ +$ EDEADLK +11 リソースデッドãƒãƒƒã‚¯ã‚’回é¿ã—ã¾ã—㟠+$ ENOMEM +12 メモリã®å‰²ã‚Šå½“ã¦ãŒã§ãã¾ã›ã‚“ +$ EACCES +13 パーミッションãŒæ‹’絶ã•ã‚Œã¾ã—㟠+$ EFAULT +14 無効ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã§ã™ +$ ENOTBLK +15 ブãƒãƒƒã‚¯ãƒ‡ãƒã‚¤ã‚¹ãŒè¦æ±‚ã•ã‚Œã¦ã„ã¾ã™ +$ EBUSY +16 デãƒã‚¤ã‚¹ãŒãƒ“ジー状態ã§ã™ +$ EEXIST +17 ファイルãŒå˜åœ¨ã—ã¾ã™ +$ EXDEV +18 デãƒã‚¤ã‚¹ã‚’ã¾ãŸãリンクã§ã™ +$ ENODEV +19 デãƒã‚¤ã‚¹ãŒå¯¾å¿œã—ã¦ãªã„æ“作ã§ã™ +$ ENOTDIR +20 ディレクトリã§ã¯ã‚ã‚Šã¾ã›ã‚“ +$ EISDIR +21 ディレクトリã§ã™ +$ EINVAL +22 無効ãªå¼•æ•°ã§ã™ +$ ENFILE +23 システム内ã§ã‚ªãƒ¼ãƒ—ンã•ã‚Œã¦ã„るファイルãŒå¤šã™ãŽã¾ã™ +$ EMFILE +24 オープンã—ã¦ã„るファイルãŒå¤šã™ãŽã¾ã™ +$ ENOTTY +25 デãƒã‚¤ã‚¹ãŒå¯¾å¿œã—ã¦ã„ãªã„ ioctl ã§ã™ +$ ETXTBSY +26 テã‚ストファイルãŒãƒ“ジー状態ã§ã™ +$ EFBIG +27 ファイルãŒå¤§ãã™ãŽã¾ã™ +$ ENOSPC +28 デãƒã‚¤ã‚¹ã®ç©ºãé ˜åŸŸä¸è¶³ã§ã™ +$ ESPIPE +29 無効ãªã‚·ãƒ¼ã‚¯ã§ã™ +$ EROFS +30 èªã¿è¾¼ã¿å°‚用ファイルシステムã§ã™ +$ EMLINK +31 リンク数ãŒå¤šã™ãŽã¾ã™ +$ EPIPE +32 パイプãŒç ´å£Šã•ã‚Œã¦ã¾ã—㟠+$ EDOM +33 数値引数ãŒç¯„囲外ã§ã™ +$ ERANGE +34 çµæžœãŒå¤§ãéŽãŽã¾ã™ +$ EAGAIN, EWOULDBLOCK +35 リソースãŒä¸€æ™‚çš„ã«åˆ©ç”¨ã§ãã¾ã›ã‚“ +$ EINPROGRESS +36 æ“作ãŒç¾åœ¨é€²è¡Œä¸ã§ã™ +$ EALREADY +37 æ“作ã¯æ—¢ã«é€²è¡Œä¸ã§ã™ +$ ENOTSOCK +38 ソケットã§ãªã„ã‚‚ã®ã«ã¤ã„ã¦ã‚½ã‚±ãƒƒãƒˆæ“作を行ã„ã¾ã—㟠+$ EDESTADDRREQ +39 宛先アドレスãŒè¦æ±‚ã•ã‚Œã¦ã„ã¾ã™ +$ EMSGSIZE +40 メッセージãŒé•·ã™ãŽã¾ã™ +$ EPROTOTYPE +41 ソケットãŒå¯¾å¿œã—ã¦ã„ãªã„プãƒãƒˆã‚³ãƒ«ã‚¿ã‚¤ãƒ—ã§ã™ +$ ENOPROTOOPT +42 利用ã§ããªã„プãƒãƒˆã‚³ãƒ«ã§ã™ +$ EPROTONOSUPPORT +43 対応ã—ã¦ã„ãªã„プãƒãƒˆã‚³ãƒ«ã§ã™ +$ ESOCKTNOSUPPORT +44 対応ã—ã¦ã„ãªã„ソケットタイプã§ã™ +$ EOPNOTSUPP +45 対応ã—ã¦ã„ãªã„æ“作ã§ã™ +$ EPFNOSUPPORT +46 対応ã—ã¦ã„ãªã„プãƒãƒˆã‚³ãƒ«ãƒ•ã‚¡ãƒŸãƒªã§ã™ +$ EAFNOSUPPORT +47 プãƒãƒˆã‚³ãƒ«ãƒ•ã‚¡ãƒŸãƒªãŒå¯¾å¿œã—ã¦ã„ãªã„アドレスファミリãŒæŒ‡å®šã•ã‚Œã¾ã—㟠+$ EADDRINUSE +48 アドレスãŒæ—¢ã«ä½¿ç”¨ä¸ã§ã™ +$ EADDRNOTAVAIL +49 è¦æ±‚ã•ã‚ŒãŸã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’割り当ã¦ã§ãã¾ã›ã‚“ +$ ENETDOWN +50 ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒãƒ€ã‚¦ãƒ³ã—ã¦ã„ã¾ã™ +$ ENETUNREACH +51 ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«åˆ°é”ã§ãã¾ã›ã‚“ +$ ENETRESET +52 リセットã«ã‚ˆã‚Šãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®æŽ¥ç¶šãŒå¤±ã‚ã‚Œã¾ã—㟠+$ ECONNABORTED +53 ソフトウェアã«ã‚ˆã£ã¦æŽ¥ç¶šãŒåˆ‡æ–ã•ã‚Œã¾ã—㟠+$ ECONNRESET +54 接続ãŒé€šä¿¡ç›¸æ‰‹ã«ã‚ˆã£ã¦ãƒªã‚»ãƒƒãƒˆã•ã‚Œã¾ã—㟠+$ ENOBUFS +55 ãƒãƒƒãƒ•ã‚¡ã®å®¹é‡ä¸è¶³ã§ã™ +$ EISCONN +56 ソケットã¯æ—¢ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã™ +$ ENOTCONN +57 ソケットã¯æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã›ã‚“ +$ ESHUTDOWN +58 ソケットã®ã‚·ãƒ£ãƒƒãƒˆãƒ€ã‚¦ãƒ³ã®å¾Œã§é€ä¿¡ãŒã§ãã¾ã›ã‚“ +$ ETOOMANYREFS +59 処ç†é™ç•Œã‚’超ãˆã‚‹å¤šé‡å‚ç…§ã§ã™ +$ ETIMEDOUT +60 æ“作ãŒã‚¿ã‚¤ãƒ アウトã—ã¾ã—㟠+$ ECONNREFUSED +61 接続ãŒæ‹’絶ã•ã‚Œã¾ã—㟠+$ ELOOP +62 処ç†é™ç•Œã‚’超ãˆã‚‹ã‚·ãƒ³ãƒœãƒªãƒƒã‚¯ãƒªãƒ³ã‚¯ãƒ¬ãƒ™ãƒ«ã§ã™ +$ ENAMETOOLONG +63 ファイルåãŒé•·ã™ãŽã¾ã™ +$ EHOSTDOWN +64 ホストãŒãƒ€ã‚¦ãƒ³ã—ã¦ã„ã¾ã™ +$ EHOSTUNREACH +65 ホストã¸ã®çµŒè·¯ãŒã‚ã‚Šã¾ã›ã‚“ +$ ENOTEMPTY +66 ディレクトリãŒç©ºã§ã¯ã‚ã‚Šã¾ã›ã‚“ +$ EPROCLIM +67 プãƒã‚»ã‚¹ãŒå¤šã™ãŽã¾ã™ +$ EUSERS +68 ユーザãŒå¤šã™ãŽã¾ã™ +$ EDQUOT +69 ディスククォータãŒè¶…éŽã—ã¾ã—㟠+$ ESTALE +70 失効ã—㟠NFS ファイルãƒãƒ³ãƒ‰ãƒ«ã§ã™ +$ EREMOTE +71 パスä¸ã®ãƒªãƒ¢ãƒ¼ãƒˆã®ãƒ¬ãƒ™ãƒ«ãŒå¤šã™ãŽã¾ã™ +$ EBADRPC +72 無効㪠RPC æ§‹é€ ä½“ã§ã™ +$ ERPCMISMATCH +73 RPC ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒé–“é•ã£ã¦ã„ã¾ã™ +$ EPROGUNAVAIL +74 RPC プãƒã‚°ãƒ©ãƒ ãŒåˆ©ç”¨ã§ãã¾ã›ã‚“ +$ EPROGMISMATCH +75 プãƒã‚°ãƒ©ãƒ ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒåˆã£ã¦ã„ã¾ã›ã‚“ +$ EPROCUNAVAIL +76 プãƒã‚°ãƒ©ãƒ ã§ã¯åˆ©ç”¨ã§ããªã„ procedure ã§ã™ +$ ENOLCK +77 ãƒãƒƒã‚¯ãŒåˆ©ç”¨ã§ãã¾ã›ã‚“ +$ ENOSYS +78 関数ãŒå®Ÿè£…ã•ã‚Œã¦ã„ã¾ã›ã‚“ +$ EFTYPE +79 ファイルã®åž‹ã¾ãŸã¯å½¢å¼ãŒä¸é©åˆ‡ã§ã™ +$ EAUTH +80 èªè¨¼ã‚¨ãƒ©ãƒ¼ã§ã™ +$ ENEEDAUTH +81 èªè¨¼ç‰©ãŒå¿…è¦ã§ã™ +$ EIDRM +82 è˜åˆ¥åã¯å‰Šé™¤ã•ã‚Œã¾ã—㟠+$ ENOMSG +83 è¦æ±‚ã•ã‚ŒãŸåž‹ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒã‚ã‚Šã¾ã›ã‚“ +$ EOVERFLOW +84 データ型ã«æ ¼ç´ã™ã‚‹ã«ã¯å¤§ãã™ãŽã‚‹å€¤ã§ã™ +$ ECANCELED +85 処ç†ãŒã‚ャンセルã•ã‚Œã¾ã—㟠+$ EILSEQ +86 ä¸æ£ãªãƒã‚¤ãƒˆåˆ—ã§ã™ +$ ENOATTR +87 ãã®ã‚ˆã†ãªå±žæ€§ã¯ã‚ã‚Šã¾ã›ã‚“ +$ EDOOFUS +88 プãƒã‚°ãƒ©ãƒŸãƒ³ã‚°ã‚¨ãƒ©ãƒ¼ã§ã™ +$ EBADMSG +89 無効ãªãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã§ã™ +$ EMULTIHOP +90 マルãƒãƒ›ãƒƒãƒ—ãŒè©¦ã¿ã‚‰ã‚Œã¾ã—㟠+$ ENOLINK +91 リンクãŒåˆ‡æ–ã•ã‚Œã¦ã„ã¾ã™ +$ EPROTO +92 プãƒãƒˆã‚³ãƒ«ã‚¨ãƒ©ãƒ¼ã§ã™ +$ ENOTCAPABLE +93 ケーパビリティãŒä¸è¶³ã§ã™ +$ +$ strsignal() support catalog +$ +$set 2 +$ SIGHUP +1 ãƒãƒ³ã‚°ã‚¢ãƒƒãƒ— +$ SIGINT +2 割り込㿠+$ SIGQUIT +3 ä¸æ– +$ SIGILL +4 ä¸æ£å‘½ä»¤ +$ SIGTRAP +5 トレース/BPT トラップ +$ SIGABRT +6 アボートトラップ +$ SIGEMT +7 EMT トラップ +$ SIGFPE +8 浮動å°æ•°ç‚¹ä¾‹å¤– +$ SIGKILL +9 Kill ã•ã‚ŒãŸ +$ SIGBUS +10 ãƒã‚¹ã‚¨ãƒ©ãƒ¼ +$ SIGSEGV +11 セグメンテーションé•å +$ SIGSYS +12 å˜åœ¨ã—ãªã„システムコール +$ SIGPIPE +13 ãƒ‘ã‚¤ãƒ—ç ´å£Š +$ SIGALRM +14 アラームクãƒãƒƒã‚¯ +$ SIGTERM +15 終了 +$ SIGURG +16 ç·Šæ€¥å…¥å‡ºåŠ›çŠ¶æ³ +$ SIGSTOP +17 一時åœæ¢ (シグナル) +$ SIGTSTP +18 一時åœæ¢ +$ SIGCONT +19 継続 +$ SIGCHLD +20 åプãƒã‚»ã‚¹ã®çµ‚了 +$ SIGTTIN +21 一時åœæ¢ (tty 入力) +$ SIGTTOU +22 一時åœæ¢ (tty 出力) +$ SIGIO +23 入出力å¯èƒ½ +$ SIGXCPU +24 CPU 時間ã®åˆ¶é™è¶…éŽ +$ SIGXFSZ +25 ファイルサイズã®åˆ¶é™è¶…éŽ +$ SIGVTALRM +26 仮想タイマã®æœŸé™è¶…éŽ +$ SIGPROF +27 プãƒãƒ•ã‚¡ã‚¤ãƒ«ã‚¿ã‚¤ãƒžã®æœŸé™è¶…éŽ +$ SIGWINCH +28 ウィンドウサイズã®å¤‰åŒ– +$ SIGINFO +29 æƒ…å ±è¦æ±‚ +$ SIGUSR1 +30 ユーザ定義シグナル 1 +$ SIGUSR2 +31 ユーザ定義シグナル 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsolete) +1 ホストåã®ã‚¢ãƒ‰ãƒ¬ã‚¹ãƒ•ã‚¡ãƒŸãƒªãƒ¼ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¾ã›ã‚“ +$ EAI_AGAIN +2 åå‰è§£æ±ºã§ã®ä¸€æ™‚çš„ãªå¤±æ•— +$ EAI_BADFLAGS +3 ai_flags ã®å€¤ãŒç„¡åŠ¹ +$ EAI_FAIL +4 åå‰è§£æ±ºã§ã®å›žå¾©ä¸èƒ½ãªå¤±æ•— +$ EAI_FAMILY +5 ai_family ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¾ã›ã‚“ +$ EAI_MEMORY +6 メモリ割り当ã¦å¤±æ•— +$ 7 (obsolete) +7 ホストåã«å¯¾å¿œã™ã‚‹ã‚¢ãƒ‰ãƒ¬ã‚¹ã¯ã‚ã‚Šã¾ã›ã‚“ +$ EAI_NONAME +8 ホストåã‹ã‚µãƒ¼ãƒ“スåãŒæŒ‡å®šã•ã‚Œãªã„ã€ã¾ãŸã¯ä¸æ˜Ž +$ EAI_SERVICE +9 サービスå㯠ai_socktype ã«å¯¾ã—ã¦ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¾ã›ã‚“ +$ EAI_SOCKTYPE +10 ai_socktype ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¾ã›ã‚“ +$ EAI_SYSTEM +11 システムエラーã€errno å‚ç…§ +$ EAI_BADHINTS +12 hints ã®å€¤ãŒç„¡åŠ¹ +$ EAI_PROTOCOL +13 解決ã•ã‚ŒãŸãƒ—ãƒãƒˆã‚³ãƒ«ã¯ä¸æ˜Žã§ã™ +$ EAI_OVERFLOW +14 引数ãƒãƒƒãƒ•ã‚¡ã‚ªãƒ¼ãƒãƒ•ãƒãƒ¼ +$ 0 +32766 æˆåŠŸ +$ NL_MSGMAX +32767 ä¸æ˜Žãªã‚¨ãƒ©ãƒ¼ diff --git a/lib/libc/nls/ja_JP.eucJP.msg b/lib/libc/nls/ja_JP.eucJP.msg new file mode 100644 index 0000000..e3fac48 --- /dev/null +++ b/lib/libc/nls/ja_JP.eucJP.msg @@ -0,0 +1,295 @@ +$ $FreeBSD$ +$ +$ Message catalog for ja_JP.eucJP locale +$ +$ strerror() support catalog +$ +$set 1 +$ EPERM +1 µö²Ä¤µ¤ì¤Æ¤¤¤Ê¤¤Áàºî¤Ç¤¹ +$ ENOENT +2 ¤½¤Î¤è¤¦¤Ê¥Õ¥¡¥¤¥ë¤Þ¤¿¤Ï¥Ç¥£¥ì¥¯¥È¥ê¤Ï¤¢¤ê¤Þ¤»¤ó +$ ESRCH +3 ¤½¤Î¤è¤¦¤Ê¥×¥í¥»¥¹¤Ï¤¢¤ê¤Þ¤»¤ó +$ EINTR +4 ¥·¥¹¥Æ¥à¥³¡¼¥ë¤¬ÃæÃǤµ¤ì¤Þ¤·¤¿ +$ EIO +5 Æþ½ÐÎÏ¥¨¥é¡¼¤Ç¤¹ +$ ENXIO +6 ¥Ç¥Ð¥¤¥¹¤¬½àÈ÷¤µ¤ì¤Æ¤¤¤Þ¤»¤ó +$ E2BIG +7 °ú¿ô¤Î¥ê¥¹¥È¤¬Ä¹¤¹¤®¤Þ¤¹ +$ ENOEXEC +8 ̵¸ú¤Ê¼Â¹Ô·Á¼°¤Ç¤¹ +$ EBADF +9 ̵¸ú¤Ê¥Õ¥¡¥¤¥ëµ½Ò»Ò¤Ç¤¹ +$ ECHILD +10 »Ò¥×¥í¥»¥¹¤¬¤¢¤ê¤Þ¤»¤ó +$ EDEADLK +11 ¥ê¥½¡¼¥¹¥Ç¥Ã¥É¥í¥Ã¥¯¤ò²óÈò¤·¤Þ¤·¤¿ +$ ENOMEM +12 ¥á¥â¥ê¤Î³ä¤êÅö¤Æ¤¬¤Ç¤¤Þ¤»¤ó +$ EACCES +13 ¥Ñ¡¼¥ß¥Ã¥·¥ç¥ó¤¬µñÀ䤵¤ì¤Þ¤·¤¿ +$ EFAULT +14 ̵¸ú¤Ê¥¢¥É¥ì¥¹¤Ç¤¹ +$ ENOTBLK +15 ¥Ö¥í¥Ã¥¯¥Ç¥Ð¥¤¥¹¤¬Í׵ᤵ¤ì¤Æ¤¤¤Þ¤¹ +$ EBUSY +16 ¥Ç¥Ð¥¤¥¹¤¬¥Ó¥¸¡¼¾õÂ֤Ǥ¹ +$ EEXIST +17 ¥Õ¥¡¥¤¥ë¤¬Â¸ºß¤·¤Þ¤¹ +$ EXDEV +18 ¥Ç¥Ð¥¤¥¹¤ò¤Þ¤¿¤°¥ê¥ó¥¯¤Ç¤¹ +$ ENODEV +19 ¥Ç¥Ð¥¤¥¹¤¬Âбþ¤·¤Æ¤Ê¤¤Áàºî¤Ç¤¹ +$ ENOTDIR +20 ¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó +$ EISDIR +21 ¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤¹ +$ EINVAL +22 ̵¸ú¤Ê°ú¿ô¤Ç¤¹ +$ ENFILE +23 ¥·¥¹¥Æ¥àÆâ¤Ç¥ª¡¼¥×¥ó¤µ¤ì¤Æ¤¤¤ë¥Õ¥¡¥¤¥ë¤¬Â¿¤¹¤®¤Þ¤¹ +$ EMFILE +24 ¥ª¡¼¥×¥ó¤·¤Æ¤¤¤ë¥Õ¥¡¥¤¥ë¤¬Â¿¤¹¤®¤Þ¤¹ +$ ENOTTY +25 ¥Ç¥Ð¥¤¥¹¤¬Âбþ¤·¤Æ¤¤¤Ê¤¤ ioctl ¤Ç¤¹ +$ ETXTBSY +26 ¥Æ¥¥¹¥È¥Õ¥¡¥¤¥ë¤¬¥Ó¥¸¡¼¾õÂ֤Ǥ¹ +$ EFBIG +27 ¥Õ¥¡¥¤¥ë¤¬Â礤¹¤®¤Þ¤¹ +$ ENOSPC +28 ¥Ç¥Ð¥¤¥¹¤Î¶õ¤ÎΰèÉÔ¤Ǥ¹ +$ ESPIPE +29 ̵¸ú¤Ê¥·¡¼¥¯¤Ç¤¹ +$ EROFS +30 Æɤ߹þ¤ßÀìÍÑ¥Õ¥¡¥¤¥ë¥·¥¹¥Æ¥à¤Ç¤¹ +$ EMLINK +31 ¥ê¥ó¥¯¿ô¤¬Â¿¤¹¤®¤Þ¤¹ +$ EPIPE +32 ¥Ñ¥¤¥×¤¬Ç˲õ¤µ¤ì¤Æ¤Þ¤·¤¿ +$ EDOM +33 ¿ôÃÍ°ú¿ô¤¬Èϰϳ°¤Ç¤¹ +$ ERANGE +34 ·ë²Ì¤¬Â礲᤮¤Þ¤¹ +$ EAGAIN, EWOULDBLOCK +35 ¥ê¥½¡¼¥¹¤¬°ì»þŪ¤ËÍøÍѤǤ¤Þ¤»¤ó +$ EINPROGRESS +36 Áàºî¤¬¸½ºß¿Ê¹ÔÃæ¤Ç¤¹ +$ EALREADY +37 Áàºî¤Ï´û¤Ë¿Ê¹ÔÃæ¤Ç¤¹ +$ ENOTSOCK +38 ¥½¥±¥Ã¥È¤Ç¤Ê¤¤¤â¤Î¤Ë¤Ä¤¤¤Æ¥½¥±¥Ã¥ÈÁàºî¤ò¹Ô¤¤¤Þ¤·¤¿ +$ EDESTADDRREQ +39 °¸À襢¥É¥ì¥¹¤¬Í׵ᤵ¤ì¤Æ¤¤¤Þ¤¹ +$ EMSGSIZE +40 ¥á¥Ã¥»¡¼¥¸¤¬Ä¹¤¹¤®¤Þ¤¹ +$ EPROTOTYPE +41 ¥½¥±¥Ã¥È¤¬Âбþ¤·¤Æ¤¤¤Ê¤¤¥×¥í¥È¥³¥ë¥¿¥¤¥×¤Ç¤¹ +$ ENOPROTOOPT +42 ÍøÍѤǤ¤Ê¤¤¥×¥í¥È¥³¥ë¤Ç¤¹ +$ EPROTONOSUPPORT +43 Âбþ¤·¤Æ¤¤¤Ê¤¤¥×¥í¥È¥³¥ë¤Ç¤¹ +$ ESOCKTNOSUPPORT +44 Âбþ¤·¤Æ¤¤¤Ê¤¤¥½¥±¥Ã¥È¥¿¥¤¥×¤Ç¤¹ +$ EOPNOTSUPP +45 Âбþ¤·¤Æ¤¤¤Ê¤¤Áàºî¤Ç¤¹ +$ EPFNOSUPPORT +46 Âбþ¤·¤Æ¤¤¤Ê¤¤¥×¥í¥È¥³¥ë¥Õ¥¡¥ß¥ê¤Ç¤¹ +$ EAFNOSUPPORT +47 ¥×¥í¥È¥³¥ë¥Õ¥¡¥ß¥ê¤¬Âбþ¤·¤Æ¤¤¤Ê¤¤¥¢¥É¥ì¥¹¥Õ¥¡¥ß¥ê¤¬»ØÄꤵ¤ì¤Þ¤·¤¿ +$ EADDRINUSE +48 ¥¢¥É¥ì¥¹¤¬´û¤Ë»ÈÍÑÃæ¤Ç¤¹ +$ EADDRNOTAVAIL +49 Í׵ᤵ¤ì¤¿¥¢¥É¥ì¥¹¤ò³ä¤êÅö¤Æ¤Ç¤¤Þ¤»¤ó +$ ENETDOWN +50 ¥Í¥Ã¥È¥ï¡¼¥¯¤¬¥À¥¦¥ó¤·¤Æ¤¤¤Þ¤¹ +$ ENETUNREACH +51 ¥Í¥Ã¥È¥ï¡¼¥¯¤ËÅþã¤Ç¤¤Þ¤»¤ó +$ ENETRESET +52 ¥ê¥»¥Ã¥È¤Ë¤è¤ê¥Í¥Ã¥È¥ï¡¼¥¯¤ÎÀܳ¤¬¼º¤ï¤ì¤Þ¤·¤¿ +$ ECONNABORTED +53 ¥½¥Õ¥È¥¦¥§¥¢¤Ë¤è¤Ã¤ÆÀܳ¤¬ÀÚÃǤµ¤ì¤Þ¤·¤¿ +$ ECONNRESET +54 Àܳ¤¬ÄÌ¿®Áê¼ê¤Ë¤è¤Ã¤Æ¥ê¥»¥Ã¥È¤µ¤ì¤Þ¤·¤¿ +$ ENOBUFS +55 ¥Ð¥Ã¥Õ¥¡¤ÎÍÆÎÌÉÔ¤Ǥ¹ +$ EISCONN +56 ¥½¥±¥Ã¥È¤Ï´û¤ËÀܳ¤µ¤ì¤Æ¤¤¤Þ¤¹ +$ ENOTCONN +57 ¥½¥±¥Ã¥È¤ÏÀܳ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó +$ ESHUTDOWN +58 ¥½¥±¥Ã¥È¤Î¥·¥ã¥Ã¥È¥À¥¦¥ó¤Î¸å¤ÇÁ÷¿®¤¬¤Ç¤¤Þ¤»¤ó +$ ETOOMANYREFS +59 ½èÍý¸Â³¦¤òĶ¤¨¤ë¿½Å»²¾È¤Ç¤¹ +$ ETIMEDOUT +60 Áàºî¤¬¥¿¥¤¥à¥¢¥¦¥È¤·¤Þ¤·¤¿ +$ ECONNREFUSED +61 Àܳ¤¬µñÀ䤵¤ì¤Þ¤·¤¿ +$ ELOOP +62 ½èÍý¸Â³¦¤òĶ¤¨¤ë¥·¥ó¥Ü¥ê¥Ã¥¯¥ê¥ó¥¯¥ì¥Ù¥ë¤Ç¤¹ +$ ENAMETOOLONG +63 ¥Õ¥¡¥¤¥ë̾¤¬Ä¹¤¹¤®¤Þ¤¹ +$ EHOSTDOWN +64 ¥Û¥¹¥È¤¬¥À¥¦¥ó¤·¤Æ¤¤¤Þ¤¹ +$ EHOSTUNREACH +65 ¥Û¥¹¥È¤Ø¤Î·ÐÏ©¤¬¤¢¤ê¤Þ¤»¤ó +$ ENOTEMPTY +66 ¥Ç¥£¥ì¥¯¥È¥ê¤¬¶õ¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó +$ EPROCLIM +67 ¥×¥í¥»¥¹¤¬Â¿¤¹¤®¤Þ¤¹ +$ EUSERS +68 ¥æ¡¼¥¶¤¬Â¿¤¹¤®¤Þ¤¹ +$ EDQUOT +69 ¥Ç¥£¥¹¥¯¥¯¥©¡¼¥¿¤¬Ä¶²á¤·¤Þ¤·¤¿ +$ ESTALE +70 ¼º¸ú¤·¤¿ NFS ¥Õ¥¡¥¤¥ë¥Ï¥ó¥É¥ë¤Ç¤¹ +$ EREMOTE +71 ¥Ñ¥¹Ãæ¤Î¥ê¥â¡¼¥È¤Î¥ì¥Ù¥ë¤¬Â¿¤¹¤®¤Þ¤¹ +$ EBADRPC +72 ̵¸ú¤Ê RPC ¹½Â¤ÂΤǤ¹ +$ ERPCMISMATCH +73 RPC ¥Ð¡¼¥¸¥ç¥ó¤¬´Ö°ã¤Ã¤Æ¤¤¤Þ¤¹ +$ EPROGUNAVAIL +74 RPC ¥×¥í¥°¥é¥à¤¬ÍøÍѤǤ¤Þ¤»¤ó +$ EPROGMISMATCH +75 ¥×¥í¥°¥é¥à¤Î¥Ð¡¼¥¸¥ç¥ó¤¬¹ç¤Ã¤Æ¤¤¤Þ¤»¤ó +$ EPROCUNAVAIL +76 ¥×¥í¥°¥é¥à¤Ç¤ÏÍøÍѤǤ¤Ê¤¤ procedure ¤Ç¤¹ +$ ENOLCK +77 ¥í¥Ã¥¯¤¬ÍøÍѤǤ¤Þ¤»¤ó +$ ENOSYS +78 ´Ø¿ô¤¬¼ÂÁõ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó +$ EFTYPE +79 ¥Õ¥¡¥¤¥ë¤Î·¿¤Þ¤¿¤Ï·Á¼°¤¬ÉÔŬÀڤǤ¹ +$ EAUTH +80 ǧ¾Ú¥¨¥é¡¼¤Ç¤¹ +$ ENEEDAUTH +81 ǧ¾Úʪ¤¬É¬ÍפǤ¹ +$ EIDRM +82 ¼±Ê̻ҤϺï½ü¤µ¤ì¤Þ¤·¤¿ +$ ENOMSG +83 Í׵ᤵ¤ì¤¿·¿¤Î¥á¥Ã¥»¡¼¥¸¤¬¤¢¤ê¤Þ¤»¤ó +$ EOVERFLOW +84 ¥Ç¡¼¥¿·¿¤Ë³ÊǼ¤¹¤ë¤Ë¤ÏÂ礤¹¤®¤ëÃͤǤ¹ +$ ECANCELED +85 ½èÍý¤¬¥¥ã¥ó¥»¥ë¤µ¤ì¤Þ¤·¤¿ +$ EILSEQ +86 ÉÔÀµ¤Ê¥Ð¥¤¥ÈÎó¤Ç¤¹ +$ ENOATTR +87 ¤½¤Î¤è¤¦¤Ê°À¤Ï¤¢¤ê¤Þ¤»¤ó +$ EDOOFUS +88 ¥×¥í¥°¥é¥ß¥ó¥°¥¨¥é¡¼¤Ç¤¹ +$ EBADMSG +89 ̵¸ú¤Ê¥á¥Ã¥»¡¼¥¸¤Ç¤¹ +$ EMULTIHOP +90 ¥Þ¥ë¥Á¥Û¥Ã¥×¤¬»î¤ß¤é¤ì¤Þ¤·¤¿ +$ ENOLINK +91 ¥ê¥ó¥¯¤¬ÀÚÃǤµ¤ì¤Æ¤¤¤Þ¤¹ +$ EPROTO +92 ¥×¥í¥È¥³¥ë¥¨¥é¡¼¤Ç¤¹ +$ ENOTCAPABLE +93 ¥±¡¼¥Ñ¥Ó¥ê¥Æ¥£¤¬ÉÔ¤Ǥ¹ +$ +$ strsignal() support catalog +$ +$set 2 +$ SIGHUP +1 ¥Ï¥ó¥°¥¢¥Ã¥× +$ SIGINT +2 ³ä¤ê¹þ¤ß +$ SIGQUIT +3 ÃæÃÇ +$ SIGILL +4 ÉÔÀµÌ¿Îá +$ SIGTRAP +5 ¥È¥ì¡¼¥¹/BPT ¥È¥é¥Ã¥× +$ SIGABRT +6 ¥¢¥Ü¡¼¥È¥È¥é¥Ã¥× +$ SIGEMT +7 EMT ¥È¥é¥Ã¥× +$ SIGFPE +8 ÉâÆ°¾®¿ôÅÀÎã³° +$ SIGKILL +9 Kill ¤µ¤ì¤¿ +$ SIGBUS +10 ¥Ð¥¹¥¨¥é¡¼ +$ SIGSEGV +11 ¥»¥°¥á¥ó¥Æ¡¼¥·¥ç¥ó°ãÈ¿ +$ SIGSYS +12 ¸ºß¤·¤Ê¤¤¥·¥¹¥Æ¥à¥³¡¼¥ë +$ SIGPIPE +13 ¥Ñ¥¤¥×Ç˲õ +$ SIGALRM +14 ¥¢¥é¡¼¥à¥¯¥í¥Ã¥¯ +$ SIGTERM +15 ½ªÎ» +$ SIGURG +16 ¶ÛµÞÆþ½ÐÎϾõ¶· +$ SIGSTOP +17 °ì»þÄä»ß (¥·¥°¥Ê¥ë) +$ SIGTSTP +18 °ì»þÄä»ß +$ SIGCONT +19 ·Ñ³ +$ SIGCHLD +20 »Ò¥×¥í¥»¥¹¤Î½ªÎ» +$ SIGTTIN +21 °ì»þÄä»ß (tty ÆþÎÏ) +$ SIGTTOU +22 °ì»þÄä»ß (tty ½ÐÎÏ) +$ SIGIO +23 Æþ½ÐÎϲÄǽ +$ SIGXCPU +24 CPU »þ´Ö¤ÎÀ©¸ÂĶ²á +$ SIGXFSZ +25 ¥Õ¥¡¥¤¥ë¥µ¥¤¥º¤ÎÀ©¸ÂĶ²á +$ SIGVTALRM +26 ²¾ÁÛ¥¿¥¤¥Þ¤Î´ü¸ÂĶ²á +$ SIGPROF +27 ¥×¥í¥Õ¥¡¥¤¥ë¥¿¥¤¥Þ¤Î´ü¸ÂĶ²á +$ SIGWINCH +28 ¥¦¥£¥ó¥É¥¦¥µ¥¤¥º¤ÎÊѲ½ +$ SIGINFO +29 ¾ðÊóÍ×µá +$ SIGUSR1 +30 ¥æ¡¼¥¶ÄêµÁ¥·¥°¥Ê¥ë 1 +$ SIGUSR2 +31 ¥æ¡¼¥¶ÄêµÁ¥·¥°¥Ê¥ë 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsolete) +1 ¥Û¥¹¥È̾¤Î¥¢¥É¥ì¥¹¥Õ¥¡¥ß¥ê¡¼¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Þ¤»¤ó +$ EAI_AGAIN +2 ̾Á°²ò·è¤Ç¤Î°ì»þŪ¤Ê¼ºÇÔ +$ EAI_BADFLAGS +3 ai_flags ¤ÎÃͤ¬Ìµ¸ú +$ EAI_FAIL +4 ̾Á°²ò·è¤Ç¤Î²óÉüÉÔǽ¤Ê¼ºÇÔ +$ EAI_FAMILY +5 ai_family ¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Þ¤»¤ó +$ EAI_MEMORY +6 ¥á¥â¥ê³ä¤êÅö¤Æ¼ºÇÔ +$ 7 (obsolete) +7 ¥Û¥¹¥È̾¤ËÂбþ¤¹¤ë¥¢¥É¥ì¥¹¤Ï¤¢¤ê¤Þ¤»¤ó +$ EAI_NONAME +8 ¥Û¥¹¥È̾¤«¥µ¡¼¥Ó¥¹Ì¾¤¬»ØÄꤵ¤ì¤Ê¤¤¡¢¤Þ¤¿¤ÏÉÔÌÀ +$ EAI_SERVICE +9 ¥µ¡¼¥Ó¥¹Ì¾¤Ï ai_socktype ¤ËÂФ·¤Æ¥µ¥Ý¡¼¥È¤µ¤ì¤Þ¤»¤ó +$ EAI_SOCKTYPE +10 ai_socktype ¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Þ¤»¤ó +$ EAI_SYSTEM +11 ¥·¥¹¥Æ¥à¥¨¥é¡¼¡¢errno »²¾È +$ EAI_BADHINTS +12 hints ¤ÎÃͤ¬Ìµ¸ú +$ EAI_PROTOCOL +13 ²ò·è¤µ¤ì¤¿¥×¥í¥È¥³¥ë¤ÏÉÔÌÀ¤Ç¤¹ +$ EAI_OVERFLOW +14 °ú¿ô¥Ð¥Ã¥Õ¥¡¥ª¡¼¥Ð¥Õ¥í¡¼ +$ 0 +32766 À®¸ù +$ NL_MSGMAX +32767 ÉÔÌÀ¤Ê¥¨¥é¡¼ diff --git a/lib/libc/nls/ko_KR.UTF-8.msg b/lib/libc/nls/ko_KR.UTF-8.msg index 4fc4e78..60e8a12 100644 --- a/lib/libc/nls/ko_KR.UTF-8.msg +++ b/lib/libc/nls/ko_KR.UTF-8.msg @@ -181,6 +181,16 @@ $ ENOATTR 87 ì†ì„±ì„ ì°¾ì„ ìˆ˜ 없습니다 $ EDOOFUS 88 í”„ë¡œê·¸ëž¨ìƒ ì˜¤ë¥˜ìž…ë‹ˆë‹¤ +$ EBADMSG +89 ìž˜ëª»ëœ ë©”ì‹œì§€ìž…ë‹ˆë‹¤ +$ EMULTIHOP +90 ë©€í‹°í™‰ì´ ì‹œë„ë˜ì—ˆìŠµë‹ˆë‹¤ +$ ENOLINK +91 ì—°ê²°ì´ ëŠê²¼ìŠµë‹ˆë‹¤ +$ EPROTO +92 í”„ë¡œí† ì½œì´ ìž˜ëª»ë˜ì—ˆìŠµë‹ˆë‹¤ +$ ENOTCAPABLE +93 ì ‘ê·¼ ëŠ¥ë ¥ì´ ì¶©ë¶„ì¹˜ 않습니다 $ $ strsignal() support catalog $ @@ -230,7 +240,7 @@ $ SIGTTIN $ SIGTTOU 22 ì •ì§€ (í„°ë¯¸ë„ ì¶œë ¥) $ SIGIO -23 I/O possible +23 I/O 가능 $ SIGXCPU 24 CPU 사용 시간 초과 $ SIGXFSZ @@ -247,3 +257,39 @@ $ SIGUSR1 30 ì‚¬ìš©ìž ì •ì˜ ì‹œê·¸ë„ 1 $ SIGUSR2 31 ì‚¬ìš©ìž ì •ì˜ ì‹œê·¸ë„ 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsolete) +1 호스트 ì´ë¦„ì´ ì§€ì›í•˜ì§€ 않는 주소군입니다 +$ EAI_AGAIN +2 주소 ë³€í™˜ì— ì¼ì‹œì 으로 실패했습니다 +$ EAI_BADFLAGS +3 ìž˜ëª»ëœ ai_flags입니다 +$ EAI_FAIL +4 주소 ë³€í™˜ì— ì™„ì „ížˆ 실패했습니다 +$ EAI_FAMILY +5 지ì›ë˜ì§€ 않는 ai_family입니다 +$ EAI_MEMORY +6 메모리 í• ë‹¹ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤ +$ 7 (obsolete) +7 호스트 ì´ë¦„ê³¼ ì¼ì¹˜í•˜ëŠ” 주소가 없습니다 +$ EAI_NONAME +8 호스트 ì´ë¦„ ë˜ëŠ” 서비스 ì´ë¦„ì´ ì§€ì •ë˜ì§€ 않았거나 ì•Œ 수 없습니다 +$ EAI_SERVICE +9 서비스 ì´ë¦„ì€ ai_socktypeì—ì„œ 지ì›ë˜ì§€ 않습니다 +$ EAI_SOCKTYPE +10 지ì›ë˜ì§€ 않는 ai_socktype입니다 +$ EAI_SYSTEM +11 시스템 오류가 errnoì— ë°˜í™˜ë˜ì—ˆìŠµë‹ˆë‹¤ +$ EAI_BADHINTS +12 ìž˜ëª»ëœ hints입니다 +$ EAI_PROTOCOL +13 ì•Œ 수 없는 í”„ë¡œí† ì½œì´ ë³€í™˜ë˜ì—ˆìŠµë‹ˆë‹¤ +$ EAI_OVERFLOW +14 ì¸ìž ë²„í¼ ê³µê°„ì´ ëª¨ìžëžë‹ˆë‹¤ +$ 0 +32766 성공 +$ NL_MSGMAX +32767 ì•Œ 수 없는 오류 diff --git a/lib/libc/nls/ko_KR.eucKR.msg b/lib/libc/nls/ko_KR.eucKR.msg index c2ca1a0..bb1fa30 100644 --- a/lib/libc/nls/ko_KR.eucKR.msg +++ b/lib/libc/nls/ko_KR.eucKR.msg @@ -181,6 +181,16 @@ $ ENOATTR 87 ¼Ó¼ºÀ» ãÀ» ¼ö ¾ø½À´Ï´Ù $ EDOOFUS 88 ÇÁ·Î±×·¥»ó ¿À·ùÀÔ´Ï´Ù +$ EBADMSG +89 À߸øµÈ ¸Þ½ÃÁöÀÔ´Ï´Ù +$ EMULTIHOP +90 ¸ÖƼȩÀÌ ½ÃµµµÇ¾ú½À´Ï´Ù +$ ENOLINK +91 ¿¬°áÀÌ ²÷°å½À´Ï´Ù +$ EPROTO +92 ÇÁ·ÎÅäÄÝÀÌ À߸øµÇ¾ú½À´Ï´Ù +$ ENOTCAPABLE +93 Á¢±Ù ´É·ÂÀÌ ÃæºÐÄ¡ ¾Ê½À´Ï´Ù $ $ strsignal() support catalog $ @@ -230,7 +240,7 @@ $ SIGTTIN $ SIGTTOU 22 Á¤Áö (Å͹̳ΠÃâ·Â) $ SIGIO -23 I/O possible +23 I/O °¡´É $ SIGXCPU 24 CPU »ç¿ë ½Ã°£ ÃÊ°ú $ SIGXFSZ @@ -247,3 +257,39 @@ $ SIGUSR1 30 »ç¿ëÀÚ Á¤ÀÇ ½Ã±×³Î 1 $ SIGUSR2 31 »ç¿ëÀÚ Á¤ÀÇ ½Ã±×³Î 2 +$ +$ gai_strerror() support catalog +$ +$set 3 +$ 1 (obsolete) +1 È£½ºÆ® À̸§ÀÌ Áö¿øÇÏÁö ¾Ê´Â ÁÖ¼Ò±ºÀÔ´Ï´Ù +$ EAI_AGAIN +2 ÁÖ¼Ò º¯È¯¿¡ ÀϽÃÀûÀ¸·Î ½ÇÆÐÇß½À´Ï´Ù +$ EAI_BADFLAGS +3 À߸øµÈ ai_flagsÀÔ´Ï´Ù +$ EAI_FAIL +4 ÁÖ¼Ò º¯È¯¿¡ ¿ÏÀüÈ÷ ½ÇÆÐÇß½À´Ï´Ù +$ EAI_FAMILY +5 Áö¿øµÇÁö ¾Ê´Â ai_familyÀÔ´Ï´Ù +$ EAI_MEMORY +6 ¸Þ¸ð¸® ÇÒ´ç¿¡ ½ÇÆÐÇß½À´Ï´Ù +$ 7 (obsolete) +7 È£½ºÆ® À̸§°ú ÀÏÄ¡ÇÏ´Â ÁÖ¼Ò°¡ ¾ø½À´Ï´Ù +$ EAI_NONAME +8 È£½ºÆ® À̸§ ¶Ç´Â ¼ºñ½º À̸§ÀÌ ÁöÁ¤µÇÁö ¾Ê¾Ò°Å³ª ¾Ë ¼ö ¾ø½À´Ï´Ù +$ EAI_SERVICE +9 ¼ºñ½º À̸§Àº ai_socktype¿¡¼ Áö¿øµÇÁö ¾Ê½À´Ï´Ù +$ EAI_SOCKTYPE +10 Áö¿øµÇÁö ¾Ê´Â ai_socktypeÀÔ´Ï´Ù +$ EAI_SYSTEM +11 ½Ã½ºÅÛ ¿À·ù°¡ errno¿¡ ¹ÝȯµÇ¾ú½À´Ï´Ù +$ EAI_BADHINTS +12 À߸øµÈ hintsÀÔ´Ï´Ù +$ EAI_PROTOCOL +13 ¾Ë ¼ö ¾ø´Â ÇÁ·ÎÅäÄÝÀÌ º¯È¯µÇ¾ú½À´Ï´Ù +$ EAI_OVERFLOW +14 ÀÎÀÚ ¹öÆÛ °ø°£ÀÌ ¸ðÀÚ¶ø´Ï´Ù +$ 0 +32766 ¼º°ø +$ NL_MSGMAX +32767 ¾Ë ¼ö ¾ø´Â ¿À·ù diff --git a/lib/libc/nls/msgcat.c b/lib/libc/nls/msgcat.c index 8689b7e..4532e90 100644 --- a/lib/libc/nls/msgcat.c +++ b/lib/libc/nls/msgcat.c @@ -1,5 +1,6 @@ /*********************************************************** Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. +Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org> All Rights Reserved @@ -39,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> +#include <sys/queue.h> #include <arpa/inet.h> /* for ntohl() */ @@ -47,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <limits.h> #include <locale.h> #include <nl_types.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -57,38 +60,109 @@ __FBSDID("$FreeBSD$"); #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L" +#define RLOCK(fail) { int ret; \ + if (__isthreaded && \ + ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \ + errno = ret; \ + return (fail); \ + }} +#define WLOCK(fail) { int ret; \ + if (__isthreaded && \ + ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \ + errno = ret; \ + return (fail); \ + }} +#define UNLOCK { if (__isthreaded) \ + _pthread_rwlock_unlock(&rwlock); } + #define NLERR ((nl_catd) -1) #define NLRETERR(errc) { errno = errc; return (NLERR); } +#define SAVEFAIL(n, l, e) { WLOCK(NLERR); \ + np = malloc(sizeof(struct catentry)); \ + if (np != NULL) { \ + np->name = strdup(n); \ + np->path = NULL; \ + np->lang = (l == NULL) ? NULL : \ + strdup(l); \ + np->caterrno = e; \ + SLIST_INSERT_HEAD(&cache, np, list); \ + } \ + UNLOCK; \ + errno = e; \ + } + +static nl_catd load_msgcat(const char *, const char *, const char *); -static nl_catd load_msgcat(const char *); +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +struct catentry { + SLIST_ENTRY(catentry) list; + char *name; + char *path; + int caterrno; + nl_catd catd; + char *lang; + int refcount; +}; + +SLIST_HEAD(listhead, catentry) cache = + SLIST_HEAD_INITIALIZER(cache); nl_catd catopen(const char *name, int type) { - int spcleft, saverr; - char path[PATH_MAX]; - char *nlspath, *lang, *base, *cptr, *pathP, *tmpptr; - char *cptr1, *plang, *pter, *pcode; - struct stat sbuf; - + struct stat sbuf; + struct catentry *np; + char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode; + char *plang, *pter, *tmpptr; + int saverr, spcleft; + char path[PATH_MAX]; + + /* sanity checking */ if (name == NULL || *name == '\0') NLRETERR(EINVAL); - /* is it absolute path ? if yes, load immediately */ if (strchr(name, '/') != NULL) - return (load_msgcat(name)); + /* have a pathname */ + lang = NULL; + else { + if (type == NL_CAT_LOCALE) + lang = setlocale(LC_MESSAGES, NULL); + else + lang = getenv("LANG"); + + if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || + (lang[0] == '.' && + (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || + strchr(lang, '/') != NULL) + lang = "C"; + } - if (type == NL_CAT_LOCALE) - lang = setlocale(LC_MESSAGES, NULL); - else - lang = getenv("LANG"); + /* Try to get it from the cache first */ + RLOCK(NLERR); + SLIST_FOREACH(np, &cache, list) { + if ((strcmp(np->name, name) == 0) && + ((lang != NULL && np->lang != NULL && + strcmp(np->lang, lang) == 0) || (np->lang == lang))) { + if (np->caterrno != 0) { + /* Found cached failing entry */ + UNLOCK; + NLRETERR(np->caterrno); + } else { + /* Found cached successful entry */ + np->refcount++; + UNLOCK; + return (np->catd); + } + } + } + UNLOCK; - if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || - (lang[0] == '.' && - (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || - strchr(lang, '/') != NULL) - lang = "C"; + /* is it absolute path ? if yes, load immediately */ + if (strchr(name, '/') != NULL) + return (load_msgcat(name, name, lang)); + /* sanity checking */ if ((plang = cptr1 = strdup(lang)) == NULL) return (NLERR); if ((cptr = strchr(cptr1, '@')) != NULL) @@ -136,7 +210,7 @@ catopen(const char *name, int type) break; case '%': ++nlspath; - /* fallthrough */ + /* FALLTHROUGH */ default: if (pathP - path >= sizeof(path) - 1) @@ -153,6 +227,7 @@ catopen(const char *name, int type) too_long: free(plang); free(base); + SAVEFAIL(name, lang, ENAMETOOLONG); NLRETERR(ENAMETOOLONG); } pathP += strlen(tmpptr); @@ -166,7 +241,7 @@ catopen(const char *name, int type) if (stat(path, &sbuf) == 0) { free(plang); free(base); - return (load_msgcat(path)); + return (load_msgcat(path, name, lang)); } } else { tmpptr = (char *)name; @@ -176,6 +251,7 @@ catopen(const char *name, int type) } free(plang); free(base); + SAVEFAIL(name, lang, ENOENT); NLRETERR(ENOENT); } @@ -183,19 +259,19 @@ char * catgets(nl_catd catd, int set_id, int msg_id, const char *s) { struct _nls_cat_hdr *cat_hdr; - struct _nls_set_hdr *set_hdr; struct _nls_msg_hdr *msg_hdr; - int l, u, i, r; + struct _nls_set_hdr *set_hdr; + int i, l, r, u; if (catd == NULL || catd == NLERR) { errno = EBADF; /* LINTED interface problem */ - return (char *) s; -} + return ((char *)s); + } - cat_hdr = (struct _nls_cat_hdr *)catd->__data; - set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data - + sizeof(struct _nls_cat_hdr)); + cat_hdr = (struct _nls_cat_hdr *)catd->__data; + set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data + + sizeof(struct _nls_cat_hdr)); /* binary search, see knuth algorithm b */ l = 0; @@ -228,7 +304,7 @@ catgets(nl_catd catd, int set_id, int msg_id, const char *s) } else { l = i + 1; } -} + } /* not found */ goto notfound; @@ -238,25 +314,44 @@ catgets(nl_catd catd, int set_id, int msg_id, const char *s) } else { l = i + 1; } -} + } notfound: /* not found */ errno = ENOMSG; /* LINTED interface problem */ - return (char *) s; + return ((char *)s); } int catclose(nl_catd catd) { + struct catentry *np; + + /* sanity checking */ if (catd == NULL || catd == NLERR) { errno = EBADF; return (-1); } - munmap(catd->__data, (size_t)catd->__size); - free(catd); + /* Remove from cache if not referenced any more */ + WLOCK(-1); + SLIST_FOREACH(np, &cache, list) { + if (catd == np->catd) { + np->refcount--; + if (np->refcount == 0) { + munmap(catd->__data, (size_t)catd->__size); + free(catd); + SLIST_REMOVE(&cache, np, catentry, list); + free(np->name); + free(np->path); + free(np->lang); + free(np); + } + break; + } + } + UNLOCK; return (0); } @@ -265,43 +360,88 @@ catclose(nl_catd catd) */ static nl_catd -load_msgcat(const char *path) +load_msgcat(const char *path, const char *name, const char *lang) { struct stat st; - nl_catd catd; + nl_catd catd; + struct catentry *np; void *data; int fd; - /* XXX: path != NULL? */ + /* path/name will never be NULL here */ + + /* + * One more try in cache; if it was not found by name, + * it might still be found by absolute path. + */ + RLOCK(NLERR); + SLIST_FOREACH(np, &cache, list) { + if ((np->path != NULL) && (strcmp(np->path, path) == 0)) { + np->refcount++; + UNLOCK; + return (np->catd); + } + } + UNLOCK; - if ((fd = _open(path, O_RDONLY)) == -1) - return (NLERR); + if ((fd = _open(path, O_RDONLY)) == -1) { + SAVEFAIL(name, lang, errno); + NLRETERR(errno); + } if (_fstat(fd, &st) != 0) { _close(fd); - return (NLERR); + SAVEFAIL(name, lang, EFTYPE); + NLRETERR(EFTYPE); } - data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, - (off_t)0); - _close(fd); + /* + * If the file size cannot be held in size_t we cannot mmap() + * it to the memory. Probably, this will not be a problem given + * that catalog files are usually small. + */ + if (st.st_size > SIZE_T_MAX) { + _close(fd); + SAVEFAIL(name, lang, EFBIG); + NLRETERR(EFBIG); + } - if (data == MAP_FAILED) - return (NLERR); + if ((data = mmap(0, (size_t)st.st_size, PROT_READ, + MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { + int saved_errno = errno; + _close(fd); + SAVEFAIL(name, lang, saved_errno); + NLRETERR(saved_errno); + } + _close(fd); if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) != _NLS_MAGIC) { munmap(data, (size_t)st.st_size); - NLRETERR(EINVAL); + SAVEFAIL(name, lang, EFTYPE); + NLRETERR(EFTYPE); } if ((catd = malloc(sizeof (*catd))) == NULL) { munmap(data, (size_t)st.st_size); - return (NLERR); + SAVEFAIL(name, lang, ENOMEM); + NLRETERR(ENOMEM); } catd->__data = data; catd->__size = (int)st.st_size; + + /* Caching opened catalog */ + WLOCK(NLERR); + if ((np = malloc(sizeof(struct catentry))) != NULL) { + np->name = strdup(name); + np->path = strdup(path); + np->catd = catd; + np->lang = (lang == NULL) ? NULL : strdup(lang); + np->refcount = 1; + np->caterrno = 0; + SLIST_INSERT_HEAD(&cache, np, list); + } + UNLOCK; return (catd); } - diff --git a/lib/libc/nls/nl_NL.ISO8859-1.msg b/lib/libc/nls/nl_NL.ISO8859-1.msg index 47103d4..2939116 100644 --- a/lib/libc/nls/nl_NL.ISO8859-1.msg +++ b/lib/libc/nls/nl_NL.ISO8859-1.msg @@ -26,7 +26,7 @@ $ EBADF $ ECHILD 10 Geen kindprocessen $ EDEADLK -11 Een deadlock is vermeden +11 Een deadlock op een bron is vermeden $ ENOMEM 12 Kan geen geheugen meer verkrijgen $ EACCES @@ -40,13 +40,13 @@ $ EBUSY $ EEXIST 17 Bestand bestaat reeds $ EXDEV -18 Verwijzing tussen bestanden op verschillende bestandssystemen +18 Verwijzing tussen verschillende apparaten $ ENODEV 19 Bewerking wordt niet ondersteund door dit apparaat $ ENOTDIR 20 Dit is geen map $ EISDIR -21 Dit is een map +21 Dit is een map $ EINVAL 22 Ongeldig argument $ ENFILE @@ -62,7 +62,7 @@ $ EFBIG $ ENOSPC 28 Geen ruimte meer op dit apparaat $ ESPIPE -29 Onuitvoerbare zoekopdracht +29 Ongeldige zoekopdracht $ EROFS 30 Van dit bestandssysteem kan alleen worden gelezen $ EMLINK @@ -84,7 +84,7 @@ $ ENOTSOCK $ EDESTADDRREQ 39 Een bestemmingsadres is vereist $ EMSGSIZE -40 Te grote bericht +40 Te groot bericht $ EPROTOTYPE 41 Protocol past niet bij dit contactpunt $ ENOPROTOOPT @@ -116,7 +116,7 @@ $ ECONNRESET $ ENOBUFS 55 Geen bufferruimte meer beschikbaar $ EISCONN -56 Dit contactpunt is al verbonden +56 Contactpunt is al verbonden $ ENOTCONN 57 Contactpunt is niet verbonden $ ESHUTDOWN @@ -136,9 +136,9 @@ $ EHOSTDOWN $ EHOSTUNREACH 65 Bestemming niet bereikbaar $ ENOTEMPTY -66 Directory is niet leeg +66 Map is niet leeg $ EPROCLIM -67 Te veel taken +67 Te veel processen $ EUSERS 68 Te veel gebruikers $ EDQUOT @@ -160,7 +160,7 @@ $ EPROCUNAVAIL $ ENOLCK 77 Geen sloten beschikbaar $ ENOSYS -78 Deze systeemfunctie is niet geimplementeerd +78 Systeemfunctie is niet geimplementeerd $ EFTYPE 79 Bestandsformaat niet van toepassing $ EAUTH @@ -173,30 +173,24 @@ $ ENOMSG 83 Geen bericht van het gewenste type $ EOVERFLOW 84 Waarde te groot om te bewaren in gegevenstype -$ EILSEQ -85 Ongeldige bytereeks -$ ENOTSUP -86 Niet ondersteund $ ECANCELED -87 Bewerking geannuleerd -$ EBADMSG -88 Verkeerd of defect bericht -$ ENODATA -89 Geen bericht beschikbaar -$ ENOSR -90 Geen STREAM-voorraad -$ ENOSTR -91 Dit is geen STREAM -$ ETIME -92 STREAM-ioctl verlopen +85 Bewerking geannuleerd +$ EILSEQ +86 Ongeldige bytereeks $ ENOATTR -93 Attribuut niet gevonden +87 Attribuut niet gevonden +$ EDOOFUS +88 Programmeerfout +$ EBADMSG +89 Verkeerd of defect bericht $ EMULTIHOP -94 Multihopverzoek +90 Multihopverzoek $ ENOLINK -95 Verbinding werd verstoord +91 Verbinding werd verstoord $ EPROTO -96 Protocolfout +92 Protocolfout +$ ENOTCAPABLE +93 Onvoldoende mogelijkheden $ $ strsignal() support catalog $ @@ -263,5 +257,38 @@ $ SIGUSR1 30 Gebruikersignaal 1 $ SIGUSR2 31 Gebruikersignaal 2 -$ SIGPWR -32 Stroomuitval/stroominschakeling +$ +$ gai_strerror() support catalog +$set 3 +$ 1 (obsolete) +1 Adresfamilie voor hostnaam niet ondersteund +$ EAI_AGAIN +2 Tijdelijke fout in naamresolutie +$ EAI_BADFLAGS +3 Ongeldige waarde voor ai_flags +$ EAI_FAIL +4 Onherstelbare fout in naamresolutie +$ EAI_FAMILY +5 ai_familie niet ondersteund +$ EAI_MEMORY +6 Geheugenallocatiefout +$ 7 (obsolete) +7 Geen adres met hostnaam geassocieerd +$ EAI_NONAME +8 hostname noch servname gegeven, of onbekend +$ EAI_SERVICE +9 servname niet ondersteund voor ai_socktype +$ EAI_SOCKTYPE +10 ai_socktype niet ondersteund +$ EAI_SYSTEM +11 Systeemfout geretourneerd in errno +$ EAI_BADHINTS +12 Ongeldige waarde voor hints +$ EAI_PROTOCOL +13 Opgelost protocol is onbekend +$ EAI_OVERFLOW +14 Argumentbuffer overstroomd +$ 0 +32766 Succes +$ NL_MSGMAX +32767 Onbekende fout diff --git a/lib/libc/nls/uk_UA.UTF-8.msg b/lib/libc/nls/uk_UA.UTF-8.msg index e6ea2f77..af871d9 100644 --- a/lib/libc/nls/uk_UA.UTF-8.msg +++ b/lib/libc/nls/uk_UA.UTF-8.msg @@ -1,249 +1,259 @@ -$ $FreeBSD$
-$
-$ Message catalog for uk_UA.UTF-8 locale
-$
-$ strerror() support catalog
-$
-$set 1
-$ EPERM
-1 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ дозволена
-$ ENOENT
-2 Ðемає такого файлу або каталогу
-$ ESRCH
-3 Ðемає такого процеÑу
-$ EINTR
-4 Перервано виклик функції
-$ EIO
-5 Помилка вводу-виводу
-$ ENXIO
-6 Ðемає такого приÑтрою або адреÑи
-$ E2BIG
-7 Перелік аргументів надто довгий
-$ ENOEXEC
-8 Помилка формату виконуваного файлу
-$ EBADF
-9 Ðевірний деÑкриптор файлу
-$ ECHILD
-10 Ðемає дочірнього процеÑу
-$ EDEADLK
-11 Уникнуто взаємне Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑів
-$ ENOMEM
-12 Ðе доÑтатньо пам'ÑÑ‚Ñ–
-$ EACCES
-13 Відмова у доÑтупі
-$ EFAULT
-14 Ðевірна адреÑа
-$ ENOTBLK
-15 Потрібен блочний приÑтрій
-$ EBUSY
-16 РеÑÑƒÑ€Ñ Ð·Ð°Ð¹Ð½Ñтий
-$ EEXIST
-17 Файл вже Ñ–Ñнує
-$ EXDEV
-18 ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð° межі приÑтрою
-$ ENODEV
-19 Ðемає такого приÑтрою
-$ ENOTDIR
-20 Це не каталог
-$ EISDIR
-21 Це каталог
-$ EINVAL
-22 Ðедозволений аргумент
-$ ENFILE
-23 Забагато відкритих файлів у ÑиÑтемі
-$ EMFILE
-24 Забагато відкритих файлів
-$ ENOTTY
-25 Це не термінал
-$ ETXTBSY
-26 ТекÑтовий файл зайнÑтий
-$ EFBIG
-27 Файл надто великий
-$ ENOSPC
-28 Ðе залишилоÑÑŒ міÑÑ†Ñ Ð½Ð° приÑтрої
-$ ESPIPE
-29 Ðедозволене позиціонуваннÑ
-$ EROFS
-30 Файлова ÑиÑтема лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ
-$ EMLINK
-31 Забагато поÑилань
-$ EPIPE
-32 Канал зруйновано
-$ EDOM
-33 Помилка облаÑÑ‚Ñ– визначеннÑ
-$ ERANGE
-34 Результат надто великий
-$ EAGAIN, EWOULDBLOCK
-35 РеÑÑƒÑ€Ñ Ñ‚Ð¸Ð¼Ñ‡Ð°Ñово не доÑтупний
-$ EINPROGRESS
-36 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ñƒ процеÑÑ– виконаннÑ
-$ EALREADY
-37 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ð¶Ðµ виконуєтьÑÑ
-$ ENOTSOCK
-38 Це не Ñокет
-$ EDESTADDRREQ
-39 Ðеобхідна адреÑа призначеннÑ
-$ EMSGSIZE
-40 ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð´Ñ‚Ð¾ довге
-$ EPROTOTYPE
-41 Помилковий тип протоколу Ð´Ð»Ñ Ñокету
-$ ENOPROTOOPT
-42 Ðемає такого протоколу
-$ EPROTONOSUPPORT
-43 Протокол не підтримуєтьÑÑ
-$ ESOCKTNOSUPPORT
-44 Цей тип Ñокету не підтримуєтьÑÑ
-$ EOPNOTSUPP
-45 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ
-$ EPFNOSUPPORT
-46 Родина протоколів не підтримуєтьÑÑ
-$ EAFNOSUPPORT
-47 Родина Ð°Ð´Ñ€ÐµÑ Ð½Ðµ підтримуєтьÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¾Ð¼
-$ EADDRINUSE
-48 ÐдреÑа вже викориÑтовуєтьÑÑ
-$ EADDRNOTAVAIL
-49 ÐдреÑа недоÑÑжна
-$ ENETDOWN
-50 Мережа не працює
-$ ENETUNREACH
-51 Мережа недоÑÑжна
-$ ENETRESET
-52 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾ мережею
-$ ECONNABORTED
-53 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾
-$ ECONNRESET
-54 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾ протилежною Ñтороною
-$ ENOBUFS
-55 Ðемає вільних буферів
-$ EISCONN
-56 Сокет вже під'єднано
-$ ENOTCONN
-57 Сокет не під'єднано
-$ ESHUTDOWN
-58 Ðе можу відіÑлати піÑÐ»Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñокету протилежною Ñтороною
-$ ETOOMANYREFS
-59 Забагато поÑилань: не можу з'єднати
-$ ETIMEDOUT
-60 Вийшов ліміт чаÑу Ð´Ð»Ñ Ð·'єднаннÑ
-$ ECONNREFUSED
-61 Відмова у з'єднанні
-$ ELOOP
-62 Забагато рівнів Ñимволічних поÑилань
-$ ENAMETOOLONG
-63 Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ надто довге
-$ EHOSTDOWN
-64 ХоÑÑ‚ не працює
-$ EHOSTUNREACH
-65 ХоÑÑ‚ недоÑÑжний
-$ ENOTEMPTY
-66 Каталог не порожній
-$ EPROCLIM
-67 Забагато процеÑів
-$ EUSERS
-68 Забагато кориÑтувачів
-$ EDQUOT
-69 Перевищена диÑкова квота
-$ ESTALE
-70 ЗаÑтарілий деÑкриптор файлу NFS
-$ EREMOTE
-71 Віддалений об'єкт
-$ EBADRPC
-72 Погана Ñтруктура RPC
-$ ERPCMISMATCH
-73 Ðевірна верÑÑ–Ñ RPC
-$ EPROGUNAVAIL
-74 Програма RPC недоÑÑжна
-$ EPROGMISMATCH
-75 Ðевірна верÑÑ–Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸
-$ EPROCUNAVAIL
-76 Погана процедура Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸
-$ ENOLCK
-77 Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ доÑтупне
-$ ENOSYS
-78 Функцію не реалізовано
-$ EFTYPE
-79 Ðепридатний тип чи формат файлу
-$ EAUTH
-80 Помилка аутентифікації
-$ ENEEDAUTH
-81 Потрібна аутентифікаціÑ
-$ EIDRM
-82 Ідентифікатор вилучено
-$ ENOMSG
-83 Ðемає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð¶Ð°Ð½Ð¾Ð³Ð¾ типу
-$ EOVERFLOW
-84 Завелике Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ типу даних
-$ ECANCELED
-85 Операцію ÑкаÑовано
-$ EILSEQ
-86 Ðедозволена поÑлідовніÑÑ‚ÑŒ байтів
-$ ENOATTR
-87 Ðтрибут не знайдено
-$ EDOOFUS
-88 Помилка програмуваннÑ
-$
-$ strsignal() support catalog
-$
-$set 2
-$ SIGHUP
-1 ВідключеннÑ
-$ SIGINT
-2 ПерериваннÑ
-$ SIGQUIT
-3 Вихід
-$ SIGILL
-4 ÐеприпуÑтима інÑтрукціÑ
-$ SIGTRAP
-5 ПаÑтка траÑуваннÑ
-$ SIGABRT
-6 Ðварійне завершеннÑ
-$ SIGEMT
-7 ÐŸÐµÑ€ÐµÑ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»ÑŒÐ¾Ð²Ð°Ð½Ð¾Ñ— інÑтрукції
-$ SIGFPE
-8 Помилка роботи з плаваючою крапкою
-$ SIGKILL
-9 Вбито
-$ SIGBUS
-10 Помилка шини
-$ SIGSEGV
-11 ÐŸÐ¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ñегментації
-$ SIGSYS
-12 Поганий ÑиÑтемний виклик
-$ SIGPIPE
-13 Канал зруйновано
-$ SIGALRM
-14 Таймер вичерпано
-$ SIGTERM
-15 ЗавершеннÑ
-$ SIGURG
-16 Ðевідкладний Ñтан на Ñокеті
-$ SIGSTOP
-17 Призупинено (Ñигнал)
-$ SIGTSTP
-18 Призупинено
-$ SIGCONT
-19 ÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸
-$ SIGCHLD
-20 Зміна ÑтатуÑу дочірнього процеÑу
-$ SIGTTIN
-21 Зупинено (ввід з терміналу)
-$ SIGTTOU
-22 Зупинено (вивід на термінал)
-$ SIGIO
-23 Ввід-вивід можливий
-$ SIGXCPU
-24 Перевищено ліміт процеÑорного чаÑу
-$ SIGXFSZ
-25 Перевищено ліміт макÑимального розміру файла
-$ SIGVTALRM
-26 Віртуальний таймер вичерпано
-$ SIGPROF
-27 Таймер Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð°Ð½Ð¾
-$ SIGWINCH
-28 Розмір вікна змінено
-$ SIGINFO
-29 Запит інформації
-$ SIGUSR1
-30 Сигнал кориÑтувача 1
-$ SIGUSR2
-31 Сигнал кориÑтувача 2
+$ $FreeBSD$ +$ +$ Message catalog for uk_UA.UTF-8 locale +$ +$ strerror() support catalog +$ +$set 1 +$ EPERM +1 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ дозволена +$ ENOENT +2 Ðемає такого файлу або каталогу +$ ESRCH +3 Ðемає такого процеÑу +$ EINTR +4 Перервано виклик функції +$ EIO +5 Помилка вводу-виводу +$ ENXIO +6 Ðемає такого приÑтрою або адреÑи +$ E2BIG +7 Перелік аргументів надто довгий +$ ENOEXEC +8 Помилка формату виконуваного файлу +$ EBADF +9 Ðевірний деÑкриптор файлу +$ ECHILD +10 Ðемає дочірнього процеÑу +$ EDEADLK +11 Уникнуто взаємне Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑів +$ ENOMEM +12 Ðе доÑтатньо пам'ÑÑ‚Ñ– +$ EACCES +13 Відмова у доÑтупі +$ EFAULT +14 Ðевірна адреÑа +$ ENOTBLK +15 Потрібен блочний приÑтрій +$ EBUSY +16 РеÑÑƒÑ€Ñ Ð·Ð°Ð¹Ð½Ñтий +$ EEXIST +17 Файл вже Ñ–Ñнує +$ EXDEV +18 ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð° межі приÑтрою +$ ENODEV +19 Ðемає такого приÑтрою +$ ENOTDIR +20 Це не каталог +$ EISDIR +21 Це каталог +$ EINVAL +22 Ðедозволений аргумент +$ ENFILE +23 Забагато відкритих файлів у ÑиÑтемі +$ EMFILE +24 Забагато відкритих файлів +$ ENOTTY +25 Це не термінал +$ ETXTBSY +26 ТекÑтовий файл зайнÑтий +$ EFBIG +27 Файл надто великий +$ ENOSPC +28 Ðе залишилоÑÑŒ міÑÑ†Ñ Ð½Ð° приÑтрої +$ ESPIPE +29 Ðедозволене Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ +$ EROFS +30 Файлова ÑиÑтема лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ +$ EMLINK +31 Забагато поÑилань +$ EPIPE +32 Канал зруйновано +$ EDOM +33 Помилка облаÑÑ‚Ñ– Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ +$ ERANGE +34 Результат надто великий +$ EAGAIN, EWOULDBLOCK +35 РеÑÑƒÑ€Ñ Ñ‚Ð¸Ð¼Ñ‡Ð°Ñово не доÑтупний +$ EINPROGRESS +36 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ñƒ процеÑÑ– Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ +$ EALREADY +37 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ð¶Ðµ виконуєтьÑÑ +$ ENOTSOCK +38 Це не Ñокет +$ EDESTADDRREQ +39 Ðеобхідна адреÑа Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ +$ EMSGSIZE +40 ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð´Ñ‚Ð¾ довге +$ EPROTOTYPE +41 Помилковий тип протоколу Ð´Ð»Ñ Ñокету +$ ENOPROTOOPT +42 Ðемає такого протоколу +$ EPROTONOSUPPORT +43 Протокол не підтримуєтьÑÑ +$ ESOCKTNOSUPPORT +44 Цей тип Ñокету не підтримуєтьÑÑ +$ EOPNOTSUPP +45 ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ +$ EPFNOSUPPORT +46 Родина протоколів не підтримуєтьÑÑ +$ EAFNOSUPPORT +47 Родина Ð°Ð´Ñ€ÐµÑ Ð½Ðµ підтримуєтьÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¾Ð¼ +$ EADDRINUSE +48 ÐдреÑа вже викориÑтовуєтьÑÑ +$ EADDRNOTAVAIL +49 ÐдреÑа недоÑÑжна +$ ENETDOWN +50 Мережа не працює +$ ENETUNREACH +51 Мережа недоÑÑжна +$ ENETRESET +52 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾ мережею +$ ECONNABORTED +53 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾ +$ ECONNRESET +54 З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð¿Ð¸Ð½ÐµÐ½Ð¾ протилежною Ñтороною +$ ENOBUFS +55 Ðемає вільних буферів +$ EISCONN +56 Сокет вже під'єднано +$ ENOTCONN +57 Сокет не під'єднано +$ ESHUTDOWN +58 Ðе можу відіÑлати піÑÐ»Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñокету протилежною Ñтороною +$ ETOOMANYREFS +59 Забагато поÑилань: не можу з'єднати +$ ETIMEDOUT +60 Вийшов ліміт чаÑу Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ +$ ECONNREFUSED +61 Відмова у з'єднанні +$ ELOOP +62 Забагато рівнів Ñимволічних поÑилань +$ ENAMETOOLONG +63 Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ надто довге +$ EHOSTDOWN +64 ХоÑÑ‚ не працює +$ EHOSTUNREACH +65 ХоÑÑ‚ недоÑÑжний +$ ENOTEMPTY +66 Каталог не порожній +$ EPROCLIM +67 Забагато процеÑів +$ EUSERS +68 Забагато кориÑтувачів +$ EDQUOT +69 Перевищена диÑкова квота +$ ESTALE +70 ЗаÑтарілий деÑкриптор файлу NFS +$ EREMOTE +71 Віддалений об'єкт +$ EBADRPC +72 Погана Ñтруктура RPC +$ ERPCMISMATCH +73 Ðевірна верÑÑ–Ñ RPC +$ EPROGUNAVAIL +74 Програма RPC недоÑÑжна +$ EPROGMISMATCH +75 Ðевірна верÑÑ–Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ +$ EPROCUNAVAIL +76 Погана процедура Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ +$ ENOLCK +77 Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ доÑтупне +$ ENOSYS +78 Функцію не реалізовано +$ EFTYPE +79 Ðепридатний тип чи формат файлу +$ EAUTH +80 Помилка аутентифікації +$ ENEEDAUTH +81 Потрібна Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ +$ EIDRM +82 Ідентифікатор вилучено +$ ENOMSG +83 Ðемає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð¶Ð°Ð½Ð¾Ð³Ð¾ типу +$ EOVERFLOW +84 Завелике Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ типу даних +$ ECANCELED +85 Операцію ÑкаÑовано +$ EILSEQ +86 Ðедозволена поÑлідовніÑÑ‚ÑŒ байтів +$ ENOATTR +87 Ðтрибут не знайдено +$ EDOOFUS +88 Помилка Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ +$ EBADMSG +89 Поганий формат Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ +$ EMULTIHOP XXX +90 Спроба мултіхопу +$ ENOLINK +91 Мережовий канал розірвано +$ EPROTO +92 Помилка протоколу +$ ENOTCAPABLE +93 МожливоÑÑ‚Ñ– недоÑтатні +$ +$ strsignal() support catalog +$ +$set 2 +$ SIGHUP +1 Ð’Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ +$ SIGINT +2 ÐŸÐµÑ€ÐµÑ€Ð¸Ð²Ð°Ð½Ð½Ñ +$ SIGQUIT +3 Вихід +$ SIGILL +4 ÐеприпуÑтима інÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ +$ SIGTRAP +5 ПаÑтка траÑÑƒÐ²Ð°Ð½Ð½Ñ +$ SIGABRT +6 Ðварійне Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ +$ SIGEMT +7 ÐŸÐµÑ€ÐµÑ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ ÐµÐ¼ÑƒÐ»ÑŒÐ¾Ð²Ð°Ð½Ð¾Ñ— інÑтрукції +$ SIGFPE +8 Помилка роботи з плаваючою крапкою +$ SIGKILL +9 Вбито +$ SIGBUS +10 Помилка шини +$ SIGSEGV +11 ÐŸÐ¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ñегментації +$ SIGSYS +12 Поганий ÑиÑтемний виклик +$ SIGPIPE +13 Канал зруйновано +$ SIGALRM +14 Таймер вичерпано +$ SIGTERM +15 Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ +$ SIGURG +16 Ðевідкладний Ñтан на Ñокеті +$ SIGSTOP +17 Призупинено (Ñигнал) +$ SIGTSTP +18 Призупинено +$ SIGCONT +19 ÐŸÑ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ +$ SIGCHLD +20 Зміна ÑтатуÑу дочірнього процеÑу +$ SIGTTIN +21 Зупинено (ввід з терміналу) +$ SIGTTOU +22 Зупинено (вивід на термінал) +$ SIGIO +23 Ввід-вивід можливий +$ SIGXCPU +24 Перевищено ліміт процеÑорного чаÑу +$ SIGXFSZ +25 Перевищено ліміт макÑимального розміру файла +$ SIGVTALRM +26 Віртуальний таймер вичерпано +$ SIGPROF +27 Таймер Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð°Ð½Ð¾ +$ SIGWINCH +28 Розмір вікна змінено +$ SIGINFO +29 Запит інформації +$ SIGUSR1 +30 Сигнал кориÑтувача 1 +$ SIGUSR2 +31 Сигнал кориÑтувача 2 diff --git a/lib/libc/posix1e/Makefile.inc b/lib/libc/posix1e/Makefile.inc index c9e78bd..30e3986 100644 --- a/lib/libc/posix1e/Makefile.inc +++ b/lib/libc/posix1e/Makefile.inc @@ -119,3 +119,5 @@ MLINKS+=acl_create_entry.3 acl_create_entry_np.3\ mac_set.3 mac_set_proc.3 \ mac_text.3 mac_from_text.3 \ mac_text.3 mac_to_text.3 + +CLEANFILES+= subr_acl_nfs4.c diff --git a/lib/libc/posix1e/acl_delete_entry.c b/lib/libc/posix1e/acl_delete_entry.c index 7dd60b8..09b4507 100644 --- a/lib/libc/posix1e/acl_delete_entry.c +++ b/lib/libc/posix1e/acl_delete_entry.c @@ -75,6 +75,7 @@ int acl_delete_entry(acl_t acl, acl_entry_t entry_d) { struct acl *acl_int; + struct acl_entry entry_int; int i, j, found = 0; if (acl == NULL || entry_d == NULL) { @@ -94,8 +95,12 @@ acl_delete_entry(acl_t acl, acl_entry_t entry_d) errno = EINVAL; return (-1); } + + /* Use a local copy to prevent deletion of more than this entry */ + entry_int = *entry_d; + for (i = 0; i < acl->ats_acl.acl_cnt;) { - if (_entry_matches(&(acl->ats_acl.acl_entry[i]), entry_d)) { + if (_entry_matches(&(acl->ats_acl.acl_entry[i]), &entry_int)) { /* ...shift the remaining entries... */ for (j = i; j < acl->ats_acl.acl_cnt - 1; ++j) acl->ats_acl.acl_entry[j] = diff --git a/lib/libc/posix1e/acl_from_text.c b/lib/libc/posix1e/acl_from_text.c index 98c5426..c600987 100644 --- a/lib/libc/posix1e/acl_from_text.c +++ b/lib/libc/posix1e/acl_from_text.c @@ -257,6 +257,7 @@ acl_from_text(const char *buf_p) } #endif + free(mybuf_p); return(acl); error_label: diff --git a/lib/libc/posix1e/acl_to_text.c b/lib/libc/posix1e/acl_to_text.c index 79a950a..e5fd1f7 100644 --- a/lib/libc/posix1e/acl_to_text.c +++ b/lib/libc/posix1e/acl_to_text.c @@ -39,7 +39,6 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <utmp.h> #include "acl_support.h" @@ -58,7 +57,7 @@ _posix1e_acl_to_text(acl_t acl, ssize_t *len_p, int flags) { struct acl *acl_int; char *buf, *tmpbuf; - char name_buf[UT_NAMESIZE+1]; + char name_buf[MAXLOGNAME]; char perm_buf[_POSIX1E_ACL_STRING_PERM_MAXSIZE+1], effective_perm_buf[_POSIX1E_ACL_STRING_PERM_MAXSIZE+1]; int i, error, len; @@ -103,7 +102,7 @@ _posix1e_acl_to_text(acl_t acl, ssize_t *len_p, int flags) goto error_label; error = _posix1e_acl_id_to_name(ae_tag, ae_id, - UT_NAMESIZE+1, name_buf, flags); + MAXLOGNAME, name_buf, flags); if (error) goto error_label; @@ -163,7 +162,7 @@ _posix1e_acl_to_text(acl_t acl, ssize_t *len_p, int flags) goto error_label; error = _posix1e_acl_id_to_name(ae_tag, ae_id, - UT_NAMESIZE+1, name_buf, flags); + MAXLOGNAME, name_buf, flags); if (error) goto error_label; diff --git a/lib/libc/posix1e/mac.3 b/lib/libc/posix1e/mac.3 index c570998..6499d6b 100644 --- a/lib/libc/posix1e/mac.3 +++ b/lib/libc/posix1e/mac.3 @@ -154,8 +154,8 @@ system objects, but without policy-specific knowledge. .Sh STANDARDS These APIs are loosely based on the APIs described in POSIX.1e, as described in IEEE POSIX.1e draft 17. -However, the resemblence of these APIS to the POSIX APIs is loose, as the -PSOXI APIS were unable to express some notinos required for flexible and +However, the resemblence of these APIs to the POSIX APIs is loose, as the +POSIX APIs were unable to express some notions required for flexible and extensible access control. .Sh HISTORY Support for Mandatory Access Control was introduced in diff --git a/lib/libc/rpc/clnt_raw.c b/lib/libc/rpc/clnt_raw.c index 9d34a3d..cd3a384 100644 --- a/lib/libc/rpc/clnt_raw.c +++ b/lib/libc/rpc/clnt_raw.c @@ -92,13 +92,13 @@ clnt_raw_create(prog, vers) rpcprog_t prog; rpcvers_t vers; { - struct clntraw_private *clp = clntraw_private; + struct clntraw_private *clp; struct rpc_msg call_msg; - XDR *xdrs = &clp->xdr_stream; - CLIENT *client = &clp->client_object; + XDR *xdrs; + CLIENT *client; mutex_lock(&clntraw_lock); - if (clp == NULL) { + if ((clp = clntraw_private) == NULL) { clp = (struct clntraw_private *)calloc(1, sizeof (*clp)); if (clp == NULL) { mutex_unlock(&clntraw_lock); @@ -110,6 +110,9 @@ clnt_raw_create(prog, vers) clp->_raw_buf = __rpc_rawcombuf; clntraw_private = clp; } + xdrs = &clp->xdr_stream; + client = &clp->client_object; + /* * pre-serialize the static part of the call msg and stash it away */ diff --git a/lib/libc/rpc/getnetconfig.c b/lib/libc/rpc/getnetconfig.c index 9baa224..e5db51a 100644 --- a/lib/libc/rpc/getnetconfig.c +++ b/lib/libc/rpc/getnetconfig.c @@ -412,13 +412,13 @@ void *handlep; * Noone needs these entries anymore, then frees them. * Make sure all info in netconfig_info structure has been reinitialized. */ - q = p = ni.head; + q = ni.head; ni.eof = ni.ref = 0; ni.head = NULL; ni.tail = NULL; mutex_unlock(&ni_lock); - while (q) { + while (q != NULL) { p = q->next; if (q->ncp->nc_lookups != NULL) free(q->ncp->nc_lookups); free(q->ncp); diff --git a/lib/libc/rpc/getnetpath.c b/lib/libc/rpc/getnetpath.c index 0563544..d1ea554 100644 --- a/lib/libc/rpc/getnetpath.c +++ b/lib/libc/rpc/getnetpath.c @@ -101,7 +101,7 @@ setnetpath() if ((np_sessionp->nc_handlep = setnetconfig()) == NULL) { free(np_sessionp); syslog (LOG_ERR, "rpc: failed to open " NETCONFIG); - goto failed; + return (NULL); } np_sessionp->valid = NP_VALID; np_sessionp->ncp_list = NULL; diff --git a/lib/libc/rpc/getrpcent.c b/lib/libc/rpc/getrpcent.c index abee480..1f198fa 100644 --- a/lib/libc/rpc/getrpcent.c +++ b/lib/libc/rpc/getrpcent.c @@ -698,7 +698,7 @@ rpc_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, return (NS_RETURN); } - memcpy(&new_rpc, rpc, sizeof(struct rpcent)); + new_rpc = *rpc; *buffer_size = desired_size; memset(buffer, 0, desired_size); diff --git a/lib/libc/rpc/key_call.c b/lib/libc/rpc/key_call.c index 615f24d..6cc1139 100644 --- a/lib/libc/rpc/key_call.c +++ b/lib/libc/rpc/key_call.c @@ -302,7 +302,7 @@ int vers; void *localhandle; struct netconfig *nconf; struct netconfig *tpconf; - struct key_call_private *kcp = key_call_private_main; + struct key_call_private *kcp; struct timeval wait_time; struct utsname u; int main_thread; diff --git a/lib/libc/rpc/svc.c b/lib/libc/rpc/svc.c index d205121..282c2be 100644 --- a/lib/libc/rpc/svc.c +++ b/lib/libc/rpc/svc.c @@ -627,8 +627,8 @@ svc_getreqset(readfds) maskp = readfds->fds_bits; for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) { - for (mask = *maskp++; (bit = ffs(mask)) != 0; - mask ^= (1 << (bit - 1))) { + for (mask = *maskp++; (bit = ffsl(mask)) != 0; + mask ^= (1ul << (bit - 1))) { /* sock has input waiting */ fd = sock + bit - 1; svc_getreq_common(fd); diff --git a/lib/libc/rpc/svc_auth_des.c b/lib/libc/rpc/svc_auth_des.c index 84f1e19..de4d1b4 100644 --- a/lib/libc/rpc/svc_auth_des.c +++ b/lib/libc/rpc/svc_auth_des.c @@ -449,10 +449,10 @@ cache_spot(key, name, timestamp) #define INVALID -1 /* grouplen, if cache entry is invalid */ struct bsdcred { - short uid; /* cached uid */ - short gid; /* cached gid */ - short grouplen; /* length of cached groups */ - short groups[NGROUPS]; /* cached groups */ + uid_t uid; /* cached uid */ + gid_t gid; /* cached gid */ + int grouplen; /* length of cached groups */ + gid_t groups[NGRPS]; /* cached groups */ }; /* diff --git a/lib/libc/rpc/svc_raw.c b/lib/libc/rpc/svc_raw.c index 7492046..67bcba1 100644 --- a/lib/libc/rpc/svc_raw.c +++ b/lib/libc/rpc/svc_raw.c @@ -176,9 +176,8 @@ svc_raw_reply(xprt, msg) msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; msg->acpted_rply.ar_results.where = NULL; - if (!xdr_replymsg(xdrs, msg) || - !SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where)) - stat = FALSE; + stat = xdr_replymsg(xdrs, msg) && + SVCAUTH_WRAP(&SVC_AUTH(xprt), xdrs, xdr_proc, xdr_where); } else { stat = xdr_replymsg(xdrs, msg); } diff --git a/lib/libc/stdio/fgetws.c b/lib/libc/stdio/fgetws.c index bbc0299..550843d 100644 --- a/lib/libc/stdio/fgetws.c +++ b/lib/libc/stdio/fgetws.c @@ -89,7 +89,7 @@ fgetws(wchar_t * __restrict ws, int n, FILE * __restrict fp) if (!__mbsinit(&fp->_mbstate)) /* Incomplete character */ goto error; - *wsp++ = L'\0'; + *wsp = L'\0'; FUNLOCKFILE(fp); return (ws); diff --git a/lib/libc/stdio/findfp.c b/lib/libc/stdio/findfp.c index 586e15c..5bc4af7 100644 --- a/lib/libc/stdio/findfp.c +++ b/lib/libc/stdio/findfp.c @@ -168,7 +168,7 @@ __warn_references(f_prealloc, "warning: this program uses f_prealloc(), which is not recommended."); void -f_prealloc() +f_prealloc(void) { struct glue *g; int n; diff --git a/lib/libc/stdio/fread.c b/lib/libc/stdio/fread.c index 6253856..ad3ea29 100644 --- a/lib/libc/stdio/fread.c +++ b/lib/libc/stdio/fread.c @@ -37,6 +37,8 @@ static char sccsid[] = "@(#)fread.c 8.2 (Berkeley) 12/11/93"; __FBSDID("$FreeBSD$"); #include "namespace.h" +#include <errno.h> +#include <stdint.h> #include <stdio.h> #include <string.h> #include "un-namespace.h" @@ -69,8 +71,27 @@ __fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp) /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ - if ((resid = count * size) == 0) + if ((count == 0) || (size == 0)) return (0); + + /* + * Check for integer overflow. As an optimization, first check that + * at least one of {count, size} is at least 2^16, since if both + * values are less than that, their product can't possible overflow + * (size_t is always at least 32 bits on FreeBSD). + */ + if (((count | size) > 0xFFFF) && + (count > SIZE_MAX / size)) { + errno = EINVAL; + fp->_flags |= __SERR; + return (0); + } + + /* + * Compute the (now required to not overflow) number of bytes to + * read and actually do the work. + */ + resid = count * size; ORIENT(fp, -1); if (fp->_r < 0) fp->_r = 0; diff --git a/lib/libc/stdio/funopen.c b/lib/libc/stdio/funopen.c index 9535340..573589f 100644 --- a/lib/libc/stdio/funopen.c +++ b/lib/libc/stdio/funopen.c @@ -42,11 +42,11 @@ __FBSDID("$FreeBSD$"); #include "local.h" FILE * -funopen(cookie, readfn, writefn, seekfn, closefn) - const void *cookie; - int (*readfn)(), (*writefn)(); - fpos_t (*seekfn)(void *cookie, fpos_t off, int whence); - int (*closefn)(); +funopen(const void *cookie, + int (*readfn)(void *, char *, int), + int (*writefn)(void *, const char *, int), + fpos_t (*seekfn)(void *, fpos_t, int), + int (*closefn)(void *)) { FILE *fp; int flags; diff --git a/lib/libc/stdio/fvwrite.c b/lib/libc/stdio/fvwrite.c index fd69eb9..7206676 100644 --- a/lib/libc/stdio/fvwrite.c +++ b/lib/libc/stdio/fvwrite.c @@ -60,7 +60,7 @@ __sfvwrite(fp, uio) char *nl; int nlknown, nldist; - if ((len = uio->uio_resid) == 0) + if (uio->uio_resid == 0) return (0); /* make sure we can write */ if (prepwrite(fp) != 0) diff --git a/lib/libc/stdio/fwrite.c b/lib/libc/stdio/fwrite.c index cf52e42..acac943 100644 --- a/lib/libc/stdio/fwrite.c +++ b/lib/libc/stdio/fwrite.c @@ -37,6 +37,8 @@ static char sccsid[] = "@(#)fwrite.c 8.1 (Berkeley) 6/4/93"; __FBSDID("$FreeBSD$"); #include "namespace.h" +#include <errno.h> +#include <stdint.h> #include <stdio.h> #include "un-namespace.h" #include "local.h" @@ -60,10 +62,24 @@ fwrite(buf, size, count, fp) /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ - n = count * size; - if (n == 0) + if ((count == 0) || (size == 0)) return (0); + /* + * Check for integer overflow. As an optimization, first check that + * at least one of {count, size} is at least 2^16, since if both + * values are less than that, their product can't possible overflow + * (size_t is always at least 32 bits on FreeBSD). + */ + if (((count | size) > 0xFFFF) && + (count > SIZE_MAX / size)) { + errno = EINVAL; + fp->_flags |= __SERR; + return (0); + } + + n = count * size; + iov.iov_base = (void *)buf; uio.uio_resid = iov.iov_len = n; uio.uio_iov = &iov; diff --git a/lib/libc/stdio/getc.3 b/lib/libc/stdio/getc.3 index c8b9386..d0f3c15 100644 --- a/lib/libc/stdio/getc.3 +++ b/lib/libc/stdio/getc.3 @@ -56,7 +56,7 @@ .Ft int .Fn getchar void .Ft int -.Fn getchar_unlocked "void" +.Fn getchar_unlocked void .Ft int .Fn getw "FILE *stream" .Sh DESCRIPTION @@ -141,7 +141,7 @@ until the condition is cleared with .Sh STANDARDS The .Fn fgetc , -.Fn getc +.Fn getc , and .Fn getchar functions @@ -167,4 +167,3 @@ The size and byte order of an varies from one machine to another, and .Fn getw is not recommended for portable applications. -.Pp diff --git a/lib/libc/stdio/getdelim.c b/lib/libc/stdio/getdelim.c index 7af154f..d7d5627 100644 --- a/lib/libc/stdio/getdelim.c +++ b/lib/libc/stdio/getdelim.c @@ -120,8 +120,8 @@ getdelim(char ** __restrict linep, size_t * __restrict linecapp, int delim, goto error; } - if (*linecapp == 0) - *linep = NULL; + if (*linep == NULL) + *linecapp = 0; if (fp->_r <= 0 && __srefill(fp)) { /* If fp is at EOF already, we just need space for the NUL. */ diff --git a/lib/libc/stdio/getline.3 b/lib/libc/stdio/getline.3 index 0465f93..a5b39da 100644 --- a/lib/libc/stdio/getline.3 +++ b/lib/libc/stdio/getline.3 @@ -78,7 +78,8 @@ and .Fn getline functions return the number of characters written, excluding the terminating -.Dv NUL . +.Dv NUL +character. The value \-1 is returned if an error occurs, or if end-of-file is reached. .Sh EXAMPLES The following code fragment reads lines from a file and diff --git a/lib/libc/stdio/mktemp.c b/lib/libc/stdio/mktemp.c index 3f1e699..a30b930 100644 --- a/lib/libc/stdio/mktemp.c +++ b/lib/libc/stdio/mktemp.c @@ -116,6 +116,10 @@ _gettemp(path, doopen, domkdir, slen) for (trv = path; *trv != '\0'; ++trv) ; + if (trv - path >= MAXPATHLEN) { + errno = ENAMETOOLONG; + return (0); + } trv -= slen; suffp = trv; --trv; diff --git a/lib/libc/stdio/printf.3 b/lib/libc/stdio/printf.3 index 27d5bf0..8de2bb8 100644 --- a/lib/libc/stdio/printf.3 +++ b/lib/libc/stdio/printf.3 @@ -32,7 +32,7 @@ .\" @(#)printf.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd March 3, 2009 +.Dd December 2, 2009 .Dt PRINTF 3 .Os .Sh NAME @@ -55,7 +55,7 @@ .Ft int .Fn asprintf "char **ret" "const char *format" ... .Ft int -.Fn dprintf "int" "const char * restrict format" ... +.Fn dprintf "int fd" "const char * restrict format" ... .In stdarg.h .Ft int .Fn vprintf "const char * restrict format" "va_list ap" @@ -812,7 +812,7 @@ available. The conversion formats .Cm \&%D , \&%O , and -.Cm %U +.Cm \&%U are not standard and are provided only for backward compatibility. The effect of padding the diff --git a/lib/libc/stdio/sprintf.c b/lib/libc/stdio/sprintf.c index aaaae55..b55bd6c 100644 --- a/lib/libc/stdio/sprintf.c +++ b/lib/libc/stdio/sprintf.c @@ -46,17 +46,9 @@ sprintf(char * __restrict str, char const * __restrict fmt, ...) { int ret; va_list ap; - FILE f; - f._file = -1; - f._flags = __SWR | __SSTR; - f._bf._base = f._p = (unsigned char *)str; - f._bf._size = f._w = INT_MAX; - f._orientation = 0; - memset(&f._mbstate, 0, sizeof(mbstate_t)); va_start(ap, fmt); - ret = __vfprintf(&f, fmt, ap); + ret = vsprintf(str, fmt, ap); va_end(ap); - *f._p = 0; return (ret); } diff --git a/lib/libc/stdio/sscanf.c b/lib/libc/stdio/sscanf.c index 3c792e0..c793b86 100644 --- a/lib/libc/stdio/sscanf.c +++ b/lib/libc/stdio/sscanf.c @@ -41,37 +41,14 @@ __FBSDID("$FreeBSD$"); #include <stdarg.h> #include "local.h" -static int eofread(void *, char *, int); - -/* ARGSUSED */ -static int -eofread(cookie, buf, len) - void *cookie; - char *buf; - int len; -{ - - return (0); -} - int sscanf(const char * __restrict str, char const * __restrict fmt, ...) { int ret; va_list ap; - FILE f; - f._file = -1; - f._flags = __SRD; - f._bf._base = f._p = (unsigned char *)str; - f._bf._size = f._r = strlen(str); - f._read = eofread; - f._ub._base = NULL; - f._lb._base = NULL; - f._orientation = 0; - memset(&f._mbstate, 0, sizeof(mbstate_t)); va_start(ap, fmt); - ret = __svfscanf(&f, fmt, ap); + ret = vsscanf(str, fmt, ap); va_end(ap); return (ret); } diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c index f3768de..d34f559 100644 --- a/lib/libc/stdio/vfwprintf.c +++ b/lib/libc/stdio/vfwprintf.c @@ -293,7 +293,7 @@ __mbsconv(char *mbsarg, int prec) * number of characters to print. */ p = mbsarg; - insize = nchars = 0; + insize = nchars = nconv = 0; mbs = initial_mbs; while (nchars != (size_t)prec) { nconv = mbrlen(p, MB_CUR_MAX, &mbs); diff --git a/lib/libc/stdio/vsscanf.c b/lib/libc/stdio/vsscanf.c index e5e9691..22b5d2b 100644 --- a/lib/libc/stdio/vsscanf.c +++ b/lib/libc/stdio/vsscanf.c @@ -45,20 +45,15 @@ eofread(void *, char *, int); /* ARGSUSED */ static int -eofread(cookie, buf, len) - void *cookie; - char *buf; - int len; +eofread(void *cookie, char *buf, int len) { return (0); } int -vsscanf(str, fmt, ap) - const char * __restrict str; - const char * __restrict fmt; - __va_list ap; +vsscanf(const char * __restrict str, const char * __restrict fmt, + __va_list ap) { FILE f; diff --git a/lib/libc/stdio/xprintf_time.c b/lib/libc/stdio/xprintf_time.c index 81697f1..9d732fe 100644 --- a/lib/libc/stdio/xprintf_time.c +++ b/lib/libc/stdio/xprintf_time.c @@ -64,7 +64,6 @@ __printf_render_time(struct __printf_io *io, const struct printf_info *pi, const intmax_t t, tx; int i, prec, nsec; - prec = 0; if (pi->is_long) { tv = *((struct timeval **)arg[0]); t = tv->tv_sec; @@ -78,6 +77,8 @@ __printf_render_time(struct __printf_io *io, const struct printf_info *pi, const } else { tp = *((time_t **)arg[0]); t = *tp; + nsec = 0; + prec = 0; } p = buf; diff --git a/lib/libc/stdlib/getenv.3 b/lib/libc/stdlib/getenv.3 index 33d9c36..fd7d856 100644 --- a/lib/libc/stdlib/getenv.3 +++ b/lib/libc/stdlib/getenv.3 @@ -107,6 +107,15 @@ function deletes all instances of the variable name pointed to by .Fa name from the list. +.Pp +If corruption (e.g., a name without a value) is detected while making a copy of +environ for internal usage, then +.Fn setenv , +.Fn unsetenv +and +.Fn putenv +will output a warning to stderr about the issue, drop the corrupt entry and +complete the task without error. .Sh RETURN VALUES The .Fn getenv @@ -159,15 +168,6 @@ The function or .Fn putenv failed because they were unable to allocate memory for the environment. -.It Bq Er EFAULT -The functions -.Fn setenv , -.Fn unsetenv -or -.Fn putenv -failed to make a valid copy of the environment due to the environment being -corrupt (i.e., a name without a value). A warning will be output to stderr with -information about the issue. .El .Sh SEE ALSO .Xr csh 1 , diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index 2abf7fc..b7826d7 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2007-2008 Sean C. Farley <scf@FreeBSD.org> + * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -160,7 +160,7 @@ __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) /* * Find environment variable from end of array (more likely to be - * active). A variable created by putenv is always active or it is not + * active). A variable created by putenv is always active, or it is not * tracked in the array. */ for (ndx = *envNdx; ndx >= 0; ndx--) @@ -426,13 +426,14 @@ getenv(const char *name) } /* - * An empty environment (environ or its first value) regardless if - * environ has been copied before will return a NULL. + * Variable search order: + * 1. Check for an empty environ. This allows an application to clear + * the environment. + * 2. Search the external environ array. + * 3. Search the internal environment. * - * If the environment is not empty, find an environment variable via - * environ if environ has not been copied via an *env() call or been - * replaced by a running program, otherwise, use the rebuilt - * environment. + * Since malloc() depends upon getenv(), getenv() must never cause the + * internal environment storage to be generated. */ if (environ == NULL || environ[0] == NULL) return (NULL); diff --git a/lib/libc/stdlib/malloc.3 b/lib/libc/stdlib/malloc.3 index 308ba7b..9b9fe96 100644 --- a/lib/libc/stdlib/malloc.3 +++ b/lib/libc/stdlib/malloc.3 @@ -32,7 +32,7 @@ .\" @(#)malloc.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd September 26, 2009 +.Dd January 31, 2010 .Dt MALLOC 3 .Os .Sh NAME @@ -55,9 +55,7 @@ .Ft const char * .Va _malloc_options ; .Ft void -.Fo \*(lp*_malloc_message\*(rp -.Fa "const char *p1" "const char *p2" "const char *p3" "const char *p4" -.Fc +.Fn \*(lp*_malloc_message\*(rp "const char *p1" "const char *p2" "const char *p3" "const char *p4" .In malloc_np.h .Ft size_t .Fn malloc_usable_size "const void *ptr" @@ -124,7 +122,9 @@ will free the passed pointer when the requested memory cannot be allocated. This is a .Fx specific API designed to ease the problems with traditional coding styles -for realloc causing memory leaks in libraries. +for +.Fn realloc +causing memory leaks in libraries. .Pp The .Fn free @@ -184,18 +184,6 @@ flags being set) become fatal. The process will call .Xr abort 3 in these cases. -.It B -Double/halve the per-arena lock contention threshold at which a thread is -randomly re-assigned to an arena. -This dynamic load balancing tends to push threads away from highly contended -arenas, which avoids worst case contention scenarios in which threads -disproportionately utilize arenas. -However, due to the highly dynamic load that applications may place on the -allocator, it is impossible for the allocator to know in advance how sensitive -it should be to contention over arenas. -Therefore, some applications may benefit from increasing or decreasing this -threshold parameter. -This option is not available for some configurations (non-PIC). .It C Double/halve the size of the maximum size class that is a multiple of the cacheline size (64). @@ -209,44 +197,62 @@ This option is enabled by default. See the .Dq M option for related information and interactions. +.It E +Double/halve the size of the maximum medium size class. +The valid range is from one page to one half chunk. +The default value is 32 KiB. .It F -Double/halve the per-arena maximum number of dirty unused pages that are -allowed to accumulate before informing the kernel about at least half of those -pages via +Halve/double the per-arena minimum ratio of active to dirty pages. +Some dirty unused pages may be allowed to accumulate, within the limit set by +the ratio, before informing the kernel about at least half of those pages via .Xr madvise 2 . This provides the kernel with sufficient information to recycle dirty pages if physical memory becomes scarce and the pages remain unused. -The default is 512 pages per arena; -.Ev MALLOC_OPTIONS=10f -will prevent any dirty unused pages from accumulating. +The default minimum ratio is 32:1; +.Ev MALLOC_OPTIONS=6F +will disable dirty page purging. .It G -When there are multiple threads, use thread-specific caching for objects that -are smaller than one page. -This option is enabled by default. -Thread-specific caching allows many allocations to be satisfied without -performing any thread synchronization, at the cost of increased memory use. +Double/halve the approximate interval (counted in terms of +thread-specific cache allocation/deallocation events) between full +thread-specific cache garbage collection sweeps. +Garbage collection is actually performed incrementally, one size +class at a time, in order to avoid large collection pauses. +The default sweep interval is 8192; +.Ev JEMALLOC_OPTIONS=14g +will disable garbage collection. +.It H +Double/halve the number of thread-specific cache slots per size +class. +When there are multiple threads, each thread uses a +thread-specific cache for small and medium objects. +Thread-specific caching allows many allocations to be satisfied +without performing any thread synchronization, at the cost of +increased memory use. See the -.Dq R +.Dq G option for related tuning information. -This option is not available for some configurations (non-PIC). +The default number of cache slots is 128; +.Ev JEMALLOC_OPTIONS=7h +will disable thread-specific caching. +Note that one cache slot per size class is not a valid +configuration due to implementation details. .It J Each byte of new memory allocated by .Fn malloc , -.Fn realloc +.Fn realloc , or .Fn reallocf will be initialized to 0xa5. All memory returned by .Fn free , -.Fn realloc +.Fn realloc , or .Fn reallocf will be initialized to 0x5a. This is intended for debugging and will impact performance negatively. .It K Double/halve the virtual memory chunk size. -The default chunk size is the maximum of 1 MB and the largest -page size that is less than or equal to 4 MB. +The default chunk size is 4 MiB. .It M Use .Xr mmap 2 @@ -279,14 +285,6 @@ Double/halve the size of the maximum size class that is a multiple of the quantum (8 or 16 bytes, depending on architecture). Above this size, cacheline spacing is used for size classes. The default value is 128 bytes. -.It R -Double/halve magazine size, which approximately doubles/halves the number of -rounds in each magazine. -Magazines are used by the thread-specific caching machinery to acquire and -release objects in bulk. -Increasing the magazine size decreases locking overhead, at the expense of -increased memory usage. -This option is not available for some configurations (non-PIC). .It U Generate .Dq utrace @@ -297,8 +295,7 @@ Consult the source for details on this option. .It V Attempting to allocate zero bytes will return a .Dv NULL -pointer instead of -a valid pointer. +pointer instead of a valid pointer. (The default behavior is to make a minimal allocation and return a pointer to it.) This option is provided for System V compatibility. @@ -306,21 +303,20 @@ This option is incompatible with the .Dq X option. .It X -Rather than return failure for any allocation function, -display a diagnostic message on -.Dv stderr -and cause the program to drop -core (using +Rather than return failure for any allocation function, display a diagnostic +message on +.Dv STDERR_FILENO +and cause the program to drop core (using .Xr abort 3 ) . -This option should be set at compile time by including the following in -the source code: +This option should be set at compile time by including the following in the +source code: .Bd -literal -offset indent _malloc_options = "X"; .Ed .It Z Each byte of new memory allocated by .Fn malloc , -.Fn realloc +.Fn realloc , or .Fn reallocf will be initialized to 0. @@ -378,9 +374,9 @@ improve performance, mainly due to reduced cache performance. However, it may make sense to reduce the number of arenas if an application does not make much use of the allocation functions. .Pp -In addition to multiple arenas, this allocator supports thread-specific -caching for small objects (smaller than one page), in order to make it -possible to completely avoid synchronization for most small allocation requests. +In addition to multiple arenas, this allocator supports thread-specific caching +for small and medium objects, in order to make it possible to completely avoid +synchronization for most small and medium allocation requests. Such caching allows very fast allocation in the common case, but it increases memory usage and fragmentation, since a bounded number of objects can remain allocated in each thread cache. @@ -391,23 +387,27 @@ Chunks are always aligned to multiples of the chunk size. This alignment makes it possible to find metadata for user objects very quickly. .Pp -User objects are broken into three categories according to size: small, large, -and huge. +User objects are broken into four categories according to size: small, medium, +large, and huge. Small objects are smaller than one page. +Medium objects range from one page to an upper limit determined at run time (see +the +.Dq E +option). Large objects are smaller than the chunk size. Huge objects are a multiple of the chunk size. -Small and large objects are managed by arenas; huge objects are managed +Small, medium, and large objects are managed by arenas; huge objects are managed separately in a single data structure that is shared by all threads. Huge objects are used by applications infrequently enough that this single data structure is not a scalability issue. .Pp Each chunk that is managed by an arena tracks its contents as runs of -contiguous pages (unused, backing a set of small objects, or backing one large -object). +contiguous pages (unused, backing a set of small or medium objects, or backing +one large object). The combination of chunk alignment and chunk page maps makes it possible to determine all metadata regarding small and large allocations in constant time. .Pp -Small objects are managed in groups by page runs. +Small and medium objects are managed in groups by page runs. Each run maintains a bitmap that tracks which regions are in use. Allocation requests that are no more than half the quantum (8 or 16, depending on architecture) are rounded up to the nearest power of two. @@ -419,10 +419,17 @@ Allocation requests that are more than the minumum cacheline-multiple size class, but no more than the minimum subpage-multiple size class (see the .Dq C option) are rounded up to the nearest multiple of the cacheline size (64). -Allocation requests that are more than the minimum subpage-multiple size class -are rounded up to the nearest multiple of the subpage size (256). -Allocation requests that are more than one page, but small enough to fit in -an arena-managed chunk (see the +Allocation requests that are more than the minimum subpage-multiple size class, +but no more than the maximum subpage-multiple size class are rounded up to the +nearest multiple of the subpage size (256). +Allocation requests that are more than the maximum subpage-multiple size class, +but no more than the maximum medium size class (see the +.Dq M +option) are rounded up to the nearest medium size class; spacing is an +automatically determined power of two and ranges from the subpage size to the +page size. +Allocation requests that are more than the maximum medium size class, but small +enough to fit in an arena-managed chunk (see the .Dq K option), are rounded up to the nearest run size. Allocation requests that are too large to fit in an arena-managed chunk are @@ -480,13 +487,12 @@ option is set, all warnings are treated as errors. .Pp The .Va _malloc_message -variable allows the programmer to override the function which emits -the text strings forming the errors and warnings if for some reason -the -.Dv stderr +variable allows the programmer to override the function which emits the text +strings forming the errors and warnings if for some reason the +.Dv STDERR_FILENO file descriptor is not suitable for this. -Please note that doing anything which tries to allocate memory in -this function is likely to result in a crash or deadlock. +Please note that doing anything which tries to allocate memory in this function +is likely to result in a crash or deadlock. .Pp All messages are prefixed by .Dq Ao Ar progname Ac Ns Li : (malloc) . diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c index bdc26b6..295a168 100644 --- a/lib/libc/stdlib/malloc.c +++ b/lib/libc/stdlib/malloc.c @@ -1,5 +1,5 @@ /*- - * Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>. + * Copyright (C) 2006-2010 Jason Evans <jasone@FreeBSD.org>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,58 +47,67 @@ * * Allocation requests are rounded up to the nearest size class, and no record * of the original request size is maintained. Allocations are broken into - * categories according to size class. Assuming runtime defaults, 4 kB pages + * categories according to size class. Assuming runtime defaults, 4 KiB pages * and a 16 byte quantum on a 32-bit system, the size classes in each category * are as follows: * - * |=======================================| - * | Category | Subcategory | Size | - * |=======================================| - * | Small | Tiny | 2 | - * | | | 4 | - * | | | 8 | - * | |------------------+---------| - * | | Quantum-spaced | 16 | - * | | | 32 | - * | | | 48 | - * | | | ... | - * | | | 96 | - * | | | 112 | - * | | | 128 | - * | |------------------+---------| - * | | Cacheline-spaced | 192 | - * | | | 256 | - * | | | 320 | - * | | | 384 | - * | | | 448 | - * | | | 512 | - * | |------------------+---------| - * | | Sub-page | 760 | - * | | | 1024 | - * | | | 1280 | - * | | | ... | - * | | | 3328 | - * | | | 3584 | - * | | | 3840 | - * |=======================================| - * | Large | 4 kB | - * | | 8 kB | - * | | 12 kB | - * | | ... | - * | | 1012 kB | - * | | 1016 kB | - * | | 1020 kB | - * |=======================================| - * | Huge | 1 MB | - * | | 2 MB | - * | | 3 MB | - * | | ... | - * |=======================================| + * |========================================| + * | Category | Subcategory | Size | + * |========================================| + * | Small | Tiny | 2 | + * | | | 4 | + * | | | 8 | + * | |------------------+----------| + * | | Quantum-spaced | 16 | + * | | | 32 | + * | | | 48 | + * | | | ... | + * | | | 96 | + * | | | 112 | + * | | | 128 | + * | |------------------+----------| + * | | Cacheline-spaced | 192 | + * | | | 256 | + * | | | 320 | + * | | | 384 | + * | | | 448 | + * | | | 512 | + * | |------------------+----------| + * | | Sub-page | 760 | + * | | | 1024 | + * | | | 1280 | + * | | | ... | + * | | | 3328 | + * | | | 3584 | + * | | | 3840 | + * |========================================| + * | Medium | 4 KiB | + * | | 6 KiB | + * | | 8 KiB | + * | | ... | + * | | 28 KiB | + * | | 30 KiB | + * | | 32 KiB | + * |========================================| + * | Large | 36 KiB | + * | | 40 KiB | + * | | 44 KiB | + * | | ... | + * | | 1012 KiB | + * | | 1016 KiB | + * | | 1020 KiB | + * |========================================| + * | Huge | 1 MiB | + * | | 2 MiB | + * | | 3 MiB | + * | | ... | + * |========================================| * - * A different mechanism is used for each category: + * Different mechanisms are used accoding to category: * - * Small : Each size class is segregated into its own set of runs. Each run - * maintains a bitmap of which regions are free/allocated. + * Small/medium : Each size class is segregated into its own set of runs. + * Each run maintains a bitmap of which regions are + * free/allocated. * * Large : Each allocation is backed by a dedicated run. Metadata are stored * in the associated arena chunk header maps. @@ -134,18 +143,11 @@ #define MALLOC_TINY /* - * MALLOC_MAG enables a magazine-based thread-specific caching layer for small + * MALLOC_TCACHE enables a thread-specific caching layer for small and medium * objects. This makes it possible to allocate/deallocate objects without any * locking when the cache is in the steady state. */ -#define MALLOC_MAG - -/* - * MALLOC_BALANCE enables monitoring of arena lock contention and dynamically - * re-balances arena load if exponentially averaged contention exceeds a - * certain threshold. - */ -#define MALLOC_BALANCE +#define MALLOC_TCACHE /* * MALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage @@ -166,7 +168,6 @@ __FBSDID("$FreeBSD$"); #include "namespace.h" #include <sys/mman.h> #include <sys/param.h> -#include <sys/stddef.h> #include <sys/time.h> #include <sys/types.h> #include <sys/sysctl.h> @@ -185,6 +186,7 @@ __FBSDID("$FreeBSD$"); #include <stdbool.h> #include <stdio.h> #include <stdint.h> +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <strings.h> @@ -192,18 +194,12 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" -#ifdef MALLOC_DEBUG -# ifdef NDEBUG -# undef NDEBUG -# endif -#else -# ifndef NDEBUG -# define NDEBUG -# endif -#endif -#include <assert.h> - +#define RB_COMPACT #include "rb.h" +#if (defined(MALLOC_TCACHE) && defined(MALLOC_STATS)) +#include "qr.h" +#include "ql.h" +#endif #ifdef MALLOC_DEBUG /* Disable inlining to make debugging easier. */ @@ -214,55 +210,61 @@ __FBSDID("$FreeBSD$"); #define STRERROR_BUF 64 /* - * Minimum alignment of allocations is 2^QUANTUM_2POW bytes. + * Minimum alignment of allocations is 2^LG_QUANTUM bytes. */ #ifdef __i386__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 2 +# define LG_QUANTUM 4 +# define LG_SIZEOF_PTR 2 # define CPU_SPINWAIT __asm__ volatile("pause") +# define TLS_MODEL __attribute__((tls_model("initial-exec"))) #endif #ifdef __ia64__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 3 +# define LG_QUANTUM 4 +# define LG_SIZEOF_PTR 3 +# define TLS_MODEL /* default */ #endif #ifdef __alpha__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 3 +# define LG_QUANTUM 4 +# define LG_SIZEOF_PTR 3 # define NO_TLS #endif #ifdef __sparc64__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 3 +# define LG_QUANTUM 4 +# define LG_SIZEOF_PTR 3 # define NO_TLS #endif #ifdef __amd64__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 3 +# define LG_QUANTUM 4 +# define LG_SIZEOF_PTR 3 # define CPU_SPINWAIT __asm__ volatile("pause") +# define TLS_MODEL __attribute__((tls_model("initial-exec"))) #endif #ifdef __arm__ -# define QUANTUM_2POW 3 -# define SIZEOF_PTR_2POW 2 +# define LG_QUANTUM 3 +# define LG_SIZEOF_PTR 2 # define NO_TLS #endif #ifdef __mips__ -# define QUANTUM_2POW 3 -# define SIZEOF_PTR_2POW 2 +# define LG_QUANTUM 3 +# define LG_SIZEOF_PTR 2 # define NO_TLS #endif #ifdef __powerpc__ -# define QUANTUM_2POW 4 -# define SIZEOF_PTR_2POW 2 +# define LG_QUANTUM 4 +# define TLS_MODEL /* default */ +#endif +#ifdef __s390x__ +# define LG_QUANTUM 4 #endif -#define QUANTUM ((size_t)(1U << QUANTUM_2POW)) +#define QUANTUM ((size_t)(1U << LG_QUANTUM)) #define QUANTUM_MASK (QUANTUM - 1) -#define SIZEOF_PTR (1U << SIZEOF_PTR_2POW) +#define SIZEOF_PTR (1U << LG_SIZEOF_PTR) -/* sizeof(int) == (1U << SIZEOF_INT_2POW). */ -#ifndef SIZEOF_INT_2POW -# define SIZEOF_INT_2POW 2 +/* sizeof(int) == (1U << LG_SIZEOF_INT). */ +#ifndef LG_SIZEOF_INT +# define LG_SIZEOF_INT 2 #endif /* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */ @@ -271,13 +273,9 @@ __FBSDID("$FreeBSD$"); #endif #ifdef NO_TLS - /* MALLOC_MAG requires TLS. */ -# ifdef MALLOC_MAG -# undef MALLOC_MAG -# endif - /* MALLOC_BALANCE requires TLS. */ -# ifdef MALLOC_BALANCE -# undef MALLOC_BALANCE + /* MALLOC_TCACHE requires TLS. */ +# ifdef MALLOC_TCACHE +# undef MALLOC_TCACHE # endif #endif @@ -285,17 +283,24 @@ __FBSDID("$FreeBSD$"); * Size and alignment of memory chunks that are allocated by the OS's virtual * memory system. */ -#define CHUNK_2POW_DEFAULT 20 +#define LG_CHUNK_DEFAULT 22 -/* Maximum number of dirty pages per arena. */ -#define DIRTY_MAX_DEFAULT (1U << 9) +/* + * The minimum ratio of active:dirty pages per arena is computed as: + * + * (nactive >> opt_lg_dirty_mult) >= ndirty + * + * So, supposing that opt_lg_dirty_mult is 5, there can be no less than 32 + * times as many active pages as dirty pages. + */ +#define LG_DIRTY_MULT_DEFAULT 5 /* * Maximum size of L1 cache line. This is used to avoid cache line aliasing. * In addition, this controls the spacing of cacheline-spaced size classes. */ -#define CACHELINE_2POW 6 -#define CACHELINE ((size_t)(1U << CACHELINE_2POW)) +#define LG_CACHELINE 6 +#define CACHELINE ((size_t)(1U << LG_CACHELINE)) #define CACHELINE_MASK (CACHELINE - 1) /* @@ -305,13 +310,13 @@ __FBSDID("$FreeBSD$"); * There must be at least 4 subpages per page, due to the way size classes are * handled. */ -#define SUBPAGE_2POW 8 -#define SUBPAGE ((size_t)(1U << SUBPAGE_2POW)) +#define LG_SUBPAGE 8 +#define SUBPAGE ((size_t)(1U << LG_SUBPAGE)) #define SUBPAGE_MASK (SUBPAGE - 1) #ifdef MALLOC_TINY /* Smallest size class to support. */ -# define TINY_MIN_2POW 1 +# define LG_TINY_MIN 1 #endif /* @@ -319,14 +324,20 @@ __FBSDID("$FreeBSD$"); * a power of 2. Above this size, allocations are rounded up to the nearest * power of 2. */ -#define QSPACE_MAX_2POW_DEFAULT 7 +#define LG_QSPACE_MAX_DEFAULT 7 /* * Maximum size class that is a multiple of the cacheline, but not (necessarily) * a power of 2. Above this size, allocations are rounded up to the nearest * power of 2. */ -#define CSPACE_MAX_2POW_DEFAULT 9 +#define LG_CSPACE_MAX_DEFAULT 9 + +/* + * Maximum medium size class. This must not be more than 1/4 of a chunk + * (LG_MEDIUM_MAX_DEFAULT <= LG_CHUNK_DEFAULT - 2). + */ +#define LG_MEDIUM_MAX_DEFAULT 15 /* * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized @@ -350,7 +361,10 @@ __FBSDID("$FreeBSD$"); #define RUN_MAX_OVRHD_RELAX 0x00001800U /* Put a cap on small object run size. This overrides RUN_MAX_OVRHD. */ -#define RUN_MAX_SMALL (12 * PAGE_SIZE) +#define RUN_MAX_SMALL \ + (arena_maxclass <= (1U << (CHUNK_MAP_LG_PG_RANGE + PAGE_SHIFT)) \ + ? arena_maxclass : (1U << (CHUNK_MAP_LG_PG_RANGE + \ + PAGE_SHIFT))) /* * Hyper-threaded CPUs may need a special instruction inside spin loops in @@ -366,40 +380,21 @@ __FBSDID("$FreeBSD$"); * potential for priority inversion deadlock. Backing off past a certain point * can actually waste time. */ -#define SPIN_LIMIT_2POW 11 - -/* - * Conversion from spinning to blocking is expensive; we use (1U << - * BLOCK_COST_2POW) to estimate how many more times costly blocking is than - * worst-case spinning. - */ -#define BLOCK_COST_2POW 4 +#define LG_SPIN_LIMIT 11 -#ifdef MALLOC_MAG +#ifdef MALLOC_TCACHE /* - * Default magazine size, in bytes. max_rounds is calculated to make - * optimal use of the space, leaving just enough room for the magazine - * header. + * Default number of cache slots for each bin in the thread cache (0: + * disabled). */ -# define MAG_SIZE_2POW_DEFAULT 9 -#endif - -#ifdef MALLOC_BALANCE +# define LG_TCACHE_NSLOTS_DEFAULT 7 /* - * We use an exponential moving average to track recent lock contention, - * where the size of the history window is N, and alpha=2/(N+1). - * - * Due to integer math rounding, very small values here can cause - * substantial degradation in accuracy, thus making the moving average decay - * faster than it would with precise calculation. + * (1U << opt_lg_tcache_gc_sweep) is the approximate number of + * allocation events between full GC sweeps (-1: disabled). Integer + * rounding may cause the actual number to be slightly higher, since GC is + * performed incrementally. */ -# define BALANCE_ALPHA_INV_2POW 9 - - /* - * Threshold value for the exponential moving contention average at which to - * re-assign a thread. - */ -# define BALANCE_THRESHOLD_DEFAULT (1U << (SPIN_LIMIT_2POW-4)) +# define LG_TCACHE_GC_SWEEP_DEFAULT 13 #endif /******************************************************************************/ @@ -426,6 +421,17 @@ static malloc_mutex_t init_lock = {_SPINLOCK_INITIALIZER}; #ifdef MALLOC_STATS +#ifdef MALLOC_TCACHE +typedef struct tcache_bin_stats_s tcache_bin_stats_t; +struct tcache_bin_stats_s { + /* + * Number of allocation requests that corresponded to the size of this + * bin. + */ + uint64_t nrequests; +}; +#endif + typedef struct malloc_bin_stats_s malloc_bin_stats_t; struct malloc_bin_stats_s { /* @@ -434,9 +440,12 @@ struct malloc_bin_stats_s { */ uint64_t nrequests; -#ifdef MALLOC_MAG - /* Number of magazine reloads from this bin. */ - uint64_t nmags; +#ifdef MALLOC_TCACHE + /* Number of tcache fills from this bin. */ + uint64_t nfills; + + /* Number of tcache flushes to this bin. */ + uint64_t nflushes; #endif /* Total number of runs created for this bin's size class. */ @@ -449,10 +458,24 @@ struct malloc_bin_stats_s { uint64_t reruns; /* High-water mark for this bin. */ - unsigned long highruns; + size_t highruns; /* Current number of runs in this bin. */ - unsigned long curruns; + size_t curruns; +}; + +typedef struct malloc_large_stats_s malloc_large_stats_t; +struct malloc_large_stats_s { + /* + * Number of allocation requests that corresponded to this size class. + */ + uint64_t nrequests; + + /* High-water mark for this size class. */ + size_t highruns; + + /* Current number of runs of this size class. */ + size_t curruns; }; typedef struct arena_stats_s arena_stats_t; @@ -474,14 +497,21 @@ struct arena_stats_s { uint64_t nmalloc_small; uint64_t ndalloc_small; + size_t allocated_medium; + uint64_t nmalloc_medium; + uint64_t ndalloc_medium; + size_t allocated_large; uint64_t nmalloc_large; uint64_t ndalloc_large; -#ifdef MALLOC_BALANCE - /* Number of times this arena reassigned a thread due to contention. */ - uint64_t nbalance; -#endif + /* + * One element for each possible size class, including sizes that + * overlap with bin size classes. This is necessary because ipalloc() + * sometimes has to use such large objects in order to assure proper + * alignment. + */ + malloc_large_stats_t *lstats; }; typedef struct chunk_stats_s chunk_stats_t; @@ -490,14 +520,14 @@ struct chunk_stats_s { uint64_t nchunks; /* High-water mark for number of chunks allocated. */ - unsigned long highchunks; + size_t highchunks; /* * Current number of chunks allocated. This value isn't maintained for * any other purpose, so keep track of it in order to be able to set * highchunks. */ - unsigned long curchunks; + size_t curchunks; }; #endif /* #ifdef MALLOC_STATS */ @@ -550,14 +580,14 @@ struct arena_chunk_map_s { * Run address (or size) and various flags are stored together. The bit * layout looks like (assuming 32-bit system): * - * ???????? ???????? ????---- ---kdzla + * ???????? ???????? ????cccc ccccdzla * * ? : Unallocated: Run address for first/last pages, unset for internal * pages. - * Small: Run address. + * Small/medium: Don't care. * Large: Run size for first page, unset for trailing pages. * - : Unused. - * k : key? + * c : refcount (could overflow for PAGE_SIZE >= 128 KiB) * d : dirty? * z : zeroed? * l : large? @@ -565,7 +595,7 @@ struct arena_chunk_map_s { * * Following are example bit patterns for the three types of runs. * - * r : run address + * p : run page offset * s : run size * x : don't care * - : 0 @@ -576,10 +606,10 @@ struct arena_chunk_map_s { * xxxxxxxx xxxxxxxx xxxx---- ----d--- * ssssssss ssssssss ssss---- -----z-- * - * Small: - * rrrrrrrr rrrrrrrr rrrr---- -------a - * rrrrrrrr rrrrrrrr rrrr---- -------a - * rrrrrrrr rrrrrrrr rrrr---- -------a + * Small/medium: + * pppppppp ppppcccc cccccccc cccc---a + * pppppppp ppppcccc cccccccc cccc---a + * pppppppp ppppcccc cccccccc cccc---a * * Large: * ssssssss ssssssss ssss---- ------la @@ -587,11 +617,19 @@ struct arena_chunk_map_s { * -------- -------- -------- ------la */ size_t bits; -#define CHUNK_MAP_KEY ((size_t)0x10U) -#define CHUNK_MAP_DIRTY ((size_t)0x08U) -#define CHUNK_MAP_ZEROED ((size_t)0x04U) -#define CHUNK_MAP_LARGE ((size_t)0x02U) -#define CHUNK_MAP_ALLOCATED ((size_t)0x01U) +#define CHUNK_MAP_PG_MASK ((size_t)0xfff00000U) +#define CHUNK_MAP_PG_SHIFT 20 +#define CHUNK_MAP_LG_PG_RANGE 12 + +#define CHUNK_MAP_RC_MASK ((size_t)0xffff0U) +#define CHUNK_MAP_RC_ONE ((size_t)0x00010U) + +#define CHUNK_MAP_FLAGS_MASK ((size_t)0xfU) +#define CHUNK_MAP_DIRTY ((size_t)0x8U) +#define CHUNK_MAP_ZEROED ((size_t)0x4U) +#define CHUNK_MAP_LARGE ((size_t)0x2U) +#define CHUNK_MAP_ALLOCATED ((size_t)0x1U) +#define CHUNK_MAP_KEY (CHUNK_MAP_DIRTY | CHUNK_MAP_ALLOCATED) }; typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; @@ -605,6 +643,13 @@ struct arena_chunk_s { /* Linkage for the arena's chunks_dirty tree. */ rb_node(arena_chunk_t) link_dirty; + /* + * True if the chunk is currently in the chunks_dirty tree, due to + * having at some point contained one or more dirty pages. Removal + * from chunks_dirty is lazy, so (dirtied && ndirty == 0) is possible. + */ + bool dirtied; + /* Number of dirty pages. */ size_t ndirty; @@ -670,6 +715,10 @@ struct arena_bin_s { #endif }; +#ifdef MALLOC_TCACHE +typedef struct tcache_s tcache_t; +#endif + struct arena_s { #ifdef MALLOC_DEBUG uint32_t magic; @@ -681,6 +730,13 @@ struct arena_s { #ifdef MALLOC_STATS arena_stats_t stats; +# ifdef MALLOC_TCACHE + /* + * List of tcaches for extant threads associated with this arena. + * Stats from these are merged incrementally, and at exit. + */ + ql_head(tcache_t) tcache_ql; +# endif #endif /* Tree of dirty-page-containing chunks this arena manages. */ @@ -698,6 +754,9 @@ struct arena_s { */ arena_chunk_t *spare; + /* Number of pages in active runs. */ + size_t nactive; + /* * Current count of pages within unused runs that are potentially * dirty, and for which madvise(... MADV_FREE) has not been called. By @@ -712,67 +771,77 @@ struct arena_s { */ arena_avail_tree_t runs_avail; -#ifdef MALLOC_BALANCE - /* - * The arena load balancing machinery needs to keep track of how much - * lock contention there is. This value is exponentially averaged. - */ - uint32_t contention; -#endif - /* - * bins is used to store rings of free regions of the following sizes, - * assuming a 16-byte quantum, 4kB page size, and default + * bins is used to store trees of free regions of the following sizes, + * assuming a 16-byte quantum, 4 KiB page size, and default * MALLOC_OPTIONS. * - * bins[i] | size | - * --------+------+ - * 0 | 2 | - * 1 | 4 | - * 2 | 8 | - * --------+------+ - * 3 | 16 | - * 4 | 32 | - * 5 | 48 | - * 6 | 64 | - * : : - * : : - * 33 | 496 | - * 34 | 512 | - * --------+------+ - * 35 | 1024 | - * 36 | 2048 | - * --------+------+ + * bins[i] | size | + * --------+--------+ + * 0 | 2 | + * 1 | 4 | + * 2 | 8 | + * --------+--------+ + * 3 | 16 | + * 4 | 32 | + * 5 | 48 | + * : : + * 8 | 96 | + * 9 | 112 | + * 10 | 128 | + * --------+--------+ + * 11 | 192 | + * 12 | 256 | + * 13 | 320 | + * 14 | 384 | + * 15 | 448 | + * 16 | 512 | + * --------+--------+ + * 17 | 768 | + * 18 | 1024 | + * 19 | 1280 | + * : : + * 27 | 3328 | + * 28 | 3584 | + * 29 | 3840 | + * --------+--------+ + * 30 | 4 KiB | + * 31 | 6 KiB | + * 33 | 8 KiB | + * : : + * 43 | 28 KiB | + * 44 | 30 KiB | + * 45 | 32 KiB | + * --------+--------+ */ arena_bin_t bins[1]; /* Dynamically sized. */ }; /******************************************************************************/ /* - * Magazine data structures. + * Thread cache data structures. */ -#ifdef MALLOC_MAG -typedef struct mag_s mag_t; -struct mag_s { - size_t binind; /* Index of associated bin. */ - size_t nrounds; - void *rounds[1]; /* Dynamically sized. */ -}; - -/* - * Magazines are lazily allocated, but once created, they remain until the - * associated mag_rack is destroyed. - */ -typedef struct bin_mags_s bin_mags_t; -struct bin_mags_s { - mag_t *curmag; - mag_t *sparemag; +#ifdef MALLOC_TCACHE +typedef struct tcache_bin_s tcache_bin_t; +struct tcache_bin_s { +# ifdef MALLOC_STATS + tcache_bin_stats_t tstats; +# endif + unsigned low_water; /* Min # cached since last GC. */ + unsigned high_water; /* Max # cached since last GC. */ + unsigned ncached; /* # of cached objects. */ + void *slots[1]; /* Dynamically sized. */ }; -typedef struct mag_rack_s mag_rack_t; -struct mag_rack_s { - bin_mags_t bin_mags[1]; /* Dynamically sized. */ +struct tcache_s { +# ifdef MALLOC_STATS + ql_elm(tcache_t) link; /* Used for aggregating stats. */ +# endif + arena_t *arena; /* This thread's arena. */ + unsigned ev_cnt; /* Event count since incremental GC. */ + unsigned next_gc_bin; /* Next bin to GC. */ + tcache_bin_t *tbins[1]; /* Dynamically sized. */ }; #endif @@ -786,14 +855,16 @@ static unsigned ncpus; /* Various bin-related settings. */ #ifdef MALLOC_TINY /* Number of (2^n)-spaced tiny bins. */ -# define ntbins ((unsigned)(QUANTUM_2POW - TINY_MIN_2POW)) +# define ntbins ((unsigned)(LG_QUANTUM - LG_TINY_MIN)) #else # define ntbins 0 #endif static unsigned nqbins; /* Number of quantum-spaced bins. */ static unsigned ncbins; /* Number of cacheline-spaced bins. */ static unsigned nsbins; /* Number of subpage-spaced bins. */ +static unsigned nmbins; /* Number of medium bins. */ static unsigned nbins; +static unsigned mbin0; /* mbin offset (nbins - nmbins). */ #ifdef MALLOC_TINY # define tspace_max ((size_t)(QUANTUM >> 1)) #endif @@ -803,13 +874,26 @@ static size_t cspace_min; static size_t cspace_max; static size_t sspace_min; static size_t sspace_max; -#define bin_maxclass sspace_max +#define small_maxclass sspace_max +#define medium_min PAGE_SIZE +static size_t medium_max; +#define bin_maxclass medium_max + +/* + * Soft limit on the number of medium size classes. Spacing between medium + * size classes never exceeds pagesize, which can force more than NBINS_MAX + * medium size classes. + */ +#define NMBINS_MAX 16 +/* Spacing between medium size classes. */ +static size_t lg_mspace; +static size_t mspace_mask; -static uint8_t const *size2bin; +static uint8_t const *small_size2bin; /* - * const_size2bin is a static constant lookup table that in the common case can - * be used as-is for size2bin. For dynamically linked programs, this avoids - * a page of memory overhead per process. + * const_small_size2bin is a static constant lookup table that in the common + * case can be used as-is for small_size2bin. For dynamically linked programs, + * this avoids a page of memory overhead per process. */ #define S2B_1(i) i, #define S2B_2(i) S2B_1(i) S2B_1(i) @@ -820,9 +904,9 @@ static uint8_t const *size2bin; #define S2B_64(i) S2B_32(i) S2B_32(i) #define S2B_128(i) S2B_64(i) S2B_64(i) #define S2B_256(i) S2B_128(i) S2B_128(i) -static const uint8_t const_size2bin[PAGE_SIZE - 255] = { +static const uint8_t const_small_size2bin[PAGE_SIZE - 255] = { S2B_1(0xffU) /* 0 */ -#if (QUANTUM_2POW == 4) +#if (LG_QUANTUM == 4) /* 64-bit system ************************/ # ifdef MALLOC_TINY S2B_2(0) /* 2 */ @@ -923,10 +1007,6 @@ static const uint8_t const_size2bin[PAGE_SIZE - 255] = { #undef S2B_CMIN #undef S2B_SMIN -#ifdef MALLOC_MAG -static size_t max_rounds; -#endif - /* Various chunk-related settings. */ static size_t chunksize; static size_t chunksize_mask; /* (chunksize - 1). */ @@ -1006,31 +1086,46 @@ static size_t base_mapped; static arena_t **arenas; static unsigned narenas; #ifndef NO_TLS -# ifdef MALLOC_BALANCE -static unsigned narenas_2pow; -# else static unsigned next_arena; -# endif #endif static pthread_mutex_t arenas_lock; /* Protects arenas initialization. */ #ifndef NO_TLS /* - * Map of pthread_self() --> arenas[???], used for selecting an arena to use + * Map of _pthread_self() --> arenas[???], used for selecting an arena to use * for allocations. */ -static __thread arena_t *arenas_map; +static __thread arena_t *arenas_map TLS_MODEL; +#endif + +#ifdef MALLOC_TCACHE +/* Map of thread-specific caches. */ +static __thread tcache_t *tcache_tls TLS_MODEL; + +/* + * Number of cache slots for each bin in the thread cache, or 0 if tcache is + * disabled. + */ +size_t tcache_nslots; + +/* Number of tcache allocation/deallocation events between incremental GCs. */ +unsigned tcache_gc_incr; #endif -#ifdef MALLOC_MAG /* - * Map of thread-specific magazine racks, used for thread-specific object - * caching. + * Used by chunk_alloc_mmap() to decide whether to attempt the fast path and + * potentially avoid some system calls. We can get away without TLS here, + * since the state of mmap_unaligned only affects performance, rather than + * correct function. */ -static __thread mag_rack_t *mag_rack; +#ifndef NO_TLS +static __thread bool mmap_unaligned TLS_MODEL; +#else +static bool mmap_unaligned; #endif #ifdef MALLOC_STATS +static malloc_mutex_t chunks_mtx; /* Chunk statistics. */ static chunk_stats_t stats_chunks; #endif @@ -1048,22 +1143,20 @@ static bool opt_junk = true; static bool opt_abort = false; static bool opt_junk = false; #endif +#ifdef MALLOC_TCACHE +static size_t opt_lg_tcache_nslots = LG_TCACHE_NSLOTS_DEFAULT; +static ssize_t opt_lg_tcache_gc_sweep = LG_TCACHE_GC_SWEEP_DEFAULT; +#endif #ifdef MALLOC_DSS static bool opt_dss = true; static bool opt_mmap = true; #endif -#ifdef MALLOC_MAG -static bool opt_mag = true; -static size_t opt_mag_size_2pow = MAG_SIZE_2POW_DEFAULT; -#endif -static size_t opt_dirty_max = DIRTY_MAX_DEFAULT; -#ifdef MALLOC_BALANCE -static uint64_t opt_balance_threshold = BALANCE_THRESHOLD_DEFAULT; -#endif -static bool opt_print_stats = false; -static size_t opt_qspace_max_2pow = QSPACE_MAX_2POW_DEFAULT; -static size_t opt_cspace_max_2pow = CSPACE_MAX_2POW_DEFAULT; -static size_t opt_chunk_2pow = CHUNK_2POW_DEFAULT; +static ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT; +static bool opt_stats_print = false; +static size_t opt_lg_qspace_max = LG_QSPACE_MAX_DEFAULT; +static size_t opt_lg_cspace_max = LG_CSPACE_MAX_DEFAULT; +static size_t opt_lg_medium_max = LG_MEDIUM_MAX_DEFAULT; +static size_t opt_lg_chunk = LG_CHUNK_DEFAULT; static bool opt_utrace = false; static bool opt_sysv = false; static bool opt_xmalloc = false; @@ -1092,12 +1185,15 @@ typedef struct { static void malloc_mutex_init(malloc_mutex_t *mutex); static bool malloc_spin_init(pthread_mutex_t *lock); +#ifdef MALLOC_TINY +static size_t pow2_ceil(size_t x); +#endif static void wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4); #ifdef MALLOC_STATS static void malloc_printf(const char *format, ...); #endif -static char *umax2s(uintmax_t x, char *s); +static char *umax2s(uintmax_t x, unsigned base, char *s); #ifdef MALLOC_DSS static bool base_pages_alloc_dss(size_t minsize); #endif @@ -1107,17 +1203,15 @@ static void *base_alloc(size_t size); static void *base_calloc(size_t number, size_t size); static extent_node_t *base_node_alloc(void); static void base_node_dealloc(extent_node_t *node); -#ifdef MALLOC_STATS -static void stats_print(arena_t *arena); -#endif static void *pages_map(void *addr, size_t size); static void pages_unmap(void *addr, size_t size); #ifdef MALLOC_DSS -static void *chunk_alloc_dss(size_t size); -static void *chunk_recycle_dss(size_t size, bool zero); +static void *chunk_alloc_dss(size_t size, bool *zero); +static void *chunk_recycle_dss(size_t size, bool *zero); #endif +static void *chunk_alloc_mmap_slow(size_t size, bool unaligned); static void *chunk_alloc_mmap(size_t size); -static void *chunk_alloc(size_t size, bool zero); +static void *chunk_alloc(size_t size, bool *zero); #ifdef MALLOC_DSS static extent_node_t *chunk_dealloc_dss_record(void *chunk, size_t size); static bool chunk_dealloc_dss(void *chunk, size_t size); @@ -1142,51 +1236,163 @@ static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin); static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin); static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size); -#ifdef MALLOC_BALANCE -static void arena_lock_balance_hard(arena_t *arena); -#endif -#ifdef MALLOC_MAG -static void mag_load(mag_t *mag); +#ifdef MALLOC_TCACHE +static void tcache_bin_fill(tcache_t *tcache, tcache_bin_t *tbin, + size_t binind); +static void *tcache_alloc_hard(tcache_t *tcache, tcache_bin_t *tbin, + size_t binind); #endif +static void *arena_malloc_medium(arena_t *arena, size_t size, bool zero); static void *arena_malloc_large(arena_t *arena, size_t size, bool zero); static void *arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size); +static bool arena_is_large(const void *ptr); static size_t arena_salloc(const void *ptr); -#ifdef MALLOC_MAG -static void mag_unload(mag_t *mag); +static void +arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + arena_bin_t *bin); +#ifdef MALLOC_STATS +static void arena_stats_print(arena_t *arena); +#endif +static void stats_print_atexit(void); +#ifdef MALLOC_TCACHE +static void tcache_bin_flush(tcache_bin_t *tbin, size_t binind, + unsigned rem); #endif static void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr); +#ifdef MALLOC_TCACHE +static void arena_dalloc_hard(arena_t *arena, arena_chunk_t *chunk, + void *ptr, arena_chunk_map_t *mapelm, tcache_t *tcache); +#endif static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t size, size_t oldsize); static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t size, size_t oldsize); static bool arena_ralloc_large(void *ptr, size_t size, size_t oldsize); static void *arena_ralloc(void *ptr, size_t size, size_t oldsize); -static bool arena_new(arena_t *arena); +static bool arena_new(arena_t *arena, unsigned ind); static arena_t *arenas_extend(unsigned ind); -#ifdef MALLOC_MAG -static mag_t *mag_create(arena_t *arena, size_t binind); -static void mag_destroy(mag_t *mag); -static mag_rack_t *mag_rack_create(arena_t *arena); -static void mag_rack_destroy(mag_rack_t *rack); +#ifdef MALLOC_TCACHE +static tcache_bin_t *tcache_bin_create(arena_t *arena); +static void tcache_bin_destroy(tcache_t *tcache, tcache_bin_t *tbin, + unsigned binind); +# ifdef MALLOC_STATS +static void tcache_stats_merge(tcache_t *tcache, arena_t *arena); +# endif +static tcache_t *tcache_create(arena_t *arena); +static void tcache_destroy(tcache_t *tcache); #endif static void *huge_malloc(size_t size, bool zero); static void *huge_palloc(size_t alignment, size_t size); static void *huge_ralloc(void *ptr, size_t size, size_t oldsize); static void huge_dalloc(void *ptr); -static void malloc_print_stats(void); +static void malloc_stats_print(void); #ifdef MALLOC_DEBUG -static void size2bin_validate(void); +static void small_size2bin_validate(void); #endif -static bool size2bin_init(void); -static bool size2bin_init_hard(void); +static bool small_size2bin_init(void); +static bool small_size2bin_init_hard(void); +static unsigned malloc_ncpus(void); static bool malloc_init_hard(void); /* * End function prototypes. */ /******************************************************************************/ + +static void +wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4) +{ + + if (_write(STDERR_FILENO, p1, strlen(p1)) < 0 + || _write(STDERR_FILENO, p2, strlen(p2)) < 0 + || _write(STDERR_FILENO, p3, strlen(p3)) < 0 + || _write(STDERR_FILENO, p4, strlen(p4)) < 0) + return; +} + +void (*_malloc_message)(const char *p1, const char *p2, const char *p3, + const char *p4) = wrtmessage; + +/* + * We don't want to depend on vsnprintf() for production builds, since that can + * cause unnecessary bloat for static binaries. umax2s() provides minimal + * integer printing functionality, so that malloc_printf() use can be limited to + * MALLOC_STATS code. + */ +#define UMAX2S_BUFSIZE 65 +static char * +umax2s(uintmax_t x, unsigned base, char *s) +{ + unsigned i; + + i = UMAX2S_BUFSIZE - 1; + s[i] = '\0'; + switch (base) { + case 10: + do { + i--; + s[i] = "0123456789"[x % 10]; + x /= 10; + } while (x > 0); + break; + case 16: + do { + i--; + s[i] = "0123456789abcdef"[x & 0xf]; + x >>= 4; + } while (x > 0); + break; + default: + do { + i--; + s[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base]; + x /= base; + } while (x > 0); + } + + return (&s[i]); +} + +/* + * Define a custom assert() in order to reduce the chances of deadlock during + * assertion failure. + */ +#ifdef MALLOC_DEBUG +# define assert(e) do { \ + if (!(e)) { \ + char line_buf[UMAX2S_BUFSIZE]; \ + _malloc_message(_getprogname(), ": (malloc) ", \ + __FILE__, ":"); \ + _malloc_message(umax2s(__LINE__, 10, line_buf), \ + ": Failed assertion: ", "\"", #e); \ + _malloc_message("\"\n", "", "", ""); \ + abort(); \ + } \ +} while (0) +#else +#define assert(e) +#endif + +#ifdef MALLOC_STATS +/* + * Print to stderr in such a way as to (hopefully) avoid memory allocation. + */ +static void +malloc_printf(const char *format, ...) +{ + char buf[4096]; + va_list ap; + + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + _malloc_message(buf, "", "", ""); +} +#endif + +/******************************************************************************/ /* * Begin mutex. We can't use normal pthread mutexes in all places, because * they require malloc()ed memory, which causes bootstrapping issues in some @@ -1255,10 +1461,9 @@ malloc_spin_init(pthread_mutex_t *lock) return (false); } -static inline unsigned +static inline void malloc_spin_lock(pthread_mutex_t *lock) { - unsigned ret = 0; if (__isthreaded) { if (_pthread_mutex_trylock(lock) != 0) { @@ -1267,14 +1472,13 @@ malloc_spin_lock(pthread_mutex_t *lock) unsigned i; volatile unsigned j; - for (i = 1; i <= SPIN_LIMIT_2POW; i++) { + for (i = 1; i <= LG_SPIN_LIMIT; i++) { for (j = 0; j < (1U << i); j++) { - ret++; CPU_SPINWAIT; } if (_pthread_mutex_trylock(lock) == 0) - return (ret); + return; } } @@ -1284,12 +1488,8 @@ malloc_spin_lock(pthread_mutex_t *lock) * inversion. */ _pthread_mutex_lock(lock); - assert((ret << BLOCK_COST_2POW) != 0 || ncpus == 1); - return (ret << BLOCK_COST_2POW); } } - - return (ret); } static inline void @@ -1332,13 +1532,17 @@ malloc_spin_unlock(pthread_mutex_t *lock) #define SUBPAGE_CEILING(s) \ (((s) + SUBPAGE_MASK) & ~SUBPAGE_MASK) -/* Return the smallest PAGE_SIZE multiple that is >= s. */ +/* Return the smallest medium size class that is >= s. */ +#define MEDIUM_CEILING(s) \ + (((s) + mspace_mask) & ~mspace_mask) + +/* Return the smallest pagesize multiple that is >= s. */ #define PAGE_CEILING(s) \ (((s) + PAGE_MASK) & ~PAGE_MASK) #ifdef MALLOC_TINY /* Compute the smallest power of 2 that is >= x. */ -static inline size_t +static size_t pow2_ceil(size_t x) { @@ -1356,112 +1560,6 @@ pow2_ceil(size_t x) } #endif -#ifdef MALLOC_BALANCE -/* - * Use a simple linear congruential pseudo-random number generator: - * - * prn(y) = (a*x + c) % m - * - * where the following constants ensure maximal period: - * - * a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4. - * c == Odd number (relatively prime to 2^n). - * m == 2^32 - * - * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints. - * - * This choice of m has the disadvantage that the quality of the bits is - * proportional to bit position. For example. the lowest bit has a cycle of 2, - * the next has a cycle of 4, etc. For this reason, we prefer to use the upper - * bits. - */ -# define PRN_DEFINE(suffix, var, a, c) \ -static inline void \ -sprn_##suffix(uint32_t seed) \ -{ \ - var = seed; \ -} \ - \ -static inline uint32_t \ -prn_##suffix(uint32_t lg_range) \ -{ \ - uint32_t ret, x; \ - \ - assert(lg_range > 0); \ - assert(lg_range <= 32); \ - \ - x = (var * (a)) + (c); \ - var = x; \ - ret = x >> (32 - lg_range); \ - \ - return (ret); \ -} -# define SPRN(suffix, seed) sprn_##suffix(seed) -# define PRN(suffix, lg_range) prn_##suffix(lg_range) -#endif - -#ifdef MALLOC_BALANCE -/* Define the PRNG used for arena assignment. */ -static __thread uint32_t balance_x; -PRN_DEFINE(balance, balance_x, 1297, 1301) -#endif - -static void -wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4) -{ - - _write(STDERR_FILENO, p1, strlen(p1)); - _write(STDERR_FILENO, p2, strlen(p2)); - _write(STDERR_FILENO, p3, strlen(p3)); - _write(STDERR_FILENO, p4, strlen(p4)); -} - -void (*_malloc_message)(const char *p1, const char *p2, const char *p3, - const char *p4) = wrtmessage; - -#ifdef MALLOC_STATS -/* - * Print to stderr in such a way as to (hopefully) avoid memory allocation. - */ -static void -malloc_printf(const char *format, ...) -{ - char buf[4096]; - va_list ap; - - va_start(ap, format); - vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - _malloc_message(buf, "", "", ""); -} -#endif - -/* - * We don't want to depend on vsnprintf() for production builds, since that can - * cause unnecessary bloat for static binaries. umax2s() provides minimal - * integer printing functionality, so that malloc_printf() use can be limited to - * MALLOC_STATS code. - */ -#define UMAX2S_BUFSIZE 21 -static char * -umax2s(uintmax_t x, char *s) -{ - unsigned i; - - /* Make sure UMAX2S_BUFSIZE is large enough. */ - assert(sizeof(uintmax_t) <= 8); - - i = UMAX2S_BUFSIZE - 1; - s[i] = '\0'; - do { - i--; - s[i] = "0123456789"[x % 10]; - x /= 10; - } while (x > 0); - - return (&s[i]); -} - /******************************************************************************/ #ifdef MALLOC_DSS @@ -1587,7 +1685,8 @@ base_calloc(size_t number, size_t size) void *ret; ret = base_alloc(number * size); - memset(ret, 0, number * size); + if (ret != NULL) + memset(ret, 0, number * size); return (ret); } @@ -1620,93 +1719,6 @@ base_node_dealloc(extent_node_t *node) malloc_mutex_unlock(&base_mtx); } -/******************************************************************************/ - -#ifdef MALLOC_STATS -static void -stats_print(arena_t *arena) -{ - unsigned i, gap_start; - - malloc_printf("dirty: %zu page%s dirty, %llu sweep%s," - " %llu madvise%s, %llu page%s purged\n", - arena->ndirty, arena->ndirty == 1 ? "" : "s", - arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", - arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", - arena->stats.purged, arena->stats.purged == 1 ? "" : "s"); - - malloc_printf(" allocated nmalloc ndalloc\n"); - malloc_printf("small: %12zu %12llu %12llu\n", - arena->stats.allocated_small, arena->stats.nmalloc_small, - arena->stats.ndalloc_small); - malloc_printf("large: %12zu %12llu %12llu\n", - arena->stats.allocated_large, arena->stats.nmalloc_large, - arena->stats.ndalloc_large); - malloc_printf("total: %12zu %12llu %12llu\n", - arena->stats.allocated_small + arena->stats.allocated_large, - arena->stats.nmalloc_small + arena->stats.nmalloc_large, - arena->stats.ndalloc_small + arena->stats.ndalloc_large); - malloc_printf("mapped: %12zu\n", arena->stats.mapped); - -#ifdef MALLOC_MAG - if (__isthreaded && opt_mag) { - malloc_printf("bins: bin size regs pgs mags " - "newruns reruns maxruns curruns\n"); - } else { -#endif - malloc_printf("bins: bin size regs pgs requests " - "newruns reruns maxruns curruns\n"); -#ifdef MALLOC_MAG - } -#endif - for (i = 0, gap_start = UINT_MAX; i < nbins; i++) { - if (arena->bins[i].stats.nruns == 0) { - if (gap_start == UINT_MAX) - gap_start = i; - } else { - if (gap_start != UINT_MAX) { - if (i > gap_start + 1) { - /* Gap of more than one size class. */ - malloc_printf("[%u..%u]\n", - gap_start, i - 1); - } else { - /* Gap of one size class. */ - malloc_printf("[%u]\n", gap_start); - } - gap_start = UINT_MAX; - } - malloc_printf( - "%13u %1s %4u %4u %3u %9llu %9llu" - " %9llu %7lu %7lu\n", - i, - i < ntbins ? "T" : i < ntbins + nqbins ? "Q" : - i < ntbins + nqbins + ncbins ? "C" : "S", - arena->bins[i].reg_size, - arena->bins[i].nregs, - arena->bins[i].run_size >> PAGE_SHIFT, -#ifdef MALLOC_MAG - (__isthreaded && opt_mag) ? - arena->bins[i].stats.nmags : -#endif - arena->bins[i].stats.nrequests, - arena->bins[i].stats.nruns, - arena->bins[i].stats.reruns, - arena->bins[i].stats.highruns, - arena->bins[i].stats.curruns); - } - } - if (gap_start != UINT_MAX) { - if (i > gap_start + 1) { - /* Gap of more than one size class. */ - malloc_printf("[%u..%u]\n", gap_start, i - 1); - } else { - /* Gap of one size class. */ - malloc_printf("[%u]\n", gap_start); - } - } -} -#endif - /* * End Utility functions/macros. */ @@ -1735,7 +1747,7 @@ extent_szad_comp(extent_node_t *a, extent_node_t *b) } /* Wrap red-black tree macros in functions. */ -rb_wrap(__unused static, extent_tree_szad_, extent_tree_t, extent_node_t, +rb_gen(__unused static, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad, extent_szad_comp) #endif @@ -1749,7 +1761,7 @@ extent_ad_comp(extent_node_t *a, extent_node_t *b) } /* Wrap red-black tree macros in functions. */ -rb_wrap(__unused static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, +rb_gen(__unused static, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, extent_ad_comp) /* @@ -1813,8 +1825,13 @@ pages_unmap(void *addr, size_t size) #ifdef MALLOC_DSS static void * -chunk_alloc_dss(size_t size) +chunk_alloc_dss(size_t size, bool *zero) { + void *ret; + + ret = chunk_recycle_dss(size, zero); + if (ret != NULL) + return (ret); /* * sbrk() uses a signed increment argument, so take care not to @@ -1833,8 +1850,6 @@ chunk_alloc_dss(size_t size) * malloc. */ do { - void *ret; - /* Get the current end of the DSS. */ dss_max = sbrk(0); @@ -1856,6 +1871,7 @@ chunk_alloc_dss(size_t size) /* Success. */ dss_max = (void *)((intptr_t)dss_prev + incr); malloc_mutex_unlock(&dss_mtx); + *zero = true; return (ret); } } while (dss_prev != (void *)-1); @@ -1866,7 +1882,7 @@ chunk_alloc_dss(size_t size) } static void * -chunk_recycle_dss(size_t size, bool zero) +chunk_recycle_dss(size_t size, bool *zero) { extent_node_t *node, key; @@ -1895,7 +1911,7 @@ chunk_recycle_dss(size_t size, bool zero) } malloc_mutex_unlock(&dss_mtx); - if (zero) + if (*zero) memset(ret, 0, size); return (ret); } @@ -1906,82 +1922,124 @@ chunk_recycle_dss(size_t size, bool zero) #endif static void * -chunk_alloc_mmap(size_t size) +chunk_alloc_mmap_slow(size_t size, bool unaligned) { void *ret; size_t offset; + /* Beware size_t wrap-around. */ + if (size + chunksize <= size) + return (NULL); + + ret = pages_map(NULL, size + chunksize); + if (ret == NULL) + return (NULL); + + /* Clean up unneeded leading/trailing space. */ + offset = CHUNK_ADDR2OFFSET(ret); + if (offset != 0) { + /* Note that mmap() returned an unaligned mapping. */ + unaligned = true; + + /* Leading space. */ + pages_unmap(ret, chunksize - offset); + + ret = (void *)((uintptr_t)ret + + (chunksize - offset)); + + /* Trailing space. */ + pages_unmap((void *)((uintptr_t)ret + size), + offset); + } else { + /* Trailing space only. */ + pages_unmap((void *)((uintptr_t)ret + size), + chunksize); + } + + /* + * If mmap() returned an aligned mapping, reset mmap_unaligned so that + * the next chunk_alloc_mmap() execution tries the fast allocation + * method. + */ + if (unaligned == false) + mmap_unaligned = false; + + return (ret); +} + +static void * +chunk_alloc_mmap(size_t size) +{ + void *ret; + /* * Ideally, there would be a way to specify alignment to mmap() (like * NetBSD has), but in the absence of such a feature, we have to work * hard to efficiently create aligned mappings. The reliable, but - * expensive method is to create a mapping that is over-sized, then - * trim the excess. However, that always results in at least one call - * to pages_unmap(). + * slow method is to create a mapping that is over-sized, then trim the + * excess. However, that always results in at least one call to + * pages_unmap(). * * A more optimistic approach is to try mapping precisely the right * amount, then try to append another mapping if alignment is off. In * practice, this works out well as long as the application is not * interleaving mappings via direct mmap() calls. If we do run into a * situation where there is an interleaved mapping and we are unable to - * extend an unaligned mapping, our best option is to momentarily - * revert to the reliable-but-expensive method. This will tend to - * leave a gap in the memory map that is too small to cause later - * problems for the optimistic method. + * extend an unaligned mapping, our best option is to switch to the + * slow method until mmap() returns another aligned mapping. This will + * tend to leave a gap in the memory map that is too small to cause + * later problems for the optimistic method. + * + * Another possible confounding factor is address space layout + * randomization (ASLR), which causes mmap(2) to disregard the + * requested address. mmap_unaligned tracks whether the previous + * chunk_alloc_mmap() execution received any unaligned or relocated + * mappings, and if so, the current execution will immediately fall + * back to the slow method. However, we keep track of whether the fast + * method would have succeeded, and if so, we make a note to try the + * fast method next time. */ - ret = pages_map(NULL, size); - if (ret == NULL) - return (NULL); - - offset = CHUNK_ADDR2OFFSET(ret); - if (offset != 0) { - /* Try to extend chunk boundary. */ - if (pages_map((void *)((uintptr_t)ret + size), - chunksize - offset) == NULL) { - /* - * Extension failed. Clean up, then revert to the - * reliable-but-expensive method. - */ - pages_unmap(ret, size); - - /* Beware size_t wrap-around. */ - if (size + chunksize <= size) - return NULL; - - ret = pages_map(NULL, size + chunksize); - if (ret == NULL) - return (NULL); + if (mmap_unaligned == false) { + size_t offset; - /* Clean up unneeded leading/trailing space. */ - offset = CHUNK_ADDR2OFFSET(ret); - if (offset != 0) { - /* Leading space. */ - pages_unmap(ret, chunksize - offset); - - ret = (void *)((uintptr_t)ret + - (chunksize - offset)); + ret = pages_map(NULL, size); + if (ret == NULL) + return (NULL); - /* Trailing space. */ - pages_unmap((void *)((uintptr_t)ret + size), - offset); + offset = CHUNK_ADDR2OFFSET(ret); + if (offset != 0) { + mmap_unaligned = true; + /* Try to extend chunk boundary. */ + if (pages_map((void *)((uintptr_t)ret + size), + chunksize - offset) == NULL) { + /* + * Extension failed. Clean up, then revert to + * the reliable-but-expensive method. + */ + pages_unmap(ret, size); + ret = chunk_alloc_mmap_slow(size, true); } else { - /* Trailing space only. */ - pages_unmap((void *)((uintptr_t)ret + size), - chunksize); + /* Clean up unneeded leading space. */ + pages_unmap(ret, chunksize - offset); + ret = (void *)((uintptr_t)ret + (chunksize - + offset)); } - } else { - /* Clean up unneeded leading space. */ - pages_unmap(ret, chunksize - offset); - ret = (void *)((uintptr_t)ret + (chunksize - offset)); } - } + } else + ret = chunk_alloc_mmap_slow(size, false); return (ret); } +/* + * If the caller specifies (*zero == false), it is still possible to receive + * zeroed memory, in which case *zero is toggled to true. arena_chunk_alloc() + * takes advantage of this to avoid demanding zeroed chunks, but taking + * advantage of them if they are returned. + */ static void * -chunk_alloc(size_t size, bool zero) +chunk_alloc(size_t size, bool *zero) { void *ret; @@ -1993,18 +2051,15 @@ chunk_alloc(size_t size, bool zero) #endif { ret = chunk_alloc_mmap(size); - if (ret != NULL) + if (ret != NULL) { + *zero = true; goto RETURN; + } } #ifdef MALLOC_DSS if (opt_dss) { - ret = chunk_recycle_dss(size, zero); - if (ret != NULL) { - goto RETURN; - } - - ret = chunk_alloc_dss(size); + ret = chunk_alloc_dss(size, zero); if (ret != NULL) goto RETURN; } @@ -2015,11 +2070,13 @@ chunk_alloc(size_t size, bool zero) RETURN: #ifdef MALLOC_STATS if (ret != NULL) { + malloc_mutex_lock(&chunks_mtx); stats_chunks.nchunks += (size / chunksize); stats_chunks.curchunks += (size / chunksize); + if (stats_chunks.curchunks > stats_chunks.highchunks) + stats_chunks.highchunks = stats_chunks.curchunks; + malloc_mutex_unlock(&chunks_mtx); } - if (stats_chunks.curchunks > stats_chunks.highchunks) - stats_chunks.highchunks = stats_chunks.curchunks; #endif assert(CHUNK_ADDR2BASE(ret) == ret); @@ -2088,6 +2145,7 @@ chunk_dealloc_dss_record(void *chunk, size_t size) static bool chunk_dealloc_dss(void *chunk, size_t size) { + bool ret; malloc_mutex_lock(&dss_mtx); if ((uintptr_t)chunk >= (uintptr_t)dss_base @@ -2121,17 +2179,17 @@ chunk_dealloc_dss(void *chunk, size_t size) extent_tree_ad_remove(&dss_chunks_ad, node); base_node_dealloc(node); } - malloc_mutex_unlock(&dss_mtx); - } else { - malloc_mutex_unlock(&dss_mtx); + } else madvise(chunk, size, MADV_FREE); - } - return (false); + ret = false; + goto RETURN; } - malloc_mutex_unlock(&dss_mtx); - return (true); + ret = true; +RETURN: + malloc_mutex_unlock(&dss_mtx); + return (ret); } #endif @@ -2152,7 +2210,9 @@ chunk_dealloc(void *chunk, size_t size) assert((size & chunksize_mask) == 0); #ifdef MALLOC_STATS + malloc_mutex_lock(&chunks_mtx); stats_chunks.curchunks -= (size / chunksize); + malloc_mutex_unlock(&chunks_mtx); #endif #ifdef MALLOC_DSS @@ -2259,29 +2319,12 @@ choose_arena_hard(void) assert(__isthreaded); -#ifdef MALLOC_BALANCE - /* Seed the PRNG used for arena load balancing. */ - SPRN(balance, (uint32_t)(uintptr_t)(_pthread_self())); -#endif - if (narenas > 1) { -#ifdef MALLOC_BALANCE - unsigned ind; - - ind = PRN(balance, narenas_2pow); - if ((ret = arenas[ind]) == NULL) { - malloc_spin_lock(&arenas_lock); - if ((ret = arenas[ind]) == NULL) - ret = arenas_extend(ind); - malloc_spin_unlock(&arenas_lock); - } -#else malloc_spin_lock(&arenas_lock); if ((ret = arenas[next_arena]) == NULL) ret = arenas_extend(next_arena); next_arena = (next_arena + 1) % narenas; malloc_spin_unlock(&arenas_lock); -#endif } else ret = arenas[0]; @@ -2304,7 +2347,7 @@ arena_chunk_comp(arena_chunk_t *a, arena_chunk_t *b) } /* Wrap red-black tree macros in functions. */ -rb_wrap(__unused static, arena_chunk_tree_dirty_, arena_chunk_tree_t, +rb_gen(__unused static, arena_chunk_tree_dirty_, arena_chunk_tree_t, arena_chunk_t, link_dirty, arena_chunk_comp) static inline int @@ -2320,7 +2363,7 @@ arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) } /* Wrap red-black tree macros in functions. */ -rb_wrap(__unused static, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, +rb_gen(__unused static, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, link, arena_run_comp) static inline int @@ -2334,7 +2377,7 @@ arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) if (ret == 0) { uintptr_t a_mapelm, b_mapelm; - if ((a->bits & CHUNK_MAP_KEY) == 0) + if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY) a_mapelm = (uintptr_t)a; else { /* @@ -2352,9 +2395,108 @@ arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) } /* Wrap red-black tree macros in functions. */ -rb_wrap(__unused static, arena_avail_tree_, arena_avail_tree_t, +rb_gen(__unused static, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, link, arena_avail_comp) +static inline void +arena_run_rc_incr(arena_run_t *run, arena_bin_t *bin, const void *ptr) +{ + arena_chunk_t *chunk; + arena_t *arena; + size_t pagebeg, pageend, i; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + arena = chunk->arena; + pagebeg = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT; + pageend = ((uintptr_t)ptr + (uintptr_t)(bin->reg_size - 1) - + (uintptr_t)chunk) >> PAGE_SHIFT; + + for (i = pagebeg; i <= pageend; i++) { + size_t mapbits = chunk->map[i].bits; + + if (mapbits & CHUNK_MAP_DIRTY) { + assert((mapbits & CHUNK_MAP_RC_MASK) == 0); + chunk->ndirty--; + arena->ndirty--; + mapbits ^= CHUNK_MAP_DIRTY; + } + assert((mapbits & CHUNK_MAP_RC_MASK) != CHUNK_MAP_RC_MASK); + mapbits += CHUNK_MAP_RC_ONE; + chunk->map[i].bits = mapbits; + } +} + +static inline void +arena_run_rc_decr(arena_run_t *run, arena_bin_t *bin, const void *ptr) +{ + arena_chunk_t *chunk; + arena_t *arena; + size_t pagebeg, pageend, mapbits, i; + bool dirtier = false; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + arena = chunk->arena; + pagebeg = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT; + pageend = ((uintptr_t)ptr + (uintptr_t)(bin->reg_size - 1) - + (uintptr_t)chunk) >> PAGE_SHIFT; + + /* First page. */ + mapbits = chunk->map[pagebeg].bits; + mapbits -= CHUNK_MAP_RC_ONE; + if ((mapbits & CHUNK_MAP_RC_MASK) == 0) { + dirtier = true; + assert((mapbits & CHUNK_MAP_DIRTY) == 0); + mapbits |= CHUNK_MAP_DIRTY; + chunk->ndirty++; + arena->ndirty++; + } + chunk->map[pagebeg].bits = mapbits; + + if (pageend - pagebeg >= 1) { + /* + * Interior pages are completely consumed by the object being + * deallocated, which means that the pages can be + * unconditionally marked dirty. + */ + for (i = pagebeg + 1; i < pageend; i++) { + mapbits = chunk->map[i].bits; + mapbits -= CHUNK_MAP_RC_ONE; + assert((mapbits & CHUNK_MAP_RC_MASK) == 0); + dirtier = true; + assert((mapbits & CHUNK_MAP_DIRTY) == 0); + mapbits |= CHUNK_MAP_DIRTY; + chunk->ndirty++; + arena->ndirty++; + chunk->map[i].bits = mapbits; + } + + /* Last page. */ + mapbits = chunk->map[pageend].bits; + mapbits -= CHUNK_MAP_RC_ONE; + if ((mapbits & CHUNK_MAP_RC_MASK) == 0) { + dirtier = true; + assert((mapbits & CHUNK_MAP_DIRTY) == 0); + mapbits |= CHUNK_MAP_DIRTY; + chunk->ndirty++; + arena->ndirty++; + } + chunk->map[pageend].bits = mapbits; + } + + if (dirtier) { + if (chunk->dirtied == false) { + arena_chunk_tree_dirty_insert(&arena->chunks_dirty, + chunk); + chunk->dirtied = true; + } + + /* Enforce opt_lg_dirty_mult. */ + if (opt_lg_dirty_mult >= 0 && (arena->nactive >> + opt_lg_dirty_mult) < arena->ndirty) + arena_purge(arena); + } +} + static inline void * arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) { @@ -2375,7 +2517,7 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) /* Usable allocation found. */ bit = ffs((int)mask) - 1; - regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); + regind = ((i << (LG_SIZEOF_INT + 3)) + bit); assert(regind < bin->nregs); ret = (void *)(((uintptr_t)run) + bin->reg0_offset + (bin->reg_size * regind)); @@ -2384,6 +2526,8 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) mask ^= (1U << bit); run->regs_mask[i] = mask; + arena_run_rc_incr(run, bin, ret); + return (ret); } @@ -2393,7 +2537,7 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) /* Usable allocation found. */ bit = ffs((int)mask) - 1; - regind = ((i << (SIZEOF_INT_2POW + 3)) + bit); + regind = ((i << (LG_SIZEOF_INT + 3)) + bit); assert(regind < bin->nregs); ret = (void *)(((uintptr_t)run) + bin->reg0_offset + (bin->reg_size * regind)); @@ -2408,6 +2552,8 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) */ run->regs_minelm = i; /* Low payoff: + (mask == 0); */ + arena_run_rc_incr(run, bin, ret); + return (ret); } } @@ -2419,7 +2565,7 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin) static inline void arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size) { - unsigned diff, regind, elm, bit; + unsigned shift, diff, regind, elm, bit; assert(run->magic == ARENA_RUN_MAGIC); @@ -2428,31 +2574,16 @@ arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size) * actual division here can reduce allocator throughput by over 20%! */ diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset); - if ((size & (size - 1)) == 0) { - /* - * log2_table allows fast division of a power of two in the - * [1..128] range. - * - * (x / divisor) becomes (x >> log2_table[divisor - 1]). - */ - static const unsigned char log2_table[] = { - 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7 - }; - if (size <= 128) - regind = (diff >> log2_table[size - 1]); - else if (size <= 32768) - regind = diff >> (8 + log2_table[(size >> 8) - 1]); - else - regind = diff / size; - } else if (size < qspace_max) { + /* Rescale (factor powers of 2 out of the numerator and denominator). */ + shift = ffs(size) - 1; + diff >>= shift; + size >>= shift; + + if (size == 1) { + /* The divisor was a power of 2. */ + regind = diff; + } else { /* * To divide by a number D that is not a power of two we * multiply by (2^21 / D) and then right shift by 21 positions. @@ -2461,87 +2592,43 @@ arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size) * * becomes * - * (X * qsize_invs[(D >> QUANTUM_2POW) - 3]) - * >> SIZE_INV_SHIFT + * (X * size_invs[D - 3]) >> SIZE_INV_SHIFT * * We can omit the first three elements, because we never - * divide by 0, and QUANTUM and 2*QUANTUM are both powers of - * two, which are handled above. + * divide by 0, and 1 and 2 are both powers of two, which are + * handled above. */ #define SIZE_INV_SHIFT 21 -#define QSIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s << QUANTUM_2POW)) + 1) - static const unsigned qsize_invs[] = { - QSIZE_INV(3), - QSIZE_INV(4), QSIZE_INV(5), QSIZE_INV(6), QSIZE_INV(7) -#if (QUANTUM_2POW < 4) - , - QSIZE_INV(8), QSIZE_INV(9), QSIZE_INV(10), QSIZE_INV(11), - QSIZE_INV(12),QSIZE_INV(13), QSIZE_INV(14), QSIZE_INV(15) -#endif +#define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s)) + 1) + static const unsigned size_invs[] = { + SIZE_INV(3), + SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7), + SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11), + SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15), + SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19), + SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23), + SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27), + SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31) }; - assert(QUANTUM * (((sizeof(qsize_invs)) / sizeof(unsigned)) + 3) - >= (1U << QSPACE_MAX_2POW_DEFAULT)); - if (size <= (((sizeof(qsize_invs) / sizeof(unsigned)) + 2) << - QUANTUM_2POW)) { - regind = qsize_invs[(size >> QUANTUM_2POW) - 3] * diff; - regind >>= SIZE_INV_SHIFT; - } else - regind = diff / size; -#undef QSIZE_INV - } else if (size < cspace_max) { -#define CSIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s << CACHELINE_2POW)) + 1) - static const unsigned csize_invs[] = { - CSIZE_INV(3), - CSIZE_INV(4), CSIZE_INV(5), CSIZE_INV(6), CSIZE_INV(7) - }; - assert(CACHELINE * (((sizeof(csize_invs)) / sizeof(unsigned)) + - 3) >= (1U << CSPACE_MAX_2POW_DEFAULT)); - - if (size <= (((sizeof(csize_invs) / sizeof(unsigned)) + 2) << - CACHELINE_2POW)) { - regind = csize_invs[(size >> CACHELINE_2POW) - 3] * - diff; - regind >>= SIZE_INV_SHIFT; - } else - regind = diff / size; -#undef CSIZE_INV - } else { -#define SSIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s << SUBPAGE_2POW)) + 1) - static const unsigned ssize_invs[] = { - SSIZE_INV(3), - SSIZE_INV(4), SSIZE_INV(5), SSIZE_INV(6), SSIZE_INV(7), - SSIZE_INV(8), SSIZE_INV(9), SSIZE_INV(10), SSIZE_INV(11), - SSIZE_INV(12), SSIZE_INV(13), SSIZE_INV(14), SSIZE_INV(15) -#if (PAGE_SHIFT == 13) - , - SSIZE_INV(16), SSIZE_INV(17), SSIZE_INV(18), SSIZE_INV(19), - SSIZE_INV(20), SSIZE_INV(21), SSIZE_INV(22), SSIZE_INV(23), - SSIZE_INV(24), SSIZE_INV(25), SSIZE_INV(26), SSIZE_INV(27), - SSIZE_INV(28), SSIZE_INV(29), SSIZE_INV(29), SSIZE_INV(30) -#endif - }; - assert(SUBPAGE * (((sizeof(ssize_invs)) / sizeof(unsigned)) + 3) - >= PAGE_SIZE); - - if (size < (((sizeof(ssize_invs) / sizeof(unsigned)) + 2) << - SUBPAGE_2POW)) { - regind = ssize_invs[(size >> SUBPAGE_2POW) - 3] * diff; - regind >>= SIZE_INV_SHIFT; - } else + if (size <= ((sizeof(size_invs) / sizeof(unsigned)) + 2)) + regind = (diff * size_invs[size - 3]) >> SIZE_INV_SHIFT; + else regind = diff / size; -#undef SSIZE_INV - } +#undef SIZE_INV #undef SIZE_INV_SHIFT + } assert(diff == regind * size); assert(regind < bin->nregs); - elm = regind >> (SIZEOF_INT_2POW + 3); + elm = regind >> (LG_SIZEOF_INT + 3); if (elm < run->regs_minelm) run->regs_minelm = elm; - bit = regind - (elm << (SIZEOF_INT_2POW + 3)); + bit = regind - (elm << (LG_SIZEOF_INT + 3)); assert((run->regs_mask[elm] & (1U << bit)) == 0); run->regs_mask[elm] |= (1U << bit); + + arena_run_rc_decr(run, bin, ptr); } static void @@ -2563,15 +2650,16 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, rem_pages = total_pages - need_pages; arena_avail_tree_remove(&arena->runs_avail, &chunk->map[run_ind]); + arena->nactive += need_pages; /* Keep track of trailing unused pages for later use. */ if (rem_pages > 0) { chunk->map[run_ind+need_pages].bits = (rem_pages << PAGE_SHIFT) | (chunk->map[run_ind+need_pages].bits & - PAGE_MASK); + CHUNK_MAP_FLAGS_MASK); chunk->map[run_ind+total_pages-1].bits = (rem_pages << PAGE_SHIFT) | (chunk->map[run_ind+total_pages-1].bits & - PAGE_MASK); + CHUNK_MAP_FLAGS_MASK); arena_avail_tree_insert(&arena->runs_avail, &chunk->map[run_ind+need_pages]); } @@ -2599,22 +2687,26 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, chunk->map[run_ind + i].bits = CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; } else { - chunk->map[run_ind + i].bits = (size_t)run + chunk->map[run_ind + i].bits = (i << CHUNK_MAP_PG_SHIFT) | CHUNK_MAP_ALLOCATED; } } - /* - * Set the run size only in the first element for large runs. This is - * primarily a debugging aid, since the lack of size info for trailing - * pages only matters if the application tries to operate on an - * interior pointer. - */ - if (large) + if (large) { + /* + * Set the run size only in the first element for large runs. + * This is primarily a debugging aid, since the lack of size + * info for trailing pages only matters if the application + * tries to operate on an interior pointer. + */ chunk->map[run_ind].bits |= size; - - if (chunk->ndirty == 0 && old_ndirty > 0) - arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); + } else { + /* + * Initialize the first page's refcount to 1, so that the run + * header is protected from dirty page purging. + */ + chunk->map[run_ind].bits += CHUNK_MAP_RC_ONE; + } } static arena_chunk_t * @@ -2627,7 +2719,11 @@ arena_chunk_alloc(arena_t *arena) chunk = arena->spare; arena->spare = NULL; } else { - chunk = (arena_chunk_t *)chunk_alloc(chunksize, true); + bool zero; + size_t zeroed; + + zero = false; + chunk = (arena_chunk_t *)chunk_alloc(chunksize, &zero); if (chunk == NULL) return (NULL); #ifdef MALLOC_STATS @@ -2635,6 +2731,7 @@ arena_chunk_alloc(arena_t *arena) #endif chunk->arena = arena; + chunk->dirtied = false; /* * Claim that no pages are in use, since the header is merely @@ -2644,15 +2741,16 @@ arena_chunk_alloc(arena_t *arena) /* * Initialize the map to contain one maximal free untouched run. + * Mark the pages as zeroed iff chunk_alloc() returned a zeroed + * chunk. */ + zeroed = zero ? CHUNK_MAP_ZEROED : 0; for (i = 0; i < arena_chunk_header_npages; i++) chunk->map[i].bits = 0; - chunk->map[i].bits = arena_maxclass | CHUNK_MAP_ZEROED; - for (i++; i < chunk_npages-1; i++) { - chunk->map[i].bits = CHUNK_MAP_ZEROED; - } - chunk->map[chunk_npages-1].bits = arena_maxclass | - CHUNK_MAP_ZEROED; + chunk->map[i].bits = arena_maxclass | zeroed; + for (i++; i < chunk_npages-1; i++) + chunk->map[i].bits = zeroed; + chunk->map[chunk_npages-1].bits = arena_maxclass | zeroed; } /* Insert the run into the runs_avail tree. */ @@ -2667,7 +2765,7 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) { if (arena->spare != NULL) { - if (arena->spare->ndirty > 0) { + if (arena->spare->dirtied) { arena_chunk_tree_dirty_remove( &chunk->arena->chunks_dirty, arena->spare); arena->ndirty -= arena->spare->ndirty; @@ -2727,6 +2825,18 @@ arena_run_alloc(arena_t *arena, size_t size, bool large, bool zero) return (run); } +#ifdef MALLOC_DEBUG +static arena_chunk_t * +chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) +{ + size_t *ndirty = (size_t *)arg; + + assert(chunk->dirtied); + *ndirty += chunk->ndirty; + return (NULL); +} +#endif + static void arena_purge(arena_t *arena) { @@ -2735,13 +2845,11 @@ arena_purge(arena_t *arena) #ifdef MALLOC_DEBUG size_t ndirty = 0; - rb_foreach_begin(arena_chunk_t, link_dirty, &arena->chunks_dirty, - chunk) { - ndirty += chunk->ndirty; - } rb_foreach_end(arena_chunk_t, link_dirty, &arena->chunks_dirty, chunk) + arena_chunk_tree_dirty_iter(&arena->chunks_dirty, NULL, + chunks_dirty_iter_cb, (void *)&ndirty); assert(ndirty == arena->ndirty); #endif - assert(arena->ndirty > opt_dirty_max); + assert((arena->nactive >> opt_lg_dirty_mult) < arena->ndirty); #ifdef MALLOC_STATS arena->stats.npurge++; @@ -2753,13 +2861,13 @@ arena_purge(arena_t *arena) * number of system calls, even if a chunk has only been partially * purged. */ - while (arena->ndirty > (opt_dirty_max >> 1)) { + + while ((arena->nactive >> (opt_lg_dirty_mult + 1)) < arena->ndirty) { chunk = arena_chunk_tree_dirty_last(&arena->chunks_dirty); assert(chunk != NULL); for (i = chunk_npages - 1; chunk->ndirty > 0; i--) { assert(i >= arena_chunk_header_npages); - if (chunk->map[i].bits & CHUNK_MAP_DIRTY) { chunk->map[i].bits ^= CHUNK_MAP_DIRTY; /* Find adjacent dirty run(s). */ @@ -2779,7 +2887,8 @@ arena_purge(arena_t *arena) arena->stats.nmadvise++; arena->stats.purged += npages; #endif - if (arena->ndirty <= (opt_dirty_max >> 1)) + if ((arena->nactive >> (opt_lg_dirty_mult + 1)) + >= arena->ndirty) break; } } @@ -2787,6 +2896,7 @@ arena_purge(arena_t *arena) if (chunk->ndirty == 0) { arena_chunk_tree_dirty_remove(&arena->chunks_dirty, chunk); + chunk->dirtied = false; } } } @@ -2807,23 +2917,26 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) else size = run->bin->run_size; run_pages = (size >> PAGE_SHIFT); + arena->nactive -= run_pages; /* Mark pages as unallocated in the chunk map. */ if (dirty) { size_t i; for (i = 0; i < run_pages; i++) { + /* + * When (dirty == true), *all* pages within the run + * need to have their dirty bits set, because only + * small runs can create a mixture of clean/dirty + * pages, but such runs are passed to this function + * with (dirty == false). + */ assert((chunk->map[run_ind + i].bits & CHUNK_MAP_DIRTY) == 0); + chunk->ndirty++; + arena->ndirty++; chunk->map[run_ind + i].bits = CHUNK_MAP_DIRTY; } - - if (chunk->ndirty == 0) { - arena_chunk_tree_dirty_insert(&arena->chunks_dirty, - chunk); - } - chunk->ndirty += run_pages; - arena->ndirty += run_pages; } else { size_t i; @@ -2833,9 +2946,9 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) } } chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & - PAGE_MASK); + CHUNK_MAP_FLAGS_MASK); chunk->map[run_ind+run_pages-1].bits = size | - (chunk->map[run_ind+run_pages-1].bits & PAGE_MASK); + (chunk->map[run_ind+run_pages-1].bits & CHUNK_MAP_FLAGS_MASK); /* Try to coalesce forward. */ if (run_ind + run_pages < chunk_npages && @@ -2856,9 +2969,10 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) assert((chunk->map[run_ind+run_pages-1].bits & ~PAGE_MASK) == nrun_size); chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & - PAGE_MASK); + CHUNK_MAP_FLAGS_MASK); chunk->map[run_ind+run_pages-1].bits = size | - (chunk->map[run_ind+run_pages-1].bits & PAGE_MASK); + (chunk->map[run_ind+run_pages-1].bits & + CHUNK_MAP_FLAGS_MASK); } /* Try to coalesce backward. */ @@ -2878,25 +2992,45 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty) size += prun_size; run_pages = size >> PAGE_SHIFT; - assert((chunk->map[run_ind].bits & ~PAGE_MASK) == - prun_size); + assert((chunk->map[run_ind].bits & ~PAGE_MASK) == prun_size); chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits & - PAGE_MASK); + CHUNK_MAP_FLAGS_MASK); chunk->map[run_ind+run_pages-1].bits = size | - (chunk->map[run_ind+run_pages-1].bits & PAGE_MASK); + (chunk->map[run_ind+run_pages-1].bits & + CHUNK_MAP_FLAGS_MASK); } /* Insert into runs_avail, now that coalescing is complete. */ arena_avail_tree_insert(&arena->runs_avail, &chunk->map[run_ind]); - /* Deallocate chunk if it is now completely unused. */ + /* + * Deallocate chunk if it is now completely unused. The bit + * manipulation checks whether the first run is unallocated and extends + * to the end of the chunk. + */ if ((chunk->map[arena_chunk_header_npages].bits & (~PAGE_MASK | CHUNK_MAP_ALLOCATED)) == arena_maxclass) arena_chunk_dealloc(arena, chunk); - /* Enforce opt_dirty_max. */ - if (arena->ndirty > opt_dirty_max) - arena_purge(arena); + /* + * It is okay to do dirty page processing even if the chunk was + * deallocated above, since in that case it is the spare. Waiting + * until after possible chunk deallocation to do dirty processing + * allows for an old spare to be fully deallocated, thus decreasing the + * chances of spuriously crossing the dirty page purging threshold. + */ + if (dirty) { + if (chunk->dirtied == false) { + arena_chunk_tree_dirty_insert(&arena->chunks_dirty, + chunk); + chunk->dirtied = true; + } + + /* Enforce opt_lg_dirty_mult. */ + if (opt_lg_dirty_mult >= 0 && (arena->nactive >> + opt_lg_dirty_mult) < arena->ndirty) + arena_purge(arena); + } } static void @@ -2912,8 +3046,10 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, * Update the chunk map so that arena_run_dalloc() can treat the * leading run as separately allocated. */ + assert((chunk->map[pageind].bits & CHUNK_MAP_DIRTY) == 0); chunk->map[pageind].bits = (oldsize - newsize) | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; + assert((chunk->map[pageind+head_npages].bits & CHUNK_MAP_DIRTY) == 0); chunk->map[pageind+head_npages].bits = newsize | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; @@ -2933,8 +3069,10 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, * Update the chunk map so that arena_run_dalloc() can treat the * trailing run as separately allocated. */ + assert((chunk->map[pageind].bits & CHUNK_MAP_DIRTY) == 0); chunk->map[pageind].bits = newsize | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; + assert((chunk->map[pageind+npages].bits & CHUNK_MAP_DIRTY) == 0); chunk->map[pageind+npages].bits = (oldsize - newsize) | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; @@ -2952,9 +3090,18 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) /* Look for a usable run. */ mapelm = arena_run_tree_first(&bin->runs); if (mapelm != NULL) { + arena_chunk_t *chunk; + size_t pageind; + /* run is guaranteed to have available space. */ arena_run_tree_remove(&bin->runs, mapelm); - run = (arena_run_t *)(mapelm->bits & ~PAGE_MASK); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); + pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / + sizeof(arena_chunk_map_t)); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + ((mapelm->bits & CHUNK_MAP_PG_MASK) >> CHUNK_MAP_PG_SHIFT)) + << PAGE_SHIFT)); #ifdef MALLOC_STATS bin->stats.reruns++; #endif @@ -2972,12 +3119,12 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) for (i = 0; i < bin->regs_mask_nelms - 1; i++) run->regs_mask[i] = UINT_MAX; - remainder = bin->nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1); + remainder = bin->nregs & ((1U << (LG_SIZEOF_INT + 3)) - 1); if (remainder == 0) run->regs_mask[i] = UINT_MAX; else { /* The last element has spare bits that need to be unset. */ - run->regs_mask[i] = (UINT_MAX >> ((1U << (SIZEOF_INT_2POW + 3)) + run->regs_mask[i] = (UINT_MAX >> ((1U << (LG_SIZEOF_INT + 3)) - remainder)); } @@ -3034,6 +3181,7 @@ arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) * *) bin->run_size <= arena_maxclass * *) bin->run_size <= RUN_MAX_SMALL * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). + * *) run header size < PAGE_SIZE * * bin->nregs, bin->regs_mask_nelms, and bin->reg0_offset are * also calculated here, since these settings are all interdependent. @@ -3064,8 +3212,8 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) + 1; /* Counter-act try_nregs-- in loop. */ do { try_nregs--; - try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + - ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? 1 : 0); + try_mask_nelms = (try_nregs >> (LG_SIZEOF_INT + 3)) + + ((try_nregs & ((1U << (LG_SIZEOF_INT + 3)) - 1)) ? 1 : 0); try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); } while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1)) > try_reg0_offset); @@ -3086,8 +3234,8 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) bin->reg_size) + 1; /* Counter-act try_nregs-- in loop. */ do { try_nregs--; - try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) + - ((try_nregs & ((1U << (SIZEOF_INT_2POW + 3)) - 1)) ? + try_mask_nelms = (try_nregs >> (LG_SIZEOF_INT + 3)) + + ((try_nregs & ((1U << (LG_SIZEOF_INT + 3)) - 1)) ? 1 : 0); try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); @@ -3095,11 +3243,13 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) (try_mask_nelms - 1)) > try_reg0_offset); } while (try_run_size <= arena_maxclass && try_run_size <= RUN_MAX_SMALL && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX - && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size); + && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size + && (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1))) + < PAGE_SIZE); assert(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1)) <= good_reg0_offset); - assert((good_mask_nelms << (SIZEOF_INT_2POW + 3)) >= good_nregs); + assert((good_mask_nelms << (LG_SIZEOF_INT + 3)) >= good_nregs); /* Copy final settings. */ bin->run_size = good_run_size; @@ -3110,141 +3260,137 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size) return (good_run_size); } -#ifdef MALLOC_BALANCE +#ifdef MALLOC_TCACHE static inline void -arena_lock_balance(arena_t *arena) +tcache_event(tcache_t *tcache) { - unsigned contention; - contention = malloc_spin_lock(&arena->lock); - if (narenas > 1) { - /* - * Calculate the exponentially averaged contention for this - * arena. Due to integer math always rounding down, this value - * decays somewhat faster than normal. - */ - arena->contention = (((uint64_t)arena->contention - * (uint64_t)((1U << BALANCE_ALPHA_INV_2POW)-1)) - + (uint64_t)contention) >> BALANCE_ALPHA_INV_2POW; - if (arena->contention >= opt_balance_threshold) - arena_lock_balance_hard(arena); - } -} + if (tcache_gc_incr == 0) + return; -static void -arena_lock_balance_hard(arena_t *arena) -{ - uint32_t ind; + tcache->ev_cnt++; + assert(tcache->ev_cnt <= tcache_gc_incr); + if (tcache->ev_cnt >= tcache_gc_incr) { + size_t binind = tcache->next_gc_bin; + tcache_bin_t *tbin = tcache->tbins[binind]; - arena->contention = 0; -#ifdef MALLOC_STATS - arena->stats.nbalance++; -#endif - ind = PRN(balance, narenas_2pow); - if (arenas[ind] != NULL) - arenas_map = arenas[ind]; - else { - malloc_spin_lock(&arenas_lock); - if (arenas[ind] != NULL) - arenas_map = arenas[ind]; - else - arenas_map = arenas_extend(ind); - malloc_spin_unlock(&arenas_lock); + if (tbin != NULL) { + if (tbin->high_water == 0) { + /* + * This bin went completely unused for an + * entire GC cycle, so throw away the tbin. + */ + assert(tbin->ncached == 0); + tcache_bin_destroy(tcache, tbin, binind); + tcache->tbins[binind] = NULL; + } else { + if (tbin->low_water > 0) { + /* + * Flush (ceiling) half of the objects + * below the low water mark. + */ + tcache_bin_flush(tbin, binind, + tbin->ncached - (tbin->low_water >> + 1) - (tbin->low_water & 1)); + } + tbin->low_water = tbin->ncached; + tbin->high_water = tbin->ncached; + } + } + + tcache->next_gc_bin++; + if (tcache->next_gc_bin == nbins) + tcache->next_gc_bin = 0; + tcache->ev_cnt = 0; } } -#endif -#ifdef MALLOC_MAG static inline void * -mag_alloc(mag_t *mag) +tcache_bin_alloc(tcache_bin_t *tbin) { - if (mag->nrounds == 0) + if (tbin->ncached == 0) return (NULL); - mag->nrounds--; - - return (mag->rounds[mag->nrounds]); + tbin->ncached--; + if (tbin->ncached < tbin->low_water) + tbin->low_water = tbin->ncached; + return (tbin->slots[tbin->ncached]); } static void -mag_load(mag_t *mag) +tcache_bin_fill(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) { arena_t *arena; arena_bin_t *bin; arena_run_t *run; - void *round; - size_t i; + void *ptr; + unsigned i; - arena = choose_arena(); - bin = &arena->bins[mag->binind]; -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else + assert(tbin->ncached == 0); + + arena = tcache->arena; + bin = &arena->bins[binind]; malloc_spin_lock(&arena->lock); -#endif - for (i = mag->nrounds; i < max_rounds; i++) { + for (i = 0; i < (tcache_nslots >> 1); i++) { if ((run = bin->runcur) != NULL && run->nfree > 0) - round = arena_bin_malloc_easy(arena, bin, run); + ptr = arena_bin_malloc_easy(arena, bin, run); else - round = arena_bin_malloc_hard(arena, bin); - if (round == NULL) + ptr = arena_bin_malloc_hard(arena, bin); + if (ptr == NULL) break; - mag->rounds[i] = round; + /* + * Fill tbin such that the objects lowest in memory are used + * first. + */ + tbin->slots[(tcache_nslots >> 1) - 1 - i] = ptr; } #ifdef MALLOC_STATS - bin->stats.nmags++; - arena->stats.nmalloc_small += (i - mag->nrounds); - arena->stats.allocated_small += (i - mag->nrounds) * bin->reg_size; + bin->stats.nfills++; + bin->stats.nrequests += tbin->tstats.nrequests; + if (bin->reg_size <= small_maxclass) { + arena->stats.nmalloc_small += (i - tbin->ncached); + arena->stats.allocated_small += (i - tbin->ncached) * + bin->reg_size; + arena->stats.nmalloc_small += tbin->tstats.nrequests; + } else { + arena->stats.nmalloc_medium += (i - tbin->ncached); + arena->stats.allocated_medium += (i - tbin->ncached) * + bin->reg_size; + arena->stats.nmalloc_medium += tbin->tstats.nrequests; + } + tbin->tstats.nrequests = 0; #endif malloc_spin_unlock(&arena->lock); - mag->nrounds = i; + tbin->ncached = i; + if (tbin->ncached > tbin->high_water) + tbin->high_water = tbin->ncached; } static inline void * -mag_rack_alloc(mag_rack_t *rack, size_t size, bool zero) +tcache_alloc(tcache_t *tcache, size_t size, bool zero) { void *ret; - bin_mags_t *bin_mags; - mag_t *mag; + tcache_bin_t *tbin; size_t binind; - binind = size2bin[size]; + if (size <= small_maxclass) + binind = small_size2bin[size]; + else { + binind = mbin0 + ((MEDIUM_CEILING(size) - medium_min) >> + lg_mspace); + } assert(binind < nbins); - bin_mags = &rack->bin_mags[binind]; - - mag = bin_mags->curmag; - if (mag == NULL) { - /* Create an initial magazine for this size class. */ - assert(bin_mags->sparemag == NULL); - mag = mag_create(choose_arena(), binind); - if (mag == NULL) + tbin = tcache->tbins[binind]; + if (tbin == NULL) { + tbin = tcache_bin_create(tcache->arena); + if (tbin == NULL) return (NULL); - bin_mags->curmag = mag; - mag_load(mag); + tcache->tbins[binind] = tbin; } - ret = mag_alloc(mag); + ret = tcache_bin_alloc(tbin); if (ret == NULL) { - if (bin_mags->sparemag != NULL) { - if (bin_mags->sparemag->nrounds > 0) { - /* Swap magazines. */ - bin_mags->curmag = bin_mags->sparemag; - bin_mags->sparemag = mag; - mag = bin_mags->curmag; - } else { - /* Reload the current magazine. */ - mag_load(mag); - } - } else { - /* Create a second magazine. */ - mag = mag_create(choose_arena(), binind); - if (mag == NULL) - return (NULL); - mag_load(mag); - bin_mags->sparemag = bin_mags->curmag; - bin_mags->curmag = mag; - } - ret = mag_alloc(mag); + ret = tcache_alloc_hard(tcache, tbin, binind); if (ret == NULL) return (NULL); } @@ -3257,6 +3403,21 @@ mag_rack_alloc(mag_rack_t *rack, size_t size, bool zero) } else memset(ret, 0, size); +#ifdef MALLOC_STATS + tbin->tstats.nrequests++; +#endif + tcache_event(tcache); + return (ret); +} + +static void * +tcache_alloc_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) +{ + void *ret; + + tcache_bin_fill(tcache, tbin, binind); + ret = tcache_bin_alloc(tbin); + return (ret); } #endif @@ -3269,16 +3430,12 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) arena_run_t *run; size_t binind; - binind = size2bin[size]; - assert(binind < nbins); + binind = small_size2bin[size]; + assert(binind < mbin0); bin = &arena->bins[binind]; size = bin->reg_size; -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif if ((run = bin->runcur) != NULL && run->nfree > 0) ret = arena_bin_malloc_easy(arena, bin, run); else @@ -3290,8 +3447,14 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) } #ifdef MALLOC_STATS - bin->stats.nrequests++; - arena->stats.nmalloc_small++; +# ifdef MALLOC_TCACHE + if (__isthreaded == false) { +# endif + bin->stats.nrequests++; + arena->stats.nmalloc_small++; +# ifdef MALLOC_TCACHE + } +# endif arena->stats.allocated_small += size; #endif malloc_spin_unlock(&arena->lock); @@ -3308,17 +3471,62 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) } static void * +arena_malloc_medium(arena_t *arena, size_t size, bool zero) +{ + void *ret; + arena_bin_t *bin; + arena_run_t *run; + size_t binind; + + size = MEDIUM_CEILING(size); + binind = mbin0 + ((size - medium_min) >> lg_mspace); + assert(binind < nbins); + bin = &arena->bins[binind]; + assert(bin->reg_size == size); + + malloc_spin_lock(&arena->lock); + if ((run = bin->runcur) != NULL && run->nfree > 0) + ret = arena_bin_malloc_easy(arena, bin, run); + else + ret = arena_bin_malloc_hard(arena, bin); + + if (ret == NULL) { + malloc_spin_unlock(&arena->lock); + return (NULL); + } + +#ifdef MALLOC_STATS +# ifdef MALLOC_TCACHE + if (__isthreaded == false) { +# endif + bin->stats.nrequests++; + arena->stats.nmalloc_medium++; +# ifdef MALLOC_TCACHE + } +# endif + arena->stats.allocated_medium += size; +#endif + malloc_spin_unlock(&arena->lock); + + if (zero == false) { + if (opt_junk) + memset(ret, 0xa5, size); + else if (opt_zero) + memset(ret, 0, size); + } else + memset(ret, 0, size); + + return (ret); +} + +static void * arena_malloc_large(arena_t *arena, size_t size, bool zero) { void *ret; /* Large allocation. */ size = PAGE_CEILING(size); -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif ret = (void *)arena_run_alloc(arena, size, true, zero); if (ret == NULL) { malloc_spin_unlock(&arena->lock); @@ -3327,6 +3535,13 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero) #ifdef MALLOC_STATS arena->stats.nmalloc_large++; arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } #endif malloc_spin_unlock(&arena->lock); @@ -3341,30 +3556,35 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero) } static inline void * -arena_malloc(arena_t *arena, size_t size, bool zero) +arena_malloc(size_t size, bool zero) { - assert(arena != NULL); - assert(arena->magic == ARENA_MAGIC); assert(size != 0); assert(QUANTUM_CEILING(size) <= arena_maxclass); if (size <= bin_maxclass) { -#ifdef MALLOC_MAG - if (__isthreaded && opt_mag) { - mag_rack_t *rack = mag_rack; - if (rack == NULL) { - rack = mag_rack_create(arena); - if (rack == NULL) +#ifdef MALLOC_TCACHE + if (__isthreaded && tcache_nslots) { + tcache_t *tcache = tcache_tls; + if ((uintptr_t)tcache > (uintptr_t)1) + return (tcache_alloc(tcache, size, zero)); + else if (tcache == NULL) { + tcache = tcache_create(choose_arena()); + if (tcache == NULL) return (NULL); - mag_rack = rack; + return (tcache_alloc(tcache, size, zero)); } - return (mag_rack_alloc(rack, size, zero)); - } else + } #endif - return (arena_malloc_small(arena, size, zero)); + if (size <= small_maxclass) { + return (arena_malloc_small(choose_arena(), size, + zero)); + } else { + return (arena_malloc_medium(choose_arena(), + size, zero)); + } } else - return (arena_malloc_large(arena, size, zero)); + return (arena_malloc_large(choose_arena(), size, zero)); } static inline void * @@ -3374,7 +3594,7 @@ imalloc(size_t size) assert(size != 0); if (size <= arena_maxclass) - return (arena_malloc(choose_arena(), size, false)); + return (arena_malloc(size, false)); else return (huge_malloc(size, false)); } @@ -3384,7 +3604,7 @@ icalloc(size_t size) { if (size <= arena_maxclass) - return (arena_malloc(choose_arena(), size, true)); + return (arena_malloc(size, true)); else return (huge_malloc(size, true)); } @@ -3400,11 +3620,7 @@ arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) assert((size & PAGE_MASK) == 0); assert((alignment & PAGE_MASK) == 0); -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif ret = (void *)arena_run_alloc(arena, alloc_size, true, false); if (ret == NULL) { malloc_spin_unlock(&arena->lock); @@ -3440,6 +3656,13 @@ arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) #ifdef MALLOC_STATS arena->stats.nmalloc_large++; arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } #endif malloc_spin_unlock(&arena->lock); @@ -3486,7 +3709,7 @@ ipalloc(size_t alignment, size_t size) if (ceil_size <= PAGE_SIZE || (alignment <= PAGE_SIZE && ceil_size <= arena_maxclass)) - ret = arena_malloc(choose_arena(), ceil_size, false); + ret = arena_malloc(ceil_size, false); else { size_t run_size; @@ -3545,6 +3768,22 @@ ipalloc(size_t alignment, size_t size) return (ret); } +static bool +arena_is_large(const void *ptr) +{ + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(ptr != NULL); + assert(CHUNK_ADDR2BASE(ptr) != ptr); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + mapbits = chunk->map[pageind].bits; + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + return ((mapbits & CHUNK_MAP_LARGE) != 0); +} + /* Return the size of the allocation pointed to by ptr. */ static size_t arena_salloc(const void *ptr) @@ -3561,7 +3800,9 @@ arena_salloc(const void *ptr) mapbits = chunk->map[pageind].bits; assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); if ((mapbits & CHUNK_MAP_LARGE) == 0) { - arena_run_t *run = (arena_run_t *)(mapbits & ~PAGE_MASK); + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + + (uintptr_t)((pageind - ((mapbits & CHUNK_MAP_PG_MASK) >> + CHUNK_MAP_PG_SHIFT)) << PAGE_SHIFT)); assert(run->magic == ARENA_RUN_MAGIC); ret = run->bin->reg_size; } else { @@ -3607,14 +3848,18 @@ isalloc(const void *ptr) } static inline void -arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, +arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_chunk_map_t *mapelm) { + size_t pageind; arena_run_t *run; arena_bin_t *bin; size_t size; - run = (arena_run_t *)(mapelm->bits & ~PAGE_MASK); + pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + ((mapelm->bits & CHUNK_MAP_PG_MASK) >> CHUNK_MAP_PG_SHIFT)) << + PAGE_SHIFT)); assert(run->magic == ARENA_RUN_MAGIC); bin = run->bin; size = bin->reg_size; @@ -3625,30 +3870,9 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_run_reg_dalloc(run, bin, ptr, size); run->nfree++; - if (run->nfree == bin->nregs) { - /* Deallocate run. */ - if (run == bin->runcur) - bin->runcur = NULL; - else if (bin->nregs != 1) { - size_t run_pageind = (((uintptr_t)run - - (uintptr_t)chunk)) >> PAGE_SHIFT; - arena_chunk_map_t *run_mapelm = - &chunk->map[run_pageind]; - /* - * This block's conditional is necessary because if the - * run only contains one region, then it never gets - * inserted into the non-full runs tree. - */ - arena_run_tree_remove(&bin->runs, run_mapelm); - } -#ifdef MALLOC_DEBUG - run->magic = 0; -#endif - arena_run_dalloc(arena, run, true); -#ifdef MALLOC_STATS - bin->stats.curruns--; -#endif - } else if (run->nfree == 1 && run != bin->runcur) { + if (run->nfree == bin->nregs) + arena_dalloc_bin_run(arena, chunk, run, bin); + else if (run->nfree == 1 && run != bin->runcur) { /* * Make sure that bin->runcur always refers to the lowest * non-full run, if one exists. @@ -3682,67 +3906,297 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_run_tree_insert(&bin->runs, run_mapelm); } } + +#ifdef MALLOC_STATS + if (size <= small_maxclass) { + arena->stats.allocated_small -= size; + arena->stats.ndalloc_small++; + } else { + arena->stats.allocated_medium -= size; + arena->stats.ndalloc_medium++; + } +#endif +} + +static void +arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + arena_bin_t *bin) +{ + size_t run_ind; + + /* Deallocate run. */ + if (run == bin->runcur) + bin->runcur = NULL; + else if (bin->nregs != 1) { + size_t run_pageind = (((uintptr_t)run - + (uintptr_t)chunk)) >> PAGE_SHIFT; + arena_chunk_map_t *run_mapelm = + &chunk->map[run_pageind]; + /* + * This block's conditional is necessary because if the + * run only contains one region, then it never gets + * inserted into the non-full runs tree. + */ + arena_run_tree_remove(&bin->runs, run_mapelm); + } + /* + * Mark the first page as dirty. The dirty bit for every other page in + * the run is already properly set, which means we can call + * arena_run_dalloc(..., false), thus potentially avoiding the needless + * creation of many dirty pages. + */ + run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT); + assert((chunk->map[run_ind].bits & CHUNK_MAP_DIRTY) == 0); + chunk->map[run_ind].bits |= CHUNK_MAP_DIRTY; + chunk->ndirty++; + arena->ndirty++; + +#ifdef MALLOC_DEBUG + run->magic = 0; +#endif + arena_run_dalloc(arena, run, false); #ifdef MALLOC_STATS - arena->stats.allocated_small -= size; - arena->stats.ndalloc_small++; + bin->stats.curruns--; +#endif + + if (chunk->dirtied == false) { + arena_chunk_tree_dirty_insert(&arena->chunks_dirty, chunk); + chunk->dirtied = true; + } + /* Enforce opt_lg_dirty_mult. */ + if (opt_lg_dirty_mult >= 0 && (arena->nactive >> opt_lg_dirty_mult) < + arena->ndirty) + arena_purge(arena); +} + +#ifdef MALLOC_STATS +static void +arena_stats_print(arena_t *arena) +{ + + malloc_printf("dirty pages: %zu:%zu active:dirty, %"PRIu64" sweep%s," + " %"PRIu64" madvise%s, %"PRIu64" purged\n", + arena->nactive, arena->ndirty, + arena->stats.npurge, arena->stats.npurge == 1 ? "" : "s", + arena->stats.nmadvise, arena->stats.nmadvise == 1 ? "" : "s", + arena->stats.purged); + + malloc_printf(" allocated nmalloc ndalloc\n"); + malloc_printf("small: %12zu %12"PRIu64" %12"PRIu64"\n", + arena->stats.allocated_small, arena->stats.nmalloc_small, + arena->stats.ndalloc_small); + malloc_printf("medium: %12zu %12"PRIu64" %12"PRIu64"\n", + arena->stats.allocated_medium, arena->stats.nmalloc_medium, + arena->stats.ndalloc_medium); + malloc_printf("large: %12zu %12"PRIu64" %12"PRIu64"\n", + arena->stats.allocated_large, arena->stats.nmalloc_large, + arena->stats.ndalloc_large); + malloc_printf("total: %12zu %12"PRIu64" %12"PRIu64"\n", + arena->stats.allocated_small + arena->stats.allocated_medium + + arena->stats.allocated_large, arena->stats.nmalloc_small + + arena->stats.nmalloc_medium + arena->stats.nmalloc_large, + arena->stats.ndalloc_small + arena->stats.ndalloc_medium + + arena->stats.ndalloc_large); + malloc_printf("mapped: %12zu\n", arena->stats.mapped); + + if (arena->stats.nmalloc_small + arena->stats.nmalloc_medium > 0) { + unsigned i, gap_start; +#ifdef MALLOC_TCACHE + malloc_printf("bins: bin size regs pgs requests " + "nfills nflushes newruns reruns maxruns curruns\n"); +#else + malloc_printf("bins: bin size regs pgs requests " + "newruns reruns maxruns curruns\n"); +#endif + for (i = 0, gap_start = UINT_MAX; i < nbins; i++) { + if (arena->bins[i].stats.nruns == 0) { + if (gap_start == UINT_MAX) + gap_start = i; + } else { + if (gap_start != UINT_MAX) { + if (i > gap_start + 1) { + /* + * Gap of more than one size + * class. + */ + malloc_printf("[%u..%u]\n", + gap_start, i - 1); + } else { + /* Gap of one size class. */ + malloc_printf("[%u]\n", + gap_start); + } + gap_start = UINT_MAX; + } + malloc_printf( + "%13u %1s %5u %4u %3u %9"PRIu64" %9"PRIu64 +#ifdef MALLOC_TCACHE + " %9"PRIu64" %9"PRIu64 +#endif + " %9"PRIu64" %7zu %7zu\n", + i, + i < ntbins ? "T" : i < ntbins + nqbins ? + "Q" : i < ntbins + nqbins + ncbins ? "C" : + i < ntbins + nqbins + ncbins + nsbins ? "S" + : "M", + arena->bins[i].reg_size, + arena->bins[i].nregs, + arena->bins[i].run_size >> PAGE_SHIFT, + arena->bins[i].stats.nrequests, +#ifdef MALLOC_TCACHE + arena->bins[i].stats.nfills, + arena->bins[i].stats.nflushes, +#endif + arena->bins[i].stats.nruns, + arena->bins[i].stats.reruns, + arena->bins[i].stats.highruns, + arena->bins[i].stats.curruns); + } + } + if (gap_start != UINT_MAX) { + if (i > gap_start + 1) { + /* Gap of more than one size class. */ + malloc_printf("[%u..%u]\n", gap_start, i - 1); + } else { + /* Gap of one size class. */ + malloc_printf("[%u]\n", gap_start); + } + } + } + + if (arena->stats.nmalloc_large > 0) { + size_t i; + ssize_t gap_start; + size_t nlclasses = (chunksize - PAGE_SIZE) >> PAGE_SHIFT; + + malloc_printf( + "large: size pages nrequests maxruns curruns\n"); + + for (i = 0, gap_start = -1; i < nlclasses; i++) { + if (arena->stats.lstats[i].nrequests == 0) { + if (gap_start == -1) + gap_start = i; + } else { + if (gap_start != -1) { + malloc_printf("[%zu]\n", i - gap_start); + gap_start = -1; + } + malloc_printf( + "%13zu %5zu %9"PRIu64" %9zu %9zu\n", + (i+1) << PAGE_SHIFT, i+1, + arena->stats.lstats[i].nrequests, + arena->stats.lstats[i].highruns, + arena->stats.lstats[i].curruns); + } + } + if (gap_start != -1) + malloc_printf("[%zu]\n", i - gap_start); + } +} #endif + +static void +stats_print_atexit(void) +{ + +#if (defined(MALLOC_TCACHE) && defined(MALLOC_STATS)) + unsigned i; + + /* + * Merge stats from extant threads. This is racy, since individual + * threads do not lock when recording tcache stats events. As a + * consequence, the final stats may be slightly out of date by the time + * they are reported, if other threads continue to allocate. + */ + for (i = 0; i < narenas; i++) { + arena_t *arena = arenas[i]; + if (arena != NULL) { + tcache_t *tcache; + + malloc_spin_lock(&arena->lock); + ql_foreach(tcache, &arena->tcache_ql, link) { + tcache_stats_merge(tcache, arena); + } + malloc_spin_unlock(&arena->lock); + } + } +#endif + malloc_stats_print(); } -#ifdef MALLOC_MAG +#ifdef MALLOC_TCACHE static void -mag_unload(mag_t *mag) +tcache_bin_flush(tcache_bin_t *tbin, size_t binind, unsigned rem) { arena_chunk_t *chunk; arena_t *arena; - void *round; - size_t i, ndeferred, nrounds; + void *ptr; + unsigned i, ndeferred, ncached; - for (ndeferred = mag->nrounds; ndeferred > 0;) { - nrounds = ndeferred; - /* Lock the arena associated with the first round. */ - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mag->rounds[0]); + for (ndeferred = tbin->ncached - rem; ndeferred > 0;) { + ncached = ndeferred; + /* Lock the arena associated with the first object. */ + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(tbin->slots[0]); arena = chunk->arena; -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif - /* Deallocate every round that belongs to the locked arena. */ - for (i = ndeferred = 0; i < nrounds; i++) { - round = mag->rounds[i]; - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(round); + /* Deallocate every object that belongs to the locked arena. */ + for (i = ndeferred = 0; i < ncached; i++) { + ptr = tbin->slots[i]; + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); if (chunk->arena == arena) { - size_t pageind = (((uintptr_t)round - + size_t pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); arena_chunk_map_t *mapelm = &chunk->map[pageind]; - arena_dalloc_small(arena, chunk, round, mapelm); + arena_dalloc_bin(arena, chunk, ptr, mapelm); } else { /* - * This round was allocated via a different + * This object was allocated via a different * arena than the one that is currently locked. - * Stash the round, so that it can be handled + * Stash the object, so that it can be handled * in a future pass. */ - mag->rounds[ndeferred] = round; + tbin->slots[ndeferred] = ptr; ndeferred++; } } +#ifdef MALLOC_STATS + arena->bins[binind].stats.nflushes++; + { + arena_bin_t *bin = &arena->bins[binind]; + bin->stats.nrequests += tbin->tstats.nrequests; + if (bin->reg_size <= small_maxclass) { + arena->stats.nmalloc_small += + tbin->tstats.nrequests; + } else { + arena->stats.nmalloc_medium += + tbin->tstats.nrequests; + } + tbin->tstats.nrequests = 0; + } +#endif malloc_spin_unlock(&arena->lock); } - mag->nrounds = 0; + if (rem > 0) { + /* + * Shift the remaining valid pointers to the base of the slots + * array. + */ + memmove(&tbin->slots[0], &tbin->slots[tbin->ncached - rem], + rem * sizeof(void *)); + } + tbin->ncached = rem; } static inline void -mag_rack_dalloc(mag_rack_t *rack, void *ptr) +tcache_dalloc(tcache_t *tcache, void *ptr) { arena_t *arena; arena_chunk_t *chunk; arena_run_t *run; arena_bin_t *bin; - bin_mags_t *bin_mags; - mag_t *mag; + tcache_bin_t *tbin; size_t pageind, binind; arena_chunk_map_t *mapelm; @@ -3750,7 +4204,9 @@ mag_rack_dalloc(mag_rack_t *rack, void *ptr) arena = chunk->arena; pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT); mapelm = &chunk->map[pageind]; - run = (arena_run_t *)(mapelm->bits & ~PAGE_MASK); + run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - + ((mapelm->bits & CHUNK_MAP_PG_MASK) >> CHUNK_MAP_PG_SHIFT)) << + PAGE_SHIFT)); assert(run->magic == ARENA_RUN_MAGIC); bin = run->bin; binind = ((uintptr_t)bin - (uintptr_t)&arena->bins) / @@ -3760,53 +4216,34 @@ mag_rack_dalloc(mag_rack_t *rack, void *ptr) if (opt_junk) memset(ptr, 0x5a, arena->bins[binind].reg_size); - bin_mags = &rack->bin_mags[binind]; - mag = bin_mags->curmag; - if (mag == NULL) { - /* Create an initial magazine for this size class. */ - assert(bin_mags->sparemag == NULL); - mag = mag_create(choose_arena(), binind); - if (mag == NULL) { + tbin = tcache->tbins[binind]; + if (tbin == NULL) { + tbin = tcache_bin_create(choose_arena()); + if (tbin == NULL) { malloc_spin_lock(&arena->lock); - arena_dalloc_small(arena, chunk, ptr, mapelm); + arena_dalloc_bin(arena, chunk, ptr, mapelm); malloc_spin_unlock(&arena->lock); return; } - bin_mags->curmag = mag; + tcache->tbins[binind] = tbin; } - if (mag->nrounds == max_rounds) { - if (bin_mags->sparemag != NULL) { - if (bin_mags->sparemag->nrounds < max_rounds) { - /* Swap magazines. */ - bin_mags->curmag = bin_mags->sparemag; - bin_mags->sparemag = mag; - mag = bin_mags->curmag; - } else { - /* Unload the current magazine. */ - mag_unload(mag); - } - } else { - /* Create a second magazine. */ - mag = mag_create(choose_arena(), binind); - if (mag == NULL) { - mag = rack->bin_mags[binind].curmag; - mag_unload(mag); - } else { - bin_mags->sparemag = bin_mags->curmag; - bin_mags->curmag = mag; - } - } - assert(mag->nrounds < max_rounds); - } - mag->rounds[mag->nrounds] = ptr; - mag->nrounds++; + if (tbin->ncached == tcache_nslots) + tcache_bin_flush(tbin, binind, (tcache_nslots >> 1)); + assert(tbin->ncached < tcache_nslots); + tbin->slots[tbin->ncached] = ptr; + tbin->ncached++; + if (tbin->ncached > tbin->high_water) + tbin->high_water = tbin->ncached; + + tcache_event(tcache); } #endif static void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) { + /* Large allocation. */ malloc_spin_lock(&arena->lock); @@ -3823,12 +4260,11 @@ arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) #endif memset(ptr, 0x5a, size); #ifdef MALLOC_STATS + arena->stats.ndalloc_large++; arena->stats.allocated_large -= size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns--; #endif } -#ifdef MALLOC_STATS - arena->stats.ndalloc_large++; -#endif arena_run_dalloc(arena, (arena_run_t *)ptr, true); malloc_spin_unlock(&arena->lock); @@ -3851,32 +4287,51 @@ arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr) assert((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0); if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) { /* Small allocation. */ -#ifdef MALLOC_MAG - if (__isthreaded && opt_mag) { - mag_rack_t *rack = mag_rack; - if (rack == NULL) { - rack = mag_rack_create(arena); - if (rack == NULL) { - malloc_spin_lock(&arena->lock); - arena_dalloc_small(arena, chunk, ptr, - mapelm); - malloc_spin_unlock(&arena->lock); - } - mag_rack = rack; +#ifdef MALLOC_TCACHE + if (__isthreaded && tcache_nslots) { + tcache_t *tcache = tcache_tls; + if ((uintptr_t)tcache > (uintptr_t)1) + tcache_dalloc(tcache, ptr); + else { + arena_dalloc_hard(arena, chunk, ptr, mapelm, + tcache); } - mag_rack_dalloc(rack, ptr); } else { #endif malloc_spin_lock(&arena->lock); - arena_dalloc_small(arena, chunk, ptr, mapelm); + arena_dalloc_bin(arena, chunk, ptr, mapelm); malloc_spin_unlock(&arena->lock); -#ifdef MALLOC_MAG +#ifdef MALLOC_TCACHE } #endif } else arena_dalloc_large(arena, chunk, ptr); } +#ifdef MALLOC_TCACHE +static void +arena_dalloc_hard(arena_t *arena, arena_chunk_t *chunk, void *ptr, + arena_chunk_map_t *mapelm, tcache_t *tcache) +{ + + if (tcache == NULL) { + tcache = tcache_create(arena); + if (tcache == NULL) { + malloc_spin_lock(&arena->lock); + arena_dalloc_bin(arena, chunk, ptr, mapelm); + malloc_spin_unlock(&arena->lock); + } else + tcache_dalloc(tcache, ptr); + } else { + /* This thread is currently exiting, so directly deallocate. */ + assert(tcache == (void *)(uintptr_t)1); + malloc_spin_lock(&arena->lock); + arena_dalloc_bin(arena, chunk, ptr, mapelm); + malloc_spin_unlock(&arena->lock); + } +} +#endif + static inline void idalloc(void *ptr) { @@ -3902,15 +4357,23 @@ arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, * Shrink the run, and make trailing pages available for other * allocations. */ -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, true); #ifdef MALLOC_STATS - arena->stats.allocated_large -= oldsize - size; + arena->stats.ndalloc_large++; + arena->stats.allocated_large -= oldsize; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--; + + arena->stats.nmalloc_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } #endif malloc_spin_unlock(&arena->lock); } @@ -3926,11 +4389,7 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, /* Try to extend the run. */ assert(size > oldsize); -#ifdef MALLOC_BALANCE - arena_lock_balance(arena); -#else malloc_spin_lock(&arena->lock); -#endif if (pageind + npages < chunk_npages && (chunk->map[pageind+npages].bits & CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[pageind+npages].bits & ~PAGE_MASK) >= size - oldsize) { @@ -3949,7 +4408,19 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, CHUNK_MAP_ALLOCATED; #ifdef MALLOC_STATS - arena->stats.allocated_large += size - oldsize; + arena->stats.ndalloc_large++; + arena->stats.allocated_large -= oldsize; + arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--; + + arena->stats.nmalloc_large++; + arena->stats.allocated_large += size; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++; + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++; + if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns > + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) { + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns = + arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns; + } #endif malloc_spin_unlock(&arena->lock); return (false); @@ -4011,13 +4482,48 @@ arena_ralloc(void *ptr, size_t size, size_t oldsize) void *ret; size_t copysize; + /* + * Try to avoid moving the allocation. + * + * posix_memalign() can cause allocation of "large" objects that are + * smaller than bin_maxclass (in order to meet alignment requirements). + * Therefore, do not assume that (oldsize <= bin_maxclass) indicates + * ptr refers to a bin-allocated object. + */ + if (oldsize <= arena_maxclass) { + if (arena_is_large(ptr) == false ) { + if (size <= small_maxclass) { + if (oldsize <= small_maxclass && + small_size2bin[size] == + small_size2bin[oldsize]) + goto IN_PLACE; + } else if (size <= bin_maxclass) { + if (small_maxclass < oldsize && oldsize <= + bin_maxclass && MEDIUM_CEILING(size) == + MEDIUM_CEILING(oldsize)) + goto IN_PLACE; + } + } else { + assert(size <= arena_maxclass); + if (size > bin_maxclass) { + if (arena_ralloc_large(ptr, size, oldsize) == + false) + return (ptr); + } + } + } + /* Try to avoid moving the allocation. */ - if (size <= bin_maxclass) { - if (oldsize <= bin_maxclass && size2bin[size] == - size2bin[oldsize]) + if (size <= small_maxclass) { + if (oldsize <= small_maxclass && small_size2bin[size] == + small_size2bin[oldsize]) + goto IN_PLACE; + } else if (size <= bin_maxclass) { + if (small_maxclass < oldsize && oldsize <= bin_maxclass && + MEDIUM_CEILING(size) == MEDIUM_CEILING(oldsize)) goto IN_PLACE; } else { - if (oldsize > bin_maxclass && oldsize <= arena_maxclass) { + if (bin_maxclass < oldsize && oldsize <= arena_maxclass) { assert(size > bin_maxclass); if (arena_ralloc_large(ptr, size, oldsize) == false) return (ptr); @@ -4029,7 +4535,7 @@ arena_ralloc(void *ptr, size_t size, size_t oldsize) * need to move the object. In that case, fall back to allocating new * space and copying. */ - ret = arena_malloc(choose_arena(), size, false); + ret = arena_malloc(size, false); if (ret == NULL) return (NULL); @@ -4063,7 +4569,7 @@ iralloc(void *ptr, size_t size) } static bool -arena_new(arena_t *arena) +arena_new(arena_t *arena, unsigned ind) { unsigned i; arena_bin_t *bin; @@ -4074,20 +4580,27 @@ arena_new(arena_t *arena) #ifdef MALLOC_STATS memset(&arena->stats, 0, sizeof(arena_stats_t)); + arena->stats.lstats = (malloc_large_stats_t *)base_alloc( + sizeof(malloc_large_stats_t) * ((chunksize - PAGE_SIZE) >> + PAGE_SHIFT)); + if (arena->stats.lstats == NULL) + return (true); + memset(arena->stats.lstats, 0, sizeof(malloc_large_stats_t) * + ((chunksize - PAGE_SIZE) >> PAGE_SHIFT)); +# ifdef MALLOC_TCACHE + ql_new(&arena->tcache_ql); +# endif #endif /* Initialize chunks. */ arena_chunk_tree_dirty_new(&arena->chunks_dirty); arena->spare = NULL; + arena->nactive = 0; arena->ndirty = 0; arena_avail_tree_new(&arena->runs_avail); -#ifdef MALLOC_BALANCE - arena->contention = 0; -#endif - /* Initialize bins. */ prev_run_size = PAGE_SIZE; @@ -4099,7 +4612,7 @@ arena_new(arena_t *arena) bin->runcur = NULL; arena_run_tree_new(&bin->runs); - bin->reg_size = (1U << (TINY_MIN_2POW + i)); + bin->reg_size = (1U << (LG_TINY_MIN + i)); prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); @@ -4115,7 +4628,7 @@ arena_new(arena_t *arena) bin->runcur = NULL; arena_run_tree_new(&bin->runs); - bin->reg_size = (i - ntbins + 1) << QUANTUM_2POW; + bin->reg_size = (i - ntbins + 1) << LG_QUANTUM; prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); @@ -4131,7 +4644,7 @@ arena_new(arena_t *arena) arena_run_tree_new(&bin->runs); bin->reg_size = cspace_min + ((i - (ntbins + nqbins)) << - CACHELINE_2POW); + LG_CACHELINE); prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); @@ -4141,13 +4654,29 @@ arena_new(arena_t *arena) } /* Subpage-spaced bins. */ - for (; i < nbins; i++) { + for (; i < ntbins + nqbins + ncbins + nsbins; i++) { bin = &arena->bins[i]; bin->runcur = NULL; arena_run_tree_new(&bin->runs); bin->reg_size = sspace_min + ((i - (ntbins + nqbins + ncbins)) - << SUBPAGE_2POW); + << LG_SUBPAGE); + + prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); + +#ifdef MALLOC_STATS + memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); +#endif + } + + /* Medium bins. */ + for (; i < nbins; i++) { + bin = &arena->bins[i]; + bin->runcur = NULL; + arena_run_tree_new(&bin->runs); + + bin->reg_size = medium_min + ((i - (ntbins + nqbins + ncbins + + nsbins)) << lg_mspace); prev_run_size = arena_bin_run_size_calc(bin, prev_run_size); @@ -4172,7 +4701,7 @@ arenas_extend(unsigned ind) /* Allocate enough space for trailing bins. */ ret = (arena_t *)base_alloc(sizeof(arena_t) + (sizeof(arena_bin_t) * (nbins - 1))); - if (ret != NULL && arena_new(ret) == false) { + if (ret != NULL && arena_new(ret, ind) == false) { arenas[ind] = ret; return (ret); } @@ -4192,92 +4721,166 @@ arenas_extend(unsigned ind) return (arenas[0]); } -#ifdef MALLOC_MAG -static mag_t * -mag_create(arena_t *arena, size_t binind) +#ifdef MALLOC_TCACHE +static tcache_bin_t * +tcache_bin_create(arena_t *arena) { - mag_t *ret; - - if (sizeof(mag_t) + (sizeof(void *) * (max_rounds - 1)) <= - bin_maxclass) { - ret = arena_malloc_small(arena, sizeof(mag_t) + (sizeof(void *) - * (max_rounds - 1)), false); - } else { - ret = imalloc(sizeof(mag_t) + (sizeof(void *) * (max_rounds - - 1))); - } + tcache_bin_t *ret; + size_t tsize; + + tsize = sizeof(tcache_bin_t) + (sizeof(void *) * (tcache_nslots - 1)); + if (tsize <= small_maxclass) + ret = (tcache_bin_t *)arena_malloc_small(arena, tsize, false); + else if (tsize <= bin_maxclass) + ret = (tcache_bin_t *)arena_malloc_medium(arena, tsize, false); + else + ret = (tcache_bin_t *)imalloc(tsize); if (ret == NULL) return (NULL); - ret->binind = binind; - ret->nrounds = 0; +#ifdef MALLOC_STATS + memset(&ret->tstats, 0, sizeof(tcache_bin_stats_t)); +#endif + ret->low_water = 0; + ret->high_water = 0; + ret->ncached = 0; return (ret); } static void -mag_destroy(mag_t *mag) +tcache_bin_destroy(tcache_t *tcache, tcache_bin_t *tbin, unsigned binind) { arena_t *arena; arena_chunk_t *chunk; - size_t pageind; + size_t pageind, tsize; arena_chunk_map_t *mapelm; - chunk = CHUNK_ADDR2BASE(mag); + chunk = CHUNK_ADDR2BASE(tbin); arena = chunk->arena; - pageind = (((uintptr_t)mag - (uintptr_t)chunk) >> PAGE_SHIFT); + pageind = (((uintptr_t)tbin - (uintptr_t)chunk) >> PAGE_SHIFT); mapelm = &chunk->map[pageind]; - assert(mag->nrounds == 0); - if (sizeof(mag_t) + (sizeof(void *) * (max_rounds - 1)) <= - bin_maxclass) { +#ifdef MALLOC_STATS + if (tbin->tstats.nrequests != 0) { + arena_t *arena = tcache->arena; + arena_bin_t *bin = &arena->bins[binind]; + malloc_spin_lock(&arena->lock); + bin->stats.nrequests += tbin->tstats.nrequests; + if (bin->reg_size <= small_maxclass) + arena->stats.nmalloc_small += tbin->tstats.nrequests; + else + arena->stats.nmalloc_medium += tbin->tstats.nrequests; + malloc_spin_unlock(&arena->lock); + } +#endif + + assert(tbin->ncached == 0); + tsize = sizeof(tcache_bin_t) + (sizeof(void *) * (tcache_nslots - 1)); + if (tsize <= bin_maxclass) { malloc_spin_lock(&arena->lock); - arena_dalloc_small(arena, chunk, mag, mapelm); + arena_dalloc_bin(arena, chunk, tbin, mapelm); malloc_spin_unlock(&arena->lock); } else - idalloc(mag); + idalloc(tbin); +} + +#ifdef MALLOC_STATS +static void +tcache_stats_merge(tcache_t *tcache, arena_t *arena) +{ + unsigned i; + + /* Merge and reset tcache stats. */ + for (i = 0; i < mbin0; i++) { + arena_bin_t *bin = &arena->bins[i]; + tcache_bin_t *tbin = tcache->tbins[i]; + if (tbin != NULL) { + bin->stats.nrequests += tbin->tstats.nrequests; + arena->stats.nmalloc_small += tbin->tstats.nrequests; + tbin->tstats.nrequests = 0; + } + } + for (; i < nbins; i++) { + arena_bin_t *bin = &arena->bins[i]; + tcache_bin_t *tbin = tcache->tbins[i]; + if (tbin != NULL) { + bin->stats.nrequests += tbin->tstats.nrequests; + arena->stats.nmalloc_medium += tbin->tstats.nrequests; + tbin->tstats.nrequests = 0; + } + } } +#endif -static mag_rack_t * -mag_rack_create(arena_t *arena) +static tcache_t * +tcache_create(arena_t *arena) { + tcache_t *tcache; + + if (sizeof(tcache_t) + (sizeof(tcache_bin_t *) * (nbins - 1)) <= + small_maxclass) { + tcache = (tcache_t *)arena_malloc_small(arena, sizeof(tcache_t) + + (sizeof(tcache_bin_t *) * (nbins - 1)), true); + } else if (sizeof(tcache_t) + (sizeof(tcache_bin_t *) * (nbins - 1)) <= + bin_maxclass) { + tcache = (tcache_t *)arena_malloc_medium(arena, sizeof(tcache_t) + + (sizeof(tcache_bin_t *) * (nbins - 1)), true); + } else { + tcache = (tcache_t *)icalloc(sizeof(tcache_t) + + (sizeof(tcache_bin_t *) * (nbins - 1))); + } + + if (tcache == NULL) + return (NULL); - assert(sizeof(mag_rack_t) + (sizeof(bin_mags_t *) * (nbins - 1)) <= - bin_maxclass); - return (arena_malloc_small(arena, sizeof(mag_rack_t) + - (sizeof(bin_mags_t) * (nbins - 1)), true)); +#ifdef MALLOC_STATS + /* Link into list of extant tcaches. */ + malloc_spin_lock(&arena->lock); + ql_elm_new(tcache, link); + ql_tail_insert(&arena->tcache_ql, tcache, link); + malloc_spin_unlock(&arena->lock); +#endif + + tcache->arena = arena; + + tcache_tls = tcache; + + return (tcache); } static void -mag_rack_destroy(mag_rack_t *rack) +tcache_destroy(tcache_t *tcache) { - arena_t *arena; - arena_chunk_t *chunk; - bin_mags_t *bin_mags; - size_t i, pageind; - arena_chunk_map_t *mapelm; + unsigned i; + +#ifdef MALLOC_STATS + /* Unlink from list of extant tcaches. */ + malloc_spin_lock(&tcache->arena->lock); + ql_remove(&tcache->arena->tcache_ql, tcache, link); + tcache_stats_merge(tcache, tcache->arena); + malloc_spin_unlock(&tcache->arena->lock); +#endif for (i = 0; i < nbins; i++) { - bin_mags = &rack->bin_mags[i]; - if (bin_mags->curmag != NULL) { - assert(bin_mags->curmag->binind == i); - mag_unload(bin_mags->curmag); - mag_destroy(bin_mags->curmag); - } - if (bin_mags->sparemag != NULL) { - assert(bin_mags->sparemag->binind == i); - mag_unload(bin_mags->sparemag); - mag_destroy(bin_mags->sparemag); + tcache_bin_t *tbin = tcache->tbins[i]; + if (tbin != NULL) { + tcache_bin_flush(tbin, i, 0); + tcache_bin_destroy(tcache, tbin, i); } } - chunk = CHUNK_ADDR2BASE(rack); - arena = chunk->arena; - pageind = (((uintptr_t)rack - (uintptr_t)chunk) >> PAGE_SHIFT); - mapelm = &chunk->map[pageind]; + if (arena_salloc(tcache) <= bin_maxclass) { + arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); + arena_t *arena = chunk->arena; + size_t pageind = (((uintptr_t)tcache - (uintptr_t)chunk) >> + PAGE_SHIFT); + arena_chunk_map_t *mapelm = &chunk->map[pageind]; - malloc_spin_lock(&arena->lock); - arena_dalloc_small(arena, chunk, rack, mapelm); - malloc_spin_unlock(&arena->lock); + malloc_spin_lock(&arena->lock); + arena_dalloc_bin(arena, chunk, tcache, mapelm); + malloc_spin_unlock(&arena->lock); + } else + idalloc(tcache); } #endif @@ -4309,7 +4912,7 @@ huge_malloc(size_t size, bool zero) if (node == NULL) return (NULL); - ret = chunk_alloc(csize, zero); + ret = chunk_alloc(csize, &zero); if (ret == NULL) { base_node_dealloc(node); return (NULL); @@ -4344,6 +4947,7 @@ huge_palloc(size_t alignment, size_t size) void *ret; size_t alloc_size, chunk_size, offset; extent_node_t *node; + bool zero; /* * This allocation requires alignment that is even larger than chunk @@ -4367,7 +4971,8 @@ huge_palloc(size_t alignment, size_t size) if (node == NULL) return (NULL); - ret = chunk_alloc(alloc_size, false); + zero = false; + ret = chunk_alloc(alloc_size, &zero); if (ret == NULL) { base_node_dealloc(node); return (NULL); @@ -4483,271 +5088,279 @@ huge_dalloc(void *ptr) } static void -malloc_print_stats(void) +malloc_stats_print(void) { + char s[UMAX2S_BUFSIZE]; - if (opt_print_stats) { - char s[UMAX2S_BUFSIZE]; - _malloc_message("___ Begin malloc statistics ___\n", "", "", - ""); - _malloc_message("Assertions ", + _malloc_message("___ Begin malloc statistics ___\n", "", "", ""); + _malloc_message("Assertions ", #ifdef NDEBUG - "disabled", + "disabled", #else - "enabled", + "enabled", #endif - "\n", ""); - _malloc_message("Boolean MALLOC_OPTIONS: ", - opt_abort ? "A" : "a", "", ""); + "\n", ""); + _malloc_message("Boolean MALLOC_OPTIONS: ", opt_abort ? "A" : "a", "", ""); #ifdef MALLOC_DSS - _malloc_message(opt_dss ? "D" : "d", "", "", ""); -#endif -#ifdef MALLOC_MAG - _malloc_message(opt_mag ? "G" : "g", "", "", ""); + _malloc_message(opt_dss ? "D" : "d", "", "", ""); #endif - _malloc_message(opt_junk ? "J" : "j", "", "", ""); + _malloc_message(opt_junk ? "J" : "j", "", "", ""); #ifdef MALLOC_DSS - _malloc_message(opt_mmap ? "M" : "m", "", "", ""); -#endif - _malloc_message(opt_utrace ? "PU" : "Pu", - opt_sysv ? "V" : "v", - opt_xmalloc ? "X" : "x", - opt_zero ? "Z\n" : "z\n"); - - _malloc_message("CPUs: ", umax2s(ncpus, s), "\n", ""); - _malloc_message("Max arenas: ", umax2s(narenas, s), "\n", ""); -#ifdef MALLOC_BALANCE - _malloc_message("Arena balance threshold: ", - umax2s(opt_balance_threshold, s), "\n", ""); -#endif - _malloc_message("Pointer size: ", umax2s(sizeof(void *), s), - "\n", ""); - _malloc_message("Quantum size: ", umax2s(QUANTUM, s), "\n", ""); - _malloc_message("Cacheline size (assumed): ", umax2s(CACHELINE, - s), "\n", ""); + _malloc_message(opt_mmap ? "M" : "m", "", "", ""); +#endif + _malloc_message("P", "", "", ""); + _malloc_message(opt_utrace ? "U" : "u", "", "", ""); + _malloc_message(opt_sysv ? "V" : "v", "", "", ""); + _malloc_message(opt_xmalloc ? "X" : "x", "", "", ""); + _malloc_message(opt_zero ? "Z" : "z", "", "", ""); + _malloc_message("\n", "", "", ""); + + _malloc_message("CPUs: ", umax2s(ncpus, 10, s), "\n", ""); + _malloc_message("Max arenas: ", umax2s(narenas, 10, s), "\n", ""); + _malloc_message("Pointer size: ", umax2s(sizeof(void *), 10, s), "\n", ""); + _malloc_message("Quantum size: ", umax2s(QUANTUM, 10, s), "\n", ""); + _malloc_message("Cacheline size (assumed): ", + umax2s(CACHELINE, 10, s), "\n", ""); + _malloc_message("Subpage spacing: ", umax2s(SUBPAGE, 10, s), "\n", ""); + _malloc_message("Medium spacing: ", umax2s((1U << lg_mspace), 10, s), "\n", + ""); #ifdef MALLOC_TINY - _malloc_message("Tiny 2^n-spaced sizes: [", umax2s((1U << - TINY_MIN_2POW), s), "..", ""); - _malloc_message(umax2s((qspace_min >> 1), s), "]\n", "", ""); -#endif - _malloc_message("Quantum-spaced sizes: [", umax2s(qspace_min, - s), "..", ""); - _malloc_message(umax2s(qspace_max, s), "]\n", "", ""); - _malloc_message("Cacheline-spaced sizes: [", umax2s(cspace_min, - s), "..", ""); - _malloc_message(umax2s(cspace_max, s), "]\n", "", ""); - _malloc_message("Subpage-spaced sizes: [", umax2s(sspace_min, - s), "..", ""); - _malloc_message(umax2s(sspace_max, s), "]\n", "", ""); -#ifdef MALLOC_MAG - _malloc_message("Rounds per magazine: ", umax2s(max_rounds, s), - "\n", ""); -#endif - _malloc_message("Max dirty pages per arena: ", - umax2s(opt_dirty_max, s), "\n", ""); - - _malloc_message("Chunk size: ", umax2s(chunksize, s), "", ""); - _malloc_message(" (2^", umax2s(opt_chunk_2pow, s), ")\n", ""); + _malloc_message("Tiny 2^n-spaced sizes: [", umax2s((1U << LG_TINY_MIN), 10, + s), "..", ""); + _malloc_message(umax2s((qspace_min >> 1), 10, s), "]\n", "", ""); +#endif + _malloc_message("Quantum-spaced sizes: [", umax2s(qspace_min, 10, s), "..", + ""); + _malloc_message(umax2s(qspace_max, 10, s), "]\n", "", ""); + _malloc_message("Cacheline-spaced sizes: [", + umax2s(cspace_min, 10, s), "..", ""); + _malloc_message(umax2s(cspace_max, 10, s), "]\n", "", ""); + _malloc_message("Subpage-spaced sizes: [", umax2s(sspace_min, 10, s), "..", + ""); + _malloc_message(umax2s(sspace_max, 10, s), "]\n", "", ""); + _malloc_message("Medium sizes: [", umax2s(medium_min, 10, s), "..", ""); + _malloc_message(umax2s(medium_max, 10, s), "]\n", "", ""); + if (opt_lg_dirty_mult >= 0) { + _malloc_message("Min active:dirty page ratio per arena: ", + umax2s((1U << opt_lg_dirty_mult), 10, s), ":1\n", ""); + } else { + _malloc_message("Min active:dirty page ratio per arena: N/A\n", "", + "", ""); + } +#ifdef MALLOC_TCACHE + _malloc_message("Thread cache slots per size class: ", + tcache_nslots ? umax2s(tcache_nslots, 10, s) : "N/A", "\n", ""); + _malloc_message("Thread cache GC sweep interval: ", + (tcache_nslots && tcache_gc_incr > 0) ? + umax2s((1U << opt_lg_tcache_gc_sweep), 10, s) : "N/A", "", ""); + _malloc_message(" (increment interval: ", + (tcache_nslots && tcache_gc_incr > 0) ? umax2s(tcache_gc_incr, 10, s) + : "N/A", ")\n", ""); +#endif + _malloc_message("Chunk size: ", umax2s(chunksize, 10, s), "", ""); + _malloc_message(" (2^", umax2s(opt_lg_chunk, 10, s), ")\n", ""); #ifdef MALLOC_STATS - { - size_t allocated, mapped; -#ifdef MALLOC_BALANCE - uint64_t nbalance = 0; -#endif - unsigned i; - arena_t *arena; - - /* Calculate and print allocated/mapped stats. */ - - /* arenas. */ - for (i = 0, allocated = 0; i < narenas; i++) { - if (arenas[i] != NULL) { - malloc_spin_lock(&arenas[i]->lock); - allocated += - arenas[i]->stats.allocated_small; - allocated += - arenas[i]->stats.allocated_large; -#ifdef MALLOC_BALANCE - nbalance += arenas[i]->stats.nbalance; -#endif - malloc_spin_unlock(&arenas[i]->lock); - } + { + size_t allocated, mapped; + unsigned i; + arena_t *arena; + + /* Calculate and print allocated/mapped stats. */ + + /* arenas. */ + for (i = 0, allocated = 0; i < narenas; i++) { + if (arenas[i] != NULL) { + malloc_spin_lock(&arenas[i]->lock); + allocated += arenas[i]->stats.allocated_small; + allocated += arenas[i]->stats.allocated_large; + malloc_spin_unlock(&arenas[i]->lock); } + } - /* huge/base. */ - malloc_mutex_lock(&huge_mtx); - allocated += huge_allocated; - mapped = stats_chunks.curchunks * chunksize; - malloc_mutex_unlock(&huge_mtx); + /* huge/base. */ + malloc_mutex_lock(&huge_mtx); + allocated += huge_allocated; + mapped = stats_chunks.curchunks * chunksize; + malloc_mutex_unlock(&huge_mtx); - malloc_mutex_lock(&base_mtx); - mapped += base_mapped; - malloc_mutex_unlock(&base_mtx); + malloc_mutex_lock(&base_mtx); + mapped += base_mapped; + malloc_mutex_unlock(&base_mtx); - malloc_printf("Allocated: %zu, mapped: %zu\n", - allocated, mapped); + malloc_printf("Allocated: %zu, mapped: %zu\n", allocated, + mapped); -#ifdef MALLOC_BALANCE - malloc_printf("Arena balance reassignments: %llu\n", - nbalance); -#endif + /* Print chunk stats. */ + { + chunk_stats_t chunks_stats; - /* Print chunk stats. */ - { - chunk_stats_t chunks_stats; + malloc_mutex_lock(&huge_mtx); + chunks_stats = stats_chunks; + malloc_mutex_unlock(&huge_mtx); - malloc_mutex_lock(&huge_mtx); - chunks_stats = stats_chunks; - malloc_mutex_unlock(&huge_mtx); + malloc_printf("chunks: nchunks " + "highchunks curchunks\n"); + malloc_printf(" %13"PRIu64"%13zu%13zu\n", + chunks_stats.nchunks, chunks_stats.highchunks, + chunks_stats.curchunks); + } - malloc_printf("chunks: nchunks " - "highchunks curchunks\n"); - malloc_printf(" %13llu%13lu%13lu\n", - chunks_stats.nchunks, - chunks_stats.highchunks, - chunks_stats.curchunks); - } + /* Print chunk stats. */ + malloc_printf( + "huge: nmalloc ndalloc allocated\n"); + malloc_printf(" %12"PRIu64" %12"PRIu64" %12zu\n", huge_nmalloc, + huge_ndalloc, huge_allocated); - /* Print chunk stats. */ - malloc_printf( - "huge: nmalloc ndalloc allocated\n"); - malloc_printf(" %12llu %12llu %12zu\n", - huge_nmalloc, huge_ndalloc, huge_allocated); - - /* Print stats for each arena. */ - for (i = 0; i < narenas; i++) { - arena = arenas[i]; - if (arena != NULL) { - malloc_printf( - "\narenas[%u]:\n", i); - malloc_spin_lock(&arena->lock); - stats_print(arena); - malloc_spin_unlock(&arena->lock); - } + /* Print stats for each arena. */ + for (i = 0; i < narenas; i++) { + arena = arenas[i]; + if (arena != NULL) { + malloc_printf("\narenas[%u]:\n", i); + malloc_spin_lock(&arena->lock); + arena_stats_print(arena); + malloc_spin_unlock(&arena->lock); } } -#endif /* #ifdef MALLOC_STATS */ - _malloc_message("--- End malloc statistics ---\n", "", "", ""); } +#endif /* #ifdef MALLOC_STATS */ + _malloc_message("--- End malloc statistics ---\n", "", "", ""); } #ifdef MALLOC_DEBUG static void -size2bin_validate(void) +small_size2bin_validate(void) { size_t i, size, binind; - assert(size2bin[0] == 0xffU); + assert(small_size2bin[0] == 0xffU); i = 1; # ifdef MALLOC_TINY /* Tiny. */ - for (; i < (1U << TINY_MIN_2POW); i++) { - size = pow2_ceil(1U << TINY_MIN_2POW); - binind = ffs((int)(size >> (TINY_MIN_2POW + 1))); - assert(size2bin[i] == binind); + for (; i < (1U << LG_TINY_MIN); i++) { + size = pow2_ceil(1U << LG_TINY_MIN); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + assert(small_size2bin[i] == binind); } for (; i < qspace_min; i++) { size = pow2_ceil(i); - binind = ffs((int)(size >> (TINY_MIN_2POW + 1))); - assert(size2bin[i] == binind); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + assert(small_size2bin[i] == binind); } # endif /* Quantum-spaced. */ for (; i <= qspace_max; i++) { size = QUANTUM_CEILING(i); - binind = ntbins + (size >> QUANTUM_2POW) - 1; - assert(size2bin[i] == binind); + binind = ntbins + (size >> LG_QUANTUM) - 1; + assert(small_size2bin[i] == binind); } /* Cacheline-spaced. */ for (; i <= cspace_max; i++) { size = CACHELINE_CEILING(i); binind = ntbins + nqbins + ((size - cspace_min) >> - CACHELINE_2POW); - assert(size2bin[i] == binind); + LG_CACHELINE); + assert(small_size2bin[i] == binind); } /* Sub-page. */ for (; i <= sspace_max; i++) { size = SUBPAGE_CEILING(i); binind = ntbins + nqbins + ncbins + ((size - sspace_min) - >> SUBPAGE_2POW); - assert(size2bin[i] == binind); + >> LG_SUBPAGE); + assert(small_size2bin[i] == binind); } } #endif static bool -size2bin_init(void) +small_size2bin_init(void) { - if (opt_qspace_max_2pow != QSPACE_MAX_2POW_DEFAULT - || opt_cspace_max_2pow != CSPACE_MAX_2POW_DEFAULT) - return (size2bin_init_hard()); + if (opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT + || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT + || sizeof(const_small_size2bin) != small_maxclass + 1) + return (small_size2bin_init_hard()); - size2bin = const_size2bin; + small_size2bin = const_small_size2bin; #ifdef MALLOC_DEBUG - assert(sizeof(const_size2bin) == bin_maxclass + 1); - size2bin_validate(); + assert(sizeof(const_small_size2bin) == small_maxclass + 1); + small_size2bin_validate(); #endif return (false); } static bool -size2bin_init_hard(void) +small_size2bin_init_hard(void) { size_t i, size, binind; - uint8_t *custom_size2bin; + uint8_t *custom_small_size2bin; - assert(opt_qspace_max_2pow != QSPACE_MAX_2POW_DEFAULT - || opt_cspace_max_2pow != CSPACE_MAX_2POW_DEFAULT); + assert(opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT + || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT + || sizeof(const_small_size2bin) != small_maxclass + 1); - custom_size2bin = (uint8_t *)base_alloc(bin_maxclass + 1); - if (custom_size2bin == NULL) + custom_small_size2bin = (uint8_t *)base_alloc(small_maxclass + 1); + if (custom_small_size2bin == NULL) return (true); - custom_size2bin[0] = 0xffU; + custom_small_size2bin[0] = 0xffU; i = 1; #ifdef MALLOC_TINY /* Tiny. */ - for (; i < (1U << TINY_MIN_2POW); i++) { - size = pow2_ceil(1U << TINY_MIN_2POW); - binind = ffs((int)(size >> (TINY_MIN_2POW + 1))); - custom_size2bin[i] = binind; + for (; i < (1U << LG_TINY_MIN); i++) { + size = pow2_ceil(1U << LG_TINY_MIN); + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + custom_small_size2bin[i] = binind; } for (; i < qspace_min; i++) { size = pow2_ceil(i); - binind = ffs((int)(size >> (TINY_MIN_2POW + 1))); - custom_size2bin[i] = binind; + binind = ffs((int)(size >> (LG_TINY_MIN + 1))); + custom_small_size2bin[i] = binind; } #endif /* Quantum-spaced. */ for (; i <= qspace_max; i++) { size = QUANTUM_CEILING(i); - binind = ntbins + (size >> QUANTUM_2POW) - 1; - custom_size2bin[i] = binind; + binind = ntbins + (size >> LG_QUANTUM) - 1; + custom_small_size2bin[i] = binind; } /* Cacheline-spaced. */ for (; i <= cspace_max; i++) { size = CACHELINE_CEILING(i); binind = ntbins + nqbins + ((size - cspace_min) >> - CACHELINE_2POW); - custom_size2bin[i] = binind; + LG_CACHELINE); + custom_small_size2bin[i] = binind; } /* Sub-page. */ for (; i <= sspace_max; i++) { size = SUBPAGE_CEILING(i); binind = ntbins + nqbins + ncbins + ((size - sspace_min) >> - SUBPAGE_2POW); - custom_size2bin[i] = binind; + LG_SUBPAGE); + custom_small_size2bin[i] = binind; } - size2bin = custom_size2bin; + small_size2bin = custom_small_size2bin; #ifdef MALLOC_DEBUG - size2bin_validate(); + small_size2bin_validate(); #endif return (false); } +static unsigned +malloc_ncpus(void) +{ + unsigned ret; + size_t retlen = sizeof(ret); + int mib[] = {CTL_HW, HW_NCPU}; + + if (sysctl(mib, sizeof(mib) / sizeof(int), &ret, &retlen, + (void *)0, 0) == -1) { + /* Error. */ + ret = 1; + } + + return (ret); +} + /* * FreeBSD's pthreads implementation calls malloc(3), so the malloc * implementation has to take pains to avoid infinite recursion during @@ -4782,18 +5395,7 @@ malloc_init_hard(void) } /* Get number of CPUs. */ - { - int mib[2]; - size_t len; - - mib[0] = CTL_HW; - mib[1] = HW_NCPU; - len = sizeof(ncpus); - if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) == -1) { - /* Error. */ - ncpus = 1; - } - } + ncpus = malloc_ncpus(); /* * Increase the chunk size to the largest page size that is greater @@ -4806,8 +5408,8 @@ malloc_init_hard(void) nsizes = getpagesizes(pagesizes, MAXPAGESIZES); for (k = 0; k < nsizes; k++) if (pagesizes[k] <= (1LU << 22)) - while ((1LU << opt_chunk_2pow) < pagesizes[k]) - opt_chunk_2pow++; + while ((1LU << opt_lg_chunk) < pagesizes[k]) + opt_lg_chunk++; } for (i = 0; i < 3; i++) { @@ -4860,6 +5462,8 @@ malloc_init_hard(void) default: /* NOTREACHED */ assert(false); + buf[0] = '\0'; + opts = buf; } for (j = 0; opts[j] != '\0'; j++) { @@ -4891,31 +5495,17 @@ MALLOC_OUT: case 'A': opt_abort = true; break; - case 'b': -#ifdef MALLOC_BALANCE - opt_balance_threshold >>= 1; -#endif - break; - case 'B': -#ifdef MALLOC_BALANCE - if (opt_balance_threshold == 0) - opt_balance_threshold = 1; - else if ((opt_balance_threshold << 1) - > opt_balance_threshold) - opt_balance_threshold <<= 1; -#endif - break; case 'c': - if (opt_cspace_max_2pow - 1 > - opt_qspace_max_2pow && - opt_cspace_max_2pow > - CACHELINE_2POW) - opt_cspace_max_2pow--; + if (opt_lg_cspace_max - 1 > + opt_lg_qspace_max && + opt_lg_cspace_max > + LG_CACHELINE) + opt_lg_cspace_max--; break; case 'C': - if (opt_cspace_max_2pow < PAGE_SHIFT + if (opt_lg_cspace_max < PAGE_SHIFT - 1) - opt_cspace_max_2pow++; + opt_lg_cspace_max++; break; case 'd': #ifdef MALLOC_DSS @@ -4927,21 +5517,42 @@ MALLOC_OUT: opt_dss = true; #endif break; + case 'e': + if (opt_lg_medium_max > PAGE_SHIFT) + opt_lg_medium_max--; + break; + case 'E': + if (opt_lg_medium_max + 1 < + opt_lg_chunk) + opt_lg_medium_max++; + break; case 'f': - opt_dirty_max >>= 1; + if (opt_lg_dirty_mult + 1 < + (sizeof(size_t) << 3)) + opt_lg_dirty_mult++; break; case 'F': - if (opt_dirty_max == 0) - opt_dirty_max = 1; - else if ((opt_dirty_max << 1) != 0) - opt_dirty_max <<= 1; + if (opt_lg_dirty_mult >= 0) + opt_lg_dirty_mult--; break; -#ifdef MALLOC_MAG +#ifdef MALLOC_TCACHE case 'g': - opt_mag = false; + if (opt_lg_tcache_gc_sweep >= 0) + opt_lg_tcache_gc_sweep--; break; case 'G': - opt_mag = true; + if (opt_lg_tcache_gc_sweep + 1 < + (sizeof(size_t) << 3)) + opt_lg_tcache_gc_sweep++; + break; + case 'h': + if (opt_lg_tcache_nslots > 0) + opt_lg_tcache_nslots--; + break; + case 'H': + if (opt_lg_tcache_nslots + 1 < + (sizeof(size_t) << 3)) + opt_lg_tcache_nslots++; break; #endif case 'j': @@ -4953,16 +5564,20 @@ MALLOC_OUT: case 'k': /* * Chunks always require at least one - * header page, so chunks can never be - * smaller than two pages. + * header page, plus enough room to + * hold a run for the largest medium + * size class (one page more than the + * size). */ - if (opt_chunk_2pow > PAGE_SHIFT + 1) - opt_chunk_2pow--; + if ((1U << (opt_lg_chunk - 1)) >= + (2U << PAGE_SHIFT) + (1U << + opt_lg_medium_max)) + opt_lg_chunk--; break; case 'K': - if (opt_chunk_2pow + 1 < + if (opt_lg_chunk + 1 < (sizeof(size_t) << 3)) - opt_chunk_2pow++; + opt_lg_chunk++; break; case 'm': #ifdef MALLOC_DSS @@ -4981,36 +5596,20 @@ MALLOC_OUT: opt_narenas_lshift++; break; case 'p': - opt_print_stats = false; + opt_stats_print = false; break; case 'P': - opt_print_stats = true; + opt_stats_print = true; break; case 'q': - if (opt_qspace_max_2pow > QUANTUM_2POW) - opt_qspace_max_2pow--; + if (opt_lg_qspace_max > LG_QUANTUM) + opt_lg_qspace_max--; break; case 'Q': - if (opt_qspace_max_2pow + 1 < - opt_cspace_max_2pow) - opt_qspace_max_2pow++; + if (opt_lg_qspace_max + 1 < + opt_lg_cspace_max) + opt_lg_qspace_max++; break; -#ifdef MALLOC_MAG - case 'R': - if (opt_mag_size_2pow + 1 < (8U << - SIZEOF_PTR_2POW)) - opt_mag_size_2pow++; - break; - case 'r': - /* - * Make sure there's always at least - * one round per magazine. - */ - if ((1U << (opt_mag_size_2pow-1)) >= - sizeof(mag_t)) - opt_mag_size_2pow--; - break; -#endif case 'u': opt_utrace = false; break; @@ -5055,50 +5654,87 @@ MALLOC_OUT: if (opt_dss == false && opt_mmap == false) opt_mmap = true; #endif - - /* Take care to call atexit() only once. */ - if (opt_print_stats) { + if (opt_stats_print) { /* Print statistics at exit. */ - atexit(malloc_print_stats); + atexit(stats_print_atexit); } -#ifdef MALLOC_MAG - /* - * Calculate the actual number of rounds per magazine, taking into - * account header overhead. - */ - max_rounds = (1LLU << (opt_mag_size_2pow - SIZEOF_PTR_2POW)) - - (sizeof(mag_t) >> SIZEOF_PTR_2POW) + 1; -#endif - /* Set variables according to the value of opt_[qc]space_max_2pow. */ - qspace_max = (1U << opt_qspace_max_2pow); + /* Set variables according to the value of opt_lg_[qc]space_max. */ + qspace_max = (1U << opt_lg_qspace_max); cspace_min = CACHELINE_CEILING(qspace_max); if (cspace_min == qspace_max) cspace_min += CACHELINE; - cspace_max = (1U << opt_cspace_max_2pow); + cspace_max = (1U << opt_lg_cspace_max); sspace_min = SUBPAGE_CEILING(cspace_max); if (sspace_min == cspace_max) sspace_min += SUBPAGE; assert(sspace_min < PAGE_SIZE); sspace_max = PAGE_SIZE - SUBPAGE; + medium_max = (1U << opt_lg_medium_max); #ifdef MALLOC_TINY - assert(QUANTUM_2POW >= TINY_MIN_2POW); + assert(LG_QUANTUM >= LG_TINY_MIN); #endif - assert(ntbins <= QUANTUM_2POW); - nqbins = qspace_max >> QUANTUM_2POW; - ncbins = ((cspace_max - cspace_min) >> CACHELINE_2POW) + 1; - nsbins = ((sspace_max - sspace_min) >> SUBPAGE_2POW) + 1; - nbins = ntbins + nqbins + ncbins + nsbins; + assert(ntbins <= LG_QUANTUM); + nqbins = qspace_max >> LG_QUANTUM; + ncbins = ((cspace_max - cspace_min) >> LG_CACHELINE) + 1; + nsbins = ((sspace_max - sspace_min) >> LG_SUBPAGE) + 1; + + /* + * Compute medium size class spacing and the number of medium size + * classes. Limit spacing to no more than pagesize, but if possible + * use the smallest spacing that does not exceed NMBINS_MAX medium size + * classes. + */ + lg_mspace = LG_SUBPAGE; + nmbins = ((medium_max - medium_min) >> lg_mspace) + 1; + while (lg_mspace < PAGE_SHIFT && nmbins > NMBINS_MAX) { + lg_mspace = lg_mspace + 1; + nmbins = ((medium_max - medium_min) >> lg_mspace) + 1; + } + mspace_mask = (1U << lg_mspace) - 1U; + + mbin0 = ntbins + nqbins + ncbins + nsbins; + nbins = mbin0 + nmbins; + /* + * The small_size2bin lookup table uses uint8_t to encode each bin + * index, so we cannot support more than 256 small size classes. This + * limit is difficult to exceed (not even possible with 16B quantum and + * 4KiB pages), and such configurations are impractical, but + * nonetheless we need to protect against this case in order to avoid + * undefined behavior. + */ + if (mbin0 > 256) { + char line_buf[UMAX2S_BUFSIZE]; + _malloc_message(_getprogname(), + ": (malloc) Too many small size classes (", + umax2s(mbin0, 10, line_buf), " > max 256)\n"); + abort(); + } - if (size2bin_init()) { + if (small_size2bin_init()) { malloc_mutex_unlock(&init_lock); return (true); } - /* Set variables according to the value of opt_chunk_2pow. */ - chunksize = (1LU << opt_chunk_2pow); +#ifdef MALLOC_TCACHE + if (opt_lg_tcache_nslots > 0) { + tcache_nslots = (1U << opt_lg_tcache_nslots); + + /* Compute incremental GC event threshold. */ + if (opt_lg_tcache_gc_sweep >= 0) { + tcache_gc_incr = ((1U << opt_lg_tcache_gc_sweep) / + nbins) + (((1U << opt_lg_tcache_gc_sweep) % nbins == + 0) ? 0 : 1); + } else + tcache_gc_incr = 0; + } else + tcache_nslots = 0; +#endif + + /* Set variables according to the value of opt_lg_chunk. */ + chunksize = (1LU << opt_lg_chunk); chunksize_mask = chunksize - 1; chunk_npages = (chunksize >> PAGE_SHIFT); { @@ -5116,9 +5752,10 @@ MALLOC_OUT: arena_maxclass = chunksize - (arena_chunk_header_npages << PAGE_SHIFT); - UTRACE(0, 0, 0); + UTRACE((void *)(intptr_t)(-1), 0, 0); #ifdef MALLOC_STATS + malloc_mutex_init(&chunks_mtx); memset(&stats_chunks, 0, sizeof(chunk_stats_t)); #endif @@ -5160,10 +5797,27 @@ MALLOC_OUT: if (ncpus > 1) { /* - * For SMP systems, create twice as many arenas as there are - * CPUs by default. + * For SMP systems, create more than one arena per CPU by + * default. */ - opt_narenas_lshift++; +#ifdef MALLOC_TCACHE + if (tcache_nslots) { + /* + * Only large object allocation/deallocation is + * guaranteed to acquire an arena mutex, so we can get + * away with fewer arenas than without thread caching. + */ + opt_narenas_lshift += 1; + } else { +#endif + /* + * All allocations must acquire an arena mutex, so use + * plenty of arenas. + */ + opt_narenas_lshift += 2; +#ifdef MALLOC_TCACHE + } +#endif } /* Determine how many arenas to use. */ @@ -5184,12 +5838,6 @@ MALLOC_OUT: if (narenas == 0) narenas = 1; } -#ifdef MALLOC_BALANCE - assert(narenas != 0); - for (narenas_2pow = 0; - (narenas >> (narenas_2pow + 1)) != 0; - narenas_2pow++); -#endif #ifdef NO_TLS if (narenas > 1) { @@ -5206,7 +5854,7 @@ MALLOC_OUT: * spread allocations evenly among the arenas. */ assert((narenas & 1) == 0); /* narenas must be even. */ - nprimes = (sizeof(primes) >> SIZEOF_INT_2POW); + nprimes = (sizeof(primes) >> LG_SIZEOF_INT); parenas = primes[nprimes - 1]; /* In case not enough primes. */ for (i = 1; i < nprimes; i++) { if (primes[i] > narenas) { @@ -5219,9 +5867,7 @@ MALLOC_OUT: #endif #ifndef NO_TLS -# ifndef MALLOC_BALANCE next_arena = 0; -# endif #endif /* Allocate and initialize arenas. */ @@ -5253,14 +5899,6 @@ MALLOC_OUT: */ arenas_map = arenas[0]; #endif - /* - * Seed here for the initial thread, since choose_arena_hard() is only - * called for other threads. The seed value doesn't really matter. - */ -#ifdef MALLOC_BALANCE - SPRN(balance, 42); -#endif - malloc_spin_init(&arenas_lock); malloc_initialized = true; @@ -5283,13 +5921,19 @@ malloc(size_t size) if (malloc_init()) { ret = NULL; - goto RETURN; + goto OOM; } if (size == 0) { if (opt_sysv == false) size = 1; else { + if (opt_xmalloc) { + _malloc_message(_getprogname(), + ": (malloc) Error in malloc(): " + "invalid size 0\n", "", ""); + abort(); + } ret = NULL; goto RETURN; } @@ -5297,7 +5941,7 @@ malloc(size_t size) ret = imalloc(size); -RETURN: +OOM: if (ret == NULL) { if (opt_xmalloc) { _malloc_message(_getprogname(), @@ -5308,6 +5952,7 @@ RETURN: errno = ENOMEM; } +RETURN: UTRACE(0, size, ret); return (ret); } @@ -5321,6 +5966,24 @@ posix_memalign(void **memptr, size_t alignment, size_t size) if (malloc_init()) result = NULL; else { + if (size == 0) { + if (opt_sysv == false) + size = 1; + else { + if (opt_xmalloc) { + _malloc_message(_getprogname(), + ": (malloc) Error in " + "posix_memalign(): invalid " + "size 0\n", "", ""); + abort(); + } + result = NULL; + *memptr = NULL; + ret = 0; + goto RETURN; + } + } + /* Make sure that alignment is a large enough power of 2. */ if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *)) { @@ -5335,15 +5998,6 @@ posix_memalign(void **memptr, size_t alignment, size_t size) goto RETURN; } - if (size == 0) { - if (opt_sysv == false) - size = 1; - else { - result = NULL; - ret = 0; - goto RETURN; - } - } result = ipalloc(alignment, size); } @@ -5504,11 +6158,6 @@ malloc_usable_size(const void *ptr) * Begin library-private functions. */ -/******************************************************************************/ -/* - * Begin thread cache. - */ - /* * We provide an unpublished interface in order to receive notifications from * the pthreads library whenever a thread exits. This allows us to clean up @@ -5518,13 +6167,13 @@ void _malloc_thread_cleanup(void) { -#ifdef MALLOC_MAG - if (mag_rack != NULL) { - assert(mag_rack != (void *)-1); - mag_rack_destroy(mag_rack); -#ifdef MALLOC_DEBUG - mag_rack = (void *)-1; -#endif +#ifdef MALLOC_TCACHE + tcache_t *tcache = tcache_tls; + + if (tcache != NULL) { + assert(tcache != (void *)(uintptr_t)1); + tcache_destroy(tcache); + tcache_tls = (void *)(uintptr_t)1; } #endif } @@ -5539,41 +6188,14 @@ _malloc_thread_cleanup(void) void _malloc_prefork(void) { - bool again; - unsigned i, j; - arena_t *larenas[narenas], *tarenas[narenas]; + unsigned i; /* Acquire all mutexes in a safe order. */ - - /* - * arenas_lock must be acquired after all of the arena mutexes, in - * order to avoid potential deadlock with arena_lock_balance[_hard](). - * Since arenas_lock protects the arenas array, the following code has - * to race with arenas_extend() callers until it succeeds in locking - * all arenas before locking arenas_lock. - */ - memset(larenas, 0, sizeof(arena_t *) * narenas); - do { - again = false; - - malloc_spin_lock(&arenas_lock); - for (i = 0; i < narenas; i++) { - if (arenas[i] != larenas[i]) { - memcpy(tarenas, arenas, sizeof(arena_t *) * - narenas); - malloc_spin_unlock(&arenas_lock); - for (j = 0; j < narenas; j++) { - if (larenas[j] != tarenas[j]) { - larenas[j] = tarenas[j]; - malloc_spin_lock( - &larenas[j]->lock); - } - } - again = true; - break; - } - } - } while (again); + malloc_spin_lock(&arenas_lock); + for (i = 0; i < narenas; i++) { + if (arenas[i] != NULL) + malloc_spin_lock(&arenas[i]->lock); + } malloc_mutex_lock(&base_mtx); @@ -5588,7 +6210,6 @@ void _malloc_postfork(void) { unsigned i; - arena_t *larenas[narenas]; /* Release all mutexes, now that fork() has completed. */ @@ -5600,12 +6221,11 @@ _malloc_postfork(void) malloc_mutex_unlock(&base_mtx); - memcpy(larenas, arenas, sizeof(arena_t *) * narenas); - malloc_spin_unlock(&arenas_lock); for (i = 0; i < narenas; i++) { - if (larenas[i] != NULL) - malloc_spin_unlock(&larenas[i]->lock); + if (arenas[i] != NULL) + malloc_spin_unlock(&arenas[i]->lock); } + malloc_spin_unlock(&arenas_lock); } /* diff --git a/lib/libc/stdlib/ql.h b/lib/libc/stdlib/ql.h new file mode 100644 index 0000000..a1bc3ab --- /dev/null +++ b/lib/libc/stdlib/ql.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * + * Copyright (C) 2002 Jason Evans <jasone@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer + * unmodified other than the allowable addition of one or more + * copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef QL_H_ +#define QL_H_ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * List definitions. + */ +#define ql_head(a_type) \ +struct { \ + a_type *qlh_first; \ +} + +#define ql_head_initializer(a_head) {NULL} + +#define ql_elm(a_type) qr(a_type) + +/* List functions. */ +#define ql_new(a_head) do { \ + (a_head)->qlh_first = NULL; \ +} while (0) + +#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field) + +#define ql_first(a_head) ((a_head)->qlh_first) + +#define ql_last(a_head, a_field) \ + ((ql_first(a_head) != NULL) \ + ? qr_prev(ql_first(a_head), a_field) : NULL) + +#define ql_next(a_head, a_elm, a_field) \ + ((ql_last(a_head, a_field) != (a_elm)) \ + ? qr_next((a_elm), a_field) : NULL) + +#define ql_prev(a_head, a_elm, a_field) \ + ((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field) \ + : NULL) + +#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \ + qr_before_insert((a_qlelm), (a_elm), a_field); \ + if (ql_first(a_head) == (a_qlelm)) { \ + ql_first(a_head) = (a_elm); \ + } \ +} while (0) + +#define ql_after_insert(a_qlelm, a_elm, a_field) \ + qr_after_insert((a_qlelm), (a_elm), a_field) + +#define ql_head_insert(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) != NULL) { \ + qr_before_insert(ql_first(a_head), (a_elm), a_field); \ + } \ + ql_first(a_head) = (a_elm); \ +} while (0) + +#define ql_tail_insert(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) != NULL) { \ + qr_before_insert(ql_first(a_head), (a_elm), a_field); \ + } \ + ql_first(a_head) = qr_next((a_elm), a_field); \ +} while (0) + +#define ql_remove(a_head, a_elm, a_field) do { \ + if (ql_first(a_head) == (a_elm)) { \ + ql_first(a_head) = qr_next(ql_first(a_head), a_field); \ + } \ + if (ql_first(a_head) != (a_elm)) { \ + qr_remove((a_elm), a_field); \ + } else { \ + ql_first(a_head) = NULL; \ + } \ +} while (0) + +#define ql_head_remove(a_head, a_type, a_field) do { \ + a_type *t = ql_first(a_head); \ + ql_remove((a_head), t, a_field); \ +} while (0) + +#define ql_tail_remove(a_head, a_type, a_field) do { \ + a_type *t = ql_last(a_head, a_field); \ + ql_remove((a_head), t, a_field); \ +} while (0) + +#define ql_foreach(a_var, a_head, a_field) \ + qr_foreach((a_var), ql_first(a_head), a_field) + +#define ql_reverse_foreach(a_var, a_head, a_field) \ + qr_reverse_foreach((a_var), ql_first(a_head), a_field) + +#endif /* QL_H_ */ diff --git a/lib/libc/stdlib/qr.h b/lib/libc/stdlib/qr.h new file mode 100644 index 0000000..6e02321 --- /dev/null +++ b/lib/libc/stdlib/qr.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright (C) 2002 Jason Evans <jasone@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer + * unmodified other than the allowable addition of one or more + * copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef QR_H_ +#define QR_H_ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* Ring definitions. */ +#define qr(a_type) \ +struct { \ + a_type *qre_next; \ + a_type *qre_prev; \ +} + +/* Ring functions. */ +#define qr_new(a_qr, a_field) do { \ + (a_qr)->a_field.qre_next = (a_qr); \ + (a_qr)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next) + +#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev) + +#define qr_before_insert(a_qrelm, a_qr, a_field) do { \ + (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev; \ + (a_qr)->a_field.qre_next = (a_qrelm); \ + (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr); \ + (a_qrelm)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_after_insert(a_qrelm, a_qr, a_field) \ + do \ + { \ + (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next; \ + (a_qr)->a_field.qre_prev = (a_qrelm); \ + (a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr); \ + (a_qrelm)->a_field.qre_next = (a_qr); \ + } while (0) + +#define qr_meld(a_qr_a, a_qr_b, a_field) do { \ + void *t; \ + (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \ + (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \ + t = (a_qr_a)->a_field.qre_prev; \ + (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev; \ + (a_qr_b)->a_field.qre_prev = t; \ +} while (0) + +/* qr_meld() and qr_split() are functionally equivalent, so there's no need to + * have two copies of the code. */ +#define qr_split(a_qr_a, a_qr_b, a_field) \ + qr_meld((a_qr_a), (a_qr_b), a_field) + +#define qr_remove(a_qr, a_field) do { \ + (a_qr)->a_field.qre_prev->a_field.qre_next \ + = (a_qr)->a_field.qre_next; \ + (a_qr)->a_field.qre_next->a_field.qre_prev \ + = (a_qr)->a_field.qre_prev; \ + (a_qr)->a_field.qre_next = (a_qr); \ + (a_qr)->a_field.qre_prev = (a_qr); \ +} while (0) + +#define qr_foreach(var, a_qr, a_field) \ + for ((var) = (a_qr); \ + (var) != NULL; \ + (var) = (((var)->a_field.qre_next != (a_qr)) \ + ? (var)->a_field.qre_next : NULL)) + +#define qr_reverse_foreach(var, a_qr, a_field) \ + for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL; \ + (var) != NULL; \ + (var) = (((var) != (a_qr)) \ + ? (var)->a_field.qre_prev : NULL)) + +#endif /* QR_H_ */ diff --git a/lib/libc/stdlib/rb.h b/lib/libc/stdlib/rb.h index 80875b0..baaec7f 100644 --- a/lib/libc/stdlib/rb.h +++ b/lib/libc/stdlib/rb.h @@ -1,6 +1,7 @@ -/****************************************************************************** +/*- + ******************************************************************************* * - * Copyright (C) 2008 Jason Evans <jasone@FreeBSD.org>. + * Copyright (C) 2008-2010 Jason Evans <jasone@FreeBSD.org>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,40 +30,23 @@ * ****************************************************************************** * - * cpp macro implementation of left-leaning red-black trees. + * cpp macro implementation of left-leaning 2-3 red-black trees. Parent + * pointers are not used, and color bits are stored in the least significant + * bit of right-child pointers (if RB_COMPACT is defined), thus making node + * linkage as compact as is possible for red-black trees. * * Usage: * - * (Optional, see assert(3).) - * #define NDEBUG - * - * (Required.) + * #include <stdint.h> + * #include <stdbool.h> + * #define NDEBUG // (Optional, see assert(3).) * #include <assert.h> + * #define RB_COMPACT // (Optional, embed color bits in right-child pointers.) * #include <rb.h> * ... * - * All operations are done non-recursively. Parent pointers are not used, and - * color bits are stored in the least significant bit of right-child pointers, - * thus making node linkage as compact as is possible for red-black trees. - * - * Some macros use a comparison function pointer, which is expected to have the - * following prototype: - * - * int (a_cmp *)(a_type *a_node, a_type *a_other); - * ^^^^^^ - * or a_key - * - * Interpretation of comparision function return values: - * - * -1 : a_node < a_other - * 0 : a_node == a_other - * 1 : a_node > a_other - * - * In all cases, the a_node or a_key macro argument is the first argument to the - * comparison function, which makes it possible to write comparison functions - * that treat the first argument specially. - * - ******************************************************************************/ + ******************************************************************************* + */ #ifndef RB_H_ #define RB_H_ @@ -70,12 +54,21 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#ifdef RB_COMPACT /* Node structure. */ #define rb_node(a_type) \ struct { \ a_type *rbn_left; \ a_type *rbn_right_red; \ } +#else +#define rb_node(a_type) \ +struct { \ + a_type *rbn_left; \ + a_type *rbn_right; \ + bool rbn_red; \ +} +#endif /* Root structure. */ #define rb_tree(a_type) \ @@ -85,863 +78,925 @@ struct { \ } /* Left accessors. */ -#define rbp_left_get(a_type, a_field, a_node) \ +#define rbtn_left_get(a_type, a_field, a_node) \ ((a_node)->a_field.rbn_left) -#define rbp_left_set(a_type, a_field, a_node, a_left) do { \ +#define rbtn_left_set(a_type, a_field, a_node, a_left) do { \ (a_node)->a_field.rbn_left = a_left; \ } while (0) +#ifdef RB_COMPACT /* Right accessors. */ -#define rbp_right_get(a_type, a_field, a_node) \ +#define rbtn_right_get(a_type, a_field, a_node) \ ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red) \ & ((ssize_t)-2))) -#define rbp_right_set(a_type, a_field, a_node, a_right) do { \ +#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right) \ | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1))); \ } while (0) /* Color accessors. */ -#define rbp_red_get(a_type, a_field, a_node) \ +#define rbtn_red_get(a_type, a_field, a_node) \ ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red) \ & ((size_t)1))) -#define rbp_color_set(a_type, a_field, a_node, a_red) do { \ +#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t) \ (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)) \ | ((ssize_t)a_red)); \ } while (0) -#define rbp_red_set(a_type, a_field, a_node) do { \ +#define rbtn_red_set(a_type, a_field, a_node) do { \ (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) \ (a_node)->a_field.rbn_right_red) | ((size_t)1)); \ } while (0) -#define rbp_black_set(a_type, a_field, a_node) do { \ +#define rbtn_black_set(a_type, a_field, a_node) do { \ (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t) \ (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)); \ } while (0) +#else +/* Right accessors. */ +#define rbtn_right_get(a_type, a_field, a_node) \ + ((a_node)->a_field.rbn_right) +#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ + (a_node)->a_field.rbn_right = a_right; \ +} while (0) + +/* Color accessors. */ +#define rbtn_red_get(a_type, a_field, a_node) \ + ((a_node)->a_field.rbn_red) +#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ + (a_node)->a_field.rbn_red = (a_red); \ +} while (0) +#define rbtn_red_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_red = true; \ +} while (0) +#define rbtn_black_set(a_type, a_field, a_node) do { \ + (a_node)->a_field.rbn_red = false; \ +} while (0) +#endif /* Node initializer. */ -#define rbp_node_new(a_type, a_field, a_tree, a_node) do { \ - rbp_left_set(a_type, a_field, (a_node), &(a_tree)->rbt_nil); \ - rbp_right_set(a_type, a_field, (a_node), &(a_tree)->rbt_nil); \ - rbp_red_set(a_type, a_field, (a_node)); \ +#define rbt_node_new(a_type, a_field, a_rbt, a_node) do { \ + rbtn_left_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ + rbtn_right_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ + rbtn_red_set(a_type, a_field, (a_node)); \ } while (0) /* Tree initializer. */ -#define rb_new(a_type, a_field, a_tree) do { \ - (a_tree)->rbt_root = &(a_tree)->rbt_nil; \ - rbp_node_new(a_type, a_field, a_tree, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &(a_tree)->rbt_nil); \ +#define rb_new(a_type, a_field, a_rbt) do { \ + (a_rbt)->rbt_root = &(a_rbt)->rbt_nil; \ + rbt_node_new(a_type, a_field, a_rbt, &(a_rbt)->rbt_nil); \ + rbtn_black_set(a_type, a_field, &(a_rbt)->rbt_nil); \ } while (0) -/* Tree operations. */ -#define rbp_black_height(a_type, a_field, a_tree, r_height) do { \ - a_type *rbp_bh_t; \ - for (rbp_bh_t = (a_tree)->rbt_root, (r_height) = 0; \ - rbp_bh_t != &(a_tree)->rbt_nil; \ - rbp_bh_t = rbp_left_get(a_type, a_field, rbp_bh_t)) { \ - if (rbp_red_get(a_type, a_field, rbp_bh_t) == false) { \ - (r_height)++; \ +/* Internal utility macros. */ +#define rbtn_first(a_type, a_field, a_rbt, a_root, r_node) do { \ + (r_node) = (a_root); \ + if ((r_node) != &(a_rbt)->rbt_nil) { \ + for (; \ + rbtn_left_get(a_type, a_field, (r_node)) != &(a_rbt)->rbt_nil;\ + (r_node) = rbtn_left_get(a_type, a_field, (r_node))) { \ } \ } \ } while (0) -#define rbp_first(a_type, a_field, a_tree, a_root, r_node) do { \ - for ((r_node) = (a_root); \ - rbp_left_get(a_type, a_field, (r_node)) != &(a_tree)->rbt_nil; \ - (r_node) = rbp_left_get(a_type, a_field, (r_node))) { \ +#define rbtn_last(a_type, a_field, a_rbt, a_root, r_node) do { \ + (r_node) = (a_root); \ + if ((r_node) != &(a_rbt)->rbt_nil) { \ + for (; rbtn_right_get(a_type, a_field, (r_node)) != \ + &(a_rbt)->rbt_nil; (r_node) = rbtn_right_get(a_type, a_field, \ + (r_node))) { \ + } \ } \ } while (0) -#define rbp_last(a_type, a_field, a_tree, a_root, r_node) do { \ - for ((r_node) = (a_root); \ - rbp_right_get(a_type, a_field, (r_node)) != &(a_tree)->rbt_nil; \ - (r_node) = rbp_right_get(a_type, a_field, (r_node))) { \ - } \ +#define rbtn_rotate_left(a_type, a_field, a_node, r_node) do { \ + (r_node) = rbtn_right_get(a_type, a_field, (a_node)); \ + rbtn_right_set(a_type, a_field, (a_node), \ + rbtn_left_get(a_type, a_field, (r_node))); \ + rbtn_left_set(a_type, a_field, (r_node), (a_node)); \ } while (0) -#define rbp_next(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - if (rbp_right_get(a_type, a_field, (a_node)) \ - != &(a_tree)->rbt_nil) { \ - rbp_first(a_type, a_field, a_tree, rbp_right_get(a_type, \ - a_field, (a_node)), (r_node)); \ +#define rbtn_rotate_right(a_type, a_field, a_node, r_node) do { \ + (r_node) = rbtn_left_get(a_type, a_field, (a_node)); \ + rbtn_left_set(a_type, a_field, (a_node), \ + rbtn_right_get(a_type, a_field, (r_node))); \ + rbtn_right_set(a_type, a_field, (r_node), (a_node)); \ +} while (0) + +/* + * The rb_proto() macro generates function prototypes that correspond to the + * functions generated by an equivalently parameterized call to rb_gen(). + */ + +#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \ +a_attr void \ +a_prefix##new(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##first(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##last(a_rbt_type *rbtree); \ +a_attr a_type * \ +a_prefix##next(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##prev(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##search(a_rbt_type *rbtree, a_type *key); \ +a_attr a_type * \ +a_prefix##nsearch(a_rbt_type *rbtree, a_type *key); \ +a_attr a_type * \ +a_prefix##psearch(a_rbt_type *rbtree, a_type *key); \ +a_attr void \ +a_prefix##insert(a_rbt_type *rbtree, a_type *node); \ +a_attr void \ +a_prefix##remove(a_rbt_type *rbtree, a_type *node); \ +a_attr a_type * \ +a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ + a_rbt_type *, a_type *, void *), void *arg); \ +a_attr a_type * \ +a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg); + +/* + * The rb_gen() macro generates a type-specific red-black tree implementation, + * based on the above cpp macros. + * + * Arguments: + * + * a_attr : Function attribute for generated functions (ex: static). + * a_prefix : Prefix for generated functions (ex: extree_). + * a_rb_type : Type for red-black tree data structure (ex: extree_t). + * a_type : Type for red-black tree node data structure (ex: + * extree_node_t). + * a_field : Name of red-black tree node linkage (ex: extree_link). + * a_cmp : Node comparison function name, with the following prototype: + * int (a_cmp *)(a_type *a_node, a_type *a_other); + * ^^^^^^ + * or a_key + * Interpretation of comparision function return values: + * -1 : a_node < a_other + * 0 : a_node == a_other + * 1 : a_node > a_other + * In all cases, the a_node or a_key macro argument is the first + * argument to the comparison function, which makes it possible + * to write comparison functions that treat the first argument + * specially. + * + * Assuming the following setup: + * + * typedef struct ex_node_s ex_node_t; + * struct ex_node_s { + * rb_node(ex_node_t) ex_link; + * }; + * typedef rb(ex_node_t) ex_t; + * rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp, 1297, 1301) + * + * The following API is generated: + * + * static void + * ex_new(ex_t *extree); + * Description: Initialize a red-black tree structure. + * Args: + * extree: Pointer to an uninitialized red-black tree object. + * + * static ex_node_t * + * ex_first(ex_t *extree); + * static ex_node_t * + * ex_last(ex_t *extree); + * Description: Get the first/last node in extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * Ret: First/last node in extree, or NULL if extree is empty. + * + * static ex_node_t * + * ex_next(ex_t *extree, ex_node_t *node); + * static ex_node_t * + * ex_prev(ex_t *extree, ex_node_t *node); + * Description: Get node's successor/predecessor. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : A node in extree. + * Ret: node's successor/predecessor in extree, or NULL if node is + * last/first. + * + * static ex_node_t * + * ex_search(ex_t *extree, ex_node_t *key); + * Description: Search for node that matches key. + * Args: + * extree: Pointer to an initialized red-black tree object. + * key : Search key. + * Ret: Node in extree that matches key, or NULL if no match. + * + * static ex_node_t * + * ex_nsearch(ex_t *extree, ex_node_t *key); + * static ex_node_t * + * ex_psearch(ex_t *extree, ex_node_t *key); + * Description: Search for node that matches key. If no match is found, + * return what would be key's successor/predecessor, were + * key in extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * key : Search key. + * Ret: Node in extree that matches key, or if no match, hypothetical + * node's successor/predecessor (NULL if no successor/predecessor). + * + * static void + * ex_insert(ex_t *extree, ex_node_t *node); + * Description: Insert node into extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : Node to be inserted into extree. + * + * static void + * ex_remove(ex_t *extree, ex_node_t *node); + * Description: Remove node from extree. + * Args: + * extree: Pointer to an initialized red-black tree object. + * node : Node in extree to be removed. + * + * static ex_node_t * + * ex_iter(ex_t *extree, ex_node_t *start, ex_node_t *(*cb)(ex_t *, + * ex_node_t *, void *), void *arg); + * static ex_node_t * + * ex_reverse_iter(ex_t *extree, ex_node_t *start, ex_node *(*cb)(ex_t *, + * ex_node_t *, void *), void *arg); + * Description: Iterate forward/backward over extree, starting at node. + * If extree is modified, iteration must be immediately + * terminated by the callback function that causes the + * modification. + * Args: + * extree: Pointer to an initialized red-black tree object. + * start : Node at which to start iteration, or NULL to start at + * first/last node. + * cb : Callback function, which is called for each node during + * iteration. Under normal circumstances the callback function + * should return NULL, which causes iteration to continue. If a + * callback function returns non-NULL, iteration is immediately + * terminated and the non-NULL return value is returned by the + * iterator. This is useful for re-starting iteration after + * modifying extree. + * arg : Opaque pointer passed to cb(). + * Ret: NULL if iteration completed, or the non-NULL callback return value + * that caused termination of the iteration. + */ +#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp) \ +a_attr void \ +a_prefix##new(a_rbt_type *rbtree) { \ + rb_new(a_type, a_field, rbtree); \ +} \ +a_attr a_type * \ +a_prefix##first(a_rbt_type *rbtree) { \ + a_type *ret; \ + rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##last(a_rbt_type *rbtree) { \ + a_type *ret; \ + rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##next(a_rbt_type *rbtree, a_type *node) { \ + a_type *ret; \ + if (rbtn_right_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ + rbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type, \ + a_field, node), ret); \ } else { \ - a_type *rbp_n_t = (a_tree)->rbt_root; \ - assert(rbp_n_t != &(a_tree)->rbt_nil); \ - (r_node) = &(a_tree)->rbt_nil; \ + a_type *tnode = rbtree->rbt_root; \ + assert(tnode != &rbtree->rbt_nil); \ + ret = &rbtree->rbt_nil; \ while (true) { \ - int rbp_n_cmp = (a_cmp)((a_node), rbp_n_t); \ - if (rbp_n_cmp < 0) { \ - (r_node) = rbp_n_t; \ - rbp_n_t = rbp_left_get(a_type, a_field, rbp_n_t); \ - } else if (rbp_n_cmp > 0) { \ - rbp_n_t = rbp_right_get(a_type, a_field, rbp_n_t); \ + int cmp = (a_cmp)(node, tnode); \ + if (cmp < 0) { \ + ret = tnode; \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ } else { \ break; \ } \ - assert(rbp_n_t != &(a_tree)->rbt_nil); \ + assert(tnode != &rbtree->rbt_nil); \ } \ } \ -} while (0) - -#define rbp_prev(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - if (rbp_left_get(a_type, a_field, (a_node)) != &(a_tree)->rbt_nil) {\ - rbp_last(a_type, a_field, a_tree, rbp_left_get(a_type, \ - a_field, (a_node)), (r_node)); \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##prev(a_rbt_type *rbtree, a_type *node) { \ + a_type *ret; \ + if (rbtn_left_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ + rbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type, \ + a_field, node), ret); \ } else { \ - a_type *rbp_p_t = (a_tree)->rbt_root; \ - assert(rbp_p_t != &(a_tree)->rbt_nil); \ - (r_node) = &(a_tree)->rbt_nil; \ + a_type *tnode = rbtree->rbt_root; \ + assert(tnode != &rbtree->rbt_nil); \ + ret = &rbtree->rbt_nil; \ while (true) { \ - int rbp_p_cmp = (a_cmp)((a_node), rbp_p_t); \ - if (rbp_p_cmp < 0) { \ - rbp_p_t = rbp_left_get(a_type, a_field, rbp_p_t); \ - } else if (rbp_p_cmp > 0) { \ - (r_node) = rbp_p_t; \ - rbp_p_t = rbp_right_get(a_type, a_field, rbp_p_t); \ + int cmp = (a_cmp)(node, tnode); \ + if (cmp < 0) { \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + ret = tnode; \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ } else { \ break; \ } \ - assert(rbp_p_t != &(a_tree)->rbt_nil); \ + assert(tnode != &rbtree->rbt_nil); \ } \ } \ -} while (0) - -#define rb_first(a_type, a_field, a_tree, r_node) do { \ - rbp_first(a_type, a_field, a_tree, (a_tree)->rbt_root, (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_last(a_type, a_field, a_tree, r_node) do { \ - rbp_last(a_type, a_field, a_tree, (a_tree)->rbt_root, r_node); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ } \ -} while (0) - -#define rb_next(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - rbp_next(a_type, a_field, a_cmp, a_tree, (a_node), (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_prev(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - rbp_prev(a_type, a_field, a_cmp, a_tree, (a_node), (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_search(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - int rbp_se_cmp; \ - (r_node) = (a_tree)->rbt_root; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && (rbp_se_cmp = (a_cmp)((a_key), (r_node))) != 0) { \ - if (rbp_se_cmp < 0) { \ - (r_node) = rbp_left_get(a_type, a_field, (r_node)); \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##search(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + int cmp; \ + ret = rbtree->rbt_root; \ + while (ret != &rbtree->rbt_nil \ + && (cmp = (a_cmp)(key, ret)) != 0) { \ + if (cmp < 0) { \ + ret = rbtn_left_get(a_type, a_field, ret); \ } else { \ - (r_node) = rbp_right_get(a_type, a_field, (r_node)); \ + ret = rbtn_right_get(a_type, a_field, ret); \ } \ } \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ } \ -} while (0) - -/* - * Find a match if it exists. Otherwise, find the next greater node, if one - * exists. - */ -#define rb_nsearch(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - a_type *rbp_ns_t = (a_tree)->rbt_root; \ - (r_node) = NULL; \ - while (rbp_ns_t != &(a_tree)->rbt_nil) { \ - int rbp_ns_cmp = (a_cmp)((a_key), rbp_ns_t); \ - if (rbp_ns_cmp < 0) { \ - (r_node) = rbp_ns_t; \ - rbp_ns_t = rbp_left_get(a_type, a_field, rbp_ns_t); \ - } else if (rbp_ns_cmp > 0) { \ - rbp_ns_t = rbp_right_get(a_type, a_field, rbp_ns_t); \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##nsearch(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + a_type *tnode = rbtree->rbt_root; \ + ret = &rbtree->rbt_nil; \ + while (tnode != &rbtree->rbt_nil) { \ + int cmp = (a_cmp)(key, tnode); \ + if (cmp < 0) { \ + ret = tnode; \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ } else { \ - (r_node) = rbp_ns_t; \ + ret = tnode; \ break; \ } \ } \ -} while (0) - -/* - * Find a match if it exists. Otherwise, find the previous lesser node, if one - * exists. - */ -#define rb_psearch(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - a_type *rbp_ps_t = (a_tree)->rbt_root; \ - (r_node) = NULL; \ - while (rbp_ps_t != &(a_tree)->rbt_nil) { \ - int rbp_ps_cmp = (a_cmp)((a_key), rbp_ps_t); \ - if (rbp_ps_cmp < 0) { \ - rbp_ps_t = rbp_left_get(a_type, a_field, rbp_ps_t); \ - } else if (rbp_ps_cmp > 0) { \ - (r_node) = rbp_ps_t; \ - rbp_ps_t = rbp_right_get(a_type, a_field, rbp_ps_t); \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ + } \ + return (ret); \ +} \ +a_attr a_type * \ +a_prefix##psearch(a_rbt_type *rbtree, a_type *key) { \ + a_type *ret; \ + a_type *tnode = rbtree->rbt_root; \ + ret = &rbtree->rbt_nil; \ + while (tnode != &rbtree->rbt_nil) { \ + int cmp = (a_cmp)(key, tnode); \ + if (cmp < 0) { \ + tnode = rbtn_left_get(a_type, a_field, tnode); \ + } else if (cmp > 0) { \ + ret = tnode; \ + tnode = rbtn_right_get(a_type, a_field, tnode); \ } else { \ - (r_node) = rbp_ps_t; \ + ret = tnode; \ break; \ } \ } \ -} while (0) - -#define rbp_rotate_left(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbp_right_get(a_type, a_field, (a_node)); \ - rbp_right_set(a_type, a_field, (a_node), \ - rbp_left_get(a_type, a_field, (r_node))); \ - rbp_left_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -#define rbp_rotate_right(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbp_left_get(a_type, a_field, (a_node)); \ - rbp_left_set(a_type, a_field, (a_node), \ - rbp_right_get(a_type, a_field, (r_node))); \ - rbp_right_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -#define rbp_lean_left(a_type, a_field, a_node, r_node) do { \ - bool rbp_ll_red; \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - rbp_ll_red = rbp_red_get(a_type, a_field, (a_node)); \ - rbp_color_set(a_type, a_field, (r_node), rbp_ll_red); \ - rbp_red_set(a_type, a_field, (a_node)); \ -} while (0) - -#define rbp_lean_right(a_type, a_field, a_node, r_node) do { \ - bool rbp_lr_red; \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_lr_red = rbp_red_get(a_type, a_field, (a_node)); \ - rbp_color_set(a_type, a_field, (r_node), rbp_lr_red); \ - rbp_red_set(a_type, a_field, (a_node)); \ -} while (0) - -#define rbp_move_red_left(a_type, a_field, a_node, r_node) do { \ - a_type *rbp_mrl_t, *rbp_mrl_u; \ - rbp_mrl_t = rbp_left_get(a_type, a_field, (a_node)); \ - rbp_red_set(a_type, a_field, rbp_mrl_t); \ - rbp_mrl_t = rbp_right_get(a_type, a_field, (a_node)); \ - rbp_mrl_u = rbp_left_get(a_type, a_field, rbp_mrl_t); \ - if (rbp_red_get(a_type, a_field, rbp_mrl_u)) { \ - rbp_rotate_right(a_type, a_field, rbp_mrl_t, rbp_mrl_u); \ - rbp_right_set(a_type, a_field, (a_node), rbp_mrl_u); \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - rbp_mrl_t = rbp_right_get(a_type, a_field, (a_node)); \ - if (rbp_red_get(a_type, a_field, rbp_mrl_t)) { \ - rbp_black_set(a_type, a_field, rbp_mrl_t); \ - rbp_red_set(a_type, a_field, (a_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrl_t); \ - rbp_left_set(a_type, a_field, (r_node), rbp_mrl_t); \ - } else { \ - rbp_black_set(a_type, a_field, (a_node)); \ - } \ - } else { \ - rbp_red_set(a_type, a_field, (a_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ + if (ret == &rbtree->rbt_nil) { \ + ret = (NULL); \ } \ -} while (0) - -#define rbp_move_red_right(a_type, a_field, a_node, r_node) do { \ - a_type *rbp_mrr_t; \ - rbp_mrr_t = rbp_left_get(a_type, a_field, (a_node)); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_t)) { \ - a_type *rbp_mrr_u, *rbp_mrr_v; \ - rbp_mrr_u = rbp_right_get(a_type, a_field, rbp_mrr_t); \ - rbp_mrr_v = rbp_left_get(a_type, a_field, rbp_mrr_u); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_v)) { \ - rbp_color_set(a_type, a_field, rbp_mrr_u, \ - rbp_red_get(a_type, a_field, (a_node))); \ - rbp_black_set(a_type, a_field, rbp_mrr_v); \ - rbp_rotate_left(a_type, a_field, rbp_mrr_t, rbp_mrr_u); \ - rbp_left_set(a_type, a_field, (a_node), rbp_mrr_u); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ - } else { \ - rbp_color_set(a_type, a_field, rbp_mrr_t, \ - rbp_red_get(a_type, a_field, (a_node))); \ - rbp_red_set(a_type, a_field, rbp_mrr_u); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ - } \ - rbp_red_set(a_type, a_field, (a_node)); \ - } else { \ - rbp_red_set(a_type, a_field, rbp_mrr_t); \ - rbp_mrr_t = rbp_left_get(a_type, a_field, rbp_mrr_t); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_t)) { \ - rbp_black_set(a_type, a_field, rbp_mrr_t); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ + return (ret); \ +} \ +a_attr void \ +a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \ + struct { \ + a_type *node; \ + int cmp; \ + } path[sizeof(void *) << 4], *pathp; \ + rbt_node_new(a_type, a_field, rbtree, node); \ + /* Wind. */ \ + path->node = rbtree->rbt_root; \ + for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ + int cmp = pathp->cmp = a_cmp(node, pathp->node); \ + assert(cmp != 0); \ + if (cmp < 0) { \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ } else { \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ + pathp[1].node = rbtn_right_get(a_type, a_field, \ + pathp->node); \ } \ } \ -} while (0) - -#define rb_insert(a_type, a_field, a_cmp, a_tree, a_node) do { \ - a_type rbp_i_s; \ - a_type *rbp_i_g, *rbp_i_p, *rbp_i_c, *rbp_i_t, *rbp_i_u; \ - int rbp_i_cmp = 0; \ - rbp_i_g = &(a_tree)->rbt_nil; \ - rbp_left_set(a_type, a_field, &rbp_i_s, (a_tree)->rbt_root); \ - rbp_right_set(a_type, a_field, &rbp_i_s, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &rbp_i_s); \ - rbp_i_p = &rbp_i_s; \ - rbp_i_c = (a_tree)->rbt_root; \ - /* Iteratively search down the tree for the insertion point, */\ - /* splitting 4-nodes as they are encountered. At the end of each */\ - /* iteration, rbp_i_g->rbp_i_p->rbp_i_c is a 3-level path down */\ - /* the tree, assuming a sufficiently deep tree. */\ - while (rbp_i_c != &(a_tree)->rbt_nil) { \ - rbp_i_t = rbp_left_get(a_type, a_field, rbp_i_c); \ - rbp_i_u = rbp_left_get(a_type, a_field, rbp_i_t); \ - if (rbp_red_get(a_type, a_field, rbp_i_t) \ - && rbp_red_get(a_type, a_field, rbp_i_u)) { \ - /* rbp_i_c is the top of a logical 4-node, so split it. */\ - /* This iteration does not move down the tree, due to the */\ - /* disruptiveness of node splitting. */\ - /* */\ - /* Rotate right. */\ - rbp_rotate_right(a_type, a_field, rbp_i_c, rbp_i_t); \ - /* Pass red links up one level. */\ - rbp_i_u = rbp_left_get(a_type, a_field, rbp_i_t); \ - rbp_black_set(a_type, a_field, rbp_i_u); \ - if (rbp_left_get(a_type, a_field, rbp_i_p) == rbp_i_c) { \ - rbp_left_set(a_type, a_field, rbp_i_p, rbp_i_t); \ - rbp_i_c = rbp_i_t; \ - } else { \ - /* rbp_i_c was the right child of rbp_i_p, so rotate */\ - /* left in order to maintain the left-leaning */\ - /* invariant. */\ - assert(rbp_right_get(a_type, a_field, rbp_i_p) \ - == rbp_i_c); \ - rbp_right_set(a_type, a_field, rbp_i_p, rbp_i_t); \ - rbp_lean_left(a_type, a_field, rbp_i_p, rbp_i_u); \ - if (rbp_left_get(a_type, a_field, rbp_i_g) == rbp_i_p) {\ - rbp_left_set(a_type, a_field, rbp_i_g, rbp_i_u); \ - } else { \ - assert(rbp_right_get(a_type, a_field, rbp_i_g) \ - == rbp_i_p); \ - rbp_right_set(a_type, a_field, rbp_i_g, rbp_i_u); \ + pathp->node = node; \ + /* Unwind. */ \ + for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ + a_type *cnode = pathp->node; \ + if (pathp->cmp < 0) { \ + a_type *left = pathp[1].node; \ + rbtn_left_set(a_type, a_field, cnode, left); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* Fix up 4-node. */ \ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, cnode, tnode); \ + cnode = tnode; \ } \ - rbp_i_p = rbp_i_u; \ - rbp_i_cmp = (a_cmp)((a_node), rbp_i_p); \ - if (rbp_i_cmp < 0) { \ - rbp_i_c = rbp_left_get(a_type, a_field, rbp_i_p); \ + } else { \ + return; \ + } \ + } else { \ + a_type *right = pathp[1].node; \ + rbtn_right_set(a_type, a_field, cnode, right); \ + if (rbtn_red_get(a_type, a_field, right)) { \ + a_type *left = rbtn_left_get(a_type, a_field, cnode); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + /* Split 4-node. */ \ + rbtn_black_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, right); \ + rbtn_red_set(a_type, a_field, cnode); \ } else { \ - assert(rbp_i_cmp > 0); \ - rbp_i_c = rbp_right_get(a_type, a_field, rbp_i_p); \ + /* Lean left. */ \ + a_type *tnode; \ + bool tred = rbtn_red_get(a_type, a_field, cnode); \ + rbtn_rotate_left(a_type, a_field, cnode, tnode); \ + rbtn_color_set(a_type, a_field, tnode, tred); \ + rbtn_red_set(a_type, a_field, cnode); \ + cnode = tnode; \ } \ - continue; \ + } else { \ + return; \ } \ } \ - rbp_i_g = rbp_i_p; \ - rbp_i_p = rbp_i_c; \ - rbp_i_cmp = (a_cmp)((a_node), rbp_i_c); \ - if (rbp_i_cmp < 0) { \ - rbp_i_c = rbp_left_get(a_type, a_field, rbp_i_c); \ - } else { \ - assert(rbp_i_cmp > 0); \ - rbp_i_c = rbp_right_get(a_type, a_field, rbp_i_c); \ - } \ + pathp->node = cnode; \ } \ - /* rbp_i_p now refers to the node under which to insert. */\ - rbp_node_new(a_type, a_field, a_tree, (a_node)); \ - if (rbp_i_cmp > 0) { \ - rbp_right_set(a_type, a_field, rbp_i_p, (a_node)); \ - rbp_lean_left(a_type, a_field, rbp_i_p, rbp_i_t); \ - if (rbp_left_get(a_type, a_field, rbp_i_g) == rbp_i_p) { \ - rbp_left_set(a_type, a_field, rbp_i_g, rbp_i_t); \ - } else if (rbp_right_get(a_type, a_field, rbp_i_g) == rbp_i_p) {\ - rbp_right_set(a_type, a_field, rbp_i_g, rbp_i_t); \ + /* Set root, and make it black. */ \ + rbtree->rbt_root = path->node; \ + rbtn_black_set(a_type, a_field, rbtree->rbt_root); \ +} \ +a_attr void \ +a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ + struct { \ + a_type *node; \ + int cmp; \ + } *pathp, *nodep, path[sizeof(void *) << 4]; \ + /* Wind. */ \ + nodep = NULL; /* Silence compiler warning. */ \ + path->node = rbtree->rbt_root; \ + for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ + int cmp = pathp->cmp = a_cmp(node, pathp->node); \ + if (cmp < 0) { \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ + } else { \ + pathp[1].node = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + if (cmp == 0) { \ + /* Find node's successor, in preparation for swap. */ \ + pathp->cmp = 1; \ + nodep = pathp; \ + for (pathp++; pathp->node != &rbtree->rbt_nil; \ + pathp++) { \ + pathp->cmp = -1; \ + pathp[1].node = rbtn_left_get(a_type, a_field, \ + pathp->node); \ + } \ + break; \ + } \ } \ - } else { \ - rbp_left_set(a_type, a_field, rbp_i_p, (a_node)); \ } \ - /* Update the root and make sure that it is black. */\ - (a_tree)->rbt_root = rbp_left_get(a_type, a_field, &rbp_i_s); \ - rbp_black_set(a_type, a_field, (a_tree)->rbt_root); \ -} while (0) - -#define rb_remove(a_type, a_field, a_cmp, a_tree, a_node) do { \ - a_type rbp_r_s; \ - a_type *rbp_r_p, *rbp_r_c, *rbp_r_xp, *rbp_r_t, *rbp_r_u; \ - int rbp_r_cmp; \ - rbp_left_set(a_type, a_field, &rbp_r_s, (a_tree)->rbt_root); \ - rbp_right_set(a_type, a_field, &rbp_r_s, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &rbp_r_s); \ - rbp_r_p = &rbp_r_s; \ - rbp_r_c = (a_tree)->rbt_root; \ - rbp_r_xp = &(a_tree)->rbt_nil; \ - /* Iterate down the tree, but always transform 2-nodes to 3- or */\ - /* 4-nodes in order to maintain the invariant that the current */\ - /* node is not a 2-node. This allows simple deletion once a leaf */\ - /* is reached. Handle the root specially though, since there may */\ - /* be no way to convert it from a 2-node to a 3-node. */\ - rbp_r_cmp = (a_cmp)((a_node), rbp_r_c); \ - if (rbp_r_cmp < 0) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_t) == false \ - && rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - /* Apply standard transform to prepare for left move. */\ - rbp_move_red_left(a_type, a_field, rbp_r_c, rbp_r_t); \ - rbp_black_set(a_type, a_field, rbp_r_t); \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - rbp_r_c = rbp_r_t; \ + assert(nodep->node == node); \ + pathp--; \ + if (pathp->node != node) { \ + /* Swap node with its successor. */ \ + bool tred = rbtn_red_get(a_type, a_field, pathp->node); \ + rbtn_color_set(a_type, a_field, pathp->node, \ + rbtn_red_get(a_type, a_field, node)); \ + rbtn_left_set(a_type, a_field, pathp->node, \ + rbtn_left_get(a_type, a_field, node)); \ + /* If node's successor is its right child, the following code */\ + /* will do the wrong thing for the right child pointer. */\ + /* However, it doesn't matter, because the pointer will be */\ + /* properly set when the successor is pruned. */\ + rbtn_right_set(a_type, a_field, pathp->node, \ + rbtn_right_get(a_type, a_field, node)); \ + rbtn_color_set(a_type, a_field, node, tred); \ + /* The pruned leaf node's child pointers are never accessed */\ + /* again, so don't bother setting them to nil. */\ + nodep->node = pathp->node; \ + pathp->node = node; \ + if (nodep == path) { \ + rbtree->rbt_root = nodep->node; \ } else { \ - /* Move left. */\ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_left_get(a_type, a_field, rbp_r_c); \ + if (nodep[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, nodep[-1].node, \ + nodep->node); \ + } else { \ + rbtn_right_set(a_type, a_field, nodep[-1].node, \ + nodep->node); \ + } \ } \ } else { \ - if (rbp_r_cmp == 0) { \ - assert((a_node) == rbp_r_c); \ - if (rbp_right_get(a_type, a_field, rbp_r_c) \ - == &(a_tree)->rbt_nil) { \ - /* Delete root node (which is also a leaf node). */\ - if (rbp_left_get(a_type, a_field, rbp_r_c) \ - != &(a_tree)->rbt_nil) { \ - rbp_lean_right(a_type, a_field, rbp_r_c, rbp_r_t); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - &(a_tree)->rbt_nil); \ + a_type *left = rbtn_left_get(a_type, a_field, node); \ + if (left != &rbtree->rbt_nil) { \ + /* node has no successor, but it has a left child. */\ + /* Splice node out, without losing the left child. */\ + assert(rbtn_red_get(a_type, a_field, node) == false); \ + assert(rbtn_red_get(a_type, a_field, left)); \ + rbtn_black_set(a_type, a_field, left); \ + if (pathp == path) { \ + rbtree->rbt_root = left; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + left); \ } else { \ - rbp_r_t = &(a_tree)->rbt_nil; \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + left); \ } \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - } else { \ - /* This is the node we want to delete, but we will */\ - /* instead swap it with its successor and delete the */\ - /* successor. Record enough information to do the */\ - /* swap later. rbp_r_xp is the a_node's parent. */\ - rbp_r_xp = rbp_r_p; \ - rbp_r_cmp = 1; /* Note that deletion is incomplete. */\ } \ + return; \ + } else if (pathp == path) { \ + /* The tree only contained one node. */ \ + rbtree->rbt_root = &rbtree->rbt_nil; \ + return; \ } \ - if (rbp_r_cmp == 1) { \ - if (rbp_red_get(a_type, a_field, rbp_left_get(a_type, \ - a_field, rbp_right_get(a_type, a_field, rbp_r_c))) \ - == false) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - if (rbp_red_get(a_type, a_field, rbp_r_t)) { \ - /* Standard transform. */\ - rbp_move_red_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ + } \ + if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + /* Prune red node, which requires no fixup. */ \ + assert(pathp[-1].cmp < 0); \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + &rbtree->rbt_nil); \ + return; \ + } \ + /* The node to be pruned is black, so unwind until balance is */\ + /* restored. */\ + pathp->node = &rbtree->rbt_nil; \ + for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ + assert(pathp->cmp != 0); \ + if (pathp->cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp->node, \ + pathp[1].node); \ + assert(rbtn_red_get(a_type, a_field, pathp[1].node) \ + == false); \ + if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + a_type *right = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + a_type *rightleft = rbtn_left_get(a_type, a_field, \ + right); \ + a_type *tnode; \ + if (rbtn_red_get(a_type, a_field, rightleft)) { \ + /* In the following diagrams, ||, //, and \\ */\ + /* indicate the path to the removed node. */\ + /* */\ + /* || */\ + /* pathp(r) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + /* */\ + rbtn_black_set(a_type, a_field, pathp->node); \ + rbtn_rotate_right(a_type, a_field, right, tnode); \ + rbtn_right_set(a_type, a_field, pathp->node, tnode);\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ } else { \ - /* Root-specific transform. */\ - rbp_red_set(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_u)) { \ - rbp_black_set(a_type, a_field, rbp_r_u); \ - rbp_rotate_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - rbp_rotate_left(a_type, a_field, rbp_r_c, \ - rbp_r_u); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - rbp_r_u); \ - } else { \ - rbp_red_set(a_type, a_field, rbp_r_t); \ - rbp_rotate_left(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - } \ + /* || */\ + /* pathp(r) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + /* */\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + } \ + /* Balance restored, but rotation modified subtree */\ + /* root. */\ + assert((uintptr_t)pathp > (uintptr_t)path); \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ } \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - rbp_r_c = rbp_r_t; \ + return; \ } else { \ - /* Move right. */\ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_right_get(a_type, a_field, rbp_r_c); \ - } \ - } \ - } \ - if (rbp_r_cmp != 0) { \ - while (true) { \ - assert(rbp_r_p != &(a_tree)->rbt_nil); \ - rbp_r_cmp = (a_cmp)((a_node), rbp_r_c); \ - if (rbp_r_cmp < 0) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - if (rbp_r_t == &(a_tree)->rbt_nil) { \ - /* rbp_r_c now refers to the successor node to */\ - /* relocate, and rbp_r_xp/a_node refer to the */\ - /* context for the relocation. */\ - if (rbp_left_get(a_type, a_field, rbp_r_xp) \ - == (a_node)) { \ - rbp_left_set(a_type, a_field, rbp_r_xp, \ - rbp_r_c); \ + a_type *right = rbtn_right_get(a_type, a_field, \ + pathp->node); \ + a_type *rightleft = rbtn_left_get(a_type, a_field, \ + right); \ + if (rbtn_red_get(a_type, a_field, rightleft)) { \ + /* || */\ + /* pathp(b) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, rightleft); \ + rbtn_rotate_right(a_type, a_field, right, tnode); \ + rbtn_right_set(a_type, a_field, pathp->node, tnode);\ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subree root, which may actually be the tree */\ + /* root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ } else { \ - assert(rbp_right_get(a_type, a_field, \ - rbp_r_xp) == (a_node)); \ - rbp_right_set(a_type, a_field, rbp_r_xp, \ - rbp_r_c); \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } else { \ + rbtn_right_set(a_type, a_field, \ + pathp[-1].node, tnode); \ + } \ } \ - rbp_left_set(a_type, a_field, rbp_r_c, \ - rbp_left_get(a_type, a_field, (a_node))); \ - rbp_right_set(a_type, a_field, rbp_r_c, \ - rbp_right_get(a_type, a_field, (a_node))); \ - rbp_color_set(a_type, a_field, rbp_r_c, \ - rbp_red_get(a_type, a_field, (a_node))); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, \ - &(a_tree)->rbt_nil); \ + return; \ + } else { \ + /* || */\ + /* pathp(b) */\ + /* // \ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + a_type *tnode; \ + rbtn_red_set(a_type, a_field, pathp->node); \ + rbtn_rotate_left(a_type, a_field, pathp->node, \ + tnode); \ + pathp->node = tnode; \ + } \ + } \ + } else { \ + a_type *left; \ + rbtn_right_set(a_type, a_field, pathp->node, \ + pathp[1].node); \ + left = rbtn_left_get(a_type, a_field, pathp->node); \ + if (rbtn_red_get(a_type, a_field, left)) { \ + a_type *tnode; \ + a_type *leftright = rbtn_right_get(a_type, a_field, \ + left); \ + a_type *leftrightleft = rbtn_left_get(a_type, a_field, \ + leftright); \ + if (rbtn_red_get(a_type, a_field, leftrightleft)) { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (r) (b) */\ + /* \ */\ + /* (b) */\ + /* / */\ + /* (r) */\ + a_type *unode; \ + rbtn_black_set(a_type, a_field, leftrightleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + unode); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + rbtn_right_set(a_type, a_field, unode, tnode); \ + rbtn_rotate_left(a_type, a_field, unode, tnode); \ + } else { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (r) (b) */\ + /* \ */\ + /* (b) */\ + /* / */\ + /* (b) */\ + assert(leftright != &rbtree->rbt_nil); \ + rbtn_red_set(a_type, a_field, leftright); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + rbtn_black_set(a_type, a_field, tnode); \ + } \ + /* Balance restored, but rotation modified subtree */\ + /* root, which may actually be the tree root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ } else { \ - assert(rbp_right_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c); \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - &(a_tree)->rbt_nil); \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ } \ - break; \ } \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_t) == false \ - && rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - rbp_move_red_left(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t);\ + return; \ + } else if (rbtn_red_get(a_type, a_field, pathp->node)) { \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* || */\ + /* pathp(r) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, pathp->node); \ + rbtn_red_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subtree root. */\ + assert((uintptr_t)pathp > (uintptr_t)path); \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, pathp[-1].node, \ + tnode); \ } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ + rbtn_right_set(a_type, a_field, pathp[-1].node, \ + tnode); \ } \ - rbp_r_c = rbp_r_t; \ + return; \ } else { \ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_left_get(a_type, a_field, rbp_r_c); \ + /* || */\ + /* pathp(r) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + rbtn_red_set(a_type, a_field, left); \ + rbtn_black_set(a_type, a_field, pathp->node); \ + /* Balance restored. */ \ + return; \ } \ } else { \ - /* Check whether to delete this node (it has to be */\ - /* the correct node and a leaf node). */\ - if (rbp_r_cmp == 0) { \ - assert((a_node) == rbp_r_c); \ - if (rbp_right_get(a_type, a_field, rbp_r_c) \ - == &(a_tree)->rbt_nil) { \ - /* Delete leaf node. */\ - if (rbp_left_get(a_type, a_field, rbp_r_c) \ - != &(a_tree)->rbt_nil) { \ - rbp_lean_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - &(a_tree)->rbt_nil); \ - } else { \ - rbp_r_t = &(a_tree)->rbt_nil; \ - } \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ + a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ + if (rbtn_red_get(a_type, a_field, leftleft)) { \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (r) */\ + a_type *tnode; \ + rbtn_black_set(a_type, a_field, leftleft); \ + rbtn_rotate_right(a_type, a_field, pathp->node, \ + tnode); \ + /* Balance restored, but rotation modified */\ + /* subtree root, which may actually be the tree */\ + /* root. */\ + if (pathp == path) { \ + /* Set root. */ \ + rbtree->rbt_root = tnode; \ + } else { \ + if (pathp[-1].cmp < 0) { \ + rbtn_left_set(a_type, a_field, \ + pathp[-1].node, tnode); \ } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ + rbtn_right_set(a_type, a_field, \ + pathp[-1].node, tnode); \ } \ - break; \ - } else { \ - /* This is the node we want to delete, but we */\ - /* will instead swap it with its successor */\ - /* and delete the successor. Record enough */\ - /* information to do the swap later. */\ - /* rbp_r_xp is a_node's parent. */\ - rbp_r_xp = rbp_r_p; \ } \ - } \ - rbp_r_t = rbp_right_get(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - rbp_move_red_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t);\ - } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ - } \ - rbp_r_c = rbp_r_t; \ + return; \ } else { \ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_right_get(a_type, a_field, rbp_r_c); \ + /* || */\ + /* pathp(b) */\ + /* / \\ */\ + /* (b) (b) */\ + /* / */\ + /* (b) */\ + rbtn_red_set(a_type, a_field, left); \ } \ } \ } \ } \ - /* Update root. */\ - (a_tree)->rbt_root = rbp_left_get(a_type, a_field, &rbp_r_s); \ -} while (0) - -/* - * The rb_wrap() macro provides a convenient way to wrap functions around the - * cpp macros. The main benefits of wrapping are that 1) repeated macro - * expansion can cause code bloat, especially for rb_{insert,remove)(), and - * 2) type, linkage, comparison functions, etc. need not be specified at every - * call point. - */ - -#define rb_wrap(a_attr, a_prefix, a_tree_type, a_type, a_field, a_cmp) \ -a_attr void \ -a_prefix##new(a_tree_type *tree) { \ - rb_new(a_type, a_field, tree); \ -} \ -a_attr a_type * \ -a_prefix##first(a_tree_type *tree) { \ - a_type *ret; \ - rb_first(a_type, a_field, tree, ret); \ - return (ret); \ + /* Set root. */ \ + rbtree->rbt_root = path->node; \ + assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false); \ } \ a_attr a_type * \ -a_prefix##last(a_tree_type *tree) { \ - a_type *ret; \ - rb_last(a_type, a_field, tree, ret); \ - return (ret); \ +a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + if (node == &rbtree->rbt_nil) { \ + return (&rbtree->rbt_nil); \ + } else { \ + a_type *ret; \ + if ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type, \ + a_field, node), cb, arg)) != &rbtree->rbt_nil \ + || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } \ } \ a_attr a_type * \ -a_prefix##next(a_tree_type *tree, a_type *node) { \ - a_type *ret; \ - rb_next(a_type, a_field, a_cmp, tree, node, ret); \ - return (ret); \ +a_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + int cmp = a_cmp(start, node); \ + if (cmp < 0) { \ + a_type *ret; \ + if ((ret = a_prefix##iter_start(rbtree, start, \ + rbtn_left_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } else if (cmp > 0) { \ + return (a_prefix##iter_start(rbtree, start, \ + rbtn_right_get(a_type, a_field, node), cb, arg)); \ + } else { \ + a_type *ret; \ + if ((ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ + a_field, node), cb, arg)); \ + } \ } \ a_attr a_type * \ -a_prefix##prev(a_tree_type *tree, a_type *node) { \ +a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ + a_rbt_type *, a_type *, void *), void *arg) { \ a_type *ret; \ - rb_prev(a_type, a_field, a_cmp, tree, node, ret); \ + if (start != NULL) { \ + ret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root, \ + cb, arg); \ + } else { \ + ret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\ + } \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ + } \ return (ret); \ } \ a_attr a_type * \ -a_prefix##search(a_tree_type *tree, a_type *key) { \ - a_type *ret; \ - rb_search(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ +a_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ + if (node == &rbtree->rbt_nil) { \ + return (&rbtree->rbt_nil); \ + } else { \ + a_type *ret; \ + if ((ret = a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_right_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } \ } \ a_attr a_type * \ -a_prefix##nsearch(a_tree_type *tree, a_type *key) { \ - a_type *ret; \ - rb_nsearch(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ +a_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start, \ + a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \ + void *arg) { \ + int cmp = a_cmp(start, node); \ + if (cmp > 0) { \ + a_type *ret; \ + if ((ret = a_prefix##reverse_iter_start(rbtree, start, \ + rbtn_right_get(a_type, a_field, node), cb, arg)) != \ + &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } else if (cmp < 0) { \ + return (a_prefix##reverse_iter_start(rbtree, start, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } else { \ + a_type *ret; \ + if ((ret = cb(rbtree, node, arg)) != NULL) { \ + return (ret); \ + } \ + return (a_prefix##reverse_iter_recurse(rbtree, \ + rbtn_left_get(a_type, a_field, node), cb, arg)); \ + } \ } \ a_attr a_type * \ -a_prefix##psearch(a_tree_type *tree, a_type *key) { \ +a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ + a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ a_type *ret; \ - rb_psearch(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ -} \ -a_attr void \ -a_prefix##insert(a_tree_type *tree, a_type *node) { \ - rb_insert(a_type, a_field, a_cmp, tree, node); \ -} \ -a_attr void \ -a_prefix##remove(a_tree_type *tree, a_type *node) { \ - rb_remove(a_type, a_field, a_cmp, tree, node); \ -} - -/* - * The iterators simulate recursion via an array of pointers that store the - * current path. This is critical to performance, since a series of calls to - * rb_{next,prev}() would require time proportional to (n lg n), whereas this - * implementation only requires time proportional to (n). - * - * Since the iterators cache a path down the tree, any tree modification may - * cause the cached path to become invalid. In order to continue iteration, - * use something like the following sequence: - * - * { - * a_type *node, *tnode; - * - * rb_foreach_begin(a_type, a_field, a_tree, node) { - * ... - * rb_next(a_type, a_field, a_cmp, a_tree, node, tnode); - * rb_remove(a_type, a_field, a_cmp, a_tree, node); - * rb_foreach_next(a_type, a_field, a_cmp, a_tree, tnode); - * ... - * } rb_foreach_end(a_type, a_field, a_tree, node) - * } - * - * Note that this idiom is not advised if every iteration modifies the tree, - * since in that case there is no algorithmic complexity improvement over a - * series of rb_{next,prev}() calls, thus making the setup overhead wasted - * effort. - */ - -#define rb_foreach_begin(a_type, a_field, a_tree, a_var) { \ - /* Compute the maximum possible tree depth (3X the black height). */\ - unsigned rbp_f_height; \ - rbp_black_height(a_type, a_field, a_tree, rbp_f_height); \ - rbp_f_height *= 3; \ - { \ - /* Initialize the path to contain the left spine. */\ - a_type *rbp_f_path[rbp_f_height]; \ - a_type *rbp_f_node; \ - bool rbp_f_synced = false; \ - unsigned rbp_f_depth = 0; \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = (a_tree)->rbt_root; \ - rbp_f_depth++; \ - while ((rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } \ - /* While the path is non-empty, iterate. */\ - while (rbp_f_depth > 0) { \ - (a_var) = rbp_f_path[rbp_f_depth-1]; - -/* Only use if modifying the tree during iteration. */ -#define rb_foreach_next(a_type, a_field, a_cmp, a_tree, a_node) \ - /* Re-initialize the path to contain the path to a_node. */\ - rbp_f_depth = 0; \ - if (a_node != NULL) { \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = (a_tree)->rbt_root; \ - rbp_f_depth++; \ - rbp_f_node = rbp_f_path[0]; \ - while (true) { \ - int rbp_f_cmp = (a_cmp)((a_node), \ - rbp_f_path[rbp_f_depth-1]); \ - if (rbp_f_cmp < 0) { \ - rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]); \ - } else if (rbp_f_cmp > 0) { \ - rbp_f_node = rbp_right_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]); \ - } else { \ - break; \ - } \ - assert(rbp_f_node != &(a_tree)->rbt_nil); \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } \ - } \ - rbp_f_synced = true; - -#define rb_foreach_end(a_type, a_field, a_tree, a_var) \ - if (rbp_f_synced) { \ - rbp_f_synced = false; \ - continue; \ - } \ - /* Find the successor. */\ - if ((rbp_f_node = rbp_right_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - /* The successor is the left-most node in the right */\ - /* subtree. */\ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - while ((rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } else { \ - /* The successor is above the current node. Unwind */\ - /* until a left-leaning edge is removed from the */\ - /* path, or the path is empty. */\ - for (rbp_f_depth--; rbp_f_depth > 0; rbp_f_depth--) { \ - if (rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]) \ - == rbp_f_path[rbp_f_depth]) { \ - break; \ - } \ - } \ - } \ - } \ + if (start != NULL) { \ + ret = a_prefix##reverse_iter_start(rbtree, start, \ + rbtree->rbt_root, cb, arg); \ + } else { \ + ret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root, \ + cb, arg); \ } \ -} - -#define rb_foreach_reverse_begin(a_type, a_field, a_tree, a_var) { \ - /* Compute the maximum possible tree depth (3X the black height). */\ - unsigned rbp_fr_height; \ - rbp_black_height(a_type, a_field, a_tree, rbp_fr_height); \ - rbp_fr_height *= 3; \ - { \ - /* Initialize the path to contain the right spine. */\ - a_type *rbp_fr_path[rbp_fr_height]; \ - a_type *rbp_fr_node; \ - bool rbp_fr_synced = false; \ - unsigned rbp_fr_depth = 0; \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = (a_tree)->rbt_root; \ - rbp_fr_depth++; \ - while ((rbp_fr_node = rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } \ - /* While the path is non-empty, iterate. */\ - while (rbp_fr_depth > 0) { \ - (a_var) = rbp_fr_path[rbp_fr_depth-1]; - -/* Only use if modifying the tree during iteration. */ -#define rb_foreach_reverse_prev(a_type, a_field, a_cmp, a_tree, a_node) \ - /* Re-initialize the path to contain the path to a_node. */\ - rbp_fr_depth = 0; \ - if (a_node != NULL) { \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = (a_tree)->rbt_root; \ - rbp_fr_depth++; \ - rbp_fr_node = rbp_fr_path[0]; \ - while (true) { \ - int rbp_fr_cmp = (a_cmp)((a_node), \ - rbp_fr_path[rbp_fr_depth-1]); \ - if (rbp_fr_cmp < 0) { \ - rbp_fr_node = rbp_left_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1]); \ - } else if (rbp_fr_cmp > 0) { \ - rbp_fr_node = rbp_right_get(a_type, a_field,\ - rbp_fr_path[rbp_fr_depth-1]); \ - } else { \ - break; \ - } \ - assert(rbp_fr_node != &(a_tree)->rbt_nil); \ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } \ - } \ - rbp_fr_synced = true; - -#define rb_foreach_reverse_end(a_type, a_field, a_tree, a_var) \ - if (rbp_fr_synced) { \ - rbp_fr_synced = false; \ - continue; \ - } \ - if (rbp_fr_depth == 0) { \ - /* rb_foreach_reverse_sync() was called with a NULL */\ - /* a_node. */\ - break; \ - } \ - /* Find the predecessor. */\ - if ((rbp_fr_node = rbp_left_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) { \ - /* The predecessor is the right-most node in the left */\ - /* subtree. */\ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - while ((rbp_fr_node = rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) {\ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } else { \ - /* The predecessor is above the current node. Unwind */\ - /* until a right-leaning edge is removed from the */\ - /* path, or the path is empty. */\ - for (rbp_fr_depth--; rbp_fr_depth > 0; rbp_fr_depth--) {\ - if (rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1]) \ - == rbp_fr_path[rbp_fr_depth]) { \ - break; \ - } \ - } \ - } \ - } \ + if (ret == &rbtree->rbt_nil) { \ + ret = NULL; \ } \ + return (ret); \ } #endif /* RB_H_ */ diff --git a/lib/libc/stdlib/strfmon.c b/lib/libc/stdlib/strfmon.c index f12c8de..6c28254 100644 --- a/lib/libc/stdlib/strfmon.c +++ b/lib/libc/stdlib/strfmon.c @@ -413,7 +413,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *cs_precedes = lc->int_n_cs_precedes; *sep_by_space = lc->int_n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->int_n_sign_posn; - *signstr = (lc->negative_sign == '\0') ? "-" + *signstr = (lc->negative_sign[0] == '\0') ? "-" : lc->negative_sign; } else if (flags & USE_INTL_CURRENCY) { *cs_precedes = lc->int_p_cs_precedes; @@ -424,7 +424,7 @@ __setup_vars(int flags, char *cs_precedes, char *sep_by_space, *cs_precedes = lc->n_cs_precedes; *sep_by_space = lc->n_sep_by_space; *sign_posn = (flags & PARENTH_POSN) ? 0 : lc->n_sign_posn; - *signstr = (lc->negative_sign == '\0') ? "-" + *signstr = (lc->negative_sign[0] == '\0') ? "-" : lc->negative_sign; } else { *cs_precedes = lc->p_cs_precedes; diff --git a/lib/libc/stdlib/system.c b/lib/libc/stdlib/system.c index 08de630..4f47edf 100644 --- a/lib/libc/stdlib/system.c +++ b/lib/libc/stdlib/system.c @@ -46,8 +46,7 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" int -__system(command) - const char *command; +__system(const char *command) { pid_t pid, savedpid; int pstat; diff --git a/lib/libc/stdtime/Makefile.inc b/lib/libc/stdtime/Makefile.inc index a58a8a2..a039bc9 100644 --- a/lib/libc/stdtime/Makefile.inc +++ b/lib/libc/stdtime/Makefile.inc @@ -1,13 +1,16 @@ # Makefile.inc,v 1.2 1994/09/13 21:26:01 wollman Exp # $FreeBSD$ -.PATH: ${.CURDIR}/stdtime ${.CURDIR}/../locale +.PATH: ${.CURDIR}/stdtime ${.CURDIR}/../locale \ + ${.CURDIR}/../../contrib/tzcode/stdtime SRCS+= asctime.c difftime.c localtime.c strftime.c strptime.c timelocal.c \ time32.c SYM_MAPS+= ${.CURDIR}/stdtime/Symbol.map +CFLAGS+= -I${.CURDIR}/../../contrib/tzcode/stdtime -I${.CURDIR}/stdtime + MAN+= ctime.3 strftime.3 strptime.3 time2posix.3 MAN+= tzfile.5 diff --git a/lib/libc/stdtime/asctime.c b/lib/libc/stdtime/asctime.c deleted file mode 100644 index 30606f1..0000000 --- a/lib/libc/stdtime/asctime.c +++ /dev/null @@ -1,142 +0,0 @@ -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -/* -** Avoid the temptation to punt entirely to strftime; -** the output of strftime is supposed to be locale specific -** whereas the output of asctime is supposed to be constant. -*/ - -#include <sys/cdefs.h> -#ifndef lint -#ifndef NOID -static char elsieid[] __unused = "@(#)asctime.c 8.2"; -#endif /* !defined NOID */ -#endif /* !defined lint */ -__FBSDID("$FreeBSD$"); - -/*LINTLIBRARY*/ - -#include "namespace.h" -#include "private.h" -#include "un-namespace.h" -#include "tzfile.h" - -/* -** Some systems only handle "%.2d"; others only handle "%02d"; -** "%02.2d" makes (most) everybody happy. -** At least some versions of gcc warn about the %02.2d; -** we conditionalize below to avoid the warning. -*/ -/* -** All years associated with 32-bit time_t values are exactly four digits long; -** some years associated with 64-bit time_t values are not. -** Vintage programs are coded for years that are always four digits long -** and may assume that the newline always lands in the same place. -** For years that are less than four digits, we pad the output with -** leading zeroes to get the newline in the traditional place. -** The -4 ensures that we get four characters of output even if -** we call a strftime variant that produces fewer characters for some years. -** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year, -** but many implementations pad anyway; most likely the standards are buggy. -*/ -#ifdef __GNUC__ -#define ASCTIME_FMT "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n" -#else /* !defined __GNUC__ */ -#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n" -#endif /* !defined __GNUC__ */ -/* -** For years that are more than four digits we put extra spaces before the year -** so that code trying to overwrite the newline won't end up overwriting -** a digit within a year and truncating the year (operating on the assumption -** that no output is better than wrong output). -*/ -#ifdef __GNUC__ -#define ASCTIME_FMT_B "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %s\n" -#else /* !defined __GNUC__ */ -#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n" -#endif /* !defined __GNUC__ */ - -#define STD_ASCTIME_BUF_SIZE 26 -/* -** Big enough for something such as -** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n -** (two three-character abbreviations, five strings denoting integers, -** seven explicit spaces, two explicit colons, a newline, -** and a trailing ASCII nul). -** The values above are for systems where an int is 32 bits and are provided -** as an example; the define below calculates the maximum for the system at -** hand. -*/ -#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1) - -static char buf_asctime[MAX_ASCTIME_BUF_SIZE]; - -/* -** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition. -*/ - -char * -asctime_r(timeptr, buf) -const struct tm * timeptr; -char * buf; -{ - static const char wday_name[][3] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char mon_name[][3] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - const char * wn; - const char * mn; - char year[INT_STRLEN_MAXIMUM(int) + 2]; - char result[MAX_ASCTIME_BUF_SIZE]; - - if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) - wn = "???"; - else wn = wday_name[timeptr->tm_wday]; - if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) - mn = "???"; - else mn = mon_name[timeptr->tm_mon]; - /* - ** Use strftime's %Y to generate the year, to avoid overflow problems - ** when computing timeptr->tm_year + TM_YEAR_BASE. - ** Assume that strftime is unaffected by other out-of-range members - ** (e.g., timeptr->tm_mday) when processing "%Y". - */ - (void) strftime(year, sizeof year, "%Y", timeptr); - /* - ** We avoid using snprintf since it's not available on all systems. - */ - (void) sprintf(result, - ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), - wn, mn, - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - year); - if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) { - (void) strcpy(buf, result); - return buf; - } else { -#ifdef EOVERFLOW - errno = EOVERFLOW; -#else /* !defined EOVERFLOW */ - errno = EINVAL; -#endif /* !defined EOVERFLOW */ - return NULL; - } -} - -/* -** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition. -*/ - -char * -asctime(timeptr) -const struct tm * timeptr; -{ - return asctime_r(timeptr, buf_asctime); -} diff --git a/lib/libc/stdtime/ctime.3 b/lib/libc/stdtime/ctime.3 deleted file mode 100644 index 143bc2c..0000000 --- a/lib/libc/stdtime/ctime.3 +++ /dev/null @@ -1,374 +0,0 @@ -.\" Copyright (c) 1989, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Arthur Olson. -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" From: @(#)ctime.3 8.1 (Berkeley) 6/4/93 -.\" $FreeBSD$ -.\" -.Dd January 2, 1999 -.Dt CTIME 3 -.Os -.Sh NAME -.Nm asctime , -.Nm asctime_r , -.Nm ctime , -.Nm ctime_r , -.Nm difftime , -.Nm gmtime , -.Nm gmtime_r , -.Nm localtime , -.Nm localtime_r , -.Nm mktime , -.Nm timegm -.Nd transform binary date and time values -.Sh LIBRARY -.Lb libc -.Sh SYNOPSIS -.In time.h -.Vt extern char *tzname[2] ; -.Ft char * -.Fn ctime "const time_t *clock" -.Ft double -.Fn difftime "time_t time1" "time_t time0" -.Ft char * -.Fn asctime "const struct tm *tm" -.Ft struct tm * -.Fn localtime "const time_t *clock" -.Ft struct tm * -.Fn gmtime "const time_t *clock" -.Ft time_t -.Fn mktime "struct tm *tm" -.Ft time_t -.Fn timegm "struct tm *tm" -.Ft char * -.Fn ctime_r "const time_t *clock" "char *buf" -.Ft struct tm * -.Fn localtime_r "const time_t *clock" "struct tm *result" -.Ft struct tm * -.Fn gmtime_r "const time_t *clock" "struct tm *result" -.Ft char * -.Fn asctime_r "const struct tm *tm" "char *buf" -.Sh DESCRIPTION -The functions -.Fn ctime , -.Fn gmtime -and -.Fn localtime -all take as an argument a time value representing the time in seconds since -the Epoch (00:00:00 -.Tn UTC , -January 1, 1970; see -.Xr time 3 ) . -.Pp -The function -.Fn localtime -converts the time value pointed at by -.Fa clock , -and returns a pointer to a -.Dq Fa struct tm -(described below) which contains -the broken-out time information for the value after adjusting for the current -time zone (and any other factors such as Daylight Saving Time). -Time zone adjustments are performed as specified by the -.Ev TZ -environment variable (see -.Xr tzset 3 ) . -The function -.Fn localtime -uses -.Xr tzset 3 -to initialize time conversion information if -.Xr tzset 3 -has not already been called by the process. -.Pp -After filling in the tm structure, -.Fn localtime -sets the -.Fa tm_isdst Ns 'th -element of -.Fa tzname -to a pointer to an -.Tn ASCII -string that is the time zone abbreviation to be -used with -.Fn localtime Ns 's -return value. -.Pp -The function -.Fn gmtime -similarly converts the time value, but without any time zone adjustment, -and returns a pointer to a tm structure (described below). -.Pp -The -.Fn ctime -function -adjusts the time value for the current time zone in the same manner as -.Fn localtime , -and returns a pointer to a 26-character string of the form: -.Bd -literal -offset indent -Thu Nov 24 18:22:48 1986\en\e0 -.Ed -.Pp -All the fields have constant width. -.Pp -The -.Fn ctime_r -function -provides the same functionality as -.Fn ctime -except the caller must provide the output buffer -.Fa buf -to store the result, which must be at least 26 characters long. -The -.Fn localtime_r -and -.Fn gmtime_r -functions -provide the same functionality as -.Fn localtime -and -.Fn gmtime -respectively, except the caller must provide the output buffer -.Fa result . -.Pp -The -.Fn asctime -function -converts the broken down time in the structure -.Fa tm -pointed at by -.Fa *tm -to the form -shown in the example above. -.Pp -The -.Fn asctime_r -function -provides the same functionality as -.Fn asctime -except the caller provide the output buffer -.Fa buf -to store the result, which must be at least 26 characters long. -.Pp -The functions -.Fn mktime -and -.Fn timegm -convert the broken-down time in the structure -pointed to by tm into a time value with the same encoding as that of the -values returned by the -.Xr time 3 -function (that is, seconds from the Epoch, -.Tn UTC ) . -The -.Fn mktime -function -interprets the input structure according to the current timezone setting -(see -.Xr tzset 3 ) . -The -.Fn timegm -function -interprets the input structure as representing Universal Coordinated Time -.Pq Tn UTC . -.Pp -The original values of the -.Fa tm_wday -and -.Fa tm_yday -components of the structure are ignored, and the original values of the -other components are not restricted to their normal ranges, and will be -normalized if needed. -For example, -October 40 is changed into November 9, -a -.Fa tm_hour -of \-1 means 1 hour before midnight, -.Fa tm_mday -of 0 means the day preceding the current month, and -.Fa tm_mon -of \-2 means 2 months before January of -.Fa tm_year . -(A positive or zero value for -.Fa tm_isdst -causes -.Fn mktime -to presume initially that summer time (for example, Daylight Saving Time) -is or is not in effect for the specified time, respectively. -A negative value for -.Fa tm_isdst -causes the -.Fn mktime -function to attempt to divine whether summer time is in effect for the -specified time. -The -.Fa tm_isdst -and -.Fa tm_gmtoff -members are forced to zero by -.Fn timegm . ) -.Pp -On successful completion, the values of the -.Fa tm_wday -and -.Fa tm_yday -components of the structure are set appropriately, and the other components -are set to represent the specified calendar time, but with their values -forced to their normal ranges; the final value of -.Fa tm_mday -is not set until -.Fa tm_mon -and -.Fa tm_year -are determined. -The -.Fn mktime -function -returns the specified calendar time; if the calendar time cannot be -represented, it returns \-1; -.Pp -The -.Fn difftime -function -returns the difference between two calendar times, -.Pf ( Fa time1 -- -.Fa time0 ) , -expressed in seconds. -.Pp -External declarations as well as the tm structure definition are in the -.In time.h -include file. -The tm structure includes at least the following fields: -.Bd -literal -offset indent -int tm_sec; /\(** seconds (0 - 60) \(**/ -int tm_min; /\(** minutes (0 - 59) \(**/ -int tm_hour; /\(** hours (0 - 23) \(**/ -int tm_mday; /\(** day of month (1 - 31) \(**/ -int tm_mon; /\(** month of year (0 - 11) \(**/ -int tm_year; /\(** year \- 1900 \(**/ -int tm_wday; /\(** day of week (Sunday = 0) \(**/ -int tm_yday; /\(** day of year (0 - 365) \(**/ -int tm_isdst; /\(** is summer time in effect? \(**/ -char \(**tm_zone; /\(** abbreviation of timezone name \(**/ -long tm_gmtoff; /\(** offset from UTC in seconds \(**/ -.Ed -.Pp -The -field -.Fa tm_isdst -is non-zero if summer time is in effect. -.Pp -The field -.Fa tm_gmtoff -is the offset (in seconds) of the time represented from -.Tn UTC , -with positive -values indicating east of the Prime Meridian. -.Sh SEE ALSO -.Xr date 1 , -.Xr gettimeofday 2 , -.Xr getenv 3 , -.Xr time 3 , -.Xr tzset 3 , -.Xr tzfile 5 -.Sh STANDARDS -The -.Fn asctime , -.Fn ctime , -.Fn difftime , -.Fn gmtime , -.Fn localtime , -and -.Fn mktime -functions conform to -.St -isoC , -and conform to -.St -p1003.1-96 -provided the selected local timezone does not contain a leap-second table -(see -.Xr zic 8 ) . -.Pp -The -.Fn asctime_r , -.Fn ctime_r , -.Fn gmtime_r , -and -.Fn localtime_r -functions are expected to conform to -.St -p1003.1-96 -(again provided the selected local timezone does not contain a leap-second -table). -.Pp -The -.Fn timegm -function is not specified by any standard; its function cannot be -completely emulated using the standard functions described above. -.Sh HISTORY -This manual page is derived from -the time package contributed to Berkeley by -.An Arthur Olson -and which appeared in -.Bx 4.3 . -.Sh BUGS -Except for -.Fn difftime , -.Fn mktime , -and the -.Fn \&_r -variants of the other functions, -these functions leaves their result in an internal static object and return -a pointer to that object. -Subsequent calls to these -function will modify the same object. -.Pp -The C Standard provides no mechanism for a program to modify its current -local timezone setting, and the -.Tn POSIX Ns No \&-standard -method is not reentrant. -(However, thread-safe implementations are provided -in the -.Tn POSIX -threaded environment.) -.Pp -The -.Va tm_zone -field of a returned -.Vt tm -structure points to a static array of characters, -which will also be overwritten by any subsequent calls (as well as by -subsequent calls to -.Xr tzset 3 -and -.Xr tzsetwall 3 ) . -.Pp -Use of the external variable -.Fa tzname -is discouraged; the -.Fa tm_zone -entry in the tm structure is preferred. diff --git a/lib/libc/stdtime/difftime.c b/lib/libc/stdtime/difftime.c deleted file mode 100644 index d16f9a0..0000000 --- a/lib/libc/stdtime/difftime.c +++ /dev/null @@ -1,69 +0,0 @@ -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -#include <sys/cdefs.h> -#ifndef lint -#ifndef NOID -static char elsieid[] __unused = "@(#)difftime.c 8.1"; -#endif /* !defined NOID */ -#endif /* !defined lint */ -__FBSDID("$FreeBSD$"); - -/*LINTLIBRARY*/ - -#include "namespace.h" -#include "private.h" /* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */ -#include "un-namespace.h" - -double -difftime(time1, time0) -const time_t time1; -const time_t time0; -{ - /* - ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract - ** (assuming that the larger type has more precision). - ** This is the common real-world case circa 2004. - */ - if (sizeof (double) > sizeof (time_t)) - return (double) time1 - (double) time0; - if (!TYPE_INTEGRAL(time_t)) { - /* - ** time_t is floating. - */ - return time1 - time0; - } - if (!TYPE_SIGNED(time_t)) { - /* - ** time_t is integral and unsigned. - ** The difference of two unsigned values can't overflow - ** if the minuend is greater than or equal to the subtrahend. - */ - if (time1 >= time0) - return time1 - time0; - else return -((double) (time0 - time1)); - } - /* - ** time_t is integral and signed. - ** Handle cases where both time1 and time0 have the same sign - ** (meaning that their difference cannot overflow). - */ - if ((time1 < 0) == (time0 < 0)) - return time1 - time0; - /* - ** time1 and time0 have opposite signs. - ** Punt if unsigned long is too narrow. - */ - if (sizeof (unsigned long) < sizeof (time_t)) - return (double) time1 - (double) time0; - /* - ** Stay calm...decent optimizers will eliminate the complexity below. - */ - if (time1 >= 0 /* && time0 < 0 */) - return (unsigned long) time1 + - (unsigned long) (-(time0 + 1)) + 1; - return -(double) ((unsigned long) time0 + - (unsigned long) (-(time1 + 1)) + 1); -} diff --git a/lib/libc/stdtime/localtime.c b/lib/libc/stdtime/localtime.c deleted file mode 100644 index 9fc5f3e..0000000 --- a/lib/libc/stdtime/localtime.c +++ /dev/null @@ -1,2248 +0,0 @@ -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -#include <sys/cdefs.h> -#ifndef lint -#ifndef NOID -static char elsieid[] __unused = "@(#)localtime.c 8.9"; -#endif /* !defined NOID */ -#endif /* !defined lint */ -__FBSDID("$FreeBSD$"); - -/* -** Leap second handling from Bradley White. -** POSIX-style TZ environment variable handling from Guy Harris. -*/ - -/*LINTLIBRARY*/ - -#include "namespace.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <fcntl.h> -#include <pthread.h> -#include "private.h" -#include "un-namespace.h" - -#include "tzfile.h" -#include "float.h" /* for FLT_MAX and DBL_MAX */ - -#ifndef TZ_ABBR_MAX_LEN -#define TZ_ABBR_MAX_LEN 16 -#endif /* !defined TZ_ABBR_MAX_LEN */ - -#ifndef TZ_ABBR_CHAR_SET -#define TZ_ABBR_CHAR_SET \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" -#endif /* !defined TZ_ABBR_CHAR_SET */ - -#ifndef TZ_ABBR_ERR_CHAR -#define TZ_ABBR_ERR_CHAR '_' -#endif /* !defined TZ_ABBR_ERR_CHAR */ - -#include "libc_private.h" - -#define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) -#define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) - -#define _RWLOCK_RDLOCK(x) \ - do { \ - if (__isthreaded) _pthread_rwlock_rdlock(x); \ - } while (0) - -#define _RWLOCK_WRLOCK(x) \ - do { \ - if (__isthreaded) _pthread_rwlock_wrlock(x); \ - } while (0) - -#define _RWLOCK_UNLOCK(x) \ - do { \ - if (__isthreaded) _pthread_rwlock_unlock(x); \ - } while (0) - -/* -** SunOS 4.1.1 headers lack O_BINARY. -*/ - -#ifdef O_BINARY -#define OPEN_MODE (O_RDONLY | O_BINARY) -#endif /* defined O_BINARY */ -#ifndef O_BINARY -#define OPEN_MODE O_RDONLY -#endif /* !defined O_BINARY */ - -#ifndef WILDABBR -/* -** Someone might make incorrect use of a time zone abbreviation: -** 1. They might reference tzname[0] before calling tzset (explicitly -** or implicitly). -** 2. They might reference tzname[1] before calling tzset (explicitly -** or implicitly). -** 3. They might reference tzname[1] after setting to a time zone -** in which Daylight Saving Time is never observed. -** 4. They might reference tzname[0] after setting to a time zone -** in which Standard Time is never observed. -** 5. They might reference tm.TM_ZONE after calling offtime. -** What's best to do in the above cases is open to debate; -** for now, we just set things up so that in any of the five cases -** WILDABBR is used. Another possibility: initialize tzname[0] to the -** string "tzname[0] used before set", and similarly for the other cases. -** And another: initialize tzname[0] to "ERA", with an explanation in the -** manual page of what this "time zone abbreviation" means (doing this so -** that tzname[0] has the "normal" length of three characters). -*/ -#define WILDABBR " " -#endif /* !defined WILDABBR */ - -static char wildabbr[] = WILDABBR; - -/* - * In June 2004 it was decided UTC was a more appropriate default time - * zone than GMT. - */ - -static const char gmt[] = "UTC"; - -/* -** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. -** We default to US rules as of 1999-08-17. -** POSIX 1003.1 section 8.1.1 says that the default DST rules are -** implementation dependent; for historical reasons, US rules are a -** common default. -*/ -#ifndef TZDEFRULESTRING -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" -#endif /* !defined TZDEFDST */ - -struct ttinfo { /* time type information */ - long tt_gmtoff; /* UTC offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UTC */ -}; - -struct lsinfo { /* leap second information */ - time_t ls_trans; /* transition time */ - long ls_corr; /* correction to apply */ -}; - -#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) - -#ifdef TZNAME_MAX -#define MY_TZNAME_MAX TZNAME_MAX -#endif /* defined TZNAME_MAX */ -#ifndef TZNAME_MAX -#define MY_TZNAME_MAX 255 -#endif /* !defined TZNAME_MAX */ - -struct state { - int leapcnt; - int timecnt; - int typecnt; - int charcnt; - int goback; - int goahead; - time_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; - struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), - (2 * (MY_TZNAME_MAX + 1)))]; - struct lsinfo lsis[TZ_MAX_LEAPS]; -}; - -struct rule { - int r_type; /* type of rule--see below */ - int r_day; /* day number of rule */ - int r_week; /* week number of rule */ - int r_mon; /* month number of rule */ - long r_time; /* transition time of rule */ -}; - -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ - -/* -** Prototypes for static functions. -*/ - -static long detzcode(const char * codep); -static time_t detzcode64(const char * codep); -static int differ_by_repeat(time_t t1, time_t t0); -static const char * getzname(const char * strp); -static const char * getqzname(const char * strp, const int delim); -static const char * getnum(const char * strp, int * nump, int min, - int max); -static const char * getsecs(const char * strp, long * secsp); -static const char * getoffset(const char * strp, long * offsetp); -static const char * getrule(const char * strp, struct rule * rulep); -static void gmtload(struct state * sp); -static struct tm * gmtsub(const time_t * timep, long offset, - struct tm * tmp); -static struct tm * localsub(const time_t * timep, long offset, - struct tm * tmp); -static int increment_overflow(int * number, int delta); -static int leaps_thru_end_of(int y); -static int long_increment_overflow(long * number, int delta); -static int long_normalize_overflow(long * tensptr, - int * unitsptr, int base); -static int normalize_overflow(int * tensptr, int * unitsptr, - int base); -static void settzname(void); -static time_t time1(struct tm * tmp, - struct tm * (*funcp)(const time_t *, - long, struct tm *), - long offset); -static time_t time2(struct tm *tmp, - struct tm * (*funcp)(const time_t *, - long, struct tm*), - long offset, int * okayp); -static time_t time2sub(struct tm *tmp, - struct tm * (*funcp)(const time_t *, - long, struct tm*), - long offset, int * okayp, int do_norm_secs); -static struct tm * timesub(const time_t * timep, long offset, - const struct state * sp, struct tm * tmp); -static int tmcomp(const struct tm * atmp, - const struct tm * btmp); -static time_t transtime(time_t janfirst, int year, - const struct rule * rulep, long offset); -static int typesequiv(const struct state * sp, int a, int b); -static int tzload(const char * name, struct state * sp, - int doextend); -static int tzparse(const char * name, struct state * sp, - int lastditch); - -#ifdef ALL_STATE -static struct state * lclptr; -static struct state * gmtptr; -#endif /* defined ALL_STATE */ - -#ifndef ALL_STATE -static struct state lclmem; -static struct state gmtmem; -#define lclptr (&lclmem) -#define gmtptr (&gmtmem) -#endif /* State Farm */ - -#ifndef TZ_STRLEN_MAX -#define TZ_STRLEN_MAX 255 -#endif /* !defined TZ_STRLEN_MAX */ - -static char lcl_TZname[TZ_STRLEN_MAX + 1]; -static int lcl_is_set; -static int gmt_is_set; -static pthread_rwlock_t lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER; -static pthread_mutex_t gmt_mutex = PTHREAD_MUTEX_INITIALIZER; - -char * tzname[2] = { - wildabbr, - wildabbr -}; - -/* -** Section 4.12.3 of X3.159-1989 requires that -** Except for the strftime function, these functions [asctime, -** ctime, gmtime, localtime] return values in one of two static -** objects: a broken-down time structure and an array of char. -** Thanks to Paul Eggert for noting this. -*/ - -static struct tm tm; - -#ifdef USG_COMPAT -time_t timezone = 0; -int daylight = 0; -#endif /* defined USG_COMPAT */ - -#ifdef ALTZONE -time_t altzone = 0; -#endif /* defined ALTZONE */ - -static long -detzcode(codep) -const char * const codep; -{ - long result; - int i; - - result = (codep[0] & 0x80) ? ~0L : 0; - for (i = 0; i < 4; ++i) - result = (result << 8) | (codep[i] & 0xff); - return result; -} - -static time_t -detzcode64(codep) -const char * const codep; -{ - register time_t result; - register int i; - - result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0; - for (i = 0; i < 8; ++i) - result = result * 256 + (codep[i] & 0xff); - return result; -} - -static void -settzname(void) -{ - struct state * sp = lclptr; - int i; - - tzname[0] = wildabbr; - tzname[1] = wildabbr; -#ifdef USG_COMPAT - daylight = 0; - timezone = 0; -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - altzone = 0; -#endif /* defined ALTZONE */ -#ifdef ALL_STATE - if (sp == NULL) { - tzname[0] = tzname[1] = gmt; - return; - } -#endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) { - const struct ttinfo * const ttisp = &sp->ttis[i]; - - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; -#ifdef USG_COMPAT - if (ttisp->tt_isdst) - daylight = 1; - if (i == 0 || !ttisp->tt_isdst) - timezone = -(ttisp->tt_gmtoff); -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); -#endif /* defined ALTZONE */ - } - /* - ** And to get the latest zone names into tzname. . . - */ - for (i = 0; i < sp->timecnt; ++i) { - const struct ttinfo * const ttisp = - &sp->ttis[ - sp->types[i]]; - - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; - } - /* - ** Finally, scrub the abbreviations. - ** First, replace bogus characters. - */ - for (i = 0; i < sp->charcnt; ++i) - if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL) - sp->chars[i] = TZ_ABBR_ERR_CHAR; - /* - ** Second, truncate long abbreviations. - */ - for (i = 0; i < sp->typecnt; ++i) { - register const struct ttinfo * const ttisp = &sp->ttis[i]; - register char * cp = &sp->chars[ttisp->tt_abbrind]; - - if (strlen(cp) > TZ_ABBR_MAX_LEN && - strcmp(cp, GRANDPARENTED) != 0) - *(cp + TZ_ABBR_MAX_LEN) = '\0'; - } -} - -static int -differ_by_repeat(t1, t0) -const time_t t1; -const time_t t0; -{ - int_fast64_t _t0 = t0; - int_fast64_t _t1 = t1; - - if (TYPE_INTEGRAL(time_t) && - TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) - return 0; - //turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT); - return _t1 - _t0 == SECSPERREPEAT; -} - -static int -tzload(name, sp, doextend) -const char * name; -struct state * const sp; -register const int doextend; -{ - const char * p; - int i; - int fid; - int stored; - int nread; - union { - struct tzhead tzhead; - char buf[2 * sizeof(struct tzhead) + - 2 * sizeof *sp + - 4 * TZ_MAX_TIMES]; - } u; - - /* XXX The following is from OpenBSD, and I'm not sure it is correct */ - if (name != NULL && issetugid() != 0) - if ((name[0] == ':' && name[1] == '/') || - name[0] == '/' || strchr(name, '.')) - name = NULL; - if (name == NULL && (name = TZDEFAULT) == NULL) - return -1; - { - int doaccess; - struct stat stab; - /* - ** Section 4.9.1 of the C standard says that - ** "FILENAME_MAX expands to an integral constant expression - ** that is the size needed for an array of char large enough - ** to hold the longest file name string that the implementation - ** guarantees can be opened." - */ - char fullname[FILENAME_MAX + 1]; - - if (name[0] == ':') - ++name; - doaccess = name[0] == '/'; - if (!doaccess) { - if ((p = TZDIR) == NULL) - return -1; - if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname) - return -1; - (void) strcpy(fullname, p); - (void) strcat(fullname, "/"); - (void) strcat(fullname, name); - /* - ** Set doaccess if '.' (as in "../") shows up in name. - */ - if (strchr(name, '.') != NULL) - doaccess = TRUE; - name = fullname; - } - if (doaccess && access(name, R_OK) != 0) - return -1; - if ((fid = _open(name, OPEN_MODE)) == -1) - return -1; - if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) { - _close(fid); - return -1; - } - } - nread = _read(fid, u.buf, sizeof u.buf); - if (_close(fid) < 0 || nread <= 0) - return -1; - for (stored = 4; stored <= 8; stored *= 2) { - int ttisstdcnt; - int ttisgmtcnt; - - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); - p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; - if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || - sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || - sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || - sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || - (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || - (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) - return -1; - if (nread - (p - u.buf) < - sp->timecnt * stored + /* ats */ - sp->timecnt + /* types */ - sp->typecnt * 6 + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (stored + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - return -1; - for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - } - for (i = 0; i < sp->timecnt; ++i) { - sp->types[i] = (unsigned char) *p++; - if (sp->types[i] >= sp->typecnt) - return -1; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - ttisp->tt_gmtoff = detzcode(p); - p += 4; - ttisp->tt_isdst = (unsigned char) *p++; - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - return -1; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - return -1; - } - for (i = 0; i < sp->charcnt; ++i) - sp->chars[i] = *p++; - sp->chars[i] = '\0'; /* ensure '\0' at end */ - for (i = 0; i < sp->leapcnt; ++i) { - struct lsinfo * lsisp; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - return -1; - } - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - return -1; - } - } - /* - ** Out-of-sort ats should mean we're running on a - ** signed time_t system but using a data file with - ** unsigned values (or vice versa). - */ - for (i = 0; i < sp->timecnt - 2; ++i) - if (sp->ats[i] > sp->ats[i + 1]) { - ++i; - if (TYPE_SIGNED(time_t)) { - /* - ** Ignore the end (easy). - */ - sp->timecnt = i; - } else { - /* - ** Ignore the beginning (harder). - */ - register int j; - - for (j = 0; j + i < sp->timecnt; ++j) { - sp->ats[j] = sp->ats[j + i]; - sp->types[j] = sp->types[j + i]; - } - sp->timecnt = j; - } - break; - } - /* - ** If this is an old file, we're done. - */ - if (u.tzhead.tzh_version[0] == '\0') - break; - nread -= p - u.buf; - for (i = 0; i < nread; ++i) - u.buf[i] = p[i]; - /* - ** If this is a narrow integer time_t system, we're done. - */ - if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t)) - break; - } - if (doextend && nread > 2 && - u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { - struct state ts; - register int result; - - u.buf[nread - 1] = '\0'; - result = tzparse(&u.buf[1], &ts, FALSE); - if (result == 0 && ts.typecnt == 2 && - sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) { - for (i = 0; i < 2; ++i) - ts.ttis[i].tt_abbrind += - sp->charcnt; - for (i = 0; i < ts.charcnt; ++i) - sp->chars[sp->charcnt++] = - ts.chars[i]; - i = 0; - while (i < ts.timecnt && - ts.ats[i] <= - sp->ats[sp->timecnt - 1]) - ++i; - while (i < ts.timecnt && - sp->timecnt < TZ_MAX_TIMES) { - sp->ats[sp->timecnt] = - ts.ats[i]; - sp->types[sp->timecnt] = - sp->typecnt + - ts.types[i]; - ++sp->timecnt; - ++i; - } - sp->ttis[sp->typecnt++] = ts.ttis[0]; - sp->ttis[sp->typecnt++] = ts.ttis[1]; - } - } - sp->goback = sp->goahead = FALSE; - if (sp->timecnt > 1) { - for (i = 1; i < sp->timecnt; ++i) - if (typesequiv(sp, sp->types[i], sp->types[0]) && - differ_by_repeat(sp->ats[i], sp->ats[0])) { - sp->goback = TRUE; - break; - } - for (i = sp->timecnt - 2; i >= 0; --i) - if (typesequiv(sp, sp->types[sp->timecnt - 1], - sp->types[i]) && - differ_by_repeat(sp->ats[sp->timecnt - 1], - sp->ats[i])) { - sp->goahead = TRUE; - break; - } - } - return 0; -} - -static int -typesequiv(sp, a, b) -const struct state * const sp; -const int a; -const int b; -{ - register int result; - - if (sp == NULL || - a < 0 || a >= sp->typecnt || - b < 0 || b >= sp->typecnt) - result = FALSE; - else { - register const struct ttinfo * ap = &sp->ttis[a]; - register const struct ttinfo * bp = &sp->ttis[b]; - result = ap->tt_gmtoff == bp->tt_gmtoff && - ap->tt_isdst == bp->tt_isdst && - ap->tt_ttisstd == bp->tt_ttisstd && - ap->tt_ttisgmt == bp->tt_ttisgmt && - strcmp(&sp->chars[ap->tt_abbrind], - &sp->chars[bp->tt_abbrind]) == 0; - } - return result; -} - -static const int mon_lengths[2][MONSPERYEAR] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -}; - -static const int year_lengths[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - -/* -** Given a pointer into a time zone string, scan until a character that is not -** a valid character in a zone name is found. Return a pointer to that -** character. -*/ - -static const char * -getzname(strp) -const char * strp; -{ - char c; - - while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && - c != '+') - ++strp; - return strp; -} - -/* -** Given a pointer into an extended time zone string, scan until the ending -** delimiter of the zone name is located. Return a pointer to the delimiter. -** -** As with getzname above, the legal character set is actually quite -** restricted, with other characters producing undefined results. -** We don't do any checking here; checking is done later in common-case code. -*/ - -static const char * -getqzname(register const char *strp, const int delim) -{ - register int c; - - while ((c = *strp) != '\0' && c != delim) - ++strp; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a number from that string. -** Check that the number is within a specified range; if it is not, return -** NULL. -** Otherwise, return a pointer to the first character not part of the number. -*/ - -static const char * -getnum(strp, nump, min, max) -const char * strp; -int * const nump; -const int min; -const int max; -{ - char c; - int num; - - if (strp == NULL || !is_digit(c = *strp)) - return NULL; - num = 0; - do { - num = num * 10 + (c - '0'); - if (num > max) - return NULL; /* illegal value */ - c = *++strp; - } while (is_digit(c)); - if (num < min) - return NULL; /* illegal value */ - *nump = num; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a number of seconds, -** in hh[:mm[:ss]] form, from the string. -** If any error occurs, return NULL. -** Otherwise, return a pointer to the first character not part of the number -** of seconds. -*/ - -static const char * -getsecs(strp, secsp) -const char * strp; -long * const secsp; -{ - int num; - - /* - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like - ** "M10.4.6/26", which does not conform to Posix, - ** but which specifies the equivalent of - ** ``02:00 on the first Sunday on or after 23 Oct''. - */ - strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); - if (strp == NULL) - return NULL; - *secsp = num * (long) SECSPERHOUR; - if (*strp == ':') { - ++strp; - strp = getnum(strp, &num, 0, MINSPERHOUR - 1); - if (strp == NULL) - return NULL; - *secsp += num * SECSPERMIN; - if (*strp == ':') { - ++strp; - /* `SECSPERMIN' allows for leap seconds. */ - strp = getnum(strp, &num, 0, SECSPERMIN); - if (strp == NULL) - return NULL; - *secsp += num; - } - } - return strp; -} - -/* -** Given a pointer into a time zone string, extract an offset, in -** [+-]hh[:mm[:ss]] form, from the string. -** If any error occurs, return NULL. -** Otherwise, return a pointer to the first character not part of the time. -*/ - -static const char * -getoffset(strp, offsetp) -const char * strp; -long * const offsetp; -{ - int neg = 0; - - if (*strp == '-') { - neg = 1; - ++strp; - } else if (*strp == '+') - ++strp; - strp = getsecs(strp, offsetp); - if (strp == NULL) - return NULL; /* illegal time */ - if (neg) - *offsetp = -*offsetp; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a rule in the form -** date[/time]. See POSIX section 8 for the format of "date" and "time". -** If a valid rule is not found, return NULL. -** Otherwise, return a pointer to the first character not part of the rule. -*/ - -static const char * -getrule(strp, rulep) -const char * strp; -struct rule * const rulep; -{ - if (*strp == 'J') { - /* - ** Julian day. - */ - rulep->r_type = JULIAN_DAY; - ++strp; - strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); - } else if (*strp == 'M') { - /* - ** Month, week, day. - */ - rulep->r_type = MONTH_NTH_DAY_OF_WEEK; - ++strp; - strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_week, 1, 5); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); - } else if (is_digit(*strp)) { - /* - ** Day of year. - */ - rulep->r_type = DAY_OF_YEAR; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); - } else return NULL; /* invalid format */ - if (strp == NULL) - return NULL; - if (*strp == '/') { - /* - ** Time specified. - */ - ++strp; - strp = getsecs(strp, &rulep->r_time); - } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ - return strp; -} - -/* -** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the -** year, a rule, and the offset from UTC at the time that rule takes effect, -** calculate the Epoch-relative time that rule takes effect. -*/ - -static time_t -transtime(janfirst, year, rulep, offset) -const time_t janfirst; -const int year; -const struct rule * const rulep; -const long offset; -{ - int leapyear; - time_t value; - int i; - int d, m1, yy0, yy1, yy2, dow; - - INITIALIZE(value); - leapyear = isleap(year); - switch (rulep->r_type) { - - case JULIAN_DAY: - /* - ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap - ** years. - ** In non-leap years, or if the day number is 59 or less, just - ** add SECSPERDAY times the day number-1 to the time of - ** January 1, midnight, to get the day. - */ - value = janfirst + (rulep->r_day - 1) * SECSPERDAY; - if (leapyear && rulep->r_day >= 60) - value += SECSPERDAY; - break; - - case DAY_OF_YEAR: - /* - ** n - day of year. - ** Just add SECSPERDAY times the day number to the time of - ** January 1, midnight, to get the day. - */ - value = janfirst + rulep->r_day * SECSPERDAY; - break; - - case MONTH_NTH_DAY_OF_WEEK: - /* - ** Mm.n.d - nth "dth day" of month m. - */ - value = janfirst; - for (i = 0; i < rulep->r_mon - 1; ++i) - value += mon_lengths[leapyear][i] * SECSPERDAY; - - /* - ** Use Zeller's Congruence to get day-of-week of first day of - ** month. - */ - m1 = (rulep->r_mon + 9) % 12 + 1; - yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = yy0 / 100; - yy2 = yy0 % 100; - dow = ((26 * m1 - 2) / 10 + - 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; - if (dow < 0) - dow += DAYSPERWEEK; - - /* - ** "dow" is the day-of-week of the first day of the month. Get - ** the day-of-month (zero-origin) of the first "dow" day of the - ** month. - */ - d = rulep->r_day - dow; - if (d < 0) - d += DAYSPERWEEK; - for (i = 1; i < rulep->r_week; ++i) { - if (d + DAYSPERWEEK >= - mon_lengths[leapyear][rulep->r_mon - 1]) - break; - d += DAYSPERWEEK; - } - - /* - ** "d" is the day-of-month (zero-origin) of the day we want. - */ - value += d * SECSPERDAY; - break; - } - - /* - ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local - ** time on that day, add the transition time and the current offset - ** from UTC. - */ - return value + rulep->r_time + offset; -} - -/* -** Given a POSIX section 8-style TZ string, fill in the rule tables as -** appropriate. -*/ - -static int -tzparse(name, sp, lastditch) -const char * name; -struct state * const sp; -const int lastditch; -{ - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - long stdoffset; - long dstoffset; - time_t * atp; - unsigned char * typep; - char * cp; - int load_result; - - INITIALIZE(dstname); - stdname = name; - if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ - name += stdlen; - if (stdlen >= sizeof sp->chars) - stdlen = (sizeof sp->chars) - 1; - stdoffset = 0; - } else { - if (*name == '<') { - name++; - stdname = name; - name = getqzname(name, '>'); - if (*name != '>') - return (-1); - stdlen = name - stdname; - name++; - } else { - name = getzname(name); - stdlen = name - stdname; - } - if (*name == '\0') - return -1; /* was "stdoffset = 0;" */ - else { - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; - } - } - load_result = tzload(TZDEFRULES, sp, FALSE); - if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ - if (*name != '\0') { - if (*name == '<') { - dstname = ++name; - name = getqzname(name, '>'); - if (*name != '>') - return -1; - dstlen = name - dstname; - name++; - } else { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - } - if (*name != '\0' && *name != ',' && *name != ';') { - name = getoffset(name, &dstoffset); - if (name == NULL) - return -1; - } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && load_result != 0) - name = TZDEFRULESTRING; - if (*name == ',' || *name == ';') { - struct rule start; - struct rule end; - int year; - time_t janfirst; - time_t starttime; - time_t endtime; - - ++name; - if ((name = getrule(name, &start)) == NULL) - return -1; - if (*name++ != ',') - return -1; - if ((name = getrule(name, &end)) == NULL) - return -1; - if (*name != '\0') - return -1; - sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR forward. - */ - sp->ttis[0].tt_gmtoff = -dstoffset; - sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = 0; - sp->ttis[1].tt_abbrind = 0; - atp = sp->ats; - typep = sp->types; - janfirst = 0; - sp->timecnt = 0; - for (year = EPOCH_YEAR; - sp->timecnt + 2 <= TZ_MAX_TIMES; - ++year) { - time_t newfirst; - - starttime = transtime(janfirst, year, &start, - stdoffset); - endtime = transtime(janfirst, year, &end, - dstoffset); - if (starttime > endtime) { - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - } else { - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - } - sp->timecnt += 2; - newfirst = janfirst; - newfirst += year_lengths[isleap(year)] * - SECSPERDAY; - if (newfirst <= janfirst) - break; - janfirst = newfirst; - } - } else { - long theirstdoffset; - long theirdstoffset; - long theiroffset; - int isdst; - int i; - int j; - - if (*name != '\0') - return -1; - /* - ** Initial values of theirstdoffset and theirdstoffset. - */ - theirstdoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (!sp->ttis[j].tt_isdst) { - theirstdoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - theirdstoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (sp->ttis[j].tt_isdst) { - theirdstoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - /* - ** Initially we're assumed to be in standard time. - */ - isdst = FALSE; - theiroffset = theirstdoffset; - /* - ** Now juggle transition times and types - ** tracking offsets as you do. - */ - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { - /* No adjustment to transition time */ - } else { - /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. - */ - /* - ** Transitions from DST to DDST - ** will effectively disappear since - ** POSIX provides for only one DST - ** offset. - */ - if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; - } else { - sp->ats[i] += stdoffset - - theirstdoffset; - } - } - theiroffset = -sp->ttis[j].tt_gmtoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; - } - /* - ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. - */ - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = FALSE; - sp->ttis[0].tt_abbrind = 0; - sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = TRUE; - sp->ttis[1].tt_abbrind = stdlen + 1; - sp->typecnt = 2; - } - } else { - dstlen = 0; - sp->typecnt = 1; /* only standard time */ - sp->timecnt = 0; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; - } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; - cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); - cp += stdlen; - *cp++ = '\0'; - if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); - *(cp + dstlen) = '\0'; - } - return 0; -} - -static void -gmtload(sp) -struct state * const sp; -{ - if (tzload(gmt, sp, TRUE) != 0) - (void) tzparse(gmt, sp, TRUE); -} - -static void -tzsetwall_basic(int rdlocked) -{ - if (!rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); - if (lcl_is_set < 0) { - if (!rdlocked) - _RWLOCK_UNLOCK(&lcl_rwlock); - return; - } - _RWLOCK_UNLOCK(&lcl_rwlock); - - _RWLOCK_WRLOCK(&lcl_rwlock); - lcl_is_set = -1; - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - _RWLOCK_UNLOCK(&lcl_rwlock); - if (rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); - return; - } - } -#endif /* defined ALL_STATE */ - if (tzload((char *) NULL, lclptr, TRUE) != 0) - gmtload(lclptr); - settzname(); - _RWLOCK_UNLOCK(&lcl_rwlock); - - if (rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); -} - -void -tzsetwall(void) -{ - tzsetwall_basic(0); -} - -static void -tzset_basic(int rdlocked) -{ - const char * name; - - name = getenv("TZ"); - if (name == NULL) { - tzsetwall_basic(rdlocked); - return; - } - - if (!rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) { - if (!rdlocked) - _RWLOCK_UNLOCK(&lcl_rwlock); - return; - } - _RWLOCK_UNLOCK(&lcl_rwlock); - - _RWLOCK_WRLOCK(&lcl_rwlock); - lcl_is_set = strlen(name) < sizeof lcl_TZname; - if (lcl_is_set) - (void) strcpy(lcl_TZname, name); - -#ifdef ALL_STATE - if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { - settzname(); /* all we can do */ - _RWLOCK_UNLOCK(&lcl_rwlock); - if (rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); - return; - } - } -#endif /* defined ALL_STATE */ - if (*name == '\0') { - /* - ** User wants it fast rather than right. - */ - lclptr->leapcnt = 0; /* so, we're off a little */ - lclptr->timecnt = 0; - lclptr->typecnt = 0; - lclptr->ttis[0].tt_isdst = 0; - lclptr->ttis[0].tt_gmtoff = 0; - lclptr->ttis[0].tt_abbrind = 0; - (void) strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr, TRUE) != 0) - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - (void) gmtload(lclptr); - settzname(); - _RWLOCK_UNLOCK(&lcl_rwlock); - - if (rdlocked) - _RWLOCK_RDLOCK(&lcl_rwlock); -} - -void -tzset(void) -{ - tzset_basic(0); -} - -/* -** The easy way to behave "as if no library function calls" localtime -** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- -** but it *is* desirable.) -** -** The unused offset argument is for the benefit of mktime variants. -*/ - -/*ARGSUSED*/ -static struct tm * -localsub(timep, offset, tmp) -const time_t * const timep; -const long offset; -struct tm * const tmp; -{ - struct state * sp; - const struct ttinfo * ttisp; - int i; - struct tm * result; - const time_t t = *timep; - - sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) - return gmtsub(timep, offset, tmp); -#endif /* defined ALL_STATE */ - if ((sp->goback && t < sp->ats[0]) || - (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt = t; - register time_t seconds; - register time_t tcycles; - register int_fast64_t icycles; - - if (t < sp->ats[0]) - seconds = sp->ats[0] - t; - else seconds = t - sp->ats[sp->timecnt - 1]; - --seconds; - tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; - ++tcycles; - icycles = tcycles; - if (tcycles - icycles >= 1 || icycles - tcycles >= 1) - return NULL; - seconds = icycles; - seconds *= YEARSPERREPEAT; - seconds *= AVGSECSPERYEAR; - if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; - if (newt < sp->ats[0] || - newt > sp->ats[sp->timecnt - 1]) - return NULL; /* "cannot happen" */ - result = localsub(&newt, offset, tmp); - if (result == tmp) { - register time_t newy; - - newy = tmp->tm_year; - if (t < sp->ats[0]) - newy -= icycles * YEARSPERREPEAT; - else newy += icycles * YEARSPERREPEAT; - tmp->tm_year = newy; - if (tmp->tm_year != newy) - return NULL; - } - return result; - } - if (sp->timecnt == 0 || t < sp->ats[0]) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } - } else { - register int lo = 1; - register int hi = sp->timecnt; - - while (lo < hi) { - register int mid = (lo + hi) >> 1; - - if (t < sp->ats[mid]) - hi = mid; - else lo = mid + 1; - } - i = (int) sp->types[lo - 1]; - } - ttisp = &sp->ttis[i]; - /* - ** To get (wrong) behavior that's compatible with System V Release 2.0 - ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; - ** timesub(&t, 0L, sp, tmp); - */ - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; -#ifdef TM_ZONE - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; -#endif /* defined TM_ZONE */ - return result; -} - -struct tm * -localtime(timep) -const time_t * const timep; -{ - static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER; - static pthread_key_t localtime_key = -1; - struct tm *p_tm; - int r; - - if (__isthreaded != 0) { - if (localtime_key < 0) { - _pthread_mutex_lock(&localtime_mutex); - if (localtime_key < 0) { - if ((r = _pthread_key_create(&localtime_key, - free)) != 0) { - _pthread_mutex_unlock(&localtime_mutex); - errno = r; - return(NULL); - } - } - _pthread_mutex_unlock(&localtime_mutex); - } - p_tm = _pthread_getspecific(localtime_key); - if (p_tm == NULL) { - if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) - == NULL) - return(NULL); - _pthread_setspecific(localtime_key, p_tm); - } - _RWLOCK_RDLOCK(&lcl_rwlock); - tzset_basic(1); - localsub(timep, 0L, p_tm); - _RWLOCK_UNLOCK(&lcl_rwlock); - return(p_tm); - } else { - tzset_basic(0); - localsub(timep, 0L, &tm); - return(&tm); - } -} - -/* -** Re-entrant version of localtime. -*/ - -struct tm * -localtime_r(timep, tmp) -const time_t * const timep; -struct tm * tmp; -{ - _RWLOCK_RDLOCK(&lcl_rwlock); - tzset_basic(1); - localsub(timep, 0L, tmp); - _RWLOCK_UNLOCK(&lcl_rwlock); - return tmp; -} - -/* -** gmtsub is to gmtime as localsub is to localtime. -*/ - -static struct tm * -gmtsub(timep, offset, tmp) -const time_t * const timep; -const long offset; -struct tm * const tmp; -{ - register struct tm * result; - - _MUTEX_LOCK(&gmt_mutex); - if (!gmt_is_set) { -#ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - gmt_is_set = TRUE; - } - _MUTEX_UNLOCK(&gmt_mutex); - result = timesub(timep, offset, gmtptr, tmp); -#ifdef TM_ZONE - /* - ** Could get fancy here and deliver something such as - ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, - ** but this is no time for a treasure hunt. - */ - if (offset != 0) - tmp->TM_ZONE = wildabbr; - else { -#ifdef ALL_STATE - if (gmtptr == NULL) - tmp->TM_ZONE = gmt; - else tmp->TM_ZONE = gmtptr->chars; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - tmp->TM_ZONE = gmtptr->chars; -#endif /* State Farm */ - } -#endif /* defined TM_ZONE */ - return result; -} - -struct tm * -gmtime(timep) -const time_t * const timep; -{ - static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER; - static pthread_key_t gmtime_key = -1; - struct tm *p_tm; - int r; - - if (__isthreaded != 0) { - if (gmtime_key < 0) { - _pthread_mutex_lock(&gmtime_mutex); - if (gmtime_key < 0) { - if ((r = _pthread_key_create(&gmtime_key, - free)) != 0) { - _pthread_mutex_unlock(&gmtime_mutex); - errno = r; - return(NULL); - } - } - _pthread_mutex_unlock(&gmtime_mutex); - } - /* - * Changed to follow POSIX.1 threads standard, which - * is what BSD currently has. - */ - if ((p_tm = _pthread_getspecific(gmtime_key)) == NULL) { - if ((p_tm = (struct tm *)malloc(sizeof(struct tm))) - == NULL) { - return(NULL); - } - _pthread_setspecific(gmtime_key, p_tm); - } - gmtsub(timep, 0L, p_tm); - return(p_tm); - } - else { - gmtsub(timep, 0L, &tm); - return(&tm); - } -} - -/* -* Re-entrant version of gmtime. -*/ - -struct tm * -gmtime_r(timep, tmp) -const time_t * const timep; -struct tm * tmp; -{ - return gmtsub(timep, 0L, tmp); -} - -#ifdef STD_INSPIRED - -struct tm * -offtime(timep, offset) -const time_t * const timep; -const long offset; -{ - return gmtsub(timep, offset, &tm); -} - -#endif /* defined STD_INSPIRED */ - -/* -** Return the number of leap years through the end of the given year -** where, to make the math easy, the answer for year zero is defined as zero. -*/ - -static int -leaps_thru_end_of(y) -register const int y; -{ - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); -} - -static struct tm * -timesub(timep, offset, sp, tmp) -const time_t * const timep; -const long offset; -const struct state * const sp; -struct tm * const tmp; -{ - const struct lsinfo * lp; - time_t tdays; - int idays; /* unsigned would be so 2003 */ - long rem; - int y; - const int * ip; - long corr; - int hit; - int i; - - corr = 0; - hit = 0; -#ifdef ALL_STATE - i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - i = sp->leapcnt; -#endif /* State Farm */ - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } - corr = lp->ls_corr; - break; - } - } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { - int newy; - register time_t tdelta; - register int idelta; - register int leapdays; - - tdelta = tdays / DAYSPERLYEAR; - idelta = tdelta; - if (tdelta - idelta >= 1 || idelta - tdelta >= 1) - return NULL; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; - leapdays = leaps_thru_end_of(newy - 1) - - leaps_thru_end_of(y - 1); - tdays -= ((time_t) newy - y) * DAYSPERNYEAR; - tdays -= leapdays; - y = newy; - } - { - register long seconds; - - seconds = tdays * SECSPERDAY + 0.5; - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; - } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += year_lengths[isleap(y)]; - } - while (idays >= year_lengths[isleap(y)]) { - idays -= year_lengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; - tmp->tm_yday = idays; - /* - ** The "extra" mods below avoid overflow problems. - */ - tmp->tm_wday = EPOCH_WDAY + - ((y - EPOCH_YEAR) % DAYSPERWEEK) * - (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - - leaps_thru_end_of(EPOCH_YEAR - 1) + - idays; - tmp->tm_wday %= DAYSPERWEEK; - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem %= SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - ip = mon_lengths[isleap(y)]; - for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) - idays -= ip[tmp->tm_mon]; - tmp->tm_mday = (int) (idays + 1); - tmp->tm_isdst = 0; -#ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ - return tmp; -} - -char * -ctime(timep) -const time_t * const timep; -{ -/* -** Section 4.12.3.2 of X3.159-1989 requires that -** The ctime function converts the calendar time pointed to by timer -** to local time in the form of a string. It is equivalent to -** asctime(localtime(timer)) -*/ - return asctime(localtime(timep)); -} - -char * -ctime_r(timep, buf) -const time_t * const timep; -char * buf; -{ - struct tm mytm; - - return asctime_r(localtime_r(timep, &mytm), buf); -} - -/* -** Adapted from code provided by Robert Elz, who writes: -** The "best" way to do mktime I think is based on an idea of Bob -** Kridle's (so its said...) from a long time ago. -** It does a binary search of the time_t space. Since time_t's are -** just 32 bits, its a max of 32 iterations (even at 64 bits it -** would still be very reasonable). -*/ - -#ifndef WRONG -#define WRONG (-1) -#endif /* !defined WRONG */ - -/* -** Simplified normalize logic courtesy Paul Eggert. -*/ - -static int -increment_overflow(number, delta) -int * number; -int delta; -{ - int number0; - - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -} - -static int -long_increment_overflow(number, delta) -long * number; -int delta; -{ - long number0; - - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -} - -static int -normalize_overflow(tensptr, unitsptr, base) -int * const tensptr; -int * const unitsptr; -const int base; -{ - int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int -long_normalize_overflow(tensptr, unitsptr, base) -long * const tensptr; -int * const unitsptr; -const int base; -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return long_increment_overflow(tensptr, tensdelta); -} - -static int -tmcomp(atmp, btmp) -const struct tm * const atmp; -const struct tm * const btmp; -{ - int result; - - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && - (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && - (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && - (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; - return result; -} - -static time_t -time2sub(tmp, funcp, offset, okayp, do_norm_secs) -struct tm * const tmp; -struct tm * (* const funcp)(const time_t*, long, struct tm*); -const long offset; -int * const okayp; -const int do_norm_secs; -{ - const struct state * sp; - int dir; - int i, j; - int saved_seconds; - long li; - time_t lo; - time_t hi; - long y; - time_t newt; - time_t t; - struct tm yourtm, mytm; - - *okayp = FALSE; - yourtm = *tmp; - if (do_norm_secs) { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - y = yourtm.tm_year; - if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - /* - ** Turn y into an actual year number for now. - ** It is converted back to an offset from TM_YEAR_BASE later. - */ - if (long_increment_overflow(&y, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) { - if (long_increment_overflow(&y, -1)) - return WRONG; - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(li)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) { - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(li)]; - if (long_increment_overflow(&y, 1)) - return WRONG; - } - for ( ; ; ) { - i = mon_lengths[isleap(y)][yourtm.tm_mon]; - if (yourtm.tm_mday <= i) - break; - yourtm.tm_mday -= i; - if (++yourtm.tm_mon >= MONSPERYEAR) { - yourtm.tm_mon = 0; - if (long_increment_overflow(&y, 1)) - return WRONG; - } - } - if (long_increment_overflow(&y, -TM_YEAR_BASE)) - return WRONG; - yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; - /* Don't go below 1900 for POLA */ - if (yourtm.tm_year < 0) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (y + TM_YEAR_BASE < EPOCH_YEAR) { - /* - ** We can't set tm_sec to 0, because that might push the - ** time below the minimum representable time. - ** Set tm_sec to 59 instead. - ** This assumes that the minimum representable time is - ** not in the same minute that a leap second was deleted from, - ** which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } else { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - /* - ** Do a binary search (this works whatever time_t's type is). - */ - if (!TYPE_SIGNED(time_t)) { - lo = 0; - hi = lo - 1; - } else if (!TYPE_INTEGRAL(time_t)) { - if (sizeof(time_t) > sizeof(float)) - hi = (time_t) DBL_MAX; - else hi = (time_t) FLT_MAX; - lo = -hi; - } else { - lo = 1; - for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) - lo *= 2; - hi = -(lo + 1); - } - for ( ; ; ) { - t = lo / 2 + hi / 2; - if (t < lo) - t = lo; - else if (t > hi) - t = hi; - if ((*funcp)(&t, offset, &mytm) == NULL) { - /* - ** Assume that t is too extreme to be represented in - ** a struct tm; arrange things so that it is less - ** extreme on the next pass. - */ - dir = (t > 0) ? 1 : -1; - } else dir = tmcomp(&mytm, &yourtm); - if (dir != 0) { - if (t == lo) { - ++t; - if (t <= lo) - return WRONG; - ++lo; - } else if (t == hi) { - --t; - if (t >= hi) - return WRONG; - --hi; - } - if (lo > hi) - return WRONG; - if (dir > 0) - hi = t; - else lo = t; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - /* - ** Right time, wrong type. - ** Hunt for right time, right type. - ** It's okay to guess wrong since the guess - ** gets checked. - */ - sp = (const struct state *) - ((funcp == localsub) ? lclptr : gmtptr); -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (i = sp->typecnt - 1; i >= 0; --i) { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm) == NULL) - continue; - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - /* - ** We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - if ((*funcp)(&t, offset, tmp)) - *okayp = TRUE; - return t; -} - -static time_t -time2(tmp, funcp, offset, okayp) -struct tm * const tmp; -struct tm * (* const funcp)(const time_t*, long, struct tm*); -const long offset; -int * const okayp; -{ - time_t t; - - /* - ** First try without normalization of seconds - ** (in case tm_sec contains a value associated with a leap second). - ** If that fails, try with normalization of seconds. - */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); -} - -static time_t -time1(tmp, funcp, offset) -struct tm * const tmp; -struct tm * (* const funcp)(const time_t *, long, struct tm *); -const long offset; -{ - time_t t; - const struct state * sp; - int samei, otheri; - int sameind, otherind; - int i; - int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; - - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); -#ifdef PCTS - /* - ** PCTS code courtesy Grant Sullivan. - */ - if (okay) - return t; - if (tmp->tm_isdst < 0) - tmp->tm_isdst = 0; /* reset to std and try again */ -#endif /* defined PCTS */ -#ifndef PCTS - if (okay || tmp->tm_isdst < 0) - return t; -#endif /* !defined PCTS */ - /* - ** We're supposed to assume that somebody took a time of one type - ** and did some math on it that yielded a "struct tm" that's bad. - ** We try to divine the type they started from and adjust to the - ** type they need. - */ - sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr); -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = TRUE; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -time_t -mktime(tmp) -struct tm * const tmp; -{ - time_t mktime_return_value; - _RWLOCK_RDLOCK(&lcl_rwlock); - tzset_basic(1); - mktime_return_value = time1(tmp, localsub, 0L); - _RWLOCK_UNLOCK(&lcl_rwlock); - return(mktime_return_value); -} - -#ifdef STD_INSPIRED - -time_t -timelocal(tmp) -struct tm * const tmp; -{ - tmp->tm_isdst = -1; /* in case it wasn't initialized */ - return mktime(tmp); -} - -time_t -timegm(tmp) -struct tm * const tmp; -{ - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); -} - -time_t -timeoff(tmp, offset) -struct tm * const tmp; -const long offset; -{ - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); -} - -#endif /* defined STD_INSPIRED */ - -#ifdef CMUCS - -/* -** The following is supplied for compatibility with -** previous versions of the CMUCS runtime library. -*/ - -long -gtime(tmp) -struct tm * const tmp; -{ - const time_t t = mktime(tmp); - - if (t == WRONG) - return -1; - return t; -} - -#endif /* defined CMUCS */ - -/* -** XXX--is the below the right way to conditionalize?? -*/ - -#ifdef STD_INSPIRED - -/* -** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which -** is not the case if we are accounting for leap seconds. -** So, we provide the following conversion routines for use -** when exchanging timestamps with POSIX conforming systems. -*/ - -static long -leapcorr(timep) -time_t * timep; -{ - struct state * sp; - struct lsinfo * lp; - int i; - - sp = lclptr; - i = sp->leapcnt; - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) - return lp->ls_corr; - } - return 0; -} - -time_t -time2posix(t) -time_t t; -{ - tzset(); - return t - leapcorr(&t); -} - -time_t -posix2time(t) -time_t t; -{ - time_t x; - time_t y; - - tzset(); - /* - ** For a positive leap second hit, the result - ** is not unique. For a negative leap second - ** hit, the corresponding time doesn't exist, - ** so we return an adjacent second. - */ - x = t + leapcorr(&t); - y = x - leapcorr(&x); - if (y < t) { - do { - x++; - y = x - leapcorr(&x); - } while (y < t); - if (t != y) - return x - 1; - } else if (y > t) { - do { - --x; - y = x - leapcorr(&x); - } while (y > t); - if (t != y) - return x + 1; - } - return x; -} - -#endif /* defined STD_INSPIRED */ diff --git a/lib/libc/stdtime/private.h b/lib/libc/stdtime/private.h deleted file mode 100644 index dda5bef..0000000 --- a/lib/libc/stdtime/private.h +++ /dev/null @@ -1,326 +0,0 @@ -#ifndef PRIVATE_H - -#define PRIVATE_H - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -** -** $FreeBSD$ -*/ - -/* Stuff moved from Makefile.inc to reduce clutter */ -#ifndef TM_GMTOFF -#define TM_GMTOFF tm_gmtoff -#define TM_ZONE tm_zone -#define STD_INSPIRED 1 -#define PCTS 1 -#define HAVE_LONG_DOUBLE 1 -#define HAVE_STRERROR 1 -#define HAVE_UNISTD_H 1 -#define LOCALE_HOME _PATH_LOCALE -#define TZDIR "/usr/share/zoneinfo" -#endif /* ndef TM_GMTOFF */ - -/* -** This header is for use ONLY with the time conversion code. -** There is no guarantee that it will remain unchanged, -** or that it will remain at all. -** Do NOT copy it to any system include directory. -** Thank you! -*/ - -/* -** ID -*/ - -#ifndef lint -#ifndef NOID -/* -static char privatehid[] = "@(#)private.h 8.6"; -*/ -#endif /* !defined NOID */ -#endif /* !defined lint */ - -#define GRANDPARENTED "Local time zone must be set--see zic manual page" - -/* -** Defaults for preprocessor symbols. -** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. -*/ - -#ifndef HAVE_ADJTIME -#define HAVE_ADJTIME 1 -#endif /* !defined HAVE_ADJTIME */ - -#ifndef HAVE_GETTEXT -#define HAVE_GETTEXT 0 -#endif /* !defined HAVE_GETTEXT */ - -#ifndef HAVE_INCOMPATIBLE_CTIME_R -#define HAVE_INCOMPATIBLE_CTIME_R 0 -#endif /* !defined INCOMPATIBLE_CTIME_R */ - -#ifndef HAVE_SETTIMEOFDAY -#define HAVE_SETTIMEOFDAY 3 -#endif /* !defined HAVE_SETTIMEOFDAY */ - -#ifndef HAVE_SYMLINK -#define HAVE_SYMLINK 1 -#endif /* !defined HAVE_SYMLINK */ - -#ifndef HAVE_SYS_STAT_H -#define HAVE_SYS_STAT_H 1 -#endif /* !defined HAVE_SYS_STAT_H */ - -#ifndef HAVE_SYS_WAIT_H -#define HAVE_SYS_WAIT_H 1 -#endif /* !defined HAVE_SYS_WAIT_H */ - -#ifndef HAVE_UNISTD_H -#define HAVE_UNISTD_H 1 -#endif /* !defined HAVE_UNISTD_H */ - -#ifndef HAVE_UTMPX_H -#define HAVE_UTMPX_H 0 -#endif /* !defined HAVE_UTMPX_H */ - -#ifndef LOCALE_HOME -#define LOCALE_HOME "/usr/lib/locale" -#endif /* !defined LOCALE_HOME */ - -#if HAVE_INCOMPATIBLE_CTIME_R -#define asctime_r _incompatible_asctime_r -#define ctime_r _incompatible_ctime_r -#endif /* HAVE_INCOMPATIBLE_CTIME_R */ - -/* -** Nested includes -*/ - -#include "sys/types.h" /* for time_t */ -#include "stdio.h" -#include "errno.h" -#include "string.h" -#include "limits.h" /* for CHAR_BIT et al. */ -#include "time.h" -#include "stdlib.h" - -#if HAVE_GETTEXT -#include "libintl.h" -#endif /* HAVE_GETTEXT */ - -#if HAVE_SYS_WAIT_H -#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ -#endif /* HAVE_SYS_WAIT_H */ - -#ifndef WIFEXITED -#define WIFEXITED(status) (((status) & 0xff) == 0) -#endif /* !defined WIFEXITED */ -#ifndef WEXITSTATUS -#define WEXITSTATUS(status) (((status) >> 8) & 0xff) -#endif /* !defined WEXITSTATUS */ - -#if HAVE_UNISTD_H -#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */ -#endif /* HAVE_UNISTD_H */ - -#if !(HAVE_UNISTD_H) -#ifndef F_OK -#define F_OK 0 -#endif /* !defined F_OK */ -#ifndef R_OK -#define R_OK 4 -#endif /* !defined R_OK */ -#endif /* !(HAVE_UNISTD_H) */ - -/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ -#define is_digit(c) ((unsigned)(c) - '0' <= 9) - -/* -** Define HAVE_STDINT_H's default value here, rather than at the -** start, since __GLIBC__'s value depends on previously-included -** files. -** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.) -*/ -#ifndef HAVE_STDINT_H -#define HAVE_STDINT_H \ - (199901 <= __STDC_VERSION__ || \ - 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) -#endif /* !defined HAVE_STDINT_H */ - -#if HAVE_STDINT_H -#include "stdint.h" -#endif /* !HAVE_STDINT_H */ - -#ifndef INT_FAST64_MAX -/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ -#if defined LLONG_MAX || defined __LONG_LONG_MAX__ -typedef long long int_fast64_t; -#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#if (LONG_MAX >> 31) < 0xffffffff -Please use a compiler that supports a 64-bit integer type (or wider); -you may need to compile with "-DHAVE_STDINT_H". -#endif /* (LONG_MAX >> 31) < 0xffffffff */ -typedef long int_fast64_t; -#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#endif /* !defined INT_FAST64_MAX */ - -#ifndef INT32_MAX -#define INT32_MAX 0x7fffffff -#endif /* !defined INT32_MAX */ -#ifndef INT32_MIN -#define INT32_MIN (-1 - INT32_MAX) -#endif /* !defined INT32_MIN */ - -/* -** Workarounds for compilers/systems. -*/ - -/* -** Some time.h implementations don't declare asctime_r. -** Others might define it as a macro. -** Fix the former without affecting the latter. -*/ - -#ifndef asctime_r -extern char * asctime_r(struct tm const *, char *); -#endif - -/* -** Private function declarations. -*/ - -char * icalloc(int nelem, int elsize); -char * icatalloc(char * old, const char * new); -char * icpyalloc(const char * string); -char * imalloc(int n); -void * irealloc(void * pointer, int size); -void icfree(char * pointer); -void ifree(char * pointer); -const char * scheck(const char * string, const char * format); - -/* -** Finally, some convenience items. -*/ - -#ifndef TRUE -#define TRUE 1 -#endif /* !defined TRUE */ - -#ifndef FALSE -#define FALSE 0 -#endif /* !defined FALSE */ - -#ifndef TYPE_BIT -#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) -#endif /* !defined TYPE_BIT */ - -#ifndef TYPE_SIGNED -#define TYPE_SIGNED(type) (((type) -1) < 0) -#endif /* !defined TYPE_SIGNED */ - -/* -** Since the definition of TYPE_INTEGRAL contains floating point numbers, -** it cannot be used in preprocessor directives. -*/ - -#ifndef TYPE_INTEGRAL -#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) -#endif /* !defined TYPE_INTEGRAL */ - -/* -** Since the definition of TYPE_INTEGRAL contains floating point numbers, -** it cannot be used in preprocessor directives. -*/ - -#ifndef TYPE_INTEGRAL -#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) -#endif /* !defined TYPE_INTEGRAL */ - -#ifndef INT_STRLEN_MAXIMUM -/* -** 302 / 1000 is log10(2.0) rounded up. -** Subtract one for the sign bit if the type is signed; -** add one for integer division truncation; -** add one more for a minus sign if the type is signed. -*/ -#define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ - 1 + TYPE_SIGNED(type)) -#endif /* !defined INT_STRLEN_MAXIMUM */ - -/* -** INITIALIZE(x) -*/ - -#ifndef GNUC_or_lint -#ifdef lint -#define GNUC_or_lint -#endif /* defined lint */ -#ifndef lint -#ifdef __GNUC__ -#define GNUC_or_lint -#endif /* defined __GNUC__ */ -#endif /* !defined lint */ -#endif /* !defined GNUC_or_lint */ - -#ifndef INITIALIZE -#ifdef GNUC_or_lint -#define INITIALIZE(x) ((x) = 0) -#endif /* defined GNUC_or_lint */ -#ifndef GNUC_or_lint -#define INITIALIZE(x) -#endif /* !defined GNUC_or_lint */ -#endif /* !defined INITIALIZE */ - -/* -** For the benefit of GNU folk... -** `_(MSGID)' uses the current locale's message library string for MSGID. -** The default is to use gettext if available, and use MSGID otherwise. -*/ - -#ifndef _ -#if HAVE_GETTEXT -#define _(msgid) gettext(msgid) -#else /* !HAVE_GETTEXT */ -#define _(msgid) msgid -#endif /* !HAVE_GETTEXT */ -#endif /* !defined _ */ - -#ifndef TZ_DOMAIN -#define TZ_DOMAIN "tz" -#endif /* !defined TZ_DOMAIN */ - -#if HAVE_INCOMPATIBLE_CTIME_R -#undef asctime_r -#undef ctime_r -char *asctime_r(struct tm const *, char *); -char *ctime_r(time_t const *, char *); -#endif /* HAVE_INCOMPATIBLE_CTIME_R */ - -#ifndef YEARSPERREPEAT -#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ -#endif /* !defined YEARSPERREPEAT */ - -/* -** The Gregorian year averages 365.2425 days, which is 31556952 seconds. -*/ - -#ifndef AVGSECSPERYEAR -#define AVGSECSPERYEAR 31556952L -#endif /* !defined AVGSECSPERYEAR */ - -#ifndef SECSPERREPEAT -#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) -#endif /* !defined SECSPERREPEAT */ - -#ifndef SECSPERREPEAT_BITS -#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#endif /* !defined SECSPERREPEAT_BITS */ - -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ - -#endif /* !defined PRIVATE_H */ diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3 index d763fbc..763696b 100644 --- a/lib/libc/stdtime/strptime.3 +++ b/lib/libc/stdtime/strptime.3 @@ -149,7 +149,7 @@ and 12PM is taken as noon. .Pp The -.Fa %U +.Fa \&%U and .Fa %W format specifiers accept any value within the range 00 to 53 diff --git a/lib/libc/stdtime/time2posix.3 b/lib/libc/stdtime/time2posix.3 deleted file mode 100644 index 1a1ec95..0000000 --- a/lib/libc/stdtime/time2posix.3 +++ /dev/null @@ -1,123 +0,0 @@ -.\" $FreeBSD$ -.\" -.Dd September 11, 2005 -.Dt TIME2POSIX 3 -.Os -.Sh NAME -.Nm time2posix , -.Nm posix2time -.Nd convert seconds since the Epoch -.Sh LIBRARY -.Lb libc -.Sh SYNOPSIS -.In time.h -.Ft time_t -.Fn time2posix "time_t t" -.Ft time_t -.Fn posix2time "time_t t" -.Sh DESCRIPTION -.St -p1003.1-88 -legislates that a time_t value of -536457599 shall correspond to "Wed Dec 31 23:59:59 GMT 1986." -This effectively implies that POSIX time_t's cannot include leap -seconds and, -therefore, -that the system time must be adjusted as each leap occurs. -.Pp -If the time package is configured with leap-second support -enabled, -however, -no such adjustment is needed and -time_t values continue to increase over leap events -(as a true `seconds since...' value). -This means that these values will differ from those required by POSIX -by the net number of leap seconds inserted since the Epoch. -.Pp -Typically this is not a problem as the type time_t is intended -to be -(mostly) -opaque\(emtime_t values should only be obtained-from and -passed-to functions such as -.Xr time 3 , -.Xr localtime 3 , -.Xr mktime 3 -and -.Xr difftime 3 . -However, -.St -p1003.1-88 -gives an arithmetic -expression for directly computing a time_t value from a given date/time, -and the same relationship is assumed by some -(usually older) -applications. -Any programs creating/dissecting time_t's -using such a relationship will typically not handle intervals -over leap seconds correctly. -.Pp -The -.Fn time2posix -and -.Fn posix2time -functions are provided to address this time_t mismatch by converting -between local time_t values and their POSIX equivalents. -This is done by accounting for the number of time-base changes that -would have taken place on a POSIX system as leap seconds were inserted -or deleted. -These converted values can then be used in lieu of correcting the older -applications, -or when communicating with POSIX-compliant systems. -.Pp -The -.Fn time2posix -function is single-valued. -That is, -every local time_t -corresponds to a single POSIX time_t. -The -.Fn posix2time -function is less well-behaved: -for a positive leap second hit the result is not unique, -and for a negative leap second hit the corresponding -POSIX time_t does not exist so an adjacent value is returned. -Both of these are good indicators of the inferiority of the -POSIX representation. -.Pp -The following table summarizes the relationship between time_t -and its conversion to, -and back from, -the POSIX representation over the leap second inserted at the end of June, -1993. -.Bl -column "93/06/30" "23:59:59" "A+0" "X=time2posix(T)" -.It Sy "DATE TIME T X=time2posix(T) posix2time(X)" -.It "93/06/30 23:59:59 A+0 B+0 A+0" -.It "93/06/30 23:59:60 A+1 B+1 A+1 or A+2" -.It "93/07/01 00:00:00 A+2 B+1 A+1 or A+2" -.It "93/07/01 00:00:01 A+3 B+2 A+3" -.El -.Pp -A leap second deletion would look like... -.Bl -column "??/06/30" "23:59:58" "A+0" "X=time2posix(T)" -.It Sy "DATE TIME T X=time2posix(T) posix2time(X)" -.It "??/06/30 23:59:58 A+0 B+0 A+0" -.It "??/07/01 00:00:00 A+1 B+2 A+1" -.It "??/07/01 00:00:01 A+2 B+3 A+2" -.El -.Pp -.D1 No "[Note: posix2time(B+1) => A+0 or A+1]" -.Pp -If leap-second support is not enabled, -local time_t's and -POSIX time_t's are equivalent, -and both -.Fn time2posix -and -.Fn posix2time -degenerate to the identity function. -.Sh "SEE ALSO" -.Xr difftime 3 , -.Xr localtime 3 , -.Xr mktime 3 , -.Xr time 3 -.\" @(#)time2posix.3 8.2 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/stdtime/tzfile.5 b/lib/libc/stdtime/tzfile.5 deleted file mode 100644 index 15625d2..0000000 --- a/lib/libc/stdtime/tzfile.5 +++ /dev/null @@ -1,152 +0,0 @@ -.\" $FreeBSD$ -.Dd September 13, 1994 -.Dt TZFILE 5 -.Os -.Sh NAME -.Nm tzfile -.Nd timezone information -.Sh SYNOPSIS -.Fd #include \&"/usr/src/lib/libc/stdtime/tzfile.h\&" -.Sh DESCRIPTION -The time zone information files used by -.Xr tzset 3 -begin with the magic characters -.Dq Li TZif -to identify them as -time zone information files, -followed by a character identifying the version of the file's format -(as of 2005, either an ASCII NUL or a '2') -followed by fifteen bytes containing zeroes reserved for future use, -followed by four four-byte values -written in a ``standard'' byte order -(the high-order byte of the value is written first). -These values are, -in order: -.Pp -.Bl -tag -compact -width tzh_ttisstdcnt -.It Va tzh_ttisgmtcnt -The number of UTC/local indicators stored in the file. -.It Va tzh_ttisstdcnt -The number of standard/wall indicators stored in the file. -.It Va tzh_leapcnt -The number of leap seconds for which data is stored in the file. -.It Va tzh_timecnt -The number of ``transition times'' for which data is stored -in the file. -.It Va tzh_typecnt -The number of ``local time types'' for which data is stored -in the file (must not be zero). -.It Va tzh_charcnt -The number of characters of ``time zone abbreviation strings'' -stored in the file. -.El -.Pp -The above header is followed by -.Va tzh_timecnt -four-byte values of type -.Fa long , -sorted in ascending order. -These values are written in ``standard'' byte order. -Each is used as a transition time (as returned by -.Xr time 3 ) -at which the rules for computing local time change. -Next come -.Va tzh_timecnt -one-byte values of type -.Fa "unsigned char" ; -each one tells which of the different types of ``local time'' types -described in the file is associated with the same-indexed transition time. -These values serve as indices into an array of -.Fa ttinfo -structures (with -.Fa tzh_typecnt -entries) that appears next in the file; -these structures are defined as follows: -.Pp -.Bd -literal -offset indent -struct ttinfo { - long tt_gmtoff; - int tt_isdst; - unsigned int tt_abbrind; -}; -.Ed -.Pp -Each structure is written as a four-byte value for -.Va tt_gmtoff -of type -.Fa long , -in a standard byte order, followed by a one-byte value for -.Va tt_isdst -and a one-byte value for -.Va tt_abbrind . -In each structure, -.Va tt_gmtoff -gives the number of seconds to be added to UTC, -.Li tt_isdst -tells whether -.Li tm_isdst -should be set by -.Xr localtime 3 -and -.Va tt_abbrind -serves as an index into the array of time zone abbreviation characters -that follow the -.Li ttinfo -structure(s) in the file. -.Pp -Then there are -.Va tzh_leapcnt -pairs of four-byte values, written in standard byte order; -the first value of each pair gives the time -(as returned by -.Xr time 3 ) -at which a leap second occurs; -the second gives the -.Em total -number of leap seconds to be applied after the given time. -The pairs of values are sorted in ascending order by time. -.Pp -Then there are -.Va tzh_ttisstdcnt -standard/wall indicators, each stored as a one-byte value; -they tell whether the transition times associated with local time types -were specified as standard time or wall clock time, -and are used when a time zone file is used in handling POSIX-style -time zone environment variables. -.Pp -Finally there are -.Va tzh_ttisgmtcnt -UTC/local indicators, each stored as a one-byte value; -they tell whether the transition times associated with local time types -were specified as UTC or local time, -and are used when a time zone file is used in handling POSIX-style -time zone environment variables. -.Pp -.Nm localtime -uses the first standard-time -.Li ttinfo -structure in the file -(or simply the first -.Li ttinfo -structure in the absence of a standard-time structure) -if either -.Li tzh_timecnt -is zero or the time argument is less than the first transition time recorded -in the file. -.Pp -For version-2-format time zone files, -the above header and data is followed by a second header and data, -identical in format except that eight bytes are used for each -transition time or leap second time. -After the second header and data comes a newline-enclosed, -POSIX-TZ-environment-variable-style string for use in handling instants -after the last transition time stored in the file -(with nothing between the newlines if there is no POSIX representation for -such instants). -.Sh SEE ALSO -.Xr ctime 3 , -.Xr time2posix 3 , -.Xr zic 8 -.\" @(#)tzfile.5 8.2 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/stdtime/tzfile.h b/lib/libc/stdtime/tzfile.h deleted file mode 100644 index 85b945e..0000000 --- a/lib/libc/stdtime/tzfile.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef TZFILE_H -#define TZFILE_H - - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -** -** $FreeBSD$ -*/ - -/* -** This header is for use ONLY with the time conversion code. -** There is no guarantee that it will remain unchanged, -** or that it will remain at all. -** Do NOT copy it to any system include directory. -** Thank you! -*/ - -/* -** ID -*/ - -#ifndef lint -#ifndef NOID -/* -static char tzfilehid[] = "@(#)tzfile.h 8.1"; -*/ -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/* -** Information about time zone files. -*/ - -#ifndef TZDIR -#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ -#endif /* !defined TZDIR */ - -#ifndef TZDEFAULT -#define TZDEFAULT "/etc/localtime" -#endif /* !defined TZDEFAULT */ - -#ifndef TZDEFRULES -#define TZDEFRULES "posixrules" -#endif /* !defined TZDEFRULES */ - -/* -** Each file begins with. . . -*/ - -#define TZ_MAGIC "TZif" - -struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' as of 2005 */ - char tzh_reserved[15]; /* reserved--must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ -}; - -/* -** . . .followed by. . . -** -** tzh_timecnt (char [4])s coded transition times a la time(2) -** tzh_timecnt (unsigned char)s types of local time starting at above -** tzh_typecnt repetitions of -** one (char [4]) coded UTC offset in seconds -** one (unsigned char) used to set tm_isdst -** one (unsigned char) that's an abbreviation list index -** tzh_charcnt (char)s '\0'-terminated zone abbreviations -** tzh_leapcnt repetitions of -** one (char [4]) coded leap second transition times -** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition -** time is standard time, if FALSE, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition -** time is UTC, if FALSE, -** transition time is local time -** if absent, transition times are -** assumed to be local time -*/ - -/* -** If tzh_version is '2' or greater, the above is followed by a second instance -** of tzhead and a second instance of the data in which each coded transition -** time uses 8 rather than 4 chars, -** then a POSIX-TZ-environment-variable-style string for use in handling -** instants after the last transition time stored in the file -** (with nothing between the newlines if there is no POSIX representation for -** such instants). -*/ - -/* -** In the current implementation, "tzset()" refuses to deal with files that -** exceed any of the limits below. -*/ - -#ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 1200 -#endif /* !defined TZ_MAX_TIMES */ - -#ifndef TZ_MAX_TYPES -#ifndef NOSOLAR -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ -#endif /* !defined NOSOLAR */ -#ifdef NOSOLAR -/* -** Must be at least 14 for Europe/Riga as of Jan 12 1995, -** as noted by Earl Chew. -*/ -#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ -#endif /* !defined NOSOLAR */ -#endif /* !defined TZ_MAX_TYPES */ - -#ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ - -#ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZ_MAX_LEAPS */ - -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -/* -** Since everything in isleap is modulo 400 (or a factor of 400), we know that -** isleap(y) == isleap(y % 400) -** and so -** isleap(a + b) == isleap((a + b) % 400) -** or -** isleap(a + b) == isleap(a % 400 + b % 400) -** This is true even if % means modulo rather than Fortran remainder -** (which is allowed by C89 but not C99). -** We use this to avoid addition overflow problems. -*/ - -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) - -#endif /* !defined TZFILE_H */ diff --git a/lib/libc/string/memccpy.3 b/lib/libc/string/memccpy.3 index 350f000..40a25be 100644 --- a/lib/libc/string/memccpy.3 +++ b/lib/libc/string/memccpy.3 @@ -50,7 +50,9 @@ to string .Fa dst . If the character .Fa c -(as converted to an unsigned char) occurs in the string +(as converted to an +.Vt "unsigned char" ) +occurs in the string .Fa src , the copy stops and a pointer to the byte after the copy of .Fa c diff --git a/lib/libc/string/memchr.3 b/lib/libc/string/memchr.3 index ae883bf..6e33aef 100644 --- a/lib/libc/string/memchr.3 +++ b/lib/libc/string/memchr.3 @@ -52,7 +52,8 @@ The function locates the first occurrence of .Fa c -(converted to an unsigned char) +(converted to an +.Vt "unsigned char" ) in string .Fa b . .Pp diff --git a/lib/libc/string/memcmp.3 b/lib/libc/string/memcmp.3 index 507b930..88ed9a2 100644 --- a/lib/libc/string/memcmp.3 +++ b/lib/libc/string/memcmp.3 @@ -61,7 +61,9 @@ The function returns zero if the two strings are identical, otherwise returns the difference between the first two differing bytes -(treated as unsigned char values, so that +(treated as +.Vt "unsigned char" +values, so that .Sq Li \e200 is greater than .Sq Li \&\e0 , diff --git a/lib/libc/string/memset.3 b/lib/libc/string/memset.3 index 1dc287d..07bd7aa 100644 --- a/lib/libc/string/memset.3 +++ b/lib/libc/string/memset.3 @@ -52,7 +52,9 @@ writes .Fa len bytes of value .Fa c -(converted to an unsigned char) to the string +(converted to an +.Vt "unsigned char" ) +to the string .Fa b . .Sh RETURN VALUES The diff --git a/lib/libc/string/strcat.3 b/lib/libc/string/strcat.3 index 0290994..4c9fec9 100644 --- a/lib/libc/string/strcat.3 +++ b/lib/libc/string/strcat.3 @@ -32,11 +32,12 @@ .\" @(#)strcat.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 4, 1993 +.Dd December 1, 2009 .Dt STRCAT 3 .Os .Sh NAME -.Nm strcat +.Nm strcat , +.Nm strncat .Nd concatenate strings .Sh LIBRARY .Lb libc diff --git a/lib/libc/string/strcmp.3 b/lib/libc/string/strcmp.3 index 1e13ed8..74d1a5c 100644 --- a/lib/libc/string/strcmp.3 +++ b/lib/libc/string/strcmp.3 @@ -75,7 +75,7 @@ The .Fn strcmp and .Fn strncmp -return an integer greater than, equal to, or less than 0, according +functions return an integer greater than, equal to, or less than 0, according as the string .Fa s1 is greater than, equal to, or less than the string diff --git a/lib/libc/string/strndup.c b/lib/libc/string/strndup.c index 56aa6a8..abb1e03 100644 --- a/lib/libc/string/strndup.c +++ b/lib/libc/string/strndup.c @@ -42,9 +42,7 @@ strndup(const char *str, size_t n) size_t len; char *copy; - for (len = 0; len < n && str[len]; len++) - continue; - + len = strnlen(str, n); if ((copy = malloc(len + 1)) == NULL) return (NULL); memcpy(copy, str, len); diff --git a/lib/libc/string/strsignal.c b/lib/libc/string/strsignal.c index e4267a3..c51f34d 100644 --- a/lib/libc/string/strsignal.c +++ b/lib/libc/string/strsignal.c @@ -33,22 +33,64 @@ static char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "namespace.h" #if defined(NLS) #include <nl_types.h> #endif - #include <limits.h> #include <errno.h> +#include <stdlib.h> #include <string.h> #include <signal.h> +#include "reentrant.h" +#include "un-namespace.h" #define UPREFIX "Unknown signal" +static char sig_ebuf[NL_TEXTMAX]; +static char sig_ebuf_err[NL_TEXTMAX]; +static once_t sig_init_once = ONCE_INITIALIZER; +static thread_key_t sig_key; +static int sig_keycreated = 0; + +static void +sig_keycreate(void) +{ + sig_keycreated = (thr_keycreate(&sig_key, free) == 0); +} + +static char * +sig_tlsalloc(void) +{ + char *ebuf = NULL; + + if (thr_main() != 0) + ebuf = sig_ebuf; + else { + if (thr_once(&sig_init_once, sig_keycreate) != 0 || + !sig_keycreated) + goto thr_err; + if ((ebuf = thr_getspecific(sig_key)) == NULL) { + if ((ebuf = malloc(sizeof(sig_ebuf))) == NULL) + goto thr_err; + if (thr_setspecific(sig_key, ebuf) != 0) { + free(ebuf); + ebuf = NULL; + goto thr_err; + } + } + } +thr_err: + if (ebuf == NULL) + ebuf = sig_ebuf_err; + return (ebuf); +} + /* XXX: negative 'num' ? (REGR) */ char * strsignal(int num) { - static char ebuf[NL_TEXTMAX]; + char *ebuf; char tmp[20]; size_t n; int signum; @@ -60,6 +102,8 @@ strsignal(int num) catd = catopen("libc", NL_CAT_LOCALE); #endif + ebuf = sig_tlsalloc(); + if (num > 0 && num < sys_nsig) { n = strlcpy(ebuf, #if defined(NLS) @@ -67,7 +111,7 @@ strsignal(int num) #else sys_siglist[num], #endif - sizeof(ebuf)); + sizeof(sig_ebuf)); } else { n = strlcpy(ebuf, #if defined(NLS) @@ -75,7 +119,7 @@ strsignal(int num) #else UPREFIX, #endif - sizeof(ebuf)); + sizeof(sig_ebuf)); } signum = num; diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 1e6059d..1915c55 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -83,7 +83,8 @@ MAN+= abort2.2 accept.2 access.2 acct.2 adjtime.2 \ mq_setattr.2 \ msgctl.2 msgget.2 msgrcv.2 msgsnd.2 \ msync.2 munmap.2 nanosleep.2 nfssvc.2 ntp_adjtime.2 open.2 \ - pathconf.2 pipe.2 poll.2 posix_openpt.2 profil.2 ptrace.2 quotactl.2 \ + pathconf.2 pipe.2 poll.2 posix_openpt.2 profil.2 \ + pselect.2 ptrace.2 quotactl.2 \ read.2 readlink.2 reboot.2 recv.2 rename.2 revoke.2 rfork.2 rmdir.2 \ rtprio.2 .if !defined(NO_P1003_1B) diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 56d8aaa..ce6f32a 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -211,6 +211,7 @@ FBSD_1.0 { posix_openpt; preadv; profil; + pselect; ptrace; pwritev; quotactl; @@ -769,6 +770,8 @@ FBSDprivate_1.0 { __sys_olio_listio; _open; __sys_open; + _openat; + __sys_openat; _pathconf; __sys_pathconf; _pipe; @@ -779,6 +782,8 @@ FBSDprivate_1.0 { __sys_preadv; _profil; __sys_profil; + _pselect; + __sys_pselect; _ptrace; __sys_ptrace; _pwritev; diff --git a/lib/libc/sys/__error.c b/lib/libc/sys/__error.c index d7b5529..c3f59f8 100644 --- a/lib/libc/sys/__error.c +++ b/lib/libc/sys/__error.c @@ -39,7 +39,7 @@ extern int errno; __weak_reference(__error_unthreaded, __error); int * -__error_unthreaded() +__error_unthreaded(void) { return(&errno); } diff --git a/lib/libc/sys/accept.2 b/lib/libc/sys/accept.2 index 4387dc6..df6c0b1 100644 --- a/lib/libc/sys/accept.2 +++ b/lib/libc/sys/accept.2 @@ -126,6 +126,10 @@ new socket. For some applications, performance may be enhanced by using an .Xr accept_filter 9 to pre-process incoming connections. +.Pp +Portable programs should not rely on the +.Dv O_NONBLOCK +property being inherited. .Sh RETURN VALUES The call returns \-1 on error. If it succeeds, it returns a non-negative diff --git a/lib/libc/sys/clock_gettime.2 b/lib/libc/sys/clock_gettime.2 index 242af1c..a2fa624 100644 --- a/lib/libc/sys/clock_gettime.2 +++ b/lib/libc/sys/clock_gettime.2 @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 4, 2006 +.Dd December 29, 2009 .Dt CLOCK_GETTIME 2 .Os .Sh NAME @@ -59,21 +59,43 @@ used by a clock which is specified by The .Fa clock_id argument -can be one of five values: -.Dv CLOCK_REALTIME +can be one of the following values: +.Dv CLOCK_REALTIME , +.Dv CLOCK_REALTIME_PRECISE , +.Dv CLOCK_REALTIME_FAST for time that increments as -a wall clock should, -.Dv CLOCK_MONOTONIC -which increments in SI seconds, -.Dv CLOCK_UPTIME +a wall clock should; +.Dv CLOCK_MONOTONIC , +.Dv CLOCK_MONOTONIC_PRECISE , +.Dv CLOCK_MONOTONIC_FAST +which increments in SI seconds; +.Dv CLOCK_UPTIME , +.Dv CLOCK_UPTIME_PRECISE , +.Dv CLOCK_UPTIME_FAST which starts at zero when the kernel boots and increments -monotonically in SI seconds while the machine is running, +monotonically in SI seconds while the machine is running; .Dv CLOCK_VIRTUAL for time that increments only when -the CPU is running in user mode on behalf of the calling process, or +the CPU is running in user mode on behalf of the calling process; .Dv CLOCK_PROF for time that increments when the CPU is running in user or -kernel mode. +kernel mode; or +.Dv CLOCK_SECOND +which returns the current second without performing a full time counter +query, using in-kernel cached value of current second. +.Pp +The clock IDs +.Fa CLOCK_REALTIME_FAST , +.Fa CLOCK_MONOTONIC_FAST , +.Fa CLOCK_UPTIME_FAST +are analogs of corresponding IDs without _FAST suffix but do not perform +a full time counter query, so their accuracy is one timer tick. +Similarly, +.Fa CLOCK_REALTIME_PRECISE , +.Fa CLOCK_MONOTONIC_PRECISE , +.Fa CLOCK_UPTIME_PRECISE +are used to get the most exact value as possible, at the expense of +execution time. .Pp The structure pointed to by .Fa tp @@ -88,7 +110,8 @@ struct timespec { }; .Ed .Pp -Only the super-user may set the time of day. +Only the super-user may set the time of day, using only +.Fa CLOCK_REALTIME . If the system securelevel is greater than 1 (see .Xr init 8 ) , the time may only be advanced. @@ -134,3 +157,13 @@ and .Fn clock_getres system calls conform to .St -p1003.1b-93 . +The clock IDs +.Fa CLOCK_REALTIME_FAST , +.Fa CLOCK_REALTIME_PRECISE , +.Fa CLOCK_MONOTONIC_FAST , +.Fa CLOCK_MONOTONIC_PRECISE , +.Fa CLOCK_UPTIME , +.Fa CLOCK_UPTIME_FAST , +.Fa CLOCK_UPTIME_PRECISE , +.Fa CLOCK_SECOND +are FreeBSD extensions to the POSIX interface. diff --git a/lib/libc/sys/cpuset.2 b/lib/libc/sys/cpuset.2 index c07f8d9..1cdff68 100644 --- a/lib/libc/sys/cpuset.2 +++ b/lib/libc/sys/cpuset.2 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 29, 2008 +.Dd January 8, 2010 .Dt CPUSET 2 .Os .Sh NAME @@ -96,7 +96,7 @@ The .Fa which argument may have the following values: .Bl -column CPU_WHICH_CPUSET -offset indent -.It Dv CPU_WHICH_TID Ta "id is lpwid_t (thread id)" +.It Dv CPU_WHICH_TID Ta "id is lwpid_t (thread id)" .It Dv CPU_WHICH_PID Ta "id is pid_t (process id)" .It Dv CPU_WHICH_CPUSET Ta "id is a cpusetid_t (cpuset id)" .It Dv CPU_WHICH_IRQ Ta "id is an irq number" @@ -209,14 +209,16 @@ The calling process did not have the credentials required to complete the operation. .It Bq Er ENFILE There was no free -.Fn cpusetid_t +.Ft cpusetid_t for allocation. .El .Sh SEE ALSO .Xr cpuset 1 , .Xr cpuset_getaffinity 2 , .Xr cpuset_setaffinity 2 , -.Xr CPU_SET 3 +.Xr CPU_SET 3 , +.Xr pthread_affinity_np 3 , +.Xr pthread_attr_affinity_np 3 .Sh HISTORY The .Nm diff --git a/lib/libc/sys/cpuset_getaffinity.2 b/lib/libc/sys/cpuset_getaffinity.2 index b065ac3..c8b272f 100644 --- a/lib/libc/sys/cpuset_getaffinity.2 +++ b/lib/libc/sys/cpuset_getaffinity.2 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 29, 2008 +.Dd January 8, 2010 .Dt CPUSET 2 .Os .Sh NAME @@ -147,7 +147,9 @@ operation. .Xr cpuset 2 , .Xr cpuset_getid 2 , .Xr cpuset_setid 2 , -.Xr CPU_SET 3 +.Xr CPU_SET 3 , +.Xr pthread_affinity_np 3 , +.Xr pthread_attr_affinity_np 3 .Sh HISTORY The .Nm diff --git a/lib/libc/sys/fcntl.2 b/lib/libc/sys/fcntl.2 index a16724c..250cef4 100644 --- a/lib/libc/sys/fcntl.2 +++ b/lib/libc/sys/fcntl.2 @@ -28,7 +28,7 @@ .\" @(#)fcntl.2 8.2 (Berkeley) 1/12/94 .\" $FreeBSD$ .\" -.Dd March 8, 2008 +.Dd September 28, 2009 .Dt FCNTL 2 .Os .Sh NAME @@ -241,6 +241,22 @@ will be interrupted if the signal handler has not specified the .Dv SA_RESTART (see .Xr sigaction 2 ) . +.It Dv F_READAHEAD +Set or clear the read ahead amount for sequential access to the third +argument, +.Fa arg , +which is rounded up to the nearest block size. +A zero value in +.Fa arg +turns off read ahead. +.It Dv F_RDAHEAD +Equivalent to Darwin counterpart which sets read ahead amount of 128KB +when the third argument, +.Fa arg +is non-zero. +A zero value in +.Fa arg +turns off read ahead. .El .Pp When a shared lock has been set on a segment of a file, diff --git a/lib/libc/sys/intro.2 b/lib/libc/sys/intro.2 index 1c01c39..e5ff9df 100644 --- a/lib/libc/sys/intro.2 +++ b/lib/libc/sys/intro.2 @@ -456,6 +456,17 @@ The specified extended attribute does not exist. .It Er 88 EDOOFUS Em "Programming error" . A function or API is being abused in a way which could only be detected at run-time. +.It Er 89 EBADMSG Em "Bad message" . +A corrupted message was detected. +.It Er 90 EMULTIHOP Em "Multihop attempted" . +This error code is unused, but present for compatibility with other systems. +.It Er 91 ENOLINK Em "Link has been severed" . +This error code is unused, but present for compatibility with other systems. +.It Er 92 EPROTO Em "Protocol error" . +A device or socket encountered an unrecoverable protocol error. +.It Er 93 ENOTCAPABLE Em "Capabilities insufficient" . +An operation on a capability file descriptor requires greater privilege than +the capability allows. .El .Sh DEFINITIONS .Bl -tag -width Ds diff --git a/lib/libc/sys/kqueue.2 b/lib/libc/sys/kqueue.2 index e899a1b..326632d 100644 --- a/lib/libc/sys/kqueue.2 +++ b/lib/libc/sys/kqueue.2 @@ -438,19 +438,6 @@ There is a system wide limit on the number of timers which is controlled by the .Va kern.kq_calloutmax sysctl. -.It Dv EVFILT_NETDEV -Takes a descriptor to a network interface as the identifier, and the events to watch for in -.Va fflags . -It returns, when one or more of the requested events occur on the descriptor. -The events to monitor are: -.Bl -tag -width XXNOTE_LINKDOWN -.It Dv NOTE_LINKUP -The link is up. -.It Dv NOTE_LINKDOWN -The link is down. -.It Dv NOTE_LINKINV -The link state is invalid. -.El .Pp On return, .Va fflags @@ -595,13 +582,6 @@ system and this manual page were written by .An Jonathan Lemon Aq jlemon@FreeBSD.org . .Sh BUGS The -.Dv EVFILT_NETDEV -filter is currently only implemented for devices that use the -.Xr miibus 4 -driver for LINKUP and LINKDOWN operations. -Therefore, it will not work with many non-ethernet devices. -.Pp -The .Fa timeout value is limited to 24 hours; longer timeouts will be silently reinterpreted as 24 hours. diff --git a/lib/libc/sys/mmap.2 b/lib/libc/sys/mmap.2 index e632748..4849973 100644 --- a/lib/libc/sys/mmap.2 +++ b/lib/libc/sys/mmap.2 @@ -28,7 +28,7 @@ .\" @(#)mmap.2 8.4 (Berkeley) 5/11/95 .\" $FreeBSD$ .\" -.Dd July 26, 2009 +.Dd November 6, 2009 .Dt MMAP 2 .Os .Sh NAME @@ -108,6 +108,10 @@ The argument is ignored. .\".It Dv MAP_FILE .\"Mapped from a regular file or character-special device memory. +.It Dv MAP_ANONYMOUS +This flag is identical to +.Dv MAP_ANON +and is provided for compatibility. .It Dv MAP_FIXED Do not permit the system to select a different address than the one specified. diff --git a/lib/libc/sys/mount.2 b/lib/libc/sys/mount.2 index 6ce2d4d..b65c1b6 100644 --- a/lib/libc/sys/mount.2 +++ b/lib/libc/sys/mount.2 @@ -28,7 +28,7 @@ .\" @(#)mount.2 8.3 (Berkeley) 5/24/95 .\" $FreeBSD$ .\" -.Dd February 23, 2005 +.Dd January 26, 2010 .Dt MOUNT 2 .Os .Sh NAME @@ -368,5 +368,9 @@ and .Fn unmount functions appeared in .At v6 . +The +.Fn nmount +system call first appeared in +.Fx 5.0 . .Sh BUGS Some of the error codes need translation to more obvious messages. diff --git a/lib/libc/sys/nanosleep.2 b/lib/libc/sys/nanosleep.2 index 18e4c88..f50544b 100644 --- a/lib/libc/sys/nanosleep.2 +++ b/lib/libc/sys/nanosleep.2 @@ -47,7 +47,9 @@ The .Fn nanosleep system call -causes the process to sleep for the specified time. +causes the calling thread to sleep until the time interval specified by +.Fa rqtp +has elapsed. An unmasked signal will cause it to terminate the sleep early, regardless of the .Dv SA_RESTART diff --git a/lib/libc/gen/pselect.3 b/lib/libc/sys/pselect.2 index a56a0a8..cf784a5 100644 --- a/lib/libc/gen/pselect.3 +++ b/lib/libc/sys/pselect.2 @@ -28,8 +28,8 @@ .\" .\" $FreeBSD$ .\" -.Dd June 16, 2002 -.Dt PSELECT 3 +.Dd October 27, 2009 +.Dt PSELECT 2 .Os .Sh NAME .Nm pselect @@ -88,11 +88,6 @@ for a more detailed discussion of the semantics of this interface, and for macros used to manipulate the .Vt "fd_set" data type. -.Sh IMPLEMENTATION NOTES -The -.Fn pselect -function is implemented in the C library as a wrapper around -.Fn select . .Sh RETURN VALUES The .Fn pselect @@ -121,7 +116,7 @@ The function first appeared in .Fx 5.0 . .Sh AUTHORS -The +The first implementation of .Fn pselect function and this manual page were written by .An Garrett Wollman Aq wollman@FreeBSD.org . diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index 9d8f550..8265fb6 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -2,7 +2,7 @@ .\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $ .\" .\" This file is in the public domain. -.Dd March 27, 2009 +.Dd February 11, 2010 .Dt PTRACE 2 .Os .Sh NAME @@ -327,6 +327,68 @@ This request will trace the specified process on each system call exit. .It PT_SYSCALL This request will trace the specified process on each system call entry and exit. +.It PT_VM_TIMESTAMP +This request returns the generation number or timestamp of the memory map of +the traced process as the return value from +.Fn ptrace . +This provides a low-cost way for the tracing process to determine if the +VM map changed since the last time this request was made. +.It PT_VM_ENTRY +This request is used to iterate over the entries of the VM map of the traced +process. +The +.Fa addr +argument specifies a pointer to a +.Vt "struct ptrace_vm_entry" , +which is defined as follows: +.Bd -literal +struct ptrace_vm_entry { + int pve_entry; + int pve_timestamp; + u_long pve_start; + u_long pve_end; + u_long pve_offset; + u_int pve_prot; + u_int pve_pathlen; + long pve_fileid; + uint32_t pve_fsid; + char *pve_path; +}; +.Ed +.Pp +The first entry is returned by setting +.Va pve_entry +to zero. +Subsequent entries are returned by leaving +.Va pve_entry +unmodified from the value returned by previous requests. +The +.Va pve_timestamp +field can be used to detect changes to the VM map while iterating over the +entries. +The tracing process can then take appropriate action, such as restarting. +By setting +.Va pve_pathlen +to a non-zero value on entry, the pathname of the backing object is returned +in the buffer pointed to by +.Va pve_path , +provided the entry is backed by a vnode. +The +.Va pve_pathlen +field is updated with the actual length of the pathname (including the +terminating null character). +The +.Va pve_offset +field is the offset within the backing object at which the range starts. +The range is located in the VM space at +.Va pve_start +and extends up to +.Va pve_end +(inclusive). +.Pp +The +.Fa data +argument is ignored. .El .Pp Additionally, machine-specific requests can exist. @@ -376,6 +438,11 @@ or .Dv PT_SETDBREGS was attempted on a process with no valid register set. (This is normally true only of system processes.) +.It +.Dv PT_VM_ENTRY +was given an invalid value for +.Fa pve_entry . +This can also be caused by changes to the VM map of the process. .El .It Bq Er EBUSY .Bl -bullet -compact @@ -405,6 +472,22 @@ on a process in violation of the requirements listed under .Dv PT_ATTACH above. .El +.It Bq Er ENOENT +.Bl -bullet -compact +.It +.Dv PT_VM_ENTRY +previously returned the last entry of the memory map. +No more entries exist. +.El +.It Bq Er ENAMETOOLONG +.Bl -bullet -compact +.It +.Dv PT_VM_ENTRY +cannot return the pathname of the backing object because the buffer is not big +enough. +.Fa pve_pathlen +holds the minimum buffer size required on return. +.El .El .Sh SEE ALSO .Xr execve 2 , diff --git a/lib/libc/sys/sctp_generic_recvmsg.2 b/lib/libc/sys/sctp_generic_recvmsg.2 index ccdb9db..d6fd1b5 100644 --- a/lib/libc/sys/sctp_generic_recvmsg.2 +++ b/lib/libc/sys/sctp_generic_recvmsg.2 @@ -36,7 +36,7 @@ .Os .Sh NAME .Nm sctp_generic_recvmsg -.Nd receive data from a peer. +.Nd receive data from a peer .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -46,16 +46,20 @@ .Ft int .Fn sctp_generic_recvmsg "int s" "struct iovec *iov" "int iovlen" "struct sockaddr *from" "socklen_t *fromlen" "struct sctp_sndrcvinfo *sinfo" "int *msgflags" .Sh DESCRIPTION -The .Fn sctp_generic_recvmsg -is the true system calls used by the -.Fn sctp_recvmsg -function call. This call is more efficient since it is a -true system calls but it is specific to FreeBSD and -can be expected NOT to be present on any other Operating -System. For detailed useage please see either the -.Fn sctp_recvmsg -function call. +is the true system call used by the +.Xr sctp_recvmsg 3 +function call. +This call is more efficient since it is a +true system call but it is specific to +.Fx +and can be expected +.Em not +to be present on any other operating +system. +For detailed usage please see the +.Xr sctp_recvmsg 3 +function call. .Sh RETURN VALUES The call returns the number of bytes read on success and -1 upon failure. .Sh ERRORS diff --git a/lib/libc/sys/sctp_generic_sendmsg.2 b/lib/libc/sys/sctp_generic_sendmsg.2 index d7050f2..fee4211 100644 --- a/lib/libc/sys/sctp_generic_sendmsg.2 +++ b/lib/libc/sys/sctp_generic_sendmsg.2 @@ -37,7 +37,7 @@ .Sh NAME .Nm sctp_generic_sendmsg .Nm sctp_generic_sendmsg_iov -.Nd send data to a peer. +.Nd send data to a peer .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -49,21 +49,25 @@ .Ft int .Fn sctp_generic_sendmsg_iov "int s" "struct iovec *iov" "int iovlen" "struct sockaddr *to" "struct sctp_sndrcvinfo *sinfo" "int flags" .Sh DESCRIPTION -The .Fn sctp_generic_sendmsg and .Fn sctp_generic_sendmsg_iov are the true system calls used by the -.Fn sctp_sendmsg +.Xr sctp_sendmsg 3 and -.Fn sctp_send -function calls. These are more efficient since they are -true system calls but they are specific to FreeBSD and -can be expected NOT to be present on any other Operating -System. For detailed useage please see either the -.Fn sctp_send +.Xr sctp_send 3 +function calls. +These are more efficient since they are +true system calls but they are specific to +.Fx +and can be expected +.Em not +to be present on any other operating +system. +For detailed usage please see either the +.Xr sctp_send 3 or -.Fn sctp_sendmsg +.Xr sctp_sendmsg 3 function calls. .Sh RETURN VALUES The call returns the number of bytes written on success and -1 upon failure. diff --git a/lib/libc/sys/sctp_peeloff.2 b/lib/libc/sys/sctp_peeloff.2 index 0ac5be6..c513afa 100644 --- a/lib/libc/sys/sctp_peeloff.2 +++ b/lib/libc/sys/sctp_peeloff.2 @@ -36,7 +36,7 @@ .Os .Sh NAME .Nm sctp_peeloff -.Nd detach an association from a one-to-many socket to its on fd +.Nd detach an association from a one-to-many socket to its own fd .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -58,7 +58,7 @@ upon success. .Sh ERRORS The .Fn sctp_peeloff -can return the following errors. +system call can return the following errors: .Bl -tag -width Er .It Bq Er ENOTCONN The diff --git a/lib/libc/sys/sendfile.2 b/lib/libc/sys/sendfile.2 index 322971f..d9f8cab 100644 --- a/lib/libc/sys/sendfile.2 +++ b/lib/libc/sys/sendfile.2 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 24, 2006 +.Dd January 7, 2010 .Dt SENDFILE 2 .Os .Sh NAME @@ -116,9 +116,17 @@ Busy servers may benefit by transferring requests that would block to a separate I/O worker thread. .It .Dv SF_MNOWAIT . -(description missing) +Do not wait for some kernel resource to become available, +in particular, +.Vt mbuf +and +.Vt sf_buf . +The flag does not make the +.Fn sendfile +syscall truly non-blocking, since other resources are still allocated +in a blocking fashion. .It -.Dv SF_SYNC , +.Dv SF_SYNC . .Nm sleeps until the network stack no longer references the VM pages of the file, making subsequent modifications to it safe. diff --git a/lib/libc/sys/setpgid.2 b/lib/libc/sys/setpgid.2 index a56d831..4ad89de 100644 --- a/lib/libc/sys/setpgid.2 +++ b/lib/libc/sys/setpgid.2 @@ -54,6 +54,11 @@ to the specified If .Fa pid is zero, then the call applies to the current process. +If +.Fa pgrp +is zero, then the process id of the process specified by +.Fa pid +is used instead. .Pp If the affected process is not the invoking process, then it must be a child of the invoking process, it must not have performed an diff --git a/lib/libc/sys/shm_open.2 b/lib/libc/sys/shm_open.2 index 5c5d694..a586fe3 100644 --- a/lib/libc/sys/shm_open.2 +++ b/lib/libc/sys/shm_open.2 @@ -39,6 +39,7 @@ .Sh SYNOPSIS .In sys/types.h .In sys/mman.h +.In fcntl.h .Ft int .Fn shm_open "const char *path" "int flags" "mode_t mode" .Ft int diff --git a/lib/libc/sys/unlink.2 b/lib/libc/sys/unlink.2 index 41a2288..141f3d3 100644 --- a/lib/libc/sys/unlink.2 +++ b/lib/libc/sys/unlink.2 @@ -114,6 +114,8 @@ succeeds unless: .Bl -tag -width Er .It Bq Er ENOTDIR A component of the path prefix is not a directory. +.It Bq Er EISDIR +The named file is a directory. .It Bq Er ENAMETOOLONG A component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters. diff --git a/lib/libc/sys/vfork.2 b/lib/libc/sys/vfork.2 index 928130b..3cd3368 100644 --- a/lib/libc/sys/vfork.2 +++ b/lib/libc/sys/vfork.2 @@ -28,12 +28,12 @@ .\" @(#)vfork.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 4, 1993 +.Dd November 13, 2009 .Dt VFORK 2 .Os .Sh NAME .Nm vfork -.Nd spawn new process in a virtual memory efficient way +.Nd create a new process without copying the address space .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -113,14 +113,6 @@ The system call appeared in .Bx 2.9 . .Sh BUGS -This system call will be eliminated when proper system sharing -mechanisms are implemented. -Users should not depend on the memory -sharing semantics of -.Fn vfork -as it will, in that case, be made synonymous to -.Xr fork 2 . -.Pp To avoid a possible deadlock situation, processes that are children in the middle of a diff --git a/lib/libc/sys/wait.2 b/lib/libc/sys/wait.2 index c492cd6..2e084d2 100644 --- a/lib/libc/sys/wait.2 +++ b/lib/libc/sys/wait.2 @@ -44,10 +44,10 @@ .In sys/wait.h .Ft pid_t .Fn wait "int *status" -.In sys/time.h -.In sys/resource.h .Ft pid_t .Fn waitpid "pid_t wpid" "int *status" "int options" +.In sys/time.h +.In sys/resource.h .Ft pid_t .Fn wait3 "int *status" "int options" "struct rusage *rusage" .Ft pid_t diff --git a/lib/libc/yp/yplib.c b/lib/libc/yp/yplib.c index eb5c34c..87d16dd 100644 --- a/lib/libc/yp/yplib.c +++ b/lib/libc/yp/yplib.c @@ -241,7 +241,7 @@ static bool_t ypmatch_cache_lookup(struct dom_binding *ypdb, char *map, keydat *key, valdat *val) { - struct ypmatch_ent *c = ypdb->cache; + struct ypmatch_ent *c; ypmatch_cache_expire(ypdb); |