What is "complete error trapping"? - c

Write a program in C using only low-level I/O..
The program must have complete error trapping. In particular the
program should use perror() to report system errors...
In my program...
test("checked argument count");
if((input_file1 = open(argv[1], O_RDONLY)) < 0)
{
test("couldn't open file1");
perror(argv[1]);
close(input_file1);
exit(1);
}
test("opened file1");
Would this be considered "complete error trapping" if I implement such code for every read/write attempt?
Note: test() is just for debugging and will be deleted later:
void test(const char * message)
{
printf("\ttesting: %s \n", message);
}

You shouldn't close the file descriptor that you failed to open.
Other than that, yes, you've done sufficient error checking on the open() call. Now repeat for the other open() calls, and the read() calls, and the write() calls, and presumably the close() calls that are part of the main-line processing — close() calls in the error paths are a best-effort and don't need to be error checked in the same way.
Your error reporting is not very helpful, though. You say 'file1' but that isn't the name of the file. Using perror() isn't going to help much there, either; I never use it because it doesn't give me enough control over the message format. You are passing the file name as the string; that's considerably better than people often do, but you can't also express which operation the program was attempting that failed. I'd use fprintf(stderr, ...) in conjunction with errno and strerror(). Be careful not to clobber errno by calling a function that itself sets errno (is your test() function safe?). If you aren't sure, capture errno and (if necessary) reset it to the captured value:
int errnum = errno;
test("couldn't open file", argv[1]);
errno = errnum;
perror(argv[1]);
exit(1);
The revised test() function might be:
#include <stdarg.h>
extern void test(char const *fmt, ...);
void test(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
}
That's the core of it; you'd need to adapt that to work with your current internals of the test() function. The declaration of test() with the ellipsis does not require the <stdarg.h> header; the implementation of test() does require the header.

Related

Can I use two level of variable length argument functions...?

I have one problem with variable length argument debug log print function. I will just simulate the code here.
void SecondPrint(int level, const char* format,...)
{
//Printing the log here
va_list arg;
va_start(arg, format);
vprintf(format, arg);
va_end(arg);
}
void FirstPrint(int level, const char* format,...)
{
SecondPrint(level,format);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
"SecondPrint" is supposed to print "100 Error message" as expected, But its not printing like that its printing " Error message".
I am not getting whats wrong with this one. I am suspecting the way of calling "SecondPrint" from "FirstPrint" function. FirstPrint is receiving remaining arguments through ... but its invoking SecondPrint with level and format arguments.
I can't use SecondPrint from main directly. I have to use FirstPrint and FirstPrint has to call SecondPrint to print the log. So how can I achieve this one.. I thought to use VA_ARGS but it is for only macro definitions not in function definition.
And one more thing I can't do like *#define FirstPrint(a,b,...) SecondPrint(a,b,...)*
any help is highly appreciated thanks in advance.
C varargs are not designed to be passed more than one level; the kind of necessary stack manipulation required is too deep for the language. Typically, in a case like this, you would have a version of SecondPrint analagous to vprintf -- SecondPrintV or similar, and you would have FirstPrint invoke SecondPrintV after extracting the varargs, rather than invoking SecondPrint directly (and, for consistency, usually have SecondPrint invoke SecondPrintV internally).
You need to pass a va_list to SecondPrint, so it can access the arguments to FirstPrint
void SecondPrint(int level, const char* format, va_list args)
{
//Printing the log here
vprintf(format, arg);
}
void FirstPrint(int level, const char* format,...)
{
va_list args;
va_start(args, format);
SecondPrint(level, format, args);
va_end(args);
}
void main()
{
int level = 100;
FirstPrintf("Log level = %d message = %s \n",level,"Error message");
}
It may be possible to do it in specific platform-specific ways, or (since the arguments are exactly the same in the same order) use assembly to "replace" the current function frame with the function to call. GCC also has an extension to do it with the __builtin_apply_args() and __builtin_apply() functions (although you must somehow figure out how to compute the size of the stack argument data).

Writing to pipe or pipes

What is the best way to do this theoretically? I need to let the user enter the number of processes to send to a pipe for instance "3" and as it loops through the three [three whats?] on each iteration I need to create a process, send it [what?] to the pipe and print it.
The next time the user enters another number, say "4", it should print the previous 3 + 1.. I am working on this but can't understand how do it. Here is my code. I just need guidance, no need to try to solve it for me (but suggestions would be much appreciated).
Right now I am able to send one through the pipe and return it but then the pipe closes and it does not allow for the other processes to get in there.
Suggestion #1: Use functions
Use functions, even for little jobs such as:
void create_fifo(const char *name)
{
/* Create the first named - pipe */
int ret_val = mkfifo(name, 0666);
if ((ret_val == -1) && (errno != EEXIST))
{
perror("Error creating the named pipe");
exit(1);
}
}
Now you can simply write in your main program:
create_fifo(PIPE1);
create_fifo(PIPE5);
This cuts down on the clutter in your main program. It also adheres to the Agile principle DRY - Don't Repeat Yourself.
Suggestion #2: Error check system calls.
You did that for creating the FIFOs, which is good. You don't for the open() calls, or the read() or write() calls. You probably should. I use a function similar to the following in my programs:
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static const char *arg0 = "did not call err_setarg0(argv[0])";
void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
void err_exit(const char *fmt, ...)
{
int errnum = errno; /* Capture errno before it is changed */
va_lists args;
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
You can then use:
if ((rdfd1 = open(PIPE1, O_RDONLY)) < 0)
err_exit("Failed to open FIFO %s for reading: ", PIPE1);
if ((wrfd1 = open(PIPE5, O_WRONLY)) < 0)
err_exit("Failed to open FIFO %s for writing: ", PIPE5);
Suggestion #3: Make an iterative server
Your server program currently opens the FIFOs once, then reads from one, write to the other, and terminates. You need a loop around some portion of this code, maybe two nested loops. You have to decide whether you need an inner loop to read until EOF. You also need to know how you will terminate the server.
Suggestion #4: Maybe the server needs pipe names as arguments
Your server currently works on fixed FIFO names. You probably need it to take input and output file names as command line arguments, so that when your client spawns multiple servers, each server can have its own set of FIFOs, rather than all processes sharing the same two FIFOs, which is going to lead to confusion and chaos.
Indeed, the need for generating names calls the whole design into question - are you sure using FIFOs is the best way to do this? It looks to me like a case where anonymous pipes would serve you better; you wouldn't have to invent names, and the server would simply read from its standard input and write the (modified?) data to its standard output, so you could even simply use cat or tr or sed or ... as your server.
Clearly, if you use pipes, you will need to do some careful plumbing, but you also need to do careful plumbing with the pairs of FIFOs per server.

errno, strerror and Linux system calls

I can use strerror() to get text representation of errno value after using CRT functions, like fopen(). If I use open() Linux system call instead of CRT function, it also sets errno value when it fails. Is this correct to apply strerror() to this errno value? If not, is there some Linux system call, which does the same as strerror()?
Yes, and your code might be something like (untested) this:
#include <stdio.h>
#include <errno.h>
#include <string.h> // declares: char *strerror(int errnum);
FILE *
my_fopen ( char *path_to_file, char *mode ) {
FILE *fp;
char *errmsg;
if ( fp = fopen( path_to_file, mode )) {
errmsg = strerror( errno ); // fopen( ) failed, fp is set to NULL
printf( "%s %s\n", errmsg, path_to_file );
}
else { // fopen( ) succeeded
...
}
return fp; // return NULL (failed) or open file * on success
}
Yes
Yes
In there is perror
if (-1 == open(....))
{
perror("Could not open input file");
exit(255)
}
Most of the Linux system calls are encapsulated by C library routines. open() system call is actually a function defined in the C library which calls the actual open() system call of the kernel.
errno is a variable defined and managed by the C library, not by the kernel. It is set upon the return of the system call with the error code returned by the kernel.
As an example, in the GNU C library, open() is defined in sysdeps/unix/sysv/linux/open.c as:
int
__libc_open (const char *file, int oflag, ...)
{
int mode = 0;
if (__OPEN_NEEDS_MODE (oflag))
{
va_list arg;
va_start (arg, oflag);
mode = va_arg (arg, int);
va_end (arg);
}
return SYSCALL_CANCEL (openat, AT_FDCWD, file, oflag, mode);
}
libc_hidden_def (__libc_open)
weak_alias (__libc_open, __open)
libc_hidden_weak (__open)
weak_alias (__libc_open, open)
The bottom macros establish a equivalence between __libc_open() and open().
The SYSCALL_CANCEL() macros invokes the actual system call (which is openat()) and sets errno with the error code if any error condition occured.

C: writing the following code into functions

Dear respected programmers. Please could you help me (again) on how to put the following code into functions for my program.
I have read on-line and understand how functions work but when I do it myself it all goes pear shaped/wrong(I am such a noob).
Please could you help with how to for example to write the code below into functions.(like opening the input file).
My initial code looks like:
main (int argc, char **argv)
{
int bytes_read, bytes_written;
struct stat inode;
int input_fd, output_fd;
char buffer[64];
int eof = 0;
int i;
/* Check the command line arguments */
if (argc != 3)
{
printf("syntax is: %s \n", <fromfile> <tofile>\n", argv[0]);
exit (1);
}
/* Check the input file exists and is a file */
if ((stat(argv[1], &inode) == -1) || (!S_ISREG(inode.st_mode)))
{
printf("%s is not a file\n", argv[1]);
exit(2);
}
/* Check that the output file doesnt exist */
if (stat(argv[2], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[2]);
exit(2);
}
/* Open the input file for reading */
input_fd = open(argv[1], O_RDONLY, 0);
if (input_fd == -1)
{
printf("%s cannot be opened\n", argv[1]);
exit(3);
}
output_fd = open(argv[2], O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if (output_fd == -1)
{
printf("%s cannot be opened\n", argv[2]);
exit(3);
}
/* Begin processing the input file here */
while (!eof)
{
bytes_read = read(input_fd, buffer, sizeof(buffer));
if (bytes_read == -1)
{
printf("%s cannot be read\n", argv[1]);
exit(4);
}
if (bytes_read > > 0)
{
bytes_written = write(output_fd, buffer, bytes_read);
if (bytes_written == -1)
{
printf("There was an error writing to the file %s\n",argv[2]);
exit(4);
}
if (bytes_written != bytes_read)
{
printf("Devistating failure! Bytes have either magically appeared and been written or dissapeard and been skipped. Data is inconsistant!\n");
exit(101);
}
}
else
{
eof = 1;
}
}
close(input_fd);
close(output_fd);
}
My attempt at opening an output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Any help on how I would now reference to this in my program is appreciated thank you.
I tried:
ouputfile (but I cant figure out what goes here and why either).
Maybe the most useful function for you is:
#include <stdio.h>
#include <stdarg.h>
extern void error_exit(int rc, const char *format, ...); /* In a header */
void error_exit(int rc, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(rc);
}
You can then write:
if (stat(argv[2], &inode) != -1)
error_exit(2, "Warning: The file %s exists. Not going to overwrite\n",
argv[2]);
Which has the merit of brevity.
You write functions to do sub-tasks. Deciding where to break up your code into functions is tricky - as much art as science. Your code is not so big that it is completely awful to leave it as it is - one function (though the error handling can be simplified as above).
If you want to practice writing functions, consider splitting it up:
open_input_file()
open_output_file()
checked_read()
checked_write()
checked_close()
These functions would allow your main code to be written as:
int main(int argc, char **argv)
{
int bytes_read;
int input_fd, output_fd;
char buffer[64];
if (argc != 3)
error_exit(1, "Usage: %s <fromfile> <tofile>\n", argv[0]);
input_fd = open_input_file(argv[1]);
output_fd = open_output_file(argv[2]);
while ((bytes_read = checked_read(input_fd, buffer, sizeof(buffer)) > 0)
check_write(output_fd, buffer, bytes_read);
checked_close(input_fd);
checked_close(output_fd);
return 0;
}
Because you've tucked the error handling out of sight, it is now much easier to see the structure of the program. If you don't have enough functions yet, you can bury the loop into a function void file_copy(int fd_in, int fd_out). That removes more clutter from main() and leaves you with very simple code.
Given an initial attempt at a function to open the output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Critique:
You have to define the variables used by the function in the function (you will want to avoid global variables as much as possible, and there is no call for any global variable in this code).
You have to define the return type. You are opening a file - how is the file descriptor going to be returned to the calling code? So, the return type should be int.
You pass only the information needed to the function - a simple form of 'information hiding'. In this case, you only need to pass the name of the file; the information about file modes and the like is implicit in the name of the function.
In general, you have to decide how to handle errors. Unless you have directives otherwise from your homework setter, it is reasonable to exit on error with an appropriate message. If you return an error indicator, then the calling code has to test for it, and decide what to do about the error.
Errors and warnings should be written to stderr, not to stdout. The main program output (if any) goes to stdout.
Your code is confused about whether argv[i] or argv[argc-1] is the name of the output file. In a sense, this criticism is irrelevant once you pass just the filename to the function. However, consistency is a major virtue in programming, and using the same expression to identify the same thing is usually a good idea.
Consistency of layout is also important. Don't use both if( and if ( in your programs; use the canonical if ( notation as used by the language's founding fathers, K&R.
Similarly, be consistent with no spaces before commas, a space after a comma, and be consistent with spaces around operators such as '|'. Consistency makes your code easier to read, and you'll be reading your code a lot more often than you write it (at least, once you've finished your course, you will do more reading than writing).
You cannot have return -1; inside a function that returns no value.
When you a splitting up code into functions, you need to copy/move the paragraphs of code that you are extracting, leaving behind a call to the new function. You also need to copy the relevant local variables from the calling function into the new function - possibly eliminating the variables in the calling function if they are no longer used there. You do compile with most warnings enabled, don't you? You want to know about unused variables etc.
When you create the new function, one of the most important parts is working out what the correct signature of the function is. Does it return a value? If so, which value, and what is its type? If not, how does it handle errors? In this case, you probably want the function to bail out (terminate the program) if it runs into an error. In bigger systems, you might need to consistently return an error indicator (0 implies success, negative implies failure, different negatives indicating different errors). When you work with function that return an error indicator, it is almost always crucial that you check the error indicators in the calling code. For big programs, big swathes of the code can be all about error handling. Similarly, you need to work out which values are passed into the function.
I'm omitting advice about things such as 'be const correct' as overkill for your stage in learning to program in C.
you seem to actually understand how to make a function. making a function really isnt that hard. first, you need to kind of understand that a function has a type. in otherwords, argc has type int and argv has type char *, your function (currently) has type void. void means it has no value, which means when you return, you return nothing.
however, if you look at your code, you do return -1. it looks like you want to return an interger. so you should change the top from void outputfile(...) to int outputfile(...).
next, your function must return. it wont compile if there is a circumstance where it won't return (besides infinite loops). so at the very bottom, if no errors happen, it will reach the end. since you're no longer using "void" as the return type, you must return something before the end of the function. so i suggest putting a return 1; to show that everything went great
There's several things.
The function return type isn't what you want. You either want to return a file descriptor or an error code. IIRC, the file descriptor is a nonnegative int, so you can use a return type of int rather than void. You also need to return something on either path, either -1 or file_desc_out.
You probably don't want to pass in the command-line arguments as a whole, but rather something like argv[argc - 1]. In that case, the argument should be something like char * filename rather than the argc/argv it has now. (Note that the argv[i] you've got in the last printf is almost certainly wrong.)
This means it would be called something like
int file_desc_out = outputFile(argv[argc - 1]);
You need to have all variables declared in the function, specifically inode and file_desc_out.
Finally, put an extra level of indentation on the code inside the { and } of the function itself.

How can I temporarily redirect printf output to a c-string?

I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)
PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.
Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?
If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?
If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.
You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.
To write your own printf, you will want to use stdarg functionality:
int printf(const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
if (redirect_printf)
{
#ifdef HAVE_VLOG
// If you have a vlog function that takes a va_list
vlog(fmt, ap);
rv = ...;
#else
char buffer[LARGESIZE];
rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);
log(buffer);
#endif;
}
else
{
rv = vprintf(fmt, ap);
}
return rv;
}
This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.
You're wrong — elog supports format strings just like printf. Here's an example from Postgres source code:
elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
So all you need is to add elog where there is printf using the same parameters.
The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe
#define printf(...) sprintf(buffer, __VA_ARGS__)
Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.
If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-
newstdout = freopen("/tmp/log", "w", stdout);
This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-
readfd = fopen("/tmp/log", "r");
and forward the contents that have been added using something like this:-
void forward_to_elog(void)
{
int bytesread;
char buf[100];
memset(buf,0,100);
do {
memset(buf,0,100);
bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
/* call elog(buf) */ ;
} while(bytesread);
}
If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.
The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.

Resources