January 2006
There is a utility called service that can be used to shortcut the path of init (or rc) scripts on several platforms. The service utility is written in Perl. As an exercise service is being rewritten in C. The command needs to be able to do very few operations. The version presented in this text will have room for improvement requiring some additional functions and operational changes.
system() function. In the C version, the
path and operation are built as one string which is passed
to the libc system() function.The following includes and prototype are at the beginning of the source file:
#include <unistd.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <sys/types.h> #include <sys/stat.h>
The first draft of service in C has one helper function:
int check_handle(char *dir)
{
struct stat statbuf;
return (stat(dir, &statbuf));
}
First allocate a structure for the status buffer then return the
results (not the buffer or handle) of the stat()
function.
The check_handle() function does little but
it uses a lot, specifically the status buffer. In the
context of a function it is assured that the buffer is blasted
clean after each use because it is local to the function.
main() ProgramThe actual program does little so far, first the initialization portion:
int x;
char sh_cmd[PATH_MAX];
static char *init_pth[] = {"/etc/init.d/","/sbin/init.d/","/etc/rc.d/",};
Only three variables:
int x - counter.char sh_cmd - what will hold the entire command
string.static char init_pth - a list of paths to try
out.The next step is to see if there is a path that matches one of
the paths defined in the init_pth list:
for (x = 0; x >= sizeof(init_pth); x++) {
if (check_handle(init_pth[x]) == 0) {
strcpy(sh_cmd, init_pth[x]);
break;
} else {
printf("Could not find an init path\n");
return 1;
}
}
init_pth
list.check_handle() to see if
the path that is being examined exists.sh_cmd string
and break out of the loop (stop checking).main()) 1.Time to build up the string, this is done by simply
concatenating onto sh_cmd argument 1, a space,
and argument 2 thus creating a syntax of service
service_name operation:
strcat(sh_cmd, argv[1]);
strcat(sh_cmd, " ");
strcat(sh_cmd, argv[2]);
The last operation, pass the entire string to the shell and return a success signal (0):
system(sh_cmd);
return 0;
With draft one of service in C done, it is time to look at some of the flaws.
service C Draftstrcat()The first glaring issue is the use of the strcat()
function. Concatenation onto a string with two possibly unknown
string lengths is generally not a good idea:
[jrf@vela:~/src/service-0.5$] ./a.out mmmmmmmmmmmmmmmmmm\ mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\ mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\ mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\ mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm\ mmmmmmmmm Segmentation fault
main() or Not?Should the directory check even be in a separate function? Is it
possible to perform the operation in a fast manner within the
confines of the main() routine? If a routine is needed
could recursion make it faster or would it add overhead that is not
necessary?
system() PragmaShould the system() function be checked? Is using
system() the safest way to execute the init
script? If there is an error how should it be handled?
There are several features from the original Perl version that are missing:
At first glance the C version of service seems simpler in code terms, however, next time a great deal of code and fixes have to be applied. Once the next version is finished, only after that will it be possible to do an equitable comparison.