jcs's openbsd hax
openbsd
at jcs 167 lines 5.0 kB view raw
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}