C++ : semget : Returns ENOENT - c++

#include <sys/sem.h>
#include <sys/stat.h>
#include <cerrno>
#include <iostream>
using namespace std;
main () {
string key = "/m/lb1/activity.log";
int sem_key = ftok (key.c_str (), 1);
if (access (key.c_str (), R_OK) == 0)
printf ("keypath is accessible \n");
printf("sem_getid : %d", sem_key);
errno = 0;
int sem_id = semget (sem_key, 0, 0655);
printf( " sem get val :%d :%s\n", sem_id, strerror(errno));
}
$ ./a.out
keypath is accessible
sem_getid : 16850172 sem get val :-1 :No such file or directory
ls -l /m/lb1/activity.log
-rw-r--r-- 1 apple g6 0 Feb 9 19:08 /m/lb1/activity.log
The "access" call returns 0; so the keypath gets printed out. However, the semget is returning an ENOENT (No such file or directory)
sem_getid : 16850172
sem get val :-1 :No such file or directory
What could be wrong here?

semget gets an existing semaphore set unless you specify IPC_CREAT or IPC_PRIVATE in the third argument. From man semget:
ENOENT
No semaphore set exists for key and semflg did not specify IPC_CREAT.
In this case, ENOENT means that the semaphore set didn't exist, rather than meaning the path didn't exist.

The sem_key you pass to semget is not a filename or path, its just an arbitrary 32-bit integer. The namespace of possible semaphore keys is not related to the file name space in any way.
So you need to pick some (integer) key value to use, and you need to actually create the key before using it, by calling semget with IPC_CREAT first.

Related

execlp() is not properly working after fork()

I'm writing function that takes in the value in a linked list, then forks the process and execs a new process using the parameters passed into the command line. Here is the code I have for prog2b.cc:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cplist.h"
#include <sys/time.h>
#include <iostream>
#include <sys/wait.h>
#include <errno.h>
struct timeval start, end;
char *key;
int launch_children(cplist *head){
pid_t cpid;
double execution_times = 0;
if(cpid = fork() < 0 ){ // Important to trap errors
std::cerr << "ARGH I'm likely in trouble, get me out of here!" << std::endl;
exit(1); // Important to exit on errors
}else if (cpid == 0 ){
std::cout << "Child process "<< getpid() << ". Running grep on " << key << head->path << std::endl;
execlp("prog2b", "prog2b", "grep", "-R", key, head->path, NULL);
std::cerr << "Exec failed.. Why?" << std::endl;
exit(1); // VERY IMPORTANT - DON'T LET CHILD KEEP RUNNING
} else { // parent
head->cpid = cpid;
wait(NULL);
std::cout << "Child "<< cpid << "has terminated in " << execution_times;
}
}
int main(int argc, char *argv[]){
int i;
int j = 0;
cplist *head = (cplist*) malloc(sizeof(cplist));
head->path = NULL;
head->next = NULL;
if(strcmp(argv[1], "-v") == 0){
key = argv[2];
for(i = 3; i < argc; i++){
cpl_add(head, argv[i]);
j++;
}
} else {
key = argv[1];
for(i = 2; i < argc; i++){
cpl_add(head, argv[i]);
j++;
}
}
printf("key: %s\n", key);
launch_children(head);
return(0);
}
My program is supposed to take in the key and path values from the command line, then the child process should execlp using 'grep' '-r', and the values passed in. I'm struggling to get the exec to work properly. I've spent a lot of time on the man page for execs trying to understand them better and test out other execs but I've gotten stuck. The exec process won't run. Here is an example to show how it works right now:
When I run: ./prog2b 5678 /hw1/02/ from the command line my output is:
key: 5678
Child process 70788. Running grep on 5678 /hw1/02
Exec failed.. Why?
key: 5678
Child process 70789. Running grep on 5678 /hw1/02
Exec failed.. Why?
The correct out put should be:
key: 5678
Child process nnnnn. Running grep -R 5678 hw1/02
../../hw1/02/nYyLimCI:5678
../../hw1/02/ANFGmj97:5678
../../hw1/02/oFrtX8Sy:5678
../../hw1/02/UrYt9aBz:5678
../../hw1/02/wE1AMVeh:5678
../../hw1/02/F6TGJEiJ:5678
../../hw1/02/v1HG6zmh:5678
../../hw1/02/YyOSKcJG:5678
Child process nnnnn has terminated in a.bbbbbb seconds
I know the exec is failing and I've tried to use errno and it outputted "No such file or directory". I've figured out it referred to the first prog2b but when changed to ./prog2b I believe it causes a fork bomb. I also haven't quite grasped how to grep in the exec and I feel that could be the issue. Hopefully this will help clear up my troubles with fork and exec. I have a header file and linked list function class to _add and _dump but I don't believe those are what's causing the bug
The fact that errno is set to ENOENT (no such file or directory) tells you that execlp() was not able to find the executable.
From the manual page:
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character. The file is sought in the colon-separated list of directory pathnames specified in the PATH environment variable. If this variable isn't defined, the path list defaults to the current directory followed by the list of directories returned by confstr(_CS_PATH). (This confstr(3) call typically returns the value "/bin:/usr/bin".)
Your current working directory is most likely not in your PATH, and therefore the executable is not found. A simple solution is to just prepend a ./ in front of its name:
execlp("./prog2b", "prog2b", "grep", "-R", "key","head->path", NULL);
// here ^^
Tip for the future: you can see exactly where exec*() tries to look using strace:
$ strace -f -e execve ./prog2b 5678 /hw1/02/
execve("./prog2b ", ["./prog2b", "5678", "/hw1/02/"], [/* 40 vars */]) = 0
key: 5678
Child process 70788. Running grep on 5678 /hw1/02
execve("/usr/local/bin/prog2b", ["prog2b", "grep", "-R", "key","head->path"], [/* 40 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/bin/prog2b", ["prog2b", "grep", "-R", "key","head->path"], [/* 40 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/prog2b", ["prog2b", "grep", "-R", "key","head->path"], [/* 40 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/games/prog2b", ["prog2b", "grep", "-R", "key","head->path"], [/* 40 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/games/prog2b", ["prog2b", "grep", "-R", "key","head->path"], [/* 40 vars */]) = -1 ENOENT (No such file or directory)
Exec failed.. Why?
+++ exited with 1 +++

Fail to write to /proc/<pid>/coredump_filter

My code(below) fails:
11:Resource temporarily unavailable
The code is running as root (in an abrt hook) but has seteuid to the user that the pid in question is running as.
Writing to /proc/self/coredump_filter from within the process works OK.
How can I write to the coredump_filter from the abrt hook?
void SetDumpFlags(pid_t pid, int dumpflags){
std::string c_filter_name = "/proc/" + std::to_string( pid ) + "/coredump_filter";
int f = open( c_filter_name.c_str(), O_WRONLY );
if (f < 0) {
fprintf( log, "Couldn't open %s\n", c_filter_name.c_str());
bail_out(1);
}
int wsz = write( f, &dumpflags, sizeof dumpflags);
if (wsz != sizeof dumpflags){
fprintf( log, "Couldn't write to %s, %d:%s\n", c_filter_name.c_str(),errno, strerror(errno));
close( f );
bail_out(1);
}
close( f );
fprintf( log, "Pid %d, dump filter set to 0x%x\n", pid, dumpflags);
}
I tried to replicate your problem with a C example
(I would use C++11 but I'm on an ancient netbook without C++11 and it'd be hard to get it here and aclimate in the language).
I got an EACCESS on the open (and my guess you might be getting it too but the errno could get overwritten elsewhere?).
It seems the coredump_filter (at least on this Linux 3.2) starts as owned by
root and the seteuid doesn't change it.
I tried chown before setuid to no avail.
What did work (as expected) was to open the fd while you're still root
and keep it open during the seteuid call.
Then I could write to the file again successfully even after my euid changed.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define FLAGS "0x11"
#define FLAGSSZ (sizeof(FLAGS)-1)
int main()
{
pid_t pid = getpid();
char buf[sizeof("/proc/XXXXXXXXXXXXXX/coredump_filter")];
sprintf(buf,"/proc/%ld/coredump_filter",(long)pid);
int f;
if(0>(f=open(buf,O_WRONLY|O_TRUNC))) {perror("open");exit(1);}
if(FLAGSSZ != write(f,FLAGS,FLAGSSZ)){perror("write");exit(1);}
puts("ok");
char cat[sizeof("cat /proc/XXXXXXXXXXXXXX/coredump_filter")];
sprintf(cat,"cat /proc/%ld/coredump_filter", (long)pid);
system(cat);
char ls[sizeof("ls -l /proc/XXXXXXXXXXXXXX/coredump_filter")];
sprintf(ls,"ls -l /proc/%ld/coredump_filter", (long)pid);
system(ls); //owned by root, not writable by others
if(0>chown(buf,getuid(),getgid())){perror("chown"); exit(1); }
//chown returns success but ls -l doesn't agree
system(ls); //still owned by root
if(0>seteuid(getuid())){
perror("seteuid");
exit(1);
}
//can't reopen because of the perms but can still
//use the old fd if we kept it open
if(0>lseek(f,SEEK_SET,0)){perror("lseek"); exit(1);}
#define ALTFLAGS "0x22"
#define ALTFLAGSSZ (sizeof(ALTFLAGS)-1)
if(ALTFLAGSSZ != write(f,ALTFLAGS,ALTFLAGSSZ)){perror("write");exit(1);}
puts("ok");
system(cat);
}
I compiled with gcc c.c and made the a.out setuid root with sudo sh -c 'chown 0 $1 && chmod u+s $1' - a.out before running it.
I was trying to write data to the coredump_filter whereas I should have written a string! Doing it with a literal (e.g. #define FLAGS "0x11" as in the answer given by PSkocik ) fixes the problem.
The /proc/nnnnn/coredump_filter file is owned by the user that process nnnnn is running as. In my case this is root for some processes and another user for others. Switching user (in the abrt hook) to the appropriate user, before trying to write the coredump_filter, works OK.

How to neglect the umask so as to create the file with given permission

I am creating a file using open function and using O_CREAT | O_EXCEL . I have passed the mode as "0666" . But by masking finally the permission allotted to it is -rw-r--r-- and not the
-rw-rw-rw- . Someone told me i can use umask (011) and then reset the original mask again .
But i dont know how to pass this in c++ program. This is the small snippet of What i am doing .
# include <iostream>
# include <stdio.h>
# include <conio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
int main()
{
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
getch();
return 0;
}
creates file C:\Users\Ritesh\Music\music.txt with permission -rw-r--r-- . I want it to be -rw-rw-rw-
mode_t old_mask;
old_mask = umask(011);
open( ... );
umask(old_mask);
umask means permissions that you don't want to give to files by default. So if you want to control the permissions completely while creating a file, set umask to 0, which tells the os don't reserve any permissions and let you call the shot. like this:
int main()
{
mode_t oldmask = umask(0);
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
close(fd);
umask(oldmask);
getch();
return 0;
}
The only thread-safe way to set file permissions to be what you want is to set them explicitly with chmod() or fchmod() after creating the file (example without error checking):
int fd = open("C:\\Users\\Ritesh\\Music\\music.txt", O_CREAT | O_EXCL, 0666);
fchmod(fd, 0666 );
If you use umask(), you will change the umask value for the entire process. If any other threads are running you risk files getting created with unexpected permissions, which could lead to a security issue or other problems. And any child process created while your changed umask value is in effect will be created with an unexpected umask value.

How to get file's owner name in Linux using C++?

How can I get get the owner name and group name of a file on a Linux filesystem using C++? The stat() call only gives me owner ID and group ID but not the actual name.
-rw-r--r--. 1 john devl 3052 Sep 6 18:10 blah.txt
How can I get 'john' and 'devl' programmatically?
Use getpwuid() and getgrgid().
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
struct stat info;
stat(filename, &info); // Error check omitted
struct passwd *pw = getpwuid(info.st_uid);
struct group *gr = getgrgid(info.st_gid);
// If pw != 0, pw->pw_name contains the user name
// If gr != 0, gr->gr_name contains the group name
One way would be to use stat() to get the uid of a file and then getpwuid() to get the username as a string.

Cannot access file in Network Attached Storage (NAS) by using C++ access() function?

I have an Isilon NAS in 10.20.30.11 for example, and I mounted it like following:
mount 10.20.30.11:/folder /content
I could use ls command to find the file in folder or /content. Its mod is 777.
bash-3.00# ls -l /content/a/b/1.txt
total 344131
rwxrwxrwx 1 1005 65533 140750 Feb 28 00:58 1.txt
But I cannot access it by access() function.
#include <iostream>
#include <string>
#include <unistd.h>
#include <cerrno>
using namespace std;
#include <stdio.h>
int main( int argc, const char* argv[] )
{
int returnVal = 0;
returnVal = access(argv[1], R_OK);
cout << returnVal << endl;
cout << errno << endl;
return 0;
}
It will return -1 and 2 as a result, which means 'No such file or directory'.
./a.out /content/a/b/1.txt
-1
2
#define ENOENT 2 /* No such file or directory */
It is not a permission problem I think, because the mod is 777, and the result is 'No such file or directory'.
From the Linux man pages.
access() may not work correctly on NFS
file systems with UID mapping enabled,
because UID mapping is done on the
server and hidden from the client,
which checks permissions.
Finally, it is found that it need to use following command to mount the Isilon storage.
mount -o vers=2,proto=tcp
1.2.3.4:/remote /mnt
The version and protocol need specified.
Thanks!