Builds being sent and received over SFTP! Now to unpack them!

This commit is contained in:
Scott Duensing 2023-05-03 20:44:44 -05:00
parent f34af2f6ff
commit 8de2ffc18f
6 changed files with 253 additions and 82 deletions

View file

@ -44,9 +44,7 @@ typedef struct SSHS {
int sock;
int result;
gboolean finished;
char *title;
SSHCallback callback;
GtkWidget *status;
} SSHT;
@ -61,14 +59,12 @@ typedef struct SFTPS {
uint64_t fileSize;
uint64_t fileSent;
size_t nread;
char *source;
char *target;
char *title;
char *localName;
char *remoteName;
gboolean uploading;
gboolean readMore;
gboolean finished;
gboolean success;
GtkWidget *status;
SFTPCallback callback;
} SFTPT;
@ -77,7 +73,8 @@ SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password);
void sshDisconnect(SSHT **sshData);
//int sshExecute(SSHT *sshData, char *command, char **output);
gboolean sshSFTPFileExists(SSHT *sshData, char *target);
SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPCallback callback);
SFTPT *sshSFTPReceive(SSHT *sshData, char *remote, char *local, SFTPCallback callback);
SFTPT *sshSFTPSend(SSHT *sshData, char *local, char *remote, SFTPCallback callback);
void sshShutdown(void);
void sshStartup(void);

View file

@ -53,6 +53,7 @@ gboolean utilFileSaveAs(WindowDataT *self, char *extension, char *what);
gboolean utilFileSaveOtherAs(WindowDataT *self, char *extension, char *what, char **filename);
size_t utilFileSize(char *filename);
GtkWidget *utilFindChildWidget(GtkWidget *parent, const gchar *name);
void utilForceUpdate(void);
char *utilGetToken(char *input, char *delimit, char *openblock, char *closeblock);
WindowDataT *utilGetWindowData(GtkWidget *window);
gboolean utilGetWidgetsFromMemory(char *resource, char *name[], GtkWidget **widgets[], gpointer userData);

View file

@ -26,8 +26,10 @@
static GtkWidget *_lstMessages = NULL;
static int _autoScroll = 0;
static gboolean messagesScroll(gpointer userData);
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData);
static void winMessagesDelete(gpointer userData);
@ -43,19 +45,19 @@ void message(MessageTypesT level, char *format, ...) {
NULL,
&_lstMessages
};
GtkWidget *row;
GtkWidget *box;
GtkWidget *label;
va_list args;
char *string;
char *msg;
char *tok;
char *labels[MSG_COUNT] = {
GtkWidget *row;
GtkWidget *box;
GtkWidget *label;
va_list args;
char *string;
char *msg;
char *tok;
char *labels[MSG_COUNT] = {
"<span foreground=\"gray\"> Info:</span>",
"<span foreground=\"yellow\">Warning:</span>",
"<span foreground=\"red\"> Error:</span>",
"<span foreground=\"red\"><b> Severe:</b></span>"
};
};
// Do we need to open this window?
if (!_lstMessages) {
@ -71,6 +73,9 @@ void message(MessageTypesT level, char *format, ...) {
// Show window.
gtk_widget_show_all(self->window);
// Set up automatic scrolling.
g_idle_add(messagesScroll, messagesScroll);
}
// Display message.
@ -96,13 +101,35 @@ void message(MessageTypesT level, char *format, ...) {
gtk_container_add(GTK_CONTAINER(row), box);
gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1);
// Force paint.
gtk_widget_show_all(row);
utilForceUpdate();
_autoScroll = 5; // Try 5 times to show the new row.
tok = strtok(NULL, "\n");
}
}
static gboolean messagesScroll(gpointer userData) {
GtkAdjustment *adjustment;
(void)userData;
if (_autoScroll <= 0) return G_SOURCE_CONTINUE;
// Scroll to show new line.
adjustment = gtk_list_box_get_adjustment(GTK_LIST_BOX(_lstMessages));
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment));
gtk_widget_show_all(_lstMessages);
utilForceUpdate();
_autoScroll--;
return G_SOURCE_CONTINUE;
}
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData) {
// userData is not reliable due to util indirectly calling us.
WindowDataT *self = (WindowDataT *)utilGetWindowData(object);
@ -115,6 +142,7 @@ EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData) {
static void winMessagesDelete(gpointer userData) {
g_idle_remove_by_data(messagesScroll);
utilWindowUnRegister(userData);
_lstMessages = NULL;
DEL(userData);

View file

@ -132,12 +132,14 @@ EVENT void menuProjectBuildTargets(GtkWidget *object, gpointer userData
EVENT void menuProjectBuildCookRecipes(GtkWidget *object, gpointer userData);
EVENT void menuProjectBuildBuild(GtkWidget *object, gpointer userData);
EVENT void menuProjectHelpProject(GtkWidget *object, gpointer userData);
static void receiveSFTP(SFTPT *sftp);
static void saveConfig(ProjectDataT *self);
static void sendSFTP(SFTPT *sftp);
static TargetT **targetArrayCopy(TargetT **targets);
static void targetArrayDelete(TargetT ***array);
EVENT void treeProjectRowActivated(GtkTreeView *treeView, GtkTreePath *path, GtkTreeViewColumn *column, gpointer userData);
static gboolean updateBuildOptions(ProjectDataT *self);
static gboolean waitForBuild(gpointer userData);
EVENT gboolean winProjectClose(GtkWidget *object, gpointer userData);
static void winProjectDelete(gpointer userData);
@ -955,20 +957,28 @@ EVENT void menuProjectBuildBuild(GtkWidget *object, gpointer userData) {
gboolean archPrinted;
FILE *out;
menuProjectBuildCookRecipes(object, userData);
ssh = NEW(SSHT);
ssh = sshConnect(self->buildHost, self->buildSSHPort, self->buildUser, self->buildPassword);
if (!ssh) {
message(MSG_ERROR, "Unable to connect to SSH port.");
message(MSG_ERROR, "Unable to connect to SSH port");
return;
}
// Do we have a build package to clean up?
if (sshSFTPFileExists(ssh, "build/build.tar.bz2")) {
message(MSG_INFO, "Removing stale build results");
}
// Generate build.start.
buildStart = utilCreateString("%sbuild.start", self->windowData.path);
out = fopen(buildStart, "wt");
if (!out) {
message(MSG_SEVERE, "Unable to write temporary build.start file.");
message(MSG_SEVERE, "Unable to write temporary build.start file");
unlink(buildStart); // Just in case.
message(MSG_INFO, "Build canceled");
sshDisconnect(&ssh);
return;
}
@ -1085,6 +1095,25 @@ gboolean projectAddToTree(ProjectDataT *self, char *filename) {
}
static void receiveSFTP(SFTPT *sftp) {
ProjectDataT *self = (ProjectDataT *)sftp->userData;
if (sftp->finished) {
if (sftp->success) {
//***TODO*** Unpack results
message(MSG_INFO, "Unpacking build");
} else {
message(MSG_ERROR, "Receiving %s from build server - FAILED", sftp->remoteName);
message(MSG_INFO, "Build canceled");
sshDisconnect(&sftp->sshData);
}
DEL(sftp->localName);
DEL(sftp->remoteName);
DEL(sftp);
}
}
static void saveConfig(ProjectDataT *self) {
char *temp;
FILE *out;
@ -1249,32 +1278,56 @@ static gboolean updateBuildOptions(ProjectDataT *self) {
static void sendSFTP(SFTPT *sftp) {
ProjectDataT *self = (ProjectDataT *)sftp->userData;
SFTPT *next;
char *title;
SFTPT *last;
char *target;
char *name;
last = sftp;
// If source and target are missing, this is the first file - start sending.
// Or, did the current transfer succeed? If so, send next file.
if ((sftp->source == NULL && sftp->target == NULL) || (sftp->finished && sftp->success)) {
if ((sftp->localName == NULL && sftp->remoteName == NULL) || (sftp->finished && sftp->success)) {
if (sftp->localName == NULL && sftp->remoteName == NULL) {
message(MSG_INFO, "Building %s", self->windowData.filename);
}
// Are there more files to send?
if (arrlen(_sendList) > 0) {
// Send next entry in the file list.
title = utilCreateString("Building %s", self->windowData.filename);
target = utilFileBasename(_sendList[0]);
sftp = sshSFTPSend(title, sftp->sshData, _sendList[0], target, sendSFTP);
name = utilFileBasename(_sendList[0]);
target = utilCreateString("build/%s", name);
message(MSG_INFO, "Sending %s to build server", name);
sftp = sshSFTPSend(last->sshData, _sendList[0], target, sendSFTP);
sftp->userData = self;
DEL(target);
DEL(title);
DEL(name);
arrdel(_sendList, 0);
return;
} else {
// Finished!
//***TODO***
message(MSG_INFO, "Waiting for build to complete");
g_timeout_add_seconds(5, waitForBuild, last->sshData);
}
}
// Did the transfer fail?
if (sftp->finished && !sftp->success) {
//***TODO***
// Do we have a transfer? Did the transfer fail?
if ((!sftp) || (sftp->finished && !sftp->success)) {
if (sftp) {
message(MSG_ERROR, "Sending %s to build server - FAILED", sftp->remoteName);
}
message(MSG_INFO, "Build canceled");
sshDisconnect(&last->sshData);
// Clear any remaining transfers.
while (arrlen(_sendList) > 0) {
arrdel(_sendList, 0);
}
ARRFREE(_sendList);
}
// Do we need to free this?
if (last->finished) {
// Do not delete last->sshData as we continue to need it until everything is finished.
DEL(last->localName);
DEL(last->remoteName);
DEL(last);
}
}
@ -1326,6 +1379,31 @@ static void targetArrayDelete(TargetT ***array) {
}
static gboolean waitForBuild(gpointer userData) {
SSHT *ssh = (SSHT *)userData;
SFTPT *sftp;
char *temp;
if (sshSFTPFileExists(ssh, "build/build.tar.bz2")) {
// Build complete! Next step!
message(MSG_INFO, "Retrieving build results");
temp = utilCreateString("%s%cbuild.tar.bz2", g_get_user_config_dir(), UTIL_PATH_CHAR);
sftp = sshSFTPReceive(ssh, "build/build.tar.bz2", temp, receiveSFTP);
DEL(temp);
if (!sftp) {
message(MSG_ERROR, "Receiving build from build server - FAILED");
message(MSG_INFO, "Build canceled");
sshDisconnect(&ssh);
}
return G_SOURCE_REMOVE;
}
// Keep waiting.
//***TODO*** Add a timeout of some kind here and sshDisconnect.
return G_SOURCE_CONTINUE;
}
EVENT gboolean winProjectClose(GtkWidget *object, gpointer userData) {
// userData is not reliable due to menuVectorFileClose and util indirectly calling us.
ProjectDataT *self = (ProjectDataT *)utilGetWindowData(object);

158
src/ssh.c
View file

@ -47,9 +47,10 @@ static char *_buffer = NULL;
static int _bufferLen = 0;
gboolean sshUpdate(gpointer userData); // Not static
gboolean sshSFTPUpdate(gpointer userData); // Not static
static int sshWaitSocket(int socket_fd, LIBSSH2_SESSION *session);
gboolean sshUpdate(gpointer userData); // Not static
static SFTPT *sshSFTPTransfer(SSHT *sshData, char *local, char *remote, SFTPCallback callback, gboolean uploading);
gboolean sshSFTPUpdate(gpointer userData); // Not static
static int sshWaitSocket(int socket_fd, LIBSSH2_SESSION *session);
SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password) {
@ -220,6 +221,7 @@ gboolean sshSFTPFileExists(SSHT *sshData, char *target) {
LIBSSH2_SFTP_ATTRIBUTES attrs;
char *path;
char *file;
char *temp;
char buffer[512];
int rc;
gboolean result = FALSE;
@ -233,8 +235,25 @@ gboolean sshSFTPFileExists(SSHT *sshData, char *target) {
}
} while (!session);
path = utilFilePath(target);
file = utilFileBasename(target);
// Split filename and path.
// Cannot use utilFilePath and friends because this is a remote path.
temp = strdup(target); // Make copy in case they pass in a constant.
path = strdup("/"); // Set some defaults.
file = strdup(target); // Set some defaults.
rc = (int)strlen(temp);
while (rc > 0) {
if (temp[rc] == '/') {
// Found it.
temp[rc] = 0;
DEL(path);
DEL(file);
path = strdup(temp);
file = strdup(&temp[rc + 1]);
break;
}
rc--;
}
DEL(temp);
do {
handle = libssh2_sftp_opendir(session, path);
@ -276,13 +295,23 @@ gboolean sshSFTPFileExists(SSHT *sshData, char *target) {
}
SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPCallback callback) {
SFTPT *sshSFTPReceive(SSHT *sshData, char *remote, char *local, SFTPCallback callback) {
return sshSFTPTransfer(sshData, local, remote, callback, FALSE);
}
SFTPT *sshSFTPSend(SSHT *sshData, char *local, char *remote, SFTPCallback callback) {
return sshSFTPTransfer(sshData, local, remote, callback, TRUE);
}
static SFTPT *sshSFTPTransfer(SSHT *sshData, char *local, char *remote, SFTPCallback callback, gboolean uploading) {
SFTPT *sftp = NULL;
int i;
// Do not allow two of the same transfers to be started.
for (i=0; i<arrlen(_activeSFTPs); i++) {
if ((strcmp(_activeSFTPs[i]->source, source) == 0) && (strcmp(_activeSFTPs[i]->target, target) == 0)) {
if ((strcmp(_activeSFTPs[i]->localName, local) == 0) && (strcmp(_activeSFTPs[i]->remoteName, remote) == 0)) {
// Return existing transfer handle.
return _activeSFTPs[i];
}
@ -291,21 +320,20 @@ SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPC
sftp = NEW(SFTPT);
if (sftp) {
sftp->fileSize = utilFileSize(source);
sftp->local = fopen(source, "rb");
sftp->fileSize = uploading ? utilFileSize(local) : 0;
sftp->local = fopen(local, uploading ? "rb" : "wb");
if (sftp->local) {
sftp->fileSent = 0;
sftp->nread = 0;
sftp->ptr = NULL;
sftp->title = strdup(title);
sftp->source = strdup(source);
sftp->target = strdup(target);
sftp->sshData = sshData;
sftp->uploading = TRUE;
sftp->readMore = TRUE;
sftp->finished = FALSE;
sftp->success = FALSE;
sftp->callback = callback;
sftp->fileSent = 0;
sftp->nread = 0;
sftp->ptr = NULL;
sftp->localName = strdup(local);
sftp->remoteName = strdup(remote);
sftp->sshData = sshData;
sftp->uploading = uploading;
sftp->readMore = TRUE;
sftp->finished = FALSE;
sftp->success = FALSE;
sftp->callback = callback;
do {
sftp->session = libssh2_sftp_init(sshData->session);
if (!sftp->session) {
@ -315,7 +343,17 @@ SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPC
}
} while (!sftp->session);
do {
sftp->handle = libssh2_sftp_open(sftp->session, sftp->target, LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);
if (uploading) {
sftp->handle = libssh2_sftp_open(sftp->session, sftp->remoteName,
LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP |
LIBSSH2_SFTP_S_IROTH);
} else {
sftp->handle = libssh2_sftp_open(sftp->session, sftp->remoteName,
LIBSSH2_FXF_READ,
LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP |
LIBSSH2_SFTP_S_IROTH);
}
if (!sftp->handle) {
if (libssh2_session_last_errno(sshData->session) != LIBSSH2_ERROR_EAGAIN) {
break;
@ -323,8 +361,6 @@ SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPC
}
} while (!sftp->handle);
if (sftp->handle) {
//***TODO*** SFTP Status Dialog
//sftp->status = new SFTPStatus(sftp);
arrput(_activeSFTPs, sftp);
return sftp;
} else {
@ -344,60 +380,78 @@ SFTPT *sshSFTPSend(char *title, SSHT *sshData, char *source, char *target, SFTPC
gboolean sshSFTPUpdate(gpointer userData) {
int i;
int rc;
int count = 0;
SFTPT *result = NULL;
(void)userData;
for (i=0; i<arrlen(_activeSFTPs); i++) {
count++;
// More data to read?
if (_activeSFTPs[i]->readMore) {
_activeSFTPs[i]->nread = fread(_activeSFTPs[i]->buffer, 1, sizeof(_activeSFTPs[i]->buffer), _activeSFTPs[i]->local);
if (_activeSFTPs[i]->nread <= 0) {
result = _activeSFTPs[i];
arrdel(_activeSFTPs, i);
break;
}
_activeSFTPs[i]->ptr = _activeSFTPs[i]->buffer;
_activeSFTPs[i]->readMore = FALSE;
}
// Uploading or downloading?
if (_activeSFTPs[i]->uploading) {
// Uploading
// Is there data to send?
if (_activeSFTPs[i]->nread > 0) {
// Send data.
while ((rc = (int)libssh2_sftp_write(_activeSFTPs[i]->handle, _activeSFTPs[i]->ptr, _activeSFTPs[i]->nread)) == LIBSSH2_ERROR_EAGAIN) {
sshWaitSocket(_activeSFTPs[i]->sshData->sock, _activeSFTPs[i]->sshData->session);
// More data to read?
if (_activeSFTPs[i]->readMore) {
_activeSFTPs[i]->nread = fread(_activeSFTPs[i]->buffer, 1, sizeof(_activeSFTPs[i]->buffer), _activeSFTPs[i]->local);
if (_activeSFTPs[i]->nread <= 0) {
result = _activeSFTPs[i];
arrdel(_activeSFTPs, i);
break;
}
_activeSFTPs[i]->ptr = _activeSFTPs[i]->buffer;
_activeSFTPs[i]->readMore = FALSE;
}
if (rc >= 0) {
_activeSFTPs[i]->ptr += rc;
_activeSFTPs[i]->nread -= rc;
_activeSFTPs[i]->fileSent += rc;
// Is there data to send?
if (_activeSFTPs[i]->nread > 0) {
// Send data.
while ((rc = (int)libssh2_sftp_write(_activeSFTPs[i]->handle, _activeSFTPs[i]->ptr, _activeSFTPs[i]->nread)) == LIBSSH2_ERROR_EAGAIN) {
sshWaitSocket(_activeSFTPs[i]->sshData->sock, _activeSFTPs[i]->sshData->session);
}
if (rc >= 0) {
_activeSFTPs[i]->ptr += rc;
_activeSFTPs[i]->nread -= rc;
_activeSFTPs[i]->fileSent += rc;
} else {
// We need more data!
_activeSFTPs[i]->readMore = TRUE;
}
} else {
// We need more data!
_activeSFTPs[i]->readMore = TRUE;
}
} else {
// We need more data!
_activeSFTPs[i]->readMore = TRUE;
// Downloading
// Data to read?
while ((rc = libssh2_sftp_read(_activeSFTPs[i]->handle, _activeSFTPs[i]->buffer, sizeof(_activeSFTPs[i]->buffer))) == LIBSSH2_ERROR_EAGAIN) {
sshWaitSocket(_activeSFTPs[i]->sshData->sock, _activeSFTPs[i]->sshData->session);
}
if (rc > 0) {
_activeSFTPs[i]->fileSize += rc;
fwrite(_activeSFTPs[i]->buffer, 1, rc, _activeSFTPs[i]->local);
} else {
result = _activeSFTPs[i];
arrdel(_activeSFTPs, i);
break;
}
}
}
if (result) {
result->finished = TRUE;
result->success = TRUE;
fclose(result->local);
libssh2_sftp_close(result->handle);
libssh2_sftp_shutdown(result->session);
//***TODO*** Display in status dialog
//result->status->Finished();
//result->status->Close();
if (result->callback) {
result->callback(result);
}
DEL(result);
}
return count;
return G_SOURCE_CONTINUE;
}

View file

@ -409,6 +409,16 @@ GtkWidget *utilFindChildWidget(GtkWidget *parent, const gchar *name) {
}
void utilForceUpdate(void) {
//***TODO*** This is supposed to force any pending paint operations.
// It doesn't work.
//while (g_main_context_pending(NULL)) {
g_main_context_iteration(NULL,FALSE);
//}
}
char *utilGetToken(char *input, char *delimit, char *openblock, char *closeblock) {
static char *token = NULL;
char *lead = NULL;
@ -642,6 +652,9 @@ void utilWindowRegister(gpointer windowData) {
// Grab title.
w->title = strdup(gtk_window_get_title(GTK_WINDOW(w->window)));
gtk_widget_show_all(w->window);
utilForceUpdate();
hmput(_windowList, w->window, windowData);
debug("Window Registered: %ld\n", hmlen(_windowList));
}