i have a problem with file read() function. My file is like this:
4boat
5tiger
3end
Where the number is the length of the string that follows. I need to read integer and string from input file and print them out on stdoutput, using low level I/O. This is my code:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
int main(int argc, char *argv[]){
int *len, fd, r_l, r_s;
char *s;
fd=open(argv[1], O_RDONLY);
if(fd>=0){
do{
r_l=read(fd, len, sizeof(int));
r_s=read(fd, s, (*len)*sizeof(char));
if(r_l>=0){
write(1, len, sizeof(int));
write(1, " ",sizeof(char));
}
if(r_s>=0)
write(1, s, (*len)*sizeof(char));
}while(r_l>=0 && r_s>=0);
}
return 0;
}
But it not works =/
You did not allocate space for the poitner len, you need to allocate space for it and you can simply do it by declaring it as int len; so it gets allocated in the stack and you don't need to handle it's allocation manually, so it would be something like this
int main(void) {
int len, fd, r_l, r_s;
char *s;
fd = open(argv[1], O_RDONLY);
if (fd >= 0) {
do {
r_l = read(fd, &len, sizeof(int));
s = malloc(len); /* <--- allocate space for `s' */
r_s = 0;
if (s != NULL)
r_s = read(fd, s, len);
if (r_l >= 0) {
write(1, &len, sizeof(int));
write(1, " ", 1);
}
if ((r_s >= 0) && (s != NULL))
write(1, s, len);
free(s);
} while (r_l >= 0 && r_s >= 0);
close(fd);
}
return 0;
}
you also didn't allocate space for s which is another problem, I did allocate space for s in the corrected code above by using malloc().
And sizeof(char) == 1 by definition, so you don't need that.
Although, the code above will not have the errors your code has, which invoke undefined behavior, it will not do what you expect, because your data cannot be read with this algorithm.
The numbers in your file are not really integers, they are characters, so what you really need is this
int main(void) {
char chr;
int len, fd, r_l, r_s;
char *s;
fd = open(argv[1], O_RDONLY);
if (fd >= 0) {
do {
r_l = read(fd, &chr, 1);
len = chr - '0';
s = malloc(len); /* <--- allocate space for `s' */
r_s = 0;
if (s != NULL)
r_s = read(fd, s, len);
if (r_l >= 0) {
printf("%d ", len);
}
if ((r_s >= 0) && (s != NULL))
write(1, s, len);
free(s);
} while (r_l >= 0 && r_s >= 0);
close(fd);
}
return 0;
}
Related
I managed to compile ncat. I am using -k option to keep server open. Instead of accepting data to STDOUT, my goal is to write to files instead. So far I was able to write to a file instead of STDOUT but my goal is to loop through new files on each new connection. Right now it is appending to the same filename_0 and f++ is not incrementing. Here is what I have so far. The original code will be below. The difference is in the else clause, basically if n is actually greater than 0. On each loop, n is 512 bytes until the last chunk. I just want to be able to have new files from each new connection. filename_0, filename_1, filename_3, etc.
MODIFIED CODE:
/* Read from a client socket and write to stdout. Return the number of bytes
read from the socket, or -1 on error. */
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
int f = 0;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n, s;
n = ncat_recv(fdn, buf, 512, &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
char filename[20];
snprintf(filename, sizeof(char) * 20, "filename_%i", f);
FILE *fp = fopen(filename, "a");
if (fp == NULL)
{
printf("Could not open file");
return 0;
}
//Write(STDOUT_FILENO, buf, n);
s = fwrite(buf, 1, n, fp);
fclose(fp);
f++;
nbytes += n;
}
} while (pending);
return nbytes;
}
ORIGINAL CODE:
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n;
n = ncat_recv(fdn, buf, sizeof(buf), &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
Write(STDOUT_FILENO, buf, n);
nbytes += n;
}
} while (pending);
return nbytes;
}
I was able to figure out using the other functions involved. i passed a pointer into this function to write to it. the handler is a function i added the open() file pointer to.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
char *get_next_line(int fd);
int main (void)
{
int i = 0;
char *s;
int fd;
fd = open("./text", O_RDONLY);
s = get_next_line (fd);
}
char *get_next_line(int fd)
{
char *buf;
char c;
int nread;
int cnt;
if (fd < 0 || BUFFER_SIZE < 1)
return (NULL);
buf = (char*)malloc(BUFFER_SIZE + 1);
if (!buf)
return (NULL);
while(nread = (read(fd, &c, 1)) > 0)
{
*buf = c;
buf++;
cnt++;
if (c == '\n')
break;
}
if (nread < 0)
return (NULL);
*buf = '\n';
printf("%s\n", buf);
return (buf - cnt - 1);
}
When I compile with no flags, I just get two empty line. Compiling with -fsanitize=address and I know heap-buffer-overflow happens at the line printf("%s\n", buf);
But I don't know why this happen. I tried STDIN to fix it but didn't work. Can someone check this please?
You are not terminating the buf with null character.
*buf = '\n';
*buf = '\0';
Make sure you reserve the space for null character while allocating memory to buf.
Free the memory if number of bytes read are less than 0.
if (nread < 0) {
return (NULL);
}
to
if (nread < 0) {
free(startAddress);
return (NULL);
}
You can have temporary pointer to preserve the starting address of buf instead of calculating the starting address.
char *get_next_line(int fd)
{
char *buf;
char c;
int nread;
if (fd < 0 || BUFFER_SIZE < 1)
return (NULL);
buf = (char*)malloc(BUFFER_SIZE + 2);
if (!buf)
return (NULL);
char *startAddress = buf;
while(nread = (read(fd, &c, 1)) > 0)
{
*buf = c;
buf++;
if (c == '\n')
break;
}
if (nread < 0) {
free(startAddress);
return (NULL);
}
*buf = '\0';
printf("%s\n", buf);
return startAddress;
}
I'm currently writing a program in C that reads in from a CSV file, I have a defined buffer size but am having trouble separating each line from the buffer. I can see where the line ends by checking for a '\n' char. I cannot extract that line from the buffer for parsing however. Anybody have some ideas?
#ifndef BUFFSIZE
#define BUFFSIZE 4096
#endif
int main() {
int fd;
int fdBin;
char * buf = malloc(BUFFSIZE);
int count = 0;
bool EOFFlag = false;
fd = open("SongCSV.csv", O_RDONLY);
fdBin = open("BinarySongData.bin", O_CREAT | O_WRONLY, "0600");
if (fd == -1) {
printf("failed to open a file\n");
exit(1);
}
off_t offset = 0;
off_t offsetOld = 0;
int readBytes;
while (!EOFFlag) {
offsetOld = offset;
offset = lseek(fd, offset - offsetOld, SEEK_CUR);
readBytes = read(fd, buf, BUFFSIZE);
printf("\n\n%lld\n\n", (offset));
int i = 0;
int commaCounter = 0;
while (i < readBytes) {
if (buf[i] != '\n') {
}
if (buf[i] == '\n') {
printf("\t\t THIS IS END OF LINE \t%d", i);
commaCounter = 0;
}
if (buf[i] == ',') {
commaCounter++;
if (commaCounter == 4) {
printf("****Album Name****");
}
}
write(fdBin, buf, BUFFSIZE);
printf("%c", buf[i]);
i++;
}
if (readBytes < BUFFSIZE) {
EOFFlag = true;
printf("\nREACHED END OF FILE");
}
printf("\n");
printf("AA: END OF LINE ****%d*****", count);
count++;
}
close(fd);
close(fdBin);
return 0;
}
I do it this way, easy and simple. I just did it quickly, any doubts just ask me, Cheers.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int len = sending();
char *firstline;
int i = 0;
char buf[0];
int rd ;
int fd = open("hey.csv", O_RDONLY);
rd = read(fd, buf, 1);
firstline = malloc(sizeof(char) * len);
while (i != len)
{
firstline[i] = buf[0];
i++;
rd = read(fd, buf, 1);
}
firstline[i] = '\0';
printf("%s\n", firstline);
return (0);
}
int sending()
{
int fd = open("hey.csv", O_RDONLY);
char buf[1];
int r = 0;
r = read(fd, buf, 1);
int len = 0;
while (buf[0] != '\n')//getting exact size to malloc
{
len++;
r = read(fd, buf, 1);
}
return len;
}
I want to write a function that read line by line from a socket buffer obtained from third parameter from read() function from unistd.h header.
I have wrote this:
int sgetline(int fd, char ** out)
{
int buf_size = 128;
int bytesloaded = 0;
char buf[2];
char * buffer = malloc(buf_size);
char * newbuf;
int size = 0;
assert(NULL != buffer);
while( read(fd, buf, 1) > 0 )
{
strcat(buffer, buf);
buf[1] = '\0';
bytesloaded += strlen(buf);
size = size + buf_size;
if(buf[0] == '\n')
{
*out = buffer;
return bytesloaded;
}
if(bytesloaded >= size)
{
size = size + buf_size;
newbuf = realloc(buffer, size);
if(NULL != newbuf)
{
buffer = newbuf;
}
else
{
printf("sgetline() allocation failed!\n");
exit(1);
}
}
}
*out = buffer;
return bytesloaded;
}
but I have some problems with this function, for example, if the input is something like:
HTTP/1.1 301 Moved Permanently\r\n
Cache-Control:no-cache\r\n
Content-Length:0\r\n
Location\r\nhttp://bing.com/\r\n
\r\n\r\n
and I do
int sockfd = socket( ... );
//....
char* tbuf;
while(sgetline(sockfd, &tbuf) > 0)
{
if(strcmp(tbuf,"\r\n\r\n") == 0)
{
printf("End of Headers detected.\n");
}
}
the above C application does not output "End of Header detected.". Why is this, and how can I fix this?
It's not OK to read one byte at a time, because you are making too many system calls - better is to use a buffer, read a chunk and check if you got \n. After getting a line, the rest of the bytes read remains in the buffer, so you cannot mix read/recv with read_line. Another version of read n bytes using this kind of buffer can be write...
My version to read a line, and a little example to use it.
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#define CBSIZE 2048
typedef struct cbuf {
char buf[CBSIZE];
int fd;
unsigned int rpos, wpos;
} cbuf_t;
int read_line(cbuf_t *cbuf, char *dst, unsigned int size)
{
unsigned int i = 0;
ssize_t n;
while (i < size) {
if (cbuf->rpos == cbuf->wpos) {
size_t wpos = cbuf->wpos % CBSIZE;
//if ((n = read(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos))) < 0) {
if((n = recv(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos), 0)) < 0) {
if (errno == EINTR)
continue;
return -1;
} else if (n == 0)
return 0;
cbuf->wpos += n;
}
dst[i++] = cbuf->buf[cbuf->rpos++ % CBSIZE];
if (dst[i - 1] == '\n')
break;
}
if(i == size) {
fprintf(stderr, "line too large: %d %d\n", i, size);
return -1;
}
dst[i] = 0;
return i;
}
int main()
{
cbuf_t *cbuf;
char buf[512];
struct sockaddr_in saddr;
struct hostent *h;
char *ip;
char host[] = "www.google.com";
if(!(h = gethostbyname(host))) {
perror("gethostbyname");
return NULL;
}
ip = inet_ntoa(*(struct in_addr*)h->h_addr);
cbuf = calloc(1, sizeof(*cbuf));
fprintf(stdout, "Connecting to ip: %s\n", ip);
if((cbuf->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
inet_aton(ip, &saddr.sin_addr);
if(connect(cbuf->fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
perror("connect");
return 1;
}
snprintf(buf, sizeof(buf), "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", host);
write(cbuf->fd, buf, strlen(buf));
while(read_line(cbuf, buf, sizeof(buf)) > 0) {
// if it's an empty \r\n on a line, header ends //
if(buf[0]=='\r' && buf[1] == '\n') {
printf("------------------------\n");
}
printf("[%s]", buf);
}
close(cbuf->fd);
free(cbuf);
return 0;
}
Try this implementation instead:
int sgetline(int fd, char ** out)
{
int buf_size = 0;
int in_buf = 0;
int ret;
char ch;
char * buffer = NULL;
char * new_buffer;
do
{
// read a single byte
ret = read(fd, &ch, 1);
if (ret < 1)
{
// error or disconnect
free(buffer);
return -1;
}
// has end of line been reached?
if (ch == '\n')
break; // yes
// is more memory needed?
if ((buf_size == 0) || (in_buf == buf_size))
{
buf_size += 128;
new_buffer = realloc(buffer, buf_size);
if (!new_buffer)
{
free(buffer);
return -1;
}
buffer = new_buffer;
}
buffer[in_buf] = ch;
++in_buf;
}
while (true);
// if the line was terminated by "\r\n", ignore the
// "\r". the "\n" is not in the buffer
if ((in_buf > 0) && (buffer[in_buf-1] == '\r'))
--in_buf;
// is more memory needed?
if ((buf_size == 0) || (in_buf == buf_size))
{
++buf_size;
new_buffer = realloc(buffer, buf_size);
if (!new_buffer)
{
free(buffer);
return -1;
}
buffer = new_buffer;
}
// add a null terminator
buffer[in_buf] = '\0';
*out = buffer; // complete line
return in_buf; // number of chars in the line, not counting the line break and null terminator
}
int sockfd = socket( ... );
//....
char* tbuf;
int ret;
// keep reading until end of headers is detected.
// headers are terminated by a 0-length line
do
{
// read a single line
ret = sgetline(sockfd, &tbuf);
if (ret < 0)
break; // error/disconnect
// is it a 0-length line?
if (ret == 0)
{
printf("End of Headers detected.\n");
free(tbuf);
break;
}
// tbuf contains a header line, use as needed...
free(tbuf);
}
while (true);
You are making things more difficult for yourself than they need to be. You really don't need to do strcats to get the single character you read on each read added at the current position.
But your bug is that the routine returns as soon as it sees a \n, so the string it returns can never contain anything following the first \n.
So I programmed a multi threaded web server, here is one function from the program. This function takes output file descriptor (fd), content type, pointer to data to be served (*buf) and size of the data (numbytes). It always gets stuck at 5775 bytes! I've tried using write() instead of send(), but no avail! I tried to send whole buf at a time, and even tried to transfer it in chunks, but wget shows that it gets stck at 5775 bytes! Here is the code:
int return_result(int fd, char *content_type, char *buf, int numbytes)
{
char out_buf[BUF_SIZE], numb[6];
int buf_len, total = 0, buf_size;
long int i = 0;
sprintf(numb, "%d", numbytes);
strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
strcat(out_buf, content_type);
strcat(out_buf, "\nContent-Length: ");
strcat(out_buf, numb);
strcat(out_buf, "\nConnection: Close\n \n");
printf("\nSending HTTP Header\n %d bytes sent!",
send(fd, out_buf, strlen(out_buf), 0));
char *start = NULL, *str = NULL, *temp = NULL;
start = buf;
printf("\n Start Pointer Val = %ld", &start);
while (start != NULL) {
printf("\n While Loop");
if (i + 2048 * sizeof(char) < numbytes) {
printf("\n If 1");
str = (char *)malloc(sizeof(char) * 2048);
memcpy(str, start, sizeof(char) * 2048);
i = i + 2048 * sizeof(char);
buf_size = send(fd, str, 2048, 0);
free(str);
printf("\n Sent %d bytes total : %d", buf_size, total =
total + buf_size);
temp = start + sizeof(char) * 2048;
start = temp;
} else {
i = numbytes - i * sizeof(char);
if (i > 0) {
printf("\n If 2");
printf("\n Value of i %d", i);
str = (char *)malloc(sizeof(char) * i);
memcpy(str, start, sizeof(char) * i);
printf("Total bytes finally sent:%d", total =
total + send(fd, str, i, 0));
if (total == numbytes) {
printf("\nTransfer Complete!");
}
free(str);
}
start = NULL;
}
}
printf("out of loop!");
return 0;
}
I'd like to suggest replacing your code with the following writen() function from Advanced Programming in the Unix Environment, 2nd edition:
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
This code is already debugged and known working, and further allows write(2) to write PIPE_BUF bytes at a go for better speed when things are working well.
send(2) should block if it cannot send all the data you have requested, though. I think more interesting would be debugging the version with plain send(2) without any of the surrounding efforts to break things into blocks.
Better than both write(2) and send(2) would be sendfile(2) -- open the file, pass the descriptor and socket to sendfile(2), and let the kernel handle it all for you, using zero-copy mechanisms if possible.
One last point: HTTP uses CRLF, not plain carriage returns. Each \n should be replaced with \r\n.
Try something like this (printf() statements omitted for clarity):
int send_buf(in fd, void *buf, int numbytes)
{
char *start = (char*) buf;
while (numbytes > 0)
{
int sent = send(fd, start, numbytes, 0);
if (sent <= 0)
{
if ((sent == -1) && (errno == EAGAIN))
{
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
continue;
}
return -1;
}
start += sent;
numbytes -= sent;
}
return 0;
}
int return_result(int fd, char *content_type, void *buf, int numbytes)
{
char out_buf[BUF_SIZE],
int len = sprintf(out_buf,
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Close\r\n"
"\r\n",
content_type,
numb);
if (send_buf(fd, out_buf, len) != 0)
return -1;
if (send_buf(fd, buf, numbytes) != 0)
return -1;
return 0;
}