|
/* @file metamorphosis.c |
|
* @brief Simple reproduction of BusyBox's multi-command behavior |
|
* via argv[0]. One binary, many uses. |
|
* @author Umar Ba <jUmarB@protonmail.com> <github.com/Jukoo> |
|
* @music INTERWORLD - METAMORPHOSIS |
|
* */ |
|
|
|
#define _GNU_SOURCE |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#include <sys/cdefs.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <err.h> |
|
|
|
|
|
#define METAPATHBINS \ |
|
"/usr/bin" \ |
|
":/usr/sbin" \ |
|
":/usr/local/sbin" \ |
|
":/usr/local/bin" /*You can put more paths... */ |
|
|
|
#define RLS 0x2f2e /* For relative launch symbole "./" #dot-slashed */ |
|
|
|
#define __not(__rc) (!(~0 ^ __rc)) |
|
#define perr(__fsyscall , ... ) \ |
|
do{perror(#__fsyscall) ;err(errno , __VA_ARGS__); }while(0) |
|
|
|
struct __metamorph_t { |
|
char *_progbn ; /* The Target program basename */ |
|
char **_xtrargv ; /* Extra arguments after program basename */ |
|
} metamorph ; |
|
|
|
/* |
|
* @fn in_systempathbin(const char * ) |
|
* @brief check if the passed command is available in path (see METAPATHBINS macro) |
|
* you can add more path ... |
|
* @param const char * - the target command that you look for. |
|
* @return int 0 OK otherwise ~0 not found |
|
*/ |
|
|
|
int in_systempathbin(const char * target) |
|
{ |
|
int is_cmdbuiltin = ~0; |
|
char *syspath =(char *) &(const char[]) {METAPATHBINS}, |
|
*token = (__ptr_t )00, |
|
binloc[0xff]={0}; |
|
|
|
while ((__ptr_t)00 !=(token = strtok(syspath, ":"))) |
|
{ |
|
if(syspath) syspath=(char *)00; |
|
|
|
sprintf(binloc , "%s/%s",token , target) ; |
|
if(__not(access(binloc , F_OK|X_OK))) |
|
{ |
|
bzero(binloc , strlen(binloc)); |
|
continue ; |
|
} |
|
is_cmdbuiltin^=is_cmdbuiltin; |
|
metamorph._progbn =strdup(binloc); |
|
break ; |
|
} |
|
|
|
return is_cmdbuiltin ; |
|
} |
|
|
|
/* |
|
* @fn program_basename(const char **) |
|
* @brief checks the base name of the program and preformats it by removing |
|
* the “./” see (RLS macro). Here are two scenarios |
|
1) The base name of the program is the executable itself but not in the system binary path |
|
in this case, you must pass a valid command as 1 parameter |
|
for example ./exec ls -l |
|
the “ls -l” will be executed |
|
2) The base of the program has exactly the same name as one of the commands available on your system. |
|
In this case, the program will act in the same way as your named command |
|
|
|
* @param const char ** - the argument vector |
|
* @return int 0 OK otherwise -1 |
|
*/ |
|
|
|
int program_basename(const char **av ) |
|
{ |
|
metamorph._progbn = (char*)*(av) ; |
|
/* check if the program launch start with ./ #dot-slashed */ |
|
unsigned int start_with = *metamorph._progbn | *(metamorph._progbn+1) << 8 ; |
|
if (!(start_with & 0xffff) ^ RLS) |
|
/* prettyfy the program basename without the './' #dot-slashed */ |
|
metamorph._progbn = (metamorph._progbn+02) ; |
|
|
|
/* See if the program basename is available in system binary path */ |
|
return in_systempathbin(metamorph._progbn) ; |
|
} |
|
|
|
int main(int ac , char **av , char **env) |
|
{ |
|
int pstatus = EXIT_SUCCESS ; |
|
/* See if the program basename is an builtin command */ |
|
int builtin = program_basename((const char ** )av) ; |
|
|
|
if (__not(builtin)) |
|
{ |
|
/* |
|
If the basename executable is not a built-in command, |
|
it automatically checks whether the user provides a valid command as the first argument. |
|
*/ |
|
|
|
/*See if the program receive a valid argument*/ |
|
if(!(ac & ~(1))) |
|
{ |
|
fprintf(stdout , " %s : USAGE : <COMMAND> [ARGS...]\n", metamorph._progbn); |
|
return EXIT_SUCCESS; |
|
} |
|
|
|
metamorph._xtrargv = (av+1); |
|
if (__not(in_systempathbin(*metamorph._xtrargv))) |
|
{ |
|
perr(lookup_systempathbin , "Not able to found this '%s' command\n",*(av +1)) ; |
|
free(metamorph._progbn) ; |
|
metamorph._progbn = (__ptr_t)00 ; |
|
return EXIT_FAILURE ; |
|
} |
|
}else |
|
{ |
|
*(av) = metamorph._progbn ; |
|
metamorph._xtrargv = av ; |
|
} |
|
|
|
/* Creating Sandbox Execution using fork()) |
|
* INFO: If you want to go more futher for more precise control ... |
|
* please consult the manpage(2) of clone*/ |
|
pid_t sandbox = fork() ; |
|
if (__not(sandbox)) |
|
{ |
|
perr(fork , "Cannot create Sandbox execution\n") ; |
|
free(metamorph._progbn),metamorph._progbn=00; |
|
pstatus = EXIT_FAILURE ; |
|
} |
|
|
|
/* Sanbox Context Execution : a chilp process */ |
|
if(!(sandbox & 0xffff)) |
|
{ |
|
int sandbox_ctexec = execvpe(metamorph._progbn , metamorph._xtrargv,env); |
|
if(__not(sandbox_ctexec)) |
|
return errno; |
|
return 0 ; |
|
} |
|
|
|
/* Handle sandbox execution: */ |
|
if(sandbox & 0xffff) |
|
{ |
|
int status =0 ; |
|
wait(&status) ; |
|
/*TODO : Analyse return code status for clean exit */ |
|
free(metamorph._progbn) ; |
|
metamorph._progbn = (__ptr_t)00 ; |
|
} |
|
|
|
return pstatus ; |
|
} |