1 /*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Chris Torek. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)findfp.c 8.2 (Berkeley) 1/4/94 37 * $FreeBSD: src/lib/libc/stdio/findfp.c,v 1.7.2.3 2001/08/17 02:56:31 peter Exp $ 38 * $DragonFly: src/lib/libc/stdio/findfp.c,v 1.9 2005/07/23 23:14:44 joerg Exp $ 39 */ 40 41 #include <sys/param.h> 42 #include <machine/atomic.h> 43 #include <unistd.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 48 #include <spinlock.h> 49 50 #include <libc_private.h> 51 #include "local.h" 52 #include "priv_stdio.h" 53 54 int __sdidinit; 55 56 #define NDYNAMIC 10 /* add ten more whenever necessary */ 57 58 #define std(flags, file) \ 59 {{0,flags,file,0,0,0},{NULL, 0},__sF+file,__sclose,__sread,__sseek,__swrite, \ 60 {NULL,0}, 0, {0,0,0}, {0}, {NULL,0}, 0,0, NULL, PTHREAD_MUTEX_INITIALIZER, NULL, 0 } 61 /* p flags file r w _bf cookie close read seek write */ 62 /* _ub */ 63 64 /* the usual - (stdin + stdout + stderr) */ 65 static FILE usual[FOPEN_MAX - 3]; 66 static struct glue uglue = { NULL, FOPEN_MAX - 3, usual }; 67 68 FILE __sF[3] = { 69 std(__SRD, STDIN_FILENO), /* stdin */ 70 std(__SWR, STDOUT_FILENO), /* stdout */ 71 std(__SWR|__SNBF, STDERR_FILENO) /* stderr */ 72 }; 73 74 /* 75 * note: __sglue starts the walk chain for exit flushing and other things. 76 */ 77 struct glue __sglue = { &uglue, 3, __sF }; /* GLOBAL, START OF LIST */ 78 static struct glue *lastglue = &uglue; 79 80 /* 81 * The following kludge is done to ensure enough binary compatibility 82 * with future versions of libc. Or rather it allows us to work with 83 * libraries that have been built with a newer libc that defines these 84 * symbols and expects libc to provide them. We only have need to support 85 * i386 and alpha because they are the only "old" systems we have deployed. 86 */ 87 FILE *__stdinp = &__sF[0]; 88 FILE *__stdoutp = &__sF[1]; 89 FILE *__stderrp = &__sF[2]; 90 91 static struct glue * moreglue (int); 92 93 static spinlock_t thread_lock = _SPINLOCK_INITIALIZER; 94 #define THREAD_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock) 95 #define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock) 96 97 #if NOT_YET 98 #define SET_GLUE_PTR(ptr, val) atomic_set_ptr(&(ptr), (uintptr_t)(val)) 99 #else 100 #define SET_GLUE_PTR(ptr, val) ptr = val 101 #endif 102 103 static 104 struct glue * 105 moreglue(int n) 106 { 107 struct glue *g; 108 static FILE empty; 109 FILE *p; 110 111 g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE)); 112 if (g == NULL) 113 return (NULL); 114 p = (FILE *)ALIGN(g + 1); 115 g->next = NULL; 116 g->niobs = n; 117 g->iobs = p; 118 while (--n >= 0) { 119 *p = empty; 120 p++; 121 } 122 return (g); 123 } 124 125 /* 126 * Find a free FILE for fopen et al. 127 */ 128 FILE * 129 __sfp(void) 130 { 131 FILE *fp; 132 int n; 133 struct glue *g; 134 135 if (!__sdidinit) 136 __sinit(); 137 /* 138 * The list must be locked because a FILE may be updated. 139 */ 140 THREAD_LOCK(); 141 for (g = &__sglue; g != NULL; g = g->next) { 142 for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) 143 if (fp->pub._flags == 0) 144 goto found; 145 } 146 THREAD_UNLOCK(); /* don't hold lock while malloc()ing. */ 147 if ((g = moreglue(NDYNAMIC)) == NULL) 148 return (NULL); 149 THREAD_LOCK(); /* reacquire the lock */ 150 SET_GLUE_PTR(lastglue->next, g); /* atomically append glue to list */ 151 lastglue = g; /* not atomic; only accessed when locked */ 152 fp = g->iobs; 153 found: 154 fp->pub._flags = 1; /* reserve this slot; caller sets real flags */ 155 THREAD_UNLOCK(); 156 fp->pub._p = NULL; /* no current pointer */ 157 fp->pub._w = 0; /* nothing to read or write */ 158 fp->pub._r = 0; 159 fp->_bf._base = NULL; /* no buffer */ 160 fp->_bf._size = 0; 161 fp->pub._lbfsize = 0; /* not line buffered */ 162 fp->pub._fileno = -1; /* no file */ 163 /* fp->_cookie = <any>; */ /* caller sets cookie, _read/_write etc */ 164 fp->_ub._base = NULL; /* no ungetc buffer */ 165 fp->_ub._size = 0; 166 fp->_lb._base = NULL; /* no line buffer */ 167 fp->_lb._size = 0; 168 fp->_up = NULL; 169 fp->fl_mutex = PTHREAD_MUTEX_INITIALIZER; 170 fp->fl_owner = NULL; 171 fp->fl_count = 0; 172 /* fp->_lock = NULL; */ 173 return (fp); 174 } 175 176 /* 177 * XXX. Force immediate allocation of internal memory. Not used by stdio, 178 * but documented historically for certain applications. Bad applications. 179 */ 180 void f_prealloc(void); 181 182 __warn_references(f_prealloc, 183 "warning: this program uses f_prealloc(), which is not recommended."); 184 185 void 186 f_prealloc(void) 187 { 188 struct glue *g; 189 int n; 190 191 n = getdtablesize() - FOPEN_MAX + 20; /* 20 for slop. */ 192 /* 193 * It should be safe to walk the list without locking it; 194 * new nodes are only added to the end and none are ever 195 * removed. 196 */ 197 for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next) 198 /* void */; 199 if ((n > 0) && ((g = moreglue(n)) != NULL)) { 200 THREAD_LOCK(); 201 SET_GLUE_PTR(lastglue->next, g); 202 lastglue = g; 203 THREAD_UNLOCK(); 204 } 205 } 206 207 /* 208 * exit() calls _cleanup() through *__cleanup, set whenever we 209 * open or buffer a file. This chicanery is done so that programs 210 * that do not use stdio need not link it all in. 211 * 212 * The name `_cleanup' is, alas, fairly well known outside stdio. 213 */ 214 void 215 _cleanup(void) 216 { 217 /* (void) _fwalk(fclose); */ 218 (void) _fwalk(__sflush); /* `cheating' */ 219 } 220 221 /* 222 * __sinit() is called whenever stdio's internal variables must be set up. 223 */ 224 void 225 __sinit(void) 226 { 227 THREAD_LOCK(); 228 if (__sdidinit == 0) { 229 /* Make sure we clean up on exit. */ 230 __cleanup = _cleanup; /* conservative */ 231 __sdidinit = 1; 232 } 233 THREAD_UNLOCK(); 234 } 235