1717
1818#include <assert.h>
1919#include <ctype.h>
20+ #include <errno.h>
2021#include <grp.h>
2122#include <pthread.h>
2223#include <pwd.h>
@@ -5870,6 +5871,76 @@ nc_server_config_cert_exp_notif_thread_wakeup(void)
58705871
58715872#endif /* NC_ENABLED_SSH_TLS */
58725873
5874+ /**
5875+ * @brief Wait for any pending updates to complete, then mark the server as "applying configuration".
5876+ *
5877+ * @return 0 on success, 1 on timeout.
5878+ */
5879+ static int
5880+ nc_server_config_update_start (void )
5881+ {
5882+ int r ;
5883+ struct timespec ts_timeout ;
5884+
5885+ /* get the time point of timeout */
5886+ nc_timeouttime_get (& ts_timeout , NC_SERVER_CONFIG_UPDATE_WAIT_TIMEOUT_SEC * 1000 );
5887+
5888+ while (nc_timeouttime_cur_diff (& ts_timeout ) > 0 ) {
5889+ /* WR LOCK */
5890+ r = pthread_rwlock_clockwrlock (& server_opts .config_lock , COMPAT_CLOCK_ID , & ts_timeout );
5891+ if (r == ETIMEDOUT ) {
5892+ break ;
5893+ } else if (r ) {
5894+ ERR (NULL , "Failed to acquire server configuration write lock (%s)." , strerror (r ));
5895+ return 1 ;
5896+ }
5897+
5898+ if (!server_opts .applying_config ) {
5899+ /* set the flag and end */
5900+ server_opts .applying_config = 1 ;
5901+
5902+ /* UNLOCK */
5903+ pthread_rwlock_unlock (& server_opts .config_lock );
5904+ return 0 ;
5905+ }
5906+
5907+ /* UNLOCK and wait */
5908+ pthread_rwlock_unlock (& server_opts .config_lock );
5909+ usleep (NC_TIMEOUT_STEP );
5910+ }
5911+
5912+ ERR (NULL , "Timeout expired while waiting for the server to apply the previous configuration." );
5913+ return 1 ;
5914+ }
5915+
5916+ /**
5917+ * @brief Clear the "applying configuration" flag once the configuration update is done.
5918+ */
5919+ static void
5920+ nc_server_config_update_end (void )
5921+ {
5922+ int r ;
5923+ struct timespec ts_timeout ;
5924+
5925+ /* get the time point of timeout */
5926+ nc_timeouttime_get (& ts_timeout , NC_SERVER_CONFIG_UPDATE_WAIT_TIMEOUT_SEC * 1000 );
5927+
5928+ /* WR LOCK */
5929+ r = pthread_rwlock_clockwrlock (& server_opts .config_lock , COMPAT_CLOCK_ID , & ts_timeout );
5930+ if (r ) {
5931+ /* just log the error, there is nothing we can do */
5932+ ERR (NULL , "Failed to acquire server configuration write lock (%s)." , strerror (r ));
5933+ }
5934+
5935+ /* clear the flag */
5936+ server_opts .applying_config = 0 ;
5937+
5938+ if (!r ) {
5939+ /* UNLOCK only if we locked it */
5940+ pthread_rwlock_unlock (& server_opts .config_lock );
5941+ }
5942+ }
5943+
58735944API int
58745945nc_server_config_setup_diff (const struct lyd_node * data )
58755946{
@@ -5878,6 +5949,9 @@ nc_server_config_setup_diff(const struct lyd_node *data)
58785949
58795950 NC_CHECK_ARG_RET (NULL , data , 1 );
58805951
5952+ /* wait until previous is done, then mark us as applying */
5953+ NC_CHECK_RET (nc_server_config_update_start ());
5954+
58815955 /* CONFIG RD LOCK */
58825956 pthread_rwlock_rdlock (& server_opts .config_lock );
58835957
@@ -5919,11 +5993,10 @@ nc_server_config_setup_diff(const struct lyd_node *data)
59195993 ERR (NULL , "Dispatching new call-home threads failed." ), cleanup_unlock );
59205994#endif /* NC_ENABLED_SSH_TLS */
59215995
5922- /* free the old config */
5996+ /* swap: free old, keep new, zero out the copy just in case to avoid double free */
59235997 nc_server_config_free (& server_opts .config );
5924-
5925- /* replace it with the new one */
59265998 server_opts .config = config_copy ;
5999+ memset (& config_copy , 0 , sizeof config_copy );
59276000
59286001#ifdef NC_ENABLED_SSH_TLS
59296002 /* wake up the cert expiration notif thread */
@@ -5939,6 +6012,7 @@ nc_server_config_setup_diff(const struct lyd_node *data)
59396012 /* free the new config in case of error */
59406013 nc_server_config_free (& config_copy );
59416014 }
6015+ nc_server_config_update_end ();
59426016
59436017 return ret ;
59446018}
@@ -5952,6 +6026,9 @@ nc_server_config_setup_data(const struct lyd_node *data)
59526026
59536027 NC_CHECK_ARG_RET (NULL , data , 1 );
59546028
6029+ /* wait until previous is done, then mark us as applying */
6030+ NC_CHECK_RET (nc_server_config_update_start ());
6031+
59556032 /* check that the config data are not diff (no op attr) */
59566033 LY_LIST_FOR (data , tree ) {
59576034 LYD_TREE_DFS_BEGIN (tree , iter ) {
@@ -6000,11 +6077,10 @@ nc_server_config_setup_data(const struct lyd_node *data)
60006077 ERR (NULL , "Dispatching new call-home connections failed." ), cleanup_unlock );
60016078#endif /* NC_ENABLED_SSH_TLS */
60026079
6003- /* free the old config */
6080+ /* swap: free old, keep new, zero out the copy just in case to avoid double free */
60046081 nc_server_config_free (& server_opts .config );
6005-
6006- /* replace it with the new one */
60076082 server_opts .config = config ;
6083+ memset (& config , 0 , sizeof config );
60086084
60096085#ifdef NC_ENABLED_SSH_TLS
60106086 /* wake up the cert expiration notif thread */
@@ -6020,6 +6096,7 @@ nc_server_config_setup_data(const struct lyd_node *data)
60206096 /* free the new config in case of error */
60216097 nc_server_config_free (& config );
60226098 }
6099+ nc_server_config_update_end ();
60236100
60246101 return ret ;
60256102}
0 commit comments