I have written a code which read file from remote device using libssh scp APIs.
I have a specific requirement wherein I want to scp a .tar file from a remote device.
I am able to read .tar content into a buffer, but I am not sure how to create .tar file out of that buffer.
Any help would be appreciated.
Thanks.
Code snippet:
char *t_filename, t_buffer[32768];
....
t_rc = ssh_scp_pull_request(t_scp);
switch(t_rc)
{
case SSH_SCP_REQUEST_NEWFILE:
t_filesize = ssh_scp_request_get_size(t_scp);
t_filename = strdup(ssh_scp_request_get_filename(t_scp));
t_filemode = ssh_scp_request_get_permissions(t_scp);
fprintf(stderr, "Receiving file %s, size %d, permisssions 0%o\n", t_filename, t_filesize, t_filemode);
ssh_scp_accept_request(t_scp);
t_rc = ssh_scp_read(t_scp, t_buffer, sizeof(t_buffer));
if(t_rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n", ssh_get_error(in_session));
ssh_scp_close(t_scp);
ssh_scp_free(t_scp);
return t_rc;
}
fprintf(stderr, "Bytes received = %d\n", t_rc);
FILE *fptr = fopen(t_filename, "w");
if(NULL != fptr)
{
fwrite(t_buffer,sizeof(t_buffer),1,fptr);
fclose(fptr);
}
break;
}
Create a local file using either open() or fopen(), then feed in the raw data using write() or fwrite(). When finished, call close() or fclose().
Updated your code-snippet, not compile tested, but gives you the idea.
The remote read should be repeated until the whole file has been received, also, you might receive chunks that are smaller than sizeof (t_buffer), so do not write out more data than you received.
char *t_filename, t_buffer[32768];
....
t_rc = ssh_scp_pull_request(t_scp);
switch(t_rc)
{
case SSH_SCP_REQUEST_NEWFILE:
t_filesize = ssh_scp_request_get_size(t_scp);
t_filename = strdup(ssh_scp_request_get_filename(t_scp));
t_filemode = ssh_scp_request_get_permissions(t_scp);
fprintf(stderr, "Receiving file %s, size %d, permisssions 0%o\n", t_filename, t_filesize, t_filemode);
FILE *fptr = fopen(t_filename, "w");
if(NULL == fptr)
{
fprintf (stderr, "Error opening local file: %s, error %s\n", t_filename, strerror (errno));
ssh_scp_deny_request (t_scp, "Unable to open local file");
break;
}
ssh_scp_accept_request(t_scp);
do
{
t_rc = ssh_scp_read(t_scp, t_buffer, sizeof(t_buffer));
if(t_rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n", ssh_get_error(in_session));
fclose(fptr);
ssh_scp_close(t_scp);
ssh_scp_free(t_scp);
return t_rc;
}
fprintf(stderr, "Bytes received = %d\n", t_rc);
if (fwrite(t_buffer,t_rc,1,fptr) != 1)
{
fprintf (stderr, "Error writing file data: %s\n", strerror (errno));
fclose(fptr);
ssh_scp_close(t_scp);
ssh_scp_free(t_scp);
return t_rc;
}
} while (t_rc != 0);
fclose (fptr);
break;
}
Related
I am trying to write a simple program (toy example) that copies a file from a remote host to the local machine.
It works when I try to copy a txt file, but not for files like mp4.
Here is my code, which is basically parts stitched together from the tutorial: https://pastebin.com/0FPrmeDx
This is where the error happens:
int scp_receive(ssh_session session, ssh_scp scp)
{
int rc;
int size, mode;
char *filename, *buffer;
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_NEWFILE)
{
fprintf(stderr, "Error receiving information about file: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
size = ssh_scp_request_get_size(scp);
filename = strdup(ssh_scp_request_get_filename(scp));
mode = ssh_scp_request_get_permissions(scp);
printf("Receiving file %s, size %d, permisssions 0%o\n",
filename, size, mode);
free(filename);
buffer = malloc(size);
if (buffer == NULL)
{
fprintf(stderr, "Memory allocation error\n");
return SSH_ERROR;
}
ssh_scp_accept_request(scp);
rc = ssh_scp_read(scp, buffer, size);
if (rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n",
ssh_get_error(session));
free(buffer);
return rc;
}
printf("Done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
int filedesc = open("/home/user/video.mp4", O_WRONLY | O_CREAT);
if (filedesc < 0) {
return -1;
}
write(filedesc, buffer, size);
free(buffer);
close(filedesc);
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_EOF)
{
fprintf(stderr, "Unexpected request: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
return SSH_OK;
}
Error is fired with the code:
rc = ssh_scp_pull_request(scp);
if (rc != SSH_SCP_REQUEST_EOF)
{
fprintf(stderr, "Unexpected request: %s\n",
ssh_get_error(session));
return SSH_ERROR;
}
This is the error that I get:
Unexpected request: ssh_scp_pull_request called under invalid state
I tried to figure it out, but I couldn't make any progress on it.
Update 1:
The size of the copied file is exactly the same as of the source file both for txt and mp4 files. However, the copied file seems to be largely empty...
When copied, the permissions are changed from -rwxr-xr-x to --wxr-----.
Update 2:
It seems that the file size plays a major role here. Very small files (10-15kb) are copied without problems. Bigger files are not copied and produce the above mentioned error...
You cannot expect, that ssh_scp_read() reads the whole data in a single call to it. You have to iterate, until no more data is left to read:
int r = 0;
while (r < size) {
int st = ssh_scp_read(scp, buffer+r, size-r);
r += st;
}
Now, a subsequent call to ssh_scp_pull_request(scp) should succeed.
I want to do a error check in a openFile Function in C and on errno:2 I want to recursivly call again the same function.
I don't get the right answer, if I want to do fputs() after opened the file I get a Error (Bad file descriptor)
Here is my code:
void openFile(FILE **fstream, char* path, char* mode) {
*fstream = fopen(path, mode);
if(*fstream == NULL) {
printf("\nError number %2d : ",errno);
perror("Error opening file: ");
switch (errno) {
case 2:
printf("Creating file %s now...\n", path);
*fstream = fopen(path, "a+"); //Creating file in append-mode
if (fstream == NULL) {
perror("Couldn't open the file!\nError");
exit(EXIT_FAILURE);
}
fclose(*fstream); //Closing filestream
openFile(fstream, path, mode); //Recursive call of openFile() to re-open in read-mode
/* freopen(path,mode,fstream) */ //Doesn't work either
break;
default:
exit(EXIT_FAILURE);
break;
}
} else if (*fstream != NULL) {
printf("Successfully opened %s\n", path);
}
}
The call:
openFile(&fp, path,"r");
if (fputs("blabla\nblabla\n",fp) == EOF) {
perror("Unable to write file with fputs()");
}
What I'm doing wrong? I think it's at the point of the recursive call of the function, but what I have to do here? I don't get it..
Output is:
> .\a
Content of path: test.txt
Error number 2 : Error opening file: : No such file or directory
Creating file test.txt now...
Successfully opened test.txt
Unable to write file with fputs(): Bad file descriptor
PS: I am a beginner with C, I've read and youtubed a lot about pointer, but I don't get the mistake.
Thank you in advance
You opened the file with "r" yet you're attempting to write. You need "w" instead.
I understand fopen() opens file and creates a buffer for read and write operations on that file. fopen() returns a pointer for that buffer.
So my question is, in the code below, the _copy function body has a temp matrix to transfer between the fread() and fwrite(). why cant I directly transfer from buffer to buffer?
/* example: copyfile.exe xxxxx.txt zzzzzz.txt */
#include <stdio.h>
#include <stdlib.h>
#define BUFF 8192
void _copy(FILE *source, FILE *destination);
int main(int argc, char *argv[])
{
FILE *fp1, *fp2; // fp1 source file pointer// fp2 copied file pointer
if (argc !=3 ) //command line must have 3 arguments
{
fprintf(stderr, "Usage: %s (source file) (copy file)\n", argv[0][0]);
exit(EXIT_FAILURE);
}
if ((fp1 = fopen(argv[1], "rb")) == NULL) //Opening source file
{
fprintf(stderr, "Could not open %s\n",argv[1]);
exit(EXIT_FAILURE);
}
if((fp2 = fopen(argv[2], "ab+")) == NULL) //Opening destination file
{
fprintf(stderr, "could not create %s \n",argv[2]);
exit(EXIT_FAILURE);
}
if( setvbuf(fp1,NULL, _IOFBF, BUFF) != 0) //Setting buffer for source file
{
fputs("Can't create output buffer\n", stderr);
exit(EXIT_FAILURE);
}
if( setvbuf(fp2,NULL, _IOFBF, BUFF) != 0) //Setting buffer for destination file
{
fputs("Can't create input buffer\n", stderr);
exit(EXIT_FAILURE);
}
_copy(fp1, fp2);
if (ferror(fp1)!=0)
fprintf(stderr, "Error reading file %s\n", argv[1]);
if(ferror(fp2)!=0)
fprintf(stderr, "Error writing file %s\n",argv[2]);
printf("Done coping %s (source) to %s (destination) \n",argv[1], argv[2]);
fclose(fp1);
fclose(fp2);
return (0);
}
void _copy(FILE *source, FILE *destination)
{
size_t bytes;
static char temp[BUFF];
while((bytes = fread(temp,sizeof(char),BUFF,source))>0)
fwrite(temp,sizeof(char),bytes,destination);
}
You cannot use the underlying buffer from a FILE * in another FILE *. As you were told in comment, FILE * is an opaque pointer. But you can avoid the overhead of copying data between buffers by forcing both files in non buffered mode:
setbuf(fp, NULL); // cause the stream to be unbuffered
I'm writing code that's supposed to verify that a .txt file is a certain format.
I wrote my code as I saw in a tutorial and in the website
and for some reason my program doesn't even print my file.
Can you tell me what I'm doing wrong?
The code will do something far more complex, but I'm still trying to work on my basics.
Here's my code so far:
int main(int argc, char *argv[]) {
/* argv[0] = name of my running file
* argv[1] = the first file that i receive
*/
define MAXBUFLEN 4096
char source[MAXBUFLEN + 1];
int badReturnValue = 1;
char *error = "Error! trying to open the file ";
if (argc != 2) {
printf("please supply a file \n");
return badReturnValue;
}
char *fileName = argv[1];
FILE *fp = fopen(argv[1], "r"); /* "r" = open for reading */
if (fp != NULL) {
size_t newLen = fread(&source, sizeof(char), MAXBUFLEN, fp);
if (ferror(fp) != 0) {
printf("%s %s", error, fileName);
return badReturnValue;
}
int symbol;
while ((symbol = getc(fp)) != EOF) {
putchar(symbol);
}
printf("finish");
fclose(fp);
}
else {
printf("%s %s", error, fileName);
return badReturnValue;
}
}
I think you need a bit more explanations:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// there might be a macro BUFLEN defined in stdio
// which size is optimized for reading in chunks.
// Test if avaiable otherwise define it
#ifndef BUFLEN
# define BUFLEN 4096
#endif
int main(int argc, char *argv[])
{
char source[BUFLEN];
char *filename;
FILE *fp;
size_t fpread, written;
char c;
int ret_fclose;
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// reset errno, just in case
errno = 0;
// work on copy
filename = malloc(strlen(argv[1]) + 1);
if (filename == NULL) {
fprintf(stderr, "Allocating %zu bytes failed\n", strlen(argv[1]) + 1);
exit(EXIT_FAILURE);
}
filename = strcpy(filename, argv[1]);
// try to open the file at 'filename'
fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Opening file \"%s\" filename failed\n", filename);
// errno might got set to something usable, check and print
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// You have two options here. One is to read in chunks of MAXBUFLEN
while ((fpread = fread(&source, 1, BUFLEN, fp)) > 0) {
// Do something with the stuff we read into "source"
// we do nothing with it here, we just write to stdout
written = fwrite(&source, 1, fpread, stdout);
// you can use 'written' for error check when writing to an actual file
// but it is unlikely (but not impossible!) with stdout
// test if we wrote what we read
if ((fpread - written) != 0) {
fprintf(stderr, "We did not write what we read. Diff: %d\n",
(int) (fpread - written));
}
}
// fread() does not distinguish between EOF and error, we have to check by hand
if (feof(fp)) {
// we have read all, exit
puts("\n\n\tfinish\n");
// No, wait, we want to do it again in a different way, so: no exit
// exit(EXIT_SUCCESS);
} else {
// some error may have occured, check
if (ferror(fp)) {
fprintf(stderr, "Something bad happend while reading \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
}
// the other way is to read it byte by byte
// reset the filepointers/errors et al.
rewind(fp);
// rewind() should have reseted errno, but better be safe than sorry
errno = 0;
printf("\n\n\tread and print \"%s\" again\n\n\n\n", filename);
// read one byte and print it until end of file
while ((c = fgetc(fp)) != EOF) {
// just print. Gathering them into "source" is left as an exercise
fputc(c, stdout);
}
// clean up
errno = 0;
ret_fclose = fclose(fp);
// even fclose() might fail
if (ret_fclose == EOF) {
fprintf(stderr, "Something bad happend while closing \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// The macros EXIT_FAILURE and EXIT_SUCCESS are set to the correct values for
// the OS to tell it if we had an eror or not.
// Using exit() is noot necessary here but there exits teh function atexit()
// that runs a given function (e.g: clean up, safe content etc.) when called
exit(EXIT_SUCCESS);
}
You read from the file twice but only print once.
If the file is to small the first reading will read all of the contents, and the second reading will not produce anything so you don't print anything.
I believe you have to reset the pointer after using fread.
Try fseek(fp, SEEK_SET, 0) to reset the pointer to the beginning of the file. Then print the file.
EDIT
I had a problem a while ago writing a program in C which utilizes scp to transfer and download files to a server. The program had to be written for windows. Initially I attempted to use the libCurl library which then I encountered different problems and it didn't work well. After switching to libssh and the issue was fixed.
I'm posting a segment of my sample code which downloads a file from a remote server. hopefully this could be helpful to anyone who lands here.
int scp_recv_file(ssh_session in_session,
char * in_remotefile, char * in_localfile)
{
ssh_scp t_scp = NULL;
int t_rc, t_filesize, t_filemode = -1;
char *t_filename, *t_buffer;
t_scp = ssh_scp_new
(in_session, SSH_SCP_READ | SSH_SCP_RECURSIVE, in_remotefile);
if (t_scp == NULL)
{
fprintf(stderr, "Error allocating scp session: %s\n",
ssh_get_error(in_session));
return SSH_ERROR;
}
t_rc = ssh_scp_init(t_scp);
if (t_rc != SSH_OK)
{
fprintf(stderr, "Error initializing scp session: %s\n",
ssh_get_error(in_session));
ssh_scp_free(t_scp);
return t_rc;
}
//create ssh pull a file request
t_rc = ssh_scp_pull_request(t_scp);
if (t_rc != SSH_SCP_REQUEST_NEWFILE)
{
fprintf(stderr, "Error receiving information about file: %s\n",
ssh_get_error(in_session));
return SSH_ERROR;
}
t_filesize = ssh_scp_request_get_size(t_scp);
t_filename = strdup(ssh_scp_request_get_filename(t_scp));
t_filemode = ssh_scp_request_get_permissions(t_scp);
printf("Receiving file %s, size %d, permisssions 0%o\n",
t_filename, t_filesize, t_filemode);
t_buffer = malloc(t_filesize);
if (t_buffer == NULL)
{
fprintf(stderr, "Memory allocation error\n");
return SSH_ERROR;
}
ssh_scp_accept_request(t_scp);
t_rc = ssh_scp_read(t_scp, t_buffer, t_filesize);
if (t_rc == SSH_ERROR)
{
fprintf(stderr, "Error receiving file data: %s\n",
ssh_get_error(in_session));
free(t_buffer);
return t_rc;
}
printf("Done\n");
//write buffer to file
writeToFile(in_localfile, t_buffer, t_filesize);
//write(1, buffer, size);
//free allocated memory
free(t_buffer);
free(t_filename);
t_rc = ssh_scp_pull_request(t_scp);
if (t_rc != SSH_SCP_REQUEST_EOF)
{
fprintf(stderr, "Unexpected request: %s\n",
ssh_get_error(in_session));
return SSH_ERROR;
}
//close scp and free
ssh_scp_close(t_scp);
ssh_scp_free(t_scp);
return SSH_OK;
}
Switched to lilbssh. You can manage the scp stages manually and works ok. However, I don't know if libssh2 is better or not. Any comments?