@ -70,7 +70,6 @@
# include <time.h>
# include <time.h>
# include <sys/wait.h>
# include <sys/wait.h>
# include <ctype.h>
# include <ctype.h>
# include <sys/select.h>
# include <sys/stat.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/socket.h>
# include <fcntl.h>
# include <fcntl.h>
@ -362,6 +361,17 @@ static volatile sig_atomic_t WalReceiverRequested = false;
static volatile bool StartWorkerNeeded = true ;
static volatile bool StartWorkerNeeded = true ;
static volatile bool HaveCrashedWorker = false ;
static volatile bool HaveCrashedWorker = false ;
/* set when signals arrive */
static volatile sig_atomic_t pending_pm_pmsignal ;
static volatile sig_atomic_t pending_pm_child_exit ;
static volatile sig_atomic_t pending_pm_reload_request ;
static volatile sig_atomic_t pending_pm_shutdown_request ;
static volatile sig_atomic_t pending_pm_fast_shutdown_request ;
static volatile sig_atomic_t pending_pm_immediate_shutdown_request ;
/* event multiplexing object */
static WaitEventSet * pm_wait_set ;
# ifdef USE_SSL
# ifdef USE_SSL
/* Set when and if SSL has been initialized properly */
/* Set when and if SSL has been initialized properly */
static bool LoadedSSL = false ;
static bool LoadedSSL = false ;
@ -380,10 +390,14 @@ static void getInstallationPaths(const char *argv0);
static void checkControlFile ( void ) ;
static void checkControlFile ( void ) ;
static Port * ConnCreate ( int serverFd ) ;
static Port * ConnCreate ( int serverFd ) ;
static void ConnFree ( Port * port ) ;
static void ConnFree ( Port * port ) ;
static void SIGHUP_handler ( SIGNAL_ARGS ) ;
static void handle_pm_pmsignal_signal ( SIGNAL_ARGS ) ;
static void pmdie ( SIGNAL_ARGS ) ;
static void handle_pm_child_exit_signal ( SIGNAL_ARGS ) ;
static void reaper ( SIGNAL_ARGS ) ;
static void handle_pm_reload_request_signal ( SIGNAL_ARGS ) ;
static void sigusr1_handler ( SIGNAL_ARGS ) ;
static void handle_pm_shutdown_request_signal ( SIGNAL_ARGS ) ;
static void process_pm_pmsignal ( void ) ;
static void process_pm_child_exit ( void ) ;
static void process_pm_reload_request ( void ) ;
static void process_pm_shutdown_request ( void ) ;
static void process_startup_packet_die ( SIGNAL_ARGS ) ;
static void process_startup_packet_die ( SIGNAL_ARGS ) ;
static void dummy_handler ( SIGNAL_ARGS ) ;
static void dummy_handler ( SIGNAL_ARGS ) ;
static void StartupPacketTimeoutHandler ( void ) ;
static void StartupPacketTimeoutHandler ( void ) ;
@ -401,7 +415,6 @@ static int BackendStartup(Port *port);
static int ProcessStartupPacket ( Port * port , bool ssl_done , bool gss_done ) ;
static int ProcessStartupPacket ( Port * port , bool ssl_done , bool gss_done ) ;
static void SendNegotiateProtocolVersion ( List * unrecognized_protocol_options ) ;
static void SendNegotiateProtocolVersion ( List * unrecognized_protocol_options ) ;
static void processCancelRequest ( Port * port , void * pkt ) ;
static void processCancelRequest ( Port * port , void * pkt ) ;
static int initMasks ( fd_set * rmask ) ;
static void report_fork_failure_to_client ( Port * port , int errnum ) ;
static void report_fork_failure_to_client ( Port * port , int errnum ) ;
static CAC_state canAcceptConnections ( int backend_type ) ;
static CAC_state canAcceptConnections ( int backend_type ) ;
static bool RandomCancelKey ( int32 * cancel_key ) ;
static bool RandomCancelKey ( int32 * cancel_key ) ;
@ -609,26 +622,6 @@ PostmasterMain(int argc, char *argv[])
/*
/*
* Set up signal handlers for the postmaster process .
* Set up signal handlers for the postmaster process .
*
*
* In the postmaster , we use pqsignal_pm ( ) rather than pqsignal ( ) ( which
* is used by all child processes and client processes ) . That has a
* couple of special behaviors :
*
* 1. We tell sigaction ( ) to block all signals for the duration of the
* signal handler . This is faster than our old approach of
* blocking / unblocking explicitly in the signal handler , and it should also
* prevent excessive stack consumption if signals arrive quickly .
*
* 2. We do not set the SA_RESTART flag . This is because signals will be
* blocked at all times except when ServerLoop is waiting for something to
* happen , and during that window , we want signals to exit the select ( 2 )
* wait so that ServerLoop can respond if anything interesting happened .
* On some platforms , signals marked SA_RESTART would not cause the
* select ( ) wait to end .
*
* Child processes will generally want SA_RESTART , so pqsignal ( ) sets that
* flag . We expect children to set up their own handlers before
* unblocking signals .
*
* CAUTION : when changing this list , check for side - effects on the signal
* CAUTION : when changing this list , check for side - effects on the signal
* handling setup of child processes . See tcop / postgres . c ,
* handling setup of child processes . See tcop / postgres . c ,
* bootstrap / bootstrap . c , postmaster / bgwriter . c , postmaster / walwriter . c ,
* bootstrap / bootstrap . c , postmaster / bgwriter . c , postmaster / walwriter . c ,
@ -638,26 +631,19 @@ PostmasterMain(int argc, char *argv[])
pqinitmask ( ) ;
pqinitmask ( ) ;
PG_SETMASK ( & BlockSig ) ;
PG_SETMASK ( & BlockSig ) ;
pqsignal_pm ( SIGHUP , SIGHUP_handler ) ; /* reread config file and have
pqsignal ( SIGHUP , handle_pm_reload_request_signal ) ;
* children do same */
pqsignal ( SIGINT , handle_pm_shutdown_request_signal ) ;
pqsignal_pm ( SIGINT , pmdie ) ; /* send SIGTERM and shut down */
pqsignal ( SIGQUIT , handle_pm_shutdown_request_signal ) ;
pqsignal_pm ( SIGQUIT , pmdie ) ; /* send SIGQUIT and die */
pqsignal ( SIGTERM , handle_pm_shutdown_request_signal ) ;
pqsignal_pm ( SIGTERM , pmdie ) ; /* wait for children and shut down */
pqsignal ( SIGALRM , SIG_IGN ) ; /* ignored */
pqsignal_pm ( SIGALRM , SIG_IGN ) ; /* ignored */
pqsignal ( SIGPIPE , SIG_IGN ) ; /* ignored */
pqsignal_pm ( SIGPIPE , SIG_IGN ) ; /* ignored */
pqsignal ( SIGUSR1 , handle_pm_pmsignal_signal ) ;
pqsignal_pm ( SIGUSR1 , sigusr1_handler ) ; /* message from child process */
pqsignal ( SIGUSR2 , dummy_handler ) ; /* unused, reserve for children */
pqsignal_pm ( SIGUSR2 , dummy_handler ) ; /* unused, reserve for children */
pqsignal ( SIGCHLD , handle_pm_child_exit_signal ) ;
pqsignal_pm ( SIGCHLD , reaper ) ; /* handle child termination */
# ifdef SIGURG
/*
/* This may configure SIGURG, depending on platform. */
* Ignore SIGURG for now . Child processes may change this ( see
InitializeLatchSupport ( ) ;
* InitializeLatchSupport ) , but they will not receive any such signals
InitProcessLocalLatch ( ) ;
* until they wait on a latch .
*/
pqsignal_pm ( SIGURG , SIG_IGN ) ; /* ignored */
# endif
/*
/*
* No other place in Postgres should touch SIGTTIN / SIGTTOU handling . We
* No other place in Postgres should touch SIGTTIN / SIGTTOU handling . We
@ -667,17 +653,20 @@ PostmasterMain(int argc, char *argv[])
* child processes should just allow the inherited settings to stand .
* child processes should just allow the inherited settings to stand .
*/
*/
# ifdef SIGTTIN
# ifdef SIGTTIN
pqsignal_pm ( SIGTTIN , SIG_IGN ) ; /* ignored */
pqsignal ( SIGTTIN , SIG_IGN ) ; /* ignored */
# endif
# endif
# ifdef SIGTTOU
# ifdef SIGTTOU
pqsignal_pm ( SIGTTOU , SIG_IGN ) ; /* ignored */
pqsignal ( SIGTTOU , SIG_IGN ) ; /* ignored */
# endif
# endif
/* ignore SIGXFSZ, so that ulimit violations work like disk full */
/* ignore SIGXFSZ, so that ulimit violations work like disk full */
# ifdef SIGXFSZ
# ifdef SIGXFSZ
pqsignal_pm ( SIGXFSZ , SIG_IGN ) ; /* ignored */
pqsignal ( SIGXFSZ , SIG_IGN ) ; /* ignored */
# endif
# endif
/* Begin accepting signals. */
PG_SETMASK ( & UnBlockSig ) ;
/*
/*
* Options setup
* Options setup
*/
*/
@ -1698,105 +1687,107 @@ DetermineSleepTime(struct timeval *timeout)
}
}
}
}
/*
* Activate or deactivate notifications of server socket events . Since we
* don ' t currently have a way to remove events from an existing WaitEventSet ,
* we ' ll just destroy and recreate the whole thing . This is called during
* shutdown so we can wait for backends to exit without accepting new
* connections , and during crash reinitialization when we need to start
* listening for new connections again . The WaitEventSet will be freed in fork
* children by ClosePostmasterPorts ( ) .
*/
static void
ConfigurePostmasterWaitSet ( bool accept_connections )
{
int nsockets ;
if ( pm_wait_set )
FreeWaitEventSet ( pm_wait_set ) ;
pm_wait_set = NULL ;
/* How many server sockets do we need to wait for? */
nsockets = 0 ;
if ( accept_connections )
{
while ( nsockets < MAXLISTEN & &
ListenSocket [ nsockets ] ! = PGINVALID_SOCKET )
+ + nsockets ;
}
pm_wait_set = CreateWaitEventSet ( CurrentMemoryContext , 1 + nsockets ) ;
AddWaitEventToSet ( pm_wait_set , WL_LATCH_SET , PGINVALID_SOCKET , MyLatch ,
NULL ) ;
if ( accept_connections )
{
for ( int i = 0 ; i < nsockets ; i + + )
AddWaitEventToSet ( pm_wait_set , WL_SOCKET_ACCEPT , ListenSocket [ i ] ,
NULL , NULL ) ;
}
}
/*
/*
* Main idle loop of postmaster
* Main idle loop of postmaster
*
* NB : Needs to be called with signals blocked
*/
*/
static int
static int
ServerLoop ( void )
ServerLoop ( void )
{
{
fd_set readmask ;
int nSockets ;
time_t last_lockfile_recheck_time ,
time_t last_lockfile_recheck_time ,
last_touch_time ;
last_touch_time ;
WaitEvent events [ MAXLISTEN ] ;
int nevents ;
ConfigurePostmasterWaitSet ( true ) ;
last_lockfile_recheck_time = last_touch_time = time ( NULL ) ;
last_lockfile_recheck_time = last_touch_time = time ( NULL ) ;
nSockets = initMasks ( & readmask ) ;
for ( ; ; )
for ( ; ; )
{
{
fd_set rmask ;
int selres ;
time_t now ;
time_t now ;
struct timeval timeout ;
/*
DetermineSleepTime ( & timeout ) ;
* Wait for a connection request to arrive .
*
* We block all signals except while sleeping . That makes it safe for
* signal handlers , which again block all signals while executing , to
* do nontrivial work .
*
* If we are in PM_WAIT_DEAD_END state , then we don ' t want to accept
* any new connections , so we don ' t call select ( ) , and just sleep .
*/
memcpy ( ( char * ) & rmask , ( char * ) & readmask , sizeof ( fd_set ) ) ;
if ( pmState = = PM_WAIT_DEAD_END )
{
PG_SETMASK ( & UnBlockSig ) ;
pg_usleep ( 100000L ) ; /* 100 msec seems reasonable */
selres = 0 ;
PG_SETMASK ( & BlockSig ) ;
}
else
{
/* must set timeout each time; some OSes change it! */
struct timeval timeout ;
/* Needs to run with blocked signals! */
DetermineSleepTime ( & timeout ) ;
PG_SETMASK ( & UnBlockSig ) ;
selres = select ( nSockets , & rmask , NULL , NULL , & timeout ) ;
PG_SETMASK ( & BlockSig ) ;
nevents = WaitEventSetWait ( pm_wait_set ,
}
timeout . tv_sec * 1000 + timeout . tv_usec / 1000 ,
events ,
/* Now check the select() result */
lengthof ( events ) ,
if ( selres < 0 )
0 /* postmaster posts no wait_events */ ) ;
{
if ( errno ! = EINTR & & errno ! = EWOULDBLOCK )
{
ereport ( LOG ,
( errcode_for_socket_access ( ) ,
errmsg ( " select() failed in postmaster: %m " ) ) ) ;
return STATUS_ERROR ;
}
}
/*
/*
* New connection pending on any of our sockets ? If so , fork a child
* Latch set by signal handler , or new connection pending on any of
* process to deal with it .
* our sockets ? If the latter , fork a child process to deal with it .
*/
*/
if ( selres > 0 )
for ( int i = 0 ; i < nevents ; i + + )
{
{
int i ;
if ( events [ i ] . events & WL_LATCH_SET )
for ( i = 0 ; i < MAXLISTEN ; i + + )
{
{
if ( ListenSocket [ i ] = = PGINVALID_SOCKET )
ResetLatch ( MyLatch ) ;
break ;
if ( FD_ISSET ( ListenSocket [ i ] , & rmask ) )
/* Process work requested via signal handlers. */
if ( pending_pm_shutdown_request )
process_pm_shutdown_request ( ) ;
if ( pending_pm_child_exit )
process_pm_child_exit ( ) ;
if ( pending_pm_reload_request )
process_pm_reload_request ( ) ;
if ( pending_pm_pmsignal )
process_pm_pmsignal ( ) ;
}
else if ( events [ i ] . events & WL_SOCKET_ACCEPT )
{
Port * port ;
port = ConnCreate ( events [ i ] . fd ) ;
if ( port )
{
{
Port * port ;
BackendStartup ( port ) ;
port = ConnCreate ( ListenSocket [ i ] ) ;
/*
if ( port )
* We no longer need the open socket or port structure in
{
* this process
BackendStartup ( port ) ;
*/
StreamClose ( port - > sock ) ;
/*
ConnFree ( port ) ;
* We no longer need the open socket or port structure
* in this process
*/
StreamClose ( port - > sock ) ;
ConnFree ( port ) ;
}
}
}
}
}
}
}
@ -1939,34 +1930,6 @@ ServerLoop(void)
}
}
}
}
/*
* Initialise the masks for select ( ) for the ports we are listening on .
* Return the number of sockets to listen on .
*/
static int
initMasks ( fd_set * rmask )
{
int maxsock = - 1 ;
int i ;
FD_ZERO ( rmask ) ;
for ( i = 0 ; i < MAXLISTEN ; i + + )
{
int fd = ListenSocket [ i ] ;
if ( fd = = PGINVALID_SOCKET )
break ;
FD_SET ( fd , rmask ) ;
if ( fd > maxsock )
maxsock = fd ;
}
return maxsock + 1 ;
}
/*
/*
* Read a client ' s startup packet and do something according to it .
* Read a client ' s startup packet and do something according to it .
*
*
@ -2609,6 +2572,13 @@ ClosePostmasterPorts(bool am_syslogger)
{
{
int i ;
int i ;
/* Release resources held by the postmaster's WaitEventSet. */
if ( pm_wait_set )
{
FreeWaitEventSetAfterFork ( pm_wait_set ) ;
pm_wait_set = NULL ;
}
# ifndef WIN32
# ifndef WIN32
/*
/*
@ -2707,15 +2677,46 @@ InitProcessGlobals(void)
# endif
# endif
}
}
/*
* Child processes use SIGUSR1 to notify us of ' pmsignals ' . pg_ctl uses
* SIGUSR1 to ask postmaster to check for logrotate and promote files .
*/
static void
handle_pm_pmsignal_signal ( SIGNAL_ARGS )
{
int save_errno = errno ;
pending_pm_pmsignal = true ;
SetLatch ( MyLatch ) ;
errno = save_errno ;
}
/*
/*
* SIGHUP - - reread config files , and tell children to do same
* pg_ctl uses SIGHUP to request a reload of the configuration files .
*/
*/
static void
static void
SIGHUP_handler ( SIGNAL_ARGS )
handle_pm_ reload_request_signal ( SIGNAL_ARGS )
{
{
int save_errno = errno ;
int save_errno = errno ;
pending_pm_reload_request = true ;
SetLatch ( MyLatch ) ;
errno = save_errno ;
}
/*
* Re - read config files , and tell children to do same .
*/
static void
process_pm_reload_request ( void )
{
pending_pm_reload_request = false ;
ereport ( DEBUG2 ,
( errmsg_internal ( " postmaster received reload request signal " ) ) ) ;
if ( Shutdown < = SmartShutdown )
if ( Shutdown < = SmartShutdown )
{
{
ereport ( LOG ,
ereport ( LOG ,
@ -2771,26 +2772,72 @@ SIGHUP_handler(SIGNAL_ARGS)
write_nondefault_variables ( PGC_SIGHUP ) ;
write_nondefault_variables ( PGC_SIGHUP ) ;
# endif
# endif
}
}
}
/*
* pg_ctl uses SIGTERM , SIGINT and SIGQUIT to request different types of
* shutdown .
*/
static void
handle_pm_shutdown_request_signal ( SIGNAL_ARGS )
{
int save_errno = errno ;
switch ( postgres_signal_arg )
{
case SIGTERM :
/* smart is implied if the other two flags aren't set */
pending_pm_shutdown_request = true ;
break ;
case SIGINT :
pending_pm_fast_shutdown_request = true ;
pending_pm_shutdown_request = true ;
break ;
case SIGQUIT :
pending_pm_immediate_shutdown_request = true ;
pending_pm_shutdown_request = true ;
break ;
}
SetLatch ( MyLatch ) ;
errno = save_errno ;
errno = save_errno ;
}
}
/*
/*
* pmdie - - signal handler for processing various postmaster signals .
* Process shutdown request .
*/
*/
static void
static void
pmdie ( SIGNAL_ARGS )
process_pm_shutdown_request ( void )
{
{
int save_errno = errno ;
int mode ;
ereport ( DEBUG2 ,
ereport ( DEBUG2 ,
( errmsg_internal ( " postmaster received signal %d " ,
( errmsg_internal ( " postmaster received shutdown request signal " ) ) ) ;
postgres_signal_arg ) ) ) ;
switch ( postgres_signal_arg )
pending_pm_shutdown_request = false ;
/*
* If more than one shutdown request signal arrived since the last server
* loop , take the one that is the most immediate . That matches the
* priority that would apply if we processed them one by one in any order .
*/
if ( pending_pm_immediate_shutdown_request )
{
{
case SIGTERM :
pending_pm_immediate_shutdown_request = false ;
pending_pm_fast_shutdown_request = false ;
mode = ImmediateShutdown ;
}
else if ( pending_pm_fast_shutdown_request )
{
pending_pm_fast_shutdown_request = false ;
mode = FastShutdown ;
}
else
mode = SmartShutdown ;
switch ( mode )
{
case SmartShutdown :
/*
/*
* Smart Shutdown :
* Smart Shutdown :
@ -2830,7 +2877,7 @@ pmdie(SIGNAL_ARGS)
PostmasterStateMachine ( ) ;
PostmasterStateMachine ( ) ;
break ;
break ;
case SIGINT :
case FastShutdown :
/*
/*
* Fast Shutdown :
* Fast Shutdown :
@ -2871,7 +2918,7 @@ pmdie(SIGNAL_ARGS)
PostmasterStateMachine ( ) ;
PostmasterStateMachine ( ) ;
break ;
break ;
case SIGQUIT :
case ImmediateShutdown :
/*
/*
* Immediate Shutdown :
* Immediate Shutdown :
@ -2908,20 +2955,30 @@ pmdie(SIGNAL_ARGS)
PostmasterStateMachine ( ) ;
PostmasterStateMachine ( ) ;
break ;
break ;
}
}
}
static void
handle_pm_child_exit_signal ( SIGNAL_ARGS )
{
int save_errno = errno ;
pending_pm_child_exit = true ;
SetLatch ( MyLatch ) ;
errno = save_errno ;
errno = save_errno ;
}
}
/*
/*
* Reaper - - signal handler to cleanup after a child process dies .
* C leanup after a child process dies .
*/
*/
static void
static void
reaper ( SIGNAL_ARGS )
process_pm_child_exit ( void )
{
{
int save_errno = errno ;
int pid ; /* process id of dead child process */
int pid ; /* process id of dead child process */
int exitstatus ; /* its exit status */
int exitstatus ; /* its exit status */
pending_pm_child_exit = false ;
ereport ( DEBUG4 ,
ereport ( DEBUG4 ,
( errmsg_internal ( " reaping dead processes " ) ) ) ;
( errmsg_internal ( " reaping dead processes " ) ) ) ;
@ -3213,8 +3270,6 @@ reaper(SIGNAL_ARGS)
* or actions to make .
* or actions to make .
*/
*/
PostmasterStateMachine ( ) ;
PostmasterStateMachine ( ) ;
errno = save_errno ;
}
}
/*
/*
@ -3642,8 +3697,9 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus)
/*
/*
* Advance the postmaster ' s state machine and take actions as appropriate
* Advance the postmaster ' s state machine and take actions as appropriate
*
*
* This is common code for pmdie ( ) , reaper ( ) and sigusr1_handler ( ) , which
* This is common code for process_pm_shutdown_request ( ) ,
* receive the signals that might mean we need to change state .
* process_pm_child_exit ( ) and process_pm_pmsignal ( ) , which process the signals
* that might mean we need to change state .
*/
*/
static void
static void
PostmasterStateMachine ( void )
PostmasterStateMachine ( void )
@ -3796,6 +3852,9 @@ PostmasterStateMachine(void)
if ( pmState = = PM_WAIT_DEAD_END )
if ( pmState = = PM_WAIT_DEAD_END )
{
{
/* Don't allow any new socket connection events. */
ConfigurePostmasterWaitSet ( false ) ;
/*
/*
* PM_WAIT_DEAD_END state ends when the BackendList is entirely empty
* PM_WAIT_DEAD_END state ends when the BackendList is entirely empty
* ( ie , no dead_end children remain ) , and the archiver is gone too .
* ( ie , no dead_end children remain ) , and the archiver is gone too .
@ -3905,6 +3964,9 @@ PostmasterStateMachine(void)
pmState = PM_STARTUP ;
pmState = PM_STARTUP ;
/* crash recovery started, reset SIGKILL flag */
/* crash recovery started, reset SIGKILL flag */
AbortStartTime = 0 ;
AbortStartTime = 0 ;
/* start accepting server socket connection events again */
ConfigurePostmasterWaitSet ( true ) ;
}
}
}
}
@ -5013,12 +5075,16 @@ ExitPostmaster(int status)
}
}
/*
/*
* sigusr1_handler - handle signal conditions from child processes
* Handle pmsignal conditions representing requests from backends ,
* and check for promote and logrotate requests from pg_ctl .
*/
*/
static void
static void
sigusr1_handler ( SIGNAL_ARGS )
process_pm_pmsignal ( void )
{
{
int save_errno = errno ;
pending_pm_pmsignal = false ;
ereport ( DEBUG2 ,
( errmsg_internal ( " postmaster received pmsignal signal " ) ) ) ;
/*
/*
* RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
* RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
@ -5134,7 +5200,7 @@ sigusr1_handler(SIGNAL_ARGS)
/*
/*
* Try to advance postmaster ' s state machine , if a child requests it .
* Try to advance postmaster ' s state machine , if a child requests it .
*
*
* Be careful about the order of this action relative to sigusr1_handler ' s
* Be careful about the order of this action relative to this function ' s
* other actions . Generally , this should be after other actions , in case
* other actions . Generally , this should be after other actions , in case
* they have effects PostmasterStateMachine would need to know about .
* they have effects PostmasterStateMachine would need to know about .
* However , we should do it before the CheckPromoteSignal step , which
* However , we should do it before the CheckPromoteSignal step , which
@ -5159,8 +5225,6 @@ sigusr1_handler(SIGNAL_ARGS)
*/
*/
signal_child ( StartupPID , SIGUSR2 ) ;
signal_child ( StartupPID , SIGUSR2 ) ;
}
}
errno = save_errno ;
}
}
/*
/*