1088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/* 2088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * os-posix.c 3088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * 4088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * Copyright (c) 2003-2008 Fabrice Bellard 5088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * Copyright (c) 2010 Red Hat, Inc. 6088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * 7088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * Permission is hereby granted, free of charge, to any person obtaining a copy 8088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * of this software and associated documentation files (the "Software"), to deal 9088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * in the Software without restriction, including without limitation the rights 10088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * copies of the Software, and to permit persons to whom the Software is 12088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * furnished to do so, subject to the following conditions: 13088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * 14088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * The above copyright notice and this permission notice shall be included in 15088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * all copies or substantial portions of the Software. 16088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * 17088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * THE SOFTWARE. 24088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner */ 25088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 26088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <unistd.h> 27088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <fcntl.h> 28088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <signal.h> 29088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/types.h> 30088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/wait.h> 31088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/*needed for MAP_POPULATE before including qemu-options.h */ 32088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/mman.h> 33088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <pwd.h> 34088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <libgen.h> 35088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 36088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/* Needed early for CONFIG_BSD etc. */ 37088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include "config-host.h" 38088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include "sysemu.h" 39088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include "net.h" 40088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include "qemu-options.h" 41088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 42088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#ifdef CONFIG_LINUX 43088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/prctl.h> 44088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/syscall.h> 45088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 46088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 47088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#ifdef CONFIG_EVENTFD 48088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#include <sys/eventfd.h> 49088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 50088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 51088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic struct passwd *user_pwd; 52088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic const char *chroot_dir; 53088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic int daemonize; 54088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic int fds[2]; 55088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 56088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_setup_early_signal_handling(void) 57088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 58088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner struct sigaction act; 59088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigfillset(&act.sa_mask); 60088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner act.sa_flags = 0; 61088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner act.sa_handler = SIG_IGN; 62088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigaction(SIGPIPE, &act, NULL); 63088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 64088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 65088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic void termsig_handler(int signal) 66088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 67088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner qemu_system_shutdown_request(); 68088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 69088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 70088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic void sigchld_handler(int signal) 71088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 72088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner waitpid(-1, NULL, WNOHANG); 73088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 74088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 75088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_setup_signal_handling(void) 76088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 77088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner struct sigaction act; 78088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 79088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner memset(&act, 0, sizeof(act)); 80088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner act.sa_handler = termsig_handler; 81088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigaction(SIGINT, &act, NULL); 82088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigaction(SIGHUP, &act, NULL); 83088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigaction(SIGTERM, &act, NULL); 84088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 85088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner act.sa_handler = sigchld_handler; 86088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner act.sa_flags = SA_NOCLDSTOP; 87088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner sigaction(SIGCHLD, &act, NULL); 88088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 89088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 90088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/* Find a likely location for support files using the location of the binary. 91088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner For installed binaries this will be "$bindir/../share/qemu". When 92088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner running from the build tree this will be "$bindir/../pc-bios". */ 93088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#define SHARE_SUFFIX "/share/qemu" 94088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#define BUILD_SUFFIX "/pc-bios" 95088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerchar *os_find_datadir(const char *argv0) 96088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 97088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char *dir; 98088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char *p = NULL; 99088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char *res; 100088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char buf[PATH_MAX]; 101088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner size_t max_len; 102088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 103088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#if defined(__linux__) 104088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner { 105088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner int len; 106088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); 107088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (len > 0) { 108088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner buf[len] = 0; 109088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner p = buf; 110088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 111088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 112088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#elif defined(__FreeBSD__) 113088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner { 114088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; 115088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner size_t len = sizeof(buf) - 1; 116088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 117088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner *buf = '\0'; 118088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && 119088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner *buf) { 120088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner buf[sizeof(buf) - 1] = '\0'; 121088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner p = buf; 122088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 123088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 124088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 125088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner /* If we don't have any way of figuring out the actual executable 126088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner location then try argv[0]. */ 127088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (!p) { 128088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner p = realpath(argv0, buf); 129088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (!p) { 130088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return NULL; 131088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 132088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 133088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner dir = dirname(p); 134088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner dir = dirname(dir); 135088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 136088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner max_len = strlen(dir) + 137088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; 138088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner res = qemu_mallocz(max_len); 139088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX); 140088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (access(res, R_OK)) { 141088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX); 142088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (access(res, R_OK)) { 143088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner qemu_free(res); 144088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner res = NULL; 145088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 146088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 147088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 148088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return res; 149088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 150088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#undef SHARE_SUFFIX 151088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#undef BUILD_SUFFIX 152088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 153088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_set_proc_name(const char *s) 154088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 155088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#if defined(PR_SET_NAME) 156088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char name[16]; 157088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (!s) 158088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return; 159088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner name[sizeof(name) - 1] = 0; 160088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner strncpy(name, s, sizeof(name)); 161088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner /* Could rewrite argv[0] too, but that's a bit more complicated. 162088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner This simple way is enough for `top'. */ 163088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (prctl(PR_SET_NAME, name)) { 164088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner perror("unable to change process name"); 165088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 166088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 167088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#else 168088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Change of process name not supported by your OS\n"); 169088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 170088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 171088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 172088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 173088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/* 174088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * Parse OS specific command line options. 175088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * return 0 if option handled, -1 otherwise 176088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner */ 177088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_parse_cmd_args(int index, const char *optarg) 178088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 179088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner switch (index) { 180088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#ifdef CONFIG_SLIRP 181088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner case QEMU_OPTION_smb: 182088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#if 1 183088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner net_slirp_smb(optarg); 184088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#else 185088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (net_slirp_smb(optarg) < 0) 186088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 187088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 188088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner break; 189088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 190088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner case QEMU_OPTION_runas: 191088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner user_pwd = getpwnam(optarg); 192088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (!user_pwd) { 193088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); 194088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 195088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 196088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner break; 197088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner case QEMU_OPTION_chroot: 198088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner chroot_dir = optarg; 199088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner break; 200088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner case QEMU_OPTION_daemonize: 201088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner daemonize = 1; 202088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner break; 203088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 204088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return; 205088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 206088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 207088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic void change_process_uid(void) 208088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 209088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (user_pwd) { 210088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (setgid(user_pwd->pw_gid) < 0) { 211088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); 212088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 213088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 214088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (setuid(user_pwd->pw_uid) < 0) { 215088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); 216088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 217088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 218088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (setuid(0) != -1) { 219088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Dropping privileges failed\n"); 220088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 221088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 222088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 223088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 224088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 225088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerstatic void change_root(void) 226088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 227088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (chroot_dir) { 228088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (chroot(chroot_dir) < 0) { 229088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "chroot failed\n"); 230088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 231088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 232088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (chdir("/")) { 233088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner perror("not able to chdir to /"); 234088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 235088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 236088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 237088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 238088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 239088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 240088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_daemonize(void) 241088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 242088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (daemonize) { 243088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner pid_t pid; 244088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 245088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (pipe(fds) == -1) 246088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 247088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 248088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner pid = fork(); 249088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (pid > 0) { 250088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner uint8_t status; 251088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner ssize_t len; 252088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 253088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner close(fds[1]); 254088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 255088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner again: 256088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner len = read(fds[0], &status, 1); 257088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (len == -1 && (errno == EINTR)) 258088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner goto again; 259088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 260088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (len != 1) 261088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 262088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner else if (status == 1) { 263088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno)); 264088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 265088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } else 266088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(0); 267088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } else if (pid < 0) 268088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 269088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 270088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner close(fds[0]); 271088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner qemu_set_cloexec(fds[1]); 272088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 273088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner setsid(); 274088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 275088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner pid = fork(); 276088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (pid > 0) 277088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(0); 278088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner else if (pid < 0) 279088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 280088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 281088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner umask(027); 282088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 283088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner signal(SIGTSTP, SIG_IGN); 284088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner signal(SIGTTOU, SIG_IGN); 285088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner signal(SIGTTIN, SIG_IGN); 286088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 287088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 288088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 289088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_setup_post(void) 290088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 291088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner int fd = 0; 292088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 293088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (daemonize) { 294088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner uint8_t status = 0; 295088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner ssize_t len; 296088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 297088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner again1: 298088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner len = write(fds[1], &status, 1); 299088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (len == -1 && (errno == EINTR)) 300088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner goto again1; 301088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 302088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (len != 1) 303088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 304088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 305088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (chdir("/")) { 306088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner perror("not able to chdir to /"); 307088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 308088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 309088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner TFR(fd = qemu_open("/dev/null", O_RDWR)); 310088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (fd == -1) 311088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner exit(1); 312088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 313088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 314088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner change_root(); 315088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner change_process_uid(); 316088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 317088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (daemonize) { 318088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner dup2(fd, 0); 319088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner dup2(fd, 1); 320088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner dup2(fd, 2); 321088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 322088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner close(fd); 323088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 324088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 325088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 326088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_pidfile_error(void) 327088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 328088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (daemonize) { 329088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner uint8_t status = 1; 330088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (write(fds[1], &status, 1) != 1) { 331088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner perror("daemonize. Writing to pipe\n"); 332088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 333088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } else 334088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); 335088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 336088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 337088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnervoid os_set_line_buffering(void) 338088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 339088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner setvbuf(stdout, NULL, _IOLBF, 0); 340088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 341088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 342088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner/* 343088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set. 344088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner */ 345088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerint qemu_eventfd(int fds[2]) 346088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 347088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#ifdef CONFIG_EVENTFD 348088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner int ret; 349088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 350088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner ret = eventfd(0, 0); 351088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (ret >= 0) { 352088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fds[0] = ret; 353088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner qemu_set_cloexec(ret); 354088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if ((fds[1] = dup(ret)) == -1) { 355088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner close(ret); 356088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return -1; 357088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 358088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner qemu_set_cloexec(fds[1]); 359088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return 0; 360088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 361088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 362088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (errno != ENOSYS) { 363088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return -1; 364088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 365088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 366088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 367088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return qemu_pipe(fds); 368088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 369088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 370088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerint qemu_create_pidfile(const char *filename) 371088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 372088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner char buffer[128]; 373088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner int len; 374088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner int fd; 375088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 376088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); 377088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (fd == -1) { 378088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return -1; 379088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 380088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (lockf(fd, F_TLOCK, 0) == -1) { 381088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return -1; 382088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 383088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); 384088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner if (write(fd, buffer, len) != len) { 385088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return -1; 386088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner } 387088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 388088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return 0; 389088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 390088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner 391088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turnerint qemu_get_thread_id(void) 392088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner{ 393088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#if defined (__linux__) 394088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return syscall(SYS_gettid); 395088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#else 396088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner return getpid(); 397088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner#endif 398088edf82b3d34409ed9d9fd09ec1f7e9b933304fDavid 'Digit' Turner} 399