1179838Sdavidxu/*- 2179840Sed * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org> 3179838Sdavidxu * All rights reserved. 4179838Sdavidxu * 5179838Sdavidxu * Redistribution and use in source and binary forms, with or without 6179838Sdavidxu * modification, are permitted provided that the following conditions 7179838Sdavidxu * are met: 8179838Sdavidxu * 1. Redistributions of source code must retain the above copyright 9179838Sdavidxu * notice, this list of conditions and the following disclaimer. 10179838Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright 11179838Sdavidxu * notice, this list of conditions and the following disclaimer in the 12179838Sdavidxu * documentation and/or other materials provided with the distribution. 13179838Sdavidxu * 14179838Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15179838Sdavidxu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16179838Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17179838Sdavidxu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18179838Sdavidxu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19179838Sdavidxu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20179838Sdavidxu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21179838Sdavidxu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22179838Sdavidxu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23179838Sdavidxu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24179838Sdavidxu * SUCH DAMAGE. 25179838Sdavidxu */ 26179838Sdavidxu 27179838Sdavidxu#include <sys/cdefs.h> 28179838Sdavidxu__FBSDID("$FreeBSD$"); 29179838Sdavidxu 30179838Sdavidxu#include "namespace.h" 31179838Sdavidxu#include <sys/queue.h> 32223907Sjilles#include <sys/wait.h> 33179838Sdavidxu 34179838Sdavidxu#include <errno.h> 35179838Sdavidxu#include <fcntl.h> 36179838Sdavidxu#include <sched.h> 37179838Sdavidxu#include <spawn.h> 38179838Sdavidxu#include <signal.h> 39179838Sdavidxu#include <stdlib.h> 40179838Sdavidxu#include <string.h> 41179838Sdavidxu#include <unistd.h> 42179838Sdavidxu#include "un-namespace.h" 43179947Sed#include "libc_private.h" 44179838Sdavidxu 45179838Sdavidxuextern char **environ; 46179838Sdavidxu 47179838Sdavidxustruct __posix_spawnattr { 48179838Sdavidxu short sa_flags; 49179838Sdavidxu pid_t sa_pgroup; 50179838Sdavidxu struct sched_param sa_schedparam; 51179838Sdavidxu int sa_schedpolicy; 52179838Sdavidxu sigset_t sa_sigdefault; 53179838Sdavidxu sigset_t sa_sigmask; 54179838Sdavidxu}; 55179838Sdavidxu 56179838Sdavidxustruct __posix_spawn_file_actions { 57179838Sdavidxu STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; 58179838Sdavidxu}; 59179838Sdavidxu 60179838Sdavidxutypedef struct __posix_spawn_file_actions_entry { 61179838Sdavidxu STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; 62179838Sdavidxu enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action; 63179838Sdavidxu 64179838Sdavidxu int fae_fildes; 65179838Sdavidxu union { 66179838Sdavidxu struct { 67179838Sdavidxu char *path; 68179838Sdavidxu#define fae_path fae_data.open.path 69179838Sdavidxu int oflag; 70179838Sdavidxu#define fae_oflag fae_data.open.oflag 71179838Sdavidxu mode_t mode; 72179838Sdavidxu#define fae_mode fae_data.open.mode 73179838Sdavidxu } open; 74179838Sdavidxu struct { 75179838Sdavidxu int newfildes; 76179838Sdavidxu#define fae_newfildes fae_data.dup2.newfildes 77179838Sdavidxu } dup2; 78179838Sdavidxu } fae_data; 79179838Sdavidxu} posix_spawn_file_actions_entry_t; 80179838Sdavidxu 81179838Sdavidxu/* 82179838Sdavidxu * Spawn routines 83179838Sdavidxu */ 84179838Sdavidxu 85179838Sdavidxustatic int 86179838Sdavidxuprocess_spawnattr(const posix_spawnattr_t sa) 87179838Sdavidxu{ 88179838Sdavidxu struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL }; 89179838Sdavidxu int i; 90179838Sdavidxu 91179838Sdavidxu /* 92179838Sdavidxu * POSIX doesn't really describe in which order everything 93179838Sdavidxu * should be set. We'll just set them in the order in which they 94179838Sdavidxu * are mentioned. 95179838Sdavidxu */ 96179838Sdavidxu 97179838Sdavidxu /* Set process group */ 98179838Sdavidxu if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) { 99179838Sdavidxu if (setpgid(0, sa->sa_pgroup) != 0) 100179838Sdavidxu return (errno); 101179838Sdavidxu } 102179838Sdavidxu 103179838Sdavidxu /* Set scheduler policy */ 104179838Sdavidxu if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) { 105179838Sdavidxu if (sched_setscheduler(0, sa->sa_schedpolicy, 106179838Sdavidxu &sa->sa_schedparam) != 0) 107179838Sdavidxu return (errno); 108179838Sdavidxu } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) { 109179838Sdavidxu if (sched_setparam(0, &sa->sa_schedparam) != 0) 110179838Sdavidxu return (errno); 111179838Sdavidxu } 112179866Sdavidxu 113179866Sdavidxu /* Reset user ID's */ 114179866Sdavidxu if (sa->sa_flags & POSIX_SPAWN_RESETIDS) { 115179866Sdavidxu if (setegid(getgid()) != 0) 116179866Sdavidxu return (errno); 117179866Sdavidxu if (seteuid(getuid()) != 0) 118179866Sdavidxu return (errno); 119179866Sdavidxu } 120179866Sdavidxu 121179866Sdavidxu /* Set signal masks/defaults */ 122179866Sdavidxu if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { 123179866Sdavidxu _sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); 124179866Sdavidxu } 125179866Sdavidxu 126179866Sdavidxu if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { 127179866Sdavidxu for (i = 1; i <= _SIG_MAXSIG; i++) { 128179866Sdavidxu if (sigismember(&sa->sa_sigdefault, i)) 129179866Sdavidxu if (_sigaction(i, &sigact, NULL) != 0) 130179866Sdavidxu return (errno); 131179866Sdavidxu } 132179866Sdavidxu } 133179866Sdavidxu 134179838Sdavidxu return (0); 135179838Sdavidxu} 136179838Sdavidxu 137179838Sdavidxustatic int 138179838Sdavidxuprocess_file_actions_entry(posix_spawn_file_actions_entry_t *fae) 139179838Sdavidxu{ 140179838Sdavidxu int fd; 141179838Sdavidxu 142179838Sdavidxu switch (fae->fae_action) { 143179838Sdavidxu case FAE_OPEN: 144179838Sdavidxu /* Perform an open(), make it use the right fd */ 145179838Sdavidxu fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode); 146179838Sdavidxu if (fd < 0) 147179838Sdavidxu return (errno); 148179838Sdavidxu if (fd != fae->fae_fildes) { 149179838Sdavidxu if (_dup2(fd, fae->fae_fildes) == -1) 150179838Sdavidxu return (errno); 151179838Sdavidxu if (_close(fd) != 0) { 152179838Sdavidxu if (errno == EBADF) 153179838Sdavidxu return (EBADF); 154179838Sdavidxu } 155179838Sdavidxu } 156179838Sdavidxu if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) 157179838Sdavidxu return (errno); 158179838Sdavidxu break; 159179838Sdavidxu case FAE_DUP2: 160179838Sdavidxu /* Perform a dup2() */ 161179838Sdavidxu if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) 162179838Sdavidxu return (errno); 163179838Sdavidxu if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) 164179838Sdavidxu return (errno); 165179838Sdavidxu break; 166179838Sdavidxu case FAE_CLOSE: 167222511Sjilles /* Perform a close(), do not fail if already closed */ 168222511Sjilles (void)_close(fae->fae_fildes); 169179838Sdavidxu break; 170179838Sdavidxu } 171179838Sdavidxu return (0); 172179838Sdavidxu} 173179838Sdavidxu 174179838Sdavidxustatic int 175179838Sdavidxuprocess_file_actions(const posix_spawn_file_actions_t fa) 176179838Sdavidxu{ 177179838Sdavidxu posix_spawn_file_actions_entry_t *fae; 178179838Sdavidxu int error; 179179838Sdavidxu 180179838Sdavidxu /* Replay all file descriptor modifications */ 181179838Sdavidxu STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { 182179838Sdavidxu error = process_file_actions_entry(fae); 183179838Sdavidxu if (error) 184179838Sdavidxu return (error); 185179838Sdavidxu } 186223576Sed return (0); 187179838Sdavidxu} 188179838Sdavidxu 189179838Sdavidxustatic int 190179838Sdavidxudo_posix_spawn(pid_t *pid, const char *path, 191179838Sdavidxu const posix_spawn_file_actions_t *fa, 192179838Sdavidxu const posix_spawnattr_t *sa, 193179838Sdavidxu char * const argv[], char * const envp[], int use_env_path) 194179838Sdavidxu{ 195179838Sdavidxu pid_t p; 196179838Sdavidxu volatile int error = 0; 197223576Sed 198179838Sdavidxu p = vfork(); 199179838Sdavidxu switch (p) { 200179838Sdavidxu case -1: 201179838Sdavidxu return (errno); 202179838Sdavidxu case 0: 203179838Sdavidxu if (sa != NULL) { 204179838Sdavidxu error = process_spawnattr(*sa); 205179838Sdavidxu if (error) 206179838Sdavidxu _exit(127); 207179838Sdavidxu } 208179838Sdavidxu if (fa != NULL) { 209179838Sdavidxu error = process_file_actions(*fa); 210179838Sdavidxu if (error) 211179838Sdavidxu _exit(127); 212179838Sdavidxu } 213179838Sdavidxu if (use_env_path) 214179947Sed _execvpe(path, argv, envp != NULL ? envp : environ); 215179838Sdavidxu else 216179838Sdavidxu _execve(path, argv, envp != NULL ? envp : environ); 217179838Sdavidxu error = errno; 218179838Sdavidxu _exit(127); 219179838Sdavidxu default: 220223907Sjilles if (error != 0) 221223907Sjilles _waitpid(p, NULL, WNOHANG); 222223907Sjilles else if (pid != NULL) 223179838Sdavidxu *pid = p; 224179838Sdavidxu return (error); 225179838Sdavidxu } 226179838Sdavidxu} 227179838Sdavidxu 228179838Sdavidxuint 229179838Sdavidxuposix_spawn(pid_t *pid, const char *path, 230179838Sdavidxu const posix_spawn_file_actions_t *fa, 231179838Sdavidxu const posix_spawnattr_t *sa, 232179838Sdavidxu char * const argv[], char * const envp[]) 233179838Sdavidxu{ 234179838Sdavidxu return do_posix_spawn(pid, path, fa, sa, argv, envp, 0); 235179838Sdavidxu} 236179838Sdavidxu 237179838Sdavidxuint 238179838Sdavidxuposix_spawnp(pid_t *pid, const char *path, 239179838Sdavidxu const posix_spawn_file_actions_t *fa, 240179838Sdavidxu const posix_spawnattr_t *sa, 241179838Sdavidxu char * const argv[], char * const envp[]) 242179838Sdavidxu{ 243179838Sdavidxu return do_posix_spawn(pid, path, fa, sa, argv, envp, 1); 244179838Sdavidxu} 245179838Sdavidxu 246179838Sdavidxu/* 247179838Sdavidxu * File descriptor actions 248179838Sdavidxu */ 249179838Sdavidxu 250179838Sdavidxuint 251179838Sdavidxuposix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) 252179838Sdavidxu{ 253179838Sdavidxu posix_spawn_file_actions_t fa; 254179838Sdavidxu 255179838Sdavidxu fa = malloc(sizeof(struct __posix_spawn_file_actions)); 256179838Sdavidxu if (fa == NULL) 257179838Sdavidxu return (-1); 258179838Sdavidxu 259179838Sdavidxu STAILQ_INIT(&fa->fa_list); 260179838Sdavidxu *ret = fa; 261179838Sdavidxu return (0); 262179838Sdavidxu} 263179838Sdavidxu 264179838Sdavidxuint 265179838Sdavidxuposix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) 266179838Sdavidxu{ 267179838Sdavidxu posix_spawn_file_actions_entry_t *fae; 268179838Sdavidxu 269179838Sdavidxu while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { 270179838Sdavidxu /* Remove file action entry from the queue */ 271179838Sdavidxu STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); 272179838Sdavidxu 273179838Sdavidxu /* Deallocate file action entry */ 274179838Sdavidxu if (fae->fae_action == FAE_OPEN) 275179838Sdavidxu free(fae->fae_path); 276179838Sdavidxu free(fae); 277179838Sdavidxu } 278179838Sdavidxu 279179838Sdavidxu free(*fa); 280179838Sdavidxu return (0); 281179838Sdavidxu} 282179838Sdavidxu 283179838Sdavidxuint 284179838Sdavidxuposix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, 285179838Sdavidxu int fildes, const char * __restrict path, int oflag, mode_t mode) 286179838Sdavidxu{ 287179838Sdavidxu posix_spawn_file_actions_entry_t *fae; 288179838Sdavidxu int error; 289179838Sdavidxu 290179838Sdavidxu if (fildes < 0) 291179838Sdavidxu return (EBADF); 292179838Sdavidxu 293179838Sdavidxu /* Allocate object */ 294179838Sdavidxu fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 295179838Sdavidxu if (fae == NULL) 296179838Sdavidxu return (errno); 297179838Sdavidxu 298179838Sdavidxu /* Set values and store in queue */ 299179838Sdavidxu fae->fae_action = FAE_OPEN; 300179838Sdavidxu fae->fae_path = strdup(path); 301179838Sdavidxu if (fae->fae_path == NULL) { 302179838Sdavidxu error = errno; 303179838Sdavidxu free(fae); 304179838Sdavidxu return (error); 305179838Sdavidxu } 306179838Sdavidxu fae->fae_fildes = fildes; 307179838Sdavidxu fae->fae_oflag = oflag; 308179838Sdavidxu fae->fae_mode = mode; 309179838Sdavidxu 310179838Sdavidxu STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 311179838Sdavidxu return (0); 312179838Sdavidxu} 313179838Sdavidxu 314179838Sdavidxuint 315179838Sdavidxuposix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, 316179838Sdavidxu int fildes, int newfildes) 317179838Sdavidxu{ 318179838Sdavidxu posix_spawn_file_actions_entry_t *fae; 319179838Sdavidxu 320179838Sdavidxu if (fildes < 0 || newfildes < 0) 321179838Sdavidxu return (EBADF); 322179838Sdavidxu 323179838Sdavidxu /* Allocate object */ 324179838Sdavidxu fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 325179838Sdavidxu if (fae == NULL) 326179838Sdavidxu return (errno); 327179838Sdavidxu 328179838Sdavidxu /* Set values and store in queue */ 329179838Sdavidxu fae->fae_action = FAE_DUP2; 330179838Sdavidxu fae->fae_fildes = fildes; 331179838Sdavidxu fae->fae_newfildes = newfildes; 332179838Sdavidxu 333179838Sdavidxu STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 334179838Sdavidxu return (0); 335179838Sdavidxu} 336179838Sdavidxu 337179841Sdavidxuint 338179841Sdavidxuposix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, 339179838Sdavidxu int fildes) 340179838Sdavidxu{ 341179838Sdavidxu posix_spawn_file_actions_entry_t *fae; 342179838Sdavidxu 343179838Sdavidxu if (fildes < 0) 344179838Sdavidxu return (EBADF); 345179838Sdavidxu 346179838Sdavidxu /* Allocate object */ 347179838Sdavidxu fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); 348179838Sdavidxu if (fae == NULL) 349179838Sdavidxu return (errno); 350179838Sdavidxu 351179838Sdavidxu /* Set values and store in queue */ 352179838Sdavidxu fae->fae_action = FAE_CLOSE; 353179838Sdavidxu fae->fae_fildes = fildes; 354179838Sdavidxu 355179838Sdavidxu STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); 356179838Sdavidxu return (0); 357179838Sdavidxu} 358179838Sdavidxu 359179838Sdavidxu/* 360179838Sdavidxu * Spawn attributes 361179838Sdavidxu */ 362179838Sdavidxu 363179838Sdavidxuint 364179838Sdavidxuposix_spawnattr_init(posix_spawnattr_t *ret) 365179838Sdavidxu{ 366179838Sdavidxu posix_spawnattr_t sa; 367179838Sdavidxu 368179838Sdavidxu sa = calloc(1, sizeof(struct __posix_spawnattr)); 369179838Sdavidxu if (sa == NULL) 370179838Sdavidxu return (errno); 371179838Sdavidxu 372179838Sdavidxu /* Set defaults as specified by POSIX, cleared above */ 373179838Sdavidxu *ret = sa; 374179838Sdavidxu return (0); 375179838Sdavidxu} 376179838Sdavidxu 377179838Sdavidxuint 378179838Sdavidxuposix_spawnattr_destroy(posix_spawnattr_t *sa) 379179838Sdavidxu{ 380179838Sdavidxu free(*sa); 381179838Sdavidxu return (0); 382179838Sdavidxu} 383179838Sdavidxu 384179838Sdavidxuint 385179838Sdavidxuposix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, 386179838Sdavidxu short * __restrict flags) 387179838Sdavidxu{ 388179838Sdavidxu *flags = (*sa)->sa_flags; 389179838Sdavidxu return (0); 390179838Sdavidxu} 391179838Sdavidxu 392179838Sdavidxuint 393179838Sdavidxuposix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, 394179838Sdavidxu pid_t * __restrict pgroup) 395179838Sdavidxu{ 396179838Sdavidxu *pgroup = (*sa)->sa_pgroup; 397179838Sdavidxu return (0); 398179838Sdavidxu} 399179838Sdavidxu 400179838Sdavidxuint 401179838Sdavidxuposix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, 402179838Sdavidxu struct sched_param * __restrict schedparam) 403179838Sdavidxu{ 404179838Sdavidxu *schedparam = (*sa)->sa_schedparam; 405179838Sdavidxu return (0); 406179838Sdavidxu} 407179838Sdavidxu 408179838Sdavidxuint 409179838Sdavidxuposix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, 410179838Sdavidxu int * __restrict schedpolicy) 411179838Sdavidxu{ 412179838Sdavidxu *schedpolicy = (*sa)->sa_schedpolicy; 413179838Sdavidxu return (0); 414179838Sdavidxu} 415179838Sdavidxu 416179838Sdavidxuint 417179838Sdavidxuposix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, 418179838Sdavidxu sigset_t * __restrict sigdefault) 419179838Sdavidxu{ 420179838Sdavidxu *sigdefault = (*sa)->sa_sigdefault; 421179838Sdavidxu return (0); 422179838Sdavidxu} 423179838Sdavidxu 424179838Sdavidxuint 425179838Sdavidxuposix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, 426179838Sdavidxu sigset_t * __restrict sigmask) 427179838Sdavidxu{ 428179838Sdavidxu *sigmask = (*sa)->sa_sigmask; 429179838Sdavidxu return (0); 430179838Sdavidxu} 431179838Sdavidxu 432179838Sdavidxuint 433179838Sdavidxuposix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) 434179838Sdavidxu{ 435179838Sdavidxu (*sa)->sa_flags = flags; 436179838Sdavidxu return (0); 437179838Sdavidxu} 438179838Sdavidxu 439179838Sdavidxuint 440179838Sdavidxuposix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) 441179838Sdavidxu{ 442179838Sdavidxu (*sa)->sa_pgroup = pgroup; 443179838Sdavidxu return (0); 444179838Sdavidxu} 445179838Sdavidxu 446179838Sdavidxuint 447184203Srdivackyposix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, 448179838Sdavidxu const struct sched_param * __restrict schedparam) 449179838Sdavidxu{ 450179838Sdavidxu (*sa)->sa_schedparam = *schedparam; 451179838Sdavidxu return (0); 452179838Sdavidxu} 453179838Sdavidxu 454179838Sdavidxuint 455179838Sdavidxuposix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) 456179838Sdavidxu{ 457179838Sdavidxu (*sa)->sa_schedpolicy = schedpolicy; 458179838Sdavidxu return (0); 459179838Sdavidxu} 460179838Sdavidxu 461179838Sdavidxuint 462179838Sdavidxuposix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, 463179838Sdavidxu const sigset_t * __restrict sigdefault) 464179838Sdavidxu{ 465179838Sdavidxu (*sa)->sa_sigdefault = *sigdefault; 466179838Sdavidxu return (0); 467179838Sdavidxu} 468179838Sdavidxu 469179838Sdavidxuint 470179838Sdavidxuposix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, 471179838Sdavidxu const sigset_t * __restrict sigmask) 472179838Sdavidxu{ 473179838Sdavidxu (*sa)->sa_sigmask = *sigmask; 474179838Sdavidxu return (0); 475179838Sdavidxu} 476