src/klish-plugin-infix: support for copying remote files with auth

This patch adds the optional 'user name' argument to the 'copy' command,
enabling using curl scp/sftp protocols for fetching and uploading files.

Changes to cfg_adjust() allow saving and referencing files in the user's
home directory.  Useful for both fetching upgrade bundles and container
images.

The 'dir' command now lists files in both $HOME and /cfg.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-02-25 19:49:27 +01:00
parent 3159d6c042
commit bcdd311cb8
3 changed files with 98 additions and 24 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
AM_CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE
CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
pluginsdir = $(KLISHLIBDIR)/plugins
plugins_LTLIBRARIES = kplugin-infix.la
+88 -18
View File
@@ -46,6 +46,23 @@ struct infix_ds infix_config[] = {
{ "factory-config", NULL, SR_DS_FACTORY_DEFAULT, 0, "/etc/factory-config.cfg" },
};
static void cd_home(kcontext_t *ctx)
{
const char *user = "root";
ksession_t *session;
struct passwd *pw;
session = kcontext_session(ctx);
if (session) {
user = ksession_user(session);
if (!user)
user = "root";
}
pw = getpwnam(user);
chdir(pw->pw_dir);
}
static int has_ext(const char *fn, const char *ext)
{
size_t pos = strlen(fn);
@@ -56,17 +73,47 @@ static int has_ext(const char *fn, const char *ext)
return 0;
}
static char *cfg_adjust(const char *fn, char *buf, size_t len)
static const char *basenm(const char *fn)
{
if (strncmp(fn, "/cfg/", 5)) {
if (fn[0] == '.' || fn[0] == '/')
return NULL;
const char *ptr;
snprintf(buf, len, "/cfg/%s", fn);
if (!fn)
return "";
ptr = strrchr(fn, '/');
if (!ptr)
ptr = fn;
return ptr;
}
static char *cfg_adjust(const char *fn, const char *tmpl, char *buf, size_t len)
{
if (strstr(fn, "../"))
return NULL; /* relative paths not allowed */
/* Files in /cfg must end in .cfg */
if (!strncmp(fn, "/cfg/", 5)) {
snprintf(buf, len, "%s", fn);
if (!has_ext(fn, ".cfg"))
strcat(buf, ".cfg");
return buf;
}
if (!has_ext(buf, ".cfg"))
strcat(buf, ".cfg");
/* Files ending with .cfg belong in /cfg */
if (has_ext(fn, ".cfg")) {
snprintf(buf, len, "/cfg/%s", fn);
return buf;
}
if (strlen(fn) > 0 && fn[0] == '.' && tmpl) {
if (fn[1] == '/' && fn[1] != 0)
strncpy(buf, fn, len);
else
snprintf(buf, len, "%s", basenm(tmpl));
} else
strncpy(buf, fn, len);
return buf;
}
@@ -196,7 +243,8 @@ static int files(const char *path, const char *stripext)
while ((d = readdir(dir))) {
char name[sizeof(d->d_name) + 1];
if (d->d_type != DT_REG)
/* only list regular files, skip dirs and dotfiles */
if (d->d_type != DT_REG || d->d_name[0] == '.')
continue;
strncpy(name, d->d_name, sizeof(name));
@@ -235,7 +283,15 @@ done:
int infix_dir(kcontext_t *ctx)
{
return systemf("ls --color=always --group-directories-first -A /cfg");
cd_home(ctx);
fputs("\e[7mHome directory \e[0m\n", stderr);
system("ls --color=always -p >&2");
fputs("\n\e[7m/cfg directory \e[0m\n", stderr);
system("ls --color=always -p /cfg >&2");
return 0;
}
int infix_erase(kcontext_t *ctx)
@@ -250,6 +306,7 @@ int infix_erase(kcontext_t *ctx)
return -1;
}
cd_home(ctx);
if (access(path, F_OK)) {
size_t len = strlen(path) + 10;
@@ -259,7 +316,11 @@ int infix_erase(kcontext_t *ctx)
return -1;
}
cfg_adjust(path, fn, len);
cfg_adjust(path, NULL, fn, len);
if (access(fn, F_OK)) {
fprintf(stderr, "No such file: %s\n", fn);
return -1;
}
} else
fn = (char *)path;
@@ -278,6 +339,7 @@ int infix_files(kcontext_t *ctx)
{
const char *path;
cd_home(ctx);
path = kcontext_script(ctx);
if (!path) {
fprintf(stderr, ERRMSG "missing path argument to file search.\n");
@@ -323,7 +385,9 @@ int infix_copy(kcontext_t *ctx)
const char *fn = NULL;
const char *src, *dst;
sr_conn_ctx_t *conn;
char user[256] = "";
char adjust[256];
kparg_t *parg;
int rc = 0;
src = kparg_value(kpargv_find(pargv, "src"));
@@ -331,6 +395,10 @@ int infix_copy(kcontext_t *ctx)
if (!src || !dst)
goto err;
parg = kpargv_find(pargv, "user");
if (parg)
snprintf(user, sizeof(user), "-u %s", kparg_value(parg));
src = infix_ds(src, "source", &srcds);
if (!src)
goto err;
@@ -343,6 +411,8 @@ int infix_copy(kcontext_t *ctx)
goto err;
}
cd_home(ctx);
/* 1. Regular ds copy */
if (srcds && dstds) {
/* Ensure the dst ds is writable */
@@ -387,7 +457,7 @@ int infix_copy(kcontext_t *ctx)
if (rc)
fprintf(stderr, ERRMSG "failed exporting %s to %s\n", src, fn);
else {
rc = systemf("curl -T %s %s", fn, dst);
rc = systemf("curl %s -LT %s %s", user, fn, dst);
if (rc)
fprintf(stderr, ERRMSG "failed uploading %s to %s\n", src, dst);
}
@@ -397,7 +467,7 @@ int infix_copy(kcontext_t *ctx)
if (dstds && dstds->path)
fn = dstds->path;
else
fn = cfg_adjust(dst, adjust, sizeof(adjust));
fn = cfg_adjust(dst, src, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid destination path.\n");
@@ -432,7 +502,7 @@ int infix_copy(kcontext_t *ctx)
tmpfn = mktemp(temp_file);
fn = tmpfn;
} else {
fn = cfg_adjust(src, adjust, sizeof(adjust));
fn = cfg_adjust(src, NULL, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid source file location.\n");
rc = 1;
@@ -441,7 +511,7 @@ int infix_copy(kcontext_t *ctx)
}
if (tmpfn)
rc = systemf("curl -o %s %s", fn, src);
rc = systemf("curl %s -Lo %s %s", user, fn, src);
if (rc) {
fprintf(stderr, ERRMSG "failed downloading %s", src);
} else {
@@ -456,7 +526,7 @@ int infix_copy(kcontext_t *ctx)
}
if (strstr(src, "://")) {
fn = cfg_adjust(dst, adjust, sizeof(adjust));
fn = cfg_adjust(dst, src, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid destination file location.\n");
rc = 1;
@@ -470,9 +540,9 @@ int infix_copy(kcontext_t *ctx)
}
}
rc = systemf("curl -o %s %s", fn, src);
rc = systemf("curl %s -Lo %s %s", user, fn, src);
} else if (strstr(dst, "://")) {
fn = cfg_adjust(src, adjust, sizeof(adjust));
fn = cfg_adjust(src, NULL, adjust, sizeof(adjust));
if (!fn) {
fprintf(stderr, ERRMSG "invalid source file location.\n");
rc = 1;
@@ -482,7 +552,7 @@ int infix_copy(kcontext_t *ctx)
if (access(fn, F_OK))
fprintf(stderr, ERRMSG "no such file %s, aborting.", fn);
else
rc = systemf("curl -T %s %s", fn, dst);
rc = systemf("curl %s -LT %s %s", user, fn, dst);
}
}
+8 -5
View File
@@ -158,9 +158,12 @@
<ACTION sym="nav">replace config</ACTION>
</COMMAND>
<COMMAND name="copy" help="Copy configuration, e.g., copy running-config startup-config">
<PARAM name="src" ptype="/DATASTORE" help="Source datastore"/>
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore"/>
<COMMAND name="copy" help="Copy file or configuration, e.g., copy running-config startup-config">
<PARAM name="src" ptype="/DATASTORE" help="Source datastore or file, e.g., tftp://ip/file"/>
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore or file, e.g., scp://ip/file"/>
<SWITCH name="optional" min="0">
<PARAM name="user" ptype="/STRING" help="Remote username (interactive password)"/>
</SWITCH>
<ACTION sym="copy@infix" in="tty" out="tty" interrupt="true"/>
</COMMAND>
@@ -475,8 +478,8 @@
</ACTION>
</COMMAND>
<COMMAND name="upgrade" help="Install a software update bundle">
<PARAM name="URI" ptype="/STRING" help="(ftp|tftp|http|https|sftp)://(dns.name | ip.address)/path/to/upgrade-bundle.pkg"/>
<COMMAND name="upgrade" help="Install a software update bundle from remote or local file">
<PARAM name="URI" ptype="/STRING" help="[(ftp|tftp|http|https|sftp)://(dns.name | ip.address)/path/to/]upgrade-bundle.pkg"/>
<ACTION sym="script" in="tty" out="tty" interrupt="true">
rauc install $KLISH_PARAM_URI
</ACTION>