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() Pragma
Should 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.
(based on last 2 months log reports)