A small Perl utility rewritten in C Part I

Posted by jayrfink on Jan 26, 2006 10:13 AM EDT
http://www.systhread.net/; By Jason (Jay) R Fink
Mail this story
Print this story

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

  Nav
» Read more about: Story Type: Tutorial; Groups:

« Return to the newswire homepage

This topic does not have any threads posted yet!

You cannot post until you login.