File IO unit test in C [closed] - c

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I need to write unit tests for a simple logger implemented in C (unit tests are in c++).
I'm searching for a way to mock an unsuccessful 'write' to the log file ,in order to see that the logger to responds appropriately and returns an error.
I tried renaming the log-file before writing on to it:
static bool fileWriteMiss() {
const char* testFile = "missLogFile.log";
const char* movedFile = "missedLogFile.log";
ASSERT_TRUE(
spLoggerCreate(testFile, SP_LOGGER_DEBUG_INFO_WARNING_ERROR_LEVEL) ==
SP_LOGGER_SUCCESS);
rename(testFile, movedFile);
ASSERT_TRUE(spLoggerPrintError("MSGA", "sp_logger_unit_test.c", __func__,
__LINE__) == SP_LOGGER_WRITE_FAIL);
spLoggerDestroy();
return true;
}
and I tried deleting the log-file before writing on to it:
static bool fileWriteMiss() {
const char* testFile = "missLogFile.log";
ASSERT_TRUE(
spLoggerCreate(testFile, SP_LOGGER_DEBUG_INFO_WARNING_ERROR_LEVEL) ==
SP_LOGGER_SUCCESS);
remove(testFile);
ASSERT_TRUE(spLoggerPrintError("MSGA", "sp_logger_unit_test.c", __func__,
__LINE__) == SP_LOGGER_WRITE_FAIL);
spLoggerDestroy();
return true;
}
ASSERT_TRUE function is implemented in an assertion library that is assumed to work properly.
spLoggerCreate is the initialization function of the logger structure (I'm being careful not to call it construction and object since the implementation is not oop, but pure C code).

A working hack is to write to file that you cannot write to (please note that you can write to non-existent file in an existing directory - it would be created). There are few ways, some os-dependent:
write to a file in non-existing directory. While it is complicated in a generic case, you can use some tricks like using chars unlikely to meet in a filename, like '\x01'.
const char* testFile = "non-existent\x01dir/missLogFile.log";
write to a file you have no rights to write to. In UNIX you can issue
chmod a-w file.log
command on an existing file to revoke write permission.
write to a system read-only pseudo-file.
const char* testFile = "/proc/uptime"; // linux
const char* testFile = "CLOCK$"; // windows / dos
I believe unit-tests should never access OS, and your test becomes non-unit this way, but this is a terminology question and a holy war subject.
Update:
Your comment actually makes it a poorly asked but pretty interesting question. I think you can edit it and I will try to answer. As things go hacky here, I'll focus on Linux only, answering the question
"How to make file open-for-write succeed, but subsequent write fail?"
Probably the most common error real application face is "No space left on device". This is easy to reproduce in hermetic way: let us create a fat16 filesystem in local file, precisely:
$ dd if=/dev/urandom of=test_disk bs=1048576 count=1
$ mkdosfs test_disk
$ mkdir /tmp/mnt
$ sudo mount -o loop,rw,umask=000 test_disk /tmp/mnt/
Now fill the filesystem. Alternatively we could use smaller size.
$ perl -e 'print "a"x(1024*1002)' > /tmp/mnt/a.txt
$ echo test > /tmp/mnt/test || echo "can't write test file"
And finally hit the error:
$ echo `perl -e 'print "a"x4096'` > /tmp/mnt/test || echo "test succeeded"
bash: echo: write error: No space left on device
test succeeded
To be precise the error is encountered writing byte 1009:
$ ls -l /tmp/mnt/
total 1004
-rwxrwxrwx 1 root root 1026048 Mar 27 17:56 a.txt
-rwxrwxrwx 1 root root 1009 Mar 27 17:57 test
A nice side effect of FAT filesystem is that now you are free to open more files there and write will fail from byte 0, as you asked.

Related

Implement sudo su in a C program [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I writing a program that is used by a parent process. I cannot control the parent process, its permissions, and how it runs my process.
My program runs a device that needs root permissions, while the parent process does not have the corresponding permissions.
Tried to use setuid(0); setgid(0); in my program. Also, tried to add the user of the process to use sudo. In addition, I run the solutions below. Does not works
The code
if(dry_run == 0)
{
PRINT("reached here\n");
ret = ioctl((int64_t)device, NVME_IOCTL, &usr_io_cmd);
}
if (ret != 0 ) {
PRINT("ERROR : error %x returned\n", ret);
PRINT( "%s\n",strerror(errno) );
Where PRINT prints to a log file.
The log file
[2019-09-05 14:27:25] reached here
[2019-09-05 14:27:25] ERROR : error ffffffff returned
[2019-09-05 14:27:25] Operation not permitted
What can my program do? How can I implement "sudo su" in my program? Is there an alternative solution?
Edit: why this question is locked? I tried the solutions below and it did not worked.
What you need to do is to have your program have the setuid or setgid bit set in the file permissions. This will cause the running process to have the effective user id of that of the program owner (setuid) or effective gid that of the program group (gid). You then can perform actions as those identities or become those with setuid and setgid.
sudo su is a cargo-cult way of doing things - both are programs that more or less do the same thing - elevate permissions of an unprivileged caller.
I.e. to have the program foo owned by root with setuid bit set, you'd do:
gcc foo.c -o foo
sudo chown root:root foo
sudo chmod 4755 foo

setuid not working, linux rhel6, simple example

This is a repost after being referred to "Calling a script from a setuid root C program - script does not run as root" for a solution
My problem is different in that I do not want to run the C program (and c-shell script called inside) as root. Rather, I want to run as the owner of the c program file.
Also, I tried with "setuid(0)" as this was the solution in the referenced post. This is reflected in the edited code below. Same results.
Also, I opened permissions up all the way this time with "chmod 7775" (just in case it was a permissions problem)
Here's the original note with edits to reflect the change to "setuid(0)"
I'm having a problem implementing an simple example that would demonstrate how setuid can be made to run a binary with the uid of the file owner of the binary. What I would eventually like to do is run a c-shell script using this technique.
I read that this will not work for shell scripts but also heard of a work-around using a C program to run a system() call that'll run the c-shell script ( e.g. system("source my.csh") ). I wrote a C program that attempts this, plus simply reports the current uid. This is what I tried...
From my current shell, I did a "su katman" to become the user of the binary I want to run as user katman.
I created a C program try.c. ...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
setuid(0);
printf("In try.c ... sourcing katwhoami.csh...\n");
system( "source /home/me/su_experiments/katwhoami.csh");
printf("In try.c ... using straight system call...\n");
system("whoami");
return 0;
}
I "setuid(0)" as recommended by a reference to a different note. But earlier, I tried setting it to the uid of the C program's owner as obtained with "id -u".
The katwhoami.csh shell script is simply...
date
echo "In katwhoami.csh, I am "`whoami`
echo "In katwhoami.csh, I am "$USER
exit
Then I compiled the C program and set the bit...
% gcc -o try try.c
% chmod 7775 try
% ls -l try
-rwsrwsr-x 1 katman design 6784 Mar 1 11:59 try
And then I test it...
% try
In try.c ... sourcing katwhoami.csh...
Thu Mar 1 12:28:28 EST 2018
In katwhoami.csh, I am ktadmin
In katwhoami.csh, I am ktadmin
In try.c ... using straight system call...
ktadmin
...which is what I expected.
I exit to get back to the shell I started from, hoping that if I run try there, it'll tell me I'm "katman"....
% exit
% whoami
daveg
% try
In try.c ... sourcing katwhoami.csh...
Thu Mar 1 12:30:04 EST 2018
In katwhoami.csh, I am daveg
In katwhoami.csh, I am daveg
In try.c ... using straight system call...
daveg
... which is not what I was hoping for :-(
As you can probably tell, I'm new at using this.
Any help would be appreciated !
Update....
I tried a new sample program, a.c...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fh;
setuid(1234);
system("whoami");
fh=fopen("test.file","w");
fprintf(fh,"Here I am\n");
fclose(fh);
return 0;
}
I compiled and set the bit
gcc -o a a.c
chmod 4775 a
Then I exited the "su" and ran as a user that is NOT the owner of the binary. Same result as before with regard to reported uid (the current uid), BUT, the owner of the file that the C program created ion this version was the owner of the C program !!! So that worked.
Is there something fishy about the "whoami" command? system() call ?
If I do a "system("source some.csh") does it create a new shell which assumes the original uid (not the owner of the C binary) ? Something like that ?
I really need the new uid to "stick" as far as child processes.

PAM standard macros and logging on CentOS7

According to the D(x) macro defined in pam_macros.h (source code) and used as follows:
D(("Hello PAM World"));
Where is this log located on CentOS7?
Note that I am using as flag debug in my pam.d conf file.
I tried also the following command:
grep -rnw '/var/log/' -e "Hello Pam World"
But with no success.
In the file you link, there are these lines at the top:
/*
* This is for debugging purposes ONLY. DO NOT use on live systems !!!
* You have been warned :-) - CG
*
* to get automated debugging to the log file, it must be created manually.
* _PAM_LOGFILE must exist, mode 666
*/
#ifndef _PAM_LOGFILE
#define _PAM_LOGFILE "/tmp/pam-debug.log"
#endif
So it looks like the output will be directed to /tmp/pam-debug.log, but you have to create it before, and give it full read-write permissions:
$ touch /tmp/pam-debug.log
$ chmod 666 /tmp/pam-debug.log
Looking to the Linux version of this file, it looks like it is written to /var/run/pam-debug.log, but only if it is compiled with PAM_DEBUG.
There a nice comment in configure.ac:
if test x"$enable_debug" = x"yes" ; then
AC_DEFINE([PAM_DEBUG],,
[lots of stuff gets written to /var/run/pam-debug.log])

C script running as cron giving permission denied error

I have a .c file compiled and would like to run via a cron job but I end up getting this error:
/bin/sh: /usr/local/bin/get1Receive.c: Permission denied.
What is causing this error and how do I fix it?
Should I be running the .c file in cron or a different compiled file?
Results from /tmp/myvars
GROUPS=()
HOME=/root
HOSTNAME=capture
HOSTTYPE=x86_64
IFS='
'
LOGNAME=root
MACHTYPE=x86_64-redhat-linux-gnu
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/bin:/bin
POSIXLY_CORRECT=y
PPID=11086
PS4='+ '
PWD=/root
SHELL=/bin/sh
SHELLOPTS=braceexpand:hashall:interactive-comments:posix
SHLVL=1
TERM=dumb
UID=0
USER=root
_=/bin/sh
Results from file get1Receive.c
file get1Receive.c
get1Receive.c: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
Snippet of codes.
sprintf(queryBuf1,"SELECT ipDest, macDest,portDest, sum(totalBits) FROM dataReceive WHERE timeStampID between '%s' And '%s' GROUP BY ipDest, macDest, portDest ",buff1,buff2);
printf("\nQuery receive %s",queryBuf1);
if(mysql_query(localConn, queryBuf1))
{
//fprintf(stderr, "%s\n", mysql_error(localConn));
printf("Error in first query of select %s\n",mysql_error(localConn));
exit(1);
}
localRes1 = mysql_store_result(localConn);
int num_fields = mysql_num_fields(localRes1);
printf("\nNumf of fields : %d",num_fields);
printf("\nNof of row : %lu",mysql_num_rows(localRes1));
If the output of this command:
file get1Receive1.c
shows that file name to be a valid executable that part is very unusual, but okay.
Assuming you are using biz14 (or your real username's ) crontab try this:
use the command crontab -e to create this line in your crontab:
* * * * * set > /tmp/myvars
Wait a few minutes, go back into crontab -e and delete that entry.
Use the set command from the command line to see what variables and aliases exist.
Compare that with that you see in /tmp/myvars You have to change how your C code executes by changing the variables and aliases the cron job runs with.
If you are running the cron job in someone else's crontab, then you have a bigger problem. Check file permissions on get1Receive1.c. and the directory it lives in. That other user (the one who wons the crontab) has to have permissions set on your directory and get1Receive1.c so the job can run.
Example crontab entry:
0 10 * * 1-5 /path/to/get1Receive1.c > /tmp/outputfile
Read /tmp/outputfile to see what you got. You are using printf in your code. printf only writes to the controlling terminal. There is no controlling terminal, so redirect the printf stuff to a file.
Last effort on this problem:
Check return codes on EVERYTHING. All C functions like fread(), any db function, etc. If a return code gives a fail response ( these are different for different function calls) then report the error number the line number and function - gcc provides LINE and func. Example:
printf("error on line %d in my code %s, error message =%s\n", __LINE__, __func__, [string of error message]);
If you do not check return codes you are writing very poor C code.
CHECK return codes, please, now!
Permission wise you could have two issues.
1. The 'c' file's permissions don't allow who you are running it as to run it.
2. You are running the cron with a script which doesn't have permissions.
Here's a helpful post: How to give permission for the cron job file?
The fact that you are running a 'c' file and referring to it as a script makes me think you're using C shell and not writing it as a C language program which would need to be compiled and have the generated executable run by the cron. If you're not using gcc or have never called gcc on your 'C' script then it's not C and call it C shell to avoid confusion.

How can I run this DTrace script to profile my application?

I was searching online for something to help me do assembly line profiling. I searched and found something on http://www.webservertalk.com/message897404.html
There are two parts of to this problem; finding all instructions of a particular type (inc, add, shl, etc) to determine groupings and then figuring out which are getting executed and summing correcty. The first bit is tricky unless grouping by disassembler is sufficient. For figuring which instructions are being executed, Dtrace is of course your friend here( at least in userland).
The nicest way of doing this would be instrument only the begining of each basic block; finding these would be a manual process right now... however, instrumenting each instruction is feasible for small applications. Here's an example:
First, our quite trivial C program under test:
main()
{
int i;
for (i = 0; i < 100; i++)
getpid();
}
Now, our slightly tricky D script:
#pragma D option quiet
pid$target:a.out::entry
/address[probefunc] == 0/
{
address[probefunc]=uregs[R_PC];
}
pid$target:a.out::
/address[probefunc] != 0/
{
#a[probefunc,(uregs[R_PC]-address[probefunc]), uregs[R_PC]]=count();
}
END
{
printa("%s+%#x:\t%d\t%#d\n", #a);
}
main+0x1: 1
main+0x3: 1
main+0x6: 1
main+0x9: 1
main+0xe: 1
main+0x11: 1
main+0x14: 1
main+0x17: 1
main+0x1a: 1
main+0x1c: 1
main+0x23: 101
main+0x27: 101
main+0x29: 100
main+0x2e: 100
main+0x31: 100
main+0x33: 100
main+0x35: 1
main+0x36: 1
main+0x37: 1
From the example given, this is exactly what i need. However I have no idea what it is doing, how to save the DTrace program, how to execute with the code that i want to get the results of. So i opened this hoping some people with good DTrace background could help me understand the code, save it, run it and hopefully get the results shown.
If all you want to do is run this particular DTrace script, simply save it to a .d script file and use a command like the following to run it against your compiled executable:
sudo dtrace -s dtracescript.d -c [Path to executable]
where you replace dtracescript.d with your script file name.
This assumes that you have DTrace as part of your system (I'm running Mac OS X, which has had it since Leopard).
If you're curious about how this works, I wrote a two-part tutorial on using DTrace for MacResearch a while ago, which can be found here and here.

Resources