implement multiple pipes in c - operating-system

I'm trying to implement multiple pipes in c++. Namely, I want to write a program to simulate the execution of, for example, ls -l | head -n 10 | wc -l.
The code works all fine. But after all the commands are executed correctly, I need to hit enter to return to the command line. I guess I have to "wait()" somewhere
Here is the code I'm having now.
using namespace std;
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(){
int pid;
int fd[4];
pipe(fd + 0); // pipe between the 1st and 2nd command
pipe(fd + 2); // pipe between the 2nd and 3rd command
for( int i = 0; i < 3; i++){ // 3 commands
pid = fork();
if(pid == 0){// child process
if( i == 0 ){// first command
char *arg[10];
arg[0] = "ls";
arg[1] = NULL;
close(fd[0]);
dup2(fd[1], 1);
execvp(arg[0], arg);
}
else if( i == 1){// second command
char *arg[10];
arg[0] = "head";
arg[1] = "-n1";
arg[2] = NULL;
dup2(fd[0], 0);
dup2(fd[3], 1);
execvp(arg[0], arg);
}
else if( i== 2){// third command
char *arg[10];
arg[0] = "wc";
arg[1] = "-l";
arg[2] = NULL;
close(fd[3]);
dup2(fd[2], 0);
execvp(arg[0], arg);
}
}
else{// parent
}
}
}
I think I have gone thru all posts similar to mine, but still can't figure this out.
Can anyone help?

Related

why setuid fails after capset is used?

trying to figure out the linux capabilities interface, i came across with an unexpected issue (for me at least). When seting the capabilities of a process with the capset syscall the kernel rejects a change of userid with the setuid syscall. Does anybody know why setuid fails?
This is code i wrote to test this behavior:
#undef _POSIX_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/capability.h>
#include <string.h>
int main(int argc, char** argv){
struct __user_cap_header_struct cap_header;
struct __user_cap_data_struct cap_data;
int cap_res;
FILE *file;
int sockfd;
cap_header.pid = getpid();
cap_header.version = _LINUX_CAPABILITY_VERSION_1;
__u32 cap_mask = 0;
cap_mask |= (1 << CAP_DAC_OVERRIDE);
cap_mask |= (1 << CAP_SETUID);
printf("You selected mask: %x\n", cap_mask);
cap_data.effective = cap_mask;
cap_data.permitted = cap_mask;
cap_data.inheritable = cap_mask;
cap_res = capset(&cap_header, &cap_data);
if(cap_res < 0){
printf("Trying to apply mask: FAIL\n", cap_mask);
} else {
printf("Capability set correctly\n");
}
int uid = atol(argv[1]);
int setuid_res = setuid(uid);
if (setuid_res == -1){
printf("7w7\n");
} else {
printf("UID set correctly\n");
}
}
compiled with:
$ gcc -g test1.c -o test1
Output is (for user id: 1000)
$ # ./test1 1000
You selected mask: 2
Capability set correctly
7w7
I think you might be missing a couple of steps in your question:
How do you give the binary some privilege?
It looks like you are trying to use cap_dac_override to achieve what cap_setuid is intended for.
Rewriting the program as follows:
#undef _POSIX_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/capability.h>
#include <string.h>
int main(int argc, char** argv){
struct __user_cap_header_struct cap_header;
struct __user_cap_data_struct cap_data;
int cap_res;
// need to start from known data. C does not guarantee these are
// zero filled by default. You could declare them static to get
// that.
memset(&cap_header, 0, sizeof(cap_header));
memset(&cap_data, 0, sizeof(cap_data));
cap_header.pid = getpid();
cap_header.version = _LINUX_CAPABILITY_VERSION_1;
__u32 cap_mask = 0;
cap_mask |= (1 << CAP_SETUID);
printf("You selected mask: %x\n", cap_mask);
cap_data.effective = cap_mask;
cap_data.permitted = cap_mask;
// not needed: cap_data.inheritable = cap_mask;
cap_res = capset(&cap_header, &cap_data);
if(cap_res < 0){
printf("Trying to apply mask: FAIL\n", cap_mask);
exit(1);
} else {
printf("Capability set correctly\n");
}
if (argc != 2) {
printf("usage: %s <uid>\n", argv[0]);
exit(1);
}
int uid = atol(argv[1]);
int setuid_res = setuid(uid);
if (setuid_res == -1){
printf("7w7\n");
} else {
printf("UID set correctly to %d\n", uid);
}
}
You can run the program like this:
$ sudo ./test1 1000
You selected mask: 80
Capability set correctly
UID set correctly to 1000
Or, using a file capability:
$ sudo setcap cap_setuid=p ./test1
$ ./test1 1000
You selected mask: 80
Capability set correctly
UID set correctly to 1000
This will work if you want to use the first 32 capabilities. However, there are ~40 of them under Linux at present, so I'd suggest you look into using the libcap API instead which figures out all of the kernel ABI details for you.

Socket programming, process blocked on select?

I want do a simple program, where a father process create some child processes; before child pause(), they notification father process.
Child processes run correctly, but father wait on select, otherwise child have written on socket; where is the mistake?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
typedef struct{
pid_t pid;
int sockfd;
}Child;
void err_exit(char* str)
{
perror(str);
exit(EXIT_FAILURE);
}
int convert_int(char* str)
{
int v;
char*p;
errno = 0;
v = strtol(str,&p,0);
if(errno != 0 || *p != '\0')
err_exit("errno");
return v;
}
void child_job(pid_t pid,int sockfd)
{
int v = write(sockfd,"1",1);
if(v == -1)
err_exit("write");
printf("process %d in pause()\n",pid);
pause();
}
int main(int argc, char* argv[])
{
int nsel;
fd_set masterset;
int n_child,i;
int sockfd[2];
pid_t pid;
Child* c = NULL;
if(argc != 2)
err_exit("usage: <awake2> #children\n");
FD_ZERO(&masterset);
n_child = convert_int(argv[1]);
c = malloc(n_child*sizeof(Child));
if(c == NULL)
err_exit("malloc");
for(i = 0; i <n_child; i++){
if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)) < 0) { //create socket between child and father
perror("errore in socketpair");
exit(1);
}
if ((pid = fork()) > 0) {
if (close(sockfd[1]) == -1) { //father process closes sockfd[1]
perror("errore in close");
exit(1);
}
c[i].pid = pid;
c[i].sockfd = sockfd[0];
FD_SET(c[i].sockfd, &masterset);
}
else if(!pid)
child_job(getpid(),c[i].sockfd);
}
for(;;){
if ((nsel = select(n_child+1, &masterset, NULL, NULL, NULL)) < 0) {
perror("errore in bind");
exit(1);
}
int i;
for(i = 0; i <n_child; i++){
if(FD_ISSET(c[i].sockfd, &masterset)) {
printf("changed fd\n");
}
}
}
}
One thing that's wrong is you're passing c[i].sockfd to child_job(). In the parent process, it was set to the first socket fd in the pair, but child_job() is called in the child process, where c never gets set to anything. You're passing the original contents of the malloc memory. Change that to child_job(getpid(), sockfd[1]); and you'll be getting closer.
Another thing is that the first argument to select is probably too low. n_child is the number of children, but you need to pass a number here that's greater than the highest file descriptor in your set. For example, run the program with the argument 1 so it creates 1 child. It is likely to start out with file descriptors 0, 1, and 2 open, so the socket pair will be file descriptors 3 and 4. The 3 goes into the fd_set, but the first argument to select is 1+1=2. select ignores your fd 3 because it's above the limit.
To fix that, create a new variable int maxfd; near your fd_set, initialize it to -1 when you FD_ZERO the set, and after every call to FD_SET, update it:
if( [whatever fd you just gave to FD_SET] > maxfd)
maxfd = [whatever fd you just gave to FD_SET];
and call select with maxfd+1 as the first argument.
(Or maybe switch to poll)
That should get you far enough that your first select call works. After that, you'll find more problems.
The fd_set you pass to select will be modified (that's why you can do FD_ISSET tests on it afterward). If you go back to the top of the loop and pass it again without reinitializing it, select will not be looking at all the file descriptors any more, just the ones that were ready in the first call. To fix this, make a second fd_set and copy the master into it just before the select call, and never pass the master to select. (Or you can rebuild the set from scratch each time by scanning the child table.)
If you get a readable fd from select, you should read it before calling select again, otherwise you're just in a "eat CPU calling select over and over" loop.

Behavior of select() on stdin when used on a pipe

I am trying to understand an observation on behavior of select() when used on stdin, when it is receiving data from a pipe.
Basically I had a simple C program using the following code:
hello.c:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <termios.h>
int main(int argc, char *argv[])
{
int flags, opt;
int nsecs, tfnd;
fd_set rfds;
struct timeval tv;
int retval;
int stdin_fileno_p1 = STDIN_FILENO+1;
char c;
int n;
/* Turn off canonical processing on stdin*/
static struct termios oldt, newt;
tcgetattr( STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
while (1)
{
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
retval = select(stdin_fileno_p1, &rfds, NULL, NULL, &tv);
if ( retval && (retval!=-1) )
{
n = read(STDIN_FILENO, &c, 1);
write(STDOUT_FILENO, &c, 1);
}
else printf("No Data\n");
usleep(100000);
}
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
}
If I ran the program as follows I could see characters echoing when I type keys on while the program is running. When keys are not pressed, it displays "No Data" as expected.
./hello
However, if use the program as follows, the program never gets to a state where is displays "No Data". Instead last character "c" is repeatedly displayed.
echo -n abc | ./hello
I'm a bit puzzled by this observation, and would be grateful if you could help me to understand the observed behavior.
The problem is that your program does not detect an end-of-file condition when it reads from the STDIN_FILENO descriptor. After echo has written the c character it will close its end of the pipe, which will cause the select in your program to return immediately and your read to return 0 as an indication that no more data will ever be available from that descriptor. Your program doesn't detect that condition. Instead it just calls write with whatever character was left in the buffer by the last successful read and then repeats the loop.
To fix, do if (n==0) break; after the read.

perl match function for C program

Trying to use perl API functions in C program. Couldn't find the function to do regular expression match. Wish there is a function like regexmatch in the following program.
#include <EXTERN.h> /* from the Perl distribution */
#include <perl.h> /* from the Perl distribution */
#include <sys/time.h>
typedef unsigned long ulong;
static PerlInterpreter *my_perl; /*** The Perl interpreter ***/
int main(int argc, char **argv, char **env) {
int numOfArgs = 0;
PERL_SYS_INIT3(&numOfArgs, NULL, NULL);
my_perl = perl_alloc();
perl_construct(my_perl);
SV* str = newSVpv(argv[1], strlen(argv[1]));
if (regexmatch(str, "/hi (\S+)/")) {
printf("found a match\n");
}
return 0;
}
I know it's possible to use pcre library, just wonder if it's possible to get it from perl library here (libperl.so.5.14.2 on ubuntu 12.04)
Thanks!
UPDATE 1:
Did some google search and got the following simple program compiling. But when I ran the program as ./a.out ping pin, it gave "Segmentation fault" in the "pregcomp" function. Not sure why.
#include <EXTERN.h> /* from the Perl distribution */
#include <perl.h> /* from the Perl distribution */
#include <sys/time.h>
#include <embed.h>
typedef unsigned long ulong;
static PerlInterpreter *my_perl; /*** The Perl interpreter ***/
struct REGEXP * const engine;
int main(int argc, char **argv, char **env) {
int numOfArgs = 0;
PERL_SYS_INIT3(&numOfArgs, NULL, NULL);
my_perl = perl_alloc();
perl_construct(my_perl);
SV* reStr = newSVpv(argv[2], strlen(argv[2]));
printf("compiling regexp\n");
REGEXP * const compiled_regex = pregcomp(reStr, 0);
printf("execing regexp\n");
int len = strlen(argv[1]);
pregexec(compiled_regex, argv[1], argv[1] + len, argv[1], 5, NULL, 0);
return 0;
}
Don't mess with Perl's private internals. Call a Perl sub that uses the match operator.
Say you previously compiled the following in your interpreter (using eval_pv),
sub regex_match { $_[0] =~ $_[1] }
Then you can call
static bool regex_match_sv(SV* str, SV* re) {
dSP;
bool matched;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(str);
XPUSHs(re);
PUTBACK;
call_pv("regex_match", G_SCALAR);
SPAGAIN;
matched = SvTRUE(POPs);
PUTBACK;
FREETMPS;
LEAVE;
return matched;
}

socket is not blocking on write operation: OpenSolaris

I have a unit test that checks behavior on blocking and non-blocking sockets - the server writes a long response and at some point it should not be able to write any more and it
blocks on write.
Basically one side writes and other side does not reads.
Under Solaris at some point I get a error "Not enough space" (after writing 75MB) instead of blocking on write:
Program that reproduces the problem:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
char const *address = "127.0.0.1";
#define check(x) do { if( (x) < 0) { perror(#x) ; exit(1); } } while(0)
int main()
{
signal(SIGPIPE,SIG_IGN);
struct sockaddr_in inaddr = {};
inaddr.sin_family = AF_INET;
inaddr.sin_addr.s_addr = inet_addr(address);
inaddr.sin_port = htons(8080);
int res = fork();
if(res < 0) {
perror("fork");
exit(1);
}
if(res > 0) {
int fd = -1;
int status;
sleep(1);
check(fd = socket(AF_INET,SOCK_STREAM,0));
check(connect(fd,(sockaddr*)&inaddr,sizeof(inaddr)));
sleep(5);
close(fd);
wait(&status);
return 0;
}
else {
int acc,fd;
check(acc = socket(AF_INET,SOCK_STREAM,0));
int yes = 1;
check(setsockopt(acc,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
check(bind(acc,(sockaddr*)&inaddr,sizeof(inaddr)));
check(listen(acc,10));
check(fd = accept(acc,0,0));
char buf[1000];
long long total= 0;
do {
int r = send(fd,buf,sizeof(buf),0);
if(r < 0) {
printf("write %s\n",strerror(errno));
return 0;
}
else if(r==0) {
printf("Got eof\n");
return 0;
}
total += r;
if(total > 100*1024*1024) {
printf("Too much!!!!\n");
return 0;
}
printf("%lld\n",total);
}while(1);
}
return 0;
}
The output on Solaris (last two lines)
75768000
write Not enough space
The expected output on Linux (last two lines)
271760
write Connection reset by peer
Which happens only when the other side closes the socket.
Any ideas why and how can I fix it, what options to set?
P.S.: It is OpenSolaris 2009.06, x86
Edits
Added full C code that reproduces the problem
Answer:
This seems like a bug in specific version of Solaris kernel, libc library.
From OpenSolaris source code, I'm afraid the SO_SNDTIMEO option is unsupported: https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/uts/common/inet/sockmods/socksctp.c#l1233
If you want to block if there's no space available, you need to write code to do that.
POSIX is pretty clear that write on a socket is equivalent to send with no options, and that send "may fail if ... [i]nsufficient resources were available in the system to perform the operation."