jcs's openbsd hax
openbsd
1/* $OpenBSD: findfp.c,v 1.24 2025/08/19 02:34:31 jsg Exp $ */
2/*-
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/param.h> /* ALIGN ALIGNBYTES */
35#include <errno.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include "local.h"
43#include "glue.h"
44#include "thread_private.h"
45
46int __sdidinit;
47
48#define NDYNAMIC 10 /* add ten more whenever necessary */
49
50
51#define std(flags, file, cookie) \
52 { ._flags = (flags), ._file = (file), ._cookie = (cookie), \
53 ._close = __sclose, ._read = __sread, ._seek = __sseek, \
54 ._write = __swrite, ._lock = __RCMTX_INITIALIZER() }
55
56 /* the usual - (stdin + stdout + stderr) */
57static FILE usual[FOPEN_MAX - 3];
58static struct glue uglue = { 0, FOPEN_MAX - 3, usual };
59static struct glue *lastglue = &uglue;
60static void *sfp_mutex;
61
62/*
63 * These are separate variables because they may end up copied
64 * into program images via COPY relocations, so their addresses
65 * won't be related. That also means they need separate glue :(
66 */
67FILE __stdin[1] = { std(__SRD, STDIN_FILENO, __stdin) };
68FILE __stdout[1] = { std(__SWR, STDOUT_FILENO, __stdout) };
69FILE __stderr[1] = { std(__SWR|__SNBF, STDERR_FILENO, __stderr) };
70
71static struct glue sglue2 = { &uglue, 1, __stderr };
72static struct glue sglue1 = { &sglue2, 1, __stdout };
73struct glue __sglue = { &sglue1, 1, __stdin };
74
75static struct glue *
76moreglue(int n)
77{
78 struct glue *g;
79 char *data;
80
81 data = calloc(1, sizeof(*g) + ALIGNBYTES + n * sizeof(FILE));
82 if (data == NULL)
83 return (NULL);
84 g = (struct glue *)data;
85 g->next = NULL;
86 g->niobs = n;
87 g->iobs = (FILE *)ALIGN(data + sizeof(*g));
88 return (g);
89}
90
91/*
92 * Find a free FILE for fopen et al.
93 */
94FILE *
95__sfp(void)
96{
97 FILE *fp;
98 int n;
99 struct glue *g;
100
101 if (!__sdidinit)
102 __sinit();
103
104 _MUTEX_LOCK(&sfp_mutex);
105 for (g = &__sglue; g != NULL; g = g->next) {
106 for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
107 if (fp->_flags == 0)
108 goto found;
109 }
110
111 /* release lock while mallocing */
112 _MUTEX_UNLOCK(&sfp_mutex);
113 if ((g = moreglue(NDYNAMIC)) == NULL)
114 return (NULL);
115 _MUTEX_LOCK(&sfp_mutex);
116 lastglue->next = g;
117 lastglue = g;
118 fp = g->iobs;
119found:
120 fp->_flags = 1; /* reserve this slot; caller sets real flags */
121 _MUTEX_UNLOCK(&sfp_mutex);
122
123 /* make sure this next memset covers everything but _flags */
124 extern char _ctassert[(offsetof(FILE, _flags) == 0) ? 1 : -1 ]
125 __attribute__((__unused__));
126
127 memset((char *)fp + sizeof fp->_flags, 0, sizeof *fp -
128 sizeof fp->_flags);
129 fp->_file = -1; /* no file */
130 __rcmtx_init(&fp->_lock);
131
132 return (fp);
133}
134
135/*
136 * exit() calls _cleanup() through the callback registered
137 * with __atexit_register_cleanup(), set whenever we open or buffer a
138 * file. This chicanery is done so that programs that do not use stdio
139 * need not link it all in.
140 *
141 * The name `_cleanup' is, alas, fairly well known outside stdio.
142 */
143void
144_cleanup(void)
145{
146 /* (void) _fwalk(fclose); */
147 (void) _fwalk(__sflush); /* `cheating' */
148}
149
150/*
151 * __sinit() is called whenever stdio's internal variables must be set up.
152 */
153void
154__sinit(void)
155{
156 static void *sinit_mutex;
157
158 _MUTEX_LOCK(&sinit_mutex);
159 if (__sdidinit)
160 goto out; /* bail out if caller lost the race */
161
162 /* make sure we clean up on exit */
163 __atexit_register_cleanup(_cleanup); /* conservative */
164 __sdidinit = 1;
165out:
166 _MUTEX_UNLOCK(&sinit_mutex);
167}