system("echo text > t.log") reports error - c

The following code is intend to get and set RLIMIT_NOFILE, then do exec and system:
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void show_nofile()
{
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl)) {
perror("getrlimit");
}
printf("rlim_cur = %ld, rlim_max =%ld\n", rl.rlim_cur, rl.rlim_max);
}
int set_nofile(long cur, long max)
{
struct rlimit rl;
rl.rlim_cur = cur;
rl.rlim_max = max;
if (setrlimit(RLIMIT_NOFILE, &rl)) {
perror("setrlimit");
return -1;
}
return 0;
}
int main(int argc, char* argv[])
{
show_nofile();
if (argc<2) {
set_nofile(9, 10);
show_nofile();
printf("now execlp...\n");
execlp("./a.out", "a.out", "-n", (char*)NULL);
} else {
if (-1==system("/bin/echo abc > log")) {
perror("system");
}
}
return 0;
}
This is how i compile and run it gcc -Wall limit.c && ./a.out, the result is:
rlim_cur = 1024, rlim_max =4096
rlim_cur = 9, rlim_max =10
now execlp...
rlim_cur = 9, rlim_max =10
sh: 1: 1: Invalid argument
What could be wrong here?
P.S.:
I run it in my home dir, so there is not permission problems.
Besides, the error message does not seem to relate file permission.

The problem is that you set a too low limit for RLIMIT_NOFILE.
strace your program, you will find lines like
10512 open("def", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10512 fcntl(1, F_DUPFD, 10) = -1 EINVAL (Invalid argument)
10512 close(1) = 0
which means sh cannot redirect I/O successfully.
Please replace
set_nofile(9, 10);
with
set_nofile(12, 20);
and try again.

Is the "text" a variable? If so, you may need to try this:
system("echo $text > t.log");

There seems to be nothing wrong with the code as such..
Try to check the write permissions on the directory you are trying to execute the program in.
On successful execution, your file "t.log" will only contain the word "text".
#Yunxuan Tuanmu: '$text' will be the shell's context, which will be null since it is not set within that context

Related

major() and minor() give different numbers from ls

Part of my C program is to output whether two device files are equal (i.e., same kind of device file and same major and minor numbers). It outputted that tty and tty2 are the same device files while I think they are not.
I added code to print out the retrieved major and minor numbers for each file and it printed out different numbers from what I got when I did ls -l /dev/tty and ls -l /dev/tty2. The major and minor numbers printed out for both tty and tty2 are 0 and 6, while using ls, they are 5 and 0 for tty and 4 and 2 for tty2.
I'm new to Linux and C.
I have double-checked the manpage for major() and minor() and it seemed that I used these functions correctly. So, I don't know what went wrong.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#define report_error(x) puts("error")
#define BUFFER_SIZE 1<<16
int main(int argc, char *argv[])
{
struct stat statbuf1;
struct stat statbuf2;
char *fn1;
char *fn2;
if (argc < 3) {
if (argc < 1) {
report_error("no command line");
} else {
report_error("Not enough arguments");
}
}
fn1 = argv[1];
fn2 = argv[2];
if (lstat(fn1, &statbuf1)) {
report_error(strerror(errno));
}
if (lstat(fn2, &statbuf2)) {
report_error(strerror(errno));
}
if (S_ISCHR(statbuf1.st_mode) && S_ISCHR(statbuf2.st_mode)) {
unsigned int major1 = major(statbuf1.st_dev);
unsigned int major2 = major(statbuf2.st_dev);
unsigned int minor1 = minor(statbuf1.st_dev);
unsigned int minor2 = minor(statbuf2.st_dev);
printf("%d %d\n%d %d\n", major1, major2, minor1, minor2);
if (major1 == major2 && minor1 == minor2) {
printf("the two device files are equal\n");
exit(0);
}
}
return 0;
}
st_dev is the ID of device containing file, according to the man page. In other words, the device where the file's name resides. So it's the same as for your /dev directory, as you'll see if you use the stat command from your shell.
You're interested in st_rdev, which is Device ID (if special file) (again, from the man page).
The stat command shows both:
stat /dev/tty /dev/tty1
File: /dev/tty
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 6h/6d Inode: 1035 Links: 1 Device type: 5,0
Access: (0620/crw--w----) Uid: ( 0/ root) Gid: ( 5/ tty)
....
File: /dev/tty1
Size: 0 Blocks: 0 IO Block: 4096 character special file
Device: 6h/6d Inode: 1044 Links: 1 Device type: 4,1
Access: (0620/crw--w----) Uid: ( 0/ root) Gid: ( 5/ tty)
Fixed code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
int get_char_device(const char *name,
unsigned *dev_major, unsigned *dev_minor)
{
struct stat buf;
if (stat(name, &buf)) {
perror(name);
return 1;
}
if (!S_ISCHR(buf.st_mode)) {
fprintf(stderr, "%s: not a char device\n", name);
return 1;
}
*dev_major = major(buf.st_rdev);
*dev_minor = minor(buf.st_rdev);
return 0;
}
int main(void)
{
unsigned int major1, minor1, major2, minor2;
if (get_char_device("/dev/tty1", &major1, &minor1) ||
get_char_device("/dev/tty2", &major2, &minor2)) {
return 1;
}
printf("%d %d\n%d %d\n", major1, major2, minor1, minor2);
if (major1 == major2 && minor1 == minor2) {
puts("the two device files are equal");
return 1;
}
}

Semaphore simulation program: Segmentation Fault error

I've written the following program, that simulates the work of semaphore. There are three functions: lock, unlock, lockpath.
lock = opens the file; checks if the file already exists, and if it does, puts the current process to sleep. If the file didn't exist, it is created and TRUE is returned.
unlock = deletes the file
lockpath = returns the path name corresponding to the file that might be created.
Here's the source code:
#include <unistd.h>
//exit();
#include <stdlib.h>
//errno
#include <errno.h>
//creat(..)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//strcat, strcpy
#include <string.h>
//For err_sys
#include <stdio.h>
#define LOCKDIR "/tmp/"
#define MAXTRY 3
#define WAITTIME 5
enum BOOLEAN{TRUE, FALSE};
void err_sys(const char* x) {
perror(x);
exit(1);
}
static char* lockpath(char* name) {
static char path[20];
strcpy(path, LOCKDIR);
return (strcat(path, name));
}
int lock(char* name) {
char *path;
int fd, incerc;
extern int errno;
path = lockpath(name);
int try = 0;
while ((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0
&& errno == EEXIST) {
if (++try >= MAXTRY)
return FALSE;
sleep(WAITTIME);
}
if (fd < 0 || close(fd) < 0)
err_sys("lock");
return TRUE;
}
void unlock(char* name) {
if (unlink(lockpath(name)) < 0)
err_sys("unlock");
}
int main(void) {
pid_t child_process;
child_process = fork();
char* sem_file_name = "test_semaf";
if (child_process != 0)
{
printf("\nParent process ID: %d", getpid());
}
else
{
printf("\nChild process ID: %d", getpid());
}
if (lock(sem_file_name))
{
printf("\nProcess with ID: %d", getpid());
printf("\nonly, has access to %s", strcat(LOCKDIR, sem_file_name)); //****
unlock(sem_file_name);
} else {
printf("\nProcess with ID: %d", getpid());
printf("\nwas unable to get access to %s", strcat(LOCKDIR, sem_file_name));
}
return 0;
}
The line at which the program stops is marked with: ****
The error is:
Program received signal SIGSEGV, Segmentation fault.
__strcat_ssse3 () at ../sysdeps/x86_64/multiarch/strcat-ssse3.S:571
571 ../sysdeps/x86_64/multiarch/strcat-ssse3.S: No such file or directory.
The problem is that I get Segmentation Fault, and can't find where's the problem. To me, everything's fine. A process is supposed to create file X. Then, if another process tries to create it's own file X, it is not allowed; the process is put to sleep. This second process is allowed to make MAXTRY attempts. If it does not succeed after MAXTRY attempts, the lock() function returns FALSE. Finally, when a process, that has successfully created his own X file, doesn't need it now, the file X is deleted.
Can you, please, tell what do you think is the problem with this program? Thank you in advance.
EDIT :
Here's the link to the page that explains why lockpath() function isn't correct.
Is returning a pointer to a static local variable safe?
This is the cause of your crashes:
strcat(LOCKDIR, sem_file_name)
Here you try to append to a literal string constant.
You should use the lockpath function here as well.
The problem seems to be in your misunderstanding of strcat() function. The function appends string in second parameter to the string in first parameter - but you need to ensure there is enough space for the data. Read the man page.
That means that
char * dest = "whatever";
strcat(dest, anything_else);
is always wrong. What you want is
char dest[SIZE] = "whatever";
strcat(dest, anything_else);
where SIZE is big enough for the buffer to be able to contain the whole concatenated string.
Also, your lockpath() function is broken. See this answer to learn why. You need to create the dest buffer outside the lockpath() function and pass it to it as a parameter.

How can I check if I have permissions to open a file without opening it on Linux in C?

I want to be able to check to see if a file could be opened on Linux (for read or for read and write). However I don't have control of the code which will be opening the file, so I can't do what I would normally do which is to open it and then handle the error.
I appreciate that there will always be race conditions on any check due to permissions changing after the call has returned but before the open call, but I'm trying to avoid some undesirable error logging from a library which I have no control over.
I'm aware of stat, but I'd prefer not to need to try to replicate the logic of checking user IDs and group IDs.
You can use:
access("filename", R_OK);
or
euidaccess("filename", R_OK);
To check if your UID or EUID have read access to a respective file. (UID and EUID will be different if your are running setuid)
Use euidaccess or access, although you almost certainly always want to use the former.
(edit: the reason for adding this was that with this approach you can ensure you can avoid the race conditions. That said, it is quite a tricky approach, so maybe just coping with potential race conditions is a better practical approach).
If your goal is to shield the code that you do not own from unhandled errors, using LD_PRELOAD to intercept the open call itself might be of use. An example of it with malloc is here: Overriding 'malloc' using the LD_PRELOAD mechanism
here my quick improvisation on how you could do it - basically an interceptor that will launch an interactive shell to you to correct the error.
WARNING: lots of open calls actually do fail for legit reasons, e.g. when the program is going over different directories in the path trying to find the file, so treat this code as an educational example only to be used with this example code - if you are any close to real world use, your code definitely will need to be smarter. With all this said, let's get to the meat.
First, the "offensive" program that you do not have the control over:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int res = 0;
printf("About to try to open the file...\n");
res = open("/tmp/unreadable", O_RDONLY);
printf("The result after opening: %d\n", res);
if (res < 0) {
perror("Could not open, and here is what the errno says");
} else {
char buf[1024];
int fd = res;
res = read(fd, buf, sizeof(buf));
printf("Read %d bytes, here are the first few:\n", res);
buf[30] = 0;
printf("%s\n", buf);
close(fd);
}
}
Then the interceptor:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
static int (*real_open)(const char *pathname, int flags, ...)=NULL;
static void __open_trace_init(void)
{
real_open = dlsym(RTLD_NEXT, "open");
if (NULL == real_open) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
return;
}
}
int open(const char *pathname, int flags, ...)
{
if(real_open==NULL)
__open_trace_init();
va_list va;
int res = 0;
do {
if (flags & O_CREAT) {
int mode = 0;
va_start(va, flags);
mode = va_arg(va, int);
va_end(va);
fprintf(stderr, "open(%s, %x, %x) = ", pathname, flags, mode);
res = real_open(pathname, flags, mode);
fprintf(stderr, "%d\n", res);
} else {
fprintf(stderr, "open(%s, %x) = ", pathname, flags);
res = real_open(pathname, flags);
fprintf(stderr, "%d\n", res);
}
if (res < 0) {
printf("The open has returned an error. Please correct and we retry.\n");
system("/bin/sh");
}
} while (res < 0);
return res;
}
And here is how it looks like when running:
ayourtch#ayourtch-lnx:~$ echo This is unreadable >/tmp/unreadable
ayourtch#ayourtch-lnx:~$ chmod 0 /tmp/unreadable
ayourtch#ayourtch-lnx:~/misc/stackoverflow$ LD_PRELOAD=./intercept ./a.out
About to try to open the file...
open(/tmp/unreadable, 0) = -1
The open has returned an error. Please correct and we retry.
open(/dev/tty, 802) = 3
open(/dev/tty, 802) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/lib/terminfo/x/xterm, 0) = 3
open(/etc/inputrc, 0) = 3
sh-4.1$ ls -al /tmp/unreadable
---------- 1 ayourtch ayourtch 19 2011-10-18 13:03 /tmp/unreadable
sh-4.1$ chmod 444 /tmp/unreadable
sh-4.1$ exit
open(/home/ayourtch/.bash_history, 401) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 201) = 3
open(/tmp/unreadable, 0) = 3
The result after opening: 3
Read 19 bytes, here are the first few:
This is unreadable
�0
ayourtch#ayourtch-lnx:~/misc/stackoverflow$
By the way this example also exposes an obvious bug in the first "test" code - I should have checked that the number of the chars read was at least 30 and put the null char accordingly.
Anyway, that code is supposed to be buggy and outside of the control, so it is kind of good to have a bug in it - else you would not need to use this kind of hack :-)

Bus error in C Program on Unix machine

I'm fairly unexperienced with C and am running into a "Bus error" that I cannot understand the cause of. I had never heard of gdb but came across it on this forum and tried using it on my problem program and got the following output:
% gdb Proc1 GNU gdb 5.0
...
This GDB was
configured as
"sparc-sun-solaris2.8"...
(no
debugging symbols found)...
(gdb) run
Starting program:
/home/0/vlcek/CSE660/Lab3/Proc1
(no
debugging symbols found)...
(no
debugging symbols found)...
(no
debugging symbols found)...
Program
received signal SIGSEGV, Segmentation
fault. 0x10a64 in main ()
I have no idea what this means, is that saying there's an error in line 10 in my code? If so, line 10 in my code is merely "int main()" so I'm not sure the issue there... When I try running the program all it says is "Bus error" so I'm not sure where to go from here. I even tried putting a printf right after main and it doesn't print the string, only gives me a Bus error.
Below is my code:
// Compilation Command: gcc -o Proc1 Proc1.c ssem.o sshm.o
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ssem.h"
#include "sshm.h"
// Code of Proc1
int main()
{int i, internal_reg;
int key1 = 111111, key2 = 222222, key3 = 333333, key4 = 444444;
/* here create and initialize all semaphores */
int sem1 = sem_create(key1, 1);
if (sem1 < 0) {
perror("sem failed");
}
int sem2 = sem_create(key2, 1);
if (sem2 < 0) {
perror("sem failed");
}
int sem3 = sem_create(key3, 1);
if (sem3 < 0) {
perror("sem failed");
}
int sem4 = sem_create(key4, 1);
if (sem4 < 0) {
perror("sem failed");
}
/* here created: shared memory array Account of size 3 */
int *Account;
int shmid = shm_get(123456, (void**) &Account, 3*sizeof(int));
if (shmid < 0) {
perror("shm failed");
}
Account[0]=10000;
Account[1]=10000;
Account[2]=10000;
/* synchronize with Proc2, Proc3 and Proc4 (4 process 4 way synchronization)*/
for (i = 0; i < 1000; i++)
{
sem_signal(sem1);
sem_signal(sem1);
sem_signal(sem1);
internal_reg = Account[0];
internal_reg = internal_reg - 200;
Account[0] = internal_reg;
/* same thing, except we're adding $100 to Account1 now... */
internal_reg = Account[1];
internal_reg = internal_reg + 200;
Account[1] = internal_reg;
if (i % 100 == 0 && i != 0) {
printf("Account 0: $%i\n", Account[0]);
printf("Account 1: $%i\n", Account[1]);
}
if (i == 300 || i == 600) {
sleep(1);
}
sem_wait(sem2);
sem_wait(sem3);
sem_wait(sem4);
}
/* Here add a code that prints contents of each account
and their sum after 100th, 200th, 300th, ...., and 1000th iterations*/
}
/*in the code above include some wait and signal operations on semaphores. Do no
t over-synchronize. */
Here is the documentation for ssem and sshm:
/*
* ssem.c
*
* Version 1.0.0
* Date : 10 Jan 2002
*
*/
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include "ssem.h"
#define PERMS 0600
static struct sembuf op_lock[1] = {
0, -1, 0
};
static struct sembuf op_unlock[1] = {
0, 1, IPC_NOWAIT
};
int sem_create(int key,int initval)
{
int semid,i;
semid = semget((key_t)key, 1, IPC_CREAT | PERMS);
for(i=0;i<initval;i++)
semop(semid,&op_unlock[0],1);
return semid;
}
int sem_open(int key)
{
int semid;
semid = semget(key,0,0);
return semid;
}
int sem_wait(int semid)
{
return semop(semid,&op_lock[0],1);
}
int sem_signal(int semid)
{
return semop(semid,&op_unlock[0],1);
}
int sem_rm(int semid)
{
return semctl(semid, 0, IPC_RMID, 0);
}
/*
* sshm.c
*
* Routines for Simpler shared memory operations
* Version : 1.0.0.
* Date : 10 Jan 2002
*
*/
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include "sshm.h"
#define PERMS 0600
int shm_get(int key, void **start_ptr, int size)
{
int shmid;
shmid = shmget((key_t) key, size, PERMS | IPC_CREAT);
(*start_ptr) = (void *) shmat(shmid, (char *) 0, 0);
return shmid;
}
int shm_rm(int shmid)
{
return shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0);
}
After compiling Proc1.c with the -ggdb flag and running gdb I got the following:
Program received signal SIGSEGV,
Segmentation fault. 0x10a64 in main ()
at Proc1.c:36
36 Account[0]=10000
Why would this cause a segmentation fault?
After changing the declaration of Account to
int *Account = 0;
and adding
printf("Account == %p\n", Account);
before Account[0] = 10000;
I get the following upon running Proc1:
Account == ffffffff
Bus error
In order to get more sensible results from gdb you should compile your program with the -ggdb option. This will then include debugging information (like line numbers) into your program.
What you are currently seeing is the memory address (0x10a64) of the program counter. This will not help you very much unless you can correlate the assembly instructions you find there with a part of your C program yourself.
It looks like you are using shm_get properly. I think the library designer has made a terrible mistake in naming the function so similarly to shmget.
It's just as I thought. The Account pointer is ending up with an invalid value (aka 0xffffffff (aka (void *)(-1))) in it. The value (void *)(-1) generally indicates some sort of error, and it is explicitly mentioned in the manpage for shmat. This indicates that the shmat call inside the library failed. Here is how you can tell if it failed:
if (Account == (void *)(-1)) {
perror("shmat failed");
}
Account[0] = 10000;
// ...
Now, why it failed is an interesting mystery. Apparently the shmget call succeeded.
Personally, I think System V IPC is basically deprecated at this point and you should avoid using it if you can.
Depending on your compiler and your compiler options you might encounter an aliasing problem because your are casting the address of your Account pointer. These oldish interfaces are not in phase with modern antialiasing rules, meaning that the optimizer supposes that the value of Account wouldn't change.
Also you should get the argument for shm_get as close as possible to the expected type. Try perhaps something like the following.
void volatile* shmRet;
int shmid = shm_get(123456, (void**) &shmRet, 3*sizeof(int));
int *Account = shmRet;
I don't have the same architecture, so I don't know the exact prototype of your shm_get but usually it is also a bad idea to use fixed keys for this type of functions. There should be some function that returns you some key to use in your application.

C: strange behavior with nftw()

I've this code:
#include <ftw.h>
#include <stdio.h>
#include <string.h>
int nftw_stat(const char *path, const struct stat *stat, int flags,
struct FTW *ftw)
{
if (strcmp(path, "/home/pf/.gvfs\0") == 0) {
printf("nftw()\n");
printf("mode = %d\n", stat->st_mode);
printf("size = %d\n", (int) stat->st_size);
}
return 0;
}
int main()
{
if (nftw("/home/pf", &nftw_stat, 1, FTW_PHYS)) {
perror("nftw");
return 2;
}
}
If I execute it normally, it returns the same way as stat() function:
mode = 16704 (S_IFDIR | S_IRUSR | S_IXUSR)
size = 0
But when I execute it with sudo, it returns this:
mode = 16832 (S_IFDIR | S_IRWXU)
size = 4096
What happens? If I use stat() with sudo it give me the Permission denied error. This happens only with .gvfs directory, whose permissions are 500 (dr-x------). If sudo can't read with stat() , why it works with nftw()? :|
What's probably happening is that stat has failed on the directory, but you are printing the values of the stat structure regardless, meaning you get rubbish. You need to check the value of the typeflag, which you call "flags" in your nftw_stat routine to make sure that stat has successfully set the stat structure.
int nftw_stat(const char *path, const struct stat *stat, int typeflag,
struct FTW *ftw)
{
if (typeflag == FTW_NS) {
printf("stat failed on %s\n", path);
return 1;
}
if (strcmp(path, "/home/pf/.gvfs\0") == 0) {
printf("nftw()\n");
printf("mode = %d\n", stat->st_mode);
printf("size = %d\n", (int) stat->st_size);
}
return 0;
}

Resources