Skip to content

Commit 4f31362

Browse files
committed
cli: add 'validate', or '-n', dry run to copy command
This commit adds config file validation to the copy command, discussed in #373. Allowing users to test their config files before restoring a backup. The feature could also be used for the automatic rollback when downgrading to an earlier version of the OS. Fixes #373 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent 966b9b3 commit 4f31362

File tree

3 files changed

+49
-33
lines changed

3 files changed

+49
-33
lines changed

src/bin/copy.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ struct infix_ds infix_config[] = {
3434

3535
static const char *prognm = "copy";
3636
static int timeout;
37+
static int dry_run;
38+
static int quiet;
3739

3840

3941
/*
@@ -180,11 +182,12 @@ static const char *infix_ds(const char *text, struct infix_ds **ds)
180182
/*
181183
* Load configuration from a file and replace running datastore.
182184
* This triggers all sysrepo change callbacks, unlike sr_copy_config().
185+
* In dry-run mode, only validates the configuration without applying.
183186
*/
184187
static int replace_running(sr_conn_ctx_t *conn, sr_session_ctx_t *sess,
185188
const char *file, int timeout)
186189
{
187-
uint32_t flags = LYD_PARSE_NO_STATE | LYD_PARSE_ONLY;
190+
uint32_t flags = LYD_PARSE_NO_STATE | LYD_PARSE_ONLY | LYD_PARSE_STORE_ONLY | LYD_PARSE_STRICT;
188191
struct lyd_node *data = NULL;
189192
const struct ly_ctx *ctx;
190193
LY_ERR err;
@@ -200,18 +203,26 @@ static int replace_running(sr_conn_ctx_t *conn, sr_session_ctx_t *sess,
200203
return SR_ERR_INTERNAL;
201204
}
202205

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));
206+
if (dry_run) {
207+
if (!quiet) {
208+
printf("Configuration validated, %s: OK\n", file);
209+
fflush(stdout);
210+
}
208211
lyd_free_all(data);
212+
rc = SR_ERR_OK;
213+
} else {
214+
/* Replace running config, assumes ownership of 'data' */
215+
rc = sr_replace_config(sess, NULL, data, timeout * 1000);
216+
if (rc) {
217+
emsg(sess, ERRMSG "unable to replace configuration, err %d: %s\n",
218+
rc, sr_strerror(rc));
219+
lyd_free_all(data);
220+
}
209221
}
210222

211223
return rc;
212224
}
213225

214-
215226
static int copy(const char *src, const char *dst, const char *remote_user)
216227
{
217228
struct infix_ds *srcds = NULL, *dstds = NULL;
@@ -451,6 +462,8 @@ static int usage(int rc)
451462
"\n"
452463
"Options:\n"
453464
" -h This help text\n"
465+
" -n Dry-run, validate configuration without applying\n"
466+
" -q Quiet mode, suppress informational messages\n"
454467
" -u USER Username for remote commands, like scp\n"
455468
" -t SEEC Timeout for the operation, or default %d sec\n"
456469
" -v Show version\n", prognm, timeout);
@@ -465,10 +478,16 @@ int main(int argc, char *argv[])
465478

466479
timeout = fgetint("/etc/default/confd", "=", "CONFD_TIMEOUT");
467480

468-
while ((c = getopt(argc, argv, "ht:u:v")) != EOF) {
481+
while ((c = getopt(argc, argv, "hnqt:u:v")) != EOF) {
469482
switch(c) {
470483
case 'h':
471484
return usage(0);
485+
case 'n':
486+
dry_run = 1;
487+
break;
488+
case 'q':
489+
quiet = 1;
490+
break;
472491
case 't':
473492
timeout = atoi(optarg);
474493
break;

src/klish-plugin-infix/src/infix.c

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,23 @@
2828
const uint8_t kplugin_infix_major = 1;
2929
const uint8_t kplugin_infix_minor = 0;
3030

31-
static void cd_home(kcontext_t *ctx)
31+
static const char *cd_home(kcontext_t *ctx)
3232
{
3333
const char *user = "root";
34-
ksession_t *session;
35-
struct passwd *pw;
36-
37-
session = kcontext_session(ctx);
38-
if (session) {
39-
user = ksession_user(session);
40-
if (!user)
41-
user = "root";
42-
}
34+
ksession_t *session;
35+
struct passwd *pw;
36+
37+
session = kcontext_session(ctx);
38+
if (session) {
39+
user = ksession_user(session);
40+
if (!user)
41+
user = "root";
42+
}
4343

44-
pw = getpwnam(user);
45-
chdir(pw->pw_dir);
44+
pw = getpwnam(user);
45+
chdir(pw->pw_dir);
46+
47+
return user;
4648
}
4749

4850
static int systemf(const char *fmt, ...)
@@ -201,8 +203,8 @@ int infix_copy(kcontext_t *ctx)
201203
{
202204
kpargv_t *pargv = kcontext_pargv(ctx);
203205
const char *src, *dst;
204-
const char *username;
205206
char user[256] = "";
207+
char validate[8] = "";
206208
kparg_t *parg;
207209

208210
src = kparg_value(kpargv_find(pargv, "src"));
@@ -214,26 +216,20 @@ int infix_copy(kcontext_t *ctx)
214216
if (parg)
215217
snprintf(user, sizeof(user), "-u %s", kparg_value(parg));
216218

217-
cd_home(ctx);
218-
username = ksession_user(kcontext_session(ctx));
219+
parg = kpargv_find(pargv, "validate");
220+
if (parg)
221+
strlcpy(validate, "-n", sizeof(validate));
219222

220-
return systemf("sudo -u %s copy %s %s %s", username, user, src, dst);
223+
/* Ensure we run the copy command as the logged-in user, not root (klishd) */
224+
return systemf("doas -u %s copy %s %s %s %s", cd_home(ctx), validate, user, src, dst);
221225
}
222226

223227
int infix_shell(kcontext_t *ctx)
224228
{
225-
const char *user = "root";
226-
ksession_t *session;
229+
const char *user = cd_home(ctx);
227230
pid_t pid;
228231
int rc;
229232

230-
session = kcontext_session(ctx);
231-
if (session) {
232-
user = ksession_user(session);
233-
if (!user)
234-
user = "root";
235-
}
236-
237233
pid = fork();
238234
if (pid == -1)
239235
return -1;

src/klish-plugin-infix/xml/infix.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
<PARAM name="src" ptype="/DATASTORE" help="Source datastore or file, e.g., tftp://ip/file"/>
189189
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore or file, e.g., scp://ip/file"/>
190190
<SWITCH name="optional" min="0">
191+
<COMMAND name="validate" help="Validate configuration without applying"/>
191192
<PARAM name="user" ptype="/STRING" help="Remote username (interactive password)"/>
192193
</SWITCH>
193194
<ACTION sym="copy@infix" in="tty" out="tty" interrupt="true"/>

0 commit comments

Comments
 (0)