I'm trying to have a process fork and run execve in the child process so that it will open a new terminal window and execute a custom command there.
The program I want to execute is gestore
These are the arguments I pass to execve:
char * argv_exec[5];
argv_exec[0]="/usr/bin/xfce4-terminal";
argv_exec[1]="--geometry";
argv_exec[2]="480x320";
argv_exec[3]="-x";
argv_exec[4]="./gestore"; // the program I want to execute in new window
argv_exec[5]=NULL;
char sess_m[80];
strcat(sess_m,"SESSION_MANAGER=");
strcat(sess_m,getenv("SESSION_MANAGER"));
char * envp[3];
envp[0]="DISPLAY=:0.0";
envp[1]=sess_m;
envp[2]=NULL;
and here I call execve:
if(pid_tv==0)
if(execve(argv_exec[0],argv_exec,&envp)==-1) {...}
but I keep getting Session manager variable not defined.
Anyone has a suggestion on why this does not work or how could I do it better?
Some re-writing, both to show more idiomatic use of a process's environment (see environ(7)) and to demonstrate some ways to avoid hard-wiring array sizes: always a source of fence-post errors in C.
See code comments for some of the explanations/justifications.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ; /* see environ(7) */
int
main(void)
{
char *argv_exec[] = {
"/usr/bin/xfce4-terminal",
"--geometry",
"480x320",
"-x",
"./gestore",
NULL };
/*
* Structure to drive search for a few variables present in
* current environment, and construct a new, minimal, environment
* containing just those values. Use the direct value from the
* current process's environ variable to set "value" (the string
* of the form "NAME=VALUE"). Just add more entries to nv to handle
* more variables; the code below will adjust sizes.
*
* Any value missing in the environment will be an error.
*/
struct {
char *name;
char *value;
} nv[] = {
{ "DISPLAY", NULL },
{ "SESSION_MANAGER", NULL }
};
/* size new_envp to have one more entry than nv */
char *new_envp[sizeof(nv) / sizeof(nv[0]) + 1];
char **e;
int i, error_flag;
pid_t pid;
/*
* For each variable needed...
*/
for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) {
/* ...search in current environment */
for (e = environ; *e; e++) {
size_t slen = strlen(nv[i].name);
if (strncmp(*e, nv[i].name, slen) == 0 && (*e)[slen] == '=') {
nv[i].value = *e;
break;
}
}
}
/*
* Check that we found all values, setting up new_envp as we go.
*/
error_flag = 0;
for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) {
if (nv[i].value == NULL) {
(void) fprintf(stderr, "%s not set in environment\n",
nv[i].name);
error_flag = 1;
} else {
new_envp[i] = nv[i].value;
}
}
if (error_flag) {
return 1;
}
new_envp[i] = NULL;
/* very minimal fork/exec processing */
pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
(void) execve(argv_exec[0], argv_exec, new_envp);
/*
* If execve succeeded, the invoked program has
* replaced this process, and will either run or
* (presumably) report its own errors. If we're
* still in control, the execve failed, so print
* an error and exit.
*/
perror(argv_exec[0]);
return 1;
} else {
if (wait(0) != pid) {
perror("wait");
return 1;
}
}
return 0;
}
Related
Before I start, I just want to say that this is for a school assignment of mine. I'm really close to finishing, except well, since I'm here, obvious to say, I'm stuck on a problem :(.
First of I'll explain what my assignment wants:
The assignment wants me to create a command-line program in C that allows the user to type in N number of /bin/ commands (E.g. > /bin/ps /bin/ls "/bin/which gcc"). Once the user enters the command and hits the enter key, the parent process (the parent process is the program) will create N child processes (i.e. no. of /bin/ commands entered = no. of child processes parent process will create). Each child will run one of the N commands. All the children will be running concurrently, with the parent waiting for each child to terminate.
Once a child terminates, the parent will print whether the command executed successfully or not (E.g. "Command /bin/ps has completed successfully" or "Command /bin/ps has not completed successfully") and once all children have been terminated, the parent will print "All done, bye!"
The issue:
So I've managed to get my child processes to run concurrently, the only issue is that I'm not sure how to pipe the value of the command (like /bin/ps or /bin/which gcc) from the child process to the parent process to print out the success or not message. I've tried putting the write pipe above my execv which allows me to pipe what I want but the execv won't output anything and I can't put my pipe code below my execv because in that case, then while my execv output will show, my pipe won't. I did think that it might be due to close(1) but commenting that out didn't change the result.
So what I am trying to achieve is something like this:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
/usr/bin/gcc
localdomain
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
a.txt b.c
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
All done, bye!
>
But right now, I'm getting:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
>
As you can see, my execv output for the /bin/ commands aren't shown in the output.
I've tried searching SO for people who faced this similar issue as me but none of their solutions managed to work for me which is why I'm asking here. If there's anything you're not clear about, please let me know and I will try my best to explain.
The code:
q1.c
#include "q1.h"
int main(int argc, char *argv[])
{
int
child_status,
pipe_array[2];
pid_t child;
char *success_or_fail;
char *msg_buffer = malloc(CHAR_MAX);
if (msg_buffer == NULL)
{
return -1;
}
struct timespec tw = {.tv_sec = 0, .tv_nsec = 10000000L};
Tuple *process_tuple;
size_t tuple_size = sizeof(*process_tuple) + sizeof(pid_t) + sizeof(char *);
process_tuple = calloc(argc, tuple_size);
if (process_tuple == NULL)
{
return -1;
}
// if (pipe(pipe_array) == -1)
// {
// perror("pipe: ");
// return -1;
// }
for (int j = 1; j < argc; j++)
{
child = fork();
if (child == 0)
{
int
executed,
num_of_words,
num_of_chars;
char
string[strlen(argv[j]) + 1],
*backup = argv[j];
snprintf(string, sizeof(string), "%s", argv[j]);
num_of_chars = get_num_of_chars(string);
num_of_words = get_num_of_words(string);
char *command[num_of_chars + 1];
preparing_the_command(num_of_words, string, command);
// close(pipe_array[0]);
// close(1);
// dup2(pipe_array[1], STDOUT_FILENO);
// write(pipe_array[1], backup, sizeof(backup));
process_tuple[j - 1].pid = getpid();
process_tuple[j - 1].command = backup;
printf(" %i-PID -> %i\n %i-Command -> %s\n\n", process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].command);
executed = execv(command[0], command);
nanosleep(&tw, 0);
if (executed == -1)
{
exit(EXIT_FAILURE);
}
else
{
exit(EXIT_SUCCESS);
}
}
else if (child == -1)
{
perror("fork() failed: ");
exit(EXIT_FAILURE);
}
}
printf(" PID -> %i\n Command -> %s\n\n", process_tuple[0].pid, process_tuple[0].command);
// while ((child = waitpid(-1, &child_status, 0)) != -1)
// {
// for (int o = 0; o < argc; o++)
// {
// printf(" PID -> %i\n Command -> %s\n\n", process_tuple[o].pid, process_tuple[o].command);
// }
// close(0);
// close(pipe_array[1]);
//
// dup2(pipe_array[0], STDIN_FILENO);
// char *recipient;
//
// read(pipe_array[0], recipient, sizeof(recipient));
// if (!(WIFEXITED(child_status) && (WEXITSTATUS(child_status) == 0)))
// {
// success_or_fail = "not completed successfully";
// }
// else
// {
// success_or_fail = "completed successfully";
// }
// snprintf(msg_buffer, CHAR_MAX, "Command %s has %s\n", recipient, success_or_fail);
// fputs(msg_buffer, stdout);
// }
fputs("All done, bye!\n", stdout);
free(msg_buffer);
return 0;
}
int get_num_of_chars(const char string[])
{
int
i = 0,
num_of_chars = 0;
while (string[i++] != '\0')
{
if (string[i] != ' ' && string[i] != '\t')
{
num_of_chars++;
}
}
return num_of_chars;
}
int get_num_of_words(const char string[])
{
int
i = 0,
num_of_words = 0;
bool is_not_separator = false;
while (string[i++] != '\0')
{
if (string[i] == ' ' || string[i] == '\t')
{
is_not_separator = false;
}
else if (!is_not_separator)
{
is_not_separator = true;
num_of_words++;
}
}
return num_of_words;
}
void preparing_the_command(int num_of_words, char string[], char *command[])
{
char *token;
for (int j = 0; j < num_of_words && (token = strtok_r(string, " ", &string)); j++)
{
command[j] = token;
}
command[num_of_words] = (void *) NULL;
}
q1.h
#ifndef ASSIGNMENT2Q1_Q1_H
#define ASSIGNMENT2Q1_Q1_H
/***************
** LIBRARIES **
***************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*************
** STRUCTS **
*************/
typedef struct
{
pid_t pid;
char *command;
} Tuple;
/*************************
** FUNCTION PROTOTYPES **
*************************/
int get_num_of_chars(const char string[]);
int get_num_of_words(const char string[]);
void preparing_the_command(int num_of_words, char string[], char *command[]);
#endif //ASSIGNMENT2Q1_Q1_H
I am trying to write a mock shell that saves command line history and overrides the signal action for SIGINT to trigger printing the previous 10 commands entered by the user. As far as I am aware, everything from handling all of the signals to updating cursor and running commands using execvp works fine.
I however am running into 2 problems that I am having a hard time trying to wrap my head around.
Trouble trying to actually copy the contents of the input buffer to my own c-string vector, namely histv. After calling read and storing user input in buf, I try to copy the contents of buf to the location at histv[cursor % MAX_HISTORY] (the reason I am using modulus is because it seems easier to use a kind of circular array instead of handling the case that cursor rises to some number greater than MAX_HISTORY)
When I try running the process as a background process I always get an execvp error, even when the command is valid. I know it's happing after the parent process gets the SIGUSR2 signal and creates a new child to run the commands. So I am assuming it has something to do with what happens to argv after the child process kills itself and raises SIGUSR2
Below is the code for the entire program. It is a bit messy in some spots, but overall it's rather simple. I have used all of memcpy, strcpy, and strncpy to no avail. They all compile and run without errors, but none of them seem to do anything.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
// limits
#define MAX_LINE 80
#define MAX_HISTORY 10
// function headers
void print_history(unsigned int const, char**);
void handler_func(int);
void read_func(char*[], char[], char**, unsigned int const);
int parse_args(char*, char**, size_t);
// globals
volatile sig_atomic_t sig_caught = 0;
void print_history (const unsigned int cursor, char **histv) {
int temp = (cursor > MAX_HISTORY) ? (cursor - MAX_HISTORY) : 0;
puts("\n\nprinting the previous ten commands...");
printf("cursor %d", cursor);
for (int i = 1; temp < cursor; temp++) {
printf("%d%s\n", i++, histv[temp % MAX_HISTORY]);
}
}
void handler_func(int sig)
{
/* update loop control variable */
sig_caught = 1;
}
int main(void)
{
// declare sigaction struct
struct sigaction sigactor;
// initialize sigaction struct
sigactor.sa_handler = handler_func;
sigemptyset(&sigactor.sa_mask);
sigactor.sa_flags = 0;
// set up sigaction for SIGINT
if (sigaction(SIGINT, &sigactor, NULL) == -1) {
perror("siagction() failed");
_exit(EXIT_FAILURE);
}
// set the buffer to no buffering
setvbuf(stdout, NULL, _IONBF, 0);
unsigned int cursor = 0;
/* initlialize history vector */
char **histv = (char**)malloc(sizeof(char*) * MAX_HISTORY);
for (int i = 0; i < MAX_HISTORY; i++)
histv[i] = (char*)malloc(sizeof(char) * MAX_LINE);
// enter shell loop
while (1) {
/* fork process and get child pid */
int cpid;
char *argsv[MAX_LINE/2+1];
char buf[MAX_LINE];
while(!sig_caught) {
cpid = fork();
/* child */
if (cpid == 0) {
read_func(argsv, buf, histv, cursor);
}
/* fork error */
else if (cpid < 0) {
perror("Error forking process");
_exit(EXIT_FAILURE);
}
/* parent process begins here */
else {
/* variable to store status returned from child*/
int cstatus;
/* suspend parent until child exits *
* store return status in cstatus */
waitpid(cpid, &cstatus, 0);
/* get status from child process and check for SIGTERM *
* SIGTERM is raised by child when someone enters '!q' */
switch(WTERMSIG(cstatus))
{
/* user wants to quit */
case SIGTERM:
puts("User issued quit command");
for (int i = 0; i < MAX_HISTORY; i++)
free((void *)histv[i]);
free((void *)histv);
_exit(EXIT_SUCCESS);
/* invalid string length */
case SIGUSR1:
puts("Please enter a valid string");
break;
/* background process */
case SIGUSR2:
cpid = fork();
if (cpid < 0) perror("Error forking process...");
else if (cpid == 0) {
if (execvp(argsv[0], argsv) < 0) {
--cursor;
perror("execvp");
kill(getpid(), SIGUSR1);
}
}
}
if (!sig_caught) cursor++;
}
}// signal loop
kill (cpid, SIGTERM);
print_history(cursor, histv);
fflush(stdout);
sig_caught = 0;
}
}
void read_func(char *argsv[], char buf[], char *histv[], unsigned int const cursor)
{
printf("\nCMD > ");
int background = 0;
size_t length = read(STDIN_FILENO, buf, MAX_LINE);
if (length > 80 || length <= 0) kill(getpid(), SIGUSR1);
/* copy buffer into history and update cursor */
memcpy(histv[cursor % MAX_HISTORY], buf, length);
printf("cursor %d", cursor);
/* parse arguments and return number of arguments */
background = parse_args(buf, argsv, length);
/* user entered quit command or string is invalid */
if (background == -1) kill(getpid(), SIGTERM);
/* signal parent to run process in the background */
if (background == 1) kill(getpid(), SIGUSR2);
/* run command */
if (execvp(argsv[0], argsv) < 0) {
perror("execvp");
_exit(EXIT_FAILURE);
}
}
int parse_args(char buf[], char *argsv[], size_t length)
{
int i, /* loop index for accessing buf array */
start, /* index where beginning of next command parameter is */
ct, /* index of where to place the next parameter into args[] */
bckg; /* background flag */
/* read what the user enters on the command line */
ct = 0;
start = -1;
bckg = 0;
if (buf[0] == '!' && buf[1] == 'q') return -1;
/* examine every character in the buf */
for (i = 0; i < length; i++) {
switch (buf[i]){
case ' ':
case '\t': /* argument separators */
if(start != -1){
argsv[ct] = &buf[start]; /* set up pointer */
ct++;
}
buf[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
argsv[ct] = &buf[start];
ct++;
}
buf[i] = '\0';
argsv[ct] = NULL; /* no more arguments to this command */
break;
case '&':
bckg = 1;
buf[i] = '\0';
break;
default: /* some other character */
if (start == -1)
start = i;
}
}
argsv[ct] = NULL; /* just in case the input line was > 80 */
return bckg;
}
Side note, the parse_args function was initially given to us and I changed it a bit to work with the rest of program, but for the most part I did not write that function.
Please go easy on me, it's been a long time since I've used C for anything and a lot of what I am doing here took a lot of effort to begin understanding how this program behaves. (~:
So both of the problems was with the fact that the memory wasn't being shared, as o11c described. I fixed the issue by using mmap instead of malloc which in turn simplified my program as I no longer had to handle the memory management. Changes are described below.
char **histv = (char**)malloc(sizeof(char*) * MAX_HISTORY);
for (int i = 0; i < MAX_HISTORY; i++)
histv[i] = (char*)malloc(sizeof(char) * MAX_LINE);
was changed to
char **histv = (char**)mmap(NULL, (sizeof(char*) * MAX_HISTORY), (PROT_READ | PROT_WRITE), (MAP_SHARED | MAP_ANONYMOUS), -1, 0);
for (int i = 0; i < MAX_HISTORY; i++) histv[i] = (char*)mmap(NULL, (sizeof(char) * MAX_LINE), (PROT_READ | PROT_WRITE), (MAP_SHARED | MAP_ANONYMOUS), -1, 0);
While I do not know if this is the best way to do this, it solved my problem.
Also note that I did explicitly unmap the memory using munmap even though it technically should be handled automatically on exit.
I wanted to use libgps to interface with gpsd daemon. That's why I've implemented a little testing application in order to extract a value from a specific satellite.
The documentation on its HOWTO page tells us that
The tricky part is interpreting what you get from the blocking read.
The reason it’s tricky is that you’re not guaranteed that every read
will pick up exactly one complete JSON object from the daemon. It may
grab one response object, or more than one, or part of one, or one or
more followed by a fragment.
As recommended the documentation, the PACKET_SET mask bit is checked before doing anything else.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = -1;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
if(!gps_waiting(&gpsdata, 1000000))
{
(void)gps_close(&gpsdata);
}
else
{
if(gps_read(&gpsdata) == -1)
{
return (-1);
}
else
{
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS; i++)
{
for (j = 0; j < gpsdata->satellites_visible; j++)
{
if(gpsdata->PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata->elevation[i];
ret = 1;
break;
}
}
if(gpsdata->PRN[i] == thread_args.ID)
{
break;
}
}
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
As recommended in the documentation too, I had a look at cgps and gpxlogger for example codes, but the subtleties of libgps escape me. A while loop has been added before gps_waiting() in order to get, at least, one entire response object. Before introducing pthread, I noted that call the function test_result() just after start_test() take few seconds before returning an answer. By using a thread I thought that 3 would be imediately returned, then 3 or 4 .. but it's not ! I am still losing few seconds. In addition, I voluntarily use pthread_tryjoin_np() because its man page says
The pthread_tryjoin_np() function performs a nonblocking join with the thread
Can anybody give me his help, I guess that I understand something wrongly but I am not able to say about which part yet? Basically, why I come into the do while loop at least four times before returning the first value ?
EDIT 1 :
After reading the documentation HOWTO again I highlight the lines :
The fact that the data-waiting check and the read both block means that, if your application has to deal with other input sources than the GPS, you will probably have to isolate the read loop in a thread with a mutex lock on the gps_data structure.
I am a little bit confusing. What does it really mean ?
Your loop is executing multiple times before returning a full packet because you do not have a sleep condition. Therefore each time the daemon registers a packet (even when not a full NMEA message), the gps_waiting() function returns. I'd recommend sleeping at least as long as it takes your GPS to register a full message.
For example, if you expect GPPAT messages, you could reasonably expect to have 12 characters in the message. Thus at 9600 baud, that would take 1/17.5 seconds or about 57 ms. In this case, your code could look like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <gps.h>
#include <pthread.h>
pthread_t t_thread;
struct t_args {
unsigned int ID;
};
unsigned int status = 0;
int elevation;
int p_nmea(void *targs);
void start_test(void)
{
struct t_args *args = malloc(sizeof *args);
status = 1;
args->ID = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0)
{
perror("create: \n");
}
}
int test_result(int * Svalue)
{
int res;
if(status == 1)
{
void * t_res;
if(pthread_tryjoin_np(t_thread, &t_res) != 0)
{
status = 1;
}
else
{
if((int)t_res == 1)
{
res = 3;
*Svalue = elevation;
elevation = 0;
}
else
{
res = 4;
}
}
}
return res;
}
int p_nmea(void *targs)
{
struct t_args *thread_args = targs;
struct gps_data_t gpsdata;
int ret = 0;
int count = 10;
int i,j;
if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0)
{
(void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno));
return (-1);
}
else
{
(void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
do
{
ret = 0; // Set this here to allow breaking correctly
usleep(50000); // Sleep here to wait for approx 1 msg
if(!gps_waiting(&gpsdata, 1000000)) break;
if(gps_read(&gpsdata) == -1) break;
if(gpsdata.set & PACKET_SET)
{
for (i = 0; i < MAXCHANNELS && !ret; i++)
{
for (j = 0; j < gpsdata.satellites_visible; j++)
{
if(gpsdata.PRN[i] == thread_args.ID)
{
elevation = (int)gpsdata.elevation[i]; // Be sure to not deref structure here
ret = 1;
break;
}
}
}
--count;
}while(count != 0);
}
(void)gps_stream(&gpsdata, WATCH_DISABLE, NULL);
(void)gps_close(&gpsdata);
(void)free(thread_args);
(void)pthread_exit((void*) ret);
}
Alternatively, you could just set your count higher and wait for the full message.
I'm writing a program in c that basically copies files, but I'm getting this error: Segmentation fault (core dumped). From what I'm reading I think it's because I'm trying to access memory that hasn't been allocated yet. I'm a newbie when it comes to c and I suck at pointers, so I was wondering if you guys could tell me which pointer is causing this and how to fix it if possible. Btw, this program is supposed to be a daemon, but I haven't put anything inside the infinite while loop at the bottom.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(int c, char *argv[]) {
char *source, *destination;
char *list1[30], *list2[30], *listDif[30];
unsigned char buffer[4096];
int i=0, x=0, sizeSource=0, sizeDest=0, sizeDif=0;
int outft, inft,fileread;
int sleeper;
struct dirent *ent, *ent1;
//Check number of arguments
if(c<3)
{
printf("Daemon wrongly called\n");
printf("How to use: <daemon name> <orginDirectory> <destinationDirectory> \n");
printf("or : <daemon name> <orginDirectory> <destinationDirectory> <sleeperTime(seconds)>");
return 0;
}
//Checks if sleeper time is given or will be the default 5minutes
/*if(c=4)
{
char *p;
errno = 0;
long conv = strtol(argv[3], &p, 10);
if(errno != 0 || *p != '\0')
{
printf("Number given for sleeper incorrect, it has to be an integer value.\n");
return(0);
} else
{
sleeper = conv;
}
} else
{
sleeper = 300;
}*/
//Get path of directories from arguments
source = argv[1];
destination = argv[2];
//Check if directories exist
DIR* dirSource = opendir(source);
if (!dirSource)
{
printf("Source directory incorrect\n");
return 0;
}
DIR* dirDest = opendir(destination);
if (!dirDest)
{
printf("Destination directory incorrect\n");
return 0;
}
/* save all the files and directories within directory */
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = strdup(ent->d_name);
sizeSource++;
if(sizeSource>=30){break;}
}
closedir(dirSource);
while((ent1 = readdir (dirDest)) != NULL) {
list2[sizeDest] = strdup(ent1->d_name);
sizeDest++;
if(sizeDest>=30){break;}
}
closedir(dirDest);
/* Verify the diferences between the directories and save them */
int z;
int dif = 0; //0 - False | 1 - True
printf("Diferenças:\n");
for(i=0;i<sizeSource;i++){
dif = 0;
for(z=0;z<sizeDest;z++){
if(strcmp(list1[i],list2[z])==0){ //If there is no match, it saves the name of the file to listDif[]
dif = 1;
break;
}
}
if(dif==0) {
printf("%s\n",list1[i]);
listDif[sizeDif] = list1[i];
sizeDif++;
}
}
/* This code will copy the files */
z=0;
while(z!=sizeDif){
// output file opened or created
char *pathSource, *pathDest;
strcpy(pathSource, source);
strcat(pathSource, "/");
strcat(pathSource, listDif[z]);
strcpy(pathDest, destination);
strcat(pathDest, "/");
strcat(pathDest, listDif[z]);
// output file opened or created
if((outft = open(pathDest, O_CREAT | O_APPEND | O_RDWR))==-1){
perror("open");
}
// lets open the input file
inft = open(pathSource, O_RDONLY);
if(inft >0){ // there are things to read from the input
fileread = read(inft, buffer, sizeof(buffer));
printf("%s\n", buffer);
write(outft, buffer, fileread);
close(inft);
}
close(outft);
}
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
//sleep(5); /* wait 5 seconds */
}
exit(EXIT_SUCCESS);
}
The result of ls is:
ubuntu#ubuntu:~/Desktop$ ls
Concatenar_Strings.c core D2 daemon.c examples.desktop
Concatenar_Strings.c~ D1 daemon daemon.c~ ubiquity.desktop
D1 and D2 are folders, and in D1 are three text documents that I want to copy into D2.
One other question, is this a delayed error or an immediate one? Because I doubt this message would appear on a code line that with two integers.
Thanks in advance guys.
This loop is wrong:
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = ent->d_name;
Probably, ent points to the same memory block every time, and the readdir function updates it. So when you save that pointer, you end up with your list containing invalid pointers (probably end up all pointing to the same string). Further, the string may be deallocated once you got to the end of the directory.
If you want to use the result of readdir after closing the directory or after calling readdir again you will need to take a copy of the data. In this case you can use strdup and it is usually good style to free the string at the end of the operation.
This may or may not have been the cause of your segfault. Another thing to check is that you should break out of your loops if sizeSource or sizeDest hits 30.
In the strcmp loop, you should really set dif = 0 at the start of the i loop, instead of in an else block.
Update: (more code shown by OP)
char *pathSource, *pathDest;
strcpy(pathSource, source);
You are copying to a wild pointer, which is a likely cause of segfaults. strcpy does not allocate any memory, it expects that you have already allocated enough.
One possible fix would be:
char pathSource[strlen(source) + 1 + strlen(listDif[z]) + 1];
sprintf(pathSource, "%s/%s", source, listDif[z]);
Alternatively (without using VLA):
char pathSource[MAX_PATH]; // where MAX_PATH is some large number
snprintf(pathSource, MAX_PATH, "%s/%s", source, listDif[z]);
Do the same thing for pathDest.
NB. Consider moving the closedir lines up to after the readdir loops; generally speaking you should open and close a resource as close as possible to the times you start and finish using them respectively; this makes your code easier to maintain.
I have an assignment to make a shell in C code, and I have a solution that works most of the time. My solution works if the program exists, and I can exit my shell with either Control-D or by typing exit. But when I try a command that I know doesn't exist, my shell will print an error message saying command not found but I will have to either type exit or press Control-D the same amount of times as a invalid command was entered i.e. if I type a wrong command 3 times, I then have to hit Control-D 3 times. I really don't know what is going on here. I checked all the variables and read is -1 when I press Control-D but the if statement seems to be skipped.
Here is the parts of my source code that I think the problem is in:
comp20200Shell.c
#include "comp20200Shell_header.h"
#include <signal.h>
/*
* Name: ****
* Student Number: ****
* Email: ****
*
* This is the main function of my shell implementation.
*
*/
int main(void)
{
bool end_program = false;
size_t length = 0;
ssize_t read;
char* current_directory = NULL;
char* current_time = NULL;
/* Sets up signal handler to catch SIGINT*/
if(signal(SIGINT, sigintHandler) == SIG_ERR)
{
error("An error occured while setting a signal handler\n");
}
/* Infinitive loop, so after command or invalid comman will prompt again*/
while(end_program != true)
{
char* input = NULL;
/* Gets current working directory */
current_directory = return_current_directory();
/* Gets current date and time */
current_time = return_time();
/* Prints Prompt */
printf("%s\x5b%s\x5d %s%s %s%s%s", MAGENTA_TEXT, current_time, GREEN_TEXT, current_directory, BLUE_TEXT, PROMPT, RESET_COLOUR);
/* Frees the pointers returned by return_time() and return_current_directory() */
free(current_time);
free(current_directory);
/* Reads one line from standard input */
read = getline(&input, &length, stdin);
/* Checks if ctrl d, i.e. end of file is found or exit is typed */
if(strcmp(input, "exit\n") == 0 || read == -1)
{
if(read == -1)
{
putchar('\n');
}
/* Frees input */
free(input);
return(0);
}
/* Removes newline character that will be at the end */
remove_trailing_newline(input);
/* Passes input to process input, and the return value is passed in to process errors */
process_errors(process_input(&input));
/* Frees input */
free(input);
}
return(0);
}
process_input.c
#include "comp20200Shell_header.h"
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
* Name: ****
* Student Number: ****
* Email: ****
*
* This function is used to process the command entered by the user
*
* return: the error value or 0 when everything whent ok
* arguments: the command entered by the user
*
*/
int process_input(char** input)
{
bool redirect_stdout = false;
bool redirect_stderr = false;
pid_t child_pid;
int child_status;
char** argument = malloc(sizeof(char*));
int count = 0;
char* temp = strtok(*input, " ");
while(temp != NULL)
{
argument[count] = temp;
count ++;
argument = realloc(argument, (count+2) * sizeof(char *));
temp = strtok(NULL, " ");
}
argument[count] = NULL;
if(argument[0] == NULL)
{
return(0);
}
else if(strcmp(argument[0], "cd") == 0)
{
return(change_directory(argument[1]));
}
int index;
for(index = 1; argument[index] != NULL; index++)
{
if(strcmp(argument[index], ">0") == 0)
{
if(argument[index + 1] == NULL)
{
return(EINVAL);
}
redirect_stdout = true;
break;
}
else if(strcmp(argument[index], ">2") == 0)
{
if(argument[index + 1] == NULL)
{
return(EINVAL);
}
redirect_stderr = true;
break;
}
}
child_pid = fork();
if(child_pid == 0)
{
int file;
if(redirect_stdout == true)
{
file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(file, 1);
edit_arguments(argument, index);
execvp(argument[0], argument);
return(-1);
}
else if(redirect_stderr == true)
{
file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(file, 2);
edit_arguments(argument, index);
execvp(argument[0], argument);
return(-1);
}
execvp(argument[0], argument);
return(-1);
}
else
{
wait(&child_status);
}
return(child_status);
}
comp20200Shell_header.h
/*
* Name: ****
* Student Number: ****
* Email: ****
*
* This is my header file, It includes all common used headerfiles on the top.
* Any specific header file that is only used once will be included with the .c file that needs it.
*
*/
/* included headerfiles begin */
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
/* included headerfiles end */
/* defenitions begin */
#define PROMPT "# "
#define BUFFER_SIZE 1024
#define BLUE_TEXT "\x1B[34m"
#define MAGENTA_TEXT "\x1B[35m"
#define GREEN_TEXT "\x1B[32m"
#define RESET_COLOUR "\x1B[0m"
/* defenitions end */
/* Function prototypes begin */
void remove_trailing_newline(char *input);
void sigintHandler(int sig_num);
int process_input(char** input);
char* return_time(void);
void error(const char *fmt, ...);
int change_directory(char* path);
char* return_current_directory(void);
void process_errors(int return_value);
void edit_arguments(char** argument, int index);
/* Function prototypes end */
I have omitted the rest of the source code as I don't think the problem is there.
In your child, after the call to execvp you need to call exit(EXIT_FAILURE); instead of return -1;. Otherwise your child will continue running, and will interpret the next command (that is why you need to exit N times where N is the number of inexistant commands you tried to invoke).
After the change, your parent process will see that the child terminated with a non-zero return code and should interpret the error code. There is no real way to distinguish between a failure from the execvp (due to a non-existent command) or from the invoked process. I would recommend printing the error from execvp if there is one in the child before the exit.
Note that if execvp succeed, it will never return, so the code following a call to execvp can only be executed if the command failed.
So, my recommendation is doing this:
if(child_pid == 0)
{
int file;
if(redirect_stdout == true)
{
file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(file, 1);
edit_arguments(argument, index);
execvp(argument[0], argument);
perror("execvp");
exit(EXIT_FAILURE);
}
else if(redirect_stderr == true)
{
file = open(argument[index + 1], O_WRONLY|O_CREAT|O_TRUNC, 0666);
dup2(file, 2);
edit_arguments(argument, index);
execvp(argument[0], argument);
perror("execvp");
exit(EXIT_FAILURE);
}
execvp(argument[0], argument);
perror("execvp");
exit(EXIT_FAILURE);
}
else
{
wait(&child_status);
}
You should be doing exit(1); or equivalent instead of return(-1);. You might want to use _exit(1);, or _exit(255); (or _exit(-1);, but it is equivalent to _exit(255);). You might well want to print an error message to standard error before you exit.
When you don't exit, you end up with two, then three, then N shells all trying to read input from the terminal. You have to make each one quit separately by indicating EOF with Control-D. If you tried typing commands, then it would become a lottery which shell gets each character, and that leads to chaos (and grave danger; you may have thought you typed grep fungible form.file | tr -d 'fr' > /tmp/x33 but if one of the shells got rm -fr /, you've got trouble!).
Instead of return -1, you can use exit(1) or exit (-1) to exit from that portion if it fails to execute due to some error.