@@ -177,6 +177,40 @@ static const char *infix_ds(const char *text, struct infix_ds **ds)
177177 return text ;
178178}
179179
180+ /*
181+ * Load configuration from a file and replace running datastore.
182+ * This triggers all sysrepo change callbacks, unlike sr_copy_config().
183+ */
184+ static int replace_running (sr_conn_ctx_t * conn , sr_session_ctx_t * sess ,
185+ const char * file , int timeout )
186+ {
187+ uint32_t flags = LYD_PARSE_NO_STATE | LYD_PARSE_ONLY ;
188+ struct lyd_node * data = NULL ;
189+ const struct ly_ctx * ctx ;
190+ LY_ERR err ;
191+ int rc ;
192+
193+ ctx = sr_acquire_context (conn );
194+ err = lyd_parse_data_path (ctx , file , LYD_JSON , flags , 0 , & data );
195+ sr_release_context (conn );
196+
197+ if (err ) {
198+ fprintf (stderr , ERRMSG "unable to load %s\n" , file );
199+ lyd_free_all (data );
200+ return SR_ERR_INTERNAL ;
201+ }
202+
203+ /* Replace running config (triggers callbacks, takes ownership on success) */
204+ rc = sr_replace_config (sess , NULL , data , timeout * 1000 );
205+ if (rc ) {
206+ emsg (sess , ERRMSG "unable to replace configuration, err %d: %s\n" ,
207+ rc , sr_strerror (rc ));
208+ lyd_free_all (data );
209+ }
210+
211+ return rc ;
212+ }
213+
180214
181215static int copy (const char * src , const char * dst , const char * remote_user )
182216{
@@ -229,12 +263,24 @@ static int copy(const char *src, const char *dst, const char *remote_user)
229263 fprintf (stderr , ERRMSG "unable to open transaction to %s\n" , dst );
230264 } else {
231265 sr_nacm_set_user (sess , user );
232- rc = sr_copy_config (sess , NULL , srcds -> datastore , timeout * 1000 );
233- if (rc )
234- emsg (sess , ERRMSG "unable to copy configuration, err %d: %s\n" ,
235- rc , sr_strerror (rc ));
236- else
237- set_owner (dstds -> path , user );
266+
267+ /*
268+ * When copying TO running-config, use sr_replace_config()
269+ * to trigger change callbacks. Otherwise use sr_copy_config()
270+ * for direct datastore copy without callbacks.
271+ */
272+ if (dstds -> datastore == SR_DS_RUNNING && srcds -> path ) {
273+ rc = replace_running (conn , sess , srcds -> path , timeout );
274+ if (!rc )
275+ set_owner (dstds -> path , user );
276+ } else {
277+ /* Direct copy for other datastores (no callbacks needed) */
278+ rc = sr_copy_config (sess , NULL , srcds -> datastore , timeout * 1000 );
279+ if (rc )
280+ emsg (sess , ERRMSG "unable to copy configuration, err %d: %s\n" , rc , sr_strerror (rc ));
281+ else
282+ set_owner (dstds -> path , user );
283+ }
238284 }
239285 rc = sr_disconnect (conn );
240286
@@ -320,9 +366,29 @@ static int copy(const char *src, const char *dst, const char *remote_user)
320366 if (rc ) {
321367 fprintf (stderr , ERRMSG "failed downloading %s" , src );
322368 } else {
323- rc = systemf ("sysrepocfg -d %s -I%s -f json" , dstds -> sysrepocfg , fn );
324- if (rc )
325- fprintf (stderr , ERRMSG "failed loading %s from %s" , dst , src );
369+ /* Use replace_config() for running-config (triggers callbacks), sysrepocfg for others */
370+ if (dstds -> datastore == SR_DS_RUNNING ) {
371+ if (sr_connect (SR_CONN_DEFAULT , & conn )) {
372+ fprintf (stderr , ERRMSG "connection to datastore failed\n" );
373+ rc = 1 ;
374+ } else {
375+ sr_log_syslog ("klishd" , SR_LL_WRN );
376+ if (sr_session_start (conn , SR_DS_RUNNING , & sess )) {
377+ fprintf (stderr , ERRMSG "unable to open transaction to %s\n" , dst );
378+ rc = 1 ;
379+ } else {
380+ sr_nacm_set_user (sess , user );
381+ rc = replace_running (conn , sess , fn , timeout );
382+ if (!rc )
383+ set_owner (dstds -> path , user );
384+ }
385+ sr_disconnect (conn );
386+ }
387+ } else {
388+ rc = systemf ("sysrepocfg -d %s -I%s -f json" , dstds -> sysrepocfg , fn );
389+ if (rc )
390+ fprintf (stderr , ERRMSG "failed loading %s from %s" , dst , src );
391+ }
326392 }
327393 } else {
328394 if (strstr (src , "://" ) && strstr (dst , "://" )) {
0 commit comments