Related
I have an application running on an unprivileged user, but at some point this program needs to run another one as a root, would be nice if I can reuse a configured PAM module, like, su, sudo, login or anyone.
So I am trying to write some code to authenticate the root and run this program using PAM, like sudo does, but I cant ask for password, it needs to be automatic. This unprivileged program in a specific time will have the access to root password.
Tried this example here https://www.netbsd.org/docs/guide/en/chap-pam.html but on pam_authenticate, it always return PAM_AUTH_ERR, I have tried all configured modules on my ubuntu 18.04.
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <sys/wait.h>
#include <err.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <grp.h>
#include <assert.h>
#include <string>
#include <vector>
int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data)
{
struct pam_response *aresp;
char buf[PAM_MAX_RESP_SIZE];
int i;
data = data;
if (n <= 0 || n > PAM_MAX_NUM_MSG)
return (PAM_CONV_ERR);
if ((aresp = (struct pam_response *) calloc(n, sizeof *aresp)) == NULL)
return (PAM_BUF_ERR);
for (i = 0; i < n; ++i) {
aresp[i].resp_retcode = 0;
aresp[i].resp = NULL;
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
//aresp[i].resp = strdup(getpass(msg[i]->msg));
aresp[i].resp = strdup("mypass");
aresp[i].resp_retcode = 0;
if (aresp[i].resp == NULL)
goto fail;
break;
case PAM_PROMPT_ECHO_ON:
fputs(msg[i]->msg, stderr);
if (fgets(buf, sizeof buf, stdin) == NULL)
goto fail;
aresp[i].resp = strdup(buf);
if (aresp[i].resp == NULL)
goto fail;
break;
case PAM_ERROR_MSG:
fputs(msg[i]->msg, stderr);
if (strlen(msg[i]->msg) > 0 &&
msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
fputc('\n', stderr);
break;
case PAM_TEXT_INFO:
fputs(msg[i]->msg, stdout);
if (strlen(msg[i]->msg) > 0 &&
msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
fputc('\n', stdout);
break;
default:
goto fail;
}
}
*resp = aresp;
return (PAM_SUCCESS);
fail:
for (i = 0; i < n; ++i) {
if (aresp[i].resp != NULL) {
memset(aresp[i].resp, 0, strlen(aresp[i].resp));
free(aresp[i].resp);
}
}
memset(aresp, 0, n * sizeof *aresp);
*resp = NULL;
return (PAM_CONV_ERR);
}
static struct pam_conv conv = {
converse,
//misc_conv,
NULL
};
extern char **environ;
static pam_handle_t *pamh;
static struct pam_conv pamc;
static void
usage(void)
{
fprintf(stderr, "Usage: su [login [args]]\n");
exit(1);
}
int
main(int argc, char *argv[])
{
char hostname[64];
const char *user, *tty;
char **args, **pam_envlist, **pam_env;
struct passwd *pwd;
int o, pam_err, status;
pid_t pid;
while ((o = getopt(argc, argv, "h")) != -1)
switch (o) {
case 'h':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc > 0) {
user = *argv;
--argc;
++argv;
} else {
user = "root";
}
int pam_status = PAM_SUCCESS;
/* initialize PAM */
//pamc.conv = &openpam_ttyconv;
if ((pam_status = pam_start("passwd", user, &conv, &pamh)) != PAM_SUCCESS)
{
assert(false);
}
/* set some items */
gethostname(hostname, sizeof(hostname));
if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
{
assert(false);
}
user = getlogin();
if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
{
assert(false);
}
tty = ttyname(STDERR_FILENO);
if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
{
assert(false);
}
/* authenticate the applicant */
pam_err = pam_authenticate(pamh, PAM_SILENT);
if (pam_err != PAM_SUCCESS)
{
printf("Pam Error (%d)\n", pam_err);
warn("pam_authenticate");
assert(false);
}
printf("AUTHENTICATED ;-)");
assert(false);
if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (pam_err != PAM_SUCCESS)
{
assert(false);
}
/* establish the requested credentials */
if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
{
assert(false);
}
/* authentication succeeded; open a session */
if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
{
assert(false);
}
/* get mapped user name; PAM may have changed it */
pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
{
assert(false);
}
/* export PAM environment */
if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
putenv(*pam_env);
free(*pam_env);
}
free(pam_envlist);
}
std::vector<std::string> arguments;
arguments.resize(argc + 2);
char * args_ptr [arguments.size()];
arguments[0] = pwd->pw_shell;
args_ptr[argc +1] = NULL;
args_ptr[0] = (char *)arguments[0].c_str();
for (int i = 0; i < argc; i++)
{
arguments[i + 1] = argv[i];
args_ptr[i+1] = (char *)arguments[i+1].c_str();
}
/* set uid and groups */
if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
warn("initgroups()");
_exit(1);
}
if (setgid(pwd->pw_gid) == -1) {
warn("setgid()");
_exit(1);
}
if (setuid(pwd->pw_uid) == -1) {
warn("setuid()");
_exit(1);
}
execve(args_ptr[0], args_ptr, environ);
warn("execve()");
_exit(1);
pamerr:
fprintf(stderr, "Sorry\n");
err:
pam_end(pamh, pam_err);
exit(1);
}
I expect to fork in a elevated child and run my new program, without asking for password.
I wrote a program for an assignment I have, but I made a mistake and set as
constant the number of users that this program can work with because of that struct limitation. I tried to do some realloc to expand the struct but it didn't work, and I got:
gcc: error: O"SxW��D(/��A>A�0: No such file or directory
gcc: fatal error: no input files
compilation terminated.
Here is my code with todos near the relevant lines, I would appreciate very much a method that will help me to make that code less rigid with relation to the number of users. Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <memory.h>
#include <unistd.h>
#include <wait.h>
#define MAX_LENGTH 160
#define STDERR_FILDES 2
#define OPERATION_FAILED (-1)
#define BUFFER_SIZE 512
#define CONFIG_PLACE 1
#define CORRECT_ARGS_NUM 2
#define WRONG_ARGS_NUM_MSG "Wrong number of program arguments. Please enter 2 arguments."
#define SPACE_CHAR ' '
#define NEW_LINE_CHAR '\n'
#define NULL_CHAR '\0'
#define NULL_STR "\0"
#define SLASH_CHAR '/'
#define SLASH_STR "/"
#define CURR_DIR "."
#define PAR_DIR ".."
#define C_EXTENT "c"
#define EXT_DOT '.'
#define NUM_OF_USERS 5 //todo: here I define the rigid number of users.
#define ZERO_SCORE "0"
#define SON 0
#define NO_FILE "NO_C_FILE"
#define TIMEOUT "TIMEOUT"
#define COMP_ERR "COMPILATION_ERROR"
#define SIXTY_SCORE "60"
#define BAD_OUT "BAD_OUTPUT"
#define EIGHTY_SCORE "80"
#define SIM_OUT "SIMILAR_OUTPUT"
#define HUNDRED_SCORE "100"
#define GREAT_OUT "GREAT_JOB"
#define EQUAL 0
#define EXE_FILE_NAME "temp.out"
#define RUN_FILE_NAME "./temp.out"
#define COMP_FILE_NAME "./comp.out"
#define RES_FILE_NAME "res.txt"
#define OPER "gcc"
#define OPT_FLAG "-o"
#define RESULTS_FILE_REL_PATH "results.csv"
#define RESULTS_FILE_PERMISSIONS 0644
#define CSV_LINE_MAX_LEN 500
#define CSV_SEPARATOR ","
#define CSV_END_LINE "\n"
#define SECONDS_TO_SLEEP 5
#define WAIT_FOR_ANY_CHILD 0
#define IDENTICAL 3
#define SIMILAR 2
#define DIFFERENT 1
enum Bool
{
false = 0,
true = 1
};
typedef struct User {
char name[MAX_LENGTH];
char filePath[MAX_LENGTH];
char dirPath[MAX_LENGTH];
char score[MAX_LENGTH];
char scoreInfo[MAX_LENGTH];
} User;
/**
* Prints an error message using write function to file descriptor
* number 2 (stderr), and exits with error status -1 (translates to 255).
*/
void sysCallFailureProcedure() {
char errorMsg[] = "Error in system call\n";
write(STDERR_FILDES, errorMsg, sizeof(errorMsg)-1);
exit(OPERATION_FAILED);
}
/**
* Get the file's extension.
* #param filename The filename.
* #return The file's extension.
*/
char *getFilenameExt(char *filename) {
char *dot = strrchr(filename, EXT_DOT);
if(!dot || dot == filename) return "";
return dot + 1;
}
/**
* Get the configurations from the configurations file.
* #param configfilePath The configurations file.
* #param mainDirPath The main directory path.
* #param inputFilePath The input file path.
* #param correctResFilePath The correct result file path.
*/
void getConfigFromFile(char* mainDirPath, char* inputFilePath, char* correctResFilePath, char* configfilePath){
int configFileDes = open(configfilePath, O_RDONLY);
if (configFileDes < 0){
sysCallFailureProcedure();
}
int retVal;
char buff[BUFFER_SIZE];
while ((retVal = read(configFileDes, buff, sizeof(buff))) > 0){
int lineNumber = 1;
int lineIdx = 0;
int i=0;
for (; i<retVal; i++) {
if (lineNumber == 1){
if (buff[i] == NEW_LINE_CHAR || buff[i] == SPACE_CHAR || buff[i] == NULL_CHAR) {
lineNumber = 2;
lineIdx = 0;
} else {
mainDirPath[lineIdx] = buff[i];
lineIdx++;
}
}
else if (lineNumber == 2){
if (buff[i] == NEW_LINE_CHAR || buff[i] == SPACE_CHAR || buff[i] == NULL_CHAR) {
lineNumber = 3;
lineIdx = 0;
} else {
inputFilePath[lineIdx] = buff[i];
lineIdx++;
}
}
else if (lineNumber == 3){
if (buff[i] != NEW_LINE_CHAR && buff[i] != SPACE_CHAR && buff[i] != NULL_CHAR) {
correctResFilePath[lineIdx] = buff[i];
lineIdx++;
}
}
}
}
if (retVal < 0){
sysCallFailureProcedure();
}
if (close(configFileDes) < 0){
sysCallFailureProcedure();
}
}
/**
* Get the C file.
* #param user The user.
* #param userNum The user Num.
* #param directory The directory.
* #param filePath The file path.
*/
void getCFile(User* user, int userNum, char* directory,char* filePath){
DIR* pDir = opendir(directory);
if (pDir == NULL){
sysCallFailureProcedure();
}
struct dirent* pDirent;
while((pDirent = readdir(pDir))!=NULL) {
if ((pDirent->d_type == DT_DIR) && (strcmp(pDirent->d_name, CURR_DIR) != 0)
&& (strcmp(pDirent->d_name, PAR_DIR) != 0)){
char dir[MAX_LENGTH]={};
strcpy(dir,directory);
int length = strlen(dir);
if (dir[length] != SLASH_CHAR) {
strcat(dir, SLASH_STR);
}
strcat(dir, pDirent->d_name);
getCFile(user, userNum, dir, filePath);
} else if (pDirent->d_type == DT_REG) {
char* extension = getFilenameExt(pDirent->d_name);
if(strcmp(extension,C_EXTENT) == 0) {
strcpy(filePath, pDirent->d_name);
strcpy(user[userNum].dirPath, directory);
break;
}
}
}
}
/**
* Process the main directory.
* #param users The users.
* #param userNum The user num.
* #param mainDirPath The main directory path.
*/
void processMainDir(User* users, int* userNum, char* mainDirPath){
DIR* pDir = opendir(mainDirPath);
if (pDir == NULL){
sysCallFailureProcedure();
}
struct dirent* pDirent;
int usersSize = NUM_OF_USERS; // todo: remove if may...
int userSize = sizeof(struct User);
while ((pDirent = readdir(pDir))!=NULL) {
if ((pDirent->d_type == DT_DIR) && (strcmp(pDirent->d_name, CURR_DIR) != 0)
&& (strcmp(pDirent->d_name, PAR_DIR) != 0)){
strcpy(users[*userNum].name, pDirent->d_name);
char path[MAX_LENGTH] = {};
char dir[MAX_LENGTH]={};
strcpy(dir, mainDirPath);
int length = strlen(dir);
if (dir[length] != SLASH_CHAR) {
strcat(dir, SLASH_STR);
}
strcat(dir, pDirent->d_name);
getCFile(users, *userNum, dir, path);
strcpy(users[*userNum].filePath, path);
(*userNum)++;
if ((*userNum) >= usersSize){
usersSize += NUM_OF_USERS;
users = (User*) realloc(users, usersSize * userSize); // todo: check it out. it doesn't work!
if (users == NULL){
sysCallFailureProcedure();
}
}
}
}
if (closedir(pDir) == OPERATION_FAILED){
sysCallFailureProcedure();
}
}
/*
* Return true if there is an executable file.
*/
enum Bool isThereExecutable(){
char workingDir[BUFFER_SIZE];
DIR* pDir;
struct dirent* pDirent;
getcwd(workingDir, sizeof(workingDir));
if ((pDir = opendir(workingDir)) == NULL){
sysCallFailureProcedure();
}
while ((pDirent = readdir(pDir)) != NULL){
if (strcmp(pDirent->d_name, EXE_FILE_NAME) == EQUAL){
return true;
}
}
return false;
}
/**
* Fill up the results file.
* #param resultsFilePath The path to the results file.
* #param userNum The number of users.
* #param users The users struct.
*/
void fillTheResultsFile(char* resultsFilePath, int userNum, User* users){
int resultsFileDes = open(resultsFilePath, O_WRONLY | O_CREAT | O_TRUNC, RESULTS_FILE_PERMISSIONS);
if (resultsFileDes < 0){
sysCallFailureProcedure();
}
int i;
for (i=0; i<userNum; i++) {
char line[CSV_LINE_MAX_LEN] = {};
strcpy(line, users[i].name);
strcat(line, CSV_SEPARATOR);
strcat(line, users[i].score);
strcat(line, CSV_SEPARATOR);
strcat(line, users[i].scoreInfo);
if (i < userNum -1){
strcat(line, CSV_END_LINE);
}
if (write(resultsFileDes, line, sizeof(line)-1) < 0){
sysCallFailureProcedure();
}
}
if (close(resultsFileDes) < 0){
sysCallFailureProcedure();
}
}
/**
* Execute a file.
* #param argv The file's args.
*/
void executeFile(char* argv[]){
pid_t pId = fork();
if (pId == SON) {
int retVal = execvp(argv[0], &argv[0]);
if (retVal == OPERATION_FAILED) {
sysCallFailureProcedure();
}
}else{
waitpid(pId, NULL, WCONTINUED);
}
}
void executeProg(User* users, int userNum, char* inputFilePath, char* correctResFilePath){
char* runArgs[] = {RUN_FILE_NAME, NULL};
int status;
pid_t pId = fork();
if (pId == SON){
int inputFileFileDes = open(inputFilePath, O_RDONLY);
if (inputFileFileDes < 0){
sysCallFailureProcedure();
}
int resFileFileDes = open(RES_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, RESULTS_FILE_PERMISSIONS);
if (resFileFileDes < 0){
sysCallFailureProcedure();
}
if (dup2(inputFileFileDes, STDIN_FILENO) < 0){
sysCallFailureProcedure();
}
if (dup2(resFileFileDes, STDOUT_FILENO) < 0){
sysCallFailureProcedure();
}
if (execvp(runArgs[0], &runArgs[0]) < 0){
sysCallFailureProcedure();
}
if (close(inputFileFileDes) < 0){
sysCallFailureProcedure();
}
if (close(resFileFileDes) < 0){
sysCallFailureProcedure();
}
} else {
sleep(SECONDS_TO_SLEEP);
if (waitpid(pId, &status, WNOHANG) == 0){
strcpy(users[userNum].scoreInfo, TIMEOUT);
strcpy(users[userNum].score, ZERO_SCORE);
} else {
char* copmArguments[] = {COMP_FILE_NAME , RES_FILE_NAME, correctResFilePath, NULL};
pid_t pId2 = fork();
if (pId2 == SON){
if (execvp(copmArguments[0], &copmArguments[0]) < 0){
sysCallFailureProcedure();
}
} else{
waitpid(pId2, &status, WAIT_FOR_ANY_CHILD);
if (WIFEXITED(status)){
int compRes = WEXITSTATUS(status);
if (compRes == IDENTICAL){
strcpy(users[userNum].scoreInfo, GREAT_OUT);
strcpy(users[userNum].score, HUNDRED_SCORE);
} else if (compRes == SIMILAR){
strcpy(users[userNum].scoreInfo, SIM_OUT);
strcpy(users[userNum].score, EIGHTY_SCORE);
} else if (compRes == DIFFERENT){
strcpy(users[userNum].scoreInfo, BAD_OUT);
strcpy(users[userNum].score, SIXTY_SCORE);
}
}
if (unlink(EXE_FILE_NAME) < 0){
sysCallFailureProcedure();
}
if (unlink(RES_FILE_NAME) < 0){
sysCallFailureProcedure();
}
}
}
}
}
/**
* Get the scores and score info's.
* #param inputFilePath The input file path.
* #param correctResFilePath The correct result file path.
* #param users The users.
* #param userNum The user num.
*/
void getScoresAndInfo(char* inputFilePath, char* correctResFilePath, User* users, int userNum){
int i;
for (i=0; i<userNum; i++) {
if (strcmp(NULL_STR, users[i].filePath) == EQUAL){
strcpy(users[i].scoreInfo, NO_FILE);
strcpy(users[i].score, ZERO_SCORE);
} else {
char CompletePathToFile[MAX_LENGTH] = {};
strcpy(CompletePathToFile, users[i].dirPath);
strcat(CompletePathToFile, SLASH_STR);
strcat(CompletePathToFile, users[i].filePath);
char* executeArguments[] = {OPER, OPT_FLAG, EXE_FILE_NAME, CompletePathToFile, NULL};
executeFile(executeArguments);
if (!isThereExecutable()){
strcpy(users[i].scoreInfo, COMP_ERR);
strcpy(users[i].score, ZERO_SCORE);
} else {
executeProg(users, i, inputFilePath, correctResFilePath);
}
}
}
}
int main(int argc, char *argv[]) {
if (argc != CORRECT_ARGS_NUM) {
printf(WRONG_ARGS_NUM_MSG);
exit(OPERATION_FAILED);
}
char mainDirPath[MAX_LENGTH] = {};
char inputFilePath[MAX_LENGTH] = {};
char correctResFilePath[MAX_LENGTH] = {};
getConfigFromFile(mainDirPath, inputFilePath,correctResFilePath, argv[CONFIG_PLACE]);
User* users = (User*)malloc(sizeof(User) * NUM_OF_USERS); // todo: here I malloc the users struct
int idx = 0;
if (users == NULL){
sysCallFailureProcedure();
}
processMainDir(users, &idx, mainDirPath);
getScoresAndInfo(inputFilePath, correctResFilePath, users, idx);
fillTheResultsFile(RESULTS_FILE_REL_PATH, idx, users);
}
I wrote my parallel version of unix grep program. In this program you search for patter in files given as program arguments.
In root process I calculate number of files per process and first file ID for each process. After calculations I send this values to each of root processes.
In not root process I'm receiving num of Files and first file ID and analyzing each file in analyzeFile method. In that method I send back string, which contains all lines with pattern, to root process. After collecting all data from not root precesses I print out final result.
The problem is when I try to run program with more than 3 processes. It ends up with segmentation fault.
I'm out of ideas. Valgrind does not show any error reports on base scanning.
PS. I know that code needs refactoring, but first I want to get rid of this bug.
Thanks for any help.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <mpi.h>
#define YEL "\x1B[33m"
#define BLU "\x1B[34m"
#define MAG "\x1B[35m"
#define RESET "\x1B[0m"
#define send_data_tag 1000
#define return_data_tag 1001
struct linesRange
{
int lineFrom;
int lineTo;
int posFrom;
int posTo;
};
int countNumOfLines(char *str)
{
int numOfLines = 0;
int i = 0;
while(str[i] != '\0')
{
if(str[i] == '\n')
{
++numOfLines;
}
++i;
}
return numOfLines;
}
long int findLinePos(char *str, int lineNumToFind)
{
long int linePos = 0;
long int lineNum = 0;
long int i = 0;
if(lineNumToFind == 0)
{
return linePos;
}
while(str[i] != '\0')
{
if(str[i] == '\n')
{
++lineNum;
if(lineNum == lineNumToFind)
{
linePos = i;
break;
}
}
++i;
}
return linePos;
}
long int findNextLinePos(char *str, int startPos)
{
long int i = startPos;
long int nextLinePos = 0;
while(str[i] != '\0')
{
if(str[i] == '\n')
{
nextLinePos = i;
break;
}
++i;
}
return nextLinePos;
}
bool strstrn(char *str, char *pattern, long int posStart, long int posEnd)
{
long int pos_search = 0;
long int pos_text = 0;
int len_search = strlen(pattern);
for (pos_text = posStart; pos_text < posEnd; ++pos_text)
{
if(str[pos_text] == pattern[pos_search])
{
++pos_search;
if(pos_search == len_search)
{
return true;
}
}
else
{
pos_text -= pos_search;
pos_search = 0;
}
}
return false;
}
char * searchPatternInText(char *text, char *inputPattern, char *fileName, int proc_id)
{
char *bufToAdd;
int lineNum = 0;
char * resultBuf = (char*)malloc(sizeof(char));
memset(resultBuf, 0, sizeof(char));
char * curLine = text;
while(curLine)
{
++lineNum;
char * nextLine = strchr(curLine, '\n');
if (nextLine)
*nextLine = '\0';
if(strstr(curLine, inputPattern))
{
bufToAdd = (char*)malloc(sizeof(char));
memset(bufToAdd, 0, sizeof(char));
asprintf (&bufToAdd, BLU "Ścieżka:%s\tProces:%d\n" MAG "Numer linii:%d\t" RESET "%s\n", fileName, proc_id, lineNum, curLine);
resultBuf = (char*)realloc(resultBuf, strlen(resultBuf) + strlen(bufToAdd) + 1);
strcat(resultBuf, bufToAdd);
free(bufToAdd);
}
if (nextLine)
*nextLine = '\n';
curLine = nextLine ? (nextLine+ 1) : NULL;
}
return resultBuf;
}
void analyzeFile(struct stat sb, int fd, char *fileName, char *pattern, int proc_id)
{
char *text = mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (text == MAP_FAILED) {
perror ("mmap");
return;
}
if (close (fd) == -1)
{
perror ("close");
return;
}
char *tempTxt;
tempTxt = (char*)malloc(sizeof(char) * (strlen(text) + 1));
strcpy(tempTxt, text);
tempTxt[strlen(text)] = '\0';
//munmap(text, sb.st_size);
char *result;
result = searchPatternInText(tempTxt, pattern, fileName, proc_id);
free(tempTxt);
if(proc_id != 0)
{
MPI_Send(result, strlen(result), MPI_CHAR,
0, return_data_tag, MPI_COMM_WORLD);
}
else
{
printf(result);
}
free(result);
}
int main (int argc, char *argv[])
{
int patternLen = 10;
char pattern [patternLen + 1];
float elapsed;
int numOfFiles = argc - 2;
int proc_id, num_procs, procIterator, filesIterator, root = 0;
int *numOfFilesPerProcess, *firstFileIdPerProcess;
if (argc < 3) {
fprintf (stderr, "usage: %s <file>\n", argv[0]);
return 1;
}
else
{
strncpy(pattern, argv[1], patternLen);
pattern[patternLen] = '\0';
}
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &proc_id);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Barrier(MPI_COMM_WORLD);
double startTime = MPI_Wtime();
if(proc_id == root)
{
MPI_Status statusMaster;
numOfFilesPerProcess = (int*)malloc(sizeof(int) * num_procs);
firstFileIdPerProcess = (int*)malloc(sizeof(int) * num_procs);
for(int i = 0; i < num_procs; ++i)
{
numOfFilesPerProcess[i] = 0;
firstFileIdPerProcess[i] = 0;
}
filesIterator = 0;
procIterator = 0;
while(filesIterator < numOfFiles)
{
if(procIterator == num_procs)
{
procIterator = 0;
}
++numOfFilesPerProcess[procIterator];
++procIterator;
++filesIterator;
}
firstFileIdPerProcess[0] = 2;
//pierwszy numer argumentu argv[] per proces
for(int jj = 1; jj < num_procs; ++jj)
{
firstFileIdPerProcess[jj] = firstFileIdPerProcess[jj - 1] + numOfFilesPerProcess[jj - 1];
}
for(int j = 1; j < num_procs; ++j)
{
MPI_Send(&firstFileIdPerProcess[j], 1, MPI_UNSIGNED,
j, send_data_tag, MPI_COMM_WORLD);
MPI_Send(&numOfFilesPerProcess[j], 1, MPI_UNSIGNED,
j, send_data_tag, MPI_COMM_WORLD);
}
int firstFileIdForProcess = firstFileIdPerProcess[0];
int numOfFilesForProcess = numOfFilesPerProcess[0];
int fd;
struct stat sb;
for(int i = 0; i < numOfFilesForProcess; ++i)
{
fd = open (argv[firstFileIdForProcess + i], O_RDONLY);
if ( fd == -1
|| fstat (fd, &sb) == -1
|| !S_ISREG (sb.st_mode))
{
perror("file");
}
else
{
analyzeFile(sb, fd, argv[firstFileIdForProcess + i], pattern, proc_id);
}
}
for(int ii = numOfFilesPerProcess[0]; ii < numOfFiles; ++ii)
{
int resultLen;
char *result;
MPI_Probe(MPI_ANY_SOURCE, return_data_tag, MPI_COMM_WORLD, &statusMaster);
MPI_Get_count(&statusMaster, MPI_CHAR, &resultLen);
result = (char *)malloc(sizeof(char) * resultLen + 1);
MPI_Recv( result, resultLen, MPI_CHAR,
MPI_ANY_SOURCE, return_data_tag, MPI_COMM_WORLD, &statusMaster);
result[resultLen] = '\0';
printf(result);
free(result);
}
free(numOfFilesPerProcess);
free(firstFileIdPerProcess);
}
else
{
MPI_Status statusSlave;
int firstFileIdForProcess = 0;
int numOfFilesForProcess = 0;
int fd;
struct stat sb;
MPI_Recv( &firstFileIdForProcess, 1, MPI_UNSIGNED,
root, send_data_tag, MPI_COMM_WORLD, &statusSlave);
MPI_Recv( &numOfFilesForProcess, 1, MPI_UNSIGNED,
root, send_data_tag, MPI_COMM_WORLD, &statusSlave);
for(int i = 0; i < numOfFilesForProcess; ++i)
{
fd = open (argv[firstFileIdForProcess + i], O_RDONLY);
if ( fd == -1
|| fstat (fd, &sb) == -1
|| !S_ISREG (sb.st_mode))
{
perror("file");
}
else
{
analyzeFile(sb, fd, argv[firstFileIdForProcess + i], pattern, proc_id);
}
}
}
MPI_Barrier(MPI_COMM_WORLD);
double endTime = MPI_Wtime();
if(proc_id == root)
{
printf("Czas wykonania: %fs\n", endTime - startTime);
}
MPI_Finalize();
return 0;
}
I am trying to implement something that will give me a solution for:
| --> cmd3 --> cmd4 -->
cmd2-->|
| --> cmd5 --> cmd6 -->
and so on...
This is multiple executions of processes and pipe the results via chains of other's processes with threads, each commands chain should run in different thread.
I choose socketpair for the implementation of IPC, because pipe has a a bottleneck with the buffer size limit 64K.
When I test the program with single chain - it's work as expected, but when I am running master command and the output of it I send via socketpair to read end of multiple processes in each thread - the program stuck (look like a deadlock)
Whats I am doing wrong:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/socket.h>
typedef struct command {
char** argv;
int num_children;
struct command* master_cmd;
struct command** chains;
struct command* next;
int fd;
} command;
void be_child(command* cmd);
int execute_master_command_and_pipe_to_childs(command* cmd, int input);
int run_pipeline_sockets(command *cmd, int input);
void waitfor(int fd);
int main(int argc, char* argv[]) {
handle_segfault();
command* cmd1 = (command*) malloc(sizeof(command));
command* cmd2 = (command*) malloc(sizeof(command));
command* cmd3 = (command*) malloc(sizeof(command));
command* cmd4 = (command*) malloc(sizeof(command));
command* cmd5 = (command*) malloc(sizeof(command));
command* cmd6 = (command*) malloc(sizeof(command));
command* chains1[2];
chains1[0] = cmd3;
chains1[1] = cmd5;
char* args1[] = { "cat", "/tmp/test.log", NULL };
char* args3[] = { "sort", NULL, NULL };
char* args4[] = { "wc", "-l", NULL };
char* args5[] = { "wc", "-l", NULL };
char* args6[] = { "wc", "-l", NULL };
cmd1->argv = args1;
cmd2->argv = NULL;
cmd3->argv = args3;
cmd4->argv = args4;
cmd5->argv = args5;
cmd6->argv = args6;
cmd1->master_cmd = NULL;
cmd1->next = NULL;
cmd1->chains = NULL;
cmd1->num_children = -1;
cmd2->master_cmd = cmd1;
cmd2->chains = chains1;
cmd2->next = NULL;
cmd2->num_children = 2;
cmd3->master_cmd = NULL;
cmd3->next = cmd4;
cmd3->chains = NULL;
cmd3->num_children = -1;
cmd4->master_cmd = NULL;
cmd4->next = NULL;
cmd4->chains = NULL;
cmd4->num_children = -1;
cmd5->master_cmd = NULL;
cmd5->next = cmd6;
cmd5->chains = NULL;
cmd5->num_children = -1;
cmd6->master_cmd = NULL;
cmd6->next = NULL;
cmd6->chains = NULL;
cmd6->num_children = -1;
int rc = execute_master_command_and_pipe_to_childs(cmd2, -1);
return 0;
}
int execute_master_command_and_pipe_to_childs(command* cmd, int input) {
int num_children = cmd->num_children;
int write_pipes[num_children];
pthread_t threads[num_children];
command* master_cmd = cmd->master_cmd;
pid_t pid;
int i;
for (i = 0; i < num_children; i++) {
int new_pipe[2];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, new_pipe) < 0) {
int errnum = errno;
fprintf(STDERR_FILENO, "ERROR (%d: %s)\n", errnum,
strerror(errnum));
return EXIT_FAILURE;
}
if (cmd->chains[i] != NULL) {
cmd->chains[i]->fd = new_pipe[0];
if (pthread_create(&threads[i], NULL, (void *) be_child,
cmd->chains[i]) != 0) {
perror("pthread_create"), exit(1);
}
write_pipes[i] = new_pipe[1];
} else {
perror("ERROR\n");
}
}
if (input != -1) {
waitfor(input);
}
int pipefd = run_pipeline_sockets(master_cmd, input);
int buffer[1024];
int len = 0;
while ((len = read(pipefd, buffer, sizeof(buffer))) != 0) {
int j;
for (j = 0; j < num_children; j++) {
if (write(write_pipes[j], &buffer, len) != len) {
fprintf(STDERR_FILENO, "Write failed (child %d)\n", j);
exit(1);
}
}
}
close(pipefd);
for (i = 0; i < num_children; i++) {
close(write_pipes[i]);
}
for (i = 0; i < num_children; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("pthread_join"), exit(1);
}
}
}
void waitfor(int fd) {
fd_set rfds;
struct timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 500000;
retval = select(fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
perror("select()");
else if (retval) {
printf("Data is available now on: %d\n", fd);
} else {
printf("No data on: %d\n", fd);
///waitfor(fd);
}
}
void be_child(command* cmd) {
printf(
"fd = %d , argv = %s , args = %s , next = %d , master_cmd = %d , next_chain = %d\n",
cmd->fd, cmd->argv[0], cmd->argv[1], cmd->next, cmd->master_cmd,
cmd->chains);
waitfor(cmd->fd);
int fd = run_pipeline_sockets(cmd, cmd->fd);
waitfor(fd);
int buffer[1024];
int len = 0;
while ((len = read(fd, buffer, sizeof(buffer))) != 0) {
write(STDERR_FILENO, &buffer, len);
}
close(cmd->fd);
close(fd);
}
int run_pipeline_sockets(command *cmd, int input) {
int pfds[2] = { -1, -1 };
int pid = -1;
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pfds) < 0) {
int errnum = errno;
fprintf(STDERR_FILENO, "socketpair failed (%d: %s)\n", errnum,
strerror(errnum));
return EXIT_FAILURE;
}
if ((pid = fork()) == 0) { /* child */
if (input != -1) {
dup2(input, STDIN_FILENO);
close(input);
}
if (pfds[1] != -1) {
dup2(pfds[1], STDOUT_FILENO);
close(pfds[1]);
}
if (pfds[0] != -1) {
close(pfds[0]);
}
execvp(cmd->argv[0], cmd->argv);
exit(1);
} else { /* parent */
if (input != -1) {
close(input);
}
if (pfds[1] != -1) {
close(pfds[1]);
}
if (cmd->next != NULL) {
run_pipeline_sockets(cmd->next, pfds[0]);
} else {
return pfds[0];
}
}
}
void segfault_sigaction(int signal, siginfo_t *si, void *arg) {
printf("Caught segfault at address %p\n", si->si_addr);
printf("Caught segfault errno %p\n", si->si_errno);
exit(0);
}
void handle_segfault(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sigaction));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = segfault_sigaction;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
}
I would come at this problem from a very different angle: rather than coming up with a large data structure to manage the pipe tree, and using threads (where an io blockage in a process may block in its threads) I would use only processes.
I also fail to see how a 64K buffer is your bottleneck when you're only using a 1K buffer.
2 simple functions should guide this: (error handling omitted for brevity, and using a pseudocodey parsecmd() function which turns a space separated string into an argument vector)
int mkproc(char *cmd, int outfd)
{
Command c = parsecmd(cmd);
int pipeleft[2];
pipe(pipeleft);
if(!fork()){
close(pipeleft[1]);
dup2(pipeleft[0], 0);
dup2(outfd, 1);
execvp(c.name, c.argv);
}
close(pipeleft[0]);
return pipeleft[1];
}
Mkproc takes the fd it will write to, and returns what it will read from. This way chains are really easy to initalize:
int chain_in = mkproc("cat foo.txt", mkproc("sort", mkproc("wc -l", 1)));
the next is:
int mktree(char *cmd, int ofd0, ...)
{
int piperight[2];
pipe(piperight);
int cmdin = mkproc(cmd, piperight[1]);
close(piperight[1]);
if(!fork()){
uchar buf[4096];
int n;
while((n=read(piperight[0], buf, sizeof buf))>0){
va_list ap;
int fd;
va_start(ap, ofd0);
for(fd=ofd0; fd!=-1; fd=va_arg(ap, int)){
write(fd, buf, n);
}
va_end(ap);
}
}
return cmdin;
}
Between the two of these, it is very easy to construct trees of arbitrary complexity, as so:
int tree_in = mktree("cat foo.txt",
mktree("rot13",
mkproc("uniq", mkproc("wc -l", 1)),
mkproc("wc -l", open("out.txt", O_WRONLY)), -1),
mkproc("sort", 2), -1);
This would output a sorted foo.txt to stderr, the number of lines in rot13'd foo.txt to out.txt, and the number of non-duplicate lines of rot13'd foo.txt to stdout.
a kind user here gave me some code to work with for a command line shell, but I want it to output to stdout and stderr instead of using a screen or whatever it is doing right now. I am new to C so I don't know anything about converting it. I also need its ability to detect arrow keys preserved... I'm trying to make a simplistic bash clone. This is what I have right now, it's about 50% my code and 50% others'... yes, it is buggy. There are large sections commented out because they were no longer being used or because they were broken. Ignore them. :)
The particular difficulty is in the use of draw_frame() in main().
#include "os1shell.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h> /* standard unix functions, like getpid() */
#include <sys/types.h> /* various type definitions, like pid_t */
#include <signal.h> /* signal name macros, and the kill() prototype */
#include <ncurses/curses.h> /* a library for cursor-based programs */
#include <poll.h>
#include <termios.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s {
int x;
int y;
char *data;
};
char* inputBuffer; /* the command input buffer, will be length 65 and null
* terminated. */
char** cmdHistory; /* the command history, will be no longer than 20
* elements and null terminated. */
int historySize = 0;
void addToHistory(char* newItem) {
char** h;
int historySize = 0;
for (historySize; historySize < 21; ++historySize) {
if (cmdHistory[historySize] == NULL) break;
}
if (historySize == 20) {
char** newPtr = cmdHistory + sizeof(char *);
free(cmdHistory[0]);
cmdHistory = newPtr;
h = (char**)realloc(cmdHistory,21*sizeof(char *));
cmdHistory = h;
cmdHistory[19] = newItem;
cmdHistory[20] = NULL;
} else {
h = (char**)realloc(cmdHistory,(historySize+2)*sizeof(char *));
cmdHistory = h;
cmdHistory[historySize] = newItem;
cmdHistory[historySize+1] = NULL;
}
}
/* Some help from http://stackoverflow.com/users/1491/judge-maygarden*/
char** getArguments(char* input) {
char** arguments;
int k = 0;
char* tokenized;
arguments = calloc(1, sizeof (char *));
tokenized = strtok(input, " &");
while (tokenized != NULL) {
arguments[k] = tokenized;
++k;
arguments = realloc(arguments, sizeof (char *) * (k + 1));
tokenized = strtok(NULL, " &");
}
// an extra NULL is required to terminate the array for execvp()
arguments[k] = NULL;
return arguments;
}
void printHistory(struct frame_s *frame) {
snprintf(frame->data, frame->x, "\n\n");
char** currCmd = cmdHistory;
while (*currCmd != NULL) {
snprintf(frame->data[(2*frame->x)], frame->x, "%s\n", *currCmd);
currCmd++;
}
snprintf(frame->data, frame->x, "\n\n");
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
static int draw_frame(struct frame_s *frame) {
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for ( row = 0, data = frame->data;
row < frame->y;
row++, data += frame->x ) {
// 0 for normal, 1 for bold, 7 for reverse.
attrib = 0;
// The VT100 commands to move the cursor, set the attribute,
// and the actual frame line.
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);
}
return (0);
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
int main(void) {
const struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[65]; // the input buffer
unsigned int count = 0; // the count of characters in the buff
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
cmdHistory = (char**)calloc(21,sizeof(char *)); // initialize the
// command history
cmdHistory[20] = NULL; // null terminate the history
int histInd = 0; // an index for the history for arrows
int t;
int r;
char** downTemp;
char** enterTemp;
// Set up a little frame.
frame.x = 80;
frame.y = 32;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL) {
fprintf(stderr, "No memory\n");
exit (1);
}
memset(frame.data, ' ', frame.x * frame.y);
// Get the terminal state.
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
// Turn off "cooked" mode (line buffering) and set minimum characters
// to zero (i.e. non-blocking).
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
// Set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
// Un-mask all signals while in ppoll() so any signal will cause
// ppoll() to return prematurely.
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
// Loop forever waiting for key presses. Update the output on every key
// press and every 1.0s (when ppoll() times out).
do {
fd_set rdset;
int nfds = STDIN_FILENO + 1;
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
ret = pselect(nfds, &rdset, NULL, NULL, &timeout, &sigmask);
if (ret < 0) { // check for pselect() error.
if (errno == EINTR) {
continue;
} else {
break;
}
}
if (FD_ISSET(STDIN_FILENO, &rdset)) {
ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
// do {
// fds[0].revents = 0;
// ret = poll(fds, sizeof(fds) / sizeof(struct pollfd), 1000);
//
// if (fds[0].revents & POLLIN) {
// ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
if (ret > 0) {
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0) {
if (histInd > 0) {
--histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (strcmp(&line[count],"\033[B")==0) {
char** downTemp = cmdHistory;
r = 0;
while (*downTemp != NULL) {
++downTemp;
++r;
}
if (histInd < r-1 && r!= 0) {
++histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (line[count] == 127) {
if (count != 0) {
line[count] = '\0';
count -= ret;
}
snprintf(&frame.data[(2*frame.x)], frame.x, "backspace");
} else if (line[count] == '\n') {
char** arguments = getArguments(line);
snprintf( &frame.data[(2*frame.x)],
frame.x,
"entered: %s",
line);
if (count > 0) {
int hasAmpersand = 0;
char* cmd = (char*)
malloc(65*sizeof(char));
strcpy(cmd, line);
addToHistory(cmd);
/*
char* temp = cmd;
while (*temp != '\0') {
if (*temp == '&') {
hasAmpersand = 1;
}
++temp;
}
pid_t pid;
pid = fork();
if (pid == 0) {
int exeret;
exeret = execvp(*arguments,
arguments);
if (exeret < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Exec failed.\n\n");
exit(1);
}
} else if (pid < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Fork failed.\n\n");
exit(1);
} else if (pid > 0) {
if (!hasAmpersand) {
wait(NULL);
}
free(arguments);
snprintf(frame.data,
frame.x,
"\n\n");
}*/
} else {
free(arguments);
}
enterTemp = cmdHistory;
t = 0;
while (*enterTemp != NULL) {
++enterTemp;
++t;
}
if (t > histInd) histInd = t;
count = 0;
} else {
//snprintf( frame.data,
// frame.x,
// "char: %c",
// line[count]);
count += ret;
}
}
}
// Print the current time to the output buffer.
//current_time = time(NULL);
//tp = localtime(¤t_time);
//strftime( &frame.data[1 * frame.x],
// frame.x,
// "%Y/%m/%d %H:%M:%S",
// tp);
// Print the command line.
line[count] = '\0';
snprintf( frame.data,
frame.x,
"OS1Shell -> %s",
line);
draw_frame(&frame);
} while (1);
// Restore terminal and free resources.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
int n = 0;
while (n < 21) {
free(cmdHistory[n]);
++n;
}
free(cmdHistory);
return (0);
}
Any help getting it to act more like bash would be highly appreciated! Part of the credit is for using stderr correctly anyways, so it would definitely help to take the stdin/stdout/stderr approach.
It looks to me it already is going to STDOUT
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);