How can I print all PID from /proc/ using C code? - c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#define PATH_LINE 128
void get_pid(){
char full_path[PATH_LINE]="";
int fd;
char pid[6];
for(int i=0 ;i <99999; i++){
fd =0;
memset(full_path,0,PATH_LINE);
strcat(full_path,PROC_PATH);
sprintf(pid, "%d", i);
strcat(full_path,pid);
fd = open(full_path, O_RDONLY);
if(fd != -1){
printf("%s - fd [%d]\n", full_path, fd);
}
}
}
int main(void){
get_pid();
return 0;
}
I expected this code to print all the PIDs I have on my computer.
That is, it will try to open the folder /proc/PID and if it does not return an error, then it will print the PID.
But, he prints me a lot of PIDs that don't really run on my computer...
Is there an error in my code? I'm trying to do this without built-in structs.
terminal output:

I would not loop from 0 to 99998 and try to open all those. It takes time and it misses processes with greater pids than 99998.
I suggest that you use opendir, readdir and closedir in order to read all directory entries in /proc and to filter out those you are interested in.
Example:
#include <dirent.h> // opendir, readdir, closedir
#include <sys/types.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
// a function to check if a string consists of all digits
bool all_digits(const char *str) {
for(; *str != '\0'; ++str) {
if(!isdigit((unsigned char)*str)) {
return false;
}
}
return true;
}
int main(void) {
DIR* dp = opendir("/proc"); // open /proc
if(dp) {
struct dirent *curr; // see: man 3 readdir
while((curr = readdir(dp))) { // read next directory entry
if(all_digits(curr->d_name)) { // d_name holds the leaf name
puts(curr->d_name); // only print if it only has digits
}
}
closedir(dp); // finally close the directory
}
}

I saw one serious problem here.
First of all, it is easier for us to view all processes with find /proc/ -maxdepth 1 -type d 2>/dev/null this command will show you all the processes in /proc/. I ran your code and saw that you are opening files and never closing them. I added one line close(fd); inside the if statement and I got all the processes that run on my machine.
In my opinion, this will fix your problem.
P.S. There is a limit to opened files from your program. Not closing opened files in for loop is what you missed here. You can check what is it with: ulimit -n.

Related

Communicating through pipes and sending messages

I am having trouble communicating with the child process. I am trying to make quick.c simply get an input from stdin and send it to sand.c to capitialise it and send it back to the parent and then print it to stdout. Right now the program asks for an input twice instead of only asking once.
this is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
FILE *f = fdopen(fds[0], "r");
FILE *e = fdopen(test[1], "w");
close(fds[1]);
close(test[0]);
//dup2(fds[0],0); causes infinite loop
dup2(test[1],1);
execlp("./sand", "sand", NULL);
fclose(e);
fclose(f);
} else {
// Parent
// Wrap the pipes
FILE *f = fdopen(fds[1], "w");
FILE *e = fdopen(test[0], "r");
close(fds[0]);
close(test[1]);
fgets(buffer,50, stdin);
fprintf(f,"%s",buffer);
while(fgets(buff, 50, e)) {
printf("Parent receive %s", buff);
}
fflush(stdout);
fclose(f);
fclose(e);
wait(NULL);
}
return 0;
}
This method is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
char buffer[50];
int i = 0;
fgets(buffer,50,stdin);
char chr;
// Loop
while (buffer[i]) {
buffer[i] = toupper(buffer[i]);
i++;
}
fprintf(stdout,"%s",buffer);
return 0;
}
On running the code in my machine, the commented dup2 line does not loop to infinity. That may be because pipe programs run differently on different machines. However, the program terminates after taking input. Here are the things that are wrong with your code:
You aren't waiting for the child to write data to test pipe before printing in the parent. You must put the wait statement after taking input.
You've used file pointers for handling pipes. Pipes are accessed with file descriptors and cause unexpected results when handled with file pointers. Instead of fgets and fprintf, use read and write methods to work with file descriptors.
Error in the execlp command which I've commented.
There are errors regarding buffers, I've commented them in the code where they occur.
This is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
//CHANGED: No need to open file pointers here. Pipes are already open and accessed by file descriptor instead of file pointer. File pointers create trouble when used with pipes. 0 is file descriptor of stdin, 1 for stdout.
close(fds[1]);
close(test[0]);
dup2(fds[0],0);
dup2(test[1],1);
//CHANGED: There was an error with the command you wrote.
//That's because ./sand arg will look for a 'sand' directory which doesn't exist
//This line will throw warnings because execlp requires needs a command as the second argument, but in this case the filename is the command.
//NOTE: before running quick.c, compile sand.c as sand.out and not a.out
execlp("./sand.out",NULL);
printf("Exec Error\n"); //this will only execute if execlp didn't run. Always have this line in your code to know what's happening.
}
else
{
// Parent
// Wrap the pipes
//Got rid of the file pointers
close(fds[0]);
close(test[1]);
//CHANGE: fgets is only used with file pointers. While handling pipes, we work with file descriptors, with which read and write methods are used
int n = read(0,buffer,50); //If this is new to you, I strongly recommend reading manual pages for read and write, but for right now
// The signature should be enough to understand - read/write(int file_descriptor, char *buffer, int number_of_bytes)
write(fds[1],buffer,n);
//MOST IMPORTANT: You need to wait for child after this point. Because test pipe doesn't have data yet which will be received by child.
wait(NULL);
//CHANGE: printf statements do not work well with buffere, because buffers are not terminated with null
//%s specifier will always look for a null or print garbage
//If you still want to use printf, look into $man bzero
while((n = read(test[0],buff, 50))>0)
{
write(1,buff,n);
}
fflush(stdout);
}
return 0;
}
This is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char buffer[50];
int i = 0;
int n = read(0,buffer,sizeof(buffer)); //changed fgets to read, to get number of bytes read.
char chr;
// Loop
// we have number of bytes. So change while to for
for (i=0;i<n;i++)
{
buffer[i] = toupper(buffer[i]);
}
write(1,buffer,n); //Changed fprintf to write to get rid of %s problem.
//Again, to fill remaining places of buffer with null, look up bzero.
//The reason I haven't done that is to not confuse you with so many changed methods.
return 0;
}
Let me know, if the solution also helps you find the source of the infinity loop.

Named pipes in C program (unix)

I have to use 3 processes in order to solve the problem.
First processes gets input from (entered via keyboard) and sends it to the second procces
The second process replaces all the vocals from the text with 12345 (a with 1, e with 2, ...). I got a well working sh script (tested it) that uses sed to do this task. I will put it here.
Thrid process outputs on the screen only the alphanumeric lines. I also got a script that uses grep to do this task and also works fine (tested it).
This processes should communicate trough a named pipe (a FIFO file) and i'm running into some difficulties sending and receiving the data trough the FIFO. When i use the write function to write the data to the FIFO it outputs the data on the screen and when i'm in the second process and i try to read the data from the FIFO it just waits for a new input entered by me.
First process:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main() {
char text[500]; // buffer for the inputed text
char aux[100]; // Also a buffer to save multi-line input into text buffer
char* myfifo = "myfifo";
int fd_text;
printf("\nInput: (to stop giving input just type 0 on a new line):\n");
// Forming the text
while(scanf("%[^\n]%*c", aux) == 1) {
if(strcmp(aux, "0") == 0)
break;
strcat(aux, "\n");
strcat(text, aux);
}
strcat(text, "\0");
// Everything works well reading the input
int returnValue = mkfifo(myfifo, 0666);
if(returnValue < 0) {
printf("mkfifo() failed\nerrno = %d\n", errno);
if(errno == EEXIST)
printf("That file already exists.\n");
}
if(fd_text = open(myfifo, O_WRONLY) < 0) {
printf("error while opening FIFO");
exit(0);
}
int indicator = write(fd_text, text, strlen(text) + 1);
if(indicator == 0) {
printf("error while writing to FIFO");
}
close(fd_text);
return 0;
}
Second process:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text2;
if (fd_text2 = open(myfifo, O_RDONLY) < 0) {
printf("error while opening FIFO");
exit(1);
}
int result = read(fd_text2, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
printf("\n%d bytes were read from the file\n", result);
printf("\nText read from FIFO:\n%s\n", text);
// Saving the text into a txt to perfom the sed command on it
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "w");
fprintf(fp_replace_text, "%s", text);
fclose(fp_replace_text);
system("chmod 777 replace.sh");
system("./replace.sh replace_text.txt");
close(fd_text2);
unlink(myfifo);
return 0;
}
replace.sh
#!/bin/sh
sed -i 's/a/1/gi' $1
sed -i 's/e/2/gi' $1
sed -i 's/i/3/gi' $1
sed -i 's/o/4/gi' $1
sed -i 's/u/5/gi' $1
If i can figure out why communication between process 1 and 2 is not working, process 3 will be the same, so i'm not posting it anymore.
Edit:
Second Process
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text2;
if ((fd_text2 = open(myfifo, O_RDWR)) < 0) {
printf("error while opening pipe");
exit(1);
}
int result = read(fd_text2, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "w");
fprintf(fp_replace_text, "%s", text);
fclose(fp_replace_text);
system("chmod 777 replace.sh");
system("./replace.sh replace_text.txt");
fp_replace_text = fopen("replace_text.txt", "r");
fread(text, 500, 1, fp_replace_text);
fclose(fp_replace_text);
write(fd_text2, text, strlen(text) + 1);
close(fd_text2);
return 0;
}
Third process
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text3;
if ((fd_text3 = open(myfifo, O_RDONLY)) < 0) {
printf("error while opening pipe");
exit(1);
}
int result = read(fd_text3, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "r");
fread(text, 500, 1, fp_replace_text);
fclose(fp_replace_text);
printf("Textul final este:\n");
system("chmod 777 output.sh");
system("./output.sh replace_text.txt");
unlink(myfifo);
return 0;
}
I'm also trying to send the content of the txt filo to the third process trough FIFO (i know i could just use the txt file, but i want to use the FIFO file and read from it again). So i'm use the shell script on the txt file, i read from and and then i want to send what i read from the txt file to the thrid process to FIFO, but after i run the second process the execution stops and doesn't blocks. When i'm writing to the FIFO in the first process, it blocks until the second process reads from the FIFO. Second process keeps executing and third process can't get anything. I guess there has to be something wrong with FIFO principles, that if u want to perform input/output operations on a FIFO both ends should be opened.
The problem (which ought to have cause a warning from your compiler) is in statements like this:
if(fd_text = open(myfifo, O_WRONLY) < 0)
You seem to assume that this will be evaluated from left to right, first assigning a value to fd_text, then testing that value against 0 to give a boolean value which will determine whether to enter the if block. But in C, relational operators (like <) take precedence over assignment operators (like =). So first we compare the result of the open command to 0, then assign the boolean result of that comparison to fd_text. So the file descriptor is lost, and further attempts to reach the fifo will fail. We can correct this with a pair of parentheses:
if((fd_text = open(myfifo, O_WRONLY)) < 0)
More generally, it's a good idea to develop new functionality in isolation as much as possible. If you want to send text to a fifo and receive it, just hard-code some text in the first module, don't have all that code to read user input, and don't attempt the further step of sending the text from the second module to some other process for modification, not until the fifo is working perfectly. Small steps.

Execlp function unable to find binaries

I'm doing a simple assignment involving pipes and exec, heres the code .
#include <apue.h>
#include <stdio.h>
int main(void){
int out[2];
pipe(out);
char file1[1024],file2[1024];
int pid=fork();
if(pid>0){
close(out[0]);
scanf("%s",file1);
scanf("%s",file2);
write(out[1],file1,strlen(file1));
//write(out[1],"\0",1);
write(out[1],file2,strlen(file2));
}
if(pid==0){
int c;
int cnt=0;
close(out[1]);
read(out[0],file1,1024);
read(out[0],file2,1024);
execlp("sort","sort",file1,NULL);
return 0;
}
}
The problem is, the execlp function doesn't find the sort binary,
i also used execl giving it the full path but it doesn't work.
It gives me the erros "sort: no such file or directory"
Exec never gave me this problem, what is it?
The contents of the two write()s will not be separated; the reader just reads them as one block, with the two strings concatenated:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(void){
int out[2];
pipe(out);
char file1[1024],file2[1024];
int pid=fork();
if(pid>0){
close(out[0]);
scanf(" %s",file1);
scanf(" %s",file2);
write(out[1],file1,strlen(file1));
//write(out[1],"\0",1);
write(out[1],file2,strlen(file2));
}
if(pid==0){
int cnt=0;
close(out[1]);
cnt=read(out[0],file1,1024);
file1[cnt]=0;
cnt = read(out[0],file2,1024);
file2[cnt]=0;
fprintf(stderr, "about to execlp(sort|%s|%s|NULL)\n",file1,file2);
execlp("sort","sort",file1,NULL);
return 0;
}
return 0;
}
./a.out
input is: wtf omg
Output:
plasser#pisbak$ ./a.out
wtf omg
about to execlp(sort|wtfomg||NULL)
plasser#pisbak$ sort: cannot read: wtfomg: No such file or director
So, the message is not about the binary not being found, but sort is unable to find the file wtfomg which was passed as an argument.
Locate the sort command in your setup. Normally sort exists in /usr/bin/ path. Use this
execlp("/usr/bin/sort","sort",file1,NULL);
instead of
execlp("sort","sort",file1,NULL);

Thread not hitting thread function every time

I am trying to make a program that takes several files, appends them all into one big file. Each append has to be done by a separate thread.
/*
This program creates appends several files together
*/
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
pthread_t *tids;
void *threadout(void *num);
int main(int argc, char *argv[])
{
int numOfFiles = atoi(argv[2]);
int error;
int index;
sem_t sem;
//Used for output file
int outFile;
//Checking to make sure there is the correct number of arguments
if (argc != 4)
{
printf("%s \n", "Wrong number of arguments, exiting program.");
return 1;
}
//checking to make sure there are at least two files to append
if (numOfFiles < 2)
{
printf("%s \n", "Cannot append 1 file or less.");
return 1;
}
//opening/creating file
outFile = open(argv[3], O_WRONLY | O_CREAT, S_IRUSR);
///****************** Allocate space for thread ids ******************/
tids = (pthread_t *)calloc(numOfFiles, sizeof(pthread_t));
if (tids == NULL)
{
perror("Failed to allocate memory for thread IDs");
return 1;
}
if (sem_init(&sem, 0, 1) == -1)
{
perror("Failed to initialize semaphore");
return 1;
}
/****************** Create threads *********************************/
for (index = 0; index < numOfFiles; index++)
{
if (error = pthread_create(tids + index, NULL, threadout, &index))
{
fprintf(stderr, "Failed to create thread:%s\n", strerror(error));
return 1;
}
}
return 0;
}
void * threadout(void *num)
{
printf("Hello");
return NULL;
}
Near the bottom of the program I do the actual creating of the threads. The first thing the thread should do is hit the "threadout" function. However the only way I can get anything to print is if I say to create a large number of threads. So if I tell my program to create 5000 threads, "Hello" will be printed. Not 5000 times though. If I asked it to create 10 threads nothing is printed. Am I doing something wrong when I invoke "threadout"? Thanks
Returning from main causes your entire program to exit, even if other threads are running.
Your main function exits when all threads are started. If you're starting lots of threads, this leaves enough time for the first ones to print. If you're starting few threads, it returns before the first ones get to print anything.
You might want to use pthread_join (called once per thread) to wait for all threads to terminate.

How can I check for the existence of a directory in a c program and create it if it isn't there

similar question here.
But it only covers for a single directory level. For example if you gave /home/mypc/directory and if only directory doesn’t exist, it creates one. but when it comes to /home/mypc/directory/directory2 where both directory and directory2 doesn't exist, It gives a segmentation fault error. Could anyone suggest a suitable method for this.
thanks in advance.
If you don't want to depend on external processes, you could just write a recursive function to create a directory hierarchy:
int mkdirhier(char const* target) {
int r = 0;
struct stat st = {0};
if (-1 != stat(target, &st))
return 0; // already exists
char* parent = strdup(target);
if (strcmp(dirname(parent), target))
r = mkdirhier(parent); // recurse
if (parent)
free(parent);
if (!r && (r = mkdir(target, 0700)))
perror(target);
return r;
}
Live On Coliru
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <errno.h>
#include <time.h>
int mkdirhier(char const* target);
int main() {
char buf[1024];
srand(time(NULL));
snprintf(buf, sizeof(buf), "./tree/some%d/dir%d/sub", rand(), rand());
mkdirhier(buf);
snprintf(buf, sizeof(buf), "/nopermissions/tree/some%d/dir%d/sub", rand(), rand());
return mkdirhier(buf);
}
Prints
gcc main.c; ./a.out; find .
/nopermissions: Permission denied
.
./tree
./tree/some1804649601
./tree/some1804649601/dir1553142090
./tree/some1804649601/dir1553142090/sub
./main.cpp
./a.out
./main.c
Split the path into its components, and check each and every component of the path. So for the path /home/mypc/directory/directory2 you check and possibly create, in order
/home
/home/mypc
/home/mypc/directory
/home/mypc/directory/directory2
If you are going to use mkdir to create the directory just add -p.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
struct stat st = {0};
if (stat("/home/mypc/directory/directory2", &st) == -1) {
system("mkdir --mode=744 -p /home/mypc/directory/directory2");
}

Resources