A shell pipeline is implemented using fork() to create child processes, pipe() for inter-process communication via kernel buffers, and dup2() to redirect file descriptors, enabling data to flow from one command's output to another command's input in a UNIX environment.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Building a Shell Pipeline in C from Scratch (fork, pipe, dup2)Added:
All right. So what's up my friend and welcome to this new video. So in this video I'm going to explain what is a process in C like how we can create a new process. What is a process? How a process work in C. And in order to understand this deeply we're going to implement a shell pipeline a simple one using C language. So I didn't just want to explain what is a process and give some examples and in the video. No I want to explain it in a practical way.
So the thing that we are going to implement this shell pipeline. So as you can see this shell pipeline contain a lot of things. So let's start from the beginning first. As you can see we have in file. So this file it's going to have a text. So this text is going to be like an input for this command that come that come after the file. So here we have the file and we here we have the first command. All right. So this first command is going to read from this file and uh to be executed. All right. Then here's the thing like here's the the funny the funny thing that this command like the output of this command is going to be passed to the other side which is this part. So think of it like this is a two parts like or two sides. This is the first side and this is the second side and the bridge the bridge between these two sides are these pipes. So later in this video I'm going to explain to you guys what is pipe, how you can create them, how they work, right? But now uh think of it like this is a pipe and it's a bridge between two sides. All right.
Then when we pass the output of this command to the second side, it's going to be taken by this command as the input and it's going to be executed and the output of this command is going to be redirected into this file. All right.
All right. So that's it and let's give it an example to see. So for example, I need to create a file. So env for example in file uh for example let's say hello abc uh I don't know s fault sik fault and here uh I don't know hello for example all right so here we have two lines let's contains the character a all right so if we did this command as you can see we should have two in the out because first two are gripping in the a so we will get two lines then we have w-l so we will count how many line how many line we get from the first comment so if we did count cut out file sorry we will get two all right so this is like uh the concept of this video and let's get started first what is a process a process is just a running program what just a running program like this in this simple [ __ ] Okay, a process is just a running program. So for example, we have this command which is mantop. So this montop it's a program that view like it preview the running systems like the threads the process that are actually live running and managed by Linux kernel right so if we did top we'll see all the running process in our system right now uh and here as you can see we have the audacity like the the program that I record on my phones OBS and a lot of things Chrome okay so this All are running processes. All right. And as you can see here also top is a process, right? So first it was just a binary in the local bin. But when I call top it's go it's it's have been executed. So it's turn from just a file in in my file system to a process that have an process ID and a lot of other information. So uh I know we need a proof. So for example here here I'm going to create a file called main C. So nothing I'm just going to do in main. So int main like this for example let's say while and it's going to be an infinite while loop. All right everything is good. Let's continue. GCC main C. We have a problem.
I didn't add the semicolon.
All right. Good. Now just to know this is uh this is just a useless file in my computer. This is just a binary file.
It's not a process. But but the time but when time I run this file is going to be a process. So just to improve to prove to you this guys I'm going to run top right here as you can see or let's run top right here. Okay. And here I'm going to run my pro my uh my binary. So as you can see here we have everything is clean. So when I run out as you can see we will have the ed out added to my list of running process in my Linux. All right. So as you can see it became a process with it use with with it process ID uh CPU usage and a lot of things. All right. So like this is the thing process is just a learning program. I know I know it's not that simple but this is the idea that you can get in your mind like this right so that's it and let's close all of this and let's see how we can create a process and here it come the part the final part which is this fork sys all right so as you can see here in the description a fork creates a new process by duplicating the calling process good so the new process is referred as the child process and the calling process is referred as the parent process. So let's try to draw this in order to understand it deeply.
All right. So here for example as we can see we have our running process. So here for example we have our main C and it's been uh run. So here main C for example.
Good. Let's make it a little bit bigger.
All right.
like this.
All right. Good. So here you have our main C. So by the time you are going like this then you are you are executing some instructions but the time you are like doing like this like calling the fork function. This is what what is going to be to happen. So first it's going to split your program into two phases or in a more technical way it's going to create a new process. So it will remain the the first process. So here as you can see we will have our normal main C which called referred as the parent. So this is will be called uh so this is will be called the parent process and here we will have something called the child because logic logically uh the parent should have a child and the child should have a parent logically. So here uh we call it the uh the child process.
All right. So like this how we could create a process. So here we have the parent and here we have the child. So for example here just understand we have a normal process when we when we like like uh we arrive to the part when we call the fork function it's going to split this process by two processes and it's going to create a new process called the child. Good. Good. So let's see the child process the child process and the parent process run in separate memory spaces. Good. So each one of these processes have its own memory space. So basically this will have its own memory space like this. And the parent also it will have its own memory space like its own stack, its own heap, its own data segment, its own text code and everything. All right. So just keep this in mind. Let's continue. Then at the time of fork both memory spaces have the same content. Good. So they have a separate memories but they have the same content. Oh. So for example here let's say we have a variable like in our code like in our main. C uh before calling the fork function we have for example a variable called int x and its value is 1,000 for example 100 sorry. All right.
And we have we had also a string that contain for example hello world for example like this. All right. So just to know guys that this is going to be copied. So really let's do like this. So this is going to be copied right here also and this is going to be copied also in the child process and in the parent process. So each one it's like it's going to like change its own version.
All right. So this have its own version and this have its own version but it was the same version at the first before calling the fork function. Good. Good.
So let's continue. Uhhuh. And say file mappings. All right. So the child process is an extract duplicate of the parent process except for the following points. So here we have some good points. The child has its own unique process ID. All right. So what is a process ID? just an ID that it's got it's given to any process and it's unique to each process right so like uh it's a way to dec identify this is this is the process and this is its number like so and this ID does not match uh the ID of any existing process group so there is no like two same IDs right so just keep this in mind so let's continue uh It's uh enough theory and let's go into process and let's make a process. Let's create a new process for example. So let's uh remove this. All right. So first I'm going to zoom in like this. And I need to include uh for example uh the stdu stdu like this.h and also I need to include the unitd. So here we have unic H good good and here I'm going to create also so I need to include the what the uh the C type for the P ID-G type so here we have types dot H good so here we have P ID- G P ID equal fork right so it takes nothing so here uh just to know guys it's going to reach return two returns value depend on each process. So in the child process it's going to return zero but in the parent process is going to return the process ID of the children and you are going to see this.
So here I are going to the if condition.
So if the p ID is equal equal to zero means that we are running in the child process. So simply just I'm going to do a parf example here hello from from child process like this. All right. And percentage and new line and uh p ID like this. All right. And here I'm going to do brand like this. And hello from Mhm. parent ID like this. All right. So percentage ID and new line. And here I need to pass the P ID. So logically here it should print zero. And here it should print the child P ID process ID. All right. So if we did GCC main CO out as you can see hello from the parent uh from the parent ID is this number and hello from the child the ID is zero. So I hope you understand this. So this is like the basic structure. So this is like the basic structure of creating a new process using the fork Cisco. Good.
Good. Now let's continue. So as we said uh we have something else called the pipes. So I guess now the image uh like it's going to be clear right now. So imagine this is the parent process like are going to execute the first command in this phase in the parent process and this part is going to be for example executed. So uh so imagine this part is going to be executed in the in the child process. So like in the scope or in the side of the child process we're going to execute this part like reading this file and executing this command and we are going to pass the output of this command using the pipes to the other side which is the p uh like the side of the process of the parent where it's going to exe execute this command and open this file and read to it. So I guess you started getting the image how it's going to be.
Now what is pipes right? So pipes imagine like uh as we know the real pipes in the real world are something like this like we have here for example a line like this and also let's make it let's go duplicate it and also another line like this. All right. And we have something like this. Sorry, bro.
Bro, I am suck at this.
And bro, bruh.
And we have something like this.
And also something like this. All right.
So this is the pipes in the real world.
All right. So logically everything goes from here can go out from here. So it's going it can be like this and also everything goes from here is can get out from here. All right. This is the pipes and we bring this to the programming and like we created something called the pipes. So the pipes in C is just a buffer in the kernel memory space. So it's like it's just a kernel buffer like this, right? So it's like this. It's just a kernel buffer with two file descriptors. All right. So like how we can create like the entry and and exit. We need to create something called an an FD of two elements. All right. Uh just a name FD not uh something special.
So when we create this FD of two elements, the FD of index zero will be like the entry. So the FD of index zero will be like the read because we know the std in is like when where we read from and also here we will have the sorry the FD of index one in the output like the std out. All right. So when we pass this array to the pipe function and we are going to see the description of the pipe uh sys call. So it's make like a connection between the fd of index zero and the fd of index one. So if anything you write from fd of index zero you can read it from the fd of index one and also the same thing everything you pass it to the fd of index one you can read it from the fd of index zero. So it's like this. So like it's from this way and also it can be from this way. I know the CUV is like this. It's boring but let's read just the manual and we're going to do some examples. So man pipe.
All right. So here we have pipe and description is like this. Pipe creates a pipe and a undirectional and unidirectional data channel data channel that can be used for interprocess communication like it can be used between two processes to make a connection between them or sorry a communication between them right so the array pipe fd is used to return two file descriptors referring to the ends of the pipe so pipe fd of index zero refers to the read end of the pipe and pipe FD of index one refers to the right end of the pipe. So the data return to the right end of the pipe is buffered by the kernel until it's read from the read end of the pipe. So everything I said in the explanation is was just reset or uh they said right here. All right. So you can pass data from FD of index zero and you can get it like get it from the FD of index one and the same thing the same thing like in the opposite way. All right. All right. So I'm going to stop blah blah. So I know guys I'm going to stop and let's give it an example. So let's say clear like this. NVM main C.
Let's make it like this. All right let's remove all this. So let's do int pipe fd of uh two elements. Then we are going to do pipe our function and we're going to pass with the pipe fd like this uh our array. All right. Then we are going to write one uh sorry to the fd of index one. So are going to write to the out and and we need to read from the sd in right from the index zero. So here I going to make for example hello world like this and for example just 11 bytes like this. Now we need to read from fd of index zero and print. So size dash t and bytes equal read and fd of index zero and here our buffer. So I need to create a buffer. So buffer like this. Char pointer. No, no pointer. Just buffer of 20 elements like this. All right. All right. All right. Buffer. And I'm going to need 11 elements like this. And let's print if our buffer. So print percentage s new line. And the buffer like this.
All right. Let's see. So GCC out. Yep. Yep.
uh pipe FD sorry not just FD pipe FD and here also pipe FD good as you can see here we have hello world so the thing here that we passed like this uh string to the pipe of index one so it's stored in the kernel buffer then we read from the kernel buffer using the pipe fd of index zero it's like the real world when we read from the index from the st in and we write the CD out like this. Right. So this is how the pipes work. So now we are done with the pipes. All right. So now I guess we are ready to go to implement the shell pipelines. All right. So let's remove this and let's remove all the files. Yes. And let's get started. All right. So just to know guys that I'm not going to my like my program would not expect the input to be like this but it's going to be like this. For example, dot slash for example let's say uh sh uh pipe for example uh will be like this like the sh pipe like pipe the name of my binary output file and the file for example the in file the first command for example the cmd1 then I I need I not need I don't need here the user to enter this pipe I just going to expect that here there's a pipe right then here we will have the same D2 right then here we will have the out five so uh the argu of index one will contain the the infile of index two will contain the cmd1 the index of index three will contain the cmd2 and of index file context will contain the out file so that's it and let's get started So here I'm going to create my main C and I need to include uh let's say let's say SH pipe dot h like this right and here we will have our in main like this we take inc char and we need also the environment var uh variables all right env PP.
All right. Like this. And I simply print hello world.
Like this. Sorry.
Like this. Hello world bra.
Good. Good. And let's create our sh pipe dot h like this. And let's include our important header file. So here we have the std.h the uniscd.h include also let's say sis slash types.h page include all sources slash state.h H include CD lib and also we're going to include string.h I guess I need this also. And what else?
What else? What else? Yep. Include sc.
What else? I can remember. I guess that's it. I guess I don't know but let's say right then here I need uh but before that I need if in forot and if pipe uh sh pipe define sh pipe and here we have our and def and if right all right so I need also a make file so here we have make file like this so I need my sources C files so here sources uh first we have main dot C and here the output file it's going to be in this case pipe let's say SSH just say output for example all right then output uh then here my first target all GC dash G and the sources dash O output like this output And here clean n dash force the output.
All right. Good. Now let's do make.
Good. Uh no good. Let's continue. So first I need an if condition. So as we said I I expect like the input to be something like this. Like let's say the usage should be like this like slash output uh in file send one same one sorry and here we have send the two and here the out file. All right. All right. So first I'm going to do an if condition. So if so if AC equal equal 5 which is good else if it's not equal five arguments then I'm going to print the usage. So here print and here I'm going to print this usage right here. So here here is the usage that you can use and here I need just switch this like this like this like this. All right.
That's it. And return one.
So to make Yep. I need here semicolon make great. Let's continue. So first we need to create uh some variables. So for example, we need uh our int pipe fd pipe fd two elements p id-g p id we need also I guess we need the size g byes for the buffer and let's start first I need to create the pipes so first we need to create the pipe. All right. So, it's going to be something like this like pipe as we said and the pipe fd. All right. And here we need if condition. So, we need to check if it's if it fails or not. All right. So if uh return let's say if less than zero then we are going to use the P error all right sorry like this for example let's say pipe all right like this and return one then uh what we need to create we need to create a new process as we said so it's going to be two processes. The first process is going to execute this part and the second process is going to execute this part like this. So if uh P id equal fork no no it's not like this it will be like this. So P ID will equal this fork and if P ID is is P ID less than zero then P error also uh fork and return zero. All right. And here let's say comment uh creating a new process. Then here we are going to do an uh check. So here we have if p equals zero. Let's say command check if the if the if uh like check if the process is the child process like this. All right. And here we have the else where we will have our parent process.
All right. Good. So uh I guess now I'm going to start the parsing. So first we need to open this file. So I guess it's need to be done before uh the fork. So I guess here I should open the file when I will check that the argument counter is equal to five. All right. So here I'm going to make a file descriptor. So int equal to zero for example. And here I need also the size. No. So FD equal uh the open and here the AV of index one the name of the file. Then all uh read only and all right only. Okay. And here is zero.
Then we are going to check if FD is less than zero. Okay. So if FD is less than zero then P error like this. So P error open open like this. All right. And return minus one for example.
Good. Good. Then uh what we can do then we need also check if this file a directory or not. All right. So I need uh to call state function. So I need here a strct. So strct state str and here I'm going to call let's say comment here and the comment here uh opening the file uh the infile file. All right then here checking if uh the in file is a directory.
Right. So here I going to do stage and this function it takes like uh first the the part so a view index one then it takes the address or the pointer to this structure. So others of st then we are going to check uh uh check using check using the s is directory marker right so are going to do if if is uh s is there directory like uh the stelement st mode. All right. Then if yes print percentages this is a director like this.
All right. Then return minus one. All right. Good. Now we have uh we passed our uh file. So let's do make sorry.
So let's do make and let's run first.
Everything's good. For example, the the name of the file is wrong. Then it will show us an error. For example, let's create directory. For example, like this. Then I'm going to insert my directory right here. So there. And there. This is a directory. All right.
So first of all everything is good. Then now what we need to do now we are we are going to use a function called top two.
So this cmd1 need an input to read from.
All right. So I need to make this open file descriptor that point to this file object. And I'm going to make it like make the cmd1 read from this file using the d2. So let's read the manual of this uh function. So here we have man two as we can see here we have two uh three versions I just going to use the top and the top two. So the top is a system called allocates a new file descriptor that refer to the same open file descriptor as the descriptor old fd. So in simple term the dub it takes the old fd and it creates a new one that points where this uh old fd points to. Right?
So this is just the top and the dub two do the same thing but in other specific way. So we give it a two file descriptor the new FD and the old FD and what it do the D system call performs the same task as Dub but instead of using the lowest numbered unused file scripture it uses the file scripture number specified in new in new FD. In other words, the file descriptor new FD adjust so that it now refers to the same open file descriptor as old FD. So in simple term like this two, it makes the new FD point whatever or wherever the old FD point to. So for example, if the old FD uh point uh it's a file script of a file, then this new FD will point to the same file. All right. So this is what what we are going to do. are going to make the stdn point to this uh sorry to this file. So what I going to do? We need to do like this. So uh here I need to make let's make a comment making the sdn uh like refers to the open uh the fd of the infile like this. So fd of the infile. So just I going to do d sorry uh d two and here we have the fd and here we have the the std in the file script zero file descript like the read file descript.
All right. Then what we need to do then we need to make uh the std out also point to the fd of index one like the output like uh the the the right end in this pipe because we need to pass because we don't need to show the output uh in the terminal. No, we need to pass the output to the other side using pipes. So we we're going to use the same function like here. So here we have making the the STD sorry SD in making the SD out refers to the FDF index on uh pipe.
All right. Uh let's say here right pipe right end.
Right end. All right. So d two here we have fd of index one and here the file script channel one which is sd out. All right and here I just going to close and here I'm just going to close the fd of index uh pipe fd sorry not fd pipe fd of index zero and here sorry pipe fd pipe fd. All right good. So now uh we everything is good. Now we just need to execute this command using exec. So let's see this exec exec like this. So exec execute the program referred to by the part. So exec takes three parameter the so exec takes three parameters the p the arguv and the nvp the environment variables. So this calls the program that is currently being run by the calling process to be replaced with a new program with newly initialized stack and heap and uh blah blah blah. All right. So the thing is that the part here is going to be executed by exec and it's going to replace the whole program or the whole process by this process that that exec executed. All right. So it's like this. So we're going just to pass to it the the command and it's going to execute it. So but before uh as we can see sorry let's return to the manual.
It takes the part. So first we need to find the path of this executable. So we need to create the path like / user/bin slash the command for example ls like this. Then we need the argu uh like uh the argument of this command like ls-l.
So it's going to be like this. So for example here we have uh for example the command ls-l for example. Yep like this.
So what we need to do we need to do a tonization. So we need to parse this uh command and take this argument by itself and take this argument by itself and we need to return this in a 2D array. So we need to return in this an array like this of pointers where here we have ls and here l and hidden null pointers.
So it's going to be like this like ls and here it will we will have uh sorry bro come on dash bro dashl and here we will have the so this is the form that uh this uh executive wants. All right. So for example, this is the command that we are going to get from the terminal. We need to parse it and make make it as a tokens. All right. So we need to create a function called fd split for example.
All right. So mg main c and here I need to make like this. So I need to create a variable.
So here I'm going to create char cmd. All right. And here shop counter but like this. Let's get out from here.
All right. And that's it. And here let's say uh let's say I don't know uh creation like let's say what I need first I need to make the command uh in a 2D list in a 2D array.
So let's say uh splitting cmd into a 2D array.
So cmd equal ft split and here I'm going to pass the the a of index 2 and here the separator right. So for example disability is going to be space because as you know uh for example the command is going to be like this for example uh let's say uh ls-l uh is going to be a space between them.
So we need a separator to know like how we can split these words. All right. So I need to create a file nvm utils. See, so engine utils do C like this. And I'm going to include my header file which is uh sh pipe.h.
All right. And I'm going to include my my function. So char is going to return a 2D point uh to uh like an array of pointers. So he let's say ft split. All right. Here he takes the arguments.
So here charr and here char uh separator. All right.
So first of all let's just print if the the string version is and yeah.
All right. All right. Then I need to add this uh I guess into my header file. So I'm going to do cuts. C and I need to add this in my header file.
Okay.
All right. Then we can go make All right. So we have a lot of problems in the main. C.
First of all, I need just to add some uh some uh to uh some uh ISO uh some semicolon right right here and also right here some make. Yep. Uh we need to add this file which is the utc into my make file. So here we have ut like this.
Good. Then here we need to do make and run.
Yep. Yep. Yep. Sorry.
In file and for example here we have ls dash for example dash.
All right.
So why it didn't print nothing? So let's go again to the main C.
All right. So first of all we we get inside the p ID of zero like uh the process id process id then we close the pipe fd of index zero then here the d to we make the std in point to where the f this file scripture point to and here's the problem here we make the std out point to pipe fd so like after colon is split the std out cannot print to the terminal So what you need to do, we need to make like a backup. All right. So I'm going to create a variable here. So int saved std out like this for example. All right. And I'm going to do uh saved out equal top and uh one the file descriptor one. And uh what I need to do what I need to do here so like for testing my uh functions I'm going just to do just the normal state of the out just for tests. So here I'm going to make top two make uh the file descriptor one point to the saved out. All right then we can see in the terminal like this.
All right. So uh now let's go ahead to our UTS. C. So uh let's start. First let's remove this all spaces. First I need a variable int i= z in g= z in len. All right like this equal to zero. And I'm going to start by first counting how many word I have. All right. So let's take an example right here. So for example this is our command cmd equal like this. For example get list. First I need to count how many word in order to allocate like to know how many uh pointers in this array I'm going to allocate. All right. So here I'm going to do int uh let's say let's say count word count like this equal to zero. it will equal what equal to function uh like this count word and it takes the str for example. All right. So I'm going to go up here and declare this function. So in count count like this.
So we take char counter str like this it have a variable so int count equal to zero then I guess also I need the int i is equal to zero and flag variable to indicate it's uh like we are inside the world or outside the world right so here while while index X I does not equal n terminator I ++. All right. All right.
So if uh the str of index I does not equal space.
All right. Then just we are going to do I ++ and that's it I guess. just I ++ and uh flag will equal to one. All right. Then here else if str of index I let's make a space is equal equal to the space and and the flag is equal to one. So here we need to do count ++ like we we need to count a word. So count++ and I ++ no that's it I guess.
And here continue I guess. Or you know what just here I ++ and this remove this I ++ and here else just I ++.
All right. All right. Then uh here we need to return the count like this. So here I'm going to print the count. So print percent new line and count make.
All right.
L value and and Yep. Yep.
I need two equals like this.
Good.
Why one?
Okay. So, here the flag.
All right. All right. So, so I just forget if there is no space here. So, if there's no space here, it will not count this word. So I just here need to do to add after the while loop end add an if condition if the flag is equal equal to one then just do please count plus again to count the last word that have no like space after it. All right so if we didn't make again and run everything's good. We count how many word we have in this command. So let's continue. Now we just need to uh count how many character in each word or get space for it. All right. So what we're going to do the same thing a while loop here while while what while str of index I does not equal the null terminator like this or it's going to be automatically and here I ++ okay good then here if the same thing if str of index i does not equal the separator Now we have the separator. Then I'm going to do I ++ l++ and uh flag will equal to one. So here I need the flag variable int flag equal to one like this. And then I'm going to do continue.
All right. Continue like this. Then else if it's equal to separator then else if str of index i is equal equal to separator and end flux equals one. All right. So before diving into this if condition I need to allocate or to create a pointer to pointer variable like this result and I need to allocate for it. So mar and I'm going to allocate like uh the count word. So count plus one for the null and I'm going to check if the result is null or not. All right. Then if yes is going to return null like this. All right. Then here I need uh to now I have like I am I am I am uh exactly on the separated. So I need to go back and calculate the length like uh of this word. It's it's actually calculated using L++ but I need the start index like to start copying from.
So here int start index equals zero. All right. So here start index= i minus l. All right. All right. Then what I need also not I need also then uh result of index g like index zero is going to equal to log uh len + one and here we're going to start so we're going to set another while loop. So while while what while what while what?
All right. Here we have the start index and here we have the length.
Okay.
So why the length is bigger than zero?
or equal to zero or let's just say bigger than zero then the result of index g of index k for example let's add another variable called k bra in k equal to zero right like this of k is equal to the to the str of uh start index then here we have k++ and start index ++ and then minus minus all right then uh I'm going to print then I need to add here result of index g of index k equal to null terminator then I could print this result print f percent new line and the result of index g like this and here in the else if condition there's nothing like this all right good then here I need to make the len equal to zero sorry the len should equal to zero again make all right there is a problem problem yeah we need equal here.
LS.
Yep. The same thing. We need to add another if condition here after the after the loop end. And if the flag equal to one again. If the flag equal to one, then we need to do the same thing right here. Then we need to All right.
I guess the same thing.
It's going to be copied right here.
Sorry.
All right.
Okay. else to make what?
Why?
So here we continue I ++ print and here start index.
You know what I'm going to call the GDB.
So GDB break a domain lay in run in file ls dashl same d2 out file.
Good. Let's make a little bit bigger like this. All right.
Here I need uh refresh.
Here I need set follow fork fork mode child like to follow the child process. All right. I need to do it in the beginning. I guess let's do it again.
Now what is exit?
GDP again break domain lay in air in uh run in file ls-l2 out file set follow fork mode side.
Good. Good. Good. Good. Now s let's go inside. Let's see how many word we have. We have two word. Good.
Now we are allocating for the first word.
Refresh.
Let's continue here like this.
Friend I friend L two good friend start index friends str of start index dash good everything is good Okay, branch.
Okay, here's the problem. The K is equal to zero, I guess.
Yep.
Yep. Yep. Yep. Yep.
All right.
All right. All right. All right. All right. So, let's make again this.
Let's add here after the while loop finish the K will equal to zero.
Good. But what is where is the full command?
What?
Oh, okay. Okay. Okay. like right here.
Okay, right here like this.
So make good everything is good. Now we have a 2D array that contain uh the command and its arguments. Then here in the end just we are going to do return like the final uh quantity equal to null. So result of index g equal to what equal to null and please return this result good good what right so let's remove this print right here and also this and let's add semicolon right here let's do make good let's go back to our main Okay.
So now uh we have the command. Now what we need? We need the path. All right. So here we say getting the path.
All right. All right. So here what we need to do we need to do uh the path equal to get path.
Okay. And this get this getb but need need the the first uh command like the name of the command and that's it. All right. So what you need to do uh we have a function called access.
So this function uh checks whether the calling process can access the file path. So we need to check is this file or this command is actually in the local user bin or not. All right. So what we need to do we need to pass also to the environment variable. So we're going to go inside this environment variables and look for and look for the path and run the command uh like on the access function on each part and check if this command is exist is exist or not. So we are need to pass also to pass so we need also to pass the environment variables.
So nvp and let's create this function.
So in the utils C let's go bottom here in the bottom we have what you need to do chart pointer just download pointer and get path like this text what text what text path check uh the command and text what takes the environment variables all right so Let's do a simple loop on this environment variables and print them to see. Okay.
So like this run MVP of index I and I ++ and PF percentages new line and NVP of index I.
All right. Now let's add this function to the header file like this.
So char pointer get path and it takes the char pointer cmd command and char pointer pointer envy y All right.
So, we have a problem, I guess. Get pass.
All right. So, let's see. MV main C chart path path equal get P. All right. Good. Let's see engine utter get path.
Let's copy it. Tesc.
And let's copy it like this. And let's go to our header file like this.
All right.
Yep. Char cmd here. Pinter see this is a pointer guys.
So here a pointer.
Now everything's good.
As you can see, we have So, as you can see here, we have my all uh environment variables. All right. So, it's like this. We are going to look for this parts and check each time we are going to uh split by this comm by this uh two points and each time like check uh like do this and add slash here and the command and stick and check using the access function if like this uh file exit in this path or not and also check for this and for this and all of this passes until we find a one path then can run this command. So this that's it.
Let's continue.
So, env.
And uh I guess Yep, that's it. I guess first what we need to do, what we need to do, what we need to do, what we need to do, I'm going to call chart paths.
I'm going to call FT split.
Split like this. Split.
Going to pass the NVP. And what? And I'm going No, no, bro.
So, what we need to do right here? Okay.
Yep. I need to do a while loop. All right. So, I'm going to create the char pointer pointer pass. And here I'm going to do a while loop. So while envp of index i does not equal the n the null the null i ++ and here what I need to do I need each time I'm going to make something like this like right so h no no no the wait what wait But all right, look guys, first I need to look for the path environment variable first. So I need to do an if condition. So if s n cmp okay is the nvp of index i is equals equal to what to the let's say pass equal. All right. If you if like just so I going to compare the five uh first characters, if you see S, please just print F for me. Print F percentage S like this. new line and NVP of index I and break good.
Now what we need to do now what we need to do we need to uh put All right.
So, what we need to do? What we need to do?
All right. All right. All right. All right.
All right.
So, I guess the thing is that I'm going to make a pointer called start will equal to null. And here we have int len variable called len. All right. Then here what I need to do here I'm going to do a while loop. So while while the nvp of index it is equal to len.
No, I don't need uh the i of index g. So I need another variable g++ in int g= to zero. All right. So if bro if NGP ngp of index I of index g is equal equal to equal.
Then please make the star point this place which is the NVP the NVP of index I of index G and let me print this start print as new line or you know what it's just to break right here and out of this all this [ __ ] I'm going to make a printf. So printf percentages new line and the start.
All right, you have some problems in the syntax just like this. All right. Make also here blah blah blah blah blah blah blah blah blah blah.
All right. Something like this.
Good. Now I just need to do plus one to like to escape or like to move forward after the the equals. So here just we need to do + one. So make like this. Now we need to pass all this start to FT split. So here the path will equal the start the FT split.
FD split of start and the separate is going to be what is going to be this.
Then I equ= to zero.
Then while while pass of index I does not equal the nimulator I ++ percentage S new line pass of no no no no I guess S and here also S and here also S of index I and make.
Yep. I need here also something. Where?
Where? Where? Yep. I need mountain.
All right.
Why parts pass of index I?
Where is the problem?
Yep. Doesn't equal null.
What?
Let's do GDB.
I'm going to do B main lay in file and uh lash. No, no, not here. Enter enter enter like this.
All right.
You know what? Let's do it again. Let's add here just a break.
Break a domain, bro.
in file set follow mode child side and let's continue. Yes.
All right. Let's see here. As you can see how start is good, good, good, good.
Break. Now let's go inside the split.
Here we have 2D. Okay. Everything's good.
All right. So, print strange separator.
Good.
All right, let's continue.
Print this the str of start index.
All right, good.
All right, let's print the result of uh index zero. You see? All right, everything is good. Why it doesn't get printed? Why?
Why the parts of index are does not equal to null print if what's going on?
Let's do again this while loop.
Well, you know what? I just print if percentages null.
Why?
Why it's null?
All right, let's do DB again.
I guess there is a problem. lay in run uh in file.
Six Bro, come on. Escape it. Run in files d send it to out file.
Yep. Again. All right, let's see.
All right, let's go to split.
All right, everything is good.
So print result of index g. Everything is good.
Okay.
Branch is start of index I.
Okay.
Let's continue.
opt.
So first we had this. So print result of i - one.
French result of G minus one.
Bro, what?
Bro, I don't have g++.
What a [ __ ] stupid problem.
Where is G++, bro? Where is G++?
Here also I need to do g++ make what?
G++ what it do a while loop.
Good. But what is this garbage?
CDB break in run in file in file.
SL 2 out five.
Yes.
Set follow fork mode child.
All right, let's see what's going on here.
All right. So, print G.
Good. Good.
All right. So print result of index g like this.
All right. Good.
G++ and K equal to zero.
Good. Good. Good.
Nice.
print result of index g. Good.
So what is the problem?
Where is the problem guys?
Do we have some print or something like this?
So here we have G.
All right. C++.
Why?
Where this data came from?
Uh so here guys I not I noticed that there's a problem here. The flag I never got it's never like got reset to zero.
So I just forget here to add the flag equal to zero when we allocate a new word. All right. So if we did make again and run.
Oh god, still the problem.
But why I allocate?
I guess here also I need to do it.
But where this come from?
I don't know.
All right. So here also I need to do it.
But here I guess no switch.
Flo.
I don't know where is the problem, guys.
But in this plate, we have the problem.
Uh guys, I like see the code multiple times, but I don't know where is the problem. So, I'm going to I don't know and file lit.
All right, let's See?
Everything is good.
Okay. So, print result of index is zero. Print result of index one. Good. Let's continue.
Print. result of index two.
Good print result of index tree. Everything's good.
print result of index 4.
Good.
print result of index five also is good print result of index six also I don't know what is the problem guys how many count I have What?
What?
All right. So, I guess this is the problem.
I guess this is the problem.
Yep.
So I didn't allocate enough enough memory for enough memory for this because the count will just uh count just one. But I need like the separator will not be always space. It can be like two points. So I need to modify the count word that it will take also a separator. So chart separator also and this is going to be replaced with separator and this with separator and here it's going to take the separator and I hope it's work but I don't understand really I don't understand what is the problem.
What 213.
Where this two came from?
What?
What the [ __ ] How is this 2:30?
Oh, yep. Yep. Two for the command and 13. All right. Good. Good. And now everything is good. All right. So, uh, sorry. Let's continue.
All right. Now we have the right amount of uh words.
I really don't know where is the problem, guys.
So uh guys I don't really know what's the problem.
Uh here uh we solve the problem of the count. So it is in the actual count depend on the separator. Here we are having pointer to a pointer result.
Here are our casing count.
Yeah, in case I need to to do the size like the size of the pointer. So the size for sorry. So yeah, in case I do size of ch I'm experimenting if it's going to work or not. Let's do make.
All right, finally. That was the [ __ ] problem, guys. All right, so let's continue. Let's continue, guys. Let's continue. Now what we need to do, we need to loop on all this uh parts. All right.
And uh what we are going to do, we're going to call the access function. So if we did man access as we can see here, it takes the path and the int mode. So the mode are just uh some macros as you can see. Where is the modes? Where is the modes?
All right. We have here if for the existing of the file. So this is the mode test we want. So we are going to do something like this. Clear and vut C.
And let's go down here.
All right. And let's do access. All right.
Let's add the variable here. int return access equals zero. Good. Then here I'm going to do return access equal access of the pass of index I. All right, good. Or wait, wait, wait, wait, wait, wait. First, I need to create the part. All right, so here I need to do something like this. Like I will have here char then this part will equal a function for example equal called F to join join like this. It will takes the parts of index I and the cmd.
So let's create this function up here.
Char bound FJ join text char and here char cmd. All right. First I need the len. So uh all right. So int equal s len of path and in cmd len equal str len of cmd good and here uh all right then here I need int i equal to zero in g =0 then here Sh result equal to not just like this then this result will equal mo of path len cmd len and plus it's all right Uh as we said we have here pland plus cmd len plus true non terminator and the slash. All right. So let's do while path does not equal the the null terminator. And here we have i ++ good good. Then here the result of index g will equal the path of index i.
i ++ and g++. Then when we then when then this while loop will end. Then we need to copy also the command while cmd of index i does not equal the null terminator i ++. All right. All right. So write I should equal to zero again like this. Then result of index g will equal cmd of index i and g++. All right. Then here uh in the end we'll do result of g equal this slash. No. No. I guess slush or I guess No.
I don't know. I guess I guess I should do it right here.
Or you know what?
I guess there's no need.
Because for example we have uh let's say eo.
So for example here it will check.
Yep. We need to add the slash.
So I'm going to add it right here.
The result of G++ equal slash then result of G equal to n terminator then return result and Mhm. And we are going to print the pass percent is the pass.
All right. We have some problems.
Let's see. Let's see. All right. Like this here.
Let's see what else. Yep. in the line 193.
Okay. Yep.
Uh just let's do a new line.
Let's add a new line right here.
Good. Now we have the part / ls. So we need to pass this whole part to check is this binary exist or not. So let's let's call the access function. So let's uh call the access function uh like this. So here uh access of pass and uh what also they said yep if okay I don't remember access.
So here is the vlog I guess.
All right. So let's see.
All right.
Good. Now it return either zero or minus one. So I'm going to make uh the return access uh take the value of the return value. return access bra like this.
And uh let's say let's say let's say if the return if the return access equal to zero then just return the pass and break this. And here in the else we will just return none which mean that we didn't find the path that have this executable in it. So let's go back in the main C and let's print the path printf percentage s new line and the path make good for example the pot doesn't doesn't exist nothing all right so for example grab d- a grip not doesn't exist but the the real grip exist. Uh for example, let's say cut exist. So everything is good. Let's continue. Now we need to call the exit v. All right. So now we will run the exact v to execute the command exec v. It takes first the path then the cmd then uh the environment variables. So here P error if there's something if there's a problem because if there's if the exec work well it will not even reach this pair because as we said exec replace the whole process with with a new process. So this pair is is not going to be also even printed. All right so let's say exec here. All right.
And uh let's say what what what what what it need to close I guess the FD index one like this. So make for example let's say nice. It's working very well as we can see it's working. It's working guys. As we can see for example let's say in vim for example in vim in file for example we have uh hello for example and here sec fold and we run the command and we did grip a to give us the output. All right so now everything is good. Now what we have left is to go to the parent process and continue and like get the output or read the output from the fd of index zero then execute it. So first I'm just going to try to like uh all right so now we are in the second phase as I said now we execute the first command and we have the output uh we pass the output uh to the pipe fd of index one then we need to execute the second command and uh like uh pass the output to the out file. All right. So, first I need to close what? I need to close uh the pipe fd of index I guess index one because I don't read it.
Then here what I need to do I need to make a do. So do two. I need to make uh the pipe fd of index zil. I need to make the end point to this pipe fd of index zil. Then uh that's it. I'm going to execute this command. So I'm going just to do the same thing right here. All right. And paste it right here. All right. Good.
Then then then what I need to do? I need to open. So I need to open. All right.
So here after this XV everything is going to be stopped in this process. So I need to open this this file before uh this exec uh be executed. All right. So so so what I need to do what I need to do I need here before this I need to do fd equal open uh a index 4. All right then here uh we need read only.
No, write only. Just write only. And here o create uh like this. And here the permission 06 6 0 6 6 44. All right. Like this. Then just a simple if condition if FD is less than zero then just I'm going to do P error and open like this. All right. All right. That's it. Then I'm going to do another pipe. So here uh making the std out point where the fd of the out file points to all right this simple way. Then we're going to do d two and fd and the one is going to point to fd. All right. So this way and I guess that's it. Here I need to close uh the pipe fd of index zero and that's it. And here I need to wait. So this process need to wait. So let's read for the weight function. Here we have the weight function. So all the system calls used to wait for the state changes in the colum process and obtain information about it. So simply this wait function it will make the process the the the parent process wait until this uh child process either exit or like just when the child process finish then we will uh go to this parent process and it is going to be executed. All right so here I'm going to do wait and just here now.
All right and I guess that's it. Let's run make.
All right, let's copy this and end them.
All right.
All right. Right. Read only.
And uh where I have two pipes. Pipe.
Pipe. Pipe. Pipe. Yep. Here.
And that's it, I guess.
Wait. Yep. I need to include sis uh dashwage in the header file.
So here I'm going to do include sis /w weight.h.
So make no such file or directory.
What?
And here for example w-l right all right all right all right all right all right all right all right all right all right all right right all right right all right right all right right all right right all right right all right right all right all right all right all right all right all right all right all right all right all right all right right let's see it's inv main C uh so here first of all we make the top D one point all right so here top one remove this part all right all right Second open.
So we have the problem in the second option. 0 1 2 3 4 sorry.
So the out file should be created. So if we don't have this out file, it should be created guys. All right. So I guess the problem in here in Yep. Yep.
I need to do like this or make cut out file.
Good. But the second command didn't didn't get executed, bro.
Yep. Yep. Because here uh I need to pass the AV of index three guys. Sorry. Here the AV of index three. So make and cut out file.
Good. So if we remove this out file like this like out file and we did again and cut out file will have the same thing and it it it will have like the same same thing like let's say out file two cut out file two so it's have the same behavior as you can see guys. So now guys the basic behavior of this shell pipe it's been done. Now just are going to do some test cases to test our uh program. All right. So for example as we can see uh if we did something like this like uh we passed for example here I don't know ls-l it's going to work. Yep.
Good. But if I pass like the path of this ll so / user slashbin / ls, it's not going to work. But in the real pipe, it should work. For example, here ls uh uh let's say slash user slash bin slash ls.
It should work normally. All right. So we need to uh like uh to do to to handle also this case. So to handle this case I just need to add an if condition. So before like uh going inside this case part we need to do an if condition here.
So if the cmd of index zero is equal equal to dot to slash for example uh or let's say let's say let's say let's say and and cmd of index i of index zero of index zero and here also yep here also of index zero is equal equal to dot uh and and cmd of index zero of index one equal equal to this slash which mean that we have already the path. So what we need to do just the path will equal path will equal cmd of index uh zero. All right. All right. But else then we need to call this function. All right. So we need to call this function right here as you can see.
Good. Good. Now let's do this if condition. And let's do it also right here.
Also right here. Good.
All right. Good. Let's do it again. Make all right it's not still not working but in the real command it's working.
All right. All right. All right. Exe bad address.
All right. All right. All right. So if cmd of index zero equal equal this or let's say here or now it's working.
Yep. Uh here I just mess up. Uh I missed.
So here I just messed up. Uh I need to do or uh instead of end. So here also I need to do or like this.
All right. And let's do clear make again run. For example here uh let's say uh we will have uh slash user slash bin slash this cut out file. It should work correctly. All right. So let's say for example we don't have the first command.
So what should happen? Let's see here.
Nothing. But in the real command if there's no command we should see a problem I guess right nothing but what is inside the couch out file too four why four okay so I'm going to raise a problem uh so we need to to have the command here so here in this case uh what I need to do what I need to If the cmd of index zero, right?
All right.
So here I'm going to go to the main to the uts.c and in the get part I'm going to do an if condition. So if If the pass or the cmd equal null, then I'm going to return null.
All right. So here uh this is the shields I guess. No. So I need the main C.
So here if the path is equal to null so if the path all right is equal to null. All right.
So let's see what happen for example if the path is wrong. So for example, let's say make slash user slash all right not satisfy order actually but I should handle this. All right. So here let's say uh if the path if the path equal equal to null then I'm going to do PF uh we cannot find this command and percentage is like Yes.
All right. So, uh that's it.
Okay. And here pressure minus one.
Okay, bro. For example, not this is not the case.
Bro, what?
What? What? What? Okay.
So now we are going to compile and we are going to run here. As you can see there is no problem and uh for example let's say we have a wrong command. For example let's say wrong here I need to like have a like specified specified message for this uh for this thing. So for example in here in the nvm main C uh if the command is wrong. So what I need to do? What I need to do? What I need to do?
Let's go inside the chase. C.
UTS. C.
So as we said here, if the get part if like uh Okay.
All right. So here if the flag is equal to one which means we we like we found no here we will check if the return axis is equal to zero or not else will return null okay so what we will do if let's say here uh in the nvm main C for example I'm going to add an if condition so if if the pass is equal equal to null then what I need to do I need to print if and let's say something like this and and and the cmd of index zero does not equal null. All right. Then we are going to do print percentage uh s uh cannot this command new line cmd index zero like this and return minus one.
else if he entered an empty command then we are going to do print if cannot find this comment surface like This? Nope. Said, "Bro, come on." Like this. And here also. Like this. Like this, bro.
All right. Good. Now, let's continue.
And I guess that's it. And here also I need to return minus one.
So make Yep. I have a problem here. See colon?
What? Why bro?
The std out. So here in order to print I need to make this dub to like call this function which is dub equal out and here also.
All right so make all right good let's remove this uh new line and make it right here and here also.
Good make.
All right. Wrong cannot find this command. For example, if we had a bad address. So for example, user cannot find this command. For example, uh let's say if we had slash exec not satisfy our directory. All right.
All right.
Good. Good. Good.
Now I'm going to copy this.
Let's remove this space. Copy this. And paste it also right here.
All right. Like this.
All right. Good.
Good.
For example, here we have a good command. For example, ls -l. And here I have a bad command. For example, sick fold command not found.
All right.
All right. Why?
Why it happened? If the path is equal to null why it should print here like the name of the command why it didn't like this. So here in this case if we had the wrong command here but in the second command it just print without the name of the command.
All right. Cannot find this command.
All right. So, let's see. CMD AV of index two, bro. Of index three.
Yep. Index three, guys. So, make good. No, this is good.
All right. Cannot find this command.
Let's add two points here and two points here. All right. Good. Let's do make All right. For example, we have a problem here also.
All right.
For example, here is good.
Wasn't a sick fault.
Why?
Why, bro?
You know what? You know what? I'm going to index first command and here also I'm going to index this to say first command and here I'm going to say second command.
All right let's do it.
So here it's from the first command.
All right. So I guess here it should be to have like two. For example, if you had E, why it like is it give me like this empty message. It should print the name of the wrong command.
So let's see why.
Here I said if the part null and the and cmd of index zero does not equal null like this.
What? Why bro? Come on. So here if the part of index zero equal this else but equal cmd and if part is equal equal to null and end.
All right let's see why. Let's do gdb output. Sorry.
Break a domain lay in set fork mode.
Set uh set uh uh follow fork mode child.
All right, let's continue.
Good.
Uh, Path.
Why?
Why? It's equal to WC.
Why?
Why WC guys?
What the [ __ ] So, let's run again in file.
Uh, for example, hello like this. Then again uh s fault here out file.
Yes. Set follow fork mode child print hello. All right. Good friends. cmd sick fold. Good print path. No.
Good. But what is the problem?
Yes. Like wait.
Here we are taking brah bro I switch them bro. Here the a of index two.
Come on guys like this. So here we have sik fault.
For example if this if the first command is empty.
All right.
All right.
Why it doesn't print nothing?
All right, let's do this case. Show GDB output break domain lay.
Okay, run in file uh cmd and out file. Right. All right. Let's follow fork fork mode child.
Print cmd of index zero equal to null.
Good.
What bro? again in file run in file cmd out okay okay let's continue let's print cmd of index zero equal to null why Why sick fold?
Why sick fold?
Why sick fault, guys?
All right. You know what? I'm going to add the if condition in the UTS C. So in the f split I'm going to add if if strr or separator equal equal to null then please just return none.
then n main c I'm going to remove this else if condition like this all right and I'm going to go up here if cmd equal equal to null then just I'm going to print if uh we cannot find this command which is empty one then set face then new line then return return minus one like this so make all right so let's go to the utils C env V uts C and here I had a problem. Yep, I need to compile it with just n terminate not not the null pointer. So make why GDB output.
Yep. I guess I guess I understand why because here I need to to make a redirection like I I need to make sorry in the NVM in the main C I need to make a redirection like I need to make this ACD out the one point to the AC out. So I need to make it also right here. All right.
And also let's do make All right. It's not working. So GDP break do the main lay in run in file empty command cmd out set follow fork mode child continue continue print cmd why it's not equal to one print a of index two.
All right, let's run again. In file and here empty command and here we have cmd and here we have out file and let's see what happened in the split.
All right, let's go go go. Okay, let's see split. We have here print str equal to null.
Bro, it's I am [ __ ] stupid.
engine here. I need to make the same thing like and here the same thing.
All right, let's read the EDB.
Break a do the main lay in file out file set photo site.
What?
All right.
So, you know what? I'm going to hardcore it. Hardcore it. So just if like this or you know what uh here I'm going just to make it equal to zero or not.
Okay.
Zero.
GDP break a domain lay in run in file like this.
Uh cmd2 out file.
Bro, I need to again set bro run in or you know what I guess I have the correction. I am going to do just if index zero equal equal to zero make finally guys.
All right. So let's do the same thing for this uh second command. Uh, sorry.
Inventor.
Do the same thing. So, so, so, so like this.
And I'm going to copy it right here.
And I'm going to to remove this part.
All right. So, like this, we have our command as you can see. Okay. If there's a problem also, the second one or the first one is good. For example, it is the second one should should raise an error. All right. Why?
All right.
For example, first is good. Second is wrong. All right. We have a problem. The second. So, let's debug this second thing.
Or here. I need just to add or like this.
No, no, no, no, no, no, no, no, no, no, no. Sorry, sorry. Not here.
>> Uh, what?
Uh, and here and here is the same command. So, if MDF index zero equal like this.
Okay. Okay.
If the path is equal to null.
Let's remove this.
All right. Let's see. We have a problem here. AV of index 3 is good.
All right, we can not found two saved out. Yep. Here I need to make again this top because because here when I assign this sale AC out to the top one, it is just local to this process. But I need to redo it right here. All right. If I want a copy of the SD out. So if I did make good as you can see everything is good for example let's say here is empty good for example let's say here uh it's it's uh good or it's empty and here and here for example let's say it's good all right now let's uh do some real command for example here we're going to do lsl WC-L out file the out file is good as you can see let's remove out file to remove it all content okay for example the in file it will have for example some big data like this and I will say for example grip L grip L for example and cut out file good now I'm going to make the real command so for example here grip L and let's see what is inside the cut out file two is the same output as you can see guys so this is our code uh as we can see uh I explained to you guys what is a process what is pipe what is the top and we try to implement this uh beautiful share pipeline. So I just hope you enjoyed this video and see you in the next video. Peace.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
Re: 🗣️📍theprophedu📍2026 GST 103 CLASS (E-EXAM REVISION)
theprophedu
636 views•2026-06-04
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Instagram accounts got PWNed
EricParker
13K views•2026-06-03











