I've written the following program, that simulates the work of semaphore. There are three functions: lock, unlock, lockpath.
lock = opens the file; checks if the file already exists, and if it does, puts the current process to sleep. If the file didn't exist, it is created and TRUE is returned.
unlock = deletes the file
lockpath = returns the path name corresponding to the file that might be created.
Here's the source code:
#include <unistd.h>
//exit();
#include <stdlib.h>
//errno
#include <errno.h>
//creat(..)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//strcat, strcpy
#include <string.h>
//For err_sys
#include <stdio.h>
#define LOCKDIR "/tmp/"
#define MAXTRY 3
#define WAITTIME 5
enum BOOLEAN{TRUE, FALSE};
void err_sys(const char* x) {
perror(x);
exit(1);
}
static char* lockpath(char* name) {
static char path[20];
strcpy(path, LOCKDIR);
return (strcat(path, name));
}
int lock(char* name) {
char *path;
int fd, incerc;
extern int errno;
path = lockpath(name);
int try = 0;
while ((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0
&& errno == EEXIST) {
if (++try >= MAXTRY)
return FALSE;
sleep(WAITTIME);
}
if (fd < 0 || close(fd) < 0)
err_sys("lock");
return TRUE;
}
void unlock(char* name) {
if (unlink(lockpath(name)) < 0)
err_sys("unlock");
}
int main(void) {
pid_t child_process;
child_process = fork();
char* sem_file_name = "test_semaf";
if (child_process != 0)
{
printf("\nParent process ID: %d", getpid());
}
else
{
printf("\nChild process ID: %d", getpid());
}
if (lock(sem_file_name))
{
printf("\nProcess with ID: %d", getpid());
printf("\nonly, has access to %s", strcat(LOCKDIR, sem_file_name)); //****
unlock(sem_file_name);
} else {
printf("\nProcess with ID: %d", getpid());
printf("\nwas unable to get access to %s", strcat(LOCKDIR, sem_file_name));
}
return 0;
}
The line at which the program stops is marked with: ****
The error is:
Program received signal SIGSEGV, Segmentation fault.
__strcat_ssse3 () at ../sysdeps/x86_64/multiarch/strcat-ssse3.S:571
571 ../sysdeps/x86_64/multiarch/strcat-ssse3.S: No such file or directory.
The problem is that I get Segmentation Fault, and can't find where's the problem. To me, everything's fine. A process is supposed to create file X. Then, if another process tries to create it's own file X, it is not allowed; the process is put to sleep. This second process is allowed to make MAXTRY attempts. If it does not succeed after MAXTRY attempts, the lock() function returns FALSE. Finally, when a process, that has successfully created his own X file, doesn't need it now, the file X is deleted.
Can you, please, tell what do you think is the problem with this program? Thank you in advance.
EDIT :
Here's the link to the page that explains why lockpath() function isn't correct.
Is returning a pointer to a static local variable safe?
This is the cause of your crashes:
strcat(LOCKDIR, sem_file_name)
Here you try to append to a literal string constant.
You should use the lockpath function here as well.
The problem seems to be in your misunderstanding of strcat() function. The function appends string in second parameter to the string in first parameter - but you need to ensure there is enough space for the data. Read the man page.
That means that
char * dest = "whatever";
strcat(dest, anything_else);
is always wrong. What you want is
char dest[SIZE] = "whatever";
strcat(dest, anything_else);
where SIZE is big enough for the buffer to be able to contain the whole concatenated string.
Also, your lockpath() function is broken. See this answer to learn why. You need to create the dest buffer outside the lockpath() function and pass it to it as a parameter.
Related
I'm new to using the pthread library in C and I have an assignment for my class to write a simple program using them. The basic description of the program is it takes 1 or more input files containing website names and 1 output file name. I then need to create 1 thread per input file to read in the website names and push them onto a queue. Then I need to create a couple of threads to pull those names off of the queue, find their IP Address, and then write that information out to the output file. The command line arguments are expected as follows:
./multi-lookup [one or more input files] [single output file name]
My issue is this. Whenever I run the program with only 1 thread to push information to the output file then everything works properly. When I make it two threads then the program hangs and none of my testing "printf" statements are even printed. My best guess is that deadlock is occurring somehow and that I'm not using my mutexes properly but I can't figure out how to fix it. Please help!
If you need any information that I'm not providing then just let me know. Sorry for the lack of comments in the code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include "util.h"
#include "queue.h"
#define STRING_SIZE 1025
#define INPUTFS "%1024s"
#define USAGE "<inputFilePath> <outputFilePath>"
#define NUM_RESOLVERS 2
queue q;
pthread_mutex_t locks[2];
int requestors_finished;
void* requestors(void* input_file);
void* resolvers(void* output_file);
int main(int argc, char* argv[])
{
FILE* inputfp = NULL;
FILE* outputfp = NULL;
char errorstr[STRING_SIZE];
pthread_t requestor_threads[argc - 2];
pthread_t resolver_threads[NUM_RESOLVERS];
int return_code;
requestors_finished = 0;
if(queue_init(&q, 10) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_init failed!\n");
if(argc < 3)
{
fprintf(stderr, "Not enough arguments: %d\n", (argc - 1));
fprintf(stderr, "Usage:\n %s %s\n", argv[0], USAGE);
return 1;
}
pthread_mutex_init(&locks[0], NULL);
pthread_mutex_init(&locks[1], NULL);
int i;
for(i = 0; i < (argc - 2); i++)
{
inputfp = fopen(argv[i+1], "r");
if(!inputfp)
{
sprintf(errorstr, "Error Opening Input File: %s", argv[i]);
perror(errorstr);
break;
}
return_code = pthread_create(&(requestor_threads[i]), NULL, requestors, inputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
outputfp = fopen(argv[i+1], "w");
if(!outputfp)
{
sprintf(errorstr, "Errord opening Output File: %s", argv[i+1]);
perror(errorstr);
exit(1);
}
for(i = 0; i < NUM_RESOLVERS; i++)
{
return_code = pthread_create(&(resolver_threads[i]), NULL, resolvers, outputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
for(i = 0; i < (argc - 2); i++)
pthread_join(requestor_threads[i], NULL);
requestors_finished = 1;
for(i = 0; i < NUM_RESOLVERS; i++)
pthread_join(resolver_threads[i], NULL);
pthread_mutex_destroy(&locks[0]);
pthread_mutex_destroy(&locks[1]);
return 0;
}
void* requestors(void* input_file)
{
char* hostname = (char*) malloc(STRING_SIZE);
FILE* input = input_file;
while(fscanf(input, INPUTFS, hostname) > 0)
{
while(queue_is_full(&q))
usleep((rand()%100));
if(!queue_is_full(&q))
{
pthread_mutex_lock(&locks[0]);
if(queue_push(&q, (void*)hostname) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_push failed on %s\n", hostname);
pthread_mutex_unlock(&locks[0]);
}
hostname = (char*) malloc(STRING_SIZE);
}
printf("%d\n", queue_is_full(&q));
free(hostname);
fclose(input);
pthread_exit(NULL);
}
void* resolvers(void* output_file)
{
char* hostname;
char ipstr[INET6_ADDRSTRLEN];
FILE* output = output_file;
int is_empty = queue_is_empty(&q);
//while(!queue_is_empty(&q) && !requestors_finished)
while((!requestors_finished) || (!is_empty))
{
while(is_empty)
usleep((rand()%100));
pthread_mutex_lock(&locks[0]);
hostname = (char*) queue_pop(&q);
pthread_mutex_unlock(&locks[0]);
if(dnslookup(hostname, ipstr, sizeof(ipstr)) == UTIL_FAILURE)
{
fprintf(stderr, "DNSlookup error: %s\n", hostname);
strncpy(ipstr, "", sizeof(ipstr));
}
pthread_mutex_lock(&locks[1]);
fprintf(output, "%s,%s\n", hostname, ipstr);
pthread_mutex_unlock(&locks[1]);
free(hostname);
is_empty = queue_is_empty(&q);
}
pthread_exit(NULL);
}
Although I'm not familiar with your "queue.h" library, you need to pay attention to the following:
When you check whether your queue is empty you are not acquiring the mutex, meaning that the following scenario might happen:
Some requestors thread checks for emptiness (let's call it thread1) and just before it executes pthread_mutex_lock(&locks[0]); (and after if(!queue_is_full(&q)) ) thread1 gets contex switched
Other requestors threads fill the queue up and when out thread1 finally gets hold of the mutex if will try to insert to the full queue. Now if your queue implementation crashes when one tries to insert more elements into an already full queue thread1 will never unlock the mutex and you'll have a deadlock.
Another scenario:
Some resolver thread runs first requestors_finished is initially 0 so (!requestors_finished) || (!is_empty) is initially true.
But because the queue is still empty is_empty is true.
This thread will reach while(is_empty) usleep((rand()%100)); and sleep forever, because you pthread_join this thread your program will never terminate because this value is never updated in the loop.
The general idea to remember is that when you access some resource that is not atomic and might be accessed by other threads you need to make sure you're the only one performing actions on this resource.
Using a mutex is OK but you should consider that you cannot anticipate when will a context switch occur, so if you want to chech e.g whether the queue is empty you should do this while having the mutex locked and not unlock it until you're finished with it otherwise there's no guarantee that it'll stay empty when the next line executes.
You might also want to consider reading more about the consumer producer problem.
To help you know (and control) when the consumers (resolver) threads should run and when the producer threads produce you should consider using conditional variables.
Some misc. stuff:
pthread_t requestor_threads[argc - 2]; is using VLA and not in a good way - think what will happen if I give no parameters to your program. Either decide on some maximum and define it or create it dynamically after having checked the validity of the input.
IMHO the requestors threads should open the file themselves
There might be some more problems but start by fixing those.
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'm writing a program that when run from two separate bash sessions as two separate processes, opens a named pipe between the two to allow strings to be sent from one to the other.
When the process is first executed from one terminal, it checks stat(fname, buf) == -1 to see if a file at path fname exists and if not, creates it. The process then assumes that since it was the one to make the FIFO, it is the one that will be sending messages through it and continues accordingly.
After that occurs, the program can then be run from another terminal that should determine that it will be the receiver of messages through the pipe by checking stat(fname, buf) == -1. The condition should return false now, and stat(fname, buf) itself should return 0 because there exists a file at fname now.
But for reasons I am unable to discern, when the second process is run, stat(fname, buf) still returns -1. The variable errno is set to EFAULT. The man page for stat() only decribes EFAULT as "Bad address." Any help determining why the error occurs or what is meant by "Bad address." would be greaty appreciated.
I've verified that the file is indeed created by the first process as intended. The first process waits at the line pipe = open(fname, O_WRONLY); because it can't continue until the other end of pipe is opened.
Edit: The following is a self-contained implementation of my code. I have confirmed that it compiles and experiences the problem I described here.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_LINE 80
#define oops(m,x) { perror(m); exit(x); }
int main(int argc, char const *argv[]) {
char line[MAX_LINE];
int pipe, pitcher, catcher, initPitcher, quit;
struct stat* buf;
char* fname = "/tmp/absFIFOO";
initPitcher = catcher = pitcher = quit = 0;
while (!quit) {
if (((!pitcher && !catcher && stat(fname, buf) == -1) || pitcher) && !quit) {
// Then file does not exist
if (errno == ENOENT) {
// printf("We're in the file does not exist part\n");
if (!pitcher && !catcher) {
// Then this must be the first time we're running the program. This process will take care of the unlink().
initPitcher = 1;
int stat;
if (stat = mkfifo(fname, 0600) < 0)
oops("Cannot make FIFO", stat);
}
pitcher = 1;
// open a named pipe
pipe = open(fname, O_WRONLY);
printf("Enter line: ");
fgets(line, MAX_LINE, stdin);
if (!strcmp(line, "quit\n")) {
quit = 1;
}
// actually write out the data and close the pipe
write(pipe, line, strlen(line));
close(pipe);
}
} else if (((!pitcher && !catcher) || catcher) && !quit) {
// The first condition is just a check to see if this is the first time we've run the program. We could check if stat(...) == 0, but that would be unnecessary
catcher = 1;
pipe = open("/tmp/absFIFO", O_RDONLY);
// set the mode to blocking (note '~')
int flags;
flags &= ~O_NONBLOCK;
fcntl(pipe, F_SETFL, flags); //what does this do?
// read the data from the pipe
read(pipe, line, MAX_LINE);
if (!strcmp(line, "quit\n")) {
quit = 1;
}
printf("Received line: %s\n", line);
// close the pipe
close(pipe);
}
}
if (initPitcher)
unlink(fname);
return 0;
}
You have this piece of code:
struct stat* buf;
...
if (((!pitcher && !catcher && stat(fname, buf) == -1)
When you call stat(), buf isn't initalized and there's no telling what it points to.
You must allocate some storage for it, so stat() has a valid place to store the result.
The easiest thing is to just allocate it on the stack:
struct stat buf;
...
if (((!pitcher && !catcher && stat(fname, &buf) == -1)
You have not shown your code, but EFAULT means 'bad address'. This indicates that you have not properly allocated (or passed) your buffer for stat or the filename (fname).
buf isn't initialised anywhere. What exactly do you expect to happen?
I want to be able to check to see if a file could be opened on Linux (for read or for read and write). However I don't have control of the code which will be opening the file, so I can't do what I would normally do which is to open it and then handle the error.
I appreciate that there will always be race conditions on any check due to permissions changing after the call has returned but before the open call, but I'm trying to avoid some undesirable error logging from a library which I have no control over.
I'm aware of stat, but I'd prefer not to need to try to replicate the logic of checking user IDs and group IDs.
You can use:
access("filename", R_OK);
or
euidaccess("filename", R_OK);
To check if your UID or EUID have read access to a respective file. (UID and EUID will be different if your are running setuid)
Use euidaccess or access, although you almost certainly always want to use the former.
(edit: the reason for adding this was that with this approach you can ensure you can avoid the race conditions. That said, it is quite a tricky approach, so maybe just coping with potential race conditions is a better practical approach).
If your goal is to shield the code that you do not own from unhandled errors, using LD_PRELOAD to intercept the open call itself might be of use. An example of it with malloc is here: Overriding 'malloc' using the LD_PRELOAD mechanism
here my quick improvisation on how you could do it - basically an interceptor that will launch an interactive shell to you to correct the error.
WARNING: lots of open calls actually do fail for legit reasons, e.g. when the program is going over different directories in the path trying to find the file, so treat this code as an educational example only to be used with this example code - if you are any close to real world use, your code definitely will need to be smarter. With all this said, let's get to the meat.
First, the "offensive" program that you do not have the control over:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int res = 0;
printf("About to try to open the file...\n");
res = open("/tmp/unreadable", O_RDONLY);
printf("The result after opening: %d\n", res);
if (res < 0) {
perror("Could not open, and here is what the errno says");
} else {
char buf[1024];
int fd = res;
res = read(fd, buf, sizeof(buf));
printf("Read %d bytes, here are the first few:\n", res);
buf[30] = 0;
printf("%s\n", buf);
close(fd);
}
}
Then the interceptor:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
static int (*real_open)(const char *pathname, int flags, ...)=NULL;
static void __open_trace_init(void)
{
real_open = dlsym(RTLD_NEXT, "open");
if (NULL == real_open) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
return;
}
}
int open(const char *pathname, int flags, ...)
{
if(real_open==NULL)
__open_trace_init();
va_list va;
int res = 0;
do {
if (flags & O_CREAT) {
int mode = 0;
va_start(va, flags);
mode = va_arg(va, int);
va_end(va);
fprintf(stderr, "open(%s, %x, %x) = ", pathname, flags, mode);
res = real_open(pathname, flags, mode);
fprintf(stderr, "%d\n", res);
} else {
fprintf(stderr, "open(%s, %x) = ", pathname, flags);
res = real_open(pathname, flags);
fprintf(stderr, "%d\n", res);
}
if (res < 0) {
printf("The open has returned an error. Please correct and we retry.\n");
system("/bin/sh");
}
} while (res < 0);
return res;
}
And here is how it looks like when running:
ayourtch#ayourtch-lnx:~$ echo This is unreadable >/tmp/unreadable
ayourtch#ayourtch-lnx:~$ chmod 0 /tmp/unreadable
ayourtch#ayourtch-lnx:~/misc/stackoverflow$ LD_PRELOAD=./intercept ./a.out
About to try to open the file...
open(/tmp/unreadable, 0) = -1
The open has returned an error. Please correct and we retry.
open(/dev/tty, 802) = 3
open(/dev/tty, 802) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/lib/terminfo/x/xterm, 0) = 3
open(/etc/inputrc, 0) = 3
sh-4.1$ ls -al /tmp/unreadable
---------- 1 ayourtch ayourtch 19 2011-10-18 13:03 /tmp/unreadable
sh-4.1$ chmod 444 /tmp/unreadable
sh-4.1$ exit
open(/home/ayourtch/.bash_history, 401) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 201) = 3
open(/tmp/unreadable, 0) = 3
The result after opening: 3
Read 19 bytes, here are the first few:
This is unreadable
�0
ayourtch#ayourtch-lnx:~/misc/stackoverflow$
By the way this example also exposes an obvious bug in the first "test" code - I should have checked that the number of the chars read was at least 30 and put the null char accordingly.
Anyway, that code is supposed to be buggy and outside of the control, so it is kind of good to have a bug in it - else you would not need to use this kind of hack :-)
I 'm writing a little program that implements pipes like they work in the shell.
ie:
ls -hal | sort | grep p | wc
it works fine, with the minor issue that on one line, when CMD_NO=n, the comparison i biggerthan CMD_NO does not work, but i!=(CMD_NO-1) does. I'm trying to figure out why in this particular case (the line is ocmmented as TROUBLED LINE in the code) these statements are not equivalent. Many thanks.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0
#define WRITE_END 1
#define CMDS_NO 5
int main (int argc, char **argv)
{
pid_t pid;
int new_fds[2];
int old_fds[2];
char *array[CMDS_NO];
char *param[CMDS_NO];
array[0]="ls";
array[1]="sort";
array[2]="grep";
array[3]="grep";
array[4]="wc";
param[0]="-hal";
param[1]=NULL;
param[2]="p";
param[3]="out";
param[4]=NULL;
for (int i=0; i<CMDS_NO; i++) {
if (i<CMDS_NO) //if there is a next command
pipe(new_fds);
pid=fork();
if (pid==0) { //if child
if (i!=0) { //if there is ap revoius commmand
dup2(old_fds[0], 0);
close(old_fds[0]);
close(old_fds[1]);
}
if (i!=(CMDS_NO-1)) { //TROUBLED LINE i<CMDS_NO does not work,
//if there is a next command
close(new_fds[0]);
dup2(new_fds[1],1);
close(new_fds[1]);
}
execlp(array[i], array[i], param[i], NULL);
} else {
if (i!=0) { //if there is a previous command
close(old_fds[0]);
close(old_fds[1]);
}
if (i<CMDS_NO) { //if there is a next command
old_fds[0] = new_fds[0];
old_fds[1] = new_fds[1];
}
}
}
if (CMDS_NO>1) {
close(old_fds[0]);
close(old_fds[1]);
}
while (1) { //wait for child processes to end
wait(NULL);
if(errno== ECHILD) {
printf("all children ended\n");
break;
}
}
return 0;
}
The condition i<CMDS_NO will always be true because i runs from 0 to CMDS_NO-1. I think you meant to write your condition as i<CMDS_NO-1. Of course, i!=CMDS_NO-1 is equally valid.
Note that this affects several other places where you have if (i<CMDS_NO); these should also read if (i<CMDS_NO-1).
That's normal.
for (int i=0; i<CMDS_NO; i++) {
This means "loop as long as i is less than CMDS_NO". As soon as i == CMDS_NO the loop will stop; so, inside the loop, the highest i will ever reach is CMDS_NO - 1.
PS: CMDS_NO is a really poor variable name, call it MAX_COMMANDS for example