Checking Endianness of your Operating System

Sometimes when trying to debug oddities in your C programs, especially when doing cross-compilation to other architectures it is good to know when going from an x86 (Little Endian) to say an ARM processor running in Big Endian.  This is doubly useful when dealing with encryption keys or network packets.  If you have an encryption key on a desktop machine running in Little Endian, and the decryption key on a Big Endian system, you have to take into account the differing byte order.  There is a great Wikipedia article about Endianness if you would like to learn more. There is also a nifty C program you can compile to check the Endianess of your OS.

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int x = 1;
 
    if (*(char *)&x == 1)
        fprintf(stderr, "Little Endian\n");
    else
        fprintf(stderr, "Big Endian\n");
 
    return 0;
}

To compile:

gcc -o endian endian.c

Then run:

erik@debian:~$ ./endian
Little Endian

Voila, now you know the Endianness of your Operating System.

Change File Owner and Group With chown

Every file on a Linux system has an owner and is associated with a group. Yesterday I talked about setting file permissions on your Linux system, I will now discuss the basics of owner and group permissions. These modifications can be done using the chown command while running as the root user.

To view a file’s current owner and group you can use the ls -l command.

$ ls -l taps.sh 
-rwxr-xr-x 1 erik users 1014 2010-10-28 13:30 taps.sh

In the above example, ‘erik’ is the owner and ‘users’ is the group that the file is currently within. This means that any other user that is part of the group ‘users’ will also have r-x access.

What Does chown Do?

Excerpt from the chown manual page:
chown changes the user and/or group ownership of each given file. If only an owner (a user name or numeric user ID) is given, that user is made the owner of each given file, and the files’ group is not changed. If the owner is followed by a colon and a group name (or numeric group ID), with no spaces between them, the group owner‐ship of the files is changed as well. If a colon but no group name follows the user name, that user is made the owner of the files and the group of the files is changed to that user’s login group. If the colon and group are given, but the owner is omitted, only the group of the files is changed; in this case, chown performs the same function as chgrp. If only a colon is given, or if the entire operand is empty, neither the owner nor the group is changed.

To better understand that here are a few examples:

# chown erik:cdrom taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik cdrom 1014 2010-10-28 13:30 taps.sh
# chown erik:erik taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh

As you can see I changed the group to ‘cdrom’ then changed it back to ‘erik’. In doing so, none of my file permissions changed, but I did become part of the cdrom group, which means I may have gained access to other files that were also part of the ‘cdrom’ group.

If I only specify the colon after the user setting, then the file will automatically take use the users login group as the default. For example:

# ls -l taps.sh 
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh
# chown erik:cdrom taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik cdrom 1014 2010-10-28 13:30 taps.sh
# chown erik: taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh

You can also set the file to a group that does not even necessarily exist.

# chown erik:1234 taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik 1234 1014 2010-10-28 13:30 taps.sh
# chown erik: taps.sh 
# ls -l taps.sh 
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh

As you can see the 1234 in yellow identifying my group.

Recursive Group Change

To recursively change all files within a directory you may use the -R before setting the user:group option.

# chown -R erik:1234 /home/erik/

This will change all files within my home directory to now become part of the 1234 group. Yay.

The purpose for group association permits certain users the ability to use devices or see certain files on a computer. For instance, I am currently associated with a set of groups:

$ groups
erik adm dialout cdrom plugdev lpadmin admin sambashare

Thus, I have access to certain files or devices because I am now part of that group.

How To: Modify Bash Environment in C

This article will outline how to modify environment variables from a running C process. It should be noted that setting variables from your C process into the environment will only persist during process lifetime. Environment variables are propagated forward to a child program, but not backward to the parent.

Earlier I wrote about creating, removing, changing environment variables directing in Bash, now I will show you how to do these modifications using C.

Creating An Environment Variable In C process

Creating the environment variable can easily be achieved using the C function int setenv(const char *name, const char *value, int overwrite);. It takes two parameters, the first parameter being the name of the environment variable, and the second being a flag outlining whether to overwrite a pre-existing variable of the same name.

#include<stdio.h>
#include <stdlib.h>
 
int main()
{
	char *name = "DATA";
	char *value = "erik";
 
	if(setenv(name, value, 1) < 0){
		fprintf(stderr, "Could not create environment variable.\n");
		return -1;
	}
 
	fprintf(stdout, "-%s-\n", getenv("DATA"));
	return 0;
}

The variable will persist for any children you may fork, or if you execute a system call to grab the variables.

Removing An Environment Variable Using unsetenv

To remove an environment variable using your C process, you may call int unsetenv(const char *name);. Where char *name is the name from the environment. If name does not exist in the environment, then the function succeeds, and the environment is unchanged.

unsetenv(name);

Clear The Environment Using clearenv

To clear the entire environment you can:

clearenv();

Or

environ = NULL;

Reading An Environment Variable Using getenv

To just read an environment variable there is the C call, char *getenv(const char *name);. This will return the value of the environment variable.

getenv(“data”);

Those are the basic functions you can execute from your C process to set, remove, create those environment variables.

C getchar() Usage and Examples

The getchar() function is used to grab character by character inputs from the standard input stream. getchar() is special in that it takes a void as its argument, i.e. nothing and it returns the next character from standard input. This can be used for basic input into any c program.

I will outline a basic example here.

How To Use getchar()

The getchar() function, as I mentioned above is quite basic, I will show you how to read input from standard in and print it back out to the terminal as standard out.

#include <stdio.h>
 
int main()
{
        char c;
        for(;;){
                c=getchar();
                if(c == 'q') // Compare input to 'q' character
                        break;
                fprintf(stdout, "%c\n", c);
        }
        return 0;
}

To compile this program:

erik@debian:~/getchar_ex$ gcc -o getchar_test getchar_test.c

Using this function:

erik@debian:~/getchar_ex$ ./getchar_test
a
a
 
 
b
b
 
 
c
c
 
 
q
q
erik@debian:~/getchar_ex$

As you can see, we read in each character then the process prints it back out. Nothing to it really.

getchar() Return Values

getchar() will return an unsigned char that is internally cast to an int. If there is an error or end of line the function will return an EOF (end of file).

C Debugging Macros

Any experienced programmer can relate to sprinkling their code with printf statements to try and figure out a NULL pointer, or perhaps whether a function has been reached or not. This is the first step towards debugging code.   Further down the line can include using GDB or Valgrind to check code entry paths, modify variables during runtime or check memory usage or look for memory leaks from non-free malloc’d data upon program exit.  These tools are usually pulled out when the going gets tough!  As an initial pass during basic unit testing debug information is extremely handy.  What I usually attempt to do is create a header file (*.h) that can be included in any (*.c) files I happen to be working on.  This way, if I modify my macro it only requires a change in one place to complete.  For example:

#include <stdio.h>
#include <stdlib.h>
 
#ifdef DEBUG
#define DEBUGP(x, args ...) fprintf(stderr, " [%s(), %s:%u]\n" \
x, __FUNCTION__, __FILE__,__LINE__, ## args)
#else
#define DEBUGP(x, args ...)
#endif
 
void calculation() {
	DEBUGP("data: %u\n",5 * 5);
}
 
int main()
{
	DEBUGP("Program started.\n");
	calculation();
	return 0;
}

Now every time we want to output any debug information we can call DEBUGP(..data..);.  In doing so, we also get the file name, the function it was called in, and what line it occurred at.  This is extremely helpful for debugging purposes.  This macro utilizes the built-in C macros of __LINE____FUNCTION__, and __FILE__.   Another piece to note is, I have #ifdef DEBUG, this means that if this variable is defined then we can reference DEBUGP, if the DEBUG variable is not defined then we call our “#define DEBUGP(x, args …)“, which does not print out any debug data.  This means during compilation we can pass the -DDEBUG flag to the compiler, meaning we can turn on/off our debugging flag effortlessly.

So putting this all together, lets compile the program with the flag enabled.

erik@debian:/debug$ gcc -DDEBUG -o debug debug.c

Now that it is compiled lets run it.

erik@debian:/debug$./debug
[main(), debug.c:18] Program started.
[calculation(), debug.c:12] data: 25

So as we can see, with a little bit of organization and taking advantage of C macros we can make debugging a lot easier for ourselves!