-
-
Save RogerGee/cb68c56ec16db7f0a8fe to your computer and use it in GitHub Desktop.
Background process launching program
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Launch by Roger Gee |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* launch.c */ | |
#include <sys/stat.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <pwd.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <assert.h> | |
#define MAX_ARGV 100 | |
static const char* PROGRAM_NAME; | |
static const int MAJOR_VERSION = 1; | |
static const int MINOR_VERSION = 1; | |
struct launch_options | |
{ | |
int logFile; /* redirect stdout to this */ | |
int errFile; /* redirect stderr to this */ | |
int inFile; /* redirect stdin to this */ | |
short doChdir; /* if non-zero then change directory to user home dir */ | |
char const* executable; | |
char const* argv[MAX_ARGV]; | |
int argc; | |
}; | |
static int launch_options_load(struct launch_options*); | |
static int launch_options_unload(struct launch_options*); | |
static int process_option(const char* option,const char* argument,struct launch_options* options); | |
static int detach(struct launch_options* options); | |
static int start_process(struct launch_options* options); | |
static int open_log(int* pfd,const char* fileName); | |
static int option_help(); | |
static int option_version(); | |
int main(int argc,const char** argv) | |
{ | |
int i; | |
struct launch_options options; | |
PROGRAM_NAME = argv[0]; | |
i = 1; | |
launch_options_load(&options); | |
while (i < argc) | |
{ | |
/* options may have the form: | |
-o [argument] OR --option[=argument]; they must | |
appear before the executable name */ | |
if (options.executable == NULL) | |
{ | |
if (argv[i][0]=='-' && argv[i][1]) | |
{ | |
int code; | |
const char* opt = argv[i]+1; | |
char option[256]; | |
char argument[256]; | |
option[0] = 0; | |
argument[0] = 0; | |
if (*opt == '-') | |
{ | |
int j; | |
++opt; | |
j = 0; | |
while (opt[j] && opt[j]!='=' && j<256) | |
++j; | |
strncpy(option,opt,j); | |
option[j] = 0; | |
opt += j; | |
if (*opt) | |
++opt; | |
} | |
else | |
{ | |
option[0] = *opt; | |
option[1] = 0; | |
++opt; | |
} | |
/* try the argument first to see if it needs an argument */ | |
code = process_option(option,NULL,&options); | |
if (code == 2) { | |
if (*opt && *opt!='-') | |
strncpy(argument,opt,256); | |
else if (++i<argc && argv[i][0]!='-') | |
strncpy(argument,argv[i],256); | |
code = process_option(option,argument,&options); | |
} | |
if (code == 1) { | |
launch_options_unload(&options); | |
return 1; | |
} | |
} | |
else | |
options.executable = argv[i]; | |
} | |
else | |
break; | |
++i; | |
} | |
if (options.executable == NULL) { | |
fprintf(stderr,"%s: error: expected executable name\n",argv[0]); | |
return 1; | |
} | |
options.argv[options.argc++] = options.executable; | |
for (;i<argc;i++,options.argc++) | |
{ | |
if (options.argc+1 > MAX_ARGV) { | |
fprintf(stderr,"%s: warning: max argument count has been succeeded\n",argv[0]); | |
options.argc = MAX_ARGV-1; | |
break; | |
} | |
options.argv[options.argc] = argv[i]; | |
} | |
options.argv[options.argc] = NULL; /* add null pointer to terminate argument array */ | |
/* detach from terminal */ | |
if (detach(&options) != 0) { | |
launch_options_unload(&options); | |
return 1; | |
} | |
/* invoke process; assume detach() unloaded options to close file descriptors */ | |
return start_process(&options); | |
} | |
int launch_options_load(struct launch_options* options) | |
{ | |
options->logFile = -1; | |
options->errFile = -1; | |
options->inFile = -1; | |
options->doChdir = 1; | |
options->executable = NULL; | |
options->argv[0] = NULL; | |
options->argc = 0; | |
return 0; | |
} | |
int launch_options_unload(struct launch_options* options) | |
{ | |
if (options->logFile != -1) { | |
close(options->logFile); | |
options->logFile = -1; | |
} | |
if (options->errFile != -1) { | |
close(options->errFile); | |
options->errFile = -1; | |
} | |
if (options->inFile != -1) { | |
close(options->inFile); | |
options->inFile = -1; | |
} | |
return 0; | |
} | |
int process_option(const char* option,const char* argument,struct launch_options* options) | |
{ | |
/* return 1 to mean quit program, 2 to mean argument needed */ | |
if (strcmp(option,"o")==0 || strcmp(option,"redirect-output")==0) { | |
if (argument == NULL) | |
return 2; | |
if (!*argument) { | |
fprintf(stderr,"%s: option '%s' requires argument\n",PROGRAM_NAME,option); | |
return 1; | |
} | |
return open_log(&options->logFile,argument); | |
} | |
if (strcmp(option,"e")==0 || strcmp(option,"redirect-error")==0) { | |
if (argument == NULL) | |
return 2; | |
if (!*argument) { | |
fprintf(stderr,"%s: option '%s' requires argument\n",PROGRAM_NAME,option); | |
return 1; | |
} | |
return open_log(&options->errFile,argument); | |
} | |
if (strcmp(option,"i")==0 || strcmp(option,"redirect-input")==0) { | |
if (argument == NULL) | |
return 2; | |
if (!*argument) { | |
fprintf(stderr,"%s: option '%s' requires argument\n",PROGRAM_NAME,option); | |
return 1; | |
} | |
return open_log(&options->inFile,argument); | |
} | |
if (strcmp(option,"d")==0 || strcmp(option,"curdir")==0) | |
options->doChdir = 0; /* keep the current directory */ | |
else if (strcmp(option,"?")==0 || strcmp(option,"help")==0) { | |
option_help(); | |
return 1; | |
} | |
else if (strcmp(option,"version") == 0) { | |
option_version(); | |
return 1; | |
} | |
else { | |
fprintf(stderr,"%s: option '%s' is not supported\n",PROGRAM_NAME,option); | |
return 1; | |
} | |
return 0; | |
} | |
int detach(struct launch_options* options) | |
{ | |
/*int i;*/ | |
struct passwd* pwd; | |
pid_t pid = fork(); | |
int fdnull, fdout, fderr; | |
if (pid == -1) | |
{ | |
fprintf(stderr,"%s: fail fork()\n",PROGRAM_NAME); | |
return 1; | |
} | |
if (pid != 0) | |
_exit(0); | |
if (setsid() == -1) | |
{ | |
fprintf(stderr,"%s: fail setsid()\n",PROGRAM_NAME); | |
return 1; | |
} | |
pwd = getpwuid( getuid() ); | |
if (pwd == NULL) | |
{ | |
fprintf(stderr,"%s: fail getpwuid() for uid %d\n",PROGRAM_NAME,getuid()); | |
return 1; | |
} | |
if (options->doChdir && chdir(pwd->pw_dir)==-1) | |
{ | |
fprintf(stderr,"%s: fail chdir()\n",PROGRAM_NAME); | |
return 1; | |
} | |
/* open null device */ | |
fdnull = open("/dev/null",O_RDWR); | |
if (fdnull == -1) | |
{ | |
fprintf(stderr,"%s: fail open() on /dev/null\n",PROGRAM_NAME); | |
return 1; | |
} | |
fdout = options->logFile==-1 ? fdnull : options->logFile; | |
fderr = options->errFile==-1 ? fdnull : options->errFile; | |
/* redirect standard IO to null device or log file(s) */ | |
assert(dup2(fdnull,STDIN_FILENO) == STDIN_FILENO); | |
assert(dup2(fdout,STDOUT_FILENO) == STDOUT_FILENO); | |
assert(dup2(fderr,STDERR_FILENO) == STDERR_FILENO); | |
close(fdnull); | |
launch_options_unload(options); /* close any log file descriptors */ | |
return 0; | |
} | |
int start_process(struct launch_options* options) | |
{ | |
if (execvp(options->executable,(char*const*)options->argv) == -1) | |
{ | |
fprintf(stderr,"%s: could not start '%s' process\n",PROGRAM_NAME,options->executable); | |
return 1; | |
} | |
/* control no longer in this program */ | |
return 0; | |
} | |
int open_log(int* pfd,const char* fileName) | |
{ | |
*pfd = open(fileName,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR); | |
if (*pfd == -1) | |
{ | |
fprintf(stderr,"%s: error: could not open file '%s'",PROGRAM_NAME,fileName); | |
if (errno == ENOTDIR) | |
fprintf(stderr,": part of the path does not exist\n"); | |
else if (errno == EACCES) | |
fprintf(stderr,": permission denied\n"); | |
return 1; | |
} | |
return 0; | |
} | |
int option_help() | |
{ | |
static const char* helpMessage = | |
"launch by Roger Gee\n\n\ | |
launches a process detached from the terminal\n\ | |
usage: %s [[-]-option[=] [argument]]\n\n\ | |
options:\n\ | |
--redirect-output=file-name\t- redirect process output to file\n\ | |
-o file-name\n\ | |
--redirect-error=file-name\t- redirect process error to file\n\ | |
-e file-name\n\ | |
--redirect-input=file-name\t- redirect process input to file\n\ | |
-i file-name\n\ | |
--curdir\t\t\t- keep current directory for process\n\ | |
-d\n"; | |
printf(helpMessage,PROGRAM_NAME); | |
return 0; | |
} | |
int option_version() | |
{ | |
printf("launch by Roger Gee\nversion %d.%d\n", | |
MAJOR_VERSION,MINOR_VERSION); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment