Why is this code giving bad file descriptor - c

I am having issue with the following code. It is giving me bad file descriptor error.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
int main()
{
int fd = open("/tmp/test", O_RDONLY);
long len = 20;
long word;
ssize_t ret;
if(fd == -1)
{
perror("open");
return 1;
}
if(len > SSIZE_MAX)
{
len = SSIZE_MAX;
}
ret = read(fd, &word, len);
while(len != 0 && ret != 0)
{
if(ret == -1)
{
if(errno == EINTR)
{
continue;
}
perror("read");
break;
}
len -= ret;
word += ret;
ret = read(fd, &word, len);
}
close(fd);
}
Upon debugging I have found that after first iteration the value of fd is changed to a large number. I do not know how this is happening.

In your code
ret = read(fd, &word, len);
causes undefined behavior,as you're trying to read 20 bytes into a memory designated for sizeof(long) which is much less than what is required.
One correct way of writing this would be
ret = read(fd, &word, sizeof(word));

Related

Linux C use mkfifo file descriptor with select

I'm planing to monitor the fifo and stdin with select function, but it always block the code flow even though O_NONBLOCK is configured, would anyone help check about it please ?
I'm not sure it's the code issue or solution is not a right direction to try, thanks in advance.
There are 2 problems :
stdin is not able to read when the program start.
FD_ISSET(pipe_fd, &fds) will continuously be true, if do not close pipe_fd manually.
if (FD_ISSET(pipe_fd, &fds)) {
read_pipe(pipe_fd);
close_pipe(pipe_fd); // continously triggered if not close here
}
Here is the full code.
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUF_SZ 128
#define FIFO_BUF_SZ 128
#define PIPE_PATH "./test_fifo"
int create_pipe_fd()
{
char *f_path = PIPE_PATH;
int ret;
// creating the named file(FIFO)
// mkfifo(<pathname>, <permission>)
//ret = mkfifo(f_path, 0666);
ret = mkfifo(f_path, 0666);
if (ret < 0) {
printf("create fifo file failed, err = %s\n", strerror(errno));
}
}
int open_pipe()
{
char *f_path = PIPE_PATH;
int fd;
// open fifo for read only
fd = open(f_path, O_RDONLY);
// non blocking mode
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
printf("mode set failed, err = %s\n", strerror(errno));
}
return fd;
}
int read_pipe(int fd)
{
uint8_t buf[FIFO_BUF_SZ];
ssize_t cnt;
size_t end;
size_t sz = sizeof(buf);
cnt = read(fd, buf, sz);
end = (cnt > (sz - 1)) ? (sz - 1) : cnt;
buf[end] = '\0';
printf("read pipe = %s\n", buf);
}
void close_pipe(int fd)
{
close(fd);
//printf("pipe closed.\n");
}
uint16_t read_stdin()
{
char *line = NULL;
size_t len = 0;
ssize_t lineSize = 0;
uint8_t stdin_buf[BUF_SZ];
lineSize = getline(&line, &len, stdin);
printf("[stdin %zu bytes] : %s", lineSize, line);
if (0 == strncmp("stop", line, strlen("stop"))) {
return 0;
}
free(line);
return (int)lineSize;
}
int main()
{
fd_set fds;
int max_fd = 0;
int pipe_fd = -1;
uint16_t bytes;
printf("start.\n");
// get pipe file descriptor
//pipe_fd = create_pipe_fd();
create_pipe_fd();
pipe_fd = open_pipe();
//max_fd = pipe_fd > max_fd ? pipe_fd : max_fd;
while (1) {
FD_ZERO(&fds);
// stdin
FD_SET(0, &fds);
//pipe_fd = open_pipe();
if (pipe_fd > 0) {
FD_SET(pipe_fd, &fds);
}
max_fd = pipe_fd > max_fd ? pipe_fd : max_fd;
select(max_fd + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(0, &fds)) {
bytes = read_stdin();
if (0 == bytes) {
break;
}
}
if (FD_ISSET(pipe_fd, &fds)) {
read_pipe(pipe_fd);
close_pipe(pipe_fd);
}
}
_EXIT:
if (pipe_fd) {
close_pipe(pipe_fd);
}
printf("exit.\n");
}

Using of a system call in c

I would like to create a program name patch witch recives a FILENAME and a string.In the file there is the string 'allis'.I need to replace each time the string 'allis' appears in the text with the string I recieved as an input.
I need to use only system call and not the standat libary.
This is an example code on how the code should look like(this just example of the structure it is not the mission, the code writes "allis" word instead of "hello world"):
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc , char* argv[], char* envp[])
{
char buf[6];
int fd = open(argv[1],O_WRONLY,0777);
if (fd == -1)
exit(1);
write(fd,"hello world!\n",13);
close(fd);
fd = open(argv[1], O_RDONLY);
if (fd == -1)
exit(1);
read(fd,buf,5);
buf[5]='\0';
close(fd);
fd = open(argv[1],O_WRONLY,0777);
if (fd == -1)
exit(1);
write(fd,"allis",5);
close(fd);
return 0;
}
I need to use the system call lseek but I dont know how I should use it because I dont know where in the text 'allis' is located and what happens if the string i get as an input is bigger than 'allis' I need to move all the text to the right or something because I will override other text.
EDIT:
I wrote a code which check char by char (instead of allis its shira and it replace it with sergay)It actually worked. and than I tried to change the code to work with argv[1] and argv[2] as needed and it didnt work I revert back to the code that originally worked for me and its just doesnt work now!! The problem is that the buf doesnt take 5 character (only 1)
and believe me or not it worked before.
The code :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SYS_WRITE 1
#define STDOUT 1
extern int system_call();
int main(int argc , char* argv[], char* envp[])
{
char buf[5];
int index1=0,index2=0;//the index of the file
int fd1;
int fd = open("file.txt", O_RDONLY);
if (fd == -1)
return 1;
fd1 = open("copytext.txt",O_WRONLY,0777);
if (fd1 == -1)
return 1;
int i =0;
while (fd!=-1){//while there is still info to read
printf("enter the %d loop\n",i);
i++;
if(read(fd,buf,5) == 0 ) break;//read 5 chars from the file //buf[5]='\0';//!! not sure about this
printf("the buffer is: %s\n",buf);
if ((buf[0]=='s') && (buf[1]=='h') && (buf[2]=='i') && (buf[3]=='r') && (buf[4]=='a'))// if (strcmp(buf,"shira")==0)//if we found shira
{
write(fd1,"sergay",6);//replace shira
index2 = index2 + 6;
if(lseek(fd1,index2,SEEK_SET) < 0) break;
index1 = index1 + 5;//size of shira
if(lseek(fd,index1,SEEK_SET) < 0) break;
}
else {//we did not find shira;
write(fd1,buf,1);//write only one
index2++;
if(lseek(fd1,index2,SEEK_SET) < 0) break;
index1++;
if(lseek(fd,index1,SEEK_SET) < 0) break;
}
}
printf("get out of the loop\n");
char buffer[index2];
fd1 = open("copytext.txt", O_RDONLY);
if (fd1 == -1)
return 1;
if(lseek(fd1,0,SEEK_SET) < 0) return 1;
if(read(fd1,buffer,index2) == 0 ) return 1;
fd = open("file.txt",O_WRONLY,0777);
if (fd == -1)
return 1;
if(lseek(fd,0,SEEK_SET) < 0) return 1;
write(fd,buffer,index2);
close(fd);
close(fd1);
return 0;
}

How to get a line from csv file with a custom fgets

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;
}

portable way to monitor a file for changes

I'm actually implementing a very simple version of tail(1). In FreeBSD, I use kqueue to monitor a file for changes and then printing appended lines to the output. But this is not a portable way, as kqueue is only available in BSD family. Is there a general, efficient and platform-independent way to monitor files for changes in UNIX? I prefer not to use external libraries.
This is the code I've written:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
void die(const char*);
#define MAXLINE 1024
int main(int argc, char *argv[])
{
int fdes;
int kq;
int nev;
int flags;
off_t curoff;
char line[MAXLINE + 1];
ssize_t nbytes;
struct kevent change, event;
if (2 != argc)
{
fprintf(stderr, "Usage: %s path\n", argv[0]);
exit(EXIT_FAILURE);
}
if (-1 == (fdes = open(argv[1], O_RDONLY)))
die("open()");
if (-1 == (curoff = lseek(fdes, 0, SEEK_END)))
die("lseek()");
int ch = 0;
int i = 0;
while (i < 10)
{
read(fdes, &ch, 1);
if (ch == '\n')
i++;
if (10 > i)
lseek(fdes, --curoff, SEEK_SET);
}
if (-1 == (flags = fcntl(fdes, F_GETFL)))
die("fcntl()");
flags |= O_NONBLOCK;
if (-1 == fcntl(fdes, F_SETFL, flags))
die("fcntl()1");
while ((nbytes = read(fdes, line, MAXLINE)) > 0)
if (write(STDOUT_FILENO, line, nbytes) != nbytes)
die("write()");
if (-1 == (kq = kqueue()))
die("kqueue()");
EV_SET(&change, fdes, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_EXTEND | NOTE_WRITE | NOTE_DELETE, 0, NULL);
if (-1 == kevent(kq, &change, 1, NULL, 0, NULL))
die("kevent()");
for (;;)
{
if (-1 == (nev = kevent(kq, NULL, 0, &event, 1, NULL)))
die("kevent()");
if (nev > 0)
{
if (event.fflags & NOTE_WRITE || event.fflags & NOTE_EXTEND)
{
while ((nbytes = read(fdes, line, MAXLINE)) > 0)
if (write(STDOUT_FILENO, line, nbytes) != nbytes)
die("write()");
}
else if (NOTE_DELETE & event.fflags)
{
printf("The file has been deleted\n");
break;
}
}
}
return 0;
}
void die(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
You can just keep doing the read() in a loop. If you read zero bytes,
check for an error. If there is no error, then you've hit EOF. At
EOF, stat() the filename, if it's gone, then the file was deleted. If
stat returns, compare the st_dev and st_ino fields of the stat to
the results from fstat (cache this when you open the file). If they're
different, the path was deleted and re-created. Sleep as long as you
care to after the delete check before trying another read.

APUE: Createing a file with a hole in it: Figure 3.2 pg 65

In the example from, "Advance Programming in the Unix Environment" the following sample program creates a file, then uses lseek to move the file pointer to a further address thus placing a "hole" in the file. The author says the space in between is filled with "0's". I wanted to see if those "0's" would print out. So I modified the program slightly. However I noticed that only the valid characters were writen to the file.
My question is how does the Unix/Linux filesystem manager know not to print the bytes in between?
#include "apue.h"
#include <fcntl.h>
#include <unistd.h>
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
char buf3[10];
int
main(void)
{
int fd;
if ((fd = creat("file.hole", FILE_MODE)) < 0) {
err_sys("creat error");
}
if (write(fd, buf1, 10) != 10) { /* offset is now = 10 */
err_sys("buf1 write error");
}
if (lseek(fd, 16380, SEEK_SET) == -1) { /* offset now = 16380 */
err_sys("lseek error");
}
if (write(fd, buf2, 10) != 10) { /* offset now = 16390 */
err_sys("buf2 write error");
}
close(fd);
if ((fd = open("file.hole", O_RDWR)) == -1) {
err_sys("failed to re-open file");
}
ssize_t n;
ssize_t m;
while ((n = read(fd, buf3, 10)) > 0) {
if ((m = write(STDOUT_FILENO, buf3, 10)) != 10) {
err_sys("stdout write error");
}
}
if (n == -1) {
err_sys("buf3 read error");
}
exit(0);
}
The character \000 has a null-width display representation. It is printed, but its printing is invisible. Not every codepoint is a character. In the same way, \n is printed as a newline, not as a character.

Resources