Basically what i'm trying to do is read a number from a file, increment the value by one, and then write the number back to the same file. Using fork() is supposed to have both processes accessing the file but using locks so they take turns. I keep getting a segmentation fault.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void appendValue(FILE *, int *);
int readValue(FILE *, int *);
void lockFile(FILE *);
void unlockFile(FILE *);
void whatProcess(pid_t *pID);
int main(void) {
pid_t pID;
pID = fork();
int value = 0, counter = 0;
int *valPtr = &value;
pid_t *pidPtr = &pID;
FILE *file = fopen("output.txt", "a+");
lockFile(file);
while(counter < 1000) {
whatProcess(pidPtr);
value = readValue(file, valPtr);
value++;
appendValue(file, valPtr);
rewind(file);
counter++;
}
unlockFile(file);
fclose(file);
printf("\n");
return 0;
}
void whatProcess(pid_t *pID) {
if(*pID > 0) {
printf("\n --- In Parent ---");
} else if(*pID == 0) {
printf("\n --- In Child ---");
} else {
printf("\n --- fork() Failed ---");
}
}
void lockFile(FILE *file) {
int lock;
lock = lockf(fileno(file), F_LOCK, 0);
while(lock != 0) {}
if(lock == 0) {
printf("\nPID %d: Lock Successful", getpid());
} else {
printf("\nPID %d: Lock Unsuccessful", getpid());
}
}
void unlockFile(FILE *file) {
int lock;
lock = lockf(fileno(file), F_LOCK, 0);
while(lock != 0) {}
if(lock == 0) {
printf("\nPID %d: Unlock Successful", getpid());
} else {
printf("\nPID %d: Unlock Unsuccessful", getpid());
}
}
void appendValue(FILE *file, int *value) {
fprintf(file, "%d\n", *value);
}
int readValue(FILE *file, int *value) {
while(!feof(file)) {
fscanf(file, "%d", value);
}
return *value;
}
The fscanf in readValue is writing to a non-allocated location. You are passing value in as a pointer so there is no need to use the address of operator.
int readValue(FILE *file, int *value) {
fscanf(file, "%d", value);
printf("\nreadValue(): %d", *value);
return *value;
}
Or, even better:
int readValue(FILE *file, int *value) {
if (fscanf(file, "%d", value) == 1) {
printf("\nreadValue(): %d", *value);
return 0;
}
return -1;
}
Your current function does not indicate whether it has succeeded or failed. Either add a status return value (zero, -1 are pretty common) or omit error checking and do this instead:
int readValue(FILE *file) {
int buf;
if (fscanf(file, "%d", &buf) == 1) {
printf("\nreadValue(): %d", buf);
} else {
perror("readValue(): fscanf failed");
}
return buf; /* could be a garbage value, use at own risk */
}
Related
The product type should be 1 or 2 depending on the process that created the product
product id should increase by one each time one is produced
consumer id will always be 1,2,3, or 4 depending on which thread consumer consumes the product
consumer count will increase by 1 each time one is consumed
And product type should only be 1 or 2
i think i need to change the code up a bit and add condition variables and locks .But stuck at that
I have most of it done but it has some issues.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdbool.h>
#include <fcntl.h>
#include <pthread.h>
#define BUFFER_SIZE1 20
#define BUFFER_SIZE2 30
typedef struct
{
int count;
int productType;
} product;
int count = 0;
int fd[2];
pthread_mutex_t lock;
pthread_cond_t cond;
typedef struct
{
product *values;
int head;
int tail;
int numEntries;
int size;
} queue;
queue q1;
queue q2;
void producer(int args);
void *consumer(void *args);
void *distributor(void *args);
void initQ(queue *q, int size);
bool QEmpty(queue *q);
bool QFull(queue *q);
bool put(queue *q, product prod);
product get(queue *q);
// https://www.youtube.com/watch?v=l6zkaJFjUbM
// https://www.geeksforgeeks.org/multithreading-c-2/
int main(int argc, char const *argv[])
{
// Creating 5 threads 4 consumer and 1 distributor
pthread_t th[5];
// Creating our pipe, fd[0] is read end, fd[1] is write end
if (pipe(fd) == -1)
{
perror("error creating pipe");
exit(1);
}
// Initializing both buffers
initQ(&q1, BUFFER_SIZE1);
initQ(&q2, BUFFER_SIZE2);
int pid1;
int pid2;
int consId1 = 1;
int consId2 = 2;
// Initializing lock
pthread_mutex_init(&lock, NULL);
// Initialziing condition variables
pthread_cond_init(&cond, NULL);
// Create first producer process using fork(), child process 1
pid1 = fork();
if (pid1 == 0)
{
producer(1);
}
// Create second prodcuer process using fork(), child process 2
pid2 = fork();
if ( pid2== 0)
{
producer(2);
}
// Create distrubtor and consumer threads, parent process
else
{
// Creating 4 threads using for loop and pthread_create
for (int i = 0; i < 4; i++)
{
// 2 consumer threads for product type 1
if (i == 1 || i == 2)
{
if (pthread_create(&th[i], NULL, &consumer, &consId1) != 0)
{
perror("Error creating thread");
}
}
// 2 consumer threads for product type 2
else
{
if (pthread_create(&th[i], NULL, &consumer, &consId2) != 0)
{
perror("Error creating thread");
}
}
}
// use pthread_join to wait for preivous thread to terminate
for (int i = 0; i < 4; i++)
{
if (pthread_join(th[i], NULL) != 0)
{
perror("Error joining thread");
}
}
// Distributor thread
close(fd[1]);
while (1)
{
product prod;
// Using lock and condition variable around crit section to avoid race condition
// pthread_mutex_lock(&lock);
// pthread_cond_wait(&cond, &lock);
// Read from the pipe
read(fd[0], &prod, sizeof(prod));
if (prod.productType == 1)
{
put(&q1, prod);
}
else
{
put(&q2, prod);
}
}
// pthread_cond_signal(&cond);
// pthread_mutex_unlock(&lock);
// Close read end of the pipe
close(fd[0]);
}
return 0;
}
// Creating the producers
void producer(int args)
{
int prodCount = 0;
product prod;
prod.productType = args;
// Close read end of the pipe
close(fd[0]);
while (1)
{
prodCount++;
prod.count = prodCount;
// Send product to the pipe so the consumer can use
write(fd[1], &prod, sizeof(prod));
// Sleep for 0.01 - 0.2 seconds after each loop
int time = (rand() % (200000 - 10000 + 1)) + 10000;
usleep(time);
}
// Close write end of the pipe
close(fd[1]);
}
void *consumer(void *args)
{
int consCount1;
int consCount2;
FILE *fp;
fp = fopen("output.txt", "w");
product prod;
int prodType = *(int *)args;
while (1)
{
if (prodType == 1)
{
get(&q1);
consCount1++;
fputs("Thread ID: \n", fp);
fprintf(fp, "Product Type: %d\n", prod.productType);
fprintf(fp, "Production Sequence #: %d\n", prod.count);
fprintf(fp, "Consumption Sequence #: %d\n", consCount1);
}
else
{
get(&q2);
consCount2++;
fputs("Thread ID: 2\n", fp);
fprintf(fp, "Product Type: %d\n", prod.productType);
fprintf(fp, "Production Sequence #: %d\n", prod.count);
fprintf(fp, "Consumption Sequence #: %d\n", consCount2);
}
}
fclose(fp);
}
// https://www.youtube.com/watch?v=oyX30WVuEos&t=196s
// Circular buffer
void initQ(queue *q, int size)
{
q->size = size;
q->values = malloc(sizeof(product) * q->size);
q->numEntries = 0;
q->head = NULL;
q->tail = NULL;
}
// Checks if the queue is empty
bool QEmpty(queue *q)
{
return (q->numEntries == 0);
}
// Checks if the queue is full
bool QFull(queue *q)
{
return (q->numEntries == q->size);
}
// Used for adding products to the queue
bool put(queue *q, product prod)
{
// If the queue is full we can not add to it
if (QFull(q))
{
return false;
}
// Add product to the end of the queue
q->values[q->tail] = prod;
q->numEntries++;
// Move index of the tail
q->tail = (q->tail + 1);
// If index goes out of bounds set back to 0
if (q->tail >= q->size)
{
q->tail = 0;
}
return true;
}
// Used for removing products for the queue
product get(queue *q)
{
product result;
// If the queue is empty we can not dequeue anymore
if (QEmpty(q))
{
perror("Error on dequeue");
}
// Remove from the head of the queue
result = q->values[q->head];
q->head = (q->head + 1) & q->size;
q->numEntries--;
return result;
}
My code builds a child process and multiple grandchildren (based on N_GRANDKIDDIES).
My problem appears in the handler for the SIGCHLD signal... When I write the pid of the current grandchild and another value, the outcome in the console is very inconsistent... there are often multiple rows missing, for example, like this:
PID:12311
PID:12312
PID:12314
PID:12313
File for Saving Signals
Grandchild with pid:12511 has called SIGUSR1 25139 times
(Missing the other grandchildren here)
Process returned 0 (0x0).....
Hope someone can help me here!
my Code looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <sys/resource.h>
#define TESTTIME 3
#define FNAME "testfile.txt"
#define N_GRANDKIDDIES 4
#define SLEEPSEC 0
#define SLEEPNSEC 2
// prototypen
int kiddyscode(void);
int grandkiddyscode();
int nanosleep(const struct timespec *req, struct timespec *rem);
void syserr(char* message);
// global
int pids[N_GRANDKIDDIES];
int counter[N_GRANDKIDDIES];
int n_grandkiddies;
int stop = 0;
void syserr(char* message)
{
int i,j;
j=strlen(message);
printf("Erro ");
for(i=0;i<j;i++)
{
printf("%c",*message++);
}
printf("Error No.: %d\n",errno);
strerror(errno);
exit(0);
}
void initSignalhandler(int sig, struct sigaction sa , void (*handler))
{
memset (&sa, 0,sizeof(sa));
if(sig == SIGCHLD || sig == SIGUSR1) sa.sa_flags = SA_SIGINFO;
sa.sa_handler = handler;
sigaction(sig, &sa, NULL);
if ((int)(uintptr_t)sigaction == -1)
{
syserr("Error SigAction\n");
exit(-1);
}
}
void handle_sigalrm(int sig)
{
stop = 1;
}
void handle_sigusr(int sig, siginfo_t *si, void *data)
{
for (int i=0; i<N_GRANDKIDDIES-1; i++)
{
if ((int)si->si_pid == pids[i]) counter[i]++;
}
}
void handle_sigchld(int sig, siginfo_t *si, void *data)
{
pid_t pid;
int status, i;
FILE *fp;
fp = fopen(FNAME, "a");
// Fehler
if (fp == NULL)
{
syserr("Error\n");
exit(-1);
}
wait(NULL);
n_grandkiddies--;
for (i=0; i<N_GRANDKIDDIES-1;i++)
{
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)
{
n_grandkiddies--;
fprintf(fp, "Grandchild with pid:%i has callled SIGUSR1 %i times\n", pids[i], counter[i]);
}
}
fclose(fp);
}
int main()
{
FILE *fp;
fp = fopen(FNAME, "w");
if (fp == NULL)
{
syserr("Error\n");
exit(-1);
}
fprintf(fp ,"File for Saving Signals\n");
fclose(fp);
switch(fork())
{
case -1:
syserr("Fork Error\n");
exit(-1);
break;
case 0:
kiddyscode();
return 0;
default:
if(wait(NULL) == -1)
{
syserr("Error");
}
}
fp = fopen(FNAME, "r");
if (fp == NULL)
{
syserr("Error\n");
exit(-1);
}
char c;
while((c = fgetc(fp)) != EOF)
printf("%c", c);
fclose(fp);
return 0;
}
int kiddyscode(void)
{
struct sigaction sa1;
initSignalhandler(SIGCHLD, sa1, handle_sigchld);
initSignalhandler(SIGUSR1, sa1, handle_sigusr);
for(int i = 0; i < N_GRANDKIDDIES; i++)
{
pids[i]= fork();
// Erstellung durch fork
// -1 = Fehler
if(pids[i] == -1)
{
syserr("Fork Error\n");
exit(-1);
}
else if(pids[i]==0)
{
grandkiddyscode();
exit(0);
}
// wird von Kiddy ausgeführt
else
{
n_grandkiddies++;
}
}
int i=0;
while (n_grandkiddies>0){
i++;
pause();
if (errno==EINTR) continue;
if (i>1) break;
continue;
}
return 0;
}
int grandkiddyscode()
{
printf("PID:%i\n", getpid());
// Signalhandler initialisieren
struct sigaction sa1;
initSignalhandler(SIGALRM, sa1, handle_sigalrm);
// Alarm stellen
alarm(TESTTIME);
int ppid = getppid();
struct timespec ts;
ts.tv_sec = SLEEPSEC;
ts.tv_nsec = SLEEPNSEC;
while (stop != 1)
{
kill(ppid, SIGUSR1);
nanosleep(&ts, NULL);
}
return 0;
}
I'm reading Modern Operating Systems and there's a exercise that asks you to build a counting-semaphore with a binary-semaphore.
I was wondering if I could do it with semaphores & shared memory (C and POSIX IPC).
I'll leave a simplification of my program here.
typedef struct sem {
int semid; /* contains two semaphores with value set to 1, one for the critical region and the other for locking */
int shmid; /* shared counter */
}Sem;
/* the parent process spawns a few child processes and then waits for them */
void child() {
Sem *s;
s = get_sem();
down_sem(s);
printf("working...");
sleep(5);
printf("finished!");
up_sem(s);
}
void down_sem(Sem *s) {
int n;
enter_critical(s); // down to critical region sem
n = get_counter(s);
if (n == 0) {
exit_critical(s); // up to critical region sem
acquire_lock(s); // down to lock sem
} else {
n--;
exit_critical(s); // up to critical region sem
}
}
void up_sem(Sem *s) {
int n;
enter_critical(s);
n = get_counter(s);
n++;
if (n == 1) {
exit_critical(s);
release_lock(s); // up to lock sem
} else {
exit_critical(s);
}
}
But this doesn't work since some processes block in the lock semaphore forever. I'm just now learning these concepts so my design is probably totally off.
I can share the complete code if you wish.
Thanks in advance!
EDIT: requested mre
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/shm.h>
#define NPROC 5
#define SLOTS 2
#define PATH "."
#define SID 'S'
#define SEMN 2
#define SHMID 'M'
#define SHM_SIZE 32
#define CRIT 0
#define LOCK 1
#define UP 1
#define DOWN -1
typedef struct sem {
int shmid;
int semid;
}Sem;
typedef union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}Args;
void err (char *msg) {
char error[50];
int n;
n = snprintf(error, 50, "[%d] %s", getpid(), msg);
error[n] = 0;
perror(error);
exit(1);
}
int* get_n(Sem *s) {
int *data = NULL;
data = shmat(s->shmid, (void *)0, 0);
if (data == (int *)(-1))
err("get_n shmat");
return data;
}
void dettach_n(int *data) {
if (shmdt(data) == -1)
err("dettach_n shmdt");
}
void enter_crit(Sem *s) {
struct sembuf sb;
sb.sem_num = CRIT;
sb.sem_op = DOWN;
printf("[%d] enter crit\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("enter crit");
}
void exit_crit(Sem *s) {
struct sembuf sb;
sb.sem_num = CRIT;
sb.sem_op = UP;
printf("[%d] exit crit\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("exit crit");
}
void acquire_lock(Sem *s) {
struct sembuf sb;
sb.sem_num = LOCK;
sb.sem_op = DOWN;
printf("[%d] acquire lock\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("acquire lock");
}
void release_lock(Sem *s) {
struct sembuf sb;
sb.sem_num = LOCK;
sb.sem_op = UP;
printf("[%d] release lock\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("release lock");
}
int sem_init(Sem *s, int n) {
key_t key;
Args arg;
int *data;
/* sem */
if ((key = ftok(PATH, SID)) == -1) {
return -1;
}
if ((s->semid = semget(key, SEMN, 0600 | IPC_CREAT)) == -1) {
return -1;
}
arg.val = 1;
if (semctl(s->semid, 0, SETVAL, arg) == -1) {
return -1;
}
if (semctl(s->semid, 1, SETVAL, arg) == -1) {
return -1;
}
/* mem */
if ((key = ftok(PATH, SHMID)) == -1) {
return -1;
}
if ((s->shmid = shmget(key, SHM_SIZE, 0664 | IPC_CREAT)) == -1) {
return -1;
}
data = shmat(s->shmid, (void *)0, 0);
if (data == (int *)(-1)) {
return -1;
}
*data = n;
if (shmdt(data) == -1) {
return -1;
}
return 0;
}
int sem_get(Sem *s) {
key_t key;
if ((key = ftok(PATH, SID)) == -1) {
return -1;
}
if ((s->semid = semget(key, SEMN, 0)) == -1) {
return -1;
}
if ((key = ftok(PATH, SHMID)) == -1) {
return -1;
}
if ((s->shmid = shmget(key, SHM_SIZE, 0)) == -1) {
return -1;
}
return 0;
}
int sem_up(Sem *s) {
int *data;
enter_crit(s);
data = get_n(s);
printf("[%d] read %d\n", getpid(), *data);
(*data)++;
if (*data == 1) {
printf("[%d] now is %d\n", getpid(), *data);
dettach_n(data);
exit_crit(s);
release_lock(s);
} else {
exit_crit(s);
}
return 0;
}
int sem_down(Sem *s) {
int *data;
enter_crit(s);
data = get_n(s);
printf("[%d] checked %d\n", getpid(), *data);
if (*data == 0) {
dettach_n(data);
exit_crit(s);
acquire_lock(s);
} else {
(*data)--;
dettach_n(data);
exit_crit(s);
}
return 0;
}
int sem_rm(Sem *s) {
if (semctl(s->semid, 0, IPC_RMID) == -1)
return -1;
if (shmctl(s->shmid, 0, IPC_RMID) == -1)
return -1;
return 0;
}
void child() {
pid_t pid;
Sem s;
pid = getpid();
printf("\x1b[31m[%d] hello!\033[0m\n", pid);
if (sem_get(&s) == -1) {
perror("sem_get");
exit(1);
}
sem_down(&s);
printf("\x1b[32m[%d] working...\033[0m\n", pid);
sleep(5);
printf("\x1b[32m[%d] finishing...\033[0m\n", pid);
sem_up(&s);
printf("\x1b[34m[%d] **child leaving**\033[0m\n", pid);
exit(0);
}
int main() {
Sem s;
int i;
if (sem_init(&s, SLOTS) == -1) {
perror("sem_init");
exit(1);
}
printf("[%d] parent\n", getpid());
for (i = 0; i < NPROC; i++) {
switch(fork()) {
case 0:
child();
case -1:
perror("fork");
exit(1);
}
}
printf("waiting for children...\n");
for (i = 0; i < NPROC; i++)
wait(NULL);
if (sem_rm(&s) == -1) {
perror("sem_rm");
exit(1);
}
printf("good bye!\n");
return 0;
}
The problem was in the up_sem function. It only does an up to the lock sem (release_lock func) if n was previously 0. This is incorrect since it might be several process waiting.
My solution was adding an extra shared memory counting the waiting processes and doing an up to the lock sem when that number is greater than 0.
I have a program that is supposed to copy the contents of a file exactly to another file using multiple threads. The reader thread reads a line from the file and stores it in a circular buffer. The writer thread then reads from the buffer and writes to the file. However I am getting a segmentation fault and it is not writing to the file. Any idea why I am getting a segmentation fault or is there any way that I can find out what is causing the error?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
FILE *inputFile;
FILE *outputFile;
pthread_mutex_t mutex;
int endOfFile = 0;
typedef struct bufferStruct{
int capacity;
int size;
int head;
int tail;
char **data;
}buffer;
buffer * bufferInit(int maxElements){
buffer *buf;
buf = (buffer *)malloc(sizeof(buffer));
buf->data = (char**)malloc(sizeof(char*)*maxElements);
buf->size = 0;
buf->capacity = maxElements;
buf->head = 0;
buf->tail = -1;
return buf;
}
void popFront(buffer *buf){
if(buf->size != 0){
free(buf->data);
buf->size--;
buf->head++;
if(buf->head == buf->capacity){
buf->head = 0;
}
}
return;
}
char* front(buffer *buf){
if(buf->size != 0){
return buf->data[buf->head];
}
return NULL;
}
void pushBack(buffer *buf, char *data){
if(buf->size == buf->capacity){
printf("Queue is Full\n");
}
else{
buf->size++;
buf->tail = buf->tail + 1;
if(buf->tail == buf->capacity){
buf->tail = 0;
}
buf->data[buf->tail] = (char *) malloc((sizeof data + 1)* sizeof(char));
strcpy(buf->data[buf->tail], data);
}
return;
}
buffer *buf;
void* reader(void* arg){
char line[1024];
while(endOfFile != 1){
fgets(line, sizeof(line), inputFile);
printf("Line read: %s", line);
pushBack(buf, line);
if(feof(inputFile)){
endOfFile = 1;
}
}
pthread_exit(0);
}
void* writer(void* arg){
char *line;
while(endOfFile != 1){
pthread_mutex_lock(&mutex);
line = front(buf);
fputs(line, outputFile);
popFront(buf);
pthread_mutex_unlock(&mutex);
}
pthread_exit(0);
}
int main(int argc, char **argv){
if (argc < 4) {
printf("Usage: %s <input file> <output file> <number>\n", argv[0]);
exit(-1);
}
inputFile = fopen(argv[1], "r");
outputFile = fopen(argv[2], "w");
int numOfThreads = atoi(argv[3]);
buf = bufferInit(16);
pthread_t readerTids[numOfThreads];
pthread_t writerTids[numOfThreads];
pthread_mutex_init(&mutex, NULL);
for(int i = 0; i < numOfThreads; i++){
if(endOfFile != 1){
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&readerTids[i], &attr, reader, NULL);
pthread_create(&writerTids[i], &attr, writer, NULL);
printf("Thread %d created\n", i);
}
}
for (int i = 0; i < numOfThreads; i++) {
pthread_join(readerTids[i], NULL);
pthread_join(writerTids[i], NULL);
}
fclose(inputFile);
fclose(outputFile);
}
Consider the possibility of your reader thread being slower than the writer thread. The writer thread alone holds the lock, does the locking and unlocking, not being bothered about the reader. What if the writer tries to use the buffer when reader hasn't updated the buffer yet? Use thread synchronisation, say semaphores, which does not have any ownership issues.
void* reader(void* arg){
char line[1024];
while(endOfFile != 1){
fgets(line, sizeof(line), inputFile);
printf("Line read: %s", line);
pushBack(buf, line);
--- Lock semaphore here---
if(feof(inputFile)){
endOfFile = 1;
}
}
pthread_exit(0);
}
void* writer(void* arg){
char *line;
while(endOfFile != 1){
-- Unlock semaphore here---
line = front(buf);
fputs(line, outputFile);
popFront(buf);
}
pthread_exit(0);
}
Unlike mutex, the same semaphore can be used between both threads. This helps you to sync up both threads.
I'm writing a small shell to learn C. Now I want to execute custom commands but it is not working.
$ ./a.out
OS>ls
10357: executing ls
failed to execute ls
: (2: No such file or directory)
I must not use system call to execute custom command, I should use execvp and fork. But why is it now working? The entire code is
#include<sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
int mystrcmp(char const *, char const *);
struct command
{
char * const *argv;
};
static _Noreturn void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
/* Helper function that spawns processes */
static int spawn_proc(int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork()) == 0)
{
if (in != 0)
{
if (dup2(in, 0) < 0)
err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
;
close(in);
}
if (out != 1)
{
if (dup2(out, 1) < 0)
err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
close(out);
}
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd->argv[0]);
execvp(cmd->argv[0], cmd->argv);
err_syserr("failed to execute %s: ", cmd->argv[0]);
}
else if (pid < 0) {
err_syserr("fork failed: ");
}
return pid;
}
/* Helper function that forks pipes */
static void fork_pipes(int n, struct command *cmd)
{
int i;
int in = 0;
int fd[2];
for (i = 0; i < n - 1; ++i)
{
pipe(fd);
spawn_proc(in, fd[1], cmd + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
}
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd[i].argv[0]);
execvp(cmd[i].argv[0], cmd[i].argv);
err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}
#define BUFFERSIZE 200
int main() {
char *args[80];
char buffer[BUFFERSIZE];
char *prompt = "OS";
char *a = ">";
char *tok;
tok = strtok (buffer," ");
while(buffer != NULL) {
bzero(buffer, BUFFERSIZE);
printf("%s%s",prompt,a);
fgets(buffer, BUFFERSIZE, stdin);
if(mystrcmp(buffer,"cd") == 0) {
tok = strchr(buffer,' ')+1; //use something more powerful
*strchr(tok, '\n')='\0';
cd(tok);
}
else if(mystrcmp(buffer,"exit") == 0) {
return 0;
}
else {
//system("ls"); //for testing the CWD/PWD
char *commandbuffer[] = { buffer, 0 };
//char *less[] = { "less", 0 };
struct command cmd[] = { {commandbuffer} };
fork_pipes(1, cmd);
printf("Spawned foreground process: %d\n", getpid());
}
}
return 0;
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
for(i = 0; q[i]; i++)
{
if(p[i] != q[i])
return -1;
}
return 0;
}
int cd(char *pth) {
char path[BUFFERSIZE];
strcpy(path,pth);
char *token;
char cwd[BUFFERSIZE];
if(pth[0] != '/')
{ // true for the dir in cwd
getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
chdir(cwd);
} else { //true for dir w.r.t. /
chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
return 0;
}
After
fgets(buffer, BUFFERSIZE, stdin);
the buffer always ends with an '\n' since you end your input with a return.
Thus if you just pass ls as a command you program gets ls\n and obviously there's no such command or binary in PATH.
To fix this you can simply do the following:
fgets(buffer, BUFFERSIZE, stdin);
if (buffer[strlen(buffer)-1] == '\n')
buffer[strlen(buffer)-1] = '\0';
....
The error is not with your use of execvp but with your use of fgets. fgets leaves the newline at the end of the line in the buffer, so ultimately you feed "ls\n" to execvp, and it rightly complains that it cannot find that command.
Since I'm guessing that you'll ultimately replace this code anyway, for the moment,
fgets(buffer, BUFFERSIZE, stdin);
strtok(buffer, "\n"); /* quick & dirty: remove newline if there. */
gets rid of the problem until you get around to doing the input parsing properly. I cannot recommend anything that uses strtok as a long-term solution, though. For the long term, you may be interested in the GNU-specific getline function, or indeed in libreadline (if putting your code under GPL is not a problem for you).
As usual, the case could be solved with strace.
Unfortunately, the code is too wrong and too long for me to write an exhaustive commentary.
meh.c:99:13: warning: implicit declaration of function 'cd' is invalid
in C99 [-Wimplicit-function-declaration]
cd(tok);
^
meh.c:80:11: warning: unused variable 'args' [-Wunused-variable]
char *args[80];
^
meh.c:132:11: warning: unused variable 'token' [-Wunused-variable]
char *token;
^
3 warnings generated.
What's up with this?
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
int mystrcmp(char const *, char const *);
struct command
{
char * const *argv;
};
static _Noreturn void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
Consider non-standard err func instead.
/* Helper function that spawns processes */
static int spawn_proc(int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork()) == 0)
{
if (in != 0)
{
if (dup2(in, 0) < 0)
err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
;
close(in);
}
if (out != 1)
{
if (dup2(out, 1) < 0)
err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
close(out);
}
If you have to check in and out fds like this, chances are you are already doing something wrong. Consider what happens if 'out' is 0. At this level just make sure your shell always has 0,1,2 open and that will deal with the issue.
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd->argv[0]);
execvp(cmd->argv[0], cmd->argv);
err_syserr("failed to execute %s: ", cmd->argv[0]);
}
else if (pid < 0) {
err_syserr("fork failed: ");
}
Shuffling around allows to put parent code early and avoid the indenation for long child case.
return pid;
}
/* Helper function that forks pipes */
static void fork_pipes(int n, struct command *cmd)
{
int i;
int in = 0;
int fd[2];
for (i = 0; i < n - 1; ++i)
{
pipe(fd);
spawn_proc(in, fd[1], cmd + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
}
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd[i].argv[0]);
If printfs with the newlinewere not sufficient, strace reveals the problem:
execve("/usr/bin/ls\n", ["ls\n"], [/* 58 vars */]) = -1 ENOENT (No such file or directory)
execvp(cmd[i].argv[0], cmd[i].argv);
You do realise this overwrites your shell?
err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}
#define BUFFERSIZE 200
int main() {
char *args[80];
char buffer[BUFFERSIZE];
char *prompt = "OS";
char *a = ">";
char *tok;
tok = strtok (buffer," ");
while(buffer != NULL) {
bzero(buffer, BUFFERSIZE);
printf("%s%s",prompt,a);
fgets(buffer, BUFFERSIZE, stdin);
if(mystrcmp(buffer,"cd") == 0) {
tok = strchr(buffer,' ')+1; //use something more powerful
*strchr(tok, '\n')='\0';
cd(tok);
}
else if(mystrcmp(buffer,"exit") == 0) {
return 0;
}
else {
//system("ls"); //for testing the CWD/PWD
char *commandbuffer[] = { buffer, 0 };
//char *less[] = { "less", 0 };
struct command cmd[] = { {commandbuffer} };
fork_pipes(1, cmd);
printf("Spawned foreground process: %d\n", getpid());
}
}
return 0;
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
What's up with this initialisation?
for(i = 0; q[i]; i++)
Incorrect. You assume q is not longer than p.
{
if(p[i] != q[i])
return -1;
}
There are better ways than char-by-char comparison.
return 0;
}
What is this for anyway?
int cd(char *pth) {
char path[BUFFERSIZE];
strcpy(path,pth);
path and pth? Man. Consider 'orig_path' or something. Variations of one /word/ look like a typo and in fact you can easily mistype it by accident. fscking avoid.
char *token;
char cwd[BUFFERSIZE];
if(pth[0] != '/')
{ // true for the dir in cwd
getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
chdir(cwd);
This is incorrect even while ignoring usual buffer overflow problems and missing error checking. If directory tree relevant to this process is modified after you getcwd, you enter the wrong directory (that's assuming chdir succeeds). What's more, paths including '..' are sensitive to symlinks.1
} else { //true for dir w.r.t. /
chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
Seems like a copy-pasto?
return 0;
}