Introduction to the fcntl() function

The fcntl() function can be used to change the properties of a file that is already open. The fcntl() funciton is extremely versatile, and we will be looking at it more than once. The fcntl() function can be used for five different purposes, each purpose defined by an integer constant set as its second argument. The five purposes are one, duplicating an existing descriptor; two, getting or setting file descriptor flags; three, getting or setting file status flags; four, getting or setting asynchronous I/O ownership; and, five, getting or setting record locks.

#include <stdio.h>
#include <fcntl.h>

int main(void){
    
    printf("duplicate an existing file descriptor (F_DUPFD) %d\n", F_DUPFD);
    printf("get file descriptor flags (F_GETFD) %d\n", F_GETFD);
    printf("set file descriptor flags (F_SETFD) %d\n", F_SETFD);
    printf("get file status flags (F_GETFL) %d\n", F_GETFL);
    printf("set file status flags (F_SETFL) %d\n", F_SETFL);
    printf("get record locks (F_GETLK) %d\n", F_GETLK);
    printf("set record locks (F_SETLK) %d\n", F_SETLK);
    printf("get asynchronous I/O ownership (F_GETOWN) %d\n", F_GETOWN);
    printf("set asynchronous I/O ownership (F_SETOWN) %d\n", F_SETOWN);
    
    return 0;
    
}

Note that the fcntl() function is defined in the <fcntl.h> header file.

The F_DUPFD command value duplicates the file descriptor that is the first argument to the fcntl() function. The new file descriptor is the return value of the function; as with open(), it is the lowest numbered descriptor that is not open. However, we can also specify a third numeric argument to fcntl() that defines the floor value for assigning to a file descriptor, so that the file descriptor value will be no less than the number specified.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main(void){
    
    int fdOne, fdTwo, fdThree;
    
    fdOne = open("misc.txt", O_CREAT | O_TRUNC, S_IRWXU);
    
    if(fdOne < 0){
        printf("Error opening / creating test.txt. ");
        if(errno==EACCES){
            printf("Error #%d (EACCES): Permission denied.\n", EACCES);
            exit(EXIT_FAILURE);
        }
    } else {
        printf("test.txt created / opened ");
        printf("with file descriptor %d.\n", fdOne);
    }
    
    //lets use the fcntl() function to copy the 
    //file descriptor
   if((fdTwo = fcntl(fdOne, F_DUPFD, 0))<0){
       printf("Error duplicating file descriptor.\n");
       exit(EXIT_FAILURE);
   } else {
       printf("File descriptor duplicated. ");
       printf("New file descriptor is %d.\n", fdTwo);
   }
    
   //set the file descriptor to be a higher number;
    
    if((fdThree = fcntl(fdOne, F_DUPFD, 11))<0){
        printf("Error duplicating file descriptor.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File descriptor duplicated. ");
        printf("New file descriptor is %d.\n", fdThree);
    }
    
    close(fdOne);
    close(fdTwo);
    close(fdThree);
    
    return 0;
    
}

Note that close() is defined in the <unistd.h> header file.

The F_GETFL command specifier returns the file status flags as the value of the function. These are the same file status flags used with the open() function. The O_RDONLY flag sets the file to open for reading only. The O_WRONLY flag sets the file to be open for writing only. The O_RDWR flag sets the file to be open for reading and writing.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>


int main(void){
    
    int fflags, rVal, fd;
    
    if((fd = open("afile", O_CREAT | O_APPEND, S_IRWXU))<0){
        printf("error opening file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File opened.\n");
    }
    
    if((rVal = fcntl(fd, F_GETFL))<0){
        printf("Error getting file status flags.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File status flags retrieved.\n");
    }
    
    //O_ACCMODE has the value of three
    fflags = rVal & O_ACCMODE;
    if(fflags == O_RDONLY){
        printf("File is read only.\n");
    } else if (fflags == O_WRONLY){
        printf("File is write only.\n");
    } else if (fflags == O_RDWR){
        printf("File is read / write.\n");
    } else {
        printf("No idea.\n");
    }
    
    
    if(close(fd)<0){
        printf("Error closing file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File descriptor closed.\n");
    }
    
    
    return 0;
}

The O_APPEND mode causes all write actions to happen at the end of the file. In this mode, the file pointer is moved to the end of the file before writing, disregarding wherever the file pointer was at before the call to write(). The O_NONBLOCK mode causes the file to open in non-blocking mode. In non-blocking mode, no operations on the file descriptor will cause the calling process to wait.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>

void readFlags(int fd){
    int returnValue;
    if((returnValue = fcntl(fd, F_GETFL, 0))<0){
        printf("Error opening file descriptor %d.\n", fd);
        return;
    }
    printf("Access mode flags for fd %d: ", fd);
    switch(returnValue & O_ACCMODE){
        case O_RDONLY:
            printf("Read only. ");
            break;
        case O_WRONLY:
            printf("Write only. ");
            break;
        case O_RDWR:
            printf("Read and write. ");
            break;
        default:
            printf("Unknown access mode. ");
            break;
    } // end switch
    if(returnValue & O_APPEND){
        printf("+Append.");
    }
    if(returnValue & O_NONBLOCK){
        printf("+Nonblocking");
    }
    printf("\n");
} // end function readFlags ---------------

int main(void){
    
    int fdOne, fdTwo;
    
    if((fdOne = open("append.txt", O_CREAT | O_APPEND, S_IRWXU))<0){
        printf("Error opening append.txt.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File opened with fd %d.\n", fdOne);
    }
    
    
    if((fdTwo = open("nonblocking.txt", O_CREAT | O_NONBLOCK, S_IRWXU))<0){
       printf("Error opening nonblocking.txt");
       exit(EXIT_FAILURE);
    } else {
        printf("File opened with fd %d.\n", fdTwo);
    }
    
    readFlags(fdOne);
    readFlags(fdTwo);
    
    if((close(fdOne))<0){
        printf("Error closing fdOne.\n");
        exit(EXIT_FAILURE);
    }
    
    if((close(fdTwo))<0){
        printf("Error closing fdTwo.\n");
        exit(EXIT_FAILURE);
    }
    
    return 0;
    
}

The F_SETFL command macro sets the file status flags to the value of the third argument. We can use logical OR  to combine the bit values of two separate sets of flags; likewise we can use logical AND to remove the bit values from sets of flags.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int setFlags(int fd, int flags);
int removeFlags(int fd, int flags);
void strFlagReturn(int returnValue);
void printFlags(int fd);


int main(void){
    
    int fdOne = open("another.txt", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
    
    if(fdOne < 0){
        printf("Error creating file descriptor.\n");
        exit(EXIT_FAILURE);
    }
    
    printFlags(fdOne);
    strFlagReturn(setFlags(fdOne, O_APPEND));
    printFlags(fdOne);
    strFlagReturn(setFlags(fdOne, O_NONBLOCK));
    printFlags(fdOne);
    strFlagReturn(removeFlags(fdOne, O_NONBLOCK));
    printFlags(fdOne);
    
    return 0;
    
}


void strFlagReturn(int returnVal){
    switch(returnVal){
        case 0:
            printf("Flags value altered successfully.\n");
            break;
        case -1:
            printf("Could not get flags to alter them.\n");
            break;
        case -2:
            printf("Could not alter flags after retrieving them.\n");
            break;
    }
    
}
void printFlags(int fd){
    int flags;
    
    if((flags=fcntl(fd, F_GETFL, 0))<0){
        printf("Couldn't access flags for fd %d.\n", fd);
        return;
    } else {
        printf("File Desc. %d: ", fd);
    }
    switch(flags & O_ACCMODE){
        case O_RDONLY:
            printf("Read only. ");
            break;
        case O_WRONLY:
            printf("Write only. ");
            break;
        case O_RDWR:
            printf("Read and write. ");
            break;
    } // end switch-----
    
    if(flags & O_APPEND){
        printf(" Append. ");
    }
    
    if(flags & O_NONBLOCK){
        printf(" Non-blocking.");
    }
    
    printf("\n");
}

//this uses logical and to remove flags
int removeFlags(int fd, int flags){
    int oldFlags;
    
    if((oldFlags = fcntl(fd, F_GETFL, 0))<0){
        return -1;
    }
   
    //AND the flag values
    //and inverse the flags parameter
    flags = ~flags & oldFlags;
    
    if(fcntl(fd, F_SETFL, flags) < 0){
        return -2;
    }
    
    return 0;
    
}

//this uses logical or to add flags
int setFlags(int fd, int flags){
    int oldFlags;
    
    if((oldFlags = fcntl(fd, F_GETFL, 0))<0){
        return -1;
    }
    
    //OR the flags values
    flags = flags | oldFlags;

    if((fcntl(fd, F_SETFL, flags))<0){
        return -2;
    } 
    
    return 0;
}

Note that the only file status flags that can be changed with fcntl() are O_APPEND, O_NONBLOCK, O_SYNC and O_ASYNC.

Review of Signals in Linux C

Signals provide one-way asynchronous notifications. A signal may be sent from the kernel to a process, from a process to another process, or even from a process to itself. The Linux kernel implements around 30 signals, with each signal being represented by a numeric constant and a name.

Signals interrupt a process, causing it to stop mid-execution and perform a predetermined action. A signal may be ignored, with the exceptions of the SIGKILL and SIGSTOP signals, or it may handle the signal.

Signals are defined by including the header file <signal.h>. All of the signal names begin with SIG.

#include <stdio.h>
#include <signal.h>

int main(void){
    
    
    //alarm clock
    printf("SIGALRM %d\n", SIGALRM);
    //hangup
    printf("SIGHUP %d\n", SIGHUP);
    //illegal instruction
    printf("SIGILL %d\n", SIGILL);
    //terminal interrupt
    printf("SIGINT %d\n", SIGINT);
    //write on pipe with no reader
    printf("SIGPIPE %d\n", SIGPIPE);
    //terminal quit
    printf("SIGTERM %d\n", SIGTERM);
    
    return 0;
    
}

Signals can be handled using the signal() library function, which is defined in <signal.h>. The signal() function takes two arguments. The first argument is the signal to be caught or ignored. The second argument contains the function to be called when the signal specified in the first argument is received.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void catchSignal(int sig){
    printf("Caught Ctrl-C (Signal %d)\n", SIGINT);
    fflush(stdout);
}

int main(void){
    
    
    signal(SIGINT, catchSignal);
    
    while(1){
        printf("Waiting for Ctrl-C...\n");
        sleep(1);
    }
    
    return 0;
    
}

A process can send a signal to another process, including itself, by calling the kill() function. The kill() function takes two arguments, the first being the process ID of the signal to be sent, the second being the actual signal to be sent.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

void doChild(void);
void doParent(void);

void sigtermHandler(int signal);
void sigchldHandler(int signal);
void sigintHandler(int signal);
void sigioHandler(int signal);
void sigpwrHandler(int signal);
void sigpipeHanlder(int signal);

int main(void){
    
    pid_t returnPID;
    
    if((returnPID = fork())<0){
        printf("Error forking the process!\n");
        exit(EXIT_FAILURE);
    }
    
    if(returnPID==0){
        printf("In child process %u.\n", getpid());
        doChild();
        printf("Child process has finished.\n");
    }
    if(returnPID > 0){
        printf("In parent process %u.\n", getpid());
        doParent();
        printf("Parent process has ended.\n");
    }
    
    return 0;
}

void doChild(void){
    printf("\tIn child process %u.\n", getpid());
    int arrSignals[] = {SIGTERM, SIGCHLD, SIGINT, SIGIO, SIGPWR, SIGPIPE};
    //set random
    srand(time(NULL));
    int i, r;
    //------------------for loop------------------------------------
    for(i = 0; i < 12; i++){
        //randomly select one of the signals from
        //the array
        r = rand() % 6;
        printf("\tSending signal %d to the parent process %u.\n", arrSignals[r], getppid());
        kill(getppid(), arrSignals[r]);
        sleep(2);
    } //end for loop------------------------------------------------
   
} //end doChild(void)--------------


void doParent(void){
    int i = 0;
    signal(SIGTERM, sigtermHandler);
    signal(SIGCHLD, sigchldHandler);
    signal(SIGINT, sigintHandler);
    signal(SIGIO, sigioHandler);
    signal(SIGPWR, sigpwrHandler);
    signal(SIGPIPE, sigpipeHanlder);
    while(i++ < 25){
       printf("In parent process %u.\n", getpid());
       sleep(1);
    }
}

//process was explicitly killed by someone
void sigtermHandler(int signal){
    printf("Received SIGTERM signal %d in process %u.\n", signal, getpid());
    printf("Electing not to terminate.\n");
    fflush(stdout);
}

void sigchldHandler(int signal){
    printf("Received SIGCHLD signal %d in process %u.\n", signal, getpid());
    printf("Apparently a a child of this process has terminated.\n");
    fflush(stdout);
}

//Ctrl-C
void sigintHandler(int signal){
    printf("Received SIGINT signal %d in process %u.\n", signal, getpid());
    printf("The process was interrupted. Continuing now.\n");
    fflush(stdout);
}

void sigioHandler(int signal){
    printf("Received SIGIO signal %d in process %u.\n", signal, getpid());
    printf("The process is ready for I/O.\n");
    fflush(stdout);
}

void sigpwrHandler(int signal){
    printf("Received SIGPWR signal %d in process %u.\n", signal, getpid());
    printf("The power has been switched to a short-term emergency supply.\n");
    fflush(stdout);
}

void sigpipeHanlder(int signal){
    printf("Received SIGPIPE signal %d in process %u.\n", signal, getpid());
    printf("The consumer of this process's pipe has died.\n");
    fflush(stdout);
}

Note that the kill() function will return -1 and set errno if the signal given is invalid, if it doesn’t have permission, or if the specified process does not exist.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void processSignal(int signal);

int main(void){
    
    pid_t returnID;
    int i = 0;
    
    if((returnID = fork())<0){
        printf("Error forking.\n");
        exit(EXIT_FAILURE);
    }
    if(returnID==0){
        while(i++ < 10){
            printf("In child process, %u.\n", getpid());
            if((kill(getppid(), SIGINT))<0){
                printf("***Error sending signal to %u.\n", getppid());
                exit(EXIT_FAILURE);
            }
            sleep(1);
        }
    }
    if(returnID > 0){
        signal(SIGINT, processSignal);
        while(i++ < 5){
            printf("In parent process, %u.\n", getpid());
            sleep(1);
        }
        exit(EXIT_SUCCESS);
    }
    
    return 0;
    
}


void processSignal(int signal){
    printf("\tReceived signal %d.\n", signal);
    fflush(stdin);
}

If you’re interested, I wrote a book on C that is available at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Reading and Writing to/from FIFOs

A FIFO, also known as a named pipe, is a file that acts like a pipe. One process opens the FIFO for writing, and another for reading. We can use the mkfifo() library function to open up a named pipe. The mkfifo() function accepts two parameters, the first is the pathname of the FIFO, and the second is the FIFO’s mode. The mkfifo() function returns -1 on failure and 0 on success.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>

int main(void){
    
    const char *pathname = "fifoTest";
    
    if((mkfifo(pathname, S_IRWXU | S_IRWXG))!=-1){
        printf("Pipe opened.\n");
    } else {
        printf("Could not open pipe.");
        printf(" %s\n", strerror(errno));
    }
    
    return 0;
    
}

We can remove the named pipe the same we would a regular file, by using the unlink() system call. The unlink() system call is defined in the <unistd.h> header file.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>

int main(void){
    
    char *path = "testingABC";
    
    if((mkfifo(path, S_IRWXU | S_IRWXG)) < 0){
        printf("Error creating named pipe.\n");
        printf("%s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    } else {
        printf("Named pipe %s created.\n", path);
    }
    
    if(unlink(path)<0){
        printf("Error erasing file.\n");
    } else {
        printf("%s erased.\n", path);
    }
    
    return 0;
    
}

Once a FIFO has been created using mkfifo(), we can access it via open() and read() or write().

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main(void){
    
    int fd;
    char *pathname = "theFIFO";
    pid_t childPID;
    
    if((mkfifo(pathname, S_IRWXU | S_IRWXG))<0){
        printf("Could not create named pipe %s.\n", pathname);
        printf("%s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    if((childPID = fork())<0){
        printf("Error forking process in %u.\n", getpid());
        exit(EXIT_FAILURE);
    }
    //in the child process --------------------------
    if(!childPID){
        char *msg = "Do you have stairs in your house?";
        printf("In the child process %u.\n", getpid());
        if((fd = open(pathname, O_WRONLY))<0){
            printf("Error opening FIFO in %u.\n", getpid());
            exit(EXIT_FAILURE);
        } else {
            printf("FIFO opened in %u.\n", getpid());
            write(fd, msg, strlen(msg));
        }
        printf("Exiting process %u.\n", getpid());
    }
    //in the parent process
    if(childPID){
        char buffer[256];
        printf("In the parent process %u.\n", getpid());
        if((fd = open(pathname, O_RDONLY))<0){
            printf("Error opening FIFO in %u.\n", getpid());
            exit(EXIT_FAILURE);
        } else {
            printf("FIFO opened in %u.\n", getpid());
            read(fd, buffer, sizeof(buffer));
            printf("In process %u, read: %s.\n", getpid(), buffer);
        }
        if((unlink(pathname))<0){
            printf("Error erasing %s.\n", pathname);
        } else {
            printf("FIFO '%s' erased.\n", pathname);
        }
        printf("Exiting process %u.\n", getpid());
    }
    
    return 0;
    
}

Note that the read() and write() functions operate slightly differently if we use the O_NONBLOCK flag to open the file descriptor, but that is a subject for another time.

Strings in C

C doesn’t have a string data type per se; instead, C uses an array of char elements to store a string.

A string constant is a set of characters placed between a pair of double quotes. Anything between double quotes is taken by the compiler to be a string.

#include <stdio.h>

int main(void){
    
    printf("This is a string. ");
    //newline character
    printf("As is this. \nBut this is on the second line.");
    //tab character
    printf("\nAnd this is \t on the \t third.");
    
    return 0;
    
}

Strings stored in memory end with the null character, . As strings in C are always terminated by a null character, the length of a string is always the number of characters, plus one. Strings are stored in arrays of type char.

Remember,the size of the char array must be large enough to hold the string of characters, plus one. We can also leave the array’s brackets empty and initialize the char array with the desired string, to have the size automatically set.

 #include <stdio.h>

int main(void){
    
    int i = 0;
    char strA[] = "I propose to move immediately upon your works.";
    
    while(true){
        //check if we have reached the null
        //termination characater
        if(strA[i] == ''){
            break;
        } else {
            //output the character
            //and increment the counter
            printf("%c ", strA[i++]);
        }
    }
    
    return 0;
    
}

The %s specification is used to output a null-terminated string. At wherever the %s appears in the format string argument, an output function will output successive characters from the char array until it hits the character.

#include <stdio.h>

int main(void){
    
    char str01[28] = "Nice boat!";
    char str02[128] = "It is better to die for the Emperor than live for yourself!";
    char str03[] = "My lab members are my allies, and the World is my enemy!";
    
    printf("String #1: %s\n", str01);
    printf("String #2: %s\n", str02);
    printf("String #3: %s\n", str03);
    
    
    return 0;
    
}

The main drawback to using mutable char arrays to hold a variety of strings is that you must make the char array long enough to hold the longest string that is expected. As arrays are, by definition, fixed length, we will probably end up wasting storage space much of the time.

Joining one string to the end of another is a often required; this is called appending a string.

#include <stdio.h>

int main(void){
    
    char strA[] = "Shaka, when the walls fell.";
    char strB[] = "Mirab, with sails unfurled.";
    int i, j;
    int length = sizeof(strA) + sizeof(strB);
    
    char strC[length];
      
    //copy first string
    i = 0;
    while(strA[i]!=''){
        strC[i] = strA[i];
        i++;
    }
    
    //insert a space 
    //to go between the two strings.
    strC[i] = ' '; 
    //copy second string
    j = i + 1;
    i = 0;
    while(strB[i]!=''){
      strC[j++] = strB[i++];
    }
    
    strC[j] = '';
    
    printf("strA = %s\n", strA);
    printf("strB = %s\n", strB);
    printf("strC = %s\n", strC);
    
    return 0;
    
}

The sizeof() operator returns the number of bytes in the string, including the byte allocated to store the character. This gives us some padding – we can replace the character in the first string to a blank space character before we append the second string to it within the new string.

In the real world, however, we would want to use the strlen() function rather than sizeof(). Unlike sizeof(), the strlen() function does not count the null termination character in the string.

Why should we use strlen(), then? Well, for one reason, we ought to recall here that a string in C is nothing more than a char array, and when we pass an array to a function, it is passed by reference, which in the C context means that the called function has a copy of a pointer to the array, and not the array itself. This means that if we use the sizeof() operator on a string parameter, we will get back the size of a pointer on our system, and not the number of bytes stored in the string!

#include <stdio.h>
#include <string.h>
#include <malloc.h>

//function returns a pointer to a char
//array
char * appendStr(char *strA, char *strB){
    int i, j;
    
    //sizeof wouldn't work here
    printf("Size of stringA[] in function: %d\n", sizeof(strA));
    printf("whereas strlen(stringA) gives us: %d\n", strlen(strA));
    
    //---let's us pointer arithmetic for fun
    char * returnChar = (char *)malloc(strlen(strA) + strlen(strB) + 2);

    i = 0;
    
    while(*(strA + i) != ''){
        *(returnChar + i) = *(strA + i);
        i++;
    }
    //put a space between the two
    *(returnChar + i) = ' ';
    
    //now  let's copy over the second string
    //we can used array index notation as well
    j = ++i;
    i = 0;
    
    while(strB[i]!=''){
        returnChar[j++] = strB[i++];
    }
    
    returnChar[j] = '';
   
    //since we are returning a value 
    //allocated on the heap
    //we will have to call free() in the calling function
    return returnChar;
    
} // end function --------------------


int main(void){
    
    char stringA[] = "Hi.";
    char stringB[] = "Nice boat!";
    char *buffer;
    
    printf("sizeof(stringA) = %d\n", sizeof(stringA));
    printf("strlen(stringA) = %d\n", strlen(stringA));
    
    //don't forget that an array name devolves to a pointer
    //when passed as an argument to a function
    buffer = appendStr(stringA, stringB);
    
    printf("\n%s\n", buffer);
    
    //don't forget to clean up!
    free(buffer);
    
    return 0;
    
}

Note that the strlen() function is declared in the string.h header file.

We can use a two-dimensional array of type char to store strings, where each row is used to hold a separate string. Thus, in a char array stringsArray[i][j], at any given point the i index will indicate which string we are using, and the j index will indicate which character in that string we are at.

#include <stdio.h>
#include <string.h>

int main(void){
    
 
    char stringArray[3][56] = {
                    "Valar morghulis",
                    "Don't Panic",
                    "Photons be free!"};
    
    char stringArrayTwo[][128] = {
                    "Nilbog is Goblin Spelled Backwards!",
                    "The morning sun has vanquished the horrible night.",
                    "You are technically correct -- the best kind of correct."
    };
    
    printf("%s ", stringArray[0]);
    printf("...starts with: %c\n", stringArray[0][0]);
    
    printf("%s ", stringArrayTwo[1]);
    printf("...starts with: %c\n", stringArrayTwo[1][0]);
    
    return 0;
    
}

Note that when initializing an array of strings, we can omit the first value when initializing the string array, so that the compiler deduces for us the number of strings we are declaring.

I wrote a book on C, check it out: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

I/O Multiplexing

Multiplexing essentially means reading from or writing to multiple file descriptors simultaneously. We can use the select() Linux system call to implement multiplexing. The select() system call can be used to monitor file descriptors for regular files, terminals, pipes, sockets, etc. A call to select() blocks until the given file descriptors are read to perform I/O, or until an optional timeout has passed.

The simplest call to select() contains four arguments plus NULL for the fifth argument. The first argument is an int value that contains the highest numbered file descriptor in any of the sets being monitored, plus one. The second, third, and fourth arguments are pointers to file descriptor sets, which have the data type fd_set.

These file descriptor sets are not manipulated directly, but are instead manipulated via four helper macros, FD_ZERO(), FD_SET(), FD_CLR(), and FD_ISSET(). The FD_ZERO() macro clears the specified set, and should be used every time before calling select(). The FD_SET() macro adds a file descriptor to a given set.

#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){
    
    fd_set readfds;
    fd_set writefds;
  
    int fdOne, fdTwo, fdThree;
    
    char *files[3] = {"FileOne", "FileTwo", "FileThree"};

    if((fdOne = open(files[0], O_CREAT | O_TRUNC | O_NONBLOCK, S_IRWXU))<0){
        printf("Problem opening the first file descriptor.\n");
        exit(EXIT_FAILURE);
    }
    
    if((fdTwo = open(files[1], O_CREAT | O_TRUNC | O_NONBLOCK, S_IRWXU))<0){
        printf("Problem opening the second file descriptor.\n");
        exit(EXIT_FAILURE);
    }
    
    if((fdThree = open(files[2], O_CREAT | O_TRUNC | O_NONBLOCK, S_IRWXU))<0){
        printf("Problem opening the third file descriptor.\n");
        exit(EXIT_FAILURE);
    }
    
    
    //clear the read and write sets
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    
    //add file descriptors to the sets using FD_SET() 
    //macro
    FD_SET(fdOne, &readfds);
    FD_SET(fdTwo, &writefds);
    FD_SET(fdThree, &writefds);
    
    return 0;
    
}

The select() system call lets us do I/O multiplexing. We pass arguments to select() to tell the kernel what file descriptors we are interested in, whether we want to read from or write to the descriptor, and how long we want to wait.

On success, the select() system call returns the number of file descriptors ready for I/O, among the three sets. If there was an error, it returns -1. We can then use the FD_ISSET() macro to determine whether a file descriptor is in the set, meaning that it is ready to be read or to be written to.

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>

int main(void){
    
    fd_set readfds;
    int retVal;
    int fd;
    
    fd = open("testing", O_CREAT | O_TRUNC | O_NONBLOCK, S_IRWXU);
    
    if(fd < 0){
        exit(EXIT_FAILURE);
    }
    
    //clear readfds
    FD_ZERO(&readfds);
    //add the file descriptor
    FD_SET(fd, &readfds);
    //add the descriptor for stdin
    FD_SET(STDIN_FILENO, &readfds);
    
    //ok let us see what happens
    //we're just looking to read, so pass in NULL
    //for other three parameters
    retVal = select(fd + 1, &readfds, NULL, NULL, NULL);
    
    if(retVal < 0){
        printf("select() failed.\n");
        exit(EXIT_FAILURE);
    }
    
    if(FD_ISSET(STDIN_FILENO, &readfds)){
        printf("Ready to read from stdin...\n");
    }
    
    if(FD_ISSET(fd, &readfds)){
        printf("File Descriptor %d is ready to read!", fd);
    }
      
    return 0;
    
}

Note what we mean by “ready”: a descriptor in the read set is ready if a read from that descriptor won’t block; likewise, a descriptor in the write set is ready if a write to that descriptor won’t block.

The last argument passed to select() specifies how long we want to wait.  If the argument is NULL, then we will wait forever, or until one of the specified descriptors is ready. If we pass the argument a timeval structure, we will wait the specified number of seconds and microseconds.

#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>

int main(void){
    
    int retVal;
    timeval t;
    //wait for five seconds
    t.tv_sec = 5;
    t.tv_usec = 0;
    
    fd_set writefds;
    
    FD_ZERO(&writefds);
    
    //1
    FD_SET(STDOUT_FILENO, &writefds);
    //0
    FD_SET(STDIN_FILENO, &writefds);
    
    retVal = select(STDOUT_FILENO + 1, NULL, &writefds, NULL, &t);
    
    if(retVal==0){
        printf("select() timed out.\n");
    }
    if(retVal>0){
        if(FD_ISSET(STDOUT_FILENO, &writefds)){
            printf("stdout is ready.\n");
        }
        if(FD_ISSET(STDIN_FILENO, &writefds)){
            printf("stdin is ready.\n");
        }
    }
    return 0;
    
}

Note that a return value of 0 means that no descriptors are ready and that the timeout has thus passed.

Slightly More Advanced File Handling in Linux

Two functions, read() and write(), are provided in <unistd.h>. The write() function writes X number of bytes from a buffer, and the read() function tries to read up to X number of bytes, and stores the data in the specified buffer. Both functions require a file descriptor to work and return -1 if the action has failed.

Partial reads are quite common. The read() function should be checked for two conditions: a negative return value, if it has failed, and 0, if it has reached EOF.

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <cstring>
#include <stdio.h>
#include <stddef.h>

using namespace std;

const int read_buff_length = 16;

int main(void) {

    int lineCount = 0;
    
    int writeFd, readFd, returnVal;
    
    char *path = "test.txt";
    
    char writeBuffer[] = "You, the people have the power - the power to create machines. The power to create happiness! You, the people, have the power to make this life free and beautiful, to make this life a wonderful adventure.";
    
    char readBuffer[read_buff_length+1];
    
    
    //----write to the file------------------------------------
    if((writeFd = open(path, O_CREAT | O_TRUNC | O_WRONLY)) < 0){
        printf("Error opening %s.\n", path);
        exit(EXIT_FAILURE);
    } else {
        printf("File opened!\n");
    }
    
    returnVal = write(writeFd, writeBuffer, sizeof(writeBuffer) / sizeof(char));
    if(returnVal < 0){
        printf("Error writing to file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File written to!\n");
    }
    
    if((close(writeFd))<0){
        printf("Error closing %s.\n", path);
        exit(EXIT_FAILURE);
    } else {
        printf("%s closed after writing.\n", path);
    }
    
    //---------read from the file----------------------------------
    if((readFd = open(path, O_RDONLY))<0){
        printf("Error opening %s.\n", path);
        exit(EXIT_FAILURE);
    } else {
        printf("%s opened!\n", path);
    }
    
    while(true){
        returnVal = read(readFd, readBuffer, read_buff_length);
        if(returnVal>0){
            readBuffer[read_buff_length+1]='';
            printf("Line %d: %s\n", ++lineCount, readBuffer);
        } else {
            if(returnVal==0){
                printf("Read completed.\n");
                break;
            } else {
                printf("Problem reading file.\n");
                exit(EXIT_FAILURE);
            }
        }
    }
    
    if((close(writeFd))<0){
        printf("Error closing %s.\n", path);
        exit(EXIT_FAILURE);
    } else {
        printf("%s closed after reading.\n", path);
    }
    
    return 0;
}

The third argument sent to read() and write() is size_t, which defines the number of bytes to be read in or written to the supplied buffer. The size_t type is required by POSIX and is used to store values used in measuring sizes in bytes. The ssize_t type is a signed version of size_t, and is used as the return value type of the read() and write() functions.  The maximum value for size_t is SIZE_MAX and the maximum value of ssize_t is  SSIZE_MAX. The value of SSIZE_MAX is quite large for a single read or write.

#include <stdio.h>
#include <limits.h>


int main(void){
     
    printf("SSIZE_MAX = %ld\n", SSIZE_MAX);
 
    return 0;
    
}

Note that SSIZE_MAX is found in the limits.h header file.

The open() system call can take a third argument; this argument is the file’s mode, of type mode_t, which is defined as the types.h header file.  We can define the mode_t value by OR-ing one or more of the symbolic constants from <sys/stat.h>.

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <cstring>
#include <stdio.h>
#include <sys/stat.h>

using namespace std;

int main(void) {

    printf("User permissions: \n");
    printf("S_IRWXU = %d\n", S_IRWXU);//448
    printf("S_IRUSR = %d\n", S_IRUSR);//256
    printf("S_IWUSR = %d\n", S_IWUSR);//128
    printf("S_IXUSR = %d\n\n", S_IXUSR);//64
    
    printf("Group permissions: \n");
    printf("S_IRWXG = %d\n", S_IRWXG);//56
    printf("S_IRGRP = %d\n", S_IRGRP);//32
    printf("S_IWGRP = %d\n", S_IWGRP);//16
    printf("S_IXGRP = %d\n\n", S_IXGRP);//08
    
    printf("Other permissions: \n");
    printf("S_IRWXO = %d\n", S_IRWXO);//07
    printf("S_IROTH = %d\n", S_IROTH);//04
    printf("S_IWOTH = %d\n", S_IWOTH); //02
    printf("S_IXOTH = %d\n\n", S_IXOTH); //01
    
    return 0;
}

The S_IRWXU permission allows users to read, write and execute a file; the S_IRWXG permission allows group members to read, write and execute a file, and the S_IRWXO permission allows others to read, write and execute a file.

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){
    
    char buffer[256];
    
    char *noWrite = "fileOne";
    char *noRead = "fileTwo";
    
    //can write, not read
    mode_t modeOne = S_IWUSR;
    //can read, not write
    mode_t modeTwo = S_IRUSR;
    
    int fileDescriptor;
    
    //-r--------
    if((fileDescriptor = open(noWrite, O_CREAT | O_RDONLY,modeTwo))<0){
        printf("Could not open file %s.\n", noWrite);
        exit(EXIT_FAILURE);
    }
    
    if(close(fileDescriptor)<0){
        printf("Could not close file %s.\n", noWrite);
        exit(EXIT_FAILURE);
    }
    
    //--w-------
    if((fileDescriptor = open(noRead, O_CREAT | O_WRONLY, modeOne))<0){
        printf("Could not open file %s.\n", noRead);
        exit(EXIT_FAILURE);
    }
    
    if(close(fileDescriptor)<0){
        printf("Could not close file %s.\n", noRead);
        exit(EXIT_FAILURE);
    }
    
    //try to open the read file for writing
    if((fileDescriptor = open(noWrite, O_WRONLY))<0){
        printf("Could not open %s for writing.\n", noWrite);
    }
    
    //try to open the write file for reading
    if((fileDescriptor = open(noRead, O_RDONLY))<0){
        printf("Could not open %s for reading.\n", noRead);
    }
   
    return 0;
    
}

Note that the umask defines what permissions cannot be allowed when new files are created. To allow for more permissions than a umask will allow, we must change the permissions after the file has been created.

When a file descriptor is opened in append mode, via the O_APPEND access flag, writes do not occur at the file descriptor’s current file position. Instead, writes occur at the end of the file.

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main(void){
    
    int i = 0;
    pid_t pid;
    ssize_t retVal;
    int fd;
    char buffer[256];
    char *path = "xyz";
    
    printf("Starting in process %lld\n", getpid());
    if((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU))<0){
        printf("Error creating file %s\n", path);
        exit(EXIT_FAILURE);
    } else {
        if(!(close(fd)<0)){
            printf("File %s created.\n", path);
        }
    }
    
    
    if((pid=fork())<0){
       printf("Could not fork process.\n");
       exit(EXIT_FAILURE);
    }
    
    //in child process
    if(pid==0){
       if((fd = open(path, O_WRONLY | O_APPEND))<0){
           printf("Error appending to file %s in %lld\n", path, getpid());
           exit(EXIT_FAILURE);
       }
       while(i++ < 5){
        sprintf(buffer, "Hello from process %d \t (child %d).\n", getpid(), i);
        if((retVal = write(fd, buffer, strlen(buffer)))<0){
            printf("Error writing to file %s in %lld\n", path, getpid());
            exit(EXIT_FAILURE);
        }
        printf("Writing to file from process %lld.\n", getpid());
        sleep(i);
       }
       if(close(fd)<0){
           printf("Error closing file %s in %lld\n", path, getpid());
           exit(EXIT_FAILURE);
       }
    }
    
    //in parent process
    if(pid>0){
        if((fd = open(path, O_WRONLY | O_APPEND))<0){
            printf("Error appending to file %s in %lld\n", path, getpid());
            exit(EXIT_FAILURE);
        }
        while(i++ < 5){
            sprintf(buffer, "Hello from process %d \t (parent %d).\n", getpid(), i);
            if((retVal = write(fd, buffer, strlen(buffer)))<0){
                printf("Error writing to file %s in %lld\n", path, getpid());
                exit(EXIT_FAILURE);
            }
            printf("Writing to file from process %lld\n", getpid());
            sleep(i + 2);
        }
        if(close(fd)<0){
            printf("Error closing file %s in %lld\n", path, getpid());
            exit(EXIT_FAILURE);
        }
    }
    
    return 0;
}

Alright, that’s enough mischief for today. When we next look at C in the *nix environment, we will look at lseek() as well inspecting and modifying file and directory metadata.

I/O with Linux File Descriptors

In this post let’s work with the low level open(), close(), read(), and write() calls. We will use these system calls to manipulate what are known as regular files, which is to say named bytes of data, organized into a linear array called a byte stream. Each of the four system calls we will look at rely on file descriptors.

File descriptors are small, positive integers that serve the function of index values for the array of open files that the Linux kernel maintains for each process. By default, each process start with three open files: standard input, standard output, and standard error. These correspond to file descriptors 0, 1, and 2, respectively.

A file is opened and a file descriptor is obtained by using the open() system call. The most basic version of open takes two arguments, a pointer to const char and an int. The pointer is the pathname, and the int specifies the access flag. The access flags are: O_CREAT, which creates the file if it does not already exist; O_EXCL, which is used only in conjunction with O_CREAT, it makes open() fail if the file already exists; O_TRUNC, that sets the file’s size to 0; O_APPEND, which causes writes to occur at the end of the file; O_NONBLOCK, which opens the file in non-blocking mode, and O_SYNC, which causes all writes to the file to be written to the disk before the call returns.

To use the open() system call, we need to include the <sys/types.h>, <sys/stat.h>, and <fcntl.h> header files.

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(void){
    
    printf("O_CREAT = %d\n", O_CREAT);
    printf("O_EXCL = %d\n", O_EXCL);
    printf("O_TRUNC = %d\n", O_TRUNC);
    printf("O_APPEND = %d\n", O_APPEND);
    printf("O_NONBLOCK = %d\n", O_NONBLOCK);
    printf("O_SYNC = %d\n", O_SYNC);
    
    return 0;
    
}

The open() system call associates the file indicated by the pathname argument to a file descriptor, which it returns. Unless it fails, in which case it returns a negative number.

To close a file after we have opened it, use the close() system call. The close() system call takes a single argument, which is a file descriptor. To use close(), we need to include <unistd.h>.

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int  main(void){
    
    int iFileDesc;
    char path[] = "ciel.txt";
    
    //create file if it doesn't exist
    //open for read / write
    iFileDesc = open(path, O_CREAT | O_RDWR);
    
    if(iFileDesc < 0){
        printf("Something didn't work.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("%s opened.\n", path);
    }
    
    //close the file
    if(close(iFileDesc) < 0){
        printf("Problem closing file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("%s closed.\n", path);
    }
    
    exit(EXIT_SUCCESS);    
}

Note that the close() system call returns a negative value if it fails. As we have seen, the return value from open() is either the new file descriptor or -1 to indicate an error. When open() returns a file descriptor, it returns the lowest unused integer value.

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(void){
    
    int fd, fdTwo;
    char pathname[] = "testing.txt";
    
    if((fd = open(pathname, O_CREAT | O_WRONLY))<0){
        printf("could not create %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        //file descriptor should be 3
        printf("%s opened with fd = %d\n", pathname, fd);
    }
    
    if((fdTwo = open("other", O_CREAT | O_RDWR))<0){
        printf("could not create second file\n");
        exit(EXIT_FAILURE);
    } else {
        //file descriptor should be 4
        printf("second file opened with fd = %d\n", fdTwo);
    }
    
    if(close(fd)<0){
        printf("Could not close %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        printf("%s closed\n", pathname);
    }
    
    if(close(fdTwo)<0){
        printf("Could not close second file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("second file closed\n");
    }
    
    return 0;
    
}

The write() system call is used to write data from the file corresponding to the file descriptor and is defined in <unistd.h>. On success, write() returns the number of bytes written. On error, -1 is returned. The write() call takes three arguments, the first is the file descriptor, the second is the buffer, and the third is the number of bytes to write.

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void){
    
    const char *pathname = "test4";
    int fd;
    char *buffer = "The President has been kidnapped by ninjas. Are you a bad enough dude to rescue the President?";
    ssize_t retVal;
    

    fd = open(pathname, O_CREAT | O_TRUNC | O_RDWR);
    if(fd < 0){
        printf("Error opening %s\n", pathname);
        exit(EXIT_FAILURE);
    } 
    
    retVal = write(fd, buffer, strlen(buffer));
    
    if(retVal < 0){
        printf("Error writing to %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        printf("Wrote to %d bytes to %s\n", retVal, pathname);
    }
    
    if(close(fd)<0){
        printf("Error closing %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        printf("Closed %s\n", pathname);
    }
    
    exit(EXIT_SUCCESS);
    
}

Like write(), the arguments to read() are the file descriptor for the open file, a pointer to a buffer to read data from (instead of write), and the number of bytes to read (instead of write. The return value is -1 if an error occurred.

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

const int buffer_size = 256;

int main(void){
    
    int fd;
    ssize_t bytesRead;
    char pathname[] = "test4";
    char buffer[buffer_size];
    
    if((fd = open(pathname, O_RDONLY))<0){
        printf("Error opening %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        printf("%s opened\n", pathname);
    }
    
    bytesRead = read(fd, buffer, sizeof(buffer));
    
    if(bytesRead < 0){
        printf("Could not read %s\n", buffer);
        exit(EXIT_FAILURE);
    } else {
        printf("read: %s\n", buffer);
    }
    
    if(close(fd)<0){
        printf("Could not close %s\n", pathname);
        exit(EXIT_FAILURE);
    } else {
        printf("%s closed\n", pathname);
    }
    
    exit(EXIT_SUCCESS);
    
}

Note that read() can return 0 – in this case, the file position is at EOF. We will look at read() in more detail, as well as review file modes and ownership, in a later post.