Is fopen("tftp://1.1.1.1/file.txt","rb"); a valid statement? Can urls be opened using fopen in C programming?
No, but you can use libcurl, an example:
#include <stdio.h>
#include <curl/curl.h>
/*
* This is an example showing how to get a single file from an FTP server.
* It delays the actual destination file creation until the first write
* callback so that it won't create an empty file in case the remote file
* doesn't exist or something else fails.
*/
struct FtpFile {
const char *filename;
FILE *stream;
};
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure, can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
int main(void)
{
CURL *curl;
CURLcode res;
struct FtpFile ftpfile={
"curl.tar.gz", /* name to store the file as if succesful */
NULL
};
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
/*
* You better replace the URL with one that works!
*/
curl_easy_setopt(curl, CURLOPT_URL,
"ftp://ftp.example.com/pub/www/utilities/curl/curl-7.9.2.tar.gz");
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
/* Switch on full protocol/debug output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
if(CURLE_OK != res) {
/* we failed */
fprintf(stderr, "curl told us %d\n", res);
}
}
if(ftpfile.stream)
fclose(ftpfile.stream); /* close the local file */
curl_global_cleanup();
return 0;
}
Or (as pointed out by #Paul) you can pipe a process (E.g.: wget url) with popen:
#include <stdio.h>
FILE *popen(const char *command, const char *mode);
int pclose(FILE *stream);
int main(void)
{
/* wget -q = silent mode */
FILE *cmd = popen("wget -q -O - ftp://debian.org/debian-security/README.security", "r");
char result[1024];
while (fgets(result, sizeof(result), cmd) != NULL)
printf("%s", result);
pclose(cmd);
return 0;
}
The fopen in <stdio.h> doesn't do that.
However, nothing prevents someone from writing a function called fopen() that does something else.
FILE *popen(const char *command, const char *mode) can be used to spawn a process running an appropriate command line tool such as tftp or wget, and thereby accomplish this downloading of a remote resource into a file descriptor accessible from C code. The syntax for a popen() call is very similar to what you have shown. It is missing the program name for the download utility, though. A bare url or ftp address won't work for popen().
See:
fopen man page
popen man page
Also note:
The PHP language version of fopen() does open bare URLs. But PHP != C
It's not that easy as simply using fopen but it can be done.
You need to use libcurl. Take a look here.
From the site:
/*****************************************************************************
*
* This example requires libcurl 7.9.7 or later.
*/
#include <stdio.h>
#include <string.h>
#ifndef WIN32
#include <sys/time.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <curl/curl.h>
enum fcurl_type_e {
CFTYPE_NONE=0,
CFTYPE_FILE=1,
CFTYPE_CURL=2
};
struct fcurl_data
{
enum fcurl_type_e type; /* type of handle */
union {
CURL *curl;
FILE *file;
} handle; /* handle */
char *buffer; /* buffer to store cached data*/
size_t buffer_len; /* currently allocated buffers length */
size_t buffer_pos; /* end of data in buffer*/
int still_running; /* Is background url fetch still in progress */
};
typedef struct fcurl_data URL_FILE;
/* exported functions */
URL_FILE *url_fopen(const char *url,const char *operation);
int url_fclose(URL_FILE *file);
int url_feof(URL_FILE *file);
size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
char * url_fgets(char *ptr, size_t size, URL_FILE *file);
void url_rewind(URL_FILE *file);
/* we use a global one for convenience */
CURLM *multi_handle;
/* curl calls this routine to get more data */
static size_t write_callback(char *buffer,
size_t size,
size_t nitems,
void *userp)
{
char *newbuff;
size_t rembuff;
URL_FILE *url = (URL_FILE *)userp;
size *= nitems;
rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */
if(size > rembuff) {
/* not enough space in buffer */
newbuff=realloc(url->buffer,url->buffer_len + (size - rembuff));
if(newbuff==NULL) {
fprintf(stderr,"callback buffer grow failed\n");
size=rembuff;
}
else {
/* realloc suceeded increase buffer size*/
url->buffer_len+=size - rembuff;
url->buffer=newbuff;
}
}
memcpy(&url->buffer[url->buffer_pos], buffer, size);
url->buffer_pos += size;
return size;
}
/* use to attempt to fill the read buffer up to requested number of bytes */
static int fill_buffer(URL_FILE *file, size_t want)
{
fd_set fdread;
fd_set fdwrite;
fd_set fdexcep;
struct timeval timeout;
int rc;
/* only attempt to fill buffer if transactions still running and buffer
* doesnt exceed required size already
*/
if((!file->still_running) || (file->buffer_pos > want))
return 0;
/* attempt to fill buffer */
do {
int maxfd = -1;
long curl_timeo = -1;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);
/* set a suitable timeout to fail on */
timeout.tv_sec = 60; /* 1 minute */
timeout.tv_usec = 0;
curl_multi_timeout(multi_handle, &curl_timeo);
if(curl_timeo >= 0) {
timeout.tv_sec = curl_timeo / 1000;
if(timeout.tv_sec > 1)
timeout.tv_sec = 1;
else
timeout.tv_usec = (curl_timeo % 1000) * 1000;
}
/* get file descriptors from the transfers */
curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
/* In a real-world program you OF COURSE check the return code of the
function calls. On success, the value of maxfd is guaranteed to be
greater or equal than -1. We call select(maxfd + 1, ...), specially
in case of (maxfd == -1), we call select(0, ...), which is basically
equal to sleep. */
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
switch(rc) {
case -1:
/* select error */
break;
case 0:
default:
/* timeout or readable/writable sockets */
curl_multi_perform(multi_handle, &file->still_running);
break;
}
} while(file->still_running && (file->buffer_pos < want));
return 1;
}
/* use to remove want bytes from the front of a files buffer */
static int use_buffer(URL_FILE *file,int want)
{
/* sort out buffer */
if((file->buffer_pos - want) <=0) {
/* ditch buffer - write will recreate */
if(file->buffer)
free(file->buffer);
file->buffer=NULL;
file->buffer_pos=0;
file->buffer_len=0;
}
else {
/* move rest down make it available for later */
memmove(file->buffer,
&file->buffer[want],
(file->buffer_pos - want));
file->buffer_pos -= want;
}
return 0;
}
URL_FILE *url_fopen(const char *url,const char *operation)
{
/* this code could check for URLs or types in the 'url' and
basicly use the real fopen() for standard files */
URL_FILE *file;
(void)operation;
file = malloc(sizeof(URL_FILE));
if(!file)
return NULL;
memset(file, 0, sizeof(URL_FILE));
if((file->handle.file=fopen(url,operation)))
file->type = CFTYPE_FILE; /* marked as URL */
else {
file->type = CFTYPE_CURL; /* marked as URL */
file->handle.curl = curl_easy_init();
curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
if(!multi_handle)
multi_handle = curl_multi_init();
curl_multi_add_handle(multi_handle, file->handle.curl);
/* lets start the fetch */
curl_multi_perform(multi_handle, &file->still_running);
if((file->buffer_pos == 0) && (!file->still_running)) {
/* if still_running is 0 now, we should return NULL */
/* make sure the easy handle is not in the multi handle anymore */
curl_multi_remove_handle(multi_handle, file->handle.curl);
/* cleanup */
curl_easy_cleanup(file->handle.curl);
free(file);
file = NULL;
}
}
return file;
}
int url_fclose(URL_FILE *file)
{
int ret=0;/* default is good return */
switch(file->type) {
case CFTYPE_FILE:
ret=fclose(file->handle.file); /* passthrough */
break;
case CFTYPE_CURL:
/* make sure the easy handle is not in the multi handle anymore */
curl_multi_remove_handle(multi_handle, file->handle.curl);
/* cleanup */
curl_easy_cleanup(file->handle.curl);
break;
default: /* unknown or supported type - oh dear */
ret=EOF;
errno=EBADF;
break;
}
if(file->buffer)
free(file->buffer);/* free any allocated buffer space */
free(file);
return ret;
}
int url_feof(URL_FILE *file)
{
int ret=0;
switch(file->type) {
case CFTYPE_FILE:
ret=feof(file->handle.file);
break;
case CFTYPE_CURL:
if((file->buffer_pos == 0) && (!file->still_running))
ret = 1;
break;
default: /* unknown or supported type - oh dear */
ret=-1;
errno=EBADF;
break;
}
return ret;
}
size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file)
{
size_t want;
switch(file->type) {
case CFTYPE_FILE:
want=fread(ptr,size,nmemb,file->handle.file);
break;
case CFTYPE_CURL:
want = nmemb * size;
fill_buffer(file,want);
/* check if theres data in the buffer - if not fill_buffer()
* either errored or EOF */
if(!file->buffer_pos)
return 0;
/* ensure only available data is considered */
if(file->buffer_pos < want)
want = file->buffer_pos;
/* xfer data to caller */
memcpy(ptr, file->buffer, want);
use_buffer(file,want);
want = want / size; /* number of items */
break;
default: /* unknown or supported type - oh dear */
want=0;
errno=EBADF;
break;
}
return want;
}
char *url_fgets(char *ptr, size_t size, URL_FILE *file)
{
size_t want = size - 1;/* always need to leave room for zero termination */
size_t loop;
switch(file->type) {
case CFTYPE_FILE:
ptr = fgets(ptr,size,file->handle.file);
break;
case CFTYPE_CURL:
fill_buffer(file,want);
/* check if theres data in the buffer - if not fill either errored or
* EOF */
if(!file->buffer_pos)
return NULL;
/* ensure only available data is considered */
if(file->buffer_pos < want)
want = file->buffer_pos;
/*buffer contains data */
/* look for newline or eof */
for(loop=0;loop < want;loop++) {
if(file->buffer[loop] == '\n') {
want=loop+1;/* include newline */
break;
}
}
/* xfer data to caller */
memcpy(ptr, file->buffer, want);
ptr[want]=0;/* allways null terminate */
use_buffer(file,want);
break;
default: /* unknown or supported type - oh dear */
ptr=NULL;
errno=EBADF;
break;
}
return ptr;/*success */
}
void url_rewind(URL_FILE *file)
{
switch(file->type) {
case CFTYPE_FILE:
rewind(file->handle.file); /* passthrough */
break;
case CFTYPE_CURL:
/* halt transaction */
curl_multi_remove_handle(multi_handle, file->handle.curl);
/* restart */
curl_multi_add_handle(multi_handle, file->handle.curl);
/* ditch buffer - write will recreate - resets stream pos*/
if(file->buffer)
free(file->buffer);
file->buffer=NULL;
file->buffer_pos=0;
file->buffer_len=0;
break;
default: /* unknown or supported type - oh dear */
break;
}
}
/* Small main program to retrive from a url using fgets and fread saving the
* output to two test files (note the fgets method will corrupt binary files if
* they contain 0 chars */
int main(int argc, char *argv[])
{
URL_FILE *handle;
FILE *outf;
int nread;
char buffer[256];
const char *url;
if(argc < 2)
url="http://192.168.7.3/testfile";/* default to testurl */
else
url=argv[1];/* use passed url */
/* copy from url line by line with fgets */
outf=fopen("fgets.test","w+");
if(!outf) {
perror("couldn't open fgets output file\n");
return 1;
}
handle = url_fopen(url, "r");
if(!handle) {
printf("couldn't url_fopen() %s\n", url);
fclose(outf);
return 2;
}
while(!url_feof(handle)) {
url_fgets(buffer,sizeof(buffer),handle);
fwrite(buffer,1,strlen(buffer),outf);
}
url_fclose(handle);
fclose(outf);
/* Copy from url with fread */
outf=fopen("fread.test","w+");
if(!outf) {
perror("couldn't open fread output file\n");
return 1;
}
handle = url_fopen("testfile", "r");
if(!handle) {
printf("couldn't url_fopen() testfile\n");
fclose(outf);
return 2;
}
do {
nread = url_fread(buffer, 1,sizeof(buffer), handle);
fwrite(buffer,1,nread,outf);
} while(nread);
url_fclose(handle);
fclose(outf);
/* Test rewind */
outf=fopen("rewind.test","w+");
if(!outf) {
perror("couldn't open fread output file\n");
return 1;
}
handle = url_fopen("testfile", "r");
if(!handle) {
printf("couldn't url_fopen() testfile\n");
fclose(outf);
return 2;
}
nread = url_fread(buffer, 1,sizeof(buffer), handle);
fwrite(buffer,1,nread,outf);
url_rewind(handle);
buffer[0]='\n';
fwrite(buffer,1,1,outf);
nread = url_fread(buffer, 1,sizeof(buffer), handle);
fwrite(buffer,1,nread,outf);
url_fclose(handle);
fclose(outf);
return 0;/* all done */
}
Related
I'm trying to send .amr files from my desktop to a SIM900 GSM module over UART.
I'm using teuniz's RS232 library.
I do the initialisation using AT commands and then read the file into a buffer and write it to the UART using the RS232_SendByte() library function byte-by-byte, but it doesn't seem to work.
I send the following AT commands:
AT+CFSINIT
AT+CFSWFILE=\"audio.amr\",0,6694,13000 # After which I get the CONNECT message from the SIM900 module
# Here's where I send the file
AT+CFSGFIS=\"audio.amr\"
Here's my code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rs232.h"
char *readFile(char *filename, int *size) {
char *source = NULL;
FILE *fp = fopen(filename, "rb");
if (fp != NULL) {
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0) {
/* Get the size of the file. */
long bufsize = ftell(fp);
if (bufsize == -1) { return NULL; }
/* Allocate our buffer to that size. */
source = malloc(sizeof(char) * (bufsize + 1));
if(!source) return NULL;
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0) { return NULL; }
/* Read the entire file into memory. */
size_t newLen = fread(source, sizeof(char), bufsize, fp);
if ( ferror( fp ) != 0 ) {
fputs("Error reading file", stderr);
free(source);
return NULL;
} else {
source[newLen++] = 0; /* Just to be safe. */
}
*size = bufsize;
}
fclose(fp);
}
return source;
}
int main(int argc, char *argv[])
{
int ret = 0, cport_nr = 2, bdrate=38400;
char data[2000] = {0};
if(RS232_OpenComport(cport_nr, bdrate)) {
printf("Can not open comport\n");
ret = -1;
goto END;
}
int size;
unsigned char *filebuf = readFile("audio.amr", &size);
if (!filebuf) {
ret = -1;
goto END_1;
}
/* Initialization */
RS232_cputs(cport_nr, "AT");
RS232_cputs(cport_nr, "AT+CFSINIT");
sleep(1);
RS232_cputs(cport_nr, "AT+CFSWFILE=\"audio.amr\",0,6694,13000");
/* Wait for CONNECT */
sleep(2);
printf("Sending file of size: %d\n", size);
int i;
for (i = 0; i < size; ++i) {
putchar(filebuf[i]);
RS232_SendByte(cport_nr, filebuf[i]);
}
free(filebuf);
sleep(1);
/* Check if file transferred right */
RS232_cputs(cport_nr, "AT+CFSGFIS=\"audio.amr\"");
END_1:
RS232_CloseComport(cport_nr);
END:
return ret;
}
EDIT 1
Normally, the procedure to send a file to SIM900 using AT commands would be as documented here:
AT+CFSINIT # Initialize flash; Response is OK
AT+CFSWFILE=<filename>,<writeMode>,<fileSize>,<InputTime> # Write file with these parameter; Response is CONNECT; So this is when I start sending the file
Here's where I send the file. If it worked and the sent file size matched the <filesize> sent in the above command, SIM900 must respond with OK, which it doesn't. :(
AT+CFSGFIS=<filename> # Gives the file size on flash. This gives me an error since the file didn't upload correctly.
This leads me to beleive there's something wrong with my program. I'm reading the file in binary mode. And the size reported is exacty the same as I specify in the AT+CFSWFILE=<filename>,<writeMode>,<fileSize>,<InputTime> command.
Trying to make a read function, that not only adds the numbers together. But also counts how many numbers were added.
Needs to be used in such script:
for i in 1 2 4 8 16 32 64; do
echo $i > /dev/summer0
sleep 60
done
But also just using a test case.
Here is the kernel module code:
/* the memory of the device
*/
int total;
/* Global variables declared as staic, so are global within the file.*/
static char *msg_Ptr;
static char msg[BUF_LEN];
static int n;
/* called after the device is opened
*/
int device_open(struct inode *inode, struct file *file)
{
printk("\nsummer device is open\n");
total = 0;
printk(msg);
msg_Ptr = msg;
return 0;
}
/* Read function */
static ssize_t device_read(struct file *filep, char *buffer, size_t length, loff_t *offset)
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we're at the end of the message, return 0 signifying end of file */
if (*msg_Ptr == 0) return 0;
/* Actually put the data into the buffer */
while (length && *msg_Ptr)
{
/* The buffer is in the user data segment, not the kernel segment;
* assignment won't work. We have to use put_use which copies data from
* the kernel data segment to the user data segment. */
__put_user(*(msg_Ptr++), buffer++);
length++;
n++;
}
/* Most read functions return the number of bytes put into the buffer */
return n;
}
/* handling of ioctl events
*/
long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
int number;
switch(ioctl_num)
{
case SUMMER_SET:
__get_user(number, (int*)ioctl_param);
total += number;
break;
case SUMMER_GET:
__put_user(total, (int*)ioctl_param);
break;
}
return 0;
}
/* table of event handlers of the device
*/
struct file_operations fops =
{
read : device_read,
//write : device_write,
open : device_open,
release: device_close,
unlocked_ioctl : device_ioctl
};
As for the testcase
int n;
int main(void)
{
int sum;
char buffer[BUF_LEN];
/* open character device */
int fd = open("/dev/summer0", O_RDWR); // open device
if(fd < 0)
{
printf("no device found\n");
return 0;
}
int nc = read(fd,buffer,4,n);
for(;;)
{
int num;
printf("Number (exit with 0) = ");
while(scanf("%d", &num) != 1)
;
printf("-- %d --\n", num);
if(num <= 0) break;
/* use ioctl to pass a value to the character device */
ioctl(fd, SUMMER_SET, &num);
n++;
}
/* use ioctl to get a value from the character device */
ioctl(fd, SUMMER_GET, &sum);
printf("#char = %d\n", nc);
printf("Result = %d\n", sum);
printf("Buffer: %s\n" , buffer);
close(fd); // close device
return 0;
}
The program is keeping track of the amount of numbers entered during a single test, but it is supposed to keep track of the numbers until I install the kernel module.
I am writing a character device that will be given a processes pid and return the parent process id, start time, and number of siblings. I am using this site: http://tldp.org/LDP/lkmpg/2.6/html/x892.html#AEN972 as a guide.
I would like to add an argument into the read call of the character driver but it is not letting me do that. My code looks like this:
/*
* chardev.c - Create an input/output character device
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/fs.h>
#include <asm/uaccess.h> /* for get_user and put_user */
#include "chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80
struct procinfo {
pid_t pid;
pid_t ppid;
struct timespec start_time;
int num_sib;
};
/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
static int Device_Open = 0;
/*
* The message the device will give when asked
*/
static char Message[BUF_LEN];
/*
* How far did the process reading the message get?
* Useful if the message is larger than the size of the
* buffer we get to fill in device_read.
*/
static char *Message_Ptr;
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_open(%p)\n", file);
#endif
/*
* We don't want to talk to two processes at the same time
*/
if (Device_Open)
return -EBUSY;
Device_Open++;
/*
* Initialize the message
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
#endif
/*
* We're now ready for our next caller
*/
Device_Open--;
module_put(THIS_MODULE);
return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
char __user * buffer, /* buffer to be
* filled with data */
pid_t pid,
size_t length, /* length of the buffer */
loff_t * offset)
{
/*
* Number of bytes actually written to the buffer
*/
int bytes_read = 0;
#ifdef DEBUG
printk(KERN_INFO "device_read(%p,%p,%d)\n", file, buffer, length);
#endif
/*
* If we're at the end of the message, return 0
* (which signifies end of file)
*/
if (*Message_Ptr == 0)
return 0;
/*
* Actually put the data into the buffer
*/
while (length && *Message_Ptr) {
/*
* Because the buffer is in the user data segment,
* not the kernel data segment, assignment wouldn't
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment.
*/
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}
#ifdef DEBUG
printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length);
#endif
/*
* Read functions are supposed to return the number
* of bytes actually inserted into the buffer
*/
return bytes_read;
}
/*
* This function is called when somebody tries to
* write into our device file.
*/
static ssize_t
device_write(struct file *file,
const char __user * buffer, size_t length, loff_t * offset)
{
int i;
#ifdef DEBUG
printk(KERN_INFO "device_write(%p,%s,%d)", file, buffer, length);
#endif
for (i = 0; i < length && i < BUF_LEN; i++)
get_user(Message[i], buffer + i);
Message_Ptr = Message;
/*
* Again, return the number of input characters used
*/
return i;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
long device_ioctl(struct file *file, /* see include/linux/fs.h */
unsigned int ioctl_num, /* number and param for ioctl */
unsigned long ioctl_param)
{
int i;
char *temp;
char ch;
/*
* Switch according to the ioctl called
*/
switch (ioctl_num) {
case IOCTL_SET_MSG:
/*
* Receive a pointer to a message (in user space) and set that
* to be the device's message. Get the parameter given to
* ioctl by the process.
*/
temp = (char *)ioctl_param;
/*
* Find the length of the message
*/
get_user(ch, temp);
for (i = 0; ch && i < BUF_LEN; i++, temp++)
get_user(ch, temp);
device_write(file, (char *)ioctl_param, i, 0);
break;
case IOCTL_GET_MSG:
/*
* Give the current message to the calling process -
* the parameter we got is a pointer, fill it.
*/
i = device_read(file, (char *)ioctl_param, 99, 0);
/*
* Put a zero at the end of the buffer, so it will be
* properly terminated
*/
put_user('\0', (char *)ioctl_param + i);
break;
case IOCTL_GET_NTH_BYTE:
/*
* This ioctl is both input (ioctl_param) and
* output (the return value of this function)
*/
return Message[ioctl_param];
break;
}
return SUCCESS;
}
/* Module Declarations */
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations Fops = {
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* a.k.a. close */
};
/*
* Initialize the module - Register the character device
*/
int init_module()
{
int ret_val;
/*
* Register the character device (atleast try)
*/
ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);
/*
* Negative values signify an error
*/
if (ret_val < 0) {
printk(KERN_ALERT "%s failed with %d\n",
"Sorry, registering the character device ", ret_val);
return ret_val;
}
printk(KERN_INFO "%s The major device number is %d.\n",
"Registeration is a success", MAJOR_NUM);
printk(KERN_INFO "If you want to talk to the device driver,\n");
printk(KERN_INFO "you'll have to create a device file. \n");
printk(KERN_INFO "We suggest you use:\n");
printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
printk(KERN_INFO "The device file name is important, because\n");
printk(KERN_INFO "the ioctl program assumes that's the\n");
printk(KERN_INFO "file you'll use.\n");
return 0;
}
/*
* Cleanup - unregister the appropriate file from /proc
*/
void cleanup_module()
{
unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
#if 0
int ret;
/*
* Unregister the device
*/
ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
/*
* If there's an error, report it
*/
if (ret < 0)
printk(KERN_ALERT "Error: unregister_chrdev: %d\n", ret);
#endif
}
I want to construct a RPC Program to transfer any type of files. But I am getting segmentation fault after running the client:such as
./file_client localhost
>get /home/ab/1.txt
>put /home/1.txt
Segmentation Fault
For your convenience I am pasting my client and server and .x code:
file.x
const MAXLEN = 1024;
/*
* Type for storing path
*/
typedef string filename<MAXLEN>;
/*
* Structure for sending request. Expects the path of the file
* and the byte number at which to start reading the file from
*/
struct request {
filename name;
int start;
};
/*
* Type that represents the structute for request
*/
typedef struct request request;
/*
* Type for storing a chunk of the file that is being
* sent from the server to the client in the current
* remote procedure call
*/
typedef opaque filechunk[MAXLEN];
/*
* Response sent by the server to the client as a response
* to remote procedure call, containing the filechunk for
* the current call and number of bytes actually read
*/
struct chunkreceive {
filechunk data;
int bytes;
};
/*
* Type that represents the structure for file's chunks
* to be received from the server
*/
typedef struct chunkreceive chunkreceive;
/*
* File data sent by the server from client to store
* it on the server along with the filename and the
* number of bytes in the data
*/
struct chunksend {
filename name;
filechunk data;
int bytes;
};
/*
* Type that represents the structure for file's chunks
* to be sent to the server
*/
typedef struct chunksend chunksend;
/*
* union for returning from remote procedure call, returns
* the proper chunkdata response if everything worked fine
* or will return the error number if an error occured
*/
union readfile_res switch (int errno) {
case 0:
chunkreceive chunk;
default:
void;
};
/*
* Remote procedure defined in the Interface Definition Language
* of SUN RPC, contains PROGRAM and VERSION name definitions and
* the remote procedure signature
*/
program FTPPROG {
version FTPVER {
readfile_res retrieve_file(request *) = 1;
int send_file(chunksend *) = 2;
} = 1;
} = 0x20000011;
My server code is:
#include <rpc/rpc.h>
#include <stdio.h>
#include "file.h"
extern __thread int errno;
readfile_res* retrieve_file_1_svc(request *req, struct svc_req *rqstp)
{
FILE *file;
char data[1024];
int bytes;
static readfile_res res;
file = fopen(req->name, "rb");
if (file == NULL) {
res.errno = errno;
return (&res);
}
fseek (file, req->start, SEEK_SET);
bytes = fread(res.readfile_res_u.chunk.data, 1, 1024, file);
//res.readfile_res_u.chunk.data = data;
res.readfile_res_u.chunk.bytes = bytes;
/*
* Return the result
*/
res.errno = 0;
fclose(file);
return (&res);
}
int* send_file_1_svc(chunksend *rec, struct svc_req *rqstp)
{
FILE *file;
int write_bytes;
static int result;
file = fopen(rec->name, "a");
if (file == NULL) {
result = errno;
return &result;
}
write_bytes = fwrite(rec->data, 1, rec->bytes, file);
fclose(file);
result = 0;
return &result;
}
My client code is:
#include <rpc/rpc.h>
#include <stdio.h>
#include <string.h>
#include "file.h"
extern __thread int errno;
int get_file(char *host, char *name)
{
CLIENT *clnt;
int total_bytes = 0, write_bytes;
readfile_res *result;
request req;
FILE *file;
req.name = name;
req.start = 0;
/*
* Create client handle used for calling FTPPROG on
* the server designated on the command line. Use
* the tcp protocol when contacting the server.
*/
clnt = clnt_create(host, FTPPROG, FTPVER, "tcp");
if (clnt == NULL) {
/*
* Couldn't establish connection with server.
* Print error message and stop.
*/
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "wb");
/*
* Call the remote procedure readdir on the server
*/
while (1) {
req.start = total_bytes;
result = retrieve_file_1(&req, clnt);
if (result == NULL) {
/*
* An RPC error occurred while calling the server.
* Print error message and stop.
*/
clnt_perror(clnt, host);
exit(1);
}
/*
* Okay, we successfully called the remote procedure.
*/
if (result->errno != 0) {
/*
* A remote system error occurred.
* Print error message and stop.
*/
errno = result->errno;
perror(name);
exit(1);
}
/*
* Successfully got a chunk of the file.
* Write into our local file.
*/
write_bytes = fwrite(result->readfile_res_u.chunk.data, 1, result->readfile_res_u.chunk.bytes, file);
total_bytes += result->readfile_res_u.chunk.bytes;
if (result->readfile_res_u.chunk.bytes < MAXLEN)
break;
}
fclose(file);
return 0;
}
int put_file(char *host, char *name)
{
CLIENT *clnt;
char data[1024];
int total_bytes = 0, read_bytes;
int *result;
chunksend chunk;
FILE *file;
/*
* Create client handle used for calling FTPPROG on
* the server designated on the command line. Use
* the tcp protocol when contacting the server.
*/
clnt = clnt_create(host, FTPPROG, FTPVER, "tcp");
if (clnt == NULL) {
/*
* Couldn't establish connection with server.
* Print error message and stop.
*/
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "r");
chunk.name = name;
/*
* Call the remote procedure readdir on the server
*/
while (1) {
read_bytes = fread(data, 1, MAXLEN, file);
total_bytes += read_bytes;
read_bytes = fread(chunk.data, 1, MAXLEN, file);
chunk.bytes = read_bytes;
result = send_file_1(&chunk, clnt);
if (result == NULL) {
/*
* An RPC error occurred while calling the server.
* Print error message and stop.
*/
clnt_perror(clnt, host);
exit(1);
}
/*
* Okay, we successfully called the remote procedure.
*/
if (*result != 0) {
/*
* A remote system error occurred.
* Print error message and stop.
*/
errno = *result;
perror(name);
exit(1);
}
/*
* Successfully got a chunk of the file.
* Write into our local file.
*/
if (read_bytes < MAXLEN)
break;
}
fclose(file);
return 0;
}
int read_command(char *host)
{
char command[MAXLEN], filepath[MAXLEN];
printf("> ");
fflush(stdin);
scanf("%s %s", command, filepath);
if (strcmp(command, "get") == 0) {
return get_file(host, filepath);
} else if(strcmp(command, "put") == 0){
return put_file(host, filepath);
} else if(strcmp(command, "exit") == 0){
exit(0);
} else {
return -1;
}
}
int main(int argc, char *argv[])
{
int result;
if (argc != 2) {
fprintf(stderr, "usage: %s host\n", argv[0]);
exit(1);
}
while(TRUE) {
result = read_command(argv[1]);
}
return 0;
}
Can any one advice me what are the modifications I need to follow here.
Thanx in advance.
You have defined your filechunk type to be an array. You can't assign one array to another array in C. Instead copy the data into the array.
That is, the line
chunk.data = data;
should be
memcpy(chunk.data, data, read_bytes);
But in your case you could read directly into that array,
read_bytes = fread(chunk.data, 1, MAXLEN, file);
and just delete the chunk.data = data; code
I want to transfer binary files to remote server. I am using SUN/ONC RPC (rpcgen on Linux) for my code. I am using C. I have written code for server and client and it works for text files, but when I try to transfer binary files it says the file is corrupted after transfer. I am storing data chunks in character array or XDR strings. I think there is some problem with me storing data as a character array. Can some one please tell me what the problem is? Can some one please help me?
I am attaching my code snippetes here for reference if some one wants to have a look at what I am doing.
My IDL:
const MAXLEN = 1024;
/*
* Type for storing path
*/
typedef string filename<MAXLEN>;
/*
* Structure for sending request. Expects the path of the file
* and the byte number at which to start reading the file from
*/
struct request {
filename name;
int start;
};
/*
* Type that represents the structute for request
*/
typedef struct request request;
/*
* Type for storing a chunk of the file that is being
* sent from the server to the client in the current
* remote procedure call
*/
typedef string filechunk<MAXLEN>;
/*
* Response sent by the server to the client as a response
* to remote procedure call, containing the filechunk for
* the current call and number of bytes actually read
*/
struct chunkreceive {
filechunk data;
int bytes;
};
/*
* Type that represents the structure for file's chunks
* to be received from the server
*/
typedef struct chunkreceive chunkreceive;
/*
* File data sent by the server from client to store
* it on the server along with the filename and the
* number of bytes in the data
*/
struct chunksend {
filename name;
filechunk data;
int bytes;
};
/*
* Type that represents the structure for file's chunks
* to be sent to the server
*/
typedef struct chunksend chunksend;
/*
* union for returning from remote procedure call, returns
* the proper chunkdata response if everything worked fine
* or will return the error number if an error occured
*/
union readfile_res switch (int errno) {
case 0:
chunkreceive chunk;
default:
void;
};
/*
* Remote procedure defined in the Interface Definition Language
* of SUN RPC, contains PROGRAM and VERSION name definitions and
* the remote procedure signature
*/
program FTPPROG {
version FTPVER {
readfile_res retrieve_file(request *) = 1;
int send_file(chunksend *) = 2;
} = 1;
} = 0x20000011;
My Server:
#include <rpc/rpc.h>
#include <stdio.h>
#include "ftp.h"
extern __thread int errno;
readfile_res* retrieve_file_1_svc(request *req, struct svc_req *rqstp)
{
FILE *file;
char data[1024];
int bytes;
static readfile_res res;
file = fopen(req->name, "rb");
if (file == NULL) {
res.errno = errno;
return (&res);
}
fseek (file, req->start, SEEK_SET);
bytes = fread(data, 1, 1024, file);
res.readfile_res_u.chunk.data = data;
res.readfile_res_u.chunk.bytes = bytes;
/*
* Return the result
*/
res.errno = 0;
fclose(file);
return (&res);
}
int* send_file_1_svc(chunksend *rec, struct svc_req *rqstp)
{
FILE *file;
int write_bytes;
static int result;
file = fopen(rec->name, "a");
if (file == NULL) {
result = errno;
return &result;
}
write_bytes = fwrite(rec->data, 1, rec->bytes, file);
fclose(file);
result = 0;
return &result;
}
My Client:
#include <rpc/rpc.h>
#include <stdio.h>
#include <string.h>
#include "ftp.h"
extern __thread int errno;
int get_file(char *host, char *name)
{
CLIENT *clnt;
int total_bytes = 0, write_bytes;
readfile_res *result;
request req;
FILE *file;
req.name = name;
req.start = 0;
/*
* Create client handle used for calling FTPPROG on
* the server designated on the command line. Use
* the tcp protocol when contacting the server.
*/
clnt = clnt_create(host, FTPPROG, FTPVER, "tcp");
if (clnt == NULL) {
/*
* Couldn't establish connection with server.
* Print error message and stop.
*/
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "wb");
/*
* Call the remote procedure readdir on the server
*/
while (1) {
req.start = total_bytes;
result = retrieve_file_1(&req, clnt);
if (result == NULL) {
/*
* An RPC error occurred while calling the server.
* Print error message and stop.
*/
clnt_perror(clnt, host);
exit(1);
}
/*
* Okay, we successfully called the remote procedure.
*/
if (result->errno != 0) {
/*
* A remote system error occurred.
* Print error message and stop.
*/
errno = result->errno;
perror(name);
exit(1);
}
/*
* Successfully got a chunk of the file.
* Write into our local file.
*/
write_bytes = fwrite(result->readfile_res_u.chunk.data, 1, result->readfile_res_u.chunk.bytes, file);
total_bytes += result->readfile_res_u.chunk.bytes;
if (result->readfile_res_u.chunk.bytes < MAXLEN)
break;
}
fclose(file);
return 0;
}
int put_file(char *host, char *name)
{
CLIENT *clnt;
char data[1024];
int total_bytes = 0, read_bytes;
int *result;
chunksend chunk;
FILE *file;
/*
* Create client handle used for calling FTPPROG on
* the server designated on the command line. Use
* the tcp protocol when contacting the server.
*/
clnt = clnt_create(host, FTPPROG, FTPVER, "tcp");
if (clnt == NULL) {
/*
* Couldn't establish connection with server.
* Print error message and stop.
*/
clnt_pcreateerror(host);
exit(1);
}
file = fopen(name, "r");
chunk.name = name;
/*
* Call the remote procedure readdir on the server
*/
while (1) {
read_bytes = fread(data, 1, MAXLEN, file);
total_bytes += read_bytes;
chunk.data = data;
chunk.bytes = read_bytes;
result = send_file_1(&chunk, clnt);
if (result == NULL) {
/*
* An RPC error occurred while calling the server.
* Print error message and stop.
*/
clnt_perror(clnt, host);
exit(1);
}
/*
* Okay, we successfully called the remote procedure.
*/
if (*result != 0) {
/*
* A remote system error occurred.
* Print error message and stop.
*/
errno = *result;
perror(name);
exit(1);
}
/*
* Successfully got a chunk of the file.
* Write into our local file.
*/
if (read_bytes < MAXLEN)
break;
}
fclose(file);
return 0;
}
int read_command(char *host)
{
char command[MAXLEN], filepath[MAXLEN];
printf("> ");
fflush(stdin);
scanf("%s %s", command, filepath);
if (strcmp(command, "get") == 0) {
return get_file(host, filepath);
} else if(strcmp(command, "put") == 0){
return put_file(host, filepath);
} else if(strcmp(command, "exit") == 0){
exit(0);
} else {
return -1;
}
}
int main(int argc, char *argv[])
{
int result;
if (argc != 2) {
fprintf(stderr, "usage: %s host\n", argv[0]);
exit(1);
}
while(TRUE) {
result = read_command(argv[1]);
}
return 0;
}
XDR strings are null terminated. You need to use a different data type to transfer binary data - probably 'byte array'. See, for instance, this document at Sun.
a little bit late I gess but here's a solution to your issue :
Juste change the type for storing a chunk of the file to a fixed length array of
arbitrary bytes.
So in your IDL, instead of the declaration "typedef string filechunk<MAXLEN>;" your could use Opaque data : "typedef opaque filechunk[MAXLEN];" (matter of fact, it's just a fixed array of char)
P.S.: That kind of data (fixed arrays) prevents you to use a variable as a buffer to read or write from a file. For instance, in the function *retrieve_file_1_svc* from your server, the statements
*bytes = fread(data, 1, 1024, file);
res.readfile_res_u.chunk.data = data;*
have to be changed to
*bytes = fread(res.readfile_res_u.chunk.data, 1, 1024, file);*
Just for future reference, Madhusudan.C.S own "solution" using integers will give you all kinds of fun when using machines with different endianness. RPC should translate integers in that case, mucking up your string or binary data.
The correct solution is using the 'opaque' XDR data type. It will create a struct with a _len unsigned int for the amount of bytes, and a _var pointer which you can point to your data.
Compare the files before and after transfer, that will tell you where the problem is.
You can use hexdiff for that.