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