Created
November 28, 2020 06:12
-
-
Save ObjectBoxPC/97c1836431ea6917f2e7d1802464adbf to your computer and use it in GitHub Desktop.
Simple program start a shell based on certain inferences (mainly to learn some POSIX C programming)
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
#include <unistd.h> | |
#include <pwd.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stddef.h> | |
/** | |
* @file | |
* Utility function to start a shell based on reasonable inferences | |
*/ | |
static pid_t shell_start_env(char* name, size_t len); | |
static pid_t shell_start_pwd(char* name, size_t len); | |
static pid_t shell_start_familiar(char* name, size_t len); | |
static pid_t shell_start_run( | |
const char* name, char* store_name, size_t store_len | |
); | |
/** | |
* Delegate function for starting a shell based on a specific inference | |
* @param name Buffer to store the name of the shell that was started. | |
* If this is a null pointer, the name is not stored. | |
* @param len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
*/ | |
typedef pid_t (*shell_start_delegate)(char* name, size_t len); | |
/** | |
* Delegate functions, in order of preference | |
*/ | |
static const shell_start_delegate SHELL_START_DELEGATES[] = { | |
&shell_start_env, | |
&shell_start_pwd, | |
&shell_start_familiar, | |
}; | |
/** | |
* "Familiar" shells to fall back on when other inferences fail | |
* @see shell_start_familiar | |
*/ | |
static const char* SHELL_START_FAMILIARS[] = { | |
"sh", | |
"csh", | |
"/bin/sh", | |
"/bin/csh", | |
}; | |
/** | |
* Get the size of an array. | |
* @param arr Array | |
*/ | |
#define SHELL_START_ARR_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | |
/** | |
* Start a shell based on reasonable inferences. | |
* @param name Buffer to store the name of the shell that was started. | |
* If this is a null pointer, the name is not stored. | |
* @param len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
*/ | |
pid_t shell_start(char* name, size_t len) { | |
pid_t shell_pid; | |
size_t i; | |
for (i = 0; i < SHELL_START_ARR_SIZE(SHELL_START_DELEGATES); ++i) { | |
if ((shell_pid = SHELL_START_DELEGATES[i](name, len)) > 0) { | |
return shell_pid; | |
} | |
} | |
return -1; | |
} | |
/** | |
* Start the shell indicated in the `SHELL` environment variable. If this | |
* variable is blank or not set, failure occurs. | |
* @param name Buffer to store the name of the shell that was started. | |
* If this is a null pointer, the name is not stored. | |
* @param len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
*/ | |
static pid_t shell_start_env(char* name, size_t len) { | |
const char* env_shell = getenv("SHELL"); | |
if (!env_shell || strlen(env_shell) == 0) { | |
return -1; | |
} | |
return shell_start_run(env_shell, name, len); | |
} | |
/** | |
* Start the effective user's login shell (from the user's `passwd` entry). | |
* @param name Buffer to store the name of the shell that was started. | |
* If this is a null pointer, the name is not stored. | |
* @param len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
*/ | |
static pid_t shell_start_pwd(char* name, size_t len) { | |
struct passwd* pw = getpwuid(geteuid()); | |
if (!pw) { | |
return -1; | |
} | |
return shell_start_run(pw->pw_shell, name, len); | |
} | |
/** | |
* Start a "familiar" shell, such as `sh`. Multiple shells are attempted | |
* in turn until a suitable one is found. | |
* @param name Buffer to store the name of the shell that was started. | |
* If this is a null pointer, the name is not stored. | |
* @param len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
* @warning Failure is not properly reported. If the shell fails to start, | |
* the child process will simply exit with a status of 1. | |
*/ | |
static pid_t shell_start_familiar(char* name, size_t len) { | |
size_t i; | |
pid_t child_pid; | |
for (i = 0; i < SHELL_START_ARR_SIZE(SHELL_START_FAMILIARS); ++i) { | |
if ( | |
(child_pid = shell_start_run(SHELL_START_FAMILIARS[i], name, len)) | |
> 0 | |
) { | |
return child_pid; | |
} | |
} | |
return -1; | |
} | |
/** | |
* Run a shell in a child process. | |
* @param name Name of shell to run | |
* @param store_name Buffer to store the name of the shell upon success. | |
* If this is a null pointer, the name is not stored. | |
* @param store_len Length of buffer, including room for the null terminator | |
* @return Process ID of the shell started, or -1 on failure | |
*/ | |
static pid_t shell_start_run( | |
const char* name, char* store_name, size_t store_len | |
) { | |
pid_t shell_pid; | |
shell_pid = fork(); | |
if (shell_pid > 0) { | |
/* Parent */ | |
if (store_name) { | |
store_name[0] = '\0'; | |
strncat(store_name, name, store_len - 1); | |
} | |
return shell_pid; | |
} else if (shell_pid == 0) { | |
/* Child */ | |
execlp(name, name, (const char*) 0); | |
/* Could not exec */ | |
_exit(1); | |
} else { | |
/* Could not fork */ | |
return -1; | |
} | |
} |
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
#include <stdio.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <stddef.h> | |
pid_t shell_start(char* name, size_t len); | |
int main(void) { | |
pid_t shell_pid; | |
int shell_status; | |
char shell_name[100]; | |
shell_pid = shell_start(shell_name, sizeof(shell_name)); | |
printf("Started %s with PID %ld\n", shell_name, (long) shell_pid); | |
waitpid(shell_pid, &shell_status, 0); | |
if (WIFEXITED(shell_status)) { | |
printf("Shell exited with status %d\n", WEXITSTATUS(shell_status)); | |
} else if (WIFSIGNALED(shell_status)) { | |
printf("Shell terminated by signal %d\n", WTERMSIG(shell_status)); | |
} else { | |
printf("Shell terminated in an unknown way\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment