Skip to content

Commit 40a6c09

Browse files
Added getdir command to cf-net, recursively copies directory
Ticket: CFE-2986 Changelog: Title Signed-off-by: Simon Halvorsen <simon.halvorsen@northern.tech>
1 parent 0130e1d commit 40a6c09

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

cf-net/cf-net.c

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <cleanup.h>
4545
#include <protocol.h>
4646
#include <sequence.h>
47+
#include <files_lib.h>
4748

4849
#define ARG_UNUSED __attribute__((unused))
4950

@@ -100,6 +101,8 @@ static const Description COMMANDS[] =
100101
"\t\t\t(%d can be used in both the remote and output file paths when '-j' is used)"},
101102
{"opendir", "List files and folders in a directory",
102103
"cf-net opendir masterfiles"},
104+
{"getdir", "Recursively downloads files and folders in a directory",
105+
"cf-net getdir masterfiles"},
103106
{NULL, NULL, NULL}
104107
};
105108

@@ -144,6 +147,7 @@ static const char *const HINTS[] =
144147
generator_macro(STAT) \
145148
generator_macro(GET) \
146149
generator_macro(OPENDIR) \
150+
generator_macro(GETDIR) \
147151
generator_macro(MULTI) \
148152
generator_macro(MULTITLS) \
149153
generator_macro(HELP) \
@@ -197,6 +201,7 @@ static int CFNetGet(CFNetOptions *opts, const char *hostname, char **args);
197201
static int CFNetOpenDir(CFNetOptions *opts, const char *hostname, char **args);
198202
static int CFNetMulti(const char *server);
199203
static int CFNetMultiTLS(const char *server, const char *use_protocol_version);
204+
static int CFNetGetDir(CFNetOptions *opts, const char *hostname, char **args);
200205

201206

202207
//*******************************************************************
@@ -411,6 +416,8 @@ static int CFNetCommandSwitch(CFNetOptions *opts, const char *hostname,
411416
return CFNetGet(opts, hostname, args);
412417
case CFNET_CMD_OPENDIR:
413418
return CFNetOpenDir(opts, hostname, args);
419+
case CFNET_CMD_GETDIR:
420+
return CFNetGetDir(opts, hostname, args);
414421
case CFNET_CMD_MULTI:
415422
return CFNetMulti(hostname);
416423
case CFNET_CMD_MULTITLS:
@@ -976,6 +983,231 @@ static int CFNetOpenDir(ARG_UNUSED CFNetOptions *opts, const char *hostname, cha
976983
return 0;
977984
}
978985

986+
static int CFNetGetDir( CFNetOptions *opts, const char *hostname, char **args)
987+
{
988+
assert(opts);
989+
assert(hostname);
990+
assert(args);
991+
char *local_dir = NULL;
992+
993+
int argc = 0;
994+
while (args[argc] != NULL)
995+
{
996+
++argc;
997+
}
998+
999+
static struct option longopts[] = {
1000+
{ "output", required_argument, NULL, 'o' },
1001+
{ NULL, 0, NULL, 0 }
1002+
};
1003+
assert(opts != NULL);
1004+
if (argc <= 1)
1005+
{
1006+
return invalid_command("getdir");
1007+
}
1008+
extern int optind;
1009+
optind = 0;
1010+
extern char *optarg;
1011+
int c = 0;
1012+
const char *optstr = "o:";
1013+
bool specified_path = false;
1014+
while ((c = getopt_long(argc, args, optstr, longopts, NULL))
1015+
!= -1)
1016+
{
1017+
switch (c)
1018+
{
1019+
case 'o':
1020+
{
1021+
if (local_dir != NULL)
1022+
{
1023+
Log(LOG_LEVEL_INFO,
1024+
"Warning: multiple occurences of -o in command, "\
1025+
"only last one will be used.");
1026+
free(local_dir);
1027+
}
1028+
local_dir = xstrdup(optarg);
1029+
specified_path = true;
1030+
break;
1031+
}
1032+
case ':':
1033+
{
1034+
return invalid_command("getdir");
1035+
break;
1036+
}
1037+
case '?':
1038+
{
1039+
return invalid_command("getdir");
1040+
break;
1041+
}
1042+
default:
1043+
{
1044+
printf("Default optarg = '%s', c = '%c' = %i\n",
1045+
optarg, c, (int)c);
1046+
break;
1047+
}
1048+
}
1049+
}
1050+
args = &(args[optind]);
1051+
argc -= optind;
1052+
char *remote_dir = args[0];
1053+
if (specified_path)
1054+
{
1055+
size_t len = strlen(local_dir) + strlen(basename(remote_dir)) + 2; // / and '\0'
1056+
char *temp = malloc(len);
1057+
if (temp == NULL)
1058+
{
1059+
free(local_dir);
1060+
return -1;
1061+
}
1062+
snprintf(temp, len, "%s/%s", local_dir, basename(remote_dir));
1063+
free(local_dir);
1064+
local_dir = temp;
1065+
}
1066+
1067+
if (local_dir == NULL)
1068+
{
1069+
char *base = basename(remote_dir);
1070+
local_dir = xstrdup(base);
1071+
}
1072+
1073+
AgentConnection *conn = CFNetOpenConnection(hostname, opts->use_protocol_version);
1074+
if (conn == NULL)
1075+
{
1076+
free(local_dir);
1077+
return -1;
1078+
}
1079+
struct stat sb;
1080+
bool ret = ProtocolStat(conn, remote_dir, &sb);
1081+
if (!ret)
1082+
{
1083+
printf("Could not stat: '%s'\n", remote_dir);
1084+
free(local_dir);
1085+
CFNetDisconnect(conn);
1086+
return -1;
1087+
}
1088+
if (!S_ISDIR(sb.st_mode))
1089+
{
1090+
printf("'%s' is not a directory, use 'get' for single file download\n", remote_dir);
1091+
free(local_dir);
1092+
CFNetDisconnect(conn);
1093+
return -1;
1094+
}
1095+
1096+
Seq *seq = ProtocolOpenDir(conn, remote_dir);
1097+
if (seq == NULL)
1098+
{
1099+
free(local_dir);
1100+
CFNetDisconnect(conn);
1101+
return -1;
1102+
}
1103+
1104+
for (size_t i = 0; i < SeqLength(seq); i++)
1105+
{
1106+
char dir_entry[PATH_MAX];
1107+
char *curr = SeqAt(seq, i);
1108+
1109+
if (strcmp(".", curr) == 0 || strcmp("..", curr) == 0)
1110+
{
1111+
continue;
1112+
}
1113+
1114+
snprintf(dir_entry, sizeof(dir_entry), "%s/%s", remote_dir, curr);
1115+
1116+
if (!ProtocolStat(conn, dir_entry, &sb))
1117+
{
1118+
continue;
1119+
}
1120+
1121+
if (S_ISDIR(sb.st_mode))
1122+
{
1123+
bool force = false;
1124+
char path[PATH_MAX];
1125+
int written;
1126+
1127+
if (specified_path)
1128+
{
1129+
written = snprintf(path, sizeof(path), "%s/%s/", local_dir, curr);
1130+
}
1131+
else
1132+
{
1133+
char cwd[PATH_MAX];
1134+
if (getcwd(cwd, sizeof(cwd)) != NULL)
1135+
{
1136+
written = snprintf(path, sizeof(path), "%s/%s/%s/", cwd, local_dir, curr);
1137+
}
1138+
else
1139+
{
1140+
Log(LOG_LEVEL_ERR, "Failed to get current working directory");
1141+
continue;
1142+
}
1143+
}
1144+
if (written < 0 || written >= sizeof(path))
1145+
{
1146+
Log(LOG_LEVEL_ERR, "Path too long for directory: %s", curr);
1147+
continue;
1148+
}
1149+
MakeParentDirectory(path, force, NULL);
1150+
1151+
Seq *items = ProtocolOpenDir(conn, dir_entry);
1152+
if (items == NULL)
1153+
{
1154+
Log(LOG_LEVEL_ERR, "Failed to open subdir: %s", dir_entry);
1155+
continue;
1156+
}
1157+
1158+
for (size_t j = 0; j < SeqLength(items); j++)
1159+
{
1160+
char *item = SeqAt(items, j);
1161+
if (strcmp(".", item) == 0 || strcmp("..", item) == 0)
1162+
{
1163+
continue;
1164+
}
1165+
1166+
char buf[PATH_MAX];
1167+
snprintf(buf, sizeof(buf), "%s/%s", curr, item);
1168+
SeqAppend(seq, xstrdup(buf));
1169+
}
1170+
SeqDestroy(items);
1171+
continue;
1172+
}
1173+
1174+
if (sb.st_uid == 0) // File is owned by root, bad access -> skip
1175+
{
1176+
printf("File '%s' is owned by root... Skipping\n", dir_entry);
1177+
continue;
1178+
}
1179+
1180+
char curr_dir[PATH_MAX];
1181+
snprintf(curr_dir, sizeof(curr_dir), "%s/%s", local_dir, curr);
1182+
1183+
GetFileData *filedata = calloc(1, sizeof(GetFileData));
1184+
if (filedata == NULL)
1185+
{
1186+
continue;
1187+
}
1188+
1189+
snprintf(filedata->local_file, PATH_MAX, "%s", curr_dir);
1190+
snprintf(filedata->remote_file, PATH_MAX, "%s", dir_entry);
1191+
filedata->hostname = hostname;
1192+
filedata->use_protocol_version = opts->use_protocol_version;
1193+
filedata->print_stats = opts->print_stats;
1194+
1195+
filedata->ret = ProtocolStatGet(conn, filedata->remote_file,
1196+
filedata->local_file, 0644, filedata->print_stats);
1197+
if (!filedata->ret)
1198+
{
1199+
Log(LOG_LEVEL_ERR,"Failed to get remote file: %s:%s", conn->this_server, filedata->remote_file);
1200+
}
1201+
1202+
free(filedata);
1203+
}
1204+
1205+
SeqDestroy(seq);
1206+
free(local_dir);
1207+
CFNetDisconnect(conn);
1208+
return 0;
1209+
}
1210+
9791211
static int CFNetMulti(const char *server)
9801212
{
9811213
time_t start;

0 commit comments

Comments
 (0)