setenv, unsetenv, putenv - c++

I am working on a custom shell for a systems programming class. We were instructed to implement the built-in setenv() and unsetenv() commands with a hint of check man pages for putenv().
My issue is that setenv(char*, char*, int) and putenv(char*) do not seem to be working at all. My code for executing a command entered is as follows:
//... skipping past stuff for IO redirection
pid = fork();
if(pid == 0){
//child
if(!strcmp(_simpleCommands[0]->_arguments[0],"printenv")){
//check if command is "printenv"
extern char **environ;
int i;
for(i = 0; environ[i] != NULL; i++){
printf("%s\n",environ[i]);
}
exit(0);
}
if(!strcmp(_simpleCommands[0]->_arguments[0],"setenv")){
//if command is "setenv" get parameters char* A, char* B
char * p = _simpleCommands[0]->_arguments[1];
char * s = _simpleCommands[0]->_arguments[2];
//putenv(char* s) needs to be formatted A=B; A is variable B is value
char param[strlen(p) + strlen(s) + 1];
strcat(param,p);
strcat(param,"=");
strcat(param,s);
putenv(param);
//setenv(p,s,1);
exit(0);
}
if(!strcmp(_simpleCommands[0]->_arguments[0],"unsetenv")){
//remove environment variable
unsetenv(_simpleCommands[0]->_arguments[0]);
exit(0);
}
//execute command
execvp(_simpleCommands[0]->_arguments[0],_simpleCommands->_arguments);
perror("-myshell");
_exit(1);
}
//omitting restore IO defaults...
If I run printenv it works properly, but if I try to set a new variable using either putenv() or setenv() my printenv() command returns the exact same thing, so it does not appear to be working.
As a side note, the problem may not be with the functions or how I called them, because my shell is executing the commands as though it had to format a wildcard (* or ?) which I am not sure should happen.

You appear to be calling fork unconditionally before examining the command line. But some shell built-in commands need to run in the parent process, so that their effect persists. All the built-ins that manipulate the environment fall in this category.
As an aside, I wouldn't try to use the C library's environment manipulation functions if I were writing a shell. I'd use three-argument main, copy envp into a data structure under my full control, and then feed that back into execve. This is partially because I'm a control freak, and partially because it's nigh-impossible to do anything complicated with setenv and/or putenv and not have a memory leak. See this older SO question for gory details.

What make you think it is not working? I wrote a simple test case below...and it worked as expected.
Making sure you setevn and prientevn are called in the same process.
#include <stdlib.h>
#include <assert.h>
int main()
{
char * s= "stack=overflow";
int ret = putenv(s);
assert(ret == 0);
//printout all the env
extern char **environ;
int i;
for(i = 0; environ[i] != NULL; i++){
printf("%s\n",environ[i]);
}
return 0;
}
pierr#ubuntu:~/workspace/so/c/env$ ./test | grep stack
stack=overflow

Related

C++ getpid() vs syscall(39)?

I read that syscall(39) returns the current process id (pid)
Then why these 2 programs output 2 different numbers?
int main() {
long r = syscall(39);
printf("returned %ld\n", r);
return 0;
}
and:
int main() {
long r = getpid();
printf("returned %ld\n", r);
return 0;
}
I am running my program in clion, and when I change the first line I get different result which is really strange.
Running the code in the answers I got (in macos):
returned getpid()=9390 vs. syscall(39)=8340
which is really strange.
In ubuntu I got same pid for both, why is that?
Making system calls by their number is not going to be portable.
Indeed, we see that 39 is getpid on Linux, but getppid ("get parent pid") on macOS.
getpid on macOS is 20.
So that's why you see a different result between getpid() and syscall(39) on macOS.
Note that macOS, being a BSD kernel derivative, is not related to Linux in any way. It can't possibly be, since it's closed-source.
There's one key detail that's missing here -- every time you run the program, your OS assigns it a new PID. Calling the same program twice in a row will likely return different PIDs - so what you're describing isn't a good way to test the difference between getpid() and syscall(39).
Here's a better program to compare the two that calls both functions in the same program.
#include <sys/syscall.h>
#include <stdio.h>
int main() {
long pid1 = getpid();
long pid2 = syscall(39);
printf("returned getpid()=%ld vs. syscall(39)=%ld\n", pid1, pid2);
return 0;
}

clock_gettime fails in chrooted Debian etch with CLOCK_PROCESS_CPUTIME_ID

I have setup a chrooted Debian Etch (32bit) under Ubuntu 12.04 (64bit), and it appears that clock_gettime() works with CLOCK_MONOTONIC, but fails with both CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. The errno is set to EINVAL, which according to the man page means that "The clk_id specified is not supported on this system."
All three clocks work fine outside the chrooted Debian and in 64bit chrooted Debian etch.
Can someone explains to me why this is the case and how to fix it?
Much appreciated.
I don't know the cause yet, but I have ideas that won't fit in the comment box.
First, you can make the test program simpler by compiling it as C instead of C++ and not linking it to libpthread. -lrt should be good enough to get clock_gettime. Also, compiling it with -static could make tracing easier since the dynamic linker startup stuff won't be there.
Static linking might even change the behavior of clock_gettime. It's worth trying just to find out whether it works around the bug.
Another thing I'd like to see is the output of this vdso-bypassing test program:
#define _GNU_SOURCE
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/syscall.h>
int main(void)
{
struct timespec ts;
if(syscall(SYS_clock_gettime, CLOCK_PROCESS_CPUTIME_ID, &ts)) {
perror("clock_gettime");
return 1;
}
printf("CLOCK_PROCESS_CPUTIME_ID: %lu.%09ld\n",
(unsigned long)ts.tv_sec, ts.tv_nsec);
return 0;
}
with and without -static, and if it fails, add strace.
Update (actually, skip this. go to the second update)
A couple more simple test ideas:
compile and run a 32-bit test program in the Ubuntu host system, by adding -m32 to the gcc command. It's possible that the kernel's 32-bit compatibility mode is causing the error. If that's the case, then the 32-bit version will fail no matter which libc it gets linked to.
take the non-static test programs you compiled under Debian, copy them to the Ubuntu host system and try to run them there. Change in behavior will point to libc as the cause.
Then it's time for the hard stuff. Looking at disassembled code and maybe single-stepping it in gdb. Instead of having you do that on your own, I'd like to get a copy of the code you're running. Upload a a static-compiled failing test program somewhere I can get it. Also a copy of the 32-bit vdso provided by your kernel might be interesting. To extract the vdso, run the following program (compiled in the 32-bit chroot) which will create a file called vdso.dump, and upload that too.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int getvseg(const char *which, const char *outfn)
{
FILE *maps, *outfile;
char buf[1024];
void *start, *end;
size_t sz;
void *copy;
int ret;
char search[strlen(which)+4];
maps = fopen("/proc/self/maps", "r");
if(!maps) {
perror("/proc/self/maps");
return 1;
}
outfile = fopen(outfn, "w");
if(!outfile) {
perror(outfn);
fclose(maps);
return 1;
}
sprintf(search, "[%s]\n", which);
while(fgets(buf, sizeof buf, maps)) {
if(strlen(buf)<strlen(search) ||
strcmp(buf+strlen(buf)-strlen(search),search))
continue;
if(sscanf(buf, "%p-%p", &start, &end)!=2) {
fprintf(stderr, "weird line in /proc/self/maps: %s", buf);
continue;
}
sz = (char *)end - (char *)start;
/* copy because I got an EFAULT trying to write directly from vsyscall */
copy = malloc(sz);
if(!copy) {
perror("malloc");
goto fail;
}
memcpy(copy, start, sz);
if(fwrite(copy, 1, sz, outfile)!=sz) {
if(ferror(outfile))
perror(outfn);
else
fprintf(stderr, "%s: short write", outfn);
free(copy);
goto fail;
}
free(copy);
goto success;
}
fprintf(stderr, "%s not found\n", which);
fail:
ret = 1;
goto out;
success:
ret = 0;
out:
fclose(maps);
fclose(outfile);
return ret;
}
int main(void)
{
int ret = 1;
if(!getvseg("vdso", "vdso.dump")) {
printf("vdso dumped to vdso.dump\n");
ret = 0;
}
if(!getvseg("vsyscall", "vsyscall.dump")) {
printf("vsyscall dumped to vsyscall.dump\n");
ret = 0;
}
return ret;
}
Update 2
I reproduced this by downloading an etch libc. It's definitely caused be glibc stupidity. Instead of a simple syscall wrapper for clock_gettime it has a big wad of preprocessor spaghetti culminating in "you can't use clockid's that we didn't pre-approve". You're not going to get it to work with that old glibc. Which brings us to the question I didn't want to ask: why are you trying to use an obsolete version of Debian anyway?

Can I prohibit certain functions from crashing a program?

I wish to make interactive code learning system, it allows users, (young programmers normally) to write contents of one function in c++ language, send it to server and there it will be compiled into dynamic library and called from main program.
Program expects function to return correct answer depending on given parameters.
Of course, there will be some kids, that will cause errors like segmentation fault. (server is Linux powered).
So, can I make signal handler that would exit function?
What I wish to accomplish:
for (int i = 0; i < PLAYER_NUM; i++) {
snprintf(buf, sizeof(buf), "players/%s.so", player[i]);
handle = dlopen(buf, RTLD_LAZY);
add[i] = (int (*)(int, int))dlsym(handle, "sum");
} // that was simply loading of functions from libraries.
for (int x = 0; x < 10; x++)
for (int i = 0; i < PLAYER_NUM; i++) {
if(failed[i]) continue;
ret = add[i](x, 5);
if(sigfault_received() || res != (x + 5)) {
failed[i] = true;
}
}
Faulty code can cause all kinds of issues which might not be recoverable. So handling SIGSEGV won't really help.
The solution is to run that code in a separate process and use IPC, pipes or sockets to communicate with the main process.
Use a proper sandbox, and not one you built yourself. You can't be expected to be as creative predicting mischief as 10 kids together. E.g. system("rm -rf /") won't immediately segfault your program, but it certainly is undesirable.

Using Cython to expose functionality to another application

I have this C++ code that shows how to extend a software by compiling it to a DLL and putting it in the application folder:
#include <windows.h>
#include <DemoPlugin.h>
/** A helper function to convert a char array into a
LPBYTE array. */
LPBYTE message(const char* message, long* pLen)
{
size_t length = strlen(message);
LPBYTE mem = (LPBYTE) GlobalAlloc(GPTR, length + 1);
for (unsigned int i = 0; i < length; i++)
{
mem[i] = message[i];
}
*pLen = length + 1;
return mem;
}
long __stdcall Execute(char* pMethodName, char* pParams,
char** ppBuffer, long* pBuffSize, long* pBuffType)
{
*pBuffType = 1;
if (strcmp(pMethodName, "") == 0)
{
*ppBuffer = (char*) message("Hello, World!",
pBuffSize);
}
else if (strcmp(pMethodName, "Count") == 0)
{
char buffer[1024];
int length = strlen(pParams);
*ppBuffer = (char*) message(itoa(length, buffer, 10),
pBuffSize);
}
else
{
*ppBuffer = (char*) message("Incorrect usage.",
pBuffSize);
}
return 0;
}
Is is possible to make a plugin this way using Cython? Or even py2exe? The DLL just has to have an entry point, right?
Or should I just compile it natively and embed Python using elmer?
I think the solution is to use both. Let me explain.
Cython makes it convenient to make a fast plugin using python but inconvenient (if at all possible) to make the right "kind" of DLL. You would probably have to use the standalone mode so that the necessary python runtime is included and then mess with the generated c code so an appropriate DLL gets compiled.
Conversely, elmer makes it convenient to make the DLL but runs "pure" python code which might not be fast enough. I assume speed is an issue because you are considering cython as opposed to simple embedding.
My suggestion is this: the pure python code that elmer executes should import a standard cython python extension and execute code from it. This way you don't have to hack anything ugly and you have the best of both worlds.
One more solution to consider is using shedskin, because that way you can get c++ code from your python code that is independent from the python runtime.

Can I set a data breakpoint in runtime in System C (or in Plain Vanilla C++)?

I have a class in system-C with some data members as such:
long double x[8];
I'm initializing it in the construction like this:
for (i = 0; i < 8; ++i) {
x[i] = 0;
}
But the first time I use it in my code I have garbage there.
Because of the way the system is built I can't connect a debugger easily. Are there any methods to set a data breakpoint in the code so that it tells me where in the code the variables were actually changed, but without hooking up a debugger?
Edit:
#Prakash:
Actually, this is a typo in the question, but not in my code... Thanks!
You could try starting a second thread which spins, looking for changes in the variable:
#include <pthread.h>
void *ThreadProc(void *arg)
{
volatile long double *x = (volatile long double *)arg;
while(1)
{
for(int i = 0; i < 8; i++)
{
if(x[i] != 0)
{
__asm__ __volatile__ ("int 3"); // breakpoint (x86)
}
}
return 0; // Never reached, but placate the compiler
}
...
pthread_t threadID;
pthread_create(&threadID, NULL, ThreadProc, &x[0]);
This will raise a SIGTRAP signal to your application whenever any of the x values is not zero.
Just use printk/syslog.
It's old-fashioned, but super duper easy.
Sure, it will be garbage!
The code should have been as
for (i = 0; i < 8; ++i) {
x[i] = 0;
}
EDIT: Oops, Sorry for underestimating ;)
#Frank
Actually, that lets me log debug prints to a file. What I'm looking for is something that will let me print something whenever a variable changes, without me explicitly looking for the variable.
How about Conditional breakpoints? You could try for various conditions like first element value is zero or non zero, etc??
That's assuming I can easily connect a debugger. The whole point is that I only have a library, but the executable that linked it in isn't readily available.