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.
|
|
Rewriting a Perl util in C Part I
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.
Basic Requirements
- Determine the location of the init scripts
- The Perl version forces the administrator to specify the platform
using make directives and a helper shell script. The C version
will simply look for the directory based on a list of common paths.
- Build the entire string
- In the Perl version, the full path to the init script is built first,
then the path and operation are passed directly to the shell using the
system() function.
In the C version, the path and operation are built as one string
which is passed to the libc system() function.
- Perform the command
- The last step is simply passing the entire string to the
shell.
Includes and Prototype
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>
Helper Functions
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.
The main() Program
The 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) + 1; x++) {
if (check_handle(init_pth[x]) == 0) {
strcpy(sh_cmd, init_pth[x]);
break;
} else {
printf("Could not find an init pathn");
return 1;
}
}
- Set up a loop to iterate over the
init_pth list.
- Run the helper function
check_handle() to see if the
path that is being examined exists.
- If the path exists, copy it into the
sh_cmd string and
break out of the loop (stop checking).
- If no path is found print a simple error and return (essentially
exit since it is in
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.
Problems with service C Draft
Using strcat()
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
To Break from 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?
Missing Features
There are several features from the original Perl version
that are missing:
- A verbose option
- Listing the contents of the init directory
- Just showing the path to the init directory
Summary and Next Time
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 comparision.
The original version of this can be found at Systhread.net
Full Story |