mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-01 13:23:01 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user